sys._current_frames() 中的孤立堆栈跟踪

Orphan stacktraces in sys._current_frames()

这是一个深奥的纯Python问题。

我正在使用 sys._current_frames() 进行一些统计分析。即我有一个每秒运行 sys._current_frames() 一次的后台线程,将结果转储到一个文本文件中,然后我有一些 Python 代码将回溯从最常见到最少排序.

我见过的一个奇怪的现象是像这样的回溯:

  File "/opt/foo/bar.py", line 1437, in __iter__
    yield key

这个yield是我写的生成器。奇怪的是,这个回溯中只有一个框架。这怎么可能?另一个回溯有很多框架,要么来自流程的顶层,要么来自框架的顶层。这个单帧堆栈跟踪是什么意思?

我的一个理论是,这是一个生成器的冻结状态,在它产生一个值之后它正在等待 next 再次调用它。但我想我通过一个单独的实验反驳了这个理论:我制作了一个生成器,确保它被暂停,称为 sys._current_frames() 并且我没有看到那种堆栈跟踪。

正如 sys._current_frames() documentation 警告的那样,

This is most useful for debugging deadlock: this function does not require the deadlocked threads’ cooperation, and such threads’ call stacks are frozen for as long as they remain deadlocked. The frame returned for a non-deadlocked thread may bear no relationship to that thread’s current activity by the time calling code examines the frame.

sys._current_frames() 在您无法保证感兴趣的线程暂停的任何情况下自然容易出现竞争条件。


如您所料,您看到了暂停生成器的堆栈跟踪。当生成器挂起时,它的栈帧没有父帧。它的 f_back 设置为空。

sys._current_frames() 检索当前 运行 线程的堆栈帧,但当您查看这些帧时,它们可能不再是 运行。如果生成器在您调用 sys._current_frames() 和检查帧之间暂停,这就是它的样子。如果它在其他地方恢复,您可能还会在看起来与实际调用时完全不同的调用堆栈顶部看到它 sys._current_frames()

您的测试没有显示生成器框架,因为您在调用 sys._current_frames() 之前而不是之后暂停了生成器。此时生成器的堆栈帧不是任何线程的活动帧。