gdb and signal handling ( especially sigwaitinfo )

How gdb generally works

In a debugging session, when the debugger wants to set a breakpoint, it replaces an instruction by int3

When the trap instruction (0xCC on x86) is executed, the processor notifies the OS.

On UNIX, the OS checks to see whether the process is being ptraced by somebody.

  • If no, the SIGTRAP signal is delivered to the application, which usually results in process being killed (but you can catch and handle the signal in the application).
  • If there is a ptraceer (usually a debugger), then the signal is not delivered to the application. Instead, debugger’s wait is unblocked to notify the debugger that the inferior has changed state. The debugger then looks at where the inferior process stopped, discovers that it did so because of a breakpoints, and handles the situation as appropriate (let’s you examine the inferior, or resumes it if the breakpoint is conditional and current conditions don’t match, etc.)

When the target process is stopped, it could interrupt the system_call where target process/thread is waiting/running.

GDB Interrupted System Calls

There is an unfortunate side effect when using GDB to debug multi-threaded programs.

If one thread stops for a breakpoint, or for some other reason, and another thread is blocked in a system call,

then the system call may return prematurely.

This is a consequence of the interaction between multiple threads and the signals that GDB uses to implement breakpoints and other events that stop execution.

To handle this problem, your program should check the return value of each system call and react appropriately. This is good programming style anyways.

For example, do not write code like this:

	sleep (10);

The call to sleep will return early if a different thread stops at a breakpoint or for some other reason.

Instead, write this:

	int unslept = 10;
  while (unslept > 0)
    unslept = sleep (unslept);

A system call is allowed to return early, so the system is still conforming to its specification. But GDB does cause your multi-threaded program to behave differently than it would without GDB.

Also, GDB uses internal breakpoints in the thread library to monitor certain events such as thread creation and thread destruction. When such an event happens, a system call in another thread may return prematurely, even though your program does not appear to stop.

 

GDB and sigwaitinfo

if we use sigwaitinfo to handle the signal in a thread, sigwaitinfo maybe interrupted ( return -1 ) when gdb ran step/next  in another thread etc.

so we need to check sigwaitinfo() the while loop:

void signal_handler_2()
{
    sigset_t            mask;
    siginfo_t           info;
    sigemptyset(&mask);
     sigaddset(&mask, SIGQUIT);
    sigaddset(&mask, SIGINT);
    sigaddset(&mask, SIGTERM);
     bool run = true;
    while ( run ) {
      // this sigwaitinfo could be interrupted by gdb as we are running it as thread
     if (sigwaitinfo(&mask, &info) == -1) {
            perror(“sigwaitinfo() failed”);
            continue;
       }
      switch (info.si_signo) {
          case SIGQUIT:
          case SIGINT:
          case SIGTERM:
              // info.si_pid is pid of terminated process, it is not POSIX
              printf(“a child terminated, pid = %d\n”, info.si_pid);
              run = false;
              break;
          default:
              printf(“sig recived pid = %d\n”, info.si_pid);
              run = false;
             break;
        }
    }
}

Please rate this


Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>