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: Fabian Hemmer <copy@copy.sh> Acked-by: Namhyung 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: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Родитель
48859e5293
Коммит
cef7af25c9
|
@ -58,6 +58,7 @@ perf-y += time-utils-test.o
|
|||
perf-y += genelf.o
|
||||
perf-y += api-io.o
|
||||
perf-y += demangle-java-test.o
|
||||
perf-y += demangle-ocaml-test.o
|
||||
perf-y += pfm.o
|
||||
perf-y += parse-metric.o
|
||||
perf-y += pe-file-parsing.o
|
||||
|
|
|
@ -338,6 +338,10 @@ static struct test generic_tests[] = {
|
|||
.desc = "Demangle Java",
|
||||
.func = test__demangle_java,
|
||||
},
|
||||
{
|
||||
.desc = "Demangle OCaml",
|
||||
.func = test__demangle_ocaml,
|
||||
},
|
||||
{
|
||||
.desc = "Parse and process metrics",
|
||||
.func = test__parse_metric,
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "event.h"
|
||||
#include "record.h"
|
||||
#include "util/mmap.h"
|
||||
#include "util/string2.h"
|
||||
#include "util/synthetic-events.h"
|
||||
#include "thread.h"
|
||||
|
||||
|
@ -41,15 +42,6 @@ struct state {
|
|||
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,
|
||||
size_t *buf_len)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// 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);
|
|||
int test__jit_write_elf(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_ocaml(struct test *test, int subtest);
|
||||
int test__pfm(struct test *test, int subtest);
|
||||
const char *test__pfm_subtest_get_desc(int subtest);
|
||||
int test__pfm_subtest_get_nr(void);
|
||||
|
|
|
@ -173,6 +173,7 @@ perf-$(CONFIG_ZSTD) += zstd.o
|
|||
|
||||
perf-$(CONFIG_LIBCAP) += cap.o
|
||||
|
||||
perf-y += demangle-ocaml.o
|
||||
perf-y += demangle-java.o
|
||||
perf-y += demangle-rust.o
|
||||
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// 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;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
/* 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)
|
|||
|
||||
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);
|
|||
char *strpbrk_esc(char *str, const char *stopset);
|
||||
char *strdup_esc(const char *str);
|
||||
|
||||
unsigned int hex(char c);
|
||||
|
||||
#endif /* PERF_STRING_H */
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "maps.h"
|
||||
#include "symbol.h"
|
||||
#include "symsrc.h"
|
||||
#include "demangle-ocaml.h"
|
||||
#include "demangle-java.h"
|
||||
#include "demangle-rust.h"
|
||||
#include "machine.h"
|
||||
|
@ -251,8 +252,12 @@ static char *demangle_sym(struct dso *dso, int kmodule, const char *elf_name)
|
|||
return demangled;
|
||||
|
||||
demangled = bfd_demangle(NULL, elf_name, demangle_flags);
|
||||
if (demangled == NULL)
|
||||
demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET);
|
||||
if (demangled == NULL) {
|
||||
demangled = ocaml_demangle_sym(elf_name);
|
||||
if (demangled == NULL) {
|
||||
demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET);
|
||||
}
|
||||
}
|
||||
else if (rust_is_mangled(demangled))
|
||||
/*
|
||||
* Input to Rust demangling is the BFD-demangled
|
||||
|
|
Загрузка…
Ссылка в новой задаче