簡體   English   中英

如何將調試器附加到 python 子進程?

[英]How to attach debugger to a python subproccess?

我需要調試由multiprocessing.Process()產生的子進程。 pdb degugger 似乎不知道分叉並且無法附加到已經運行的進程。

是否有任何更智能的 python 調試器可以附加到子進程?

我一直在尋找一個簡單的解決方案來解決這個問題並想出了這個:

import sys
import pdb

class ForkedPdb(pdb.Pdb):
    """A Pdb subclass that may be used
    from a forked multiprocessing child

    """
    def interaction(self, *args, **kwargs):
        _stdin = sys.stdin
        try:
            sys.stdin = open('/dev/stdin')
            pdb.Pdb.interaction(self, *args, **kwargs)
        finally:
            sys.stdin = _stdin

像使用經典 Pdb 一樣使用它:

ForkedPdb().set_trace()

Winpdb幾乎是更智能的 Python 調試器的定義。 它明確支持下叉,不確定它是否與multiprocessing.Process()一起工作,但值得一試。

有關檢查是否支持您的用例的候選列表,請參閱 wiki 中的Python 調試器列表。

這是 Romuald 的答案的詳細說明,它使用其文件描述符恢復原始標准輸入。 這使 readline 在調試器中工作。 此外,pdb 對 KeyboardInterrupt 的特殊管理被禁用,以免干擾多處理 sigint 處理程序。

class ForkablePdb(pdb.Pdb):

    _original_stdin_fd = sys.stdin.fileno()
    _original_stdin = None

    def __init__(self):
        pdb.Pdb.__init__(self, nosigint=True)

    def _cmdloop(self):
        current_stdin = sys.stdin
        try:
            if not self._original_stdin:
                self._original_stdin = os.fdopen(self._original_stdin_fd)
            sys.stdin = self._original_stdin
            self.cmdloop()
        finally:
            sys.stdin = current_stdin

基於@memplex 的想法,我必須通過在構造函數中設置sys.stdin以及通過 joblib 直接傳遞它來修改它以使其與joblib一起使用。

import os
import pdb
import signal
import sys
import joblib

_original_stdin_fd = None

class ForkablePdb(pdb.Pdb):
    _original_stdin = None
    _original_pid = os.getpid()

    def __init__(self):
        pdb.Pdb.__init__(self)
        if self._original_pid != os.getpid():
            if _original_stdin_fd is None:
                raise Exception("Must set ForkablePdb._original_stdin_fd to stdin fileno")

            self.current_stdin = sys.stdin
            if not self._original_stdin:
                self._original_stdin = os.fdopen(_original_stdin_fd)
            sys.stdin = self._original_stdin

    def _cmdloop(self):
        try:
            self.cmdloop()
        finally:
            sys.stdin = self.current_stdin

def handle_pdb(sig, frame):
    ForkablePdb().set_trace(frame)

def test(i, fileno):
    global _original_stdin_fd
    _original_stdin_fd = fileno
    while True:
        pass    

if __name__ == '__main__':
    print "PID: %d" % os.getpid()
    signal.signal(signal.SIGUSR2, handle_pdb)
    ForkablePdb().set_trace()
    fileno = sys.stdin.fileno()
    joblib.Parallel(n_jobs=2)(joblib.delayed(test)(i, fileno) for i in range(10))

remote-pdb可用於調試子進程。 安裝后,在需要調試的代碼中加入以下幾行:

import remote_pdb
remote_pdb.set_trace()

remote-pdb 將打印一個端口號,該端口號將接受用於調試該特定進程的 telnet 連接。 關於 worker 啟動順序有一些注意事項,在使用各種前端時 stdout 的位置等。為了確保使用特定端口(必須是免費的並且當前用戶可以訪問),請改用以下命令:

from remote_pdb import RemotePdb
RemotePdb('127.0.0.1', 4444).set_trace()

remote-pdb 也可以通過 Python 3.7 中的breakpoint()命令啟動。

我的一個想法是創建“虛擬”類來偽造您從多處理中使用的方法的實現:

from multiprocessing import Pool


class DummyPool():
    @staticmethod
    def apply_async(func, args, kwds):
        return DummyApplyResult(func(*args, **kwds))

    def close(self): pass
    def join(self): pass


class DummyApplyResult():
    def __init__(self, result):
        self.result = result

    def get(self):
        return self.result


def foo(a, b, switch):
    # set trace when DummyPool is used
    # import ipdb; ipdb.set_trace()
    if switch:
        return b - a
    else:
        return a - b


if __name__ == '__main__':
    xml = etree.parse('C:/Users/anmendoza/Downloads/jim - 8.1/running-config.xml')
    pool = DummyPool()  # switch between Pool() and DummyPool() here
    results = []
    results.append(pool.apply_async(foo, args=(1, 100), kwds={'switch': True}))
    pool.close()
    pool.join()
    results[0].get()

這是 ForkedPdb(Romuald 的解決方案)的版本,它適用於基於 Windows 和 *nix 的系統。

import sys
import pdb
import win32console


class MyHandle():
    def __init__(self):
        self.screenBuffer = win32console.GetStdHandle(win32console.STD_INPUT_HANDLE)
    
    def readline(self):
        return self.screenBuffer.ReadConsole(1000)

class ForkedPdb(pdb.Pdb):
    def interaction(self, *args, **kwargs):
        _stdin = sys.stdin
        try:
            if sys.platform == "win32":
                sys.stdin = MyHandle()
            else:
                sys.stdin = open('/dev/stdin')
            pdb.Pdb.interaction(self, *args, **kwargs)
        finally:
            sys.stdin = _stdin

只需使用PuDB 即可為您提供出色的 TUI(終端上的 GUI)並支持多處理,如下所示:

from pudb import forked; forked.set_trace()

如果您在受支持的平台上,請嘗試DTrace 大多數 BSD/Solaris/OS X 系列都支持 DTrace。

這是作者的介紹 您可以使用 Dtrace 來調試任何東西。

這是關於學習 DTrace 的 SO 帖子

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM