Consider this Makefile:
all: runit runit: crasher ./crasher crasher: crasher.c gcc -g -o crasher crasher.c
And, here is the program it is building:
int *x = 0; int main () { *x = 52; }
Now, if you run “make
“, eventually you will see a crash. But how to debug the crash?
Well, obviously, this is a trivial example so you’d just debug the program. But what if you had a complex script involving extensive and obscure initialization? Say, in your test suite? The traditional answer is logging plus cut and paste into gdb; or perhaps hacking an invocation of gdb --args
into your script. Nowadays you can do better, though.
Let’s start by debugging make:
$ gdb -quiet make Reading symbols from /usr/bin/make...(no debugging symbols found)...done. Missing separate debuginfos, use: debuginfo-install make-3.82-8.fc16.x86_64
Now set things up for multi-inferior debugging:
(gdb) set detach-on-fork off (gdb) set target-async on (gdb) set non-stop on (gdb) set pagination off
(Yes, it is silly how many settings you have to tweak; and yes, we’re going to fix this.)
Now do it:
(gdb) run Starting program: /usr/bin/make gcc -g -o crasher crasher.c [New inferior 9694] [New process 9694] process 9694 is executing new program: /usr/bin/gcc [New inferior 9695] [New process 9695] process 9695 is executing new program: /usr/libexec/gcc/x86_64-redhat-linux/4.6.2/cc1 Missing separate debuginfos, use: debuginfo-install gcc-4.6.2-1.fc16.x86_64 [Inferior 3 (process 9695) exited normally] [Inferior 9695 exited] Missing separate debuginfos, use: debuginfo-install cpp-4.6.2-1.fc16.x86_64 (gdb) [New inferior 9696] [New process 9696] process 9696 is executing new program: /usr/bin/as [Inferior 4 (process 9696) exited normally] [Inferior 9696 exited] [New inferior 9697] [New process 9697] process 9697 is executing new program: /usr/libexec/gcc/x86_64-redhat-linux/4.6.2/collect2 Missing separate debuginfos, use: debuginfo-install binutils-2.21.53.0.1-6.fc16.x86_64 [New inferior 9698] [New process 9698] process 9698 is executing new program: /usr/bin/ld.bfd Missing separate debuginfos, use: debuginfo-install gcc-4.6.2-1.fc16.x86_64 [Inferior 6 (process 9698) exited normally] [Inferior 9698 exited] [Inferior 5 (process 9697) exited normally] [Inferior 9697 exited] [Inferior 2 (process 9694) exited normally] [Inferior 9694 exited] ./crasher [New inferior 9699] [New process 9699] process 9699 is executing new program: /tmp/crasher Missing separate debuginfos, use: debuginfo-install binutils-2.21.53.0.1-6.fc16.x86_64 Program received signal SIGSEGV, Segmentation fault. 0x000000000040047f in main () at crasher.c:5 5 *x = 52;
Cool stuff. Now you can inspect the crashed program:
(gdb) info inferior Num Description Executable 7 process 9699 /tmp/crasher * 1 process 9691 /usr/bin/make (gdb) inferior 7 [Switching to inferior 7 [process 9699] (/tmp/crasher)] [Switching to thread 7 (process 9699)] #0 0x000000000040047f in main () at crasher.c:5 5 *x = 52;
There is still a lot of work to do here — it is still a bit too slow, setting breakpoints is still a pain, etc. These are all things we’re going to be cleaning up in the coming year.
6 Comments
That is cool. I didn’t know gdb could attach to multiple programs.
I’ve been using the following hack for a while for this exact scenario:
http://people.gnome.org/~walters/gdbifmatch.c
[…] my last post I mentioned that setting breakpoints is a pain when debugging multiple processes in GDB. While […]
[…] my last post I mentioned that setting breakpoints is a pain when debugging multiple processes in GDB. While […]
This is very nice. Thanks for sharing.
non-stop mode seems somewhat broken for multi-process in 7.7.1 ; I’m finding that inferiors are shown as wchan=ptrace_stop by ps, but gdb thinks they’re running. It won’t interrupt them, but refuses to continue them because they’re “already running”.
See http://stackoverflow.com/q/27140941/398670
Perhaps because of that, unlike in your example I’ve been unable to make this work well because gdb stops execution whenever a child process exits.
An update regarding non-stop and 7.7.1: It requires target-async on to work correctly. Without that, behaviour is confusing and broken.
GDB 7.8 has removed the requirement to set target-async on.
I’ve updated the Stack Overflow post accordingly.