Skip to content

List active signal handlers with GDB

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

Your email is never published nor shared. Required fields are marked *