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

Configure Feed

Select the types of activity you want to include in your feed.

perf addr2line: Add a libdw implementation

Add an implementation of addr2line that uses libdw.

Other addr2line implementations are slow, particularly in the case of
forking addr2line.

Add an implementation that caches the libdw information in the dso and
uses it to find the file and line number information.

Inline information is supported but because cu_walk_functions_at visits
the leaf function last add a inline_list__append_tail to reverse the
lists order.

Committer testing:

# perf probe -x ~/bin/perf libdw__addr2line
Added new event:
probe_perf:libdw_addr2line (on libdw__addr2line in /home/acme/bin/perf)

You can now use it in all perf tools, such as:

perf record -e probe_perf:libdw_addr2line -aR sleep 1

#
# perf stat -e probe_perf:libdw_addr2line perf report -f --dso perf --stdio -s srcfile,srcline
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 4K of event 'cpu/cycles/Pu'
# Event count (approx.): 5535180842
#
# Overhead Source File Source:Line
# ........ ............ ...............
#
99.04% inlineloop.c inlineloop.c:21
0.46% inlineloop.c inlineloop.c:20

#
# (Tip: For tracepoint events, try: perf report -s trace_fields)
#

Performance counter stats for 'perf report -f --dso perf --stdio -s srcfile,srcline':

44 probe_perf:libdw_addr2line

0.037260744 seconds time elapsed

0.025299000 seconds user
0.011918000 seconds sys
#

Adding probes to the other addr2line implementations (llvm__addr2line,
libbfd__addr2line and cmd__addr2line) I noticed some fallbacks to the
llvm one:

Performance counter stats for 'perf report -f --dso perf --stdio -s srcfile,srcline':

44 probe_perf:libdw_addr2line
23 probe_perf:llvm_addr2line
0 probe_perf:libbfd_addr2line
0 probe_perf:cmd_addr2line

Something to investigate further, but at least we don't fallback to the
cmd based one :-)

Reviewed-by: James Clark <james.clark@linaro.org>
Signed-off-by: Ian Rogers <irogers@google.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephen Brennan <stephen.s.brennan@oracle.com>
Cc: Tony Jones <tonyj@suse.de>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ian Rogers and committed by
Arnaldo Carvalho de Melo
88c51002 27fc6f56

+252
+1
tools/perf/util/Build
··· 224 224 perf-util-$(CONFIG_LIBDW) += dwarf-regs-x86.o 225 225 perf-util-$(CONFIG_LIBDW) += debuginfo.o 226 226 perf-util-$(CONFIG_LIBDW) += annotate-data.o 227 + perf-util-$(CONFIG_LIBDW) += libdw.o 227 228 228 229 perf-util-$(CONFIG_LIBDW_DWARF_UNWIND) += unwind-libdw.o 229 230 perf-util-$(CONFIG_LOCAL_LIBUNWIND) += unwind-libunwind-local.o
+2
tools/perf/util/dso.c
··· 32 32 #include "string2.h" 33 33 #include "vdso.h" 34 34 #include "annotate-data.h" 35 + #include "libdw.h" 35 36 36 37 static const char * const debuglink_paths[] = { 37 38 "%.0s%s", ··· 1606 1605 auxtrace_cache__free(RC_CHK_ACCESS(dso)->auxtrace_cache); 1607 1606 dso_cache__free(dso); 1608 1607 dso__free_a2l(dso); 1608 + dso__free_a2l_libdw(dso); 1609 1609 dso__free_symsrc_filename(dso); 1610 1610 nsinfo__zput(RC_CHK_ACCESS(dso)->nsinfo); 1611 1611 mutex_destroy(dso__lock(dso));
+11
tools/perf/util/dso.h
··· 268 268 const char *short_name; 269 269 const char *long_name; 270 270 void *a2l; 271 + void *a2l_libdw; 271 272 char *symsrc_filename; 272 273 #if defined(__powerpc__) 273 274 void *dwfl; /* DWARF debug info */ ··· 333 332 static inline void dso__set_a2l(struct dso *dso, void *val) 334 333 { 335 334 RC_CHK_ACCESS(dso)->a2l = val; 335 + } 336 + 337 + static inline void *dso__a2l_libdw(const struct dso *dso) 338 + { 339 + return RC_CHK_ACCESS(dso)->a2l_libdw; 340 + } 341 + 342 + static inline void dso__set_a2l_libdw(struct dso *dso, void *val) 343 + { 344 + RC_CHK_ACCESS(dso)->a2l_libdw = val; 336 345 } 337 346 338 347 static inline unsigned int dso__a2l_fails(const struct dso *dso)
+153
tools/perf/util/libdw.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include "dso.h" 3 + #include "libdw.h" 4 + #include "srcline.h" 5 + #include "symbol.h" 6 + #include "dwarf-aux.h" 7 + #include <fcntl.h> 8 + #include <unistd.h> 9 + #include <elfutils/libdwfl.h> 10 + 11 + void dso__free_a2l_libdw(struct dso *dso) 12 + { 13 + Dwfl *dwfl = dso__a2l_libdw(dso); 14 + 15 + if (dwfl) { 16 + dwfl_end(dwfl); 17 + dso__set_a2l_libdw(dso, NULL); 18 + } 19 + } 20 + 21 + struct libdw_a2l_cb_args { 22 + struct dso *dso; 23 + struct symbol *sym; 24 + struct inline_node *node; 25 + char *leaf_srcline; 26 + bool leaf_srcline_used; 27 + }; 28 + 29 + static int libdw_a2l_cb(Dwarf_Die *die, void *_args) 30 + { 31 + struct libdw_a2l_cb_args *args = _args; 32 + struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, dwarf_diename(die)); 33 + const char *call_fname = die_get_call_file(die); 34 + char *call_srcline = srcline__unknown; 35 + struct inline_list *ilist; 36 + 37 + if (!inline_sym) 38 + return -ENOMEM; 39 + 40 + /* Assign caller information to the parent. */ 41 + if (call_fname) 42 + call_srcline = srcline_from_fileline(call_fname, die_get_call_lineno(die)); 43 + 44 + list_for_each_entry(ilist, &args->node->val, list) { 45 + ilist->srcline = call_srcline; 46 + call_srcline = NULL; 47 + break; 48 + } 49 + if (call_srcline && call_fname) 50 + free(call_srcline); 51 + 52 + /* Add this symbol to the chain as the leaf. */ 53 + inline_list__append_tail(inline_sym, args->leaf_srcline, args->node); 54 + args->leaf_srcline_used = true; 55 + return 0; 56 + } 57 + 58 + int libdw__addr2line(const char *dso_name, u64 addr, 59 + char **file, unsigned int *line_nr, 60 + struct dso *dso, bool unwind_inlines, 61 + struct inline_node *node, struct symbol *sym) 62 + { 63 + static const Dwfl_Callbacks offline_callbacks = { 64 + .find_debuginfo = dwfl_standard_find_debuginfo, 65 + .section_address = dwfl_offline_section_address, 66 + .find_elf = dwfl_build_id_find_elf, 67 + }; 68 + Dwfl *dwfl = dso__a2l_libdw(dso); 69 + Dwfl_Module *mod; 70 + Dwfl_Line *dwline; 71 + Dwarf_Addr bias; 72 + const char *src; 73 + int lineno = 0; 74 + 75 + if (!dwfl) { 76 + /* 77 + * Initialize Dwfl session. 78 + * We need to open the DSO file to report it to libdw. 79 + */ 80 + int fd; 81 + 82 + fd = open(dso_name, O_RDONLY); 83 + if (fd < 0) 84 + return 0; 85 + 86 + dwfl = dwfl_begin(&offline_callbacks); 87 + if (!dwfl) { 88 + close(fd); 89 + return 0; 90 + } 91 + 92 + /* 93 + * If the report is successful, the file descriptor fd is consumed 94 + * and closed by the Dwfl. If not, it is not closed. 95 + */ 96 + mod = dwfl_report_offline(dwfl, dso_name, dso_name, fd); 97 + if (!mod) { 98 + dwfl_end(dwfl); 99 + close(fd); 100 + return 0; 101 + } 102 + 103 + dwfl_report_end(dwfl, /*removed=*/NULL, /*arg=*/NULL); 104 + dso__set_a2l_libdw(dso, dwfl); 105 + } else { 106 + /* Dwfl session already initialized, get module for address. */ 107 + mod = dwfl_addrmodule(dwfl, addr); 108 + } 109 + 110 + if (!mod) 111 + return 0; 112 + 113 + /* 114 + * Get/ignore the dwarf information. Determine the bias, difference 115 + * between the regular ELF addr2line addresses and those to use with 116 + * libdw. 117 + */ 118 + if (!dwfl_module_getdwarf(mod, &bias)) 119 + return 0; 120 + 121 + /* Find source line information for the address. */ 122 + dwline = dwfl_module_getsrc(mod, addr + bias); 123 + if (!dwline) 124 + return 0; 125 + 126 + /* Get line information. */ 127 + src = dwfl_lineinfo(dwline, /*addr=*/NULL, &lineno, /*col=*/NULL, /*mtime=*/NULL, 128 + /*length=*/NULL); 129 + 130 + if (file) 131 + *file = src ? strdup(src) : NULL; 132 + if (line_nr) 133 + *line_nr = lineno; 134 + 135 + /* Optionally unwind inline function call chain. */ 136 + if (unwind_inlines && node) { 137 + Dwarf_Addr unused_bias; 138 + Dwarf_Die *cudie = dwfl_module_addrdie(mod, addr + bias, &unused_bias); 139 + struct libdw_a2l_cb_args args = { 140 + .dso = dso, 141 + .sym = sym, 142 + .node = node, 143 + .leaf_srcline = srcline_from_fileline(src ?: "<unknown>", lineno), 144 + }; 145 + 146 + /* Walk from the parent down to the leaf. */ 147 + cu_walk_functions_at(cudie, addr, libdw_a2l_cb, &args); 148 + 149 + if (!args.leaf_srcline_used) 150 + free(args.leaf_srcline); 151 + } 152 + return 1; 153 + }
+60
tools/perf/util/libdw.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + #ifndef PERF_LIBDW_H 3 + #define PERF_LIBDW_H 4 + 5 + #include <linux/types.h> 6 + 7 + struct dso; 8 + struct inline_node; 9 + struct symbol; 10 + 11 + #ifdef HAVE_LIBDW_SUPPORT 12 + /* 13 + * libdw__addr2line - Convert address to source location using libdw 14 + * @dso_name: Name of the DSO 15 + * @addr: Address to resolve 16 + * @file: Pointer to return filename (caller must free) 17 + * @line_nr: Pointer to return line number 18 + * @dso: The dso struct 19 + * @unwind_inlines: Whether to unwind inline function calls 20 + * @node: Inline node list to append to 21 + * @sym: The symbol associated with the address 22 + * 23 + * This function initializes a Dwfl context for the DSO if not already present, 24 + * finds the source line information for the given address, and optionally 25 + * resolves inline function call chains. 26 + * 27 + * Returns 1 on success (found), 0 on failure (not found). 28 + */ 29 + int libdw__addr2line(const char *dso_name, u64 addr, char **file, 30 + unsigned int *line_nr, struct dso *dso, 31 + bool unwind_inlines, struct inline_node *node, 32 + struct symbol *sym); 33 + 34 + /* 35 + * dso__free_a2l_libdw - Free libdw resources associated with the DSO 36 + * @dso: The dso to free resources for 37 + * 38 + * This function cleans up the Dwfl context used for addr2line lookups. 39 + */ 40 + void dso__free_a2l_libdw(struct dso *dso); 41 + 42 + #else /* HAVE_LIBDW_SUPPORT */ 43 + 44 + static inline int libdw__addr2line(const char *dso_name __maybe_unused, 45 + u64 addr __maybe_unused, char **file __maybe_unused, 46 + unsigned int *line_nr __maybe_unused, 47 + struct dso *dso __maybe_unused, 48 + bool unwind_inlines __maybe_unused, 49 + struct inline_node *node __maybe_unused, 50 + struct symbol *sym __maybe_unused) 51 + { 52 + return 0; 53 + } 54 + 55 + static inline void dso__free_a2l_libdw(struct dso *dso __maybe_unused) 56 + { 57 + } 58 + #endif /* HAVE_LIBDW_SUPPORT */ 59 + 60 + #endif /* PERF_LIBDW_H */
+24
tools/perf/util/srcline.c
··· 6 6 #include "libbfd.h" 7 7 #include "llvm.h" 8 8 #include "symbol.h" 9 + #include "libdw.h" 9 10 10 11 #include <inttypes.h> 11 12 #include <string.h> ··· 48 47 list_add_tail(&ilist->list, &node->val); 49 48 else 50 49 list_add(&ilist->list, &node->val); 50 + 51 + return 0; 52 + } 53 + 54 + int inline_list__append_tail(struct symbol *symbol, char *srcline, struct inline_node *node) 55 + { 56 + struct inline_list *ilist; 57 + 58 + ilist = zalloc(sizeof(*ilist)); 59 + if (ilist == NULL) 60 + return -1; 61 + 62 + ilist->symbol = symbol; 63 + ilist->srcline = srcline; 64 + 65 + if (callchain_param.order == ORDER_CALLEE) 66 + list_add(&ilist->list, &node->val); 67 + else 68 + list_add_tail(&ilist->list, &node->val); 51 69 52 70 return 0; 53 71 } ··· 139 119 struct symbol *sym) 140 120 { 141 121 int ret; 122 + 123 + ret = libdw__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym); 124 + if (ret > 0) 125 + return ret; 142 126 143 127 ret = llvm__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym); 144 128 if (ret > 0)
+1
tools/perf/util/srcline.h
··· 57 57 void inlines__tree_delete(struct rb_root_cached *tree); 58 58 59 59 int inline_list__append(struct symbol *symbol, char *srcline, struct inline_node *node); 60 + int inline_list__append_tail(struct symbol *symbol, char *srcline, struct inline_node *node); 60 61 char *srcline_from_fileline(const char *file, unsigned int line); 61 62 struct symbol *new_inline_sym(struct dso *dso, 62 63 struct symbol *base_sym,