Listing active signal handlers (or dispositions) with GDB
A colleague at Red Hat asked whether it is possible to get GDB to list the currently active signal handlers, aka the signal dispositions. I.e., the actions taken by the inferior process on receipt of each specific signal.
You could do it manually, like this, once for each signal. Here done for SIGINT (which is 2):
(gdb) set $p = (struct sigaction *) malloc(sizeof (struct sigaction))
(gdb) print sigaction(2, 0, $p)
$1 = 0
(gdb) print $p->__sigaction_handler.sa_handler
$2 = (__sighandler_t) 0x3ff797bb200 <g_unix_signal_handler>
but that’s… rather cumbersome. Read on for something nicer.
Knowing what action a signal will take when it is received is important information. For example, it’s a common problem to have a library that “steals” a signal from the main program, or have libraries with conflicting signal handlers. E.g., toolkits such as Qt and language interpreter libraries such as Python often want to install a SIGCHLD handler to track the lifetime of processes they spawn. Or SIGINT handlers to handle Ctrl-C gracefully. Etc. This is a problem that GDB itself has tripped on internally, even. See Bug 14382 – gdb hangs after plotting with matplotlib, for example.
Answer the question already!
Back to the original question, the short answer is “no”: GDB currently does not have a built-in command to list the currently registered signal handlers. I’m not aware of any way for a debugger to get this information out of the Linux kernel directly, actually.
However, we can still script the sigaction
calls above and wrap it all in a nice user-friendly command. I whipped up something quickly for my colleague, using GDB/CLI scripting. You can find the script here:
https://github.com/palves/misc/blob/master/gdb/signals.gdb
This adds a new info signal-dispositions
command to GDB. Download it somewhere and source it from your ~/.gdbinit
to make it always handy and available.
Example output (of gdb debugging itself, on x86-64 Fedora):
(gdb) info signal-dispositions
Number Name Description Disposition
1 SIGHUP Hangup handle_sighup(int) in section .text of build/gdb/gdb
2 SIGINT Interrupt rl_signal_handler in section .text of build/gdb/gdb
3 SIGQUIT Quit rl_signal_handler in section .text of build/gdb/gdb
4 SIGILL Illegal instruction SIG_DFL
5 SIGTRAP Trace/breakpoint trap SIG_DFL
6 SIGABRT Aborted SIG_DFL
7 SIGBUS Bus error SIG_DFL
8 SIGFPE Floating point exception handle_sigfpe(int) in section .text of build/gdb/gdb
9 SIGKILL Killed SIG_DFL
10 SIGUSR1 User defined signal 1 SIG_DFL
11 SIGSEGV Segmentation fault SIG_DFL
12 SIGUSR2 User defined signal 2 SIG_DFL
13 SIGPIPE Broken pipe SIG_IGN
14 SIGALRM Alarm clock rl_signal_handler in section .text of build/gdb/gdb
15 SIGTERM Terminated rl_signal_handler in section .text of build/gdb/gdb
16 SIGSTKFLT Stack fault SIG_DFL
17 SIGCHLD Child exited sigchld_handler(int) in section .text of build/gdb/gdb
18 SIGCONT Continued tui_cont_sig(int) in section .text of build/gdb/gdb
19 SIGSTOP Stopped (signal) SIG_DFL
20 SIGTSTP Stopped rl_signal_handler in section .text of build/gdb/gdb
21 SIGTTIN Stopped (tty input) rl_signal_handler in section .text of build/gdb/gdb
22 SIGTTOU Stopped (tty output) rl_signal_handler in section .text of build/gdb/gdb
23 SIGURG Urgent I/O condition SIG_DFL
24 SIGXCPU CPU time limit exceeded GC_restart_handler in section .text of /lib64/libgc.so.1
25 SIGXFSZ File size limit exceeded SIG_IGN
26 SIGVTALRM Virtual timer expired SIG_DFL
27 SIGPROF Profiling timer expired SIG_DFL
28 SIGWINCH Window changed tui_sigwinch_handler(int) in section .text of build/gdb/gdb
29 SIGIO I/O possible SIG_DFL
30 SIGPWR Power failure GC_suspend_handler in section .text of /lib64/libgc.so.1
31 SIGSYS Bad system call SIG_DFL
34 SIG34 Real-time signal 0 SIG_DFL
35 SIG35 Real-time signal 1 SIG_DFL
[...]
(gdb) info signal-dispositions 2 5
Number Name Description Disposition
2 SIGINT Interrupt rl_signal_handler in section .text of build/gdb/gdb
5 SIGTRAP Trace/breakpoint trap SIG_DFL
I wrote it as GDB CLI script, just because that was quicker to prototype. Using GDB Python or Guile scripting would allow for error handling, nicer formatting control and better argument handling. I’m too lazy^Wbusy at the moment to rewrite it though.
Could we do better?
I think we could. The ideal solution would let the debugger retrieve the information without running code in the inferior address space, which is always risky — the inferior might be messed up already, it’s not desirable for seemingly innocent commands to potentially mess it up further.
For example, we could have the kernel expose the set of signal actions in /proc/PID/status
or some new /proc
file or /proc
directory — e.g., /proc/pid/sigaction/$signo
, with one entry per signal.
And then for core debugging, the kernel could dump the same info in an ELF core note, similarly to how mapped files end up in the NT_FILE note.
Post a Comment