我有这个Python应用程序,它有时会卡住,我找不到位置。
有什么方法可以让Python解释器向您显示正在运行的确切代码吗?
某种动态堆栈跟踪?
相关问题:
我有这个Python应用程序,它有时会卡住,我找不到位置。
有什么方法可以让Python解释器向您显示正在运行的确切代码吗?
某种动态堆栈跟踪?
相关问题:
Answers:
我有用于以下情况的模块-进程将长时间运行,但有时由于未知且不可复制的原因而卡住。它有点hacky,并且只能在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食谱。
faulthandler
模块(及其在PyPI上的反向端口)用于C级信号处理程序,该处理程序将打印Python堆栈,而无需解释器循环进行响应。
安装信号处理程序的建议是一个不错的建议,我经常使用它。例如,默认情况下,bzr安装一个SIGQUIT处理程序,该处理程序pdb.set_trace()
将立即调用以将您放入pdb提示符。(有关详细信息,请参见bzrlib.breakin模块的源代码。)使用pdb,您不仅可以获取当前的堆栈跟踪信息,还可以检查变量等。
但是,有时我需要调试一个没有先见之明的进程来安装信号处理程序。在linux上,您可以将gdb附加到该进程并获取带有某些gdb宏的python堆栈跟踪。将http://svn.python.org/projects/python/trunk/Misc/gdbinit放在中~/.gdbinit
,然后:
gdb -p
PID
pystack
不幸的是,它并不是完全可靠的,但是大多数情况下它都可以工作。
最后,附加strace
通常可以使您很好地了解流程在做什么。
python-dbg
)。没有这些符号,您似乎看不到很多有用的信息。
Unable to locate python frame
到每个命令
我几乎总是与多个线程打交道,而主线程通常不会做很多事情,所以最有趣的是转储所有堆栈(这更像Java的转储)。这是基于此博客的实现:
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
(有关@Albert的提示,以及其他工具,其答案中包含指向此的指针。)
dump_stacks.py
只是import traceback; traceback.print_stack()
traceback -l
为您提供了可以使用的预定义python脚本列表,并且dump_stacks.py
是其中之一。如果您使用自己的名称(例如,将堆栈跟踪信息写入文件),则最好使用其他名称。
apt-get install gdb python-dbg
在运行吡site石之前先运行(或等效方法),否则它将无声地失败。否则就像魅力!
>>> import traceback
>>> def x():
>>> print traceback.extract_stack()
>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]
您还可以很好地格式化堆栈跟踪,请参阅docs。
编辑:要模拟Java的行为,如@Douglas Leeder所建议,请添加以下内容:
import signal
import traceback
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
应用程序中的启动代码。然后,您可以通过发送SIGUSR1
到正在运行的Python进程来打印堆栈。
该回溯模块有一些不错的功能,其中包括:print_stack:
import traceback
traceback.print_stack()
import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
您可以尝试使用Faulthandler模块。使用安装pip install faulthandler
并添加:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
在程序开始时。然后将SIGUSR1发送到您的进程(例如:)以kill -USR1 42
显示所有线程到标准输出的Python追溯。阅读文档以了解更多选项(例如:登录文件)和其他显示回溯的方式。
该模块现在是Python 3.3的一部分。对于Python 2,请参见http://faulthandler.readthedocs.org/
真正帮助我的是spiv的技巧(如果我没有信誉点,我会投票并提出评论),以获取未经准备的 Python进程的堆栈跟踪。直到我修改了gdbinit脚本才起作用。所以:
下载http://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.
我会将其添加为haridsv的评论,但我缺乏这样做的声誉:
我们中有些人仍然停留在2.6之前的python版本(Thread.ident必需)上,因此我得到的代码在Python 2.5中工作(尽管未显示线程名称),如下所示:
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)
python -dv yourscript.py
这将使解释器以调试模式运行,并为您提供解释器正在执行的操作的轨迹。
如果要交互式调试代码,则应按以下方式运行它:
python -m pdb yourscript.py
这告诉python解释器使用模块“ pdb”(即python调试器)运行脚本,如果您像这样运行解释器,则解释器将以交互模式执行,就像GDB一样
看一下faulthandler
Python 3.3中的新模块。一个faulthandler
反向移植了在Python 2使用可PyPI上。
在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
执行相同的操作
如果您使用的是Linux系统,请结合使用gdb
Python调试扩展(可以在包装中python-dbg
或python-debuginfo
打包中)使用。它还对多线程应用程序,GUI应用程序和C模块有帮助。
使用以下命令运行程序:
$ gdb -ex r --args python <programname>.py [arguments]
这指示gdb
准备python <programname>.py <arguments>
和r
取消准备。
现在,当程序挂起时,切换到gdb
控制台,按Ctr+C并执行:
(gdb) thread apply all py-list
我一直在寻找调试我的线程的解决方案,由于使用haridsv,我在这里找到了它。我使用采用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)
对于我的需求,我还按名称过滤线程。
我一起破解了一些附加到正在运行的Python进程中的工具,并注入了一些代码来获取Python Shell。
pyrasite
效果很好!
可以使用出色的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)
pyringe是一个调试器,可以与正在运行的python进程,打印堆栈跟踪,变量等进行交互,而无需任何先验设置。
尽管我过去经常使用信号处理程序解决方案,但在某些环境中重现问题仍然常常很困难。
pyrasite
对我来说就像一个魅力。
没有办法挂接到正在运行的python进程中并获得合理的结果。如果进程锁定,我该怎么做就挂勾strace并试图弄清楚到底发生了什么。
不幸的是,strace经常是观察者“修复”竞态条件,因此输出在那里也无用。
我在python扩展的GDB阵营中。跟随https://wiki.python.org/moin/DebuggingWithGdb,这意味着
dnf install gdb python-debuginfo
要么 sudo apt-get install gdb python2.7-dbg
gdb python <pid of running process>
py-bt
同时考虑info threads
和thread apply all py-bt
。
Traceback (most recent call first): Python Exception <class 'gdb.error'> No frame is currently selected.: Error occurred in Python command: No frame is currently selected.
在跑步py-bt
中一样得到响应是否正常gdb
?
sudo
。我也需要以gdb pyton <pid>
sudo 运行。
我不知道任何类似于Java对SIGQUIT的响应,因此您可能必须将其内置到应用程序中。也许您可以在另一个线程中创建服务器,以便在响应某种消息时获得堆栈跟踪?
在Python 3中,第一次在调试器中使用c(ont(inue))时,pdb将自动安装信号处理程序。之后按Control-C将使您回到原来的位置。在Python 2中,这是一个单行代码,即使在相对较旧的版本中也可以使用(在2.7中进行了测试,但我检查了Python源代码回到2.4,看起来还可以):
import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))
如果您花费大量时间调试Python,pdb值得学习。该界面有点晦涩难懂,但使用过类似工具(例如gdb)的任何人都应该熟悉。
如果您需要使用uWSGI进行此操作,它内置了Python Tracebacker,只需在配置中启用它即可(每个工作人员的姓名均附有数字):
py-tracebacker=/var/run/uwsgi/pytrace
完成此操作后,只需连接到套接字即可打印回溯:
uwsgi --connect-and-read /var/run/uwsgi/pytrace1