我有这个 Python 应用程序时不时卡住,我不知道在哪里。
有没有办法向 Python 解释器发出信号以向您显示正在运行的确切代码?
某种即时堆栈跟踪?
相关问题:
我有这个 Python 应用程序时不时卡住,我不知道在哪里。
有没有办法向 Python 解释器发出信号以向您显示正在运行的确切代码?
某种即时堆栈跟踪?
相关问题:
我有用于这种情况的模块 - 一个进程将运行很长时间,但有时会因为未知和不可重现的原因而卡住。它有点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 食谱食谱。
安装信号处理程序的建议很好,我经常使用它。例如,默认情况下bzr会安装一个 SIGQUIT 处理程序,该处理程序会调用pdb.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
不幸的是,它并不完全可靠,但它在大多数情况下都有效。
最后,附加strace
通常可以让您很好地了解进程正在做什么。
我几乎总是在处理多个线程,而主线程通常做的不多,所以最有趣的是转储所有堆栈(这更像是 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)
获取未准备好的 python 程序的堆栈跟踪,在没有调试符号的股票 python 中运行可以使用pyrasite完成。在 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 致敬,他的回答包含指向此的指针以及其他工具。)
>>> 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()
你可以试试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
python -dv yourscript.py
这将使解释器在调试模式下运行,并为您提供解释器正在做什么的踪迹。
如果你想交互式地调试代码,你应该像这样运行它:
python -m pdb yourscript.py
这告诉 python 解释器使用作为 python 调试器的模块“pdb”运行你的脚本,如果你运行它,解释器将以交互模式执行,就像 GDB
我会将此作为评论添加到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)
看看faulthandler
Python 3.3 中的新模块。faulthandler
PyPI上提供了用于 Python 2 的反向移植。
它可以用优秀的 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)
在 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.
我一直在寻找调试线程的解决方案,多亏了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)
为了我的需要,我还按名称过滤线程。
如果您使用的是 Linux 系统,请使用gdb
Python 调试扩展(可以是 inpython-dbg
或python-debuginfo
package)的强大功能。它还有助于多线程应用程序、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
我将一些附加到正在运行的 Python 进程中并注入一些代码以获取 Python shell 的工具组合在一起。
pyringe是一个调试器,可以与正在运行的 python 进程交互、打印堆栈跟踪、变量等,而无需任何先验设置。
虽然我过去经常使用信号处理程序解决方案,但在某些环境中重现问题通常仍然很困难。
从 Austin 3.3 开始,您可以使用该-w/--where
选项发出当前堆栈跟踪。见https://stackoverflow.com/a/70905181/1838793
如果您想查看正在运行的 Python 应用程序以查看类似 top 的 fashon 中的“实时”调用堆栈,您可以使用 austin-tui ( https://github.com/p403n1x87/austin-tui )。您可以从 PyPI 安装它,例如
pipx install austin-tui
请注意,它需要 austin 二进制文件才能工作(https://github.com/p403n1x87/austin),但是您可以使用以下命令附加到正在运行的 Python 进程
austin-tui -p <pid>
您可以使用PuDB,一个带有 curses 接口的 Python 调试器来执行此操作。只需添加
from pudb import set_interrupt_handler; set_interrupt_handler()
到您的代码并在您想要中断时使用 Ctrl-C。如果您错过它并想再试一次,您可以继续c
并再次中断多次。
我在 GDB 阵营中使用 python 扩展。关注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
。
如何调试控制台中的任何功能:
在使用 pdb.set_trace()的地方创建函数,然后创建要调试的函数。
>>> import pdb
>>> import my_function
>>> def f():
... pdb.set_trace()
... my_function()
...
然后调用创建函数:
>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb)
调试愉快:)
您可以使用hypno包,如下所示:
hypno <pid> "import traceback; traceback.print_stack()"
这会将堆栈跟踪打印到程序的标准输出中。
或者,如果您不想将任何内容打印到标准输出,或者您无权访问它(例如守护程序),您可以使用madbg包,它是一个允许您附加到正在运行的 python 调试器python 程序并在您当前的终端中调试它。它类似于pyrasite
and pyringe
,但更新,不需要 gdb,并IPython
用于调试器(这意味着颜色和自动完成)。
要查看正在运行的程序的堆栈跟踪,您可以运行:
madbg attach <pid>
在调试器外壳中,输入:
bt
免责声明 - 我写了两个包
我不知道有什么类似于java 对 SIGQUIT 的响应,因此您可能必须将其构建到您的应用程序中。也许您可以在另一个线程中创建一个服务器,该服务器可以在响应某种消息时获得堆栈跟踪?
没有办法挂钩到正在运行的 python 进程并获得合理的结果。如果进程锁定,我会做些什么来连接 strace 并试图弄清楚到底发生了什么。
不幸的是,通常 strace 是“修复”竞争条件的观察者,因此输出在那里也无用。
使用检查模块。
import inspect help(inspect.stack) 模块检查中函数堆栈的帮助:
stack(context=1) 返回调用者框架上方堆栈的记录列表。
我觉得它确实很有帮助。
在 Python 3 中,pdb 将在您第一次在调试器中使用 c(ont(inue)) 时自动安装信号处理程序。之后按 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
在代码运行的地方,您可以插入这个小片段以查看格式良好的打印堆栈跟踪。它假定您logs
在项目的根目录中有一个名为的文件夹。
# DEBUG: START DEBUG -->
import traceback
with open('logs/stack-trace.log', 'w') as file:
traceback.print_stack(file=file)
# DEBUG: END DEBUG --!