使用pdb附加进程

发布于 2021-01-29 18:16:50

我有一个Python脚本,我怀疑这是一个死锁。我正在尝试进行调试,pdb但是如果逐步进行,它不会陷入死锁,并且通过返回的输出,我可以看到它没有被挂在同一迭代上。我只想在锁定脚本时将其附加到调试器,可以吗?如果需要,我愿意使用其他调试器。

关注者
0
被浏览
51
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    目前, pdb 无法停止并在正在运行的程序上开始调试。您还有其他选择:

    广东发展银行

    您可以使用GDB在C级别进行调试。这有点抽象,因为您是在研究Python的C源代码,而不是实际的Python脚本,但是在某些情况下它可能很有用。指令在这里:https
    :
    //wiki.python.org/moin/DebuggingWithGdb。他们太累了,无法在此处进行总结。

    第三方扩展和模块

    只是为了“PDB连接过程”谷歌搜索揭示了几个项目给予PDB这种能力:
    Pyringe:https://github.com/google/pyringe
    Pycharm:https://blog.jetbrains.com/pycharm/2015/02/ feature-spotlight-python-
    debugger-and-attach-to-process
    /
    Python
    Wiki的此页面具有多种替代方法:https :
    //wiki.python.org/moin/PythonDebuggingTools


    对于您的特定用例,我有一些解决方法的想法:

    讯号

    如果你是在UNIX上,可以使用的信号在像这样的博客文章,试图制止并连接到正在运行的脚本。

    此引用块直接从链接的博客文章中复制:

    当然,pdb已经具有在程序中间启动调试器的功能,其中最著名的是pdb.set_trace()。但是,这需要您知道要在哪里开始调试,这也意味着您不能将其留在生产代码中。

    但是我一直羡慕我可以用GDB做什么:只是中断正在运行的程序,然后开始使用调试器进行调试。在某些情况下这可能很方便,例如,您陷入了困境并想进行调查。今天我突然想到:注册一个设置跟踪功能的信号处理程序!这里是概念证明代码:

    import os
    import signal
    import sys
    import time
    
    def handle_pdb(sig, frame):
        import pdb
        pdb.Pdb().set_trace(frame)
    
    def loop():
        while True:
            x = 'foo'
            time.sleep(0.2)
    
    if __name__ == '__main__':
        signal.signal(signal.SIGUSR1, handle_pdb)
        print(os.getpid())
        loop()
    

    现在,我可以将SIGUSR1发送到正在运行的应用程序并获取调试器。可爱!

    我想您可以通过使用Winpdb允许远程调试来解决这个问题,以防您的应用程序不再连接到终端。上面代码的另一个问题是,调用pdb后似乎无法恢复程序,退出pdb后您仅获得了一个追溯并完成了(但是由于这只是bdb引发了bdb.BdbQuit异常,我猜这可以通过几种方式解决)。最后一个紧迫的问题是在Windows上运行此程序,我对Windows不太了解,但是我知道它们没有信号,所以我不确定在该怎么做。

    条件断点和循环

    如果没有可用的信号,将锁或信号量获取包装在增加计数器的循环中,并且仅在计数达到可笑的数量时才停止,您仍然可以使用PDB。例如,假设您有一个锁,怀疑是死锁的一部分:

    lock.acquire() # some lock or semaphore from threading or multiprocessing
    

    这样重写:

    count = 0
    while not lock.acquire(False): # Start a loop that will be infinite if deadlocked
        count += 1
    
        continue # now set a conditional breakpoint here in PDB that will only trigger when
                 # count is a ridiculously large number:
                 # pdb> <filename:linenumber>, count=9999999999
    

    当计数非常大时(希望)指示在那里发生了死锁,应该触发断点。如果您发现在锁定对象似乎未显示死锁时触发该事件,那么您可能需要在循环中插入一小段时间延迟,以免其增加得不那么快。您可能还需要处理断点的触发阈值,以使其在正确的时间触发。在我的示例中,该数字是任意的。

    另一个变体是不使用PDB,而是在计数器变大时有意引发异常,而不是触发断点。如果编写自己的异常类,则可以使用它来捆绑异常中的所有本地信号量/锁定状态,然后在退出脚本之前将其捕获到脚本的顶层以将其打印出来。

    文件指示器

    在不依赖正确计数器的情况下使用死锁循环的另一种方法是改为写入文件:

    import time
    
    while not lock.acquire(False): # Start a loop that will be infinite if deadlocked
        with open('checkpoint_a.txt', 'a') as fo: # open a unique filename
            fo.write("\nHit") # write indicator to file
            time.sleep(3)     # pause for a moment so the file size doesn't explode
    

    现在,让您的程序运行一两分钟。杀死程序并浏览那些“检查点”文件。如果死锁是造成停滞程序的原因,则在其中多次写入“ hit”一词的文件会多次表明导致死锁的锁。

    您可以通过循环打印变量或其他状态信息而不只是常量来扩展此功能。例如,您说您怀疑死锁是在循环中发生的,但不知道它正在进行什么迭代。让此锁定循环转储循环的控制变量或其他状态信息,以标识发生死锁的迭代。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看