]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
perf srcline: Add configuration support for the addr2line style
authorIan Rogers <irogers@google.com>
Sun, 11 Jan 2026 04:13:35 +0000 (20:13 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 13 Jan 2026 19:09:14 +0000 (16:09 -0300)
Allow the addr2line style to be specified on the `perf report` command
line or in the .perfconfig file.

Committer testing:

The methods:

  # perf probe -x ~/bin/perf -F *__addr2line
  cmd__addr2line
  libbfd__addr2line
  libdw__addr2line
  llvm__addr2line
  #

So if we configure one of them, say 'addr2line':

  # perf config addr2line.style=addr2line
  # perf config addr2line.style
  addr2line.style=addr2line
  #

And have probes on all of them:

  # perf probe -x ~/bin/perf *__addr2line
  Added new events:
    probe_perf:cmd__addr2line (on *__addr2line in /home/acme/bin/perf)
    probe_perf:llvm__addr2line (on *__addr2line in /home/acme/bin/perf)
    probe_perf:libbfd__addr2line (on *__addr2line in /home/acme/bin/perf)
    probe_perf:libdw__addr2line (on *__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

  #

Only the selected method should be used:

  # perf stat -e probe_perf:*_addr2line perf report -f --dso perf --stdio -s srcfile,srcline
  # 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 hierarchical output, try: perf report --hierarchy)
  #

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

                  44      probe_perf:cmd__addr2line
                   0      probe_perf:llvm__addr2line
                   0      probe_perf:libbfd__addr2line
                   0      probe_perf:libdw__addr2line

         0.035915611 seconds time elapsed

         0.028008000 seconds user
         0.009051000 seconds sys
  #

I checked and that is the case for the other methods.

Also when using:

  # perf config addr2line.style=libdw,llvm

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

                 0      probe_perf:cmd__addr2line
                23      probe_perf:llvm__addr2line
                 0      probe_perf:libbfd__addr2line
                44      probe_perf:libdw__addr2line

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>
tools/perf/builtin-report.c
tools/perf/util/config.c
tools/perf/util/srcline.c
tools/perf/util/srcline.h
tools/perf/util/symbol_conf.h

index 6c2b4f93ec78e5793161c04026296f449aa941ae..2e936928e8c052a1e8085c212066049c60aa5d71 100644 (file)
@@ -1271,6 +1271,13 @@ parse_percent_limit(const struct option *opt, const char *str,
        return 0;
 }
 
+static int
+report_parse_addr2line_config(const struct option *opt __maybe_unused,
+                             const char *arg, int unset __maybe_unused)
+{
+       return addr2line_configure("addr2line.style", arg, NULL);
+}
+
 static int process_attr(const struct perf_tool *tool __maybe_unused,
                        union perf_event *event,
                        struct evlist **pevlist)
@@ -1447,6 +1454,9 @@ int cmd_report(int argc, const char **argv)
                   "objdump binary to use for disassembly and annotations"),
        OPT_STRING(0, "addr2line", &addr2line_path, "path",
                   "addr2line binary to use for line numbers"),
+       OPT_CALLBACK(0, "addr2line-style", NULL, "addr2line style",
+                    "addr2line styles (libdw,llvm,libbfd,addr2line)",
+                    report_parse_addr2line_config),
        OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
                    "Symbol demangling. Enabled by default, use --no-demangle to disable."),
        OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
index e0219bc6330ae8554b2b95e7fc9e58ad61cad2f3..0452fbc6c0857062e783a6c6eb8968d487b2cb07 100644 (file)
@@ -20,6 +20,7 @@
 #include "util/stat.h"  /* perf_stat__set_big_num */
 #include "util/evsel.h"  /* evsel__hw_names, evsel__use_bpf_counters */
 #include "util/addr2line.h"  /* addr2line_timeout_ms */
+#include "srcline.h"
 #include "build-id.h"
 #include "debug.h"
 #include "config.h"
@@ -519,6 +520,9 @@ int perf_default_config(const char *var, const char *value,
        if (strstarts(var, "stat."))
                return perf_stat_config(var, value);
 
+       if (strstarts(var, "addr2line."))
+               return addr2line_configure(var, value, dummy);
+
        /* Add other config variables here. */
        return 0;
 }
index e2d280678b024a036e7de7edeb8a58b2eb23a309..28fa1abd1fd31b024a54d66f751f7bd778f5d283 100644 (file)
@@ -7,9 +7,11 @@
 #include "llvm.h"
 #include "symbol.h"
 #include "libdw.h"
+#include "debug.h"
 
 #include <inttypes.h>
 #include <string.h>
+#include <linux/string.h>
 
 bool srcline_full_filename;
 
@@ -138,21 +140,95 @@ static int addr2line(const char *dso_name, u64 addr, char **file, unsigned int *
                     struct dso *dso, bool unwind_inlines, struct inline_node *node,
                     struct symbol *sym)
 {
-       int ret;
+       int ret = 0;
+
+       if (symbol_conf.addr2line_style[0] == A2L_STYLE_UNKNOWN) {
+               int i = 0;
+
+               /* Default addr2line fallback order. */
+#ifdef HAVE_LIBDW_SUPPORT
+               symbol_conf.addr2line_style[i++] = A2L_STYLE_LIBDW;
+#endif
+#ifdef HAVE_LIBLLVM_SUPPORT
+               symbol_conf.addr2line_style[i++] = A2L_STYLE_LLVM;
+#endif
+#ifdef HAVE_LIBBFD_SUPPORT
+               symbol_conf.addr2line_style[i++] = A2L_STYLE_LIBBFD;
+#endif
+               symbol_conf.addr2line_style[i++] = A2L_STYLE_CMD;
+       }
+
+       for (size_t i = 0; i < ARRAY_SIZE(symbol_conf.addr2line_style); i++) {
+               switch (symbol_conf.addr2line_style[i]) {
+               case A2L_STYLE_LIBDW:
+                       ret = libdw__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines,
+                                              node, sym);
+                       break;
+               case A2L_STYLE_LLVM:
+                       ret = llvm__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines,
+                                             node, sym);
+                       break;
+               case A2L_STYLE_LIBBFD:
+                       ret = libbfd__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines,
+                                               node, sym);
+                       break;
+               case A2L_STYLE_CMD:
+                       ret = cmd__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines,
+                                            node, sym);
+                       break;
+               case A2L_STYLE_UNKNOWN:
+               default:
+                       break;
+               }
+               if (ret > 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+int addr2line_configure(const char *var, const char *value, void *cb __maybe_unused)
+{
+       static const char * const a2l_style_names[] = {
+               [A2L_STYLE_LIBDW] = "libdw",
+               [A2L_STYLE_LLVM] = "llvm",
+               [A2L_STYLE_LIBBFD] = "libbfd",
+               [A2L_STYLE_CMD] = "addr2line",
+               NULL
+       };
+
+       char *s, *p, *saveptr;
+       size_t i = 0;
 
-       ret = libdw__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym);
-       if (ret > 0)
-               return ret;
+       if (strcmp(var, "addr2line.style"))
+               return 0;
+
+       if (!value)
+               return -1;
 
-       ret = llvm__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym);
-       if (ret > 0)
-               return ret;
+       s = strdup(value);
+       if (!s)
+               return -1;
 
-       ret = libbfd__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym);
-       if (ret > 0)
-               return ret;
+       p = strtok_r(s, ",", &saveptr);
+       while (p && i < ARRAY_SIZE(symbol_conf.addr2line_style)) {
+               bool found = false;
+               char *q = strim(p);
+
+               for (size_t j = A2L_STYLE_LIBDW; j < MAX_A2L_STYLE; j++) {
+                       if (!strcasecmp(q, a2l_style_names[j])) {
+                               symbol_conf.addr2line_style[i++] = j;
+                               found = true;
+                               break;
+                       }
+               }
+               if (!found)
+                       pr_warning("Unknown addr2line style: %s\n", q);
+               p = strtok_r(NULL, ",", &saveptr);
+       }
 
-       return cmd__addr2line(dso_name, addr, file, line_nr, dso, unwind_inlines, node, sym);
+       free(s);
+       return 0;
 }
 
 static struct inline_node *addr2inlines(const char *dso_name, u64 addr,
index be9f002bf234883d52ded23f72b988e02139c63f..7c37b3bf9ce79379682d6d087f862c791cbad9af 100644 (file)
@@ -63,4 +63,6 @@ struct symbol *new_inline_sym(struct dso *dso,
                              struct symbol *base_sym,
                              const char *funcname);
 
+int addr2line_configure(const char *var, const char *value, void *cb);
+
 #endif /* PERF_SRCLINE_H */
index 7a80d2c14d9b708556d1c1d8b6085577fa1e094e..71bb17372a6cfea0d650a55a939dd184a17894f6 100644 (file)
@@ -9,6 +9,15 @@
 struct strlist;
 struct intlist;
 
+enum a2l_style {
+       A2L_STYLE_UNKNOWN = 0,
+       A2L_STYLE_LIBDW,
+       A2L_STYLE_LLVM,
+       A2L_STYLE_LIBBFD,
+       A2L_STYLE_CMD,
+};
+#define MAX_A2L_STYLE (A2L_STYLE_CMD + 1)
+
 struct symbol_conf {
        bool            nanosecs;
        unsigned short  priv_size;
@@ -70,6 +79,7 @@ struct symbol_conf {
                        *col_width_list_str,
                        *bt_stop_list_str;
        const char              *addr2line_path;
+       enum a2l_style  addr2line_style[MAX_A2L_STYLE];
        unsigned long   time_quantum;
        struct strlist  *dso_list,
                        *comm_list,