]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
perf machine: Add inline information to frame pointer and LBR callchains
authorIan Rogers <irogers@google.com>
Sat, 17 Jan 2026 05:28:49 +0000 (21:28 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 20 Jan 2026 18:43:56 +0000 (15:43 -0300)
Use append_inlines() in frame pointer and LBR cases.

Update the addr2line test to also test frame pointers.

LBR is also updated but inaccuracy in the branched to IP means the
inline information is missing in the leaf.

Leave LBR callchains untested for now.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Aditya Bodkhe <aditya.b1@linux.ibm.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Chun-Tse Shao <ctshao@google.com>
Cc: Dmitriy Vyukov <dvyukov@google.com>
Cc: Dr. David Alan Gilbert <linux@treblig.org>
Cc: Guo Ren <guoren@kernel.org>
Cc: Haibo Xu <haibo1.xu@intel.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Krzysztof Ɓopatowski <krzysztof.m.lopatowski@gmail.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Sergei Trofimovich <slyich@gmail.com>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Stephen Brennan <stephen.s.brennan@oracle.com>
Cc: Thomas Falcon <thomas.falcon@intel.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/tests/shell/addr2line_inlines.sh
tools/perf/util/machine.c

index 4a5b6f5be23d7b5ef9f948bcbb0e32e010af3932..ce30d9c7e0bf4c38debe2d6aeb714b6c9d3975a8 100755 (executable)
@@ -21,8 +21,28 @@ trap_cleanup() {
 }
 trap trap_cleanup EXIT TERM INT
 
-test_inlinedloop() {
-    echo "Inline unwinding verification test"
+test_fp() {
+    echo "Inline unwinding fp verification test"
+    # Record data. Currently only dwarf callchains support inlined functions.
+    perf record --call-graph fp -e task-clock:u -o "${perf_data}" -- perf test -w inlineloop 1
+
+    # Check output with inline (default) and srcline
+    perf script -i "${perf_data}" --fields +srcline > "${perf_script_txt}"
+
+    # Expect the leaf and middle functions to occur on lines in the 20s, with
+    # the non-inlined parent function on a line in the 30s.
+    if grep -q "inlineloop.c:2. (inlined)" "${perf_script_txt}" &&
+       grep -q "inlineloop.c:3.$" "${perf_script_txt}"
+    then
+        echo "Inline unwinding fp verification test [Success]"
+    else
+        echo "Inline unwinding fp verification test [Failed missing inlined functions]"
+        err=1
+    fi
+}
+
+test_dwarf() {
+    echo "Inline unwinding dwarf verification test"
     # Record data. Currently only dwarf callchains support inlined functions.
     perf record --call-graph dwarf -e task-clock:u -o "${perf_data}" -- perf test -w inlineloop 1
 
@@ -34,14 +54,15 @@ test_inlinedloop() {
     if grep -q "inlineloop.c:2. (inlined)" "${perf_script_txt}" &&
        grep -q "inlineloop.c:3.$" "${perf_script_txt}"
     then
-        echo "Inline unwinding verification test [Success]"
+        echo "Inline unwinding dwarf verification test [Success]"
     else
-        echo "Inline unwinding verification test [Failed missing inlined functions]"
+        echo "Inline unwinding dwarf verification test [Failed missing inlined functions]"
         err=1
     fi
 }
 
-test_inlinedloop
+test_fp
+test_dwarf
 
 cleanup
 exit $err
index 841b711d970e9457b14297116dc7be5a42a38fdb..30d606fbf04051443a53203ce1079fc91495e659 100644 (file)
@@ -2090,6 +2090,59 @@ struct iterations {
        u64 cycles;
 };
 
+static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms, u64 ip,
+                       bool branch, struct branch_flags *flags, int nr_loop_iter,
+                       u64 iter_cycles, u64 branch_from)
+{
+       struct symbol *sym = ms->sym;
+       struct map *map = ms->map;
+       struct inline_node *inline_node;
+       struct inline_list *ilist;
+       struct dso *dso;
+       u64 addr;
+       int ret = 1;
+       struct map_symbol ilist_ms;
+       bool first = true;
+
+       if (!symbol_conf.inline_name || !map || !sym)
+               return ret;
+
+       addr = map__dso_map_ip(map, ip);
+       addr = map__rip_2objdump(map, addr);
+       dso = map__dso(map);
+
+       inline_node = inlines__tree_find(dso__inlined_nodes(dso), addr);
+       if (!inline_node) {
+               inline_node = dso__parse_addr_inlines(dso, addr, sym);
+               if (!inline_node)
+                       return ret;
+               inlines__tree_insert(dso__inlined_nodes(dso), inline_node);
+       }
+
+       ilist_ms = (struct map_symbol) {
+               .maps = maps__get(ms->maps),
+               .map = map__get(map),
+       };
+       list_for_each_entry(ilist, &inline_node->val, list) {
+               ilist_ms.sym = ilist->symbol;
+               if (first) {
+                       ret = callchain_cursor_append(cursor, ip, &ilist_ms,
+                                                     branch, flags, nr_loop_iter,
+                                                     iter_cycles, branch_from, ilist->srcline);
+               } else {
+                       ret = callchain_cursor_append(cursor, ip, &ilist_ms, false,
+                                                     NULL, 0, 0, 0, ilist->srcline);
+               }
+               first = false;
+
+               if (ret != 0)
+                       return ret;
+       }
+       map_symbol__exit(&ilist_ms);
+
+       return ret;
+}
+
 static int add_callchain_ip(struct thread *thread,
                            struct callchain_cursor *cursor,
                            struct symbol **parent,
@@ -2170,6 +2223,11 @@ static int add_callchain_ip(struct thread *thread,
        ms.maps = maps__get(al.maps);
        ms.map = map__get(al.map);
        ms.sym = al.sym;
+
+       if (append_inlines(cursor, &ms, ip, branch, flags, nr_loop_iter,
+                          iter_cycles, branch_from) == 0)
+               goto out;
+
        srcline = callchain_srcline(&ms, al.addr);
        err = callchain_cursor_append(cursor, ip, &ms,
                                      branch, flags, nr_loop_iter,
@@ -2888,49 +2946,6 @@ check_calls:
        return 0;
 }
 
-static int append_inlines(struct callchain_cursor *cursor, struct map_symbol *ms, u64 ip)
-{
-       struct symbol *sym = ms->sym;
-       struct map *map = ms->map;
-       struct inline_node *inline_node;
-       struct inline_list *ilist;
-       struct dso *dso;
-       u64 addr;
-       int ret = 1;
-       struct map_symbol ilist_ms;
-
-       if (!symbol_conf.inline_name || !map || !sym)
-               return ret;
-
-       addr = map__dso_map_ip(map, ip);
-       addr = map__rip_2objdump(map, addr);
-       dso = map__dso(map);
-
-       inline_node = inlines__tree_find(dso__inlined_nodes(dso), addr);
-       if (!inline_node) {
-               inline_node = dso__parse_addr_inlines(dso, addr, sym);
-               if (!inline_node)
-                       return ret;
-               inlines__tree_insert(dso__inlined_nodes(dso), inline_node);
-       }
-
-       ilist_ms = (struct map_symbol) {
-               .maps = maps__get(ms->maps),
-               .map = map__get(map),
-       };
-       list_for_each_entry(ilist, &inline_node->val, list) {
-               ilist_ms.sym = ilist->symbol;
-               ret = callchain_cursor_append(cursor, ip, &ilist_ms, false,
-                                             NULL, 0, 0, 0, ilist->srcline);
-
-               if (ret != 0)
-                       return ret;
-       }
-       map_symbol__exit(&ilist_ms);
-
-       return ret;
-}
-
 static int unwind_entry(struct unwind_entry *entry, void *arg)
 {
        struct callchain_cursor *cursor = arg;
@@ -2940,7 +2955,8 @@ static int unwind_entry(struct unwind_entry *entry, void *arg)
        if (symbol_conf.hide_unresolved && entry->ms.sym == NULL)
                return 0;
 
-       if (append_inlines(cursor, &entry->ms, entry->ip) == 0)
+       if (append_inlines(cursor, &entry->ms, entry->ip, /*branch=*/false, /*branch_flags=*/NULL,
+                          /*nr_loop_iter=*/0, /*iter_cycles=*/0, /*branch_from=*/0) == 0)
                return 0;
 
        /*