Python 使用 pdb 附加进程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25308847/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Attaching a process with pdb
提问by Hola Soy Edu Feliz Navidad
I have a python script that I suspect that there is a deadlock. I was trying to debug with pdbbut if I go step by step it doesn't get the deadlock, and by the output returned I can see that it's not being hanged on the same iteration. I would like to attach my script to a debugger only when it gets locked, is it possible? I'm open to use other debuggers if necessary.
我有一个 python 脚本,我怀疑存在死锁。我试图调试,pdb但如果我一步一步地进行,它不会陷入死锁,并且通过返回的输出我可以看到它没有被挂在同一个迭代中。我想仅在我的脚本被锁定时将其附加到调试器,这可能吗?如有必要,我愿意使用其他调试器。
采纳答案by skrrgwasme
At this time, pdbdoes not have the ability to halt and begin debugging on a running program. You have a few other options:
此时,pdb无法停止并开始对正在运行的程序进行调试。您还有其他一些选择:
GDB
广交会
You can use GDB to debug at the C level. This is a bit more abstract because you're poking around Python's C source code rather than your actual Python script, but it can be useful for some cases. The instructions are here: https://wiki.python.org/moin/DebuggingWithGdb. They are too involved to summarise here.
您可以使用 GDB 在 C 级别进行调试。这有点抽象,因为您正在浏览 Python 的 C 源代码而不是实际的 Python 脚本,但它在某些情况下可能很有用。说明在这里:https: //wiki.python.org/moin/DebuggingWithGdb。他们太投入了,无法在此总结。
Third-Party Extensions & Modules
第三方扩展和模块
Just googling for "pdb attach process" reveals a couple of projects to give PDB this ability:
Pyringe: https://github.com/google/pyringe
Pycharm: https://blog.jetbrains.com/pycharm/2015/02/feature-spotlight-python-debugger-and-attach-to-process/
This page of the Python wiki has several alternatives: https://wiki.python.org/moin/PythonDebuggingTools
只是为了“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
For your specific use case, I have some ideas for workarounds:
对于您的特定用例,我有一些解决方法的想法:
Signals
信号
If you're on unix, you can use signalslike in this blog postto try to halt and attach to a running script.
如果你是在UNIX上,可以使用的信号在像这样的博客文章,试图制止并连接到正在运行的脚本。
This quote block is copied directly from the linked blog post:
这个引用块是直接从链接的博客文章中复制的:
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 已经有了在程序中间启动调试器的函数,最显着的是 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 了解不多,但我知道它们没有信号,所以我不确定你如何在那里做到这一点。
Conditional Breakpoints and Loops
条件断点和循环
You may still be able to use PDB if you don't have signals available, if you wrap your lock or semaphore acquisitions in a loop that increments a counter, and only halt when the count has reached a ridiculously large number. For example, say you have a lock that you suspect is part of your deadlock:
如果您没有可用的信号,您仍然可以使用 PDB,如果您将锁或信号量获取包装在一个循环中,该循环会增加一个计数器,并且只有在计数达到一个大得离谱的数字时才停止。例如,假设您有一个锁,您怀疑它是死锁的一部分:
lock.acquire() # some lock or semaphore from threading or multiprocessing
Rewrite it this way:
改写成这样:
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
The breakpoint should trigger when when count is very large, (hopefully) indicating that a deadlock has occurred there. If you find that it's triggering when the locking objects don't seem to indicate a deadlock, then you may need to insert a short time delay in the loop so it doesn't increment quite so fast. You also may have to play around with the breakpoint's triggering threshold to get it to trigger at the right time. The number in my example was arbitrary.
当计数非常大时,断点应该触发,(希望)表明那里发生了死锁。如果您发现它在锁定对象似乎没有表示死锁时触发,那么您可能需要在循环中插入一个短时间延迟,以便它不会增加得那么快。您可能还需要调整断点的触发阈值以使其在正确的时间触发。我的例子中的数字是任意的。
Another variant on this would be to not use PDB, and intentionally raise an exception when the counter gets huge, instead of triggering a breakpoint. If you write your own exception class, you can use it to bundle up all of the local semaphore/lock states in the exception, then catch it at the top-level of your script to print out right before exiting.
另一个变体是不使用 PDB,并在计数器变大时故意引发异常,而不是触发断点。如果您编写自己的异常类,您可以使用它来捆绑异常中的所有本地信号量/锁定状态,然后在脚本的顶层捕获它以在退出之前打印出来。
File Indicators
文件指标
A different way you can use your deadlocked loop without relying on getting counters right would be to write to files instead:
您可以使用死锁循环而不依赖于正确获取计数器的另一种方法是写入文件:
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
Now let your program run for a minute or two. Kill the program and go through those "checkpoint" files. If deadlock is responsible for your stalled program, the files that have the word "hit" written in them a bunch of times indicate which lock acquisitions are responsible for your deadlock.
现在让你的程序运行一两分钟。杀死程序并检查那些“检查点”文件。如果死锁是造成程序停滞的原因,那么多次写入“hit”一词的文件表明哪些锁获取是造成死锁的原因。
You can expand the usefullness of this by having the loop print variables or other state information instead of just a constant. For example, you said you suspect the deadlock is happening in a loop but don't know what iteration it's on. Have this lock loop dump your loop's controlling variables or other state information to identify the iteration the deadlock occured on.
您可以通过循环打印变量或其他状态信息而不仅仅是常量来扩展其实用性。例如,你说你怀疑死锁发生在一个循环中,但不知道它在什么迭代上。让这个锁循环转储循环的控制变量或其他状态信息,以识别发生死锁的迭代。
回答by eaglebrain
There is a clone of pdb, imaginatively called pdb-clone, which can attach to a running process.
有一个pdb 的克隆,想象中称为pdb-clone,它可以附加到正在运行的进程。
You simply add from pdb_clone import pdbhandler; pdbhandler.register()to the code for the main process, and then you can start pdb with pdb-attach --kill --pid PID.
您只需添加from pdb_clone import pdbhandler; pdbhandler.register()到主进程的代码中,然后您就可以使用pdb-attach --kill --pid PID.

