Showing the stack trace from a running Python application
我有一个python应用程序经常被卡住,我不知道在哪里。
有没有什么方法可以向python解释器发出信号来向您显示正在运行的确切代码?
某种即时的stacktrace?
相关问题:
- 从python代码中的方法打印当前调用堆栈
- 检查正在运行的进程正在做什么:打印未构造的python程序的堆栈跟踪
我有我在这种情况下使用的模块-在这种情况下,一个进程将运行很长时间,但有时会因为未知和不可恢复的原因而卡住。它有点黑客,只在Unix上工作(需要信号):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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. Traceback: " 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信号:
1 | os.kill(pid, signal.SIGUSR1) |
这将导致程序在当前位置中断到Python控制台,向您显示堆栈跟踪,并允许您操作变量。使用control-d(eof)继续运行(不过请注意,在您发出信号的时候,您可能会中断任何I/O等,因此它不是完全非侵入性的。
我有另一个脚本,它做同样的事情,除了通过管道与正在运行的进程通信(允许调试后台进程等)。它有点大,张贴在这里,但我添加它作为一个Python食谱。
安装信号处理器的建议很好,我经常使用它。例如,bzr默认安装一个sigquit处理程序,它调用
但是,有时我需要调试一个我没有预见性的进程来安装信号处理程序。在Linux上,可以将gdb附加到进程中,并使用一些gdb宏获取python堆栈跟踪。把http://svn.python.org/projects/python/trunk/misc/gdbinit放在
- 附gdb:
gdb -p PID 。 - 获取python堆栈跟踪:
pystack 。
不幸的是,它并不完全可靠,但大多数时候都能工作。
最后,附加
我几乎总是处理多个线程,而主线程通常不怎么做,所以最有趣的是转储所有栈(这更像是Java的转储)。以下是基于此博客的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(" # 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" ".join(code) import signal signal.signal(signal.SIGQUIT, dumpstacks) |
获取未准备好的python程序的堆栈跟踪,在没有调试符号的普通python中运行,可以用pyrasite完成。在Ubuntu Trusty上为我工作得很有魅力:
1 2 3 | $ 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 |
(给@albert的帽子贴士,他的答案包括一个指向这个的指针,以及其他工具。)
1 2 3 4 5 6 | >>> import traceback >>> def x(): >>> print traceback.extract_stack() >>> x() [('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)] |
您还可以很好地格式化堆栈跟踪,查看文档。
编辑:为了模拟Java的行为,如@ Douglas Leeder所建议的,添加:
1 2 3 4 | import signal import traceback signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack)) |
应用程序中的启动代码。然后您可以通过发送
回溯模块有一些很好的功能,其中包括:打印堆栈:
1 2 3 | import traceback traceback.print_stack() |
您可以尝试FaultHandler模块。使用
1 2 | import faulthandler, signal faulthandler.register(signal.SIGUSR1) |
在程序开始时。然后将sigusr1发送到您的进程(例如:
该模块现在是Python3.3的一部分。关于python 2,请参见http://faulthandler.readthedocs.org。/
在这里真正帮助我的是SPIV的提示(如果我有信誉点的话,我会投票并评论)从一个未准备好的python进程中获取堆栈跟踪。但直到我修改了gdbinit脚本它才工作。所以:
下载http://svn.python.org/projects/python/trunk/misc/gdbinit,放到
~/.gdbinit 中。编辑它,将PyEval_EvalFrame 更改为PyEval_EvalFrameEx [编辑:不再需要;链接文件已经有了这个更改,从2010-01-14开始]附gdb:EDOCX1[3]
获取python堆栈跟踪:
pystack 。
我会在haridsv的回复中加上一句话作为评论,但我缺乏这样做的声誉:
我们中的一些人仍然坚持使用比2.6更旧的Python版本(thread.ident需要),所以我让代码在Python2.5中工作(尽管没有显示线程名称),如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import traceback import sys def dumpstacks(signal, frame): code = [] for threadId, stack in sys._current_frames().items(): code.append(" # Thread: %d" % (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" ".join(code) import signal signal.signal(signal.SIGQUIT, dumpstacks) |
python-dv yourscript.py版
这将使解释器在调试模式下运行,并为您提供解释器正在执行的操作的跟踪。
如果要以交互方式调试代码,应按如下方式运行:
python-m pdb yourscript.py版
这就告诉python解释器使用模块"pdb"(即python调试器)运行脚本,如果您像这样运行脚本,解释器将以交互模式执行,就像gdb一样。
请看一下Python3.3中新增的
在Solaris上,可以使用pstack(1),不需要更改python代码。如。
1 2 3 4 5 6 7 8 9 10 11 12 | # pstack 16000 | grep : | head 16000: /usr/bin/python2.6 /usr/lib/pkg.depotd --cfg svc:/application/pkg/serv [ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:282 (_wait) ] [ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:295 (wait) ] [ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:242 (block) ] [ /usr/lib/python2.6/vendor-packages/cherrypy/_init_.py:249 (quickstart) ] [ /usr/lib/pkg.depotd:890 (<module>) ] [ /usr/lib/python2.6/threading.py:256 (wait) ] [ /usr/lib/python2.6/Queue.py:177 (get) ] [ /usr/lib/python2.6/vendor-packages/pkg/server/depot.py:2142 (run) ] [ /usr/lib/python2.6/threading.py:477 (run) etc. |
我正在寻找一个解决方案来调试我的线程,我在这里找到了它,感谢haridsv。我使用稍微简化的版本,使用traceback.print_stack():
1 2 3 4 5 6 7 8 9 10 11 12 13 | import sys, traceback, signal import threading import os def dumpstacks(signal, frame): id2name = dict((th.ident, th.name) for th in threading.enumerate()) for threadId, stack in sys._current_frames().items(): print(id2name[threadId]) traceback.print_stack(f=stack) signal.signal(signal.SIGQUIT, dumpstacks) os.killpg(os.getpgid(0), signal.SIGQUIT) |
为了我的需要,我还按名称筛选线程。
如果您使用的是Linux系统,请在python调试扩展(可以在
运行程序时使用:
1 | $ gdb -ex r --args python <programname>.py [arguments] |
这就要求
现在,当程序挂起时,切换到
1 | (gdb) thread apply all py-list |
请在这里和这里查看示例会话和更多信息。
我破解了一些附加到正在运行的Python进程中的工具,并注入了一些代码以获得Python外壳。
请参见:https://github.com/albertz/pydbattach
值得一看的是pydb,"一个基于gdb命令集的python调试器的扩展版本"。它包括信号管理器,可以在发送指定信号时启动调试器。
2006年夏天的一个代码项目研究了在名为mpdb的模块中向pydb添加远程调试功能。
pyrange是一个调试器,它可以与运行python进程、打印堆栈跟踪、变量等交互,而无需预先设置。
虽然在过去我经常使用信号处理程序解决方案,但在某些环境中重现问题仍然很困难。
如何调试控制台中的任何函数:
在使用pdb.set_trace()的地方创建函数,然后创建要调试的函数。
1 2 3 4 5 6 7 | >>> import pdb >>> import my_function >>> def f(): ... pdb.set_trace() ... my_function() ... |
然后调用创建的函数:
1 2 3 4 5 6 | >>> f() > <stdin>(3)f() (Pdb) s --Call-- > <stdin>(1)my_function() (Pdb) |
调试愉快:)
您可以使用pudb,一个带有curses接口的python调试器来执行此操作。只要添加
1 | from pudb import set_interrupt_handler; set_interrupt_handler() |
到您的代码,并在您想要中断时使用ctrl-c。您可以继续使用
无法钩住正在运行的Python进程并获得合理的结果。如果进程锁定,我要做的就是将strace连接起来,并试图找出到底发生了什么。
不幸的是,strace通常是"修复"争用条件的观察者,因此输出在那里也是无用的。
使用检查模块。
import inspect
help(inspect.stack)
Help on function stack in module inspect:< /块引用>< /块引用>
堆栈(上下文=1)返回调用方帧上方堆栈的记录列表。
我觉得这确实很有帮助。
在python 3中,当您第一次在调试器中使用c(ont(inue))时,pdb将自动安装一个信号处理程序。之后按control-c会把你放回那里。在python 2中,这里有一个一行程序,即使在相对较旧的版本中也可以使用(在2.7中测试过,但我将python源代码检查回2.4,它看起来还不错):
1
2 import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))如果您花费大量时间调试Python,那么PDB值得学习。这个接口有点迟钝,但是对于使用类似工具的人来说应该是熟悉的,比如gdb。
我不知道类似于Java对SigDug的任何响应,所以您可能需要将其构建到应用程序中。也许您可以让另一个线程中的服务器对某种类型的消息做出stacktrace响应?
如果您需要使用uwsgi进行此操作,它内置了python tracebacker,只需在配置中启用它(编号附加到每个工作人员的名称上):
1 py-tracebacker=/var/run/uwsgi/pytrace完成此操作后,只需连接到套接字即可打印回溯:
1 uwsgi --connect-and-read /var/run/uwsgi/pytrace1我在gdb阵营中使用了python扩展。遵循https://wiki.python.org/moin/debuggingwithgdb,这意味着
dnf install gdb python-debuginfo 或sudo apt-get install gdb python2.7-dbg 。gdb python py-bt 也可以考虑
info threads 和thread apply all py-bt 。