16
votes

I have a C linux application (A) that spawns another process (P) when it is started. When I want to debug P I start A as usual and I connect with ddd/gdb to P.

Problems appear when I want to debug the entry-point (start of main) of P. If I follow the usual approach when I connect the debugger to P is already to late. The solution I've found was to insert a sleep at the begining of the main of P so I have time to connect with gdb but this is not a very elegant solution.

I've also tried using asm("int $3") but it doesn't seems to work.

Do you have any idea how I could solve this problem? (preferably without altering the code of A or P)

5

5 Answers

21
votes

You should use this option:

set follow-fork-mode mode

Where mode is one of parent, child or ask.

To follow the parent (this is the default) use:

set follow-fork-mode parent

To follow the child:

set follow-fork-mode child

To have the debugger ask you each time:

set follow-fork-mode ask

So basically you'd start out connecting gdb to A, then set gdb to follow the child, and then when A spawns P, gdb will connect to P and detach from A.

11
votes

In addition to the Nathan Fellman's answer, catchpoints come in handy, i.g.:

catch exec

Catchpoint works as a breakpoint. Every time a call to exec() syscall is detected, GDB stops. This allows you to set any breakpoint (i.g. break main) in any newly loaded executable before continuing. Another catchpoint catch fork works similarly for fork() syscall detection.

It is especially convenient:

  • when both parent and child has to be followed (set detach-on-fork off);
  • when parent processes forks often loading various executables.
1
votes

exec part with file + break main

The fork was part was explained at: https://stackoverflow.com/a/377295/895245

Now for the exec:

a.c:

#include <unistd.h>

int main(void) {
    execl("./b", "./b", "ab", "cd", (char*)NULL);
    return 1;
}

b.c:

#include <stdio.h>

int main(int argc, char **argv ) {
    printf("%s\n", argv[0]);
    printf("%s\n", argv[1]);
}

Then:

gcc -g a.c -o a
gcc -g b.c -o b
gdb -nh -q a

Now on the interactive session:

Reading symbols from a...done.
(gdb) start
Temporary breakpoint 1 at 0x4004ea: file a.c, line 4.
Starting program: /home/ciro/test/gdb-exec/a 

Temporary breakpoint 1, main () at a.c:4
4           execl("./b", "./b", "ab", "cd", (char*)NULL);
(gdb) file b
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "b"? (y or n) y
Reading symbols from b...done.
(gdb) b main
Breakpoint 2 at 0x4004f5: file b.c, line 4.
(gdb) n

Breakpoint 2, main (argc=0, argv=0x7fffffffa570) at b.c:4
4               printf("%s\n", argv[1]);
(gdb) n
process 4877 is executing new program: /home/ciro/test/gdb-exec/b

Breakpoint 2, main (argc=3, argv=0x7fffffffa598) at b.c:4
4               printf("%s\n", argv[1]);
(gdb) n
ab
5               printf("%s\n", argv[2]);
(gdb) n
cd
6       }
(gdb) 

You just have to make sure that you go up to the exec before running file, possibly with a b execl, since after that you will be using symbols from the new file.

Tested in Ubuntu 14.04, gdb 7.7.1.

-1
votes

You should be able to do this by making use of gdb's remote debugging features, specifically gdbserver. In effect, launch (P) using gdbserver. These links have more detailed info:

-1
votes

set a break point at main(), it will also break at main() of the execed program.