首页 文章

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

提问于
浏览
308

我有这个Python应用程序不时被卡住,我无法找到在哪里 .

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

某种即时堆栈跟踪?

Related questions:

24 回答

  • 8

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

  • 0

    使用检查模块 .

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

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

    我发现它确实非常有用 .

  • 5

    获得一个没有准备的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,其答案中包含指向此的工具,以及其他工具 . )

  • 0

    看一下_36435_模块,Python 3.3中的新模块 . PyPI上提供了用于Python 2的faulthandler backport .

  • 19

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

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

    • 附加gdb: gdb -p PID

    • 获取python堆栈跟踪: pystack

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

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

  • 10
    >>> import traceback
    >>> def x():
    >>>    print traceback.extract_stack()
    
    >>> x()
    [('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]
    

    您也可以很好地格式化堆栈跟踪,请参阅docs .

    Edit :为了模拟Java的行为,正如@Douglas Leeder所建议的那样,添加:

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

    到应用程序中的启动代码 . 然后,您可以通过将 SIGUSR1 发送到正在运行的Python进程来打印堆栈 .

  • 41

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

    import traceback
    
    traceback.print_stack()
    
  • 0

    我有这样的情况下使用的模块 - 一个进程将运行很长一段时间,但有时会因未知和不可复制的原因而卡住 . 它有点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等,因此它不是完全非侵入式的 .

    我'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'已将其添加为python cookbook recipe .

  • 26

    我会将此作为评论添加到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)
    
  • 3

    你可以试试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/

  • 10

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

    • dnf install gdb python-debuginfosudo apt-get install gdb python2.7-dbg

    • gdb python <pid of running process>

    • py-bt

    还要考虑 info threadsthread apply all py-bt .

  • 1

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

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

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

    uwsgi --connect-and-read /var/run/uwsgi/pytrace1
    
  • 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.
    
  • 295

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

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

  • 1

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

    from pudb import set_interrupt_handler; set_interrupt_handler()
    

    到您的代码并在想要破解时使用Ctrl-C . 你可以继续使用 c 并在你错过它时多次再次中断并想再试一次 .

  • 0

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

    运行程序:

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

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

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

    (gdb) thread apply all py-list
    

    example session以及更多信息herehere .

  • 20

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

    虽然我经常使用在过去的信号处理程序解决方案中,在某些环境中仍然可能经常难以重现该问题 .

  • 6

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

    python -dv yourscript.py

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

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

    python -m pdb yourscript.py

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

  • 0

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

    为了我的需要,我也按名称过滤线程 .

  • 138

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

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

  • 6

    这里真正帮助我的是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

  • 33

    值得一看Pydb,"an expanded version of the Python debugger loosely based on the gdb command set" . 它包括信号管理器,它可以在发送指定信号时负责启动调试器 .

    2006年夏季代码项目考虑在名为mpdb的模块中向pydb添加远程调试功能 .

  • 63

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

相关问题