您对优化代码中的why个调试符号的理解并不像您预期的那样工作,这是不太正确的.
产生调试符号以匹配最终目标代码,也就是优化的代码.在优化期间不需要"更新",因为调试信息还没有保存出来.(当然,它已经是there,以编译器内部数据 struct 的形式出现,但它不需要与这些 struct 分开更新.)
相反,调试优化代码很奇怪的原因是:(A)代码行和CPU指令之间不再存在简单而单调的映射;(B)变量和寄存器/内存位置之间不再存在简单的映射.
尤其是:
断点可能会出错,因为我可能不再能够在有意义的地方暂停:如果我说暂停"here",这样的"here"可能不再存在,也不能引用其他随机的地方,因此我不能再在我想要的地方暂停.
编译器不需要输出按您指定的顺序执行操作的程序.假设您的程序有a = b*c + d; f = g - h;
个代码片段,并且假设编译器决定最终程序在从g
中减go h
之前应该将b
与c
相乘,但是将这个减法相加d
after.那么,第一行代码是在第二行代码之前还是之后?好吧,两个都是!不过,编译器会尽其所能保留部分合理的映射.你最终会住在a reasonably related place号房.
try 读取不再存在的特定变量的值可能会导致崩溃,因为根据符号,变量仍然位于某个内存地址,但由于变量不再存在,读取该内存地址可能会导致段错误.
不,它不会.调试器不会崩溃,即使它确实try 读取无效的位置,但无论如何它可能不会,因为页面可能仍然映射到可执行文件.但并不要求变量始终位于同一"位置"(内存位置或寄存器).不同的编译器和不同的编译器版本在跟踪变量的当前位置方面做得更好或更差.但正如你所说,它可能在某个特定时刻并不存在.
如果出现异常或类似Segerror的信号,调试器可以指出与实际故障点无关的代码行.
即使对于未优化的代码,如果发生堆栈粉碎(通常在段错误之前发生),情况也可能是这样.否则,这实际上只是第一个问题的变体:"实际的故障点"不是一行代码,而是一条指令.因此,编译器必须决定指令与哪行代码(如果有的话)关系最密切.
编译器为优化代码输出合理且有用的调试信息的能力各不相同.到目前为止,IMO Visual C++是最好的,Clang比GCC好,但这真的取决于优化的类型和编译器的版本.
底线:您从优化代码的调试信息中获得的信息存在明显的缺陷.但就像有人曾经说过的那样:"我知道这是不公平的,但这是镇上唯一的游戏."在StringMuncher::chew()
中看到段故障可能会也可能不会表明chew()
功能是actually造成的,但它是probably造成的,这比只看到段故障位于地址0x6AE002E0好还是坏?
可以使用各种技术来帮助调试优化的代码,但这些技术不在本问题的讨论范围内,并且可能已经在另一个问题中进行了描述.