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

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

某种飞行中的堆叠痕迹?

相关问题:

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


当前回答

如何调试任何函数在控制台:

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

愉快的调试:)

其他回答

我有一个用于这种情况的模块——一个进程将运行很长时间,但有时由于未知和不可复制的原因而卡住。它有点俗气,只在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食谱添加了。

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

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

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

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

如果你需要用uWSGI来做这件事,它有内置的Python Tracebacker,这只是在配置中启用它的问题(编号附加在每个worker的名称上):

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

一旦你这样做了,你可以简单地通过连接到套接字打印反向跟踪:

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