我有一个 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 食谱.

其他提示

安装信号处理程序的建议很好,我经常使用它。例如, 布兹尔 默认情况下安装一个 SIGQUIT 处理程序来调用 pdb.set_trace() 立即让你陷入 数据库 迅速的。(参见 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 中运行 没有调试符号 可以用 硫铁矿. 。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进程。

追溯 模块有一些不错的功能,其中:打印堆栈:

import traceback

traceback.print_stack()

您可以尝试 故障处理模块. 。安装它使用 pip install faulthandler 并添加:

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

在你的程序的开始。然后将 SIGUSR1 发送到您的进程(例如: kill -USR1 42) 将所有线程的 Python 回溯显示到标准输出。 阅读文档 更多选项(例如:登录到文件)和其他方式来显示回溯。

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

真正帮助我的是 斯皮夫的提示 (如果我有声誉点,我会投票并评论)从堆栈中获取堆栈跟踪 毫无准备的 Python 进程。但直到我之前它才起作用 修改gdbinit脚本. 。所以:

  • 下载 http://svn.python.org/projects/python/trunk/Misc/gdbinit 并把它放进去 ~/.gdbinit

  • 编辑它,改变 PyEval_EvalFramePyEval_EvalFrameEx [编辑:不再需要;截至 2010 年 1 月 14 日,链接文件已进行此更改]

  • 附上gdb: gdb -p PID

  • 获取 python 堆栈跟踪: pystack

我会将其添加为评论 哈里斯夫的回应, ,但我缺乏这样做的声誉:

我们中的一些人仍然停留在 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 中的新功能。A faulthandler 向后移植 PyPI 上提供了在 Python 2 中使用的功能。

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

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

运行你的程序:

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

这指示 gdb 准备 python <programname>.py <arguments>r单元。

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

(gdb) thread apply all py-list

示例会话 和更多信息 这里这里.

我一直在寻找一个解决方案来调试我的线程,多亏了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)

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

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

2006 年 Summer of Code 项目着眼于在名为 pydb 的模块中添加远程调试功能 MP数据库.

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

看这里: https://github.com/albertz/pydbatach

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

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

没有办法挂钩正在运行的 python 进程并获得合理的结果。如果进程锁定,我要做的就是连接 strace 并尝试找出到底发生了什么。

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

您可以使用 普数据库, ,一个带有curses接口的Python调试器可以做到这一点。只需添加

from pudb import set_interrupt_handler; set_interrupt_handler()

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

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

创建函数 你在哪里 使用 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) 

调试愉快:)

我不知道有什么类似的事情 java对SIGQUIT的响应, ,因此您可能必须将其构建到您的应用程序中。也许您可以在另一个线程中创建一个服务器,该服务器可以获得对某种消息的响应的堆栈跟踪?

使用检查模块。

导入检查帮助(Inspect.Stack)在模块Inspect中的功能堆栈上的帮助:

堆栈(上下文= 1)返回堆栈的记录列表,上方是呼叫者的框架上方。

我发现它确实非常有帮助。

在 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)的任何人都应该熟悉。

如果你需要使用 uWSGI 来做到这一点,它有 Python 回溯器 内置的,只需在配置中启用它即可(数字附加到每个工作人员的名称上):

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

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

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

我属于 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.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top