【发布时间】:2010-07-11 16:41:07
【问题描述】:
我正在开发源代码级调试器。 elf 中可用的调试信息 格式。如何实现“跨越”? 问题出在“Point1”,无论如何我可以等待 下一个源代码行(从 .debug_line 表中读取)。
谢谢
if (a == 1)
x = 1; //Point1
else if (a == 2)
x = 1;
z = 1;
【问题讨论】:
我正在开发源代码级调试器。 elf 中可用的调试信息 格式。如何实现“跨越”? 问题出在“Point1”,无论如何我可以等待 下一个源代码行(从 .debug_line 表中读取)。
谢谢
if (a == 1)
x = 1; //Point1
else if (a == 2)
x = 1;
z = 1;
【问题讨论】:
我不确定我是否完全理解这个问题,但我可以告诉你 GDB 是如何实现其step 命令的。
一旦控制进入一个特定的编译单元,GDB 就会读取那个 CU 的调试信息;特别是,它读取 .debug_line 部分的 CU 部分,并构建一个将指令地址映射到源代码位置的表。
当step 开始时,GDB 查找当前PC 的源位置。然后它通过机器指令逐步查找新PC的源位置,直到源位置发生变化。当源位置发生变化时,step 就完成了。
它还会在每一步之后计算帧 ID(堆栈帧的基地址和函数的起始地址),并检查它是否已更改。如果有,这意味着我们已经进入或从递归调用中返回,并且step 是完整的。
要了解为什么需要检查帧 ID 以及源位置,请考虑逐步调用以下函数:
int fact(n) { if (n > 0) { return n * fact(n-1); } else return 1; }
由于此函数完全在同一源代码行上定义,因此按指令单步执行直到源代码行更改将引导您完成所有递归调用而不会停止。但是,当我们进入一个新的 fact 调用时,堆栈帧的基地址会发生变化,表明我们应该停止。这给了我们以下行为:
fact (n=10) at recurse.c:4
(gdb) step
fact (n=9) at recurse.c:4
(gdb) step
fact (n=8) at recurse.c:4
GDB 的next 命令将这种一般行为与用于识别函数调用并让它们返回完成的适当逻辑相结合。和以前一样,必须使用帧 ID 来确定调用何时真正返回到原始帧;还有其他并发症。
值得考虑一下如何处理函数的内联实例(DWARF 确实描述了这一点)。不过这个问题有点过头了。
不是为了阻止实验,但如果我正在开始一个调试器项目,我想看看 Apple 的开发中调试器 lldb,它是开源的。
【讨论】: