Tag Archives: Debugging

How to debug signal handlers using GDB

At times, one might want to debug a signal handler using GDB. Looks simple, just set a breakpoint at the entry! But there is tiny glitch. Consider the following program.

  @file : sig.c


void signalhandler(int signum) {
  printf("\n SIGINT caught : %d", signum);

int main() {
  signal(SIGINT, (void*)signalhandler);

  while (1) {
    printf("\n looping : inside main()");

Now, lets try to debug the signal handler function using GDB.

# Set a breakpoint in the signal handler function.
(gdb) b signalhandler 
Breakpoint 1 at 0x40058f: file sig.c, line 5.
(gdb) r
Starting program: /home/nirbhay/project/sandbox/c/signal/a.out 

 looping : inside main()
 looping : inside main()
 looping : inside main()
 looping : inside main()
 looping : inside main()
# And now, Ctrl-C
Program received signal SIGINT, Interrupt.
0x00007ffff7af5120 in __nanosleep_nocancel () at ../sysdeps/unix/syscall-template.S:82
82	../sysdeps/unix/syscall-template.S: No such file or directory.
	in ../sysdeps/unix/syscall-template.S

Its apparent that the signal that we are trying to send to the program is being intercepted by GDB and hence control never enters the signal handler (where we have already set a breakpoint).

In such scenarios, GDB’s handle command can be used. With this command we can alter the default behaviour of GDB towards signals. The handle command takes, as argument, a list of signals (to be handled) followed by actions. Of all the actions, ones that are of interest to us are : nostop (do not re-enter debugger if this signal happens) & pass (let program see this signal).

Lets now watch ’em in action :

(gdb) handle SIGINT nostop pass
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) y

Signal        Stop	Print	Pass to program	Description
SIGINT        No	Yes	Yes		Interrupt
(gdb) r
Starting program: /home/nirbhay/project/sandbox/c/signal/a.out 

 looping : inside main()
 looping : inside main()
Program received signal SIGINT, Interrupt.

Breakpoint 1, signalhandler (signum=2) at sig.c:5
5	  printf("\n SIGINT caught : %d", signum);

Using Valgrind with GDB

Valgrind is a very powerful tool for detecting & diagnosing memory related issues in a C/C++ program. Sometimes, the task of locating the actual problematic area becomes increasingly tedious if the code base is huge and complex.

Let us now try to debug a small program running under Valgrind using GDB.

/* leaky.c */

int main()
  char *ptr= malloc(100);
  printf("I am a leaky program!\n");
  return 0;

Before we move further, one point to note here is that a process running under Valgrind is actually running on a synthetic CPU provided by Valgrind and hence it cannot be debugged directly using GDB [source]. So, in order to overcome this limitation, Valgrind-3.7.0 introduced ‘gdbserver’, an implementation of GDB remote debugging protocol, using which the process can be debugged.

Step 1 : Start the process under Valgrind with –vgdb=yes & –vgdb-error=0 options

> gcc -g leaky.c

> valgrind --vgdb=yes --vgdb-error=0 ./a.out 
==6148== Memcheck, a memory error detector
==6148== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==6148== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==6148== Command: ./a.out
==6148== (action at startup) vgdb me ... 
==6148== TO DEBUG THIS PROCESS USING GDB: start GDB like this
==6148==   /path/to/gdb ./a.out
==6148== and then give GDB the following command
==6148==   target remote | /usr/lib64/valgrind/../../bin/vgdb --pid=6148
==6148== --pid is optional if only one valgrind process is running

Step 2 : In a different terminal start GDB

> gdb a.out 
GNU gdb Red Hat Linux (6.5-25.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu"...Using host libthread_db library "/lib64/libthread_db.so.1".

Step 3 : In the same gdb session,

(gdb) target remote | /usr/lib64/valgrind/../../bin/vgdb
Remote debugging using | /usr/lib64/valgrind/../../bin/vgdb
relaying data between gdb and process 6148
[New thread 6148]
[Switching to thread 6148]
0x0000003f10000a60 in ?? ()
(gdb) b main
Breakpoint 1 at 0x4004e0: file leaky.c, line 7.
(gdb) c

Breakpoint 1, main () at leaky.c:7
7	  char *ptr= malloc(100);

And step through the code further..