diff options
author | Jin Yao <yao.jin@linux.intel.com> | 2017-03-25 16:34:26 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2017-03-27 11:00:38 -0400 |
commit | a64489c56c307bf0955f0489158c5ecf6aa10fe2 (patch) | |
tree | c509e096dd947ec390f3ec30d82f925e2e04763c | |
parent | 5580338d0f207921bc1fef5b668cd564adcc3419 (diff) |
perf report: Find the inline stack for a given address
It would be useful for perf to support a mode to query the inline stack
for a given callgraph address. This would simplify finding the right
code in code that does a lot of inlining.
The srcline.c has contained the code which supports to translate the
address to filename:line_nr. This patch just extends the function to let
it support getting the inline stacks.
It introduces the inline_list which will store the inline function
result (filename:line_nr and funcname).
If BFD lib is not supported, the result is only filename:line_nr.
Signed-off-by: Yao Jin <yao.jin@linux.intel.com>
Tested-by: Milian Wolff <milian.wolff@kdab.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@intel.com>
Link: http://lkml.kernel.org/r/1490474069-15823-3-git-send-email-yao.jin@linux.intel.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/util/srcline.c | 167 | ||||
-rw-r--r-- | tools/perf/util/symbol-elf.c | 5 | ||||
-rw-r--r-- | tools/perf/util/symbol-minimal.c | 7 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 2 | ||||
-rw-r--r-- | tools/perf/util/util.h | 16 |
5 files changed, 192 insertions, 5 deletions
diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 2953c9fecb30..3ce28f702b36 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c | |||
@@ -7,6 +7,7 @@ | |||
7 | #include "util/dso.h" | 7 | #include "util/dso.h" |
8 | #include "util/util.h" | 8 | #include "util/util.h" |
9 | #include "util/debug.h" | 9 | #include "util/debug.h" |
10 | #include "util/callchain.h" | ||
10 | 11 | ||
11 | #include "symbol.h" | 12 | #include "symbol.h" |
12 | 13 | ||
@@ -30,6 +31,34 @@ static const char *dso__name(struct dso *dso) | |||
30 | return dso_name; | 31 | return dso_name; |
31 | } | 32 | } |
32 | 33 | ||
34 | static int inline_list__append(char *filename, char *funcname, int line_nr, | ||
35 | struct inline_node *node, struct dso *dso) | ||
36 | { | ||
37 | struct inline_list *ilist; | ||
38 | char *demangled; | ||
39 | |||
40 | ilist = zalloc(sizeof(*ilist)); | ||
41 | if (ilist == NULL) | ||
42 | return -1; | ||
43 | |||
44 | ilist->filename = filename; | ||
45 | ilist->line_nr = line_nr; | ||
46 | |||
47 | if (dso != NULL) { | ||
48 | demangled = dso__demangle_sym(dso, 0, funcname); | ||
49 | if (demangled == NULL) { | ||
50 | ilist->funcname = funcname; | ||
51 | } else { | ||
52 | ilist->funcname = demangled; | ||
53 | free(funcname); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | list_add_tail(&ilist->list, &node->val); | ||
58 | |||
59 | return 0; | ||
60 | } | ||
61 | |||
33 | #ifdef HAVE_LIBBFD_SUPPORT | 62 | #ifdef HAVE_LIBBFD_SUPPORT |
34 | 63 | ||
35 | /* | 64 | /* |
@@ -169,9 +198,17 @@ static void addr2line_cleanup(struct a2l_data *a2l) | |||
169 | 198 | ||
170 | #define MAX_INLINE_NEST 1024 | 199 | #define MAX_INLINE_NEST 1024 |
171 | 200 | ||
201 | static void inline_list__reverse(struct inline_node *node) | ||
202 | { | ||
203 | struct inline_list *ilist, *n; | ||
204 | |||
205 | list_for_each_entry_safe_reverse(ilist, n, &node->val, list) | ||
206 | list_move_tail(&ilist->list, &node->val); | ||
207 | } | ||
208 | |||
172 | static int addr2line(const char *dso_name, u64 addr, | 209 | static int addr2line(const char *dso_name, u64 addr, |
173 | char **file, unsigned int *line, struct dso *dso, | 210 | char **file, unsigned int *line, struct dso *dso, |
174 | bool unwind_inlines) | 211 | bool unwind_inlines, struct inline_node *node) |
175 | { | 212 | { |
176 | int ret = 0; | 213 | int ret = 0; |
177 | struct a2l_data *a2l = dso->a2l; | 214 | struct a2l_data *a2l = dso->a2l; |
@@ -196,8 +233,21 @@ static int addr2line(const char *dso_name, u64 addr, | |||
196 | 233 | ||
197 | while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, | 234 | while (bfd_find_inliner_info(a2l->abfd, &a2l->filename, |
198 | &a2l->funcname, &a2l->line) && | 235 | &a2l->funcname, &a2l->line) && |
199 | cnt++ < MAX_INLINE_NEST) | 236 | cnt++ < MAX_INLINE_NEST) { |
200 | ; | 237 | |
238 | if (node != NULL) { | ||
239 | if (inline_list__append(strdup(a2l->filename), | ||
240 | strdup(a2l->funcname), | ||
241 | a2l->line, node, | ||
242 | dso) != 0) | ||
243 | return 0; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | if ((node != NULL) && | ||
248 | (callchain_param.order != ORDER_CALLEE)) { | ||
249 | inline_list__reverse(node); | ||
250 | } | ||
201 | } | 251 | } |
202 | 252 | ||
203 | if (a2l->found && a2l->filename) { | 253 | if (a2l->found && a2l->filename) { |
@@ -223,6 +273,35 @@ void dso__free_a2l(struct dso *dso) | |||
223 | dso->a2l = NULL; | 273 | dso->a2l = NULL; |
224 | } | 274 | } |
225 | 275 | ||
276 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, | ||
277 | struct dso *dso) | ||
278 | { | ||
279 | char *file = NULL; | ||
280 | unsigned int line = 0; | ||
281 | struct inline_node *node; | ||
282 | |||
283 | node = zalloc(sizeof(*node)); | ||
284 | if (node == NULL) { | ||
285 | perror("not enough memory for the inline node"); | ||
286 | return NULL; | ||
287 | } | ||
288 | |||
289 | INIT_LIST_HEAD(&node->val); | ||
290 | node->addr = addr; | ||
291 | |||
292 | if (!addr2line(dso_name, addr, &file, &line, dso, TRUE, node)) | ||
293 | goto out_free_inline_node; | ||
294 | |||
295 | if (list_empty(&node->val)) | ||
296 | goto out_free_inline_node; | ||
297 | |||
298 | return node; | ||
299 | |||
300 | out_free_inline_node: | ||
301 | inline_node__delete(node); | ||
302 | return NULL; | ||
303 | } | ||
304 | |||
226 | #else /* HAVE_LIBBFD_SUPPORT */ | 305 | #else /* HAVE_LIBBFD_SUPPORT */ |
227 | 306 | ||
228 | static int filename_split(char *filename, unsigned int *line_nr) | 307 | static int filename_split(char *filename, unsigned int *line_nr) |
@@ -249,7 +328,8 @@ static int filename_split(char *filename, unsigned int *line_nr) | |||
249 | static int addr2line(const char *dso_name, u64 addr, | 328 | static int addr2line(const char *dso_name, u64 addr, |
250 | char **file, unsigned int *line_nr, | 329 | char **file, unsigned int *line_nr, |
251 | struct dso *dso __maybe_unused, | 330 | struct dso *dso __maybe_unused, |
252 | bool unwind_inlines __maybe_unused) | 331 | bool unwind_inlines __maybe_unused, |
332 | struct inline_node *node __maybe_unused) | ||
253 | { | 333 | { |
254 | FILE *fp; | 334 | FILE *fp; |
255 | char cmd[PATH_MAX]; | 335 | char cmd[PATH_MAX]; |
@@ -288,6 +368,58 @@ void dso__free_a2l(struct dso *dso __maybe_unused) | |||
288 | { | 368 | { |
289 | } | 369 | } |
290 | 370 | ||
371 | static struct inline_node *addr2inlines(const char *dso_name, u64 addr, | ||
372 | struct dso *dso __maybe_unused) | ||
373 | { | ||
374 | FILE *fp; | ||
375 | char cmd[PATH_MAX]; | ||
376 | struct inline_node *node; | ||
377 | char *filename = NULL; | ||
378 | size_t len; | ||
379 | unsigned int line_nr = 0; | ||
380 | |||
381 | scnprintf(cmd, sizeof(cmd), "addr2line -e %s -i %016"PRIx64, | ||
382 | dso_name, addr); | ||
383 | |||
384 | fp = popen(cmd, "r"); | ||
385 | if (fp == NULL) { | ||
386 | pr_err("popen failed for %s\n", dso_name); | ||
387 | return NULL; | ||
388 | } | ||
389 | |||
390 | node = zalloc(sizeof(*node)); | ||
391 | if (node == NULL) { | ||
392 | perror("not enough memory for the inline node"); | ||
393 | goto out; | ||
394 | } | ||
395 | |||
396 | INIT_LIST_HEAD(&node->val); | ||
397 | node->addr = addr; | ||
398 | |||
399 | while (getline(&filename, &len, fp) != -1) { | ||
400 | if (filename_split(filename, &line_nr) != 1) { | ||
401 | free(filename); | ||
402 | goto out; | ||
403 | } | ||
404 | |||
405 | if (inline_list__append(filename, NULL, line_nr, node, | ||
406 | NULL) != 0) | ||
407 | goto out; | ||
408 | |||
409 | filename = NULL; | ||
410 | } | ||
411 | |||
412 | out: | ||
413 | pclose(fp); | ||
414 | |||
415 | if (list_empty(&node->val)) { | ||
416 | inline_node__delete(node); | ||
417 | return NULL; | ||
418 | } | ||
419 | |||
420 | return node; | ||
421 | } | ||
422 | |||
291 | #endif /* HAVE_LIBBFD_SUPPORT */ | 423 | #endif /* HAVE_LIBBFD_SUPPORT */ |
292 | 424 | ||
293 | /* | 425 | /* |
@@ -311,7 +443,7 @@ char *__get_srcline(struct dso *dso, u64 addr, struct symbol *sym, | |||
311 | if (dso_name == NULL) | 443 | if (dso_name == NULL) |
312 | goto out; | 444 | goto out; |
313 | 445 | ||
314 | if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines)) | 446 | if (!addr2line(dso_name, addr, &file, &line, dso, unwind_inlines, NULL)) |
315 | goto out; | 447 | goto out; |
316 | 448 | ||
317 | if (asprintf(&srcline, "%s:%u", | 449 | if (asprintf(&srcline, "%s:%u", |
@@ -351,3 +483,28 @@ char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym, | |||
351 | { | 483 | { |
352 | return __get_srcline(dso, addr, sym, show_sym, false); | 484 | return __get_srcline(dso, addr, sym, show_sym, false); |
353 | } | 485 | } |
486 | |||
487 | struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr) | ||
488 | { | ||
489 | const char *dso_name; | ||
490 | |||
491 | dso_name = dso__name(dso); | ||
492 | if (dso_name == NULL) | ||
493 | return NULL; | ||
494 | |||
495 | return addr2inlines(dso_name, addr, dso); | ||
496 | } | ||
497 | |||
498 | void inline_node__delete(struct inline_node *node) | ||
499 | { | ||
500 | struct inline_list *ilist, *tmp; | ||
501 | |||
502 | list_for_each_entry_safe(ilist, tmp, &node->val, list) { | ||
503 | list_del_init(&ilist->list); | ||
504 | zfree(&ilist->filename); | ||
505 | zfree(&ilist->funcname); | ||
506 | free(ilist); | ||
507 | } | ||
508 | |||
509 | free(node); | ||
510 | } | ||
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index 0e660dba58ad..d1a40bb642ff 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c | |||
@@ -390,6 +390,11 @@ out_elf_end: | |||
390 | return 0; | 390 | return 0; |
391 | } | 391 | } |
392 | 392 | ||
393 | char *dso__demangle_sym(struct dso *dso, int kmodule, char *elf_name) | ||
394 | { | ||
395 | return demangle_sym(dso, kmodule, elf_name); | ||
396 | } | ||
397 | |||
393 | /* | 398 | /* |
394 | * Align offset to 4 bytes as needed for note name and descriptor data. | 399 | * Align offset to 4 bytes as needed for note name and descriptor data. |
395 | */ | 400 | */ |
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 11cdde980545..870ef0f0659c 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c | |||
@@ -373,3 +373,10 @@ int kcore_copy(const char *from_dir __maybe_unused, | |||
373 | void symbol__elf_init(void) | 373 | void symbol__elf_init(void) |
374 | { | 374 | { |
375 | } | 375 | } |
376 | |||
377 | char *dso__demangle_sym(struct dso *dso __maybe_unused, | ||
378 | int kmodule __maybe_unused, | ||
379 | char *elf_name __maybe_unused) | ||
380 | { | ||
381 | return NULL; | ||
382 | } | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 9222c7e702f3..e36213ccfcf7 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -305,6 +305,8 @@ int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, | |||
305 | int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, | 305 | int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, |
306 | struct map *map); | 306 | struct map *map); |
307 | 307 | ||
308 | char *dso__demangle_sym(struct dso *dso, int kmodule, char *elf_name); | ||
309 | |||
308 | void __symbols__insert(struct rb_root *symbols, struct symbol *sym, bool kernel); | 310 | void __symbols__insert(struct rb_root *symbols, struct symbol *sym, bool kernel); |
309 | void symbols__insert(struct rb_root *symbols, struct symbol *sym); | 311 | void symbols__insert(struct rb_root *symbols, struct symbol *sym); |
310 | void symbols__fixup_duplicate(struct rb_root *symbols); | 312 | void symbols__fixup_duplicate(struct rb_root *symbols); |
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index b2cfa47990dc..cc0700d6fef0 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h | |||
@@ -364,4 +364,20 @@ int is_printable_array(char *p, unsigned int len); | |||
364 | int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); | 364 | int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz); |
365 | 365 | ||
366 | int unit_number__scnprintf(char *buf, size_t size, u64 n); | 366 | int unit_number__scnprintf(char *buf, size_t size, u64 n); |
367 | |||
368 | struct inline_list { | ||
369 | char *filename; | ||
370 | char *funcname; | ||
371 | unsigned int line_nr; | ||
372 | struct list_head list; | ||
373 | }; | ||
374 | |||
375 | struct inline_node { | ||
376 | u64 addr; | ||
377 | struct list_head val; | ||
378 | }; | ||
379 | |||
380 | struct inline_node *dso__parse_addr_inlines(struct dso *dso, u64 addr); | ||
381 | void inline_node__delete(struct inline_node *node); | ||
382 | |||
367 | #endif /* GIT_COMPAT_UTIL_H */ | 383 | #endif /* GIT_COMPAT_UTIL_H */ |