我必须运行一个遗留的 Zope2 网站并对它有些不满。最大的问题是,有时它只是锁定,以 100% 的 CPU 负载运行,不再响应请求。虽然该问题无法定期重现,但包含 3 个动态图的页面有时会触发它,因此我怀疑某种竞争条件会导致无限循环或卡住的忙等待。
问题是,我还没有找到调试这个东西的方法。Zope 日志中没有任何内容,系统日志中也没有任何内容。我尝试了这个问题的建议来获取堆栈跟踪,但唯一有效的信号是SIGKILL
.
当它卡住时,是否还有另一种可能性来找出该过程的确切位置?
我必须运行一个遗留的 Zope2 网站并对它有些不满。最大的问题是,有时它只是锁定,以 100% 的 CPU 负载运行,不再响应请求。虽然该问题无法定期重现,但包含 3 个动态图的页面有时会触发它,因此我怀疑某种竞争条件会导致无限循环或卡住的忙等待。
问题是,我还没有找到调试这个东西的方法。Zope 日志中没有任何内容,系统日志中也没有任何内容。我尝试了这个问题的建议来获取堆栈跟踪,但唯一有效的信号是SIGKILL
.
当它卡住时,是否还有另一种可能性来找出该过程的确切位置?
您可以使用pyrasite打印出漂亮的堆栈跟踪。
首先,您需要安装 gdb。
# Redhat, CentOS, etc
$ yum install gdb
# Ubuntu, Debian, etc
$ apt-get update && apt-get install gdb
然后,安装pyrasite。
$ 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 ''
请参阅我对这个 SO 问题的回答,使用Products.signalstack。它在产品注册时注册与您已经找到的答案相同的处理程序。也许它对你更有效。
如果没有,您可能遇到了操作系统级别的 I/O 问题,您唯一的希望是将 gdb 附加到该进程。在 Stack Overflow 上搜索 gdb 答案;这里有丰富的信息!
您可以尝试将调试器附加到正在运行的进程。另请参阅此问题。
虽然 pyrasite 可能会起作用,但它不能处理一些极端情况并静默挂起/失败。
如果包没有按预期工作,可以手动执行包在引擎盖下所做的事情,以找出问题所在。
gdb -p <PID>
sudo
set $gstate = PyGILState_Ensure()
call PyRun_SimpleString(" <some Python code> ")
call PyGILState_Release($gstate)
如果 Python 没有使用调试符号编译,则有必要为函数提供显式数据类型:
参考Python源码https://github.com/python/cpython/blob/4fe5585240f64c3d14eb635ff82b163f92074b3a/Include/pystate.h#L86-L88,类型PyGILState_STATE
是一个有2个值的枚举,所以我们“猜测”我们可以使用int
. (虽然它可能不起作用。)
总之,根据文档,功能的“正确(受上述限制)”命令是
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。如果有任何错误可以在那里报告,希望我能尽快修复它。
如果进程卡在没有其他信号通过的方式,您可能需要考虑从调试器运行它,而不是尝试在运行时附加到它。
此外,它可能对其他调试策略很有用,例如关闭代码的某些部分以找出它仍然可重现的最小情况,以便查看导致它更好的原因。
在互联网上转了一圈后,我终于来到了这里: http: //podoliaka.org/2016/04/10/debugging-cpython-gdb/ - 详细描述了所有部分如何组合在一起。我的报价是'gdb /usr/bin/python -p $PID' - 需要可执行文件的名称才能让gdb找到正确的调试信息文件。