在遗留GDB脚本中停止堆栈跟踪的条件

5 投票
1 回答
982 浏览
提问于 2025-04-18 01:18

我有一个旧版的GDB命令脚本,用来获取Python的堆栈跟踪,这个脚本是基于Python 2.6源代码中自带的GDB脚本(由于网站限制,无法提供链接,但网址是:http://#%20http://svn.python.org/view/*checkout*/python/branches/release26-maint/Misc/)。

这个脚本里有一个循环,它的退出条件是根据程序计数器来判断的,这个判断方式比较脆弱(原代码中有注释提到),可能只适用于直接运行Python的情况,而不适用于从C/C++应用程序中启动解释器的情况。

现有的循环看起来是这样的:

while $pc < Py_Main || $pc > Py_GetArgcArgv
    # ...
    # code for extracting Python stack from local vars in relevant frames
    # of C stack
    # ...

    up-silently 1

对于我想调试的程序,检查Py_MainPy_GetArgcArgv的方式不太好用,所以我在寻找一个循环条件,当到达main时能够返回假。

因此,我在考虑使用程序计数器、帧指针和栈指针,基于这样的想法:如果up-silently失败,它们会保持之前的值,这意味着我在栈的顶部,像这样:

set $oldpc = -1
set $oldfp = -1
set $oldsp = -1
while !($oldpc == $pc && $oldfp == $fp && $oldsp == $sp)
    # ...
    # code for extracting Python stack from local vars in relevant frames
    # of C stack
    # ...

    set $oldpc = $pc
    set $oldsp = $sp
    set $oldfp = $fp
    up-silently 1

我觉得这样应该可以解决问题,初步检查显示它工作得不错。不过,我对编译器可能进行的优化不太熟悉,担心在某些特殊情况下,它们可能在栈中间的某个地方是相同的。

看起来$fp在帧指针被优化掉的调用中可能会是零(例如,使用-g -O3编译时)。我也不确定$pc是否可以依赖于它是不同的,特别是在发生递归调用时。我希望$sp在仍有有效栈可处理时会是不同的,但我隐约怀疑与尾递归相关的优化可能会导致$sp保持相同。

任何建议都将非常感谢。

具体问题:

问题 1:在旧版(非Python)GDB脚本中,有没有更好的方法来判断你是否在栈的顶部?

问题 2:我对$sp$pc$fp的假设在大多数或所有优化场景中是否成立?

1 个回答

2

我对问题1没有答案,但我觉得可以部分回答问题2

尾递归确实会重用现有的栈指针和帧指针。这意味着在查看栈跟踪时,对同一个优化过的尾递归函数的多次调用在GDB中只会显示一次,因为(显然)栈指针是被重用的(而且没有新的栈或帧指针被推入)。

这似乎意味着你可以只检查之前和当前的$sp值来判断是否停止。不幸的是,$sp的值在栈中间可能是相同的。这种情况发生在某些函数调用被优化掉的时候。

所以我在问题中提出的停止条件可能相当脆弱,尽管它在几个实际例子中是有效的。

撰写回答