显示运行中Python应用程序的堆栈跟踪
我有一个Python应用程序,它有时会卡住,我不知道在哪里 是否有任何方法向Python解释器发送信号,向您显示正在运行的确切代码 某种即时跟踪 相关问题:显示运行中Python应用程序的堆栈跟踪,python,debugging,stack-trace,traceback,Python,Debugging,Stack Trace,Traceback,我有一个Python应用程序,它有时会卡住,我不知道在哪里 是否有任何方法向Python解释器发送信号,向您显示正在运行的确切代码 某种即时跟踪 相关问题: python-dv yourscript.py 这将使解释器在调试模式下运行,并为您提供解释器正在执行的操作的跟踪 如果要以交互方式调试代码,应按如下方式运行: python-m pdb yourscript.py 这告诉python解释器使用模块“pdb”运行脚本,该模块是python调试器,如果您像GDB一样以交互模式运行解释器,
import traceback
traceback.print_stack()
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)
添加到应用程序中的启动代码。然后,您可以通过将
SIGUSR1
发送到正在运行的Python进程来打印堆栈。我不知道与此类似的任何内容,因此您可能需要将其构建到应用程序中。也许您可以在另一个线程中创建一个服务器,该服务器可以在响应某种消息时获得stacktrace?无法连接到正在运行的python进程并获得合理的结果。如果进程被锁定,我所做的就是将strace连接起来,并试图弄清楚到底发生了什么
不幸的是,strace通常是“修复”竞争条件的观察者,因此输出在那里也没有用处。我有一个模块,用于类似这样的情况——进程将运行很长时间,但有时会因为未知和不可恢复的原因而卡住。它有点黑客,只在unix上工作(需要信号): 要使用它,只需在程序启动时调用listen()函数(您甚至可以将它粘贴到site.py中,让所有python程序都使用它),然后让它运行。在任何时候,都可以使用kill或python向进程发送SIGUSR1信号:
os.kill(pid, signal.SIGUSR1)
这将导致程序在其当前所在的位置断开到python控制台,向您显示堆栈跟踪,并允许您操作变量。使用control-d(EOF)继续运行(不过请注意,您可能会在发送信号时中断任何I/O等,因此它不是完全非侵入性的
我有另一个脚本做同样的事情,除了它通过管道与正在运行的进程通信(允许调试后台进程等)。在这里发布有点大,但我将其添加为一个。安装信号处理程序的建议很好,我经常使用它。例如,默认情况下,安装一个调用
pdb.set_trace()
的SIGQUIT处理程序,以立即将您放入提示中。(有关确切细节,请参阅模块的源代码。)使用pdb,您不仅可以获取当前堆栈跟踪(使用(w)here
命令),还可以检查变量等
但是,有时我需要调试一个我没有预见到要在其中安装信号处理程序的进程。在linux上,您可以将gdb附加到该进程,并使用一些gdb宏获取python堆栈跟踪。放入~/.gdbinit
,然后:
- 附加gdb:
gdb-p
PID
- 获取python堆栈跟踪:
pystack
strace
通常可以让您很好地了解进程在做什么。值得一看,“一个松散地基于gdb命令集的Python调试器扩展版本”。它包括信号管理器,可以在发送指定信号时启动调试器
2006年夏天的一个代码项目研究了在一个名为的模块中为pydb添加远程调试功能。真正帮助我的是(如果我有声望的话,我会投票并发表评论)从一个未准备好的Python进程中获取堆栈跟踪。除了它在我之前不起作用。因此:
- 下载并放入
~/.gdbinit
- 编辑它,将
更改为PyEval\u EvalFrame
[编辑:不再需要;链接文件自2010年1月14日起已经有此更改]PyEval\u EvalFrameEx
- 附加gdb:
gdb-p PID
- 获取python堆栈跟踪:
pystack
我发现它确实非常有用。我几乎总是处理多个线程,而主线程通常做得不多,因此最有趣的是转储所有堆栈(这更像Java的转储)。下面是一个基于以下内容的实现:
我想补充这一点作为评论,但我缺乏这样做的声誉: 我们中的一些人仍然坚持使用早于2.6的Python版本(Thread.ident需要),因此我让代码在Python 2.5中工作(尽管没有显示线程名称),如下所示:
在Solaris上,您可以使用pstack(1),不需要对python代码进行任何更改
# 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.
#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(等待)]
[/usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:242(block)]
[/usr/lib/python2.6/vendor-packages/cherrypy/_-init_uu.py:249(快速启动)]
[/usr/lib/pkg.depotd:890()]
[/usr/lib/python2.6/threading.py:256(等待)]
[/usr/lib/python2.6/Queue.py:177(get)]
[/usr/lib/python2.6/vendor-packages/pkg/server/depot.py:2142(运行)]
[/usr/lib/python2.6/threading.py:477(
os.kill(pid, signal.SIGUSR1)
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)
import traceback
import sys
def dumpstacks(signal, frame):
code = []
for threadId, stack in sys._current_frames().items():
code.append("\n# 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 "\n".join(code)
import signal
signal.signal(signal.SIGQUIT, dumpstacks)
# 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.
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)
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
from pudb import set_interrupt_handler; set_interrupt_handler()
$ gdb -ex r --args python <programname>.py [arguments]
(gdb) thread apply all py-list
import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))
$ 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
py-tracebacker=/var/run/uwsgi/pytrace
uwsgi --connect-and-read /var/run/uwsgi/pytrace1
$ 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 <pid> "import traceback; traceback.print_stack()"
madbg attach <pid>