debugging 从不接受信号的阻塞python进程获取stacktrace

wlzqhblo  于 2023-01-26  发布在  Python
关注(0)|答案(7)|浏览(127)

我不得不运行一个传统的Zope2网站,并对它有些不满。最大的问题是,偶尔,它只是锁定,运行在100%的CPU负载和不回答请求了。虽然这个问题是不可重现的定期基础上,一个页面包含3个动态图形触发它有时,所以我怀疑某种竞争条件,导致一个无限循环或卡住busywait。
问题是,我还没有找到一个方法来调试这个东西。在Zope日志和系统日志中什么都没有。我尝试了this question的建议来获得一个stacktrace,但唯一有效的信号是SIGKILL
有没有另外一种可能性来找出进程在卡住时的确切位置?

yuvru6vn

yuvru6vn1#

您可以使用pyrasite打印出一个不错的堆栈跟踪。
首先,您需要安装gdb。

# Redhat, CentOS, etc
$ yum install gdb

# Ubuntu, Debian, etc
$ apt-get update && apt-get install gdb

然后,安装黄铁矿。

$ pip install pyrasite

使用ps或其他方法找到阻塞的python进程的进程ID,并使用它运行pyrasite-shell

# Assuming process ID is 12345
$ pyrasite-shell 12345

现在应该看到一个python REPL,在REPL中运行以下代码,查看所有线程的堆栈跟踪。

import sys, traceback
for thread_id, frame in sys._current_frames().items():
    print 'Stack for thread {}'.format(thread_id)
    traceback.print_stack(frame)
    print ''
fcipmucu

fcipmucu2#

请参阅我对这个SO问题的回答,使用Products.signalstack。它注册的处理程序与您在产品注册时已经找到的处理程序相同。也许它更适合您。
如果没有,您可能遇到了操作系统级别的I/O问题,唯一的希望就是将gdb附加到进程。这里有丰富的信息!

vsmadaxz

vsmadaxz3#

虽然pyrasite可以工作,但它不能处理一些极端情况,并且会静默地挂起/失败。
如果软件包没有按预期工作,可以执行软件包在引擎盖下执行的操作(手动),以找出问题所在。

  • 将gdb附加到Python进程:gdb -p``(可能需要sudo。)
  • 通过在gdb中键入命令来运行以下函数
set $gstate = PyGILState_Ensure()
call          PyRun_SimpleString(" <some Python code> ")
call          PyGILState_Release($gstate)

有关函数的信息,请参见Python API文档:12.
如果Python没有使用调试符号编译,那么有必要为函数提供显式的数据类型:
参考Python源代码www.example.com,类型PyGILState_STATE是一个有2个值的枚举,所以我们"猜测"可以使用int。(although it may not work.)https://github.com/python/cpython/blob/4fe5585240f64c3d14eb635ff82b163f92074b3a/Include/pystate.h#L86-L88 , the type PyGILState_STATE is an enum with 2 values, so we "guess" that we can use int . ( although it may not work. )
总之,根据文档,函数的"正确(受上述限制)"命令为

set $gstate = ((int (*)())            PyGILState_Ensure ) ()
call          ((int (*)(const char*)) PyRun_SimpleString) (" <some Python code> ")
call          ((void(*)(int))         PyGILState_Release) ($gstate)

这个解决方案不依赖于gdb的Python调试扩展,否则可以简单地运行py-bt
我有一个更新的pyrasite分支,(目前)命名为pyrasite-ng。如果有任何bug可以在那里报告,希望我能尽快修复它。

fafcakar

fafcakar4#

您可以尝试将调试器附加到正在运行的进程。另请参阅this question

ddhy6vgd

ddhy6vgd5#

如果进程以其他信号无法通过的方式卡住,您可能需要考虑从调试器运行它,而不是在运行时尝试附加到它。
此外,它还可能对其他调试策略有用,比如关闭代码的某些部分,以找出仍然可以重现的最小情况,从而查看是什么原因导致它更好。

t98cgbkg

t98cgbkg6#

在网上兜了一圈之后,我终于来到了这里:http://podoliaka.org/2016/04/10/debugging-cpython-gdb/-详细描述了所有部分是如何组合在一起的。我的报价是“gdb**/usr/bin/python**-p $PID”-需要可执行文件的名称,以便gdb找到正确的调试信息文件。

eiee3dmh

eiee3dmh7#

随着python3.8的到来,你也可以使用faulthandler

import faulthandler
faulthandler.enable()
faulthandler.dump_traceback_later(timeout=10) 
// it will dump the traceback of all threads after a timeout of "10" seconds in this case

有关详细信息,请查阅faulthandler文档

相关问题