Search code examples
gdbmultiprocessingptrace

using gdb on with execl, wait, and ptrace


I'm trying to debug a program that calls another program and uses ptrace.

I can run it, just fine; however, when trying to debug it with gdb, the program hangs at wait(&status). What I want to do is debug the main process and possibly switch back and forth.

I think it'll be easier to show code and gdb output:

  switch(pid = fork())
  {
    case -1: /*error*/
    {
        perror("fork()");
        exit(-1);
    }
    case 0:/*child process*/
    {
        ptrace(PTRACE_TRACEME, NULL, NULL);     /*allow child process to be traced*/
        execl(path, name, NULL);                /*child will be stopped here*/
        perror("execl()");
        exit(-1);
    }
    /*parent continues execution*/
  }

  wait(&status);
  while(true)
  {
    if(WIFEXITED(status) || (WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL))
    {
      cout << "process " << child << "terminated\n";
      exit(0);
    }
    get_and_handle_input();

When using gdb, it stops at the wait(&status):

gdb ./prog
GNU gdb (GDB; openSUSE 13.2) 7.8
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-suse-linux".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.opensuse.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...

warning: /etc/gdbinit.d/gdb-heap.py: No such file or directory
Reading symbols from ./prog...done.
(gdb) r ../tests/function_test 
Starting program: /home/user/dev/src/prog ../tests/function_test
[New process 7930]
^C
Program received signal SIGINT, Interrupt.
0x00007ffff68479d2 in __libc_wait (stat_loc=0x7fffffffdc7c) at ../sysdeps/unix/sysv/linux/wait.c:30
30          return INLINE_SYSCALL (wait4, 4, WAIT_ANY, stat_loc, 0,
(gdb) bt
#0  0x00007ffff68479d2 in __libc_wait (stat_loc=0x7fffffffdc7c) at ../sysdeps/unix/sysv/linux/wait.c:30
#1  0x00000000004059bc in main (argc=<optimized out>, argv=<optimized out>) at shell.cpp:44
(gdb) info inferiors
  Num  Description       Executable        
  2    process 7930      /home/user/dev/src/prog
* 1    process 7926      /home/user/dev/src/prog 
(gdb) continue
Continuing.

If I switch to process 2 and use continue, then process 2 runs; however, that doesn't do me any good (even if I set a break point in process 1).

Hopefully this makes sense. If I need to clear anything up, please let me know.


Solution

  • When it can, gdb tries to get notified of fork events in the target process.

    On Linux, gdb calls ptrace(PTRACE_SETOPTIONS,...,TRACEFORK|TRACECLONE|TRACEEXEC). TRACECLONE ensures that after a fork, the child process is automatically traced. When the target process forks, gdb looks at the settings for follow-fork-mode and detach-on-fork, and either stays attached to both processes (but only allows one to run) or removes all the breakpoints (if any) from one process and detaches from it (thus allowing both processes to run, one traced, one not).

    Since you have set detach-on-fork to off, gdb remains attached to both processes, and runs one process and keeps the other suspended. If follow-fork-mode is parent, the default, the parent runs and that waitpid just hangs. If follow-fork-mode is child, your ptrace(PTRACE_TRACEME, ...) call will fail, because there can be only one Tracer at a time for a process. So the best course of action seems to be to set detach-on-fork to on, so that gdb will trace just the parent process.