我有这个Python应用程序不时被卡住,我无法找到在哪里.
有没有办法告诉Python解释器向您显示正在运行的确切代码?
某种即时堆栈跟踪?
相关问题:
从Python代码中的方法打印当前调用堆栈
检查正在运行的进程正在做什么:打印未经检测的Python程序的堆栈跟踪
Brian.. 306
我有这样的情况下使用的模块 - 一个进程将运行很长时间,但有时会因未知和不可复制的原因而卡住.它有点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 cookbook配方.
我有这样的情况下使用的模块 - 一个进程将运行很长时间,但有时会因未知和不可复制的原因而卡住.它有点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 cookbook配方.
安装信号处理程序的建议很好,我经常使用它.例如,bzr默认安装一个SIGQUIT处理程序,该处理程序调用pdb.set_trace()
立即将您放入pdb提示符.(有关详细信息,请参阅bzrlib.breakin模块的源代码.)使用pdb,您不仅可以获取当前堆栈跟踪,还可以检查变量等.
但是,有时我需要调试一个我没有远见来安装信号处理程序的进程.在linux上,你可以将gdb附加到进程并获得带有一些gdb宏的python堆栈跟踪.将http://svn.python.org/projects/python/trunk/Misc/gdbinit放入~/.gdbinit
,然后:
附上gdb: gdb -p
PID
获取python堆栈跟踪: 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() [('', 1, ' ', None), (' ', 2, 'x', None)]
您还可以很好地格式化堆栈跟踪,请参阅文档.
编辑:为了模拟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()
您可以尝试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-01-14,链接文件已经有此更改]PyEval_EvalFrame
改为PyEval_EvalFrameEx
附上gdb: gdb -p PID
获取python堆栈跟踪: pystack
我想将此作为对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 () ] [ /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调试扩展的强大功能(可以在python-dbg
或python-debuginfo
包中).它还有助于多线程应用程序,GUI应用程序和C模块.
运行程序:
$ gdb -ex r --args python.py [arguments]
这指示gdb
准备python
和r
解除它.
现在当程序挂起时,切换到gdb
控制台,按下Ctr+C并执行:
(gdb) thread apply all py-list
请参阅示例会话以及此处和此处的更多信息.