转储所有活动线程的堆栈跟踪

38 投票
6 回答
29605 浏览
提问于 2025-04-15 12:27

我想要获取一个所有活跃线程的列表,并查看每个线程的当前执行情况。我可以通过使用 threading.enumerate() 来获取所有线程的列表,但我不知道怎么从这个列表中获取到每个线程的执行堆栈。

背景:我的 Zope/Plone 应用偶尔会出现问题,导致 CPU 使用率达到 100%,需要重启。我觉得可能是某个循环没有正确结束,但我在测试环境中无法重现这个问题。我设法注册了一个信号处理器,可以从外部触发,这样当问题再次出现时,我可以执行一些代码。如果我能获取到所有活跃线程的堆栈跟踪信息,那就能让我找到问题的线索。整个应用运行在 Python 2.4 上……

如果有人有办法追踪到这种情况,我会非常感激 :)

谢谢,
Chriss

6 个回答

41

对于Python 3.3及以后的版本,有一个功能叫做 faulthandler.dump_traceback()

下面的代码可以产生类似的输出,但它会显示线程的名称,并且可以改进以打印更多的信息。

for th in threading.enumerate():
    print(th)
    traceback.print_stack(sys._current_frames()[th.ident])
    print()
43

正如jitter在之前的回答中提到的,sys._current_frames()可以满足你在Python 2.5及以上版本的需求。对于懒得查找的人,下面这段代码对我有用,可能也能帮到你:

print >> sys.stderr, "\n*** STACKTRACE - START ***\n"
code = []
for threadId, stack in sys._current_frames().items():
    code.append("\n# ThreadID: %s" % 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()))

for line in code:
    print >> sys.stderr, line
print >> sys.stderr, "\n*** STACKTRACE - END ***\n"
10

在使用Zope的时候,你可能需要安装Products.signalstack或者mr.freeze,这两个工具就是为了这个目的而设计的!

你可以给你的Zope服务器发送一个USR1信号,这样它就会立刻把所有线程的堆栈信息打印到控制台上。即使所有的Zope线程都被锁住了,它也能做到这一点。

这些工具的底层实际上是间接使用了threadframes;对于Python 2.5及以上版本,如果不使用Zope,你可以通过sys._current_frames()这个函数来获取每个线程的堆栈信息。

Zope 2.12.5开始,这个功能已经集成到Zope里面了,所以不需要再安装额外的工具了。

撰写回答