2015-11-02 75 views
1

我正在尝试开发一个GDB python扩展,它定义了启动一个新线程的命令,用户可以在其中检查任意类型的变量。我的Python扩展的框架是这样的:当多线程python扩展用于调试多线程程序时,GDB挂起

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() 

可以看出,我在这里定义一个情节命令。当我尝试调试下面的程序,GDB会挂起,如果我:

  1. 将断点任何地方的程序()线程内(比如说,第9行,while循环中)。
  2. 在gdb命中断点后运行命令
  3. 运行继续之后。
#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; 
} 

最奇怪的是,如果我改变了代码来调用程序()无需启动一个线程,GDB从来没有挂起(和占位符的消息仍然会打印出来,因为我期望的那样)。

到目前为止,我试着用GDB版本7.5.1和7.10来运行这个过程,但我总是遇到相同的行为。

我在做什么错? GDB不支持守护进程线程吗?这似乎并不符合section 23.2.2.1 of the documentation的建议:GDB可能不是线程安全的,但我认为在启动这样一个愚蠢的守护进程线程后它不应该挂起。

回答

1

this blog post

GDB使用该功能(sigsuspend,其中GDB挂功能)等待来自应用软件执行新的事件:当东西在调试发生(见调试器是如何工作的) ,内核会通过发送一个SIGCHLD信号来通知GDB它。当它收到时,GDB会唤醒并检查发生了什么。

但是,信号传递给GDB进程,但不一定传递给它的主线程。而且它实践中,经常发生它传递给第二个线程,谁不关心它(这是默认行为),并继续其生活,就好像什么都没有发生。

的解决方案是配置线程信号处理行为,使得仅GDB主线程得到由这些信号通知:

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() 

pysigset包是必需的,并且可以从安装点(sudo pip安装pysigset)。