Commit d80406453ad4a69932dc17a617d5b7bc7ae80b8f
Committed by
Arnaldo Carvalho de Melo
1 parent
b843f62ad9
perf symbols: Allow user probes on versioned symbols
Symbol versioning, as in glibc, results in symbols being defined as: <real symbol>@[@]<version> (Note that "@@" identifies a default symbol, if the symbol name is repeated.) perf is currently unable to deal with this, and is unable to create user probes at such symbols: -- $ nm /lib/powerpc64le-linux-gnu/libpthread.so.0 | grep pthread_create 0000000000008d30 t __pthread_create_2_1 0000000000008d30 T pthread_create@@GLIBC_2.17 $ /usr/bin/sudo perf probe -v -x /lib/powerpc64le-linux-gnu/libpthread.so.0 pthread_create probe-definition(0): pthread_create symbol:pthread_create file:(null) line:0 offset:0 return:0 lazy:(null) 0 arguments Open Debuginfo file: /usr/lib/debug/lib/powerpc64le-linux-gnu/libpthread-2.19.so Try to find probe point from debuginfo. Probe point 'pthread_create' not found. Error: Failed to add events. Reason: No such file or directory (Code: -2) -- One is not able to specify the fully versioned symbol, either, due to syntactic conflicts with other uses of "@" by perf: -- $ /usr/bin/sudo perf probe -v -x /lib/powerpc64le-linux-gnu/libpthread.so.0 pthread_create@@GLIBC_2.17 probe-definition(0): pthread_create@@GLIBC_2.17 Semantic error :SRC@SRC is not allowed. 0 arguments Error: Command Parse Error. Reason: Invalid argument (Code: -22) -- This patch ignores versioning for default symbols, thus allowing probes to be created for these symbols: -- $ /usr/bin/sudo ./perf probe -x /lib/powerpc64le-linux-gnu/libpthread.so.0 pthread_create Added new event: probe_libpthread:pthread_create (on pthread_create in /lib/powerpc64le-linux-gnu/libpthread-2.19.so) You can now use it in all perf tools, such as: perf record -e probe_libpthread:pthread_create -aR sleep 1 $ /usr/bin/sudo ./perf record -e probe_libpthread:pthread_create -aR ./test 2 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.052 MB perf.data (2 samples) ] $ /usr/bin/sudo ./perf script test 2915 [000] 19124.260729: probe_libpthread:pthread_create: (3fff99248d38) test 2916 [000] 19124.260962: probe_libpthread:pthread_create: (3fff99248d38) $ /usr/bin/sudo ./perf probe --del=probe_libpthread:pthread_create Removed event: probe_libpthread:pthread_create -- Committer note: Change the variable storing the result of strlen() to 'int', to fix the build on debian:experimental-x-mipsel, fedora:24-x-ARC-uClibc, ubuntu:16.04-x-arm, etc: util/symbol.c: In function 'symbol__match_symbol_name': util/symbol.c:422:11: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare] if (len < versioning - name) ^ Signed-off-by: Paul A. Clarke <pc@us.ibm.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Acked-by: Masami Hiramatsu <mhiramat@kernel.org> Cc: David Ahern <dsahern@gmail.com> Link: http://lkml.kernel.org/r/c2b18d9c-17f8-9285-4868-f58b6359ccac@us.ibm.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Showing 5 changed files with 74 additions and 20 deletions Side-by-side Diff
tools/perf/arch/powerpc/util/sym-handling.c
... | ... | @@ -52,6 +52,18 @@ |
52 | 52 | |
53 | 53 | return strcmp(namea, nameb); |
54 | 54 | } |
55 | + | |
56 | +int arch__compare_symbol_names_n(const char *namea, const char *nameb, | |
57 | + unsigned int n) | |
58 | +{ | |
59 | + /* Skip over initial dot */ | |
60 | + if (*namea == '.') | |
61 | + namea++; | |
62 | + if (*nameb == '.') | |
63 | + nameb++; | |
64 | + | |
65 | + return strncmp(namea, nameb, n); | |
66 | +} | |
55 | 67 | #endif |
56 | 68 | |
57 | 69 | #if defined(_CALL_ELF) && _CALL_ELF == 2 |
tools/perf/util/map.c
... | ... | @@ -325,11 +325,6 @@ |
325 | 325 | return 0; |
326 | 326 | } |
327 | 327 | |
328 | -int __weak arch__compare_symbol_names(const char *namea, const char *nameb) | |
329 | -{ | |
330 | - return strcmp(namea, nameb); | |
331 | -} | |
332 | - | |
333 | 328 | struct symbol *map__find_symbol(struct map *map, u64 addr) |
334 | 329 | { |
335 | 330 | if (map__load(map) < 0) |
tools/perf/util/map.h
... | ... | @@ -130,13 +130,14 @@ |
130 | 130 | */ |
131 | 131 | #define __map__for_each_symbol_by_name(map, sym_name, pos) \ |
132 | 132 | for (pos = map__find_symbol_by_name(map, sym_name); \ |
133 | - pos && arch__compare_symbol_names(pos->name, sym_name) == 0; \ | |
133 | + pos && \ | |
134 | + !symbol__match_symbol_name(pos->name, sym_name, \ | |
135 | + SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); \ | |
134 | 136 | pos = symbol__next_by_name(pos)) |
135 | 137 | |
136 | 138 | #define map__for_each_symbol_by_name(map, sym_name, pos) \ |
137 | 139 | __map__for_each_symbol_by_name(map, sym_name, (pos)) |
138 | 140 | |
139 | -int arch__compare_symbol_names(const char *namea, const char *nameb); | |
140 | 141 | void map__init(struct map *map, enum map_type type, |
141 | 142 | u64 start, u64 end, u64 pgoff, struct dso *dso); |
142 | 143 | struct map *map__new(struct machine *machine, u64 start, u64 len, |
tools/perf/util/symbol.c
... | ... | @@ -90,6 +90,17 @@ |
90 | 90 | return tail - str; |
91 | 91 | } |
92 | 92 | |
93 | +int __weak arch__compare_symbol_names(const char *namea, const char *nameb) | |
94 | +{ | |
95 | + return strcmp(namea, nameb); | |
96 | +} | |
97 | + | |
98 | +int __weak arch__compare_symbol_names_n(const char *namea, const char *nameb, | |
99 | + unsigned int n) | |
100 | +{ | |
101 | + return strncmp(namea, nameb, n); | |
102 | +} | |
103 | + | |
93 | 104 | int __weak arch__choose_best_symbol(struct symbol *syma, |
94 | 105 | struct symbol *symb __maybe_unused) |
95 | 106 | { |
96 | 107 | |
... | ... | @@ -399,8 +410,26 @@ |
399 | 410 | } |
400 | 411 | } |
401 | 412 | |
413 | +int symbol__match_symbol_name(const char *name, const char *str, | |
414 | + enum symbol_tag_include includes) | |
415 | +{ | |
416 | + const char *versioning; | |
417 | + | |
418 | + if (includes == SYMBOL_TAG_INCLUDE__DEFAULT_ONLY && | |
419 | + (versioning = strstr(name, "@@"))) { | |
420 | + int len = strlen(str); | |
421 | + | |
422 | + if (len < versioning - name) | |
423 | + len = versioning - name; | |
424 | + | |
425 | + return arch__compare_symbol_names_n(name, str, len); | |
426 | + } else | |
427 | + return arch__compare_symbol_names(name, str); | |
428 | +} | |
429 | + | |
402 | 430 | static struct symbol *symbols__find_by_name(struct rb_root *symbols, |
403 | - const char *name) | |
431 | + const char *name, | |
432 | + enum symbol_tag_include includes) | |
404 | 433 | { |
405 | 434 | struct rb_node *n; |
406 | 435 | struct symbol_name_rb_node *s = NULL; |
407 | 436 | |
408 | 437 | |
... | ... | @@ -414,11 +443,11 @@ |
414 | 443 | int cmp; |
415 | 444 | |
416 | 445 | s = rb_entry(n, struct symbol_name_rb_node, rb_node); |
417 | - cmp = arch__compare_symbol_names(name, s->sym.name); | |
446 | + cmp = symbol__match_symbol_name(s->sym.name, name, includes); | |
418 | 447 | |
419 | - if (cmp < 0) | |
448 | + if (cmp > 0) | |
420 | 449 | n = n->rb_left; |
421 | - else if (cmp > 0) | |
450 | + else if (cmp < 0) | |
422 | 451 | n = n->rb_right; |
423 | 452 | else |
424 | 453 | break; |
425 | 454 | |
426 | 455 | |
... | ... | @@ -427,16 +456,17 @@ |
427 | 456 | if (n == NULL) |
428 | 457 | return NULL; |
429 | 458 | |
430 | - /* return first symbol that has same name (if any) */ | |
431 | - for (n = rb_prev(n); n; n = rb_prev(n)) { | |
432 | - struct symbol_name_rb_node *tmp; | |
459 | + if (includes != SYMBOL_TAG_INCLUDE__DEFAULT_ONLY) | |
460 | + /* return first symbol that has same name (if any) */ | |
461 | + for (n = rb_prev(n); n; n = rb_prev(n)) { | |
462 | + struct symbol_name_rb_node *tmp; | |
433 | 463 | |
434 | - tmp = rb_entry(n, struct symbol_name_rb_node, rb_node); | |
435 | - if (arch__compare_symbol_names(tmp->sym.name, s->sym.name)) | |
436 | - break; | |
464 | + tmp = rb_entry(n, struct symbol_name_rb_node, rb_node); | |
465 | + if (arch__compare_symbol_names(tmp->sym.name, s->sym.name)) | |
466 | + break; | |
437 | 467 | |
438 | - s = tmp; | |
439 | - } | |
468 | + s = tmp; | |
469 | + } | |
440 | 470 | |
441 | 471 | return &s->sym; |
442 | 472 | } |
... | ... | @@ -503,7 +533,12 @@ |
503 | 533 | struct symbol *dso__find_symbol_by_name(struct dso *dso, enum map_type type, |
504 | 534 | const char *name) |
505 | 535 | { |
506 | - return symbols__find_by_name(&dso->symbol_names[type], name); | |
536 | + struct symbol *s = symbols__find_by_name(&dso->symbol_names[type], name, | |
537 | + SYMBOL_TAG_INCLUDE__NONE); | |
538 | + if (!s) | |
539 | + s = symbols__find_by_name(&dso->symbol_names[type], name, | |
540 | + SYMBOL_TAG_INCLUDE__DEFAULT_ONLY); | |
541 | + return s; | |
507 | 542 | } |
508 | 543 | |
509 | 544 | void dso__sort_by_name(struct dso *dso, enum map_type type) |
tools/perf/util/symbol.h
... | ... | @@ -348,7 +348,18 @@ |
348 | 348 | #define SYMBOL_A 0 |
349 | 349 | #define SYMBOL_B 1 |
350 | 350 | |
351 | +int arch__compare_symbol_names(const char *namea, const char *nameb); | |
352 | +int arch__compare_symbol_names_n(const char *namea, const char *nameb, | |
353 | + unsigned int n); | |
351 | 354 | int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb); |
355 | + | |
356 | +enum symbol_tag_include { | |
357 | + SYMBOL_TAG_INCLUDE__NONE = 0, | |
358 | + SYMBOL_TAG_INCLUDE__DEFAULT_ONLY | |
359 | +}; | |
360 | + | |
361 | +int symbol__match_symbol_name(const char *namea, const char *nameb, | |
362 | + enum symbol_tag_include includes); | |
352 | 363 | |
353 | 364 | /* structure containing an SDT note's info */ |
354 | 365 | struct sdt_note { |