在遗留GDB脚本中停止堆栈跟踪的条件
我有一个旧版的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_Main
和Py_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 个回答
我对问题1没有答案,但我觉得可以部分回答问题2。
尾递归确实会重用现有的栈指针和帧指针。这意味着在查看栈跟踪时,对同一个优化过的尾递归函数的多次调用在GDB中只会显示一次,因为(显然)栈指针是被重用的(而且没有新的栈或帧指针被推入)。
这似乎意味着你可以只检查之前和当前的$sp值来判断是否停止。不幸的是,$sp的值在栈中间可能是相同的。这种情况发生在某些函数调用被优化掉的时候。
所以我在问题中提出的停止条件可能相当脆弱,尽管它在几个实际例子中是有效的。