我有一个Python脚本,怀疑存在死锁。我尝试使用pdb
进行调试,但如果我逐步执行,它不会出现死锁,并且通过返回的输出可以看到它没有在同一次迭代中被挂起。我希望只在脚本被锁定时将其附加到调试器上,这可能吗?如果需要,我可以使用其他调试器。
我有一个Python脚本,怀疑存在死锁。我尝试使用pdb
进行调试,但如果我逐步执行,它不会出现死锁,并且通过返回的输出可以看到它没有在同一次迭代中被挂起。我希望只在脚本被锁定时将其附加到调试器上,这可能吗?如果需要,我可以使用其他调试器。
目前,pdb无法在运行程序时停止并开始调试。但您有几个其他选项:
GDB
您可以使用GDB在C级别进行调试。虽然这比在实际的Python脚本中操作要抽象一些,因为您需要浏览Python的C源代码,但在某些情况下这很有用。这里提供了说明:https://wiki.python.org/moin/DebuggingWithGdb。它们过于复杂,无法在此总结。
第三方扩展和模块
只需搜索“pdb attach process”,就会发现一些项目可以为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上,可以使用信号,像这篇博客文章中所述的那样,尝试停止并附加到正在运行的脚本。
这段引用块直接来自链接的博客文章:
Of course pdb has already got functions to start a debugger in the middle of your program, most notably pdb.set_trace(). This however requires you to know where you want to start debugging, it also means you can't leave it in for production code.
But I've always been envious of what I can do with GDB: just interrupt a running program and start to poke around with a debugger. This can be handy in some situations, e.g. you're stuck in a loop and want to investigate. And today it suddenly occurred to me: just register a signal handler that sets the trace function! Here the proof of concept code:
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()
Now I can send SIGUSR1 to the running application and get a debugger. Lovely!
I imagine you could spice this up by using Winpdb to allow remote debugging in case your application is no longer attached to a terminal. And the other problem the above code has is that it can't seem to resume the program after pdb got invoked, after exiting pdb you just get a traceback and are done (but since this is only bdb raising the bdb.BdbQuit exception I guess this could be solved in a few ways). The last immediate issue is running this on Windows, I don't know much about Windows but I know they don't have signals so I'm not sure how you could do this there.
如果你没有信号可用,可以将锁或信号量获取包装在一个循环中,递增计数器,并在计数达到非常大的数字时才停止,这样你仍然可以使用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
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
PyCharm
,只是想更改 Python logging
模块的日志级别... 等了一两分钟进程才显示为“暂停”,但然后我在调试器中看到了一些变量... 打开代码解释器,粘贴、执行,然后就完成了... 重新启动进程后我的调试日志就出现了! - nmz787有一个 pdb 的克隆版,被称为 pdb-clone,可以附加到正在运行的进程。
你只需在主进程的代码中添加 from pdb_clone import pdbhandler; pdbhandler.register()
,然后就可以使用 pdb-attach --kill --pid PID
启动 pdb。
ModuleNotFoundError: No module named 'readline'
-> python -m pip install readline
-> error: this module is not meant to work on Windows
-> python -m pip install pyreadline
-> AttributeError: module 'signal' has no attribute 'SIGUSR1'
- Andrypyrasite
和pyringe
,但支持Python3,不需要gdb,并使用IPython
作为调试器(这意味着有颜色和自动完成的pdb)。madbg attach <pid>
在调试器 shell 中输入:
bt
如果您没有launch.json文件,只需开始调试(F5),然后您将看到以下选项。
选择“使用进程ID附加”将添加以下内容到您的launch.json中。
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Attach using Process Id",
"type": "python",
"request": "attach",
"processId": "${command:pickProcess}",
},
}
>>> pyrasite 172483 dump_stacks.py
...其中172483是正在运行的Python进程的PID。然后,Python进程将为每个线程打印堆栈跟踪。你可以发送任意Python代码来执行或者打开一个shell。
这对于调试死锁非常有用。你甚至可以在挂起的进程启动后安装pyrasite。但请注意,你应该在相同的环境中安装它才能使其正常工作。
这不是唯一可用的工具,但由于某种原因,它似乎很难无意中发现它。它虽旧,但对于Python 2和3仍然像魔法一样有效。
此工具可能不支持win32,像大多数注入器一样使用unix头文件进行本机C函数,参见例如此开放问题。