perf debug: Add function symbols to dump_stack
authorIan Rogers <irogers@google.com>
Tue, 24 Jun 2025 21:05:00 +0000 (14:05 -0700)
committerNamhyung Kim <namhyung@kernel.org>
Wed, 25 Jun 2025 17:59:19 +0000 (10:59 -0700)
Symbolize stack traces by creating a live machine. Add this
functionality to dump_stack and switch dump_stack users to use
it. Switch TUI to use it. Add stack traces to the child test function
which can be useful to diagnose blocked code.

Example output:
```
$ perf test -vv PERF_RECORD_
...
  7: PERF_RECORD_* events & perf_sample fields:
  7: PERF_RECORD_* events & perf_sample fields                       : Running (1 active)
^C
Signal (2) while running tests.
Terminating tests with the same signal
Internal test harness failure. Completing any started tests:
:  7: PERF_RECORD_* events & perf_sample fields:

---- unexpected signal (2) ----
    #0 0x55788c6210a3 in child_test_sig_handler builtin-test.c:0
    #1 0x7fc12fe49df0 in __restore_rt libc_sigaction.c:0
    #2 0x7fc12fe99687 in __internal_syscall_cancel cancellation.c:64
    #3 0x7fc12fee5f7a in clock_nanosleep@GLIBC_2.2.5 clock_nanosleep.c:72
    #4 0x7fc12fef1393 in __nanosleep nanosleep.c:26
    #5 0x7fc12ff02d68 in __sleep sleep.c:55
    #6 0x55788c63196b in test__PERF_RECORD perf-record.c:0
    #7 0x55788c620fb0 in run_test_child builtin-test.c:0
    #8 0x55788c5bd18d in start_command run-command.c:127
    #9 0x55788c621ef3 in __cmd_test builtin-test.c:0
    #10 0x55788c6225bf in cmd_test ??:0
    #11 0x55788c5afbd0 in run_builtin perf.c:0
    #12 0x55788c5afeeb in handle_internal_command perf.c:0
    #13 0x55788c52b383 in main ??:0
    #14 0x7fc12fe33ca8 in __libc_start_call_main libc_start_call_main.h:74
    #15 0x7fc12fe33d65 in __libc_start_main@@GLIBC_2.34 libc-start.c:128
    #16 0x55788c52b9d1 in _start ??:0

---- unexpected signal (2) ----
    #0 0x55788c6210a3 in child_test_sig_handler builtin-test.c:0
    #1 0x7fc12fe49df0 in __restore_rt libc_sigaction.c:0
    #2 0x7fc12fea3a14 in pthread_sigmask@GLIBC_2.2.5 pthread_sigmask.c:45
    #3 0x7fc12fe49fd9 in __GI___sigprocmask sigprocmask.c:26
    #4 0x7fc12ff2601b in __longjmp_chk longjmp.c:36
    #5 0x55788c6210c0 in print_test_result.isra.0 builtin-test.c:0
    #6 0x7fc12fe49df0 in __restore_rt libc_sigaction.c:0
    #7 0x7fc12fe99687 in __internal_syscall_cancel cancellation.c:64
    #8 0x7fc12fee5f7a in clock_nanosleep@GLIBC_2.2.5 clock_nanosleep.c:72
    #9 0x7fc12fef1393 in __nanosleep nanosleep.c:26
    #10 0x7fc12ff02d68 in __sleep sleep.c:55
    #11 0x55788c63196b in test__PERF_RECORD perf-record.c:0
    #12 0x55788c620fb0 in run_test_child builtin-test.c:0
    #13 0x55788c5bd18d in start_command run-command.c:127
    #14 0x55788c621ef3 in __cmd_test builtin-test.c:0
    #15 0x55788c6225bf in cmd_test ??:0
    #16 0x55788c5afbd0 in run_builtin perf.c:0
    #17 0x55788c5afeeb in handle_internal_command perf.c:0
    #18 0x55788c52b383 in main ??:0
    #19 0x7fc12fe33ca8 in __libc_start_call_main libc_start_call_main.h:74
    #20 0x7fc12fe33d65 in __libc_start_main@@GLIBC_2.34 libc-start.c:128
    #21 0x55788c52b9d1 in _start ??:0
  7: PERF_RECORD_* events & perf_sample fields                       : Skip (permissions)
```

Signed-off-by: Ian Rogers <irogers@google.com>
Link: https://lore.kernel.org/r/20250624210500.2121303-1-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/tests/builtin-test.c
tools/perf/ui/tui/setup.c
tools/perf/util/debug.c
tools/perf/util/debug.h

index 45d3d8b3317a7c0a9a6add9c8dae99f3e10a8278..80375ca39a37a25607227a8896a9462dad96ef80 100644 (file)
@@ -6,6 +6,9 @@
  */
 #include <fcntl.h>
 #include <errno.h>
+#ifdef HAVE_BACKTRACE_SUPPORT
+#include <execinfo.h>
+#endif
 #include <poll.h>
 #include <unistd.h>
 #include <setjmp.h>
@@ -231,6 +234,16 @@ static jmp_buf run_test_jmp_buf;
 
 static void child_test_sig_handler(int sig)
 {
+#ifdef HAVE_BACKTRACE_SUPPORT
+       void *stackdump[32];
+       size_t stackdump_size;
+#endif
+
+       fprintf(stderr, "\n---- unexpected signal (%d) ----\n", sig);
+#ifdef HAVE_BACKTRACE_SUPPORT
+       stackdump_size = backtrace(stackdump, ARRAY_SIZE(stackdump));
+       __dump_stack(stderr, stackdump, stackdump_size);
+#endif
        siglongjmp(run_test_jmp_buf, sig);
 }
 
@@ -244,7 +257,7 @@ static int run_test_child(struct child_process *process)
 
        err = sigsetjmp(run_test_jmp_buf, 1);
        if (err) {
-               fprintf(stderr, "\n---- unexpected signal (%d) ----\n", err);
+               /* Received signal. */
                err = err > 0 ? -err : -1;
                goto err_out;
        }
index 16c6eff4d24116b0fde68f60319330f1d4a0f1ac..022534eed68ccbc34d6272251c456e024543e074 100644 (file)
@@ -108,7 +108,7 @@ static void ui__signal_backtrace(int sig)
 
        printf("-------- backtrace --------\n");
        size = backtrace(stackdump, ARRAY_SIZE(stackdump));
-       backtrace_symbols_fd(stackdump, size, STDOUT_FILENO);
+       __dump_stack(stdout, stackdump, size);
 
        exit(0);
 }
index f9ef7d045c92e73cf084d0a25ed9407eebcacd0f..2878a7363ac888f3b09f424e082c2498614c73ed 100644 (file)
 #ifdef HAVE_BACKTRACE_SUPPORT
 #include <execinfo.h>
 #endif
+#include "addr_location.h"
 #include "color.h"
-#include "event.h"
 #include "debug.h"
+#include "event.h"
+#include "machine.h"
+#include "map.h"
 #include "print_binary.h"
+#include "srcline.h"
+#include "symbol.h"
+#include "synthetic-events.h"
 #include "target.h"
+#include "thread.h"
 #include "trace-event.h"
 #include "ui/helpline.h"
 #include "ui/ui.h"
@@ -298,21 +305,60 @@ void perf_debug_setup(void)
        libapi_set_print(pr_warning_wrapper, pr_warning_wrapper, pr_debug_wrapper);
 }
 
+void __dump_stack(FILE *file, void **stackdump, size_t stackdump_size)
+{
+       /* TODO: async safety. printf, malloc, etc. aren't safe inside a signal handler. */
+       pid_t pid = getpid();
+       struct machine *machine = machine__new_live(/*kernel_maps=*/false, pid);
+       struct thread *thread = NULL;
+
+       if (machine)
+               thread = machine__find_thread(machine, pid, pid);
+
+#ifdef HAVE_BACKTRACE_SUPPORT
+       if (!machine || !thread) {
+               /*
+                * Backtrace functions are async signal safe. Fall back on them
+                * if machine/thread creation fails.
+                */
+               backtrace_symbols_fd(stackdump, stackdump_size, fileno(file));
+               machine__delete(machine);
+               return;
+       }
+#endif
+
+       for (size_t i = 0; i < stackdump_size; i++) {
+               struct addr_location al;
+               u64 addr = (u64)(uintptr_t)stackdump[i];
+               bool printed = false;
+
+               addr_location__init(&al);
+               if (thread && thread__find_map(thread, PERF_RECORD_MISC_USER, addr, &al)) {
+                       al.sym = map__find_symbol(al.map, al.addr);
+                       if (al.sym) {
+                               fprintf(file, "    #%zd %p in %s ", i, stackdump[i], al.sym->name);
+                               printed = true;
+                       }
+               }
+               if (!printed)
+                       fprintf(file, "    #%zd %p ", i, stackdump[i]);
+
+               map__fprintf_srcline(al.map, al.addr, "", file);
+               fprintf(file, "\n");
+               addr_location__exit(&al);
+       }
+       thread__put(thread);
+       machine__delete(machine);
+}
+
 /* Obtain a backtrace and print it to stdout. */
 #ifdef HAVE_BACKTRACE_SUPPORT
 void dump_stack(void)
 {
-       void *array[16];
-       size_t size = backtrace(array, ARRAY_SIZE(array));
-       char **strings = backtrace_symbols(array, size);
-       size_t i;
-
-       printf("Obtained %zd stack frames.\n", size);
-
-       for (i = 0; i < size; i++)
-               printf("%s\n", strings[i]);
+       void *stackdump[32];
+       size_t size = backtrace(stackdump, ARRAY_SIZE(stackdump));
 
-       free(strings);
+       __dump_stack(stdout, stackdump, size);
 }
 #else
 void dump_stack(void) {}
index a4026d1fd6a31119fc81d580821c4a2a9474d520..6b737e195ce1b3e2c90eaf2e62599516f89d9405 100644 (file)
@@ -85,6 +85,7 @@ void debug_set_display_time(bool set);
 void perf_debug_setup(void);
 int perf_quiet_option(void);
 
+void __dump_stack(FILE *file, void **stackdump, size_t stackdump_size);
 void dump_stack(void);
 void sighandler_dump_stack(int sig);