GDB hangs when multi-threaded python extension is used to debug multi threaded program
Question: I'm trying to develop a GDB python extension that defines a command that
launches a new thread, in which the user can inspect an arbitrary type of
variables. The skeleton of my python extension is this:

    
    
    import gdb
    import threading
    
    def plot_thread():
        import time
        while True:
            print('Placeholder for a window event loop.')
            time.sleep(1)
            pass
        pass
    
    class PlotterCommand(gdb.Command):
        def __init__(self):
            super(PlotterCommand, self).__init__("plot",
                                                gdb.COMMAND_DATA,
                                                gdb.COMPLETE_SYMBOL)
            self.dont_repeat()
            pass
    
        def invoke(self, arg, from_tty):
            plot_thread_instance=threading.Thread(target=plot_thread)
            plot_thread_instance.daemon=True
            plot_thread_instance.start()
            pass
    
        pass
    
    PlotterCommand()
    

As can be seen, I define a _plot_ command here. When I try to debug the
following program, GDB will hang if I:

  1. Put a breakpoint anywhere inside the procedure() thread (say, line 9, inside the while loop).
  2. Run the command _plot_ after gdb hits the breakpoint.
  3. Run _continue_ after that.

    
    
    #include <iostream>
    #include <thread>
    
    using namespace std;
    
    void procedure() {
        cout << "before loop"<<endl;
        while(1) {
            cout << "loop iteration"<<endl;
        }
    }
    
    int main() {
        thread t(procedure);
        t.join();
        return 0;
    }
    

The strangest thing is that, if I change this code to call _procedure()_
without launching a thread, GDB never hangs (and the placeholder messages are
still printed as I expect).

So far, I've tried to run this procedure with GDB versions 7.5.1 and 7.10, but
I always experience the same behavior.

What am I doing wrong? Aren't daemon threads not supported by GDB? That
doesn't seem to be in accordance with what the [section 23.2.2.1 of the
documentation](http://docs.adacore.com/gdb-docs/html/gdb_24.html#SEC299) is
suggesting: GDB may not be thread safe, but I don't think it should hang after
launching such a silly daemon thread.


Answer: From [this blog post](http://blog.0x972.info/?d=2015/09/04/08/54/56-bug-with-
multiple-threads-running-inside-gdb):

> GDB uses this function _(sigsuspend, the function where GDB hangs)_ to wait
> for new events from the application execution: when something occurs in the
> debuggee (see how debuggers work), the kernel will inform GDB of it by
> sending a SIGCHLD signal. When it's received, GDB awakes and check what
> happened.
>
> However, the signal is delivered to GDB process, but not necessarily to its
> main thread. And it practise, it occurs often that it's delivered to the
> second thread, who doesn't care about it (that's the default behavior), and
> continues its life as if nothing occurred.

The solution is to configure the thread signal handling behavior, so that only
the GDB main thread gets notified by these signals:

    
    
    import gdb
    import threading
    import pysigset, signal # Import these packages!
    
    def plot_thread():
        import time
        while True:
            print('Placeholder for a window event loop.')
            time.sleep(1)
            pass
        pass
    
    class PlotterCommand(gdb.Command):
        def __init__(self):
            super(PlotterCommand, self).__init__("plot",
                                                gdb.COMMAND_DATA,
                                                gdb.COMPLETE_SYMBOL)
            self.dont_repeat()
            pass
    
        def invoke(self, arg, from_tty):
            with pysigset.suspended_signals(signal.SIGCHLD): # Disable signals here!
                plot_thread_instance=threading.Thread(target=plot_thread)
                plot_thread_instance.daemon=True
                plot_thread_instance.start()
                pass
            pass
    
        pass
    
    PlotterCommand()
    

The _pysigset_ package is required, and can be installed from pip (sudo pip
install pysigset).

