简体   繁体   English

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

[英]Showing the stack trace from a running Python application

I have this Python application that gets stuck from time to time and I can't find out where.我有这个 Python 应用程序,它不时卡住,我找不到在哪里。

Is there any way to signal Python interpreter to show you the exact code that's running?有什么方法可以向 Python 解释器发出信号以显示正在运行的确切代码?

Some kind of on-the-fly stacktrace?某种即时堆栈跟踪?

Related questions:相关问题:

I have module I use for situations like this - where a process will be running for a long time but gets stuck sometimes for unknown and irreproducible reasons.我有我用于这种情况的模块 - 一个进程将运行很长时间,但有时由于未知和不可复制的原因而卡住。 Its a bit hacky, and only works on unix (requires signals):它有点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

To use, just call the listen() function at some point when your program starts up (You could even stick it in site.py to have all python programs use it), and let it run.要使用,只需在您的程序启动时调用 listen() 函数(您甚至可以将其粘贴在 site.py 中以让所有 Python 程序使用它),然后让它运行。 At any point, send the process a SIGUSR1 signal, using kill, or in python:在任何时候,使用 kill 或在 python 中向进程发送 SIGUSR1 信号:

    os.kill(pid, signal.SIGUSR1)

This will cause the program to break to a python console at the point it is currently at, showing you the stack trace, and letting you manipulate the variables.这将导致程序在当前所在的点中断到 python 控制台,向您显示堆栈跟踪,并让您操作变量。 Use control-d (EOF) to continue running (though note that you will probably interrupt any I/O etc at the point you signal, so it isn't fully non-intrusive.使用 control-d (EOF) 继续运行(但请注意,您可能会在发出信号时中断任何 I/O 等,因此它不是完全非侵入性的。

I've another script that does the same thing, except it communicates with the running process through a pipe (to allow for debugging backgrounded processes etc).我有另一个脚本做同样的事情,除了它通过管道与正在运行的进程通信(以允许调试后台进程等)。 Its a bit large to post here, but I've added it as a python cookbook recipe .在这里发布有点大,但我已将其添加为python 食谱

The suggestion to install a signal handler is a good one, and I use it a lot.安装信号处理程序的建议很好,我经常使用它。 For example, bzr by default installs a SIGQUIT handler that invokes pdb.set_trace() to immediately drop you into a pdb prompt.例如,默认情况下bzr会安装一个 SIGQUIT 处理程序,它调用pdb.set_trace()以立即将您放入pdb提示符。 (See the bzrlib.breakin module's source for the exact details.) With pdb you can not only get the current stack trace (with the (w)here command) but also inspect variables, etc. (有关确切的详细信息,请参阅bzrlib.breakin模块的源代码。)使用 pdb,您不仅可以获取当前堆栈跟踪(使用(w)here命令),还可以检查变量等。

However, sometimes I need to debug a process that I didn't have the foresight to install the signal handler in. On linux, you can attach gdb to the process and get a python stack trace with some gdb macros.但是,有时我需要调试一个我没有先见之明的进程来安装信号处理程序。在 linux 上,您可以将 gdb 附加到进程并使用一些 gdb 宏获取 python 堆栈跟踪。 Put http://svn.python.org/projects/python/trunk/Misc/gdbinit in ~/.gdbinit , then:http://svn.python.org/projects/python/trunk/Misc/gdbinit放入~/.gdbinit ,然后:

  • Attach gdb: gdb -p PID附加gdb: gdb -p PID
  • Get the python stack trace: pystack获取python堆栈跟踪: pystack

It's not totally reliable unfortunately, but it works most of the time.不幸的是,它并不完全可靠,但它大部分时间都有效。

Finally, attaching strace can often give you a good idea what a process is doing.最后,附加strace通常可以让您很好地了解进程正在做什么。

I am almost always dealing with multiple threads and main thread is generally not doing much, so what is most interesting is to dump all the stacks (which is more like the Java's dump).我几乎总是在处理多个线程,而主线程通常不会做太多事情,所以最有趣的是转储所有堆栈(这更像是 Java 的转储)。 Here is an implementation based on 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)

Getting a stack trace of an unprepared python program, running in a stock python without debugging symbols can be done with pyrasite .使用pyrasite可以获取未准备好的python 程序的堆栈跟踪,在没有调试符号的情况下运行在股票 python 中。 Worked like a charm for me in on Ubuntu Trusty:在 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

(Hat tip to @Albert, whose answer contained a pointer to this, among other tools.) (给@Albert 的帽子提示,他的回答包含一个指向此的指针,以及其他工具。)

>>> import traceback
>>> def x():
>>>    print traceback.extract_stack()

>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]

You can also nicely format the stack trace, see the docs .您还可以很好地格式化堆栈跟踪,请参阅文档

Edit : To simulate Java's behavior, as suggested by @Douglas Leeder, add this:编辑:要模拟 Java 的行为,如@Douglas Leeder 所建议的,请添加以下内容:

import signal
import traceback

signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))

to the startup code in your application.到应用程序中的启动代码。 Then you can print the stack by sending SIGUSR1 to the running Python process.然后您可以通过将SIGUSR1发送到正在运行的 Python 进程来打印堆栈。

The traceback module has some nice functions, among them: print_stack: traceback模块有一些不错的功能,其中包括:print_stack:

import traceback

traceback.print_stack()

You can try the faulthandler module .你可以试试faulthandler 模块 Install it using pip install faulthandler and add:使用pip install faulthandler安装它并添加:

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

at the beginning of your program.在你的程序开始时。 Then send SIGUSR1 to your process (ex: kill -USR1 42 ) to display the Python traceback of all threads to the standard output.然后将 SIGUSR1 发送到您的进程(例如: kill -USR1 42 )以将所有线程的 Python 回溯显示到标准输出。 Read the documentation for more options (ex: log into a file) and other ways to display the traceback.阅读文档以获取更多选项(例如:登录文件)和其他显示回溯的方式。

The module is now part of Python 3.3.该模块现在是 Python 3.3 的一部分。 For Python 2, see http://faulthandler.readthedocs.org/对于 Python 2,请参阅http://faulthandler.readthedocs.org/

What really helped me here is spiv's tip (which I would vote up and comment on if I had the reputation points) for getting a stack trace out of an unprepared Python process.在这里真正帮助我的是spiv 的提示(如果我有声望点,我会投票并发表评论),用于从未准备好的 Python 进程中获取堆栈跟踪。 Except it didn't work until I modified the gdbinit script .除了在我修改 gdbinit script之前它没有工作。 So:所以:

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

  • edit it, changing PyEval_EvalFrame to PyEval_EvalFrameEx编辑它,将PyEval_EvalFrame更改为PyEval_EvalFrameEx [edit: no longer needed; [编辑:不再需要; the linked file already has this change as of 2010-01-14]截至 2010 年 1 月 14 日,链接文件已经有此更改]

  • Attach gdb: gdb -p PID附加gdb: gdb -p PID

  • Get the python stack trace: pystack获取python堆栈跟踪: pystack

python -dv yourscript.py python -dv yourscript.py

That will make the interpreter to run in debug mode and to give you a trace of what the interpreter is doing.这将使解释器在调试模式下运行,并为您提供解释器正在做什么的跟踪。

If you want to interactively debug the code you should run it like this:如果你想交互式地调试代码,你应该像这样运行它:

python -m pdb yourscript.py python -m pdb yourscript.py

That tells the python interpreter to run your script with the module "pdb" which is the python debugger, if you run it like that the interpreter will be executed in interactive mode, much like GDB这告诉 python 解释器使用模块“pdb”运行你的脚本,它是 python 调试器,如果你像这样运行它,解释器将以交互模式执行,很像 GDB

I would add this as a comment to haridsv's response , but I lack the reputation to do so:我会将此作为评论添加到haridsv's response ,但我缺乏这样做的声誉:

Some of us are still stuck on a version of Python older than 2.6 (required for Thread.ident), so I got the code working in Python 2.5 (though without the thread name being displayed) as such:我们中的一些人仍然坚持使用早于 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)

Take a look at thefaulthandler module, new in Python 3.3.查看 Python 3.3 中新增的faulthandler模块。 A faulthandler backport for use in Python 2 is available on PyPI. PyPI 上提供了用于 Python 2 的faulthandler向后移植

It can be done with excellent py-spy .它可以通过优秀的py-spy来完成。 It's a sampling profiler for Python programs , so its job is to attach to a Python processes and sample their call stacks.它是Python 程序的采样分析器,因此它的工作是附加到 Python 进程并对其调用堆栈进行采样。 Hence, py-spy dump --pid $SOME_PID is all you need to do to dump call stacks of all threads in the $SOME_PID process.因此, py-spy dump --pid $SOME_PID是您转储$SOME_PID进程中所有线程的调用堆栈所需要做的全部工作。 Typically it needs escalated privileges (to read the target process' memory).通常它需要提升权限(读取目标进程的内存)。

Here's an example of how it looks like for a threaded Python application.下面是一个关于线程 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)  

On Solaris, you can use pstack(1) No changes to the python code are necessary.在 Solaris 上,您可以使用 pstack(1) 无需更改 python 代码。 eg.例如。

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

I was looking for a while for a solution to debug my threads and I found it here thanks to haridsv.我一直在寻找调试线程的解决方案,感谢haridsv,我在这里找到了它。 I use slightly simplified version employing the traceback.print_stack():我使用使用 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)

For my needs I also filter threads by name.根据我的需要,我还按名称过滤线程。

If you're on a Linux system, use the awesomeness of gdb with Python debug extensions (can be in python-dbg or python-debuginfo package).如果您使用的是 Linux 系统,请将gdb python-debuginfo功能与 Python 调试扩展(可以在python-dbgpython-debuginfo包中)一起使用。 It also helps with multithreaded applications, GUI applications and C modules.它还有助于多线程应用程序、GUI 应用程序和 C 模块。

Run your program with:运行你的程序:

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

This instructs gdb to prepare python <programname>.py <arguments> and r un it.这指示gdb制备python <programname>.py <arguments>r取消它。

Now when you program hangs, switch into gdb console, press Ctr+C and execute:现在当程序挂起时,切换到gdb控制台,按Ctr+C并执行:

(gdb) thread apply all py-list

See example session and more info here and here .在此处此处查看示例会话和更多信息。

I hacked together some tool which attaches into a running Python process and injects some code to get a Python shell.我编写了一些附加到正在运行的 Python 进程中的工具,并注入了一些代码来获取 Python shell。

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

It's worth looking at Pydb , "an expanded version of the Python debugger loosely based on the gdb command set".值得一看Pydb ,“松散地基于 gdb 命令集的 Python 调试器的扩展版本”。 It includes signal managers which can take care of starting the debugger when a specified signal is sent.它包括信号管理器,可以在发送指定信号时负责启动调试器。

A 2006 Summer of Code project looked at adding remote-debugging features to pydb in a module called mpdb . 2006 Summer of Code 项目着眼于在名为mpdb的模块中向 pydb 添加远程调试功能。

pyringe is a debugger that can interact with running python processes, print stack traces, variables, etc. without any a priori setup. pyringe是一个调试器,它可以与正在运行的 python 进程交互、打印堆栈跟踪、变量等,而无需任何先验设置。

While I've often used the signal handler solution in the past, it can still often be difficult to reproduce the issue in certain environments.虽然我过去经常使用信号处理程序解决方案,但在某些环境中重现该问题通常仍然很困难。

You can use PuDB , a Python debugger with a curses interface to do this.您可以使用PuDB ,这是一个带有 curses 接口的 Python 调试器来执行此操作。 Just add只需添加

from pudb import set_interrupt_handler; set_interrupt_handler()

to your code and use Ctrl-C when you want to break.到您的代码并在您想中断时使用 Ctrl-C。 You can continue with c and break again multiple times if you miss it and want to try again.如果您错过并想再试一次,您可以继续使用c并再次中断多次。

I am in the GDB camp with the python extensions.我在使用 python 扩展的 GDB 阵营。 Follow https://wiki.python.org/moin/DebuggingWithGdb , which means按照https://wiki.python.org/moin/DebuggingWithGdb ,这意味着

  1. dnf install gdb python-debuginfo or sudo apt-get install gdb python2.7-dbg dnf install gdb python-debuginfosudo apt-get install gdb python2.7-dbg
  2. gdb python <pid of running process>
  3. py-bt

Also consider info threads and thread apply all py-bt .还要考虑info threadsthread apply all py-bt

How to debug any function in console :如何在控制台中调试任何功能:

Create function where you use pdb.set_trace() , then function you want debug.创建使用 pdb.set_trace() 的函数,然后是您想要调试的函数。

>>> import pdb
>>> import my_function

>>> def f():
...     pdb.set_trace()
...     my_function()
... 

Then call created function:然后调用创建的函数:

>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb) 

Happy debugging :)调试愉快:)

You can use the hypno package, like so:您可以使用hypno包,如下所示:

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

This would print a stack trace into the program's stdout.这会将堆栈跟踪打印到程序的标准输出中。

Alternatively, if you don't want to print anything to stdout, or you don't have access to it (a daemon for example), you could use the madbg package, which is a python debugger that allows you to attach to a running python program and debug it in your current terminal.或者,如果您不想将任何内容打印到标准输出,或者您无权访问它(例如守护进程),您可以使用madbg包,它是一个 python 调试器,允许您附加到正在运行的python 程序并在您当前的终端中调试它。 It is similar to pyrasite and pyringe , but newer, doesn't require gdb, and uses IPython for the debugger (which means colors and autocomplete).它类似于pyrasitepyringe ,但较新,不需要 gdb,并且使用IPython作为调试器(这意味着颜色和自动完成)。

To see the stack trace of a running program, you could run:要查看正在运行的程序的堆栈跟踪,您可以运行:

madbg attach <pid>

And in the debugger shell, enter: bt然后在调试器 shell 中输入: bt

Disclaimer - I wrote both packages免责声明 - 我写了两个包

I don't know of anything similar to java's response to SIGQUIT , so you might have to build it in to your application.我不知道任何类似于java 对 SIGQUIT 的响应,因此您可能必须将其构建到您的应用程序中。 Maybe you could make a server in another thread that can get a stacktrace on response to a message of some kind?也许您可以在另一个线程中创建一个服务器,以便在响应某种消息时获得堆栈跟踪?

There is no way to hook into a running python process and get reasonable results.无法连接到正在运行的 python 进程并获得合理的结果。 What I do if processes lock up is hooking strace in and trying to figure out what exactly is happening.如果进程锁定,我所做的就是将 strace 挂入并试图弄清楚到底发生了什么。

Unfortunately often strace is the observer that "fixes" race conditions so that the output is useless there too.不幸的是,strace 通常是“修复”竞争条件的观察者,因此输出在那里也毫无用处。

use the inspect module.使用检查模块。

import inspect help(inspect.stack) Help on function stack in module inspect: import inspect help(inspect.stack) 模块inspect中函数栈的帮助:

stack(context=1) Return a list of records for the stack above the caller's frame. stack(context=1) 返回调用者框架上方堆栈的记录列表。

I find it very helpful indeed.我觉得它确实很有帮助。

In Python 3, pdb will automatically install a signal handler the first time you use c(ont(inue)) in the debugger.在 Python 3 中,pdb 将在您第一次在调试器中使用 c(ont(inue)) 时自动安装信号处理程序。 Pressing Control-C afterwards will drop you right back in there.之后按 Control-C 会让你回到那里。 In Python 2, here's a one-liner which should work even in relatively old versions (tested in 2.7 but I checked Python source back to 2.4 and it looked okay):在 Python 2 中,这是一个单行代码,即使在相对较旧的版本中也应该可以工作(在 2.7 中进行了测试,但我将 Python 源代码检查回 2.4,看起来没问题):

import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))

pdb is worth learning if you spend any amount of time debugging Python.如果您花费大量时间调试 Python,pdb 值得学习。 The interface is a bit obtuse but should be familiar to anyone who has used similar tools, such as gdb.该界面有点迟钝,但任何使用过类似工具(例如 gdb)的人都应该熟悉。

In case you need to do this with uWSGI, it has Python Tracebacker built-in and it's just matter of enabling it in the configuration (number is attached to the name for each worker):如果您需要使用 uWSGI 执行此操作,它内置了Python Tracebacker ,只需在配置中启用它即可(数字附加到每个工作程序的名称):

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

Once you have done this, you can print backtrace simply by connecting to the socket:完成此操作后,您只需连接到套接字即可打印回溯:

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

If you want to peek at a running Python application to see the "live" call stack in a top-like fashon you can use austin-tui ( https://github.com/p403n1x87/austin-tui ).如果您想查看正在运行的 Python 应用程序以查看类似 top 的“实时”调用堆栈,您可以使用 austin-tui ( https://github.com/p403n1x87/austin-tui )。 You can install it from PyPI with eg你可以从 PyPI 安装它,例如

pipx install austin-tui

Note that it requires the austin binary to work ( https://github.com/p403n1x87/austin ), but then you can attach to a running Python process with请注意,它需要 austin 二进制文件才能工作( https://github.com/p403n1x87/austin ),但是您可以使用以下命令附加到正在运行的 Python 进程

austin-tui -p <pid>

At the point where the code is run, you can insert this small snippet to see a nicely formatted printed stack trace.在代码运行时,您可以插入这个小片段以查看格式良好的打印堆栈跟踪。 It assumes that you have a folder called logs at your project's root directory.它假设您在项目的根目录中有一个名为logs的文件夹。

# DEBUG: START DEBUG -->
import traceback

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM