2015-11-02 94 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)。