Commit cef7af25 authored by Fabian Hemmer's avatar Fabian Hemmer Committed by Arnaldo Carvalho de Melo

perf tools: Add OCaml demangling

Detect symbols generated by the OCaml compiler based on their prefix.

Demangle OCaml symbols, returning a newly allocated string (like the
existing Java demangling functionality).

Move a helper function (hex) from tests/code-reading.c to util/string.c

To test:

  echo 'Printf.printf "%d\n" (Random.int 42)' > test.ml
  perf record ocamlopt.opt test.ml
  perf report -d ocamlopt.opt
Signed-off-by: default avatarFabian Hemmer <copy@copy.sh>
Acked-by: default avatarNamhyung Kim <namhyung@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
LPU-Reference: 20210203211537.b25ytjb6dq5jfbwx@nyu
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 48859e52
...@@ -58,6 +58,7 @@ perf-y += time-utils-test.o ...@@ -58,6 +58,7 @@ perf-y += time-utils-test.o
perf-y += genelf.o perf-y += genelf.o
perf-y += api-io.o perf-y += api-io.o
perf-y += demangle-java-test.o perf-y += demangle-java-test.o
perf-y += demangle-ocaml-test.o
perf-y += pfm.o perf-y += pfm.o
perf-y += parse-metric.o perf-y += parse-metric.o
perf-y += pe-file-parsing.o perf-y += pe-file-parsing.o
......
...@@ -338,6 +338,10 @@ static struct test generic_tests[] = { ...@@ -338,6 +338,10 @@ static struct test generic_tests[] = {
.desc = "Demangle Java", .desc = "Demangle Java",
.func = test__demangle_java, .func = test__demangle_java,
}, },
{
.desc = "Demangle OCaml",
.func = test__demangle_ocaml,
},
{ {
.desc = "Parse and process metrics", .desc = "Parse and process metrics",
.func = test__parse_metric, .func = test__parse_metric,
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "event.h" #include "event.h"
#include "record.h" #include "record.h"
#include "util/mmap.h" #include "util/mmap.h"
#include "util/string2.h"
#include "util/synthetic-events.h" #include "util/synthetic-events.h"
#include "thread.h" #include "thread.h"
...@@ -41,15 +42,6 @@ struct state { ...@@ -41,15 +42,6 @@ struct state {
size_t done_cnt; size_t done_cnt;
}; };
static unsigned int hex(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return c - 'A' + 10;
}
static size_t read_objdump_chunk(const char **line, unsigned char **buf, static size_t read_objdump_chunk(const char **line, unsigned char **buf,
size_t *buf_len) size_t *buf_len)
{ {
......
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "tests.h"
#include "session.h"
#include "debug.h"
#include "demangle-ocaml.h"
int test__demangle_ocaml(struct test *test __maybe_unused, int subtest __maybe_unused)
{
int ret = TEST_OK;
char *buf = NULL;
size_t i;
struct {
const char *mangled, *demangled;
} test_cases[] = {
{ "main",
NULL },
{ "camlStdlib__array__map_154",
"Stdlib.array.map" },
{ "camlStdlib__anon_fn$5bstdlib$2eml$3a334$2c0$2d$2d54$5d_1453",
"Stdlib.anon_fn[stdlib.ml:334,0--54]" },
{ "camlStdlib__bytes__$2b$2b_2205",
"Stdlib.bytes.++" },
};
for (i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) {
buf = ocaml_demangle_sym(test_cases[i].mangled);
if ((buf == NULL && test_cases[i].demangled != NULL)
|| (buf != NULL && test_cases[i].demangled == NULL)
|| (buf != NULL && strcmp(buf, test_cases[i].demangled))) {
pr_debug("FAILED: %s: %s != %s\n", test_cases[i].mangled,
buf == NULL ? "(null)" : buf,
test_cases[i].demangled == NULL ? "(null)" : test_cases[i].demangled);
ret = TEST_FAIL;
}
free(buf);
}
return ret;
}
...@@ -119,6 +119,7 @@ int test__time_utils(struct test *t, int subtest); ...@@ -119,6 +119,7 @@ int test__time_utils(struct test *t, int subtest);
int test__jit_write_elf(struct test *test, int subtest); int test__jit_write_elf(struct test *test, int subtest);
int test__api_io(struct test *test, int subtest); int test__api_io(struct test *test, int subtest);
int test__demangle_java(struct test *test, int subtest); int test__demangle_java(struct test *test, int subtest);
int test__demangle_ocaml(struct test *test, int subtest);
int test__pfm(struct test *test, int subtest); int test__pfm(struct test *test, int subtest);
const char *test__pfm_subtest_get_desc(int subtest); const char *test__pfm_subtest_get_desc(int subtest);
int test__pfm_subtest_get_nr(void); int test__pfm_subtest_get_nr(void);
......
...@@ -173,6 +173,7 @@ perf-$(CONFIG_ZSTD) += zstd.o ...@@ -173,6 +173,7 @@ perf-$(CONFIG_ZSTD) += zstd.o
perf-$(CONFIG_LIBCAP) += cap.o perf-$(CONFIG_LIBCAP) += cap.o
perf-y += demangle-ocaml.o
perf-y += demangle-java.o perf-y += demangle-java.o
perf-y += demangle-rust.o perf-y += demangle-rust.o
......
// SPDX-License-Identifier: GPL-2.0
#include <string.h>
#include <stdlib.h>
#include "util/string2.h"
#include "demangle-ocaml.h"
#include <linux/ctype.h>
static const char *caml_prefix = "caml";
static const size_t caml_prefix_len = 4;
/* mangled OCaml symbols start with "caml" followed by an upper-case letter */
static bool
ocaml_is_mangled(const char *sym)
{
return 0 == strncmp(sym, caml_prefix, caml_prefix_len)
&& isupper(sym[caml_prefix_len]);
}
/*
* input:
* sym: a symbol which may have been mangled by the OCaml compiler
* return:
* if the input doesn't look like a mangled OCaml symbol, NULL is returned
* otherwise, a newly allocated string containing the demangled symbol is returned
*/
char *
ocaml_demangle_sym(const char *sym)
{
char *result;
int j = 0;
int i;
int len;
if (!ocaml_is_mangled(sym)) {
return NULL;
}
len = strlen(sym);
/* the demangled symbol is always smaller than the mangled symbol */
result = malloc(len + 1);
if (!result)
return NULL;
/* skip "caml" prefix */
i = caml_prefix_len;
while (i < len) {
if (sym[i] == '_' && sym[i + 1] == '_') {
/* "__" -> "." */
result[j++] = '.';
i += 2;
}
else if (sym[i] == '$' && isxdigit(sym[i + 1]) && isxdigit(sym[i + 2])) {
/* "$xx" is a hex-encoded character */
result[j++] = (hex(sym[i + 1]) << 4) | hex(sym[i + 2]);
i += 3;
}
else {
result[j++] = sym[i++];
}
}
result[j] = '\0';
/* scan backwards to remove an "_" followed by decimal digits */
if (j != 0 && isdigit(result[j - 1])) {
while (--j) {
if (!isdigit(result[j])) {
break;
}
}
if (result[j] == '_') {
result[j] = '\0';
}
}
return result;
}
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __PERF_DEMANGLE_OCAML
#define __PERF_DEMANGLE_OCAML 1
char * ocaml_demangle_sym(const char *str);
#endif /* __PERF_DEMANGLE_OCAML */
...@@ -293,3 +293,12 @@ char *strdup_esc(const char *str) ...@@ -293,3 +293,12 @@ char *strdup_esc(const char *str)
return ret; return ret;
} }
unsigned int hex(char c)
{
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'a' && c <= 'f')
return c - 'a' + 10;
return c - 'A' + 10;
}
...@@ -38,4 +38,6 @@ char *asprintf__tp_filter_pids(size_t npids, pid_t *pids); ...@@ -38,4 +38,6 @@ char *asprintf__tp_filter_pids(size_t npids, pid_t *pids);
char *strpbrk_esc(char *str, const char *stopset); char *strpbrk_esc(char *str, const char *stopset);
char *strdup_esc(const char *str); char *strdup_esc(const char *str);
unsigned int hex(char c);
#endif /* PERF_STRING_H */ #endif /* PERF_STRING_H */
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "maps.h" #include "maps.h"
#include "symbol.h" #include "symbol.h"
#include "symsrc.h" #include "symsrc.h"
#include "demangle-ocaml.h"
#include "demangle-java.h" #include "demangle-java.h"
#include "demangle-rust.h" #include "demangle-rust.h"
#include "machine.h" #include "machine.h"
...@@ -251,8 +252,12 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name) ...@@ -251,8 +252,12 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
return demangled; return demangled;
demangled = bfd_demangle(NULL, elf_name, demangle_flags); demangled = bfd_demangle(NULL, elf_name, demangle_flags);
if (demangled == NULL) if (demangled == NULL) {
demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET); demangled = ocaml_demangle_sym(elf_name);
if (demangled == NULL) {
demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET);
}
}
else if (rust_is_mangled(demangled)) else if (rust_is_mangled(demangled))
/* /*
* Input to Rust demangling is the BFD-demangled * Input to Rust demangling is the BFD-demangled
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment