summaryrefslogtreecommitdiff
path: root/tools/perf/util/libdw.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/libdw.c')
-rw-r--r--tools/perf/util/libdw.c34
1 files changed, 30 insertions, 4 deletions
diff --git a/tools/perf/util/libdw.c b/tools/perf/util/libdw.c
index d5d2958902c0..4ca7e7e4fbe9 100644
--- a/tools/perf/util/libdw.c
+++ b/tools/perf/util/libdw.c
@@ -82,13 +82,39 @@ struct libdw_a2l_cb_args {
static int libdw_a2l_cb(Dwarf_Die *die, void *_args)
{
struct libdw_a2l_cb_args *args = _args;
- struct symbol *inline_sym = new_inline_sym(args->dso, args->sym, die_name(die));
const char *call_fname = die_get_call_file(die);
int call_lineno = die_get_call_lineno(die);
char *call_srcline = srcline__unknown;
-
- if (!inline_sym)
- goto abort_enomem;
+ struct symbol *inline_sym;
+
+ if (dwarf_tag(die) == DW_TAG_subprogram && args->sym) {
+ /*
+ * cu_walk_functions_at() opens the walk with the
+ * containing DW_TAG_subprogram DIE (the non-inlined outer
+ * function). That's just the base symbol -- use it
+ * directly. Avoids a fragile name-vs-name compare in
+ * new_inline_sym() that misfires when GCC IPA passes
+ * (.isra/.constprop/.part/.cold) rename the ELF symbol
+ * while DWARF keeps the pre-clone linkage name, which
+ * left the outer frame spuriously tagged "(inlined)".
+ */
+ inline_sym = args->sym;
+ } else {
+ /*
+ * Prefer DW_AT_linkage_name so C++ inline frames keep
+ * their namespace/class qualification. new_inline_sym()
+ * runs the name through dso__demangle_sym(), so the
+ * mangled linkage name is turned back into
+ * "Namespace::Class::method". Fall back to DW_AT_name
+ * (unqualified) when no linkage name is present, e.g.
+ * for C code or extern "C" functions.
+ */
+ const char *funcname = die_get_linkage_name(die) ?: die_name(die);
+
+ inline_sym = new_inline_sym(args->dso, args->sym, funcname);
+ if (!inline_sym)
+ goto abort_enomem;
+ }
/* Assign caller information to the parent. */
if (call_fname)