【问题标题】:Get stacktrace from stuck python process that does not accept signals从不接受信号的卡住 python 进程获取堆栈跟踪
【发布时间】:2010-12-19 09:51:16
【问题描述】:

我必须运行一个旧版 Zope2 网站,并且对此有些不满。最大的问题是,有时它只是锁定,以 100% 的 CPU 负载运行,不再响应请求。虽然问题无法定期重现,但包含 3 个动态图的页面有时会触发它,因此我怀疑某种竞争条件会导致无限循环或卡住的忙等待。

问题是,我还没有找到调试这个东西的方法。 Zope 日志中没有任何内容,系统日志中也没有任何内容。我尝试了this question 的建议来获取堆栈跟踪,但唯一有效的信号是SIGKILL

当进程卡住时,是否还有另一种可能性来找出进程的确切位置?

【问题讨论】:

标签: python debugging infinite-loop zope


【解决方案1】:

您可以使用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 ''

【讨论】:

  • 当我这样做时,pyrasite-shell 也会挂起
  • 同样,pyrasite-shell 挂起
  • 您需要与进程所有者一起运行pyrasite-shell
  • (免责声明:我的包) Pyrasite 有一个或两个可能导致它挂起的错误。我有一个 fork pyrasite-ng 可以修复其中的一些错误
【解决方案2】:

查看我对this SO question 的回复,使用Products.signalstack。它在产品注册时注册与您已经找到的答案相同的处理程序。也许它更适合你。

如果没有,您可能遇到了操作系统级别的 I/O 问题,您唯一的希望就是将 gdb 附加到该进程。在 Stack Overflow 上搜索 gdb 答案;这里有丰富的信息!

【讨论】:

    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      如果进程卡在没有其他信号通过的方式,您可能需要考虑从调试器运行它,而不是尝试在运行时附加到它。

      此外,它可能对其他调试策略很有用,例如关闭代码的某些部分以找出它仍然可重现的最小情况,以便了解导致它更好的原因。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

          【解决方案6】:

          虽然 pyrasite 可能会起作用,但它不能处理某些极端情况并静默挂起/失败。

          如果包没有按预期工作,则可以手动执行包在后台执行的操作,以找出问题所在。

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

          查看函数的 Python API 文档:12


          如果 Python 没有使用调试符号编译,则需要为函数提供显式数据类型:

          参考Python源代码https://github.com/python/cpython/blob/4fe5585240f64c3d14eb635ff82b163f92074b3a/Include/pystate.h#L86-L88PyGILState_STATE类型是一个有2个值的枚举,所以我们“猜测”我们可以使用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。如果有任何错误可以在那里报告,希望我能尽快修复它。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-11
            • 2018-08-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多