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

2025-01-09 08:47:00
admin
原创
99
摘要:问题描述:我的这个 Python 应用程序时不时会卡住,而我却找不到它在哪里。有什么方法可以向 Python 解释器发出信号,让它显示正在运行的确切代码?某种即时堆栈跟踪?相关问题:从 Python 代码中的方法打印当前调用堆栈检查正在运行的进程正在做什么:打印未检测的 Python 程序的堆栈跟踪解决方案 ...

问题描述:

我的这个 Python 应用程序时不时会卡住,而我却找不到它在哪里。

有什么方法可以向 Python 解释器发出信号,让它显示正在运行的确切代码?

某种即时堆栈跟踪?

相关问题:

  • 从 Python 代码中的方法打印当前调用堆栈

  • 检查正在运行的进程正在做什么:打印未检测的 Python 程序的堆栈跟踪


解决方案 1:

我有一个用于此类情况的模块 - 进程将运行很长时间,但有时会因未知且不可重现的原因而卡住。它有点老套,并且仅适用于 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.
Traceback:
"
    message += ''.join(traceback.format_stack(frame))
    i.interact(message)

def listen():
    signal.signal(signal.SIGUSR1, debug)  # Register handler

要使用,只需在程序启动时调用 listen() 函数(您甚至可以将其放在 site.py 中,让所有 python 程序使用它),然后让它运行。在任何时候,使用 kill 向进程发送 SIGUSR1 信号,或者在 python 中:

    os.kill(pid, signal.SIGUSR1)

这将导致程序在当前位置中断到 Python 控制台,向您显示堆栈跟踪,并允许您操作变量。使用 control-d (EOF) 继续运行(但请注意,您可能会在发出信号时中断任何 I/O 等,因此它不是完全非侵入式的。

我有另一个脚本可以做同样的事情,只不过它通过管道与正在运行的进程通信(以便调试后台进程等)。它有点大,不适合在这里发布,但我已将其添加为python 食谱。

解决方案 2:

安装信号处理程序的建议很好,我经常使用它。例如,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:gdb -p PID

  • 获取 Python 堆栈跟踪:pystack

不幸的是,它并不完全可靠,但大多数情况下它都能正常工作。另请参阅https://wiki.python.org/moin/DebuggingWithGdb

最后,附加strace通常可以让您很好地了解某个进程正在做什么。

解决方案 3:

我几乎总是处理多个线程,而主线程通常不会做太多事情,因此最有趣的是转储所有堆栈(这更像 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("
# 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("
".join(code))

import signal
signal.signal(signal.SIGQUIT, dumpstacks)

解决方案 4:

可以使用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 致敬,他的回答包含了指向此内容以及其他工具的指针。)

解决方案 5:

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

解决方案 6:

traceback模块有一些不错的函数,其中包括:print_stack:

import traceback

traceback.print_stack()

解决方案 7:

您可以尝试使用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/

解决方案 8:

可以使用出色的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)  

解决方案 9:

这里真正帮助我的是spiv 的提示(如果我有声誉点,我会投票赞成并评论)用于从未准备好的Python 进程中获取堆栈跟踪。但直到我修改了 gdbinit 脚本后它才起作用。所以:

解决方案 10:

python -dv 你的脚本.py

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

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

python -m pdb 你的脚本.py

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

解决方案 11:

我本想将此作为评论添加到haridsv 的回复中,但我缺乏这样做的声誉:

我们中的一些人仍然停留在 Python 2.6 之前的版本(Thread.ident 所需),因此我让代码在 Python 2.5 中运行(尽管没有显示线程名称),如下所示:

import traceback
import sys
def dumpstacks(signal, frame):
    code = []
    for threadId, stack in sys._current_frames().items():
            code.append("
# 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 "
".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)

解决方案 12:

查看faulthandlerPython 3.3 中的新模块。PyPI上提供了可用于 Python 2 的faulthandler反向移植。

解决方案 13:

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

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

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

或者,如果您不想将任何内容打印到 stdout,或者您无权访问它(例如守护进程),则可以使用madbg包,这是一个 Python 调试器,允许您连接到正在运行的 Python 程序并在当前终端中对其进行调试。它类似于pyrasitepyringe,但更新,不需要 gdb,并且用于IPython调试器(这意味着颜色和自动完成)。

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

madbg attach <pid>

然后在调试器 shell 中输入:
bt

免责声明 - 这两个软件包都是我编写的

解决方案 14:

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

解决方案 15:

如果您使用的是 Linux 系统,请使用gdbPython 调试扩展(可在python-dbgpython-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

请参阅此处和此处的示例会话和更多信息。

解决方案 16:

我花了一段时间寻找一个解决方案来调试我的线程,最后我在这里找到了它,感谢 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)

为了满足我的需要,我还按名称过滤线程。

解决方案 17:

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

请参阅此处:https://github.com/albertz/pydbattach

解决方案 18:

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

2006 年夏季代码项目研究了在名为mpdb 的模块中为 pydb 添加远程调试功能。

解决方案 19:

pyringe是一个调试器,它可以与正在运行的 python 进程交互,打印堆栈跟踪、变量等,无需任何事先设置。

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

解决方案 20:

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

  1. dnf install gdb python-debuginfo或者sudo apt-get install gdb python2.7-dbg

  2. gdb python <pid of running process>

  3. py-bt

还请考虑info threadsthread apply all py-bt

解决方案 21:

从 Austin 3.3 开始,您可以使用该-w/--where选项发出当前堆栈跟踪。请参阅https://stackoverflow.com/a/70905181/1838793

在此处输入图片描述

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

pipx install austin-tui

请注意,它需要 austin 二进制文件才能工作(https://github.com/p403n1x87/austin),但你可以使用以下命令附加到正在运行的 Python 进程:

austin-tui -p <pid>

解决方案 22:

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

from pudb import set_interrupt_handler; set_interrupt_handler()

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

解决方案 23:

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

创建使用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) 

调试愉快:)

解决方案 24:

我不知道有什么类似于Java 对 SIGQUIT 的响应,所以你可能必须将其内置到你的应用程序中。也许你可以在另一个线程中创建一个服务器,该服务器可以在响应某种消息时获取堆栈跟踪?

解决方案 25:

没有办法挂入正在运行的 Python 进程并获得合理的结果。如果进程锁定,我会挂入 strace 并尝试找出到底发生了什么。

不幸的是,strace 通常充当“修复”竞争条件的观察者,因此输出在那里也是无用的。

解决方案 26:

使用检查模块。

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

stack(context=1)返回调用者框架上方堆栈的记录列表。

我发现它确实非常有用。

解决方案 27:

在 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)的人来说应该很熟悉。

解决方案 28:

如果你需要使用 uWSGI 来执行此操作,它有内置的Python Tracebacker,只需在配置中启用它即可(每个工作者的名称都附加有数字):

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

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

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

解决方案 29:

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

# DEBUG: START DEBUG -->
import traceback

with open('logs/stack-trace.log', 'w') as file:
    traceback.print_stack(file=file)
# DEBUG: END DEBUG --!
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用