显示正在运行的Python应用程序的堆栈跟踪

时间:2008-09-25 08:06:06

标签: python debugging stack-trace traceback

我有这个Python应用程序不时被卡住,我无法找到它。

有没有办法告诉Python解释器向您显示正在运行的确切代码?

某种即时堆栈跟踪?

相关问题:

29 个答案:

答案 0 :(得分:305)

我使用的模块用于这样的情况 - 一个进程将运行很长时间但有时会因未知和不可复制的原因而卡住。它有点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 recipe

答案 1 :(得分:143)

安装信号处理程序的建议很好,我经常使用它。例如,bzr默认情况下安装一个SIGQUIT处理程序,该处理程序调用{​​{1}}以立即将您转到pdb提示符。 (有关详细信息,请参阅bzrlib.breakin模块的源代码。)使用pdb,您不仅可以获取当前堆栈跟踪,还可以检查变量等。

但是,有时我需要调试一个我没有远见来安装信号处理程序的进程。在linux上,你可以将gdb附加到进程并获得带有一些gdb宏的python堆栈跟踪。将http://svn.python.org/projects/python/trunk/Misc/gdbinit放入pdb.set_trace(),然后:

  • 附加gdb:~/.gdbinit gdb -p
  • 获取python堆栈跟踪:PID

不幸的是,这并不完全可靠,但它大部分时间都有效。

最后,附加pystack通常可以让您了解流程正在做什么。

答案 2 :(得分:69)

我几乎总是处理多个线程,主线程通常没有做太多,所以最有趣的是转储所有堆栈(这更像是Java的转储)。以下是基于this blog的实现:

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)

答案 3 :(得分:45)

获取未准备好的 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,其答案中包含指向此的工具,以及其他工具。)

答案 4 :(得分:34)

>>> 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进程来打印堆栈。

答案 5 :(得分:27)

traceback模块有一些很好的功能,其中包括:print_stack:

import traceback

traceback.print_stack()

答案 6 :(得分:22)

您可以尝试faulthandler module。使用pip install faulthandler安装并添加:

import faulthandler, signal
faulthandler.register(signal.SIGUSR1)

在您的计划开始时。然后将SIGUSR1发送到您的进程(例如:kill -USR1 42),以显示标准输出的所有线程的Python回溯。 Read the documentation了解更多选项(例如:登录文件)以及其他显示回溯的方法。

该模块现在是Python 3.3的一部分。对于Python 2,请参阅http://faulthandler.readthedocs.org/

答案 7 :(得分:20)

在这里真正帮助我的是spiv's tip(我会投票并评论我是否有信誉点)从未准备的 Python进程中获取堆栈跟踪。除非它在我modified the gdbinit script之前无效。所以:

  • 下载http://svn.python.org/projects/python/trunk/Misc/gdbinit并将其放入~/.gdbinit

  • 编辑它,将PyEval_EvalFrame更改为PyEval_EvalFrameEx [编辑:不再需要;截至2010-01-14]

  • ,链接文件已经有此更改
  • 附加gdb:gdb -p PID

  • 获取python堆栈跟踪:pystack

答案 8 :(得分:12)

我会将此作为对haridsv's response的评论添加,但我没有这样做的声誉:

我们中的一些人仍然坚持使用早于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)

答案 9 :(得分:11)

python -dv yourscript.py

这将使解释器以调试模式运行,并为您提供解释器正在执行的操作的跟踪。

如果你想以交互方式调试代码,你应该像这样运行它:

python -m pdb yourscript.py

告诉python解释器使用模块“pdb”运行你的脚本,这是python调试器,如果你运行它就像解释器将以交互模式执行,就像GDB一样

答案 10 :(得分:10)

查看Python 3.3中的新faulthandler模块。 PyPI上提供了在Python 2中使用的faulthandler backport

答案 11 :(得分:7)

在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.

答案 12 :(得分:6)

如果您使用的是Linux系统,请使用gdb与Python调试扩展程序的强大功能(可以在python-dbgpython-debuginfo包中)。它还有助于多线程应用程序,GUI应用程序和C模块。

使用以下命令运行程序:

$ gdb -ex r --args python <programname>.py [arguments]

这会指示gdb准备python <programname>.py <arguments>r un。

现在当程序挂起时,切换到gdb控制台,按 Ctr + C 并执行:

(gdb) thread apply all py-list

请参阅example session以及更多信息herehere

答案 13 :(得分:6)

我正在寻找一段时间来调试我的线程,我在这里找到了感谢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)

根据我的需要,我也按名称过滤线程。

答案 14 :(得分:4)

可以通过出色的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)  

答案 15 :(得分:3)

我将一些工具附加到正在运行的Python进程中并注入一些代码来获取Python shell。

见这里:https://github.com/albertz/pydbattach

答案 16 :(得分:3)

值得查看Pydb,“松散地基于gdb命令集的Python调试器的扩展版本”。它包括信号管理器,它可以在发送指定信号时负责启动调试器。

2006年夏季代码项目着眼于在名为mpdb的模块中向pydb添加远程调试功能。

答案 17 :(得分:2)

pyringe是一个调试器,可以在不进行任何先验设置的情况下与正在运行的python进程,打印堆栈跟踪,变量等进行交互。

虽然过去我经常使用信号处理程序解决方案,但在某些环境中重现问题通常仍然很困难。

答案 18 :(得分:1)

您可以使用带有curses接口的Python调试器PuDB来执行此操作。只需添加

from pudb import set_interrupt_handler; set_interrupt_handler()

到您的代码并在想要破解时使用Ctrl-C。您可以继续使用c,如果您错过了并再次尝试,可以多次再次中断。

答案 19 :(得分:1)

没有办法挂钩到正在运行的python进程并获得合理的结果。如果进程锁定,我所做的就是挂钩并试图找出究竟发生了什么。

不幸的是,通常strace是“修复”竞争条件的观察者,因此输出也没用。

答案 20 :(得分:1)

如何在控制台中调试任何功能

创建函数,在其中使用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) 

调试愉快:)

答案 21 :(得分:1)

您可以使用hypno软件包,如下所示:

hypno <pid> "import traceback; traceback.print_stack()"

这会将堆栈跟踪信息打印到程序的标准输出中。

或者,如果您不想打印任何东西到stdout,或者您无权访问它(例如,守护程序),则可以使用madbg软件包,它是一个Python调试器,可以允许您附加到正在运行的python程序并在当前终端中对其进行调试。它与pyrasitepyringe类似,但是更新,不需要gdb,并且将IPython用于调试器(表示颜色和自动完成功能)。

要查看正在运行的程序的堆栈跟踪,可以运行:

madbg attach <pid>

然后在调试器外壳中输入: bt

免责声明-我写了两个包

答案 22 :(得分:0)

使用检查模块。

  
    
      

导入检查       帮助(inspect.stack)       模块检查中的函数堆栈帮助:

    
  

栈(上下文= 1)     返回调用者帧上方的堆栈记录列表。

我发现它确实非常有用。

答案 23 :(得分:0)

在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。

答案 24 :(得分:0)

如果您需要使用uWSGI执行此操作,则内​​置Python Tracebacker并且只需在配置中启用它(数字附加到每个工作人员的名称):< / p>

py-tracebacker=/var/run/uwsgi/pytrace

完成此操作后,只需连接到套接字即可打印回溯:

uwsgi --connect-and-read /var/run/uwsgi/pytrace1

答案 25 :(得分:0)

我在使用python扩展的GDB阵营。关注https://wiki.python.org/moin/DebuggingWithGdb,这意味着

  1. dnf install gdb python-debuginfosudo apt-get install gdb python2.7-dbg
  2. gdb python <pid of running process>
  3. py-bt
  4. 另请考虑info threadsthread apply all py-bt

答案 26 :(得分:0)

我不知道与java's response to SIGQUIT类似的任何内容,因此您可能需要将其构建到您的应用程序中。也许你可以在另一个线程中创建一个服务器,可以在响应某种消息时获得堆栈跟踪?

答案 27 :(得分:0)

在代码运行时,您可以插入此小片段,以查看格式良好的打印堆栈跟踪。假定您在项目的根目录中有一个名为logs的文件夹。

# DEBUG: START DEBUG -->
import traceback

with open('logs/stack-trace.log', 'w') as file:
    traceback.print_stack(file=file)
# DEBUG: END DEBUG --!

答案 28 :(得分:0)

如果您想查看正在运行的 Python 应用程序以查看类似 top 的“实时”调用堆栈,您可以使用 austin-tui (https://github.com/p403n1x87/austin-tui)。你可以从 PyPI 安装它,例如

<LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="80dp"
        android:orientation="vertical">

请注意,它需要 austin 二进制文件才能工作 (https://github.com/p403n1x87/austin),但随后您可以使用

附加到正在运行的 Python 进程
pipx install austin-tui