使用pdb附加进程

Hol*_*dad 29 python debugging pdb

我有一个python脚本,我怀疑有一个死锁.我试图调试,pdb但如果我一步一步地去,它不会得到死锁,并且通过返回的输出我可以看到它没有被挂在同一个迭代上.我想将我的脚本仅在它被锁定时附加到调试器,是否可能?如果有必要,我愿意使用其他调试器.

skr*_*sme 45

此时,pdb无法暂停并开始在正在运行的程序上进行调试.您还有其他一些选择:

GDB

您可以使用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维基的这个页面有几个选择: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()
Run Code Online (Sandbox Code Playgroud)

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

我想你可以通过使用Winpdb来实现远程调试,以防你的应用程序不再连接到终端.上面的代码的另一个问题是,在调用pdb之后似乎无法恢复程序,退出pdb之后你只是得到一个回溯并完成(但是因为这只是bdb引发bdb.BdbQuit异常我猜这可以通过几种方式解决).最后一个直接问题是在Windows上运行它,我对Windows不太了解,但我知道它们没有信号,所以我不知道你怎么能在那里做到这一点.

条件断点和循环

如果您没有可用的信号,如果您将锁或信号量采集包装在一个递增计数器的循环中,您可能仍然可以使用PDB,并且只有在计数达到一个非常大的数字时才停止.例如,假设你有一个锁,你怀疑它是你死锁的一部分:

lock.acquire() # some lock or semaphore from threading or multiprocessing
Run Code Online (Sandbox Code Playgroud)

用这种方式重写:

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
Run Code Online (Sandbox Code Playgroud)

当count非常大时(希望)表明在那里发生了死锁,断点应该触发.如果您发现它在锁定对象似乎没有指示死锁时触发,那么您可能需要在循环中插入一个短时间延迟,因此它不会非常快地增加.您还可能需要使用断点的触发阈值来使其在正确的时间触发.我的例子中的数字是任意的.

另一个变体是不使用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
Run Code Online (Sandbox Code Playgroud)

现在让你的程序运行一两分钟.杀死程序并浏览那些"检查点"文件.如果死锁对您的停滞程序负责,那么在其中写入"hit"一词的文件会指示哪些锁定获取会导致您的死锁.

您可以通过循环打印变量或其他状态信息而不仅仅是常量来扩展其有用性.例如,您说您怀疑死锁是在循环中发生但不知道它在哪个迭代.让这个锁循环转储循环的控制变量或其他状态信息,以识别发生死锁的迭代.


eag*_*ain 13

有一个pdb的克隆,富有想象力地称为pdb-clone,可以附加到正在运行的进程.

您只需添加from pdb_clone import pdbhandler; pdbhandler.register()到主进程的代码,然后就可以启动pdb了pdb-attach --kill --pid PID.

  • @dedObed我已经更新了链接,以指向该项目在GitHub上的新家。感谢您的通知。 (4认同)
  • `ModuleNotFoundError: 没有名为 'readline' 的模块` -&gt; `python -m pip install readline` -&gt; `错误:此模块不适用于 Windows` -&gt; `python -m pip install pyreadline` -&gt; `AttributeError:模块“信号”没有属性“SIGUSR1”` (2认同)

kma*_*ork 11

您可以使用我的项目madbg。它是一个 python 调试器,允许您附加到正在运行的 python 程序并在当前终端中对其进行调试。它类似于pyrasitepyringe,但支持 python3,不需要 gdb,并用于IPython调试器(这意味着带有颜色和自动完成功能的 pdb)。

例如,要查看脚本卡在哪里,您可以运行:

madbg attach <pid>
Run Code Online (Sandbox Code Playgroud)

然后在调试器 shell 中输入: bt


Aid*_*her 7

VSCode 支持调试本地运行的 python 进程。

如果您没有 launch.json 文件,则只需开始调试 (F5),然后您将看到以下选项。

在此输入图像描述

选择“使用进程 ID 附加”会将以下内容添加到您的 launch.json

  {
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Python: Attach using Process Id",
      "type": "python",
      "request": "attach",
      "processId": "${command:pickProcess}",
    },
}
Run Code Online (Sandbox Code Playgroud)

现在,当您使用该配置进行调试时,您可以选择要调试的本地 python 进程。

在此输入图像描述


lum*_*ric 5

使用硫铁矿

>>> pyrasite 172483 dump_stacks.py
Run Code Online (Sandbox Code Playgroud)

...其中 172483 是正在运行的 python 进程的 PID。然后,Python 进程将为每个线程打印堆栈跟踪。人们可以发送任意要执行的 Python 代码或打开 shell。

这对于调试死锁非常有用。人们甚至可以在悬挂过程开始后安装火铁矿。但请注意,您应该将其安装在同一环境中才能使其正常工作。

这不是唯一可用的工具,但由于某种原因,似乎很难意外地绊倒它。它很旧,但对于 Python 2 和 3 来说却很有魅力。

该工具可能不支持 win32,就像大多数使用 unix 头文件来实现本机 C 函数的注入器一样,请参阅此未决问题