我有一个Python应用程序,它会不时地卡住,而我找不到原因。
有没有办法向Python解释器发出信号来显示正在运行的代码?
类似实时调用栈的东西?
相关问题:
我有一个Python应用程序,它会不时地卡住,而我找不到原因。
有没有办法向Python解释器发出信号来显示正在运行的代码?
类似实时调用栈的东西?
相关问题:
我有一个模块可以用于这种情况——当一个进程将长时间运行但有时因未知和无法重现的原因而停滞不前。它有点粗糙,并且仅适用于Unix(需要信号):
import code, traceback, signal
def debug(sig, frame):
"""Interrupt running process, and provide a python prompt for
interactive debugging."""
d={'_frame':frame} # Allow access to frame object.
d.update(frame.f_globals) # Unless shadowed by global
d.update(frame.f_locals)
i = code.InteractiveConsole(d)
message = "Signal received : entering python shell.\nTraceback:\n"
message += ''.join(traceback.format_stack(frame))
i.interact(message)
def listen():
signal.signal(signal.SIGUSR1, debug) # Register handler
使用时,只需在程序启动时的某个时间点调用listen()函数(甚至可以将其放入site.py中,让所有Python程序都使用它),然后让它运行。 在任何时候,使用kill或在Python中发送SIGUSR1信号给进程:
os.kill(pid, signal.SIGUSR1)
这会导致程序在当前点中断,并进入Python控制台,显示堆栈跟踪,让您操作变量。使用control-d(EOF)继续运行(请注意,您可能会在发出信号的点中断任何I/O等,因此它并不完全是非侵入性的)。
我还有另一个脚本,它实现了相同的功能,但通过管道与正在运行的进程通信(以允许调试后台进程等)。这个脚本太长了,无法在此处发布,但我已将其添加为Python cookbook recipe。
faulthandler
模块(以及其在 PyPI 上找到的后移版本)进行 C 级别的信号处理程序,它将打印 Python 堆栈而无需使解释器循环处于响应状态。 - gpspdb.set_trace()
,并立即将您带入pdb提示符中。(有关详细信息,请参见bzrlib.breakin模块的源代码。)借助pdb,您不仅可以获取当前堆栈跟踪(使用(w)here
命令),还可以检查变量等。
然而,有时我需要调试一个我没有预先安装信号处理程序的进程。在Linux上,您可以将gdb附加到该进程,并使用一些gdb宏获取python堆栈跟踪。将http://svn.python.org/projects/python/trunk/Misc/gdbinit放入~/.gdbinit
中,然后执行以下步骤:
gdb -p
PID
pystack
不幸的是,这并非总是可靠的,但它大多数时候都有效。还可以参见https://wiki.python.org/moin/DebuggingWithGdb
最后,附加strace
通常可以让您很好地了解进程正在执行什么操作。
python-dbg
)。如果没有这些符号,您似乎获得的信息不太有用。 - drevicko我几乎总是要处理多个线程,主线程通常没有做太多事情,所以最有趣的是转储所有堆栈(更像Java的dump)。这是基于这篇博客的实现:
import threading, sys, traceback
def dumpstacks(signal, frame):
id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId))
for filename, lineno, name, line in traceback.extract_stack(stack):
code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
if line:
code.append(" %s" % (line.strip()))
print("\n".join(code))
import signal
signal.signal(signal.SIGQUIT, dumpstacks)
使用pyrasite可以在没有调试符号的标准Python环境中获取未经准备的Python程序的堆栈跟踪。在Ubuntu Trusty上对我很有效。
$ sudo pip install pyrasite
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
$ sudo pyrasite 16262 dump_stacks.py # dumps stacks to stdout/stderr of the python program
< p >(向@Albert致敬,他的答案中包含了指向此工具以及其他工具的指针。)dump_stacks.py
只是一个简单的 import traceback; traceback.print_stack()
。 - John Lehmanntraceback -l
会给你提供一份预定义的Python脚本列表,其中之一便是 dump_stacks.py
。如果你使用自己的脚本(例如将堆栈跟踪写入文件),最好使用不同的名称。 - johndodoapt-get install gdb python-dbg
(或等效命令),否则它将悄无声息地失败。除此之外,它的功能非常好! - johndodo>>> import traceback
>>> def x():
>>> print traceback.extract_stack()
>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]
您也可以优美地格式化堆栈跟踪,参见文档。
编辑:为了模拟Java的行为,如@Douglas Leeder所建议的那样,添加以下内容:
import signal
import traceback
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
将以下代码添加到您应用程序的启动代码中,然后发送SIGUSR1
到正在运行的Python进程即可打印堆栈信息。
traceback 模块有一些不错的函数,其中包括:print_stack:
import traceback
traceback.print_stack()
import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
- GuruM您可以尝试使用faulthandler模块。使用pip install faulthandler
安装它,然后添加:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
在你的程序开头导入faulthandler模块。然后向你的进程发送SIGUSR1信号(例如:kill -USR1 42
),以将所有线程的Python traceback显示到标准输出。 阅读文档 以获取更多选项(例如:记录到文件)和其他显示traceback的方法。
该模块现已成为Python 3.3的一部分。对于Python 2,请参见http://faulthandler.readthedocs.org/.
这里真正帮助了我解决问题的是 spiv的提示(如果我有声望分数,我会为此点赞并评论),可以从一个未准备好的Python进程中获取堆栈跟踪。但在我修改gdbinit脚本之前它是不起作用的。所以:
下载 https://svn.python.org/projects/python/trunk/Misc/gdbinit 并将其放入 ~/.gdbinit
编辑它,将 [编辑:不再需要;截至2010年1月14日,链接的文件已经具有此更改]PyEval_EvalFrame
更改为 PyEval_EvalFrameEx
附加 gdb:gdb -p PID
获取Python堆栈跟踪:pystack
No symbol "co" in current context.
的结果。 - Nickolay使用出色的py-spy可以完成。它是Python程序的抽样性能分析器,因此它的工作是连接到Python进程并对其调用堆栈进行采样。因此,py-spy dump --pid $SOME_PID
就足以转储$SOME_PID
进程中所有线程的调用堆栈。通常需要提升权限(以读取目标进程的内存)。
以下是一个多线程Python应用程序的示例。
$ sudo py-spy dump --pid 31080
Process 31080: python3.7 -m chronologer -e production serve -u www-data -m
Python v3.7.1 (/usr/local/bin/python3.7)
Thread 0x7FEF5E410400 (active): "MainThread"
_wait (cherrypy/process/wspbus.py:370)
wait (cherrypy/process/wspbus.py:384)
block (cherrypy/process/wspbus.py:321)
start (cherrypy/daemon.py:72)
serve (chronologer/cli.py:27)
main (chronologer/cli.py:84)
<module> (chronologer/__main__.py:5)
_run_code (runpy.py:85)
_run_module_as_main (runpy.py:193)
Thread 0x7FEF55636700 (active): "_TimeoutMonitor"
run (cherrypy/process/plugins.py:518)
_bootstrap_inner (threading.py:917)
_bootstrap (threading.py:885)
Thread 0x7FEF54B35700 (active): "HTTPServer Thread-2"
accept (socket.py:212)
tick (cherrypy/wsgiserver/__init__.py:2075)
start (cherrypy/wsgiserver/__init__.py:2021)
_start_http_thread (cherrypy/process/servers.py:217)
run (threading.py:865)
_bootstrap_inner (threading.py:917)
_bootstrap (threading.py:885)
...
Thread 0x7FEF2BFFF700 (idle): "CP Server Thread-10"
wait (threading.py:296)
get (queue.py:170)
run (cherrypy/wsgiserver/__init__.py:1586)
_bootstrap_inner (threading.py:917)
_bootstrap (threading.py:885)
hypno
会产生一个Python回溯,这对于找出问题所在非常有用。 - Kiran Jonnalagaddapython -dv yourscript.py
这将使解释器运行在调试模式下,并给你提供解释器正在执行的跟踪信息。
如果您想要交互式地调试代码,您应该像这样运行它:
python -m pdb yourscript.py
这告诉python解释器使用"pdb"模块来运行您的脚本,"pdb"是python调试器,如果您以这种方式运行它,解释器将以交互模式执行,就像GDB一样。