我有一个Python应用程序,它不时卡住,我不知道在哪里。

是否有任何方法向Python解释器发出信号,以显示正在运行的确切代码?

某种飞行中的堆叠痕迹?

相关问题:

从Python代码中的方法打印当前调用堆栈 检查正在运行的进程正在做什么:打印未检测的Python程序的堆栈跟踪


当前回答

使用inspect模块。

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

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

我发现它确实很有帮助。

其他回答

我想对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)

我几乎总是处理多线程,主线程通常不做太多,所以最有趣的是转储所有堆栈(这更像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)

如果您使用的是Linux系统,请使用出色的gdb和Python调试扩展(可以在Python -dbg或Python -debuginfo包中)。它还有助于多线程应用程序、GUI应用程序和C模块。

使用以下命令运行程序:

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

这指示gdb准备python <programname>.py <arguments>并运行它。

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

(gdb) thread apply all py-list

参见示例会话和更多信息在这里和这里。

在这里真正帮助我的是spiv的技巧(如果我有声望分数,我会投票并评论),即从一个未准备好的Python进程中获得堆栈跟踪。除非我修改了gdbinit脚本,否则它无法工作。所以:

下载https://svn.python.org/projects/python/trunk/Misc/gdbinit并将其放在~/.gdbinit中 编辑它,将PyEval_EvalFrame更改为PyEval_EvalFrameEx [edit:不再需要;链接的文件在2010-01-14已经有此更改] 附加gdb: gdb -p PID 获取python堆栈跟踪:pystack

用优秀的间谍技术就能做到。它是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)