如何将调试器附加到python子进程?

时间:2011-01-17 18:32:13

标签: python debugging subprocess multiprocessing

我需要调试由multiprocessing.Process()生成的子进程。 pdb degugger似乎没有意识到分叉并且无法附加到已经运行的进程。

是否有更智能的python调试器可以附加到子进程?

8 个答案:

答案 0 :(得分:48)

我一直在寻找解决此问题的简单方法,并提出了这个问题:

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

答案 1 :(得分:12)

Winpdb几乎是智能Python调试器的定义。它明确支持going down a fork,不确定它是否与multiprocessing.Process()很好地配合使用,但值得一试。

有关检查用例支持的候选列表,请参阅Wiki中的Python Debuggers列表。

答案 2 :(得分:6)

这是对Romuald的回答的详细说明,它使用文件描述符恢复原始stdin。这使readline在调试器中工作。此外,禁用了KeyboardInterrupt的pdb特殊管理,以免干扰多处理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

答案 3 :(得分:1)

基于@memplex的想法,我必须通过在构造函数中设置joblib并通过joblib直接传递它来修改它以使其与sys.stdin一起使用。

<!-- language: lang-py -->
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))

答案 4 :(得分:1)

remote-pdb可用于调试子流程。安装后,将以下行放入需要调试的代码中:

import remote_pdb
remote_pdb.set_trace()

remote-pdb将打印一个端口号,该端口号将接受用于调试该特定进程的telnet连接。关于工作程序启动顺序有一些警告,在使用各种前端时stdout会消失,等等。为确保使用特定端口(当前用户必须免费且可访问),请改用以下命令:

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

remote-pdb也可以在Python 3.7中启动via the breakpoint() command

答案 5 :(得分:0)

我的想法是创造&#34;虚拟&#34;用于伪造您在多处理中使用的方法的实现的类:

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

答案 6 :(得分:0)

这里是适用于Windows和* nix系统的ForkedPdb(Romuald解决方案)的版本。

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

答案 7 :(得分:-1)

如果您使用的是受支持的平台,请尝试DTrace。大多数BSD / Solaris / OS X系列都支持DTrace。

Here is an intro by the author。您可以使用Dtrace来调试任何事情。

Here is a SO post学习DTrace。