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)
"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,
#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;
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,