Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Fabian Hemmer and committed by
Arnaldo Carvalho de Melo
cef7af25 48859e52

+156 -11
+1
tools/perf/tests/Build
··· 58 58 perf-y += genelf.o 59 59 perf-y += api-io.o 60 60 perf-y += demangle-java-test.o 61 + perf-y += demangle-ocaml-test.o 61 62 perf-y += pfm.o 62 63 perf-y += parse-metric.o 63 64 perf-y += pe-file-parsing.o
+4
tools/perf/tests/builtin-test.c
··· 339 339 .func = test__demangle_java, 340 340 }, 341 341 { 342 + .desc = "Demangle OCaml", 343 + .func = test__demangle_ocaml, 344 + }, 345 + { 342 346 .desc = "Parse and process metrics", 343 347 .func = test__parse_metric, 344 348 },
+1 -9
tools/perf/tests/code-reading.c
··· 26 26 #include "event.h" 27 27 #include "record.h" 28 28 #include "util/mmap.h" 29 + #include "util/string2.h" 29 30 #include "util/synthetic-events.h" 30 31 #include "thread.h" 31 32 ··· 41 40 u64 done[1024]; 42 41 size_t done_cnt; 43 42 }; 44 - 45 - static unsigned int hex(char c) 46 - { 47 - if (c >= '0' && c <= '9') 48 - return c - '0'; 49 - if (c >= 'a' && c <= 'f') 50 - return c - 'a' + 10; 51 - return c - 'A' + 10; 52 - } 53 43 54 44 static size_t read_objdump_chunk(const char **line, unsigned char **buf, 55 45 size_t *buf_len)
+43
tools/perf/tests/demangle-ocaml-test.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <string.h> 3 + #include <stdlib.h> 4 + #include <stdio.h> 5 + #include "tests.h" 6 + #include "session.h" 7 + #include "debug.h" 8 + #include "demangle-ocaml.h" 9 + 10 + int test__demangle_ocaml(struct test *test __maybe_unused, int subtest __maybe_unused) 11 + { 12 + int ret = TEST_OK; 13 + char *buf = NULL; 14 + size_t i; 15 + 16 + struct { 17 + const char *mangled, *demangled; 18 + } test_cases[] = { 19 + { "main", 20 + NULL }, 21 + { "camlStdlib__array__map_154", 22 + "Stdlib.array.map" }, 23 + { "camlStdlib__anon_fn$5bstdlib$2eml$3a334$2c0$2d$2d54$5d_1453", 24 + "Stdlib.anon_fn[stdlib.ml:334,0--54]" }, 25 + { "camlStdlib__bytes__$2b$2b_2205", 26 + "Stdlib.bytes.++" }, 27 + }; 28 + 29 + for (i = 0; i < sizeof(test_cases) / sizeof(test_cases[0]); i++) { 30 + buf = ocaml_demangle_sym(test_cases[i].mangled); 31 + if ((buf == NULL && test_cases[i].demangled != NULL) 32 + || (buf != NULL && test_cases[i].demangled == NULL) 33 + || (buf != NULL && strcmp(buf, test_cases[i].demangled))) { 34 + pr_debug("FAILED: %s: %s != %s\n", test_cases[i].mangled, 35 + buf == NULL ? "(null)" : buf, 36 + test_cases[i].demangled == NULL ? "(null)" : test_cases[i].demangled); 37 + ret = TEST_FAIL; 38 + } 39 + free(buf); 40 + } 41 + 42 + return ret; 43 + }
+1
tools/perf/tests/tests.h
··· 119 119 int test__jit_write_elf(struct test *test, int subtest); 120 120 int test__api_io(struct test *test, int subtest); 121 121 int test__demangle_java(struct test *test, int subtest); 122 + int test__demangle_ocaml(struct test *test, int subtest); 122 123 int test__pfm(struct test *test, int subtest); 123 124 const char *test__pfm_subtest_get_desc(int subtest); 124 125 int test__pfm_subtest_get_nr(void);
+1
tools/perf/util/Build
··· 173 173 174 174 perf-$(CONFIG_LIBCAP) += cap.o 175 175 176 + perf-y += demangle-ocaml.o 176 177 perf-y += demangle-java.o 177 178 perf-y += demangle-rust.o 178 179
+80
tools/perf/util/demangle-ocaml.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <string.h> 3 + #include <stdlib.h> 4 + #include "util/string2.h" 5 + 6 + #include "demangle-ocaml.h" 7 + 8 + #include <linux/ctype.h> 9 + 10 + static const char *caml_prefix = "caml"; 11 + static const size_t caml_prefix_len = 4; 12 + 13 + /* mangled OCaml symbols start with "caml" followed by an upper-case letter */ 14 + static bool 15 + ocaml_is_mangled(const char *sym) 16 + { 17 + return 0 == strncmp(sym, caml_prefix, caml_prefix_len) 18 + && isupper(sym[caml_prefix_len]); 19 + } 20 + 21 + /* 22 + * input: 23 + * sym: a symbol which may have been mangled by the OCaml compiler 24 + * return: 25 + * if the input doesn't look like a mangled OCaml symbol, NULL is returned 26 + * otherwise, a newly allocated string containing the demangled symbol is returned 27 + */ 28 + char * 29 + ocaml_demangle_sym(const char *sym) 30 + { 31 + char *result; 32 + int j = 0; 33 + int i; 34 + int len; 35 + 36 + if (!ocaml_is_mangled(sym)) { 37 + return NULL; 38 + } 39 + 40 + len = strlen(sym); 41 + 42 + /* the demangled symbol is always smaller than the mangled symbol */ 43 + result = malloc(len + 1); 44 + if (!result) 45 + return NULL; 46 + 47 + /* skip "caml" prefix */ 48 + i = caml_prefix_len; 49 + 50 + while (i < len) { 51 + if (sym[i] == '_' && sym[i + 1] == '_') { 52 + /* "__" -> "." */ 53 + result[j++] = '.'; 54 + i += 2; 55 + } 56 + else if (sym[i] == '$' && isxdigit(sym[i + 1]) && isxdigit(sym[i + 2])) { 57 + /* "$xx" is a hex-encoded character */ 58 + result[j++] = (hex(sym[i + 1]) << 4) | hex(sym[i + 2]); 59 + i += 3; 60 + } 61 + else { 62 + result[j++] = sym[i++]; 63 + } 64 + } 65 + result[j] = '\0'; 66 + 67 + /* scan backwards to remove an "_" followed by decimal digits */ 68 + if (j != 0 && isdigit(result[j - 1])) { 69 + while (--j) { 70 + if (!isdigit(result[j])) { 71 + break; 72 + } 73 + } 74 + if (result[j] == '_') { 75 + result[j] = '\0'; 76 + } 77 + } 78 + 79 + return result; 80 + }
+7
tools/perf/util/demangle-ocaml.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef __PERF_DEMANGLE_OCAML 3 + #define __PERF_DEMANGLE_OCAML 1 4 + 5 + char * ocaml_demangle_sym(const char *str); 6 + 7 + #endif /* __PERF_DEMANGLE_OCAML */
+9
tools/perf/util/string.c
··· 293 293 294 294 return ret; 295 295 } 296 + 297 + unsigned int hex(char c) 298 + { 299 + if (c >= '0' && c <= '9') 300 + return c - '0'; 301 + if (c >= 'a' && c <= 'f') 302 + return c - 'a' + 10; 303 + return c - 'A' + 10; 304 + }
+2
tools/perf/util/string2.h
··· 38 38 char *strpbrk_esc(char *str, const char *stopset); 39 39 char *strdup_esc(const char *str); 40 40 41 + unsigned int hex(char c); 42 + 41 43 #endif /* PERF_STRING_H */
+7 -2
tools/perf/util/symbol-elf.c
··· 12 12 #include "maps.h" 13 13 #include "symbol.h" 14 14 #include "symsrc.h" 15 + #include "demangle-ocaml.h" 15 16 #include "demangle-java.h" 16 17 #include "demangle-rust.h" 17 18 #include "machine.h" ··· 252 251 return demangled; 253 252 254 253 demangled = bfd_demangle(NULL, elf_name, demangle_flags); 255 - if (demangled == NULL) 256 - demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET); 254 + if (demangled == NULL) { 255 + demangled = ocaml_demangle_sym(elf_name); 256 + if (demangled == NULL) { 257 + demangled = java_demangle_sym(elf_name, JAVA_DEMANGLE_NORET); 258 + } 259 + } 257 260 else if (rust_is_mangled(demangled)) 258 261 /* 259 262 * Input to Rust demangling is the BFD-demangled