3

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

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. 在procedure() 线程内的任意位置放置一个断点(例如,第9 行,在while 循环内)。
  2. 在 gdb 命中断点后运行命令plot 。
  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;
}

最奇怪的是,如果我将此代码更改为在不启动线程的情况下调用procedure(),GDB 永远不会挂起(并且占位符消息仍按我的预期打印)。

到目前为止,我已经尝试使用 GDB 版本 7.5.1 和 7.10 运行此过程,但我总是遇到相同的行为。

我究竟做错了什么?GDB不支持守护线程吗?这似乎不符合文档第 23.2.2.1 节的建议:GDB 可能不是线程安全的,但我认为在启动这样一个愚蠢的守护线程后它不应该挂起。

4

1 回答 1

3

这篇博文

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包是必需的,可以从 pip 安装(sudo pip install pysigset)。

于 2015-11-02T19:27:29.433 回答