diff options
author | Fabian Hemmer <copy@copy.sh> | 2021-02-03 16:15:37 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2021-02-17 15:15:06 -0300 |
commit | cef7af25c9d3a7ea5d0c82424dc8bf93a95b6fc3 (patch) | |
tree | c55d0626aaa0f3dfdf360a476599b64b73453032 | |
parent | 48859e5293a261437deb0231d78a388e242ed2d3 (diff) |
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>
-rw-r--r-- | tools/perf/tests/Build | 1 | ||||
-rw-r--r-- | tools/perf/tests/builtin-test.c | 4 | ||||
-rw-r--r-- | tools/perf/tests/code-reading.c | 10 | ||||
-rw-r--r-- | tools/perf/tests/demangle-ocaml-test.c | 43 | ||||
-rw-r--r-- | tools/perf/tests/tests.h | 1 | ||||
-rw-r--r-- | tools/perf/util/Build | 1 | ||||
-rw-r--r-- | tools/perf/util/demangle-ocaml.c | 80 | ||||
-rw-r--r-- | tools/perf/util/demangle-ocaml.h | 7 | ||||
-rw-r--r-- | tools/perf/util/string.c | 9 | ||||
-rw-r--r-- | tools/perf/util/string2.h | 2 | ||||
-rw-r--r-- | tools/perf/util/symbol-elf.c | 9 |
11 files changed, 156 insertions, 11 deletions
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index aa4dc4f5abde..650aec19d490 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build @@ -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 diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 7273823d0d02..c4b888f18e9c 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -339,6 +339,10 @@ static struct test generic_tests[] = { .func = test__demangle_java, }, { + .desc = "Demangle OCaml", + .func = test__demangle_ocaml, + }, + { .desc = "Parse and process metrics", .func = test__parse_metric, }, diff --git a/tools/perf/tests/code-reading.c b/tools/perf/tests/code-reading.c index 7c098d49c77e..280f0348a09c 100644 --- a/tools/perf/tests/code-reading.c +++ b/tools/perf/tests/code-reading.c @@ -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) { diff --git a/tools/perf/tests/demangle-ocaml-test.c b/tools/perf/tests/demangle-ocaml-test.c new file mode 100644 index 000000000000..a273ed5163d7 --- /dev/null +++ b/tools/perf/tests/demangle-ocaml-test.c @@ -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; +} diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 8e24a61fe4c2..b85f005308a3 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -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); diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 188521f34347..e3e12f9d4733 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -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 diff --git a/tools/perf/util/demangle-ocaml.c b/tools/perf/util/demangle-ocaml.c new file mode 100644 index 000000000000..3df14e67c622 --- /dev/null +++ b/tools/perf/util/demangle-ocaml.c @@ -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; +} diff --git a/tools/perf/util/demangle-ocaml.h b/tools/perf/util/demangle-ocaml.h new file mode 100644 index 000000000000..843cc4fa10a6 --- /dev/null +++ b/tools/perf/util/demangle-ocaml.h @@ -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 */ diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index 52603876c548..f6d90cdd9225 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -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; +} diff --git a/tools/perf/util/string2.h b/tools/perf/util/string2.h index 73df616ced43..56c30fef9682 100644 --- a/tools/perf/util/string2.h +++ b/tools/perf/util/string2.h @@ -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 */ diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index ecc05aa8399d..6dff843fd883 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -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 |