我想了解一下,在gccclang中使用-g -O2进行编译时,调试符号如何仍然有用.

因为我理解,编译器首先添加调试符号,然后应用所有优化,而不根据对代码执行的修改来修改或更新调试符号,因此如果进行了优化,则在调试时可能会发生以下任何情况:

  • 断点可能会出错,因为我可能不再能够在有意义的地方暂停:如果我说暂停"here",这样的"here"可能不再存在,也不能引用其他随机的地方,因此我不能再在我想要的地方暂停.

  • try 读取不再存在的特定变量的值可能会导致崩溃,因为根据符号,变量仍然位于某个内存地址,但由于变量不再存在,读取该内存地址可能会导致段错误.

  • 如果出现异常或类似Segerror的信号,调试器可以指出与实际故障点无关的代码行.

即使我可以研究/理解生成的程序集输出,调试符号仍然是无用的,因为它们已经相对于原始代码"冻结"(在我的理解中),因此调试符号不再与代码的当前形状一致.因此,即使我可以理解当前优化的代码,调试符号也不能帮助我调试代码,也不能尊重原始代码,也不能帮助我调试最终代码.

我的思路正确吗?已经用-g激活的调试符号和-O2一起使用还有什么用呢?

我之所以问这个问题,是因为我读到使用Como -g -O2比较常见,这意味着它在某种程度上肯定仍然有用,但考虑到你可以再信任调试符号,我想不出它是怎么回事.

推荐答案

您对优化代码中的why个调试符号的理解并不像您预期的那样工作,这是不太正确的.

产生调试符号以匹配最终目标代码,也就是优化的代码.在优化期间不需要"更新",因为调试信息还没有保存出来.(当然,它已经是there,以编译器内部数据 struct 的形式出现,但它不需要与这些 struct 分开更新.)

相反,调试优化代码很奇怪的原因是:(A)代码行和CPU指令之间不再存在简单而单调的映射;(B)变量和寄存器/内存位置之间不再存在简单的映射.

尤其是:

断点可能会出错,因为我可能不再能够在有意义的地方暂停:如果我说暂停"here",这样的"here"可能不再存在,也不能引用其他随机的地方,因此我不能再在我想要的地方暂停.

编译器不需要输出按您指定的顺序执行操作的程序.假设您的程序有a = b*c + d; f = g - h;个代码片段,并且假设编译器决定最终程序在从g中减go h之前应该将bc相乘,但是将这个减法相加d after.那么,第一行代码是在第二行代码之前还是之后?好吧,两个都是!不过,编译器会尽其所能保留部分合理的映射.你最终会住在a reasonably related place号房.

try 读取不再存在的特定变量的值可能会导致崩溃,因为根据符号,变量仍然位于某个内存地址,但由于变量不再存在,读取该内存地址可能会导致段错误.

不,它不会.调试器不会崩溃,即使它确实try 读取无效的位置,但无论如何它可能不会,因为页面可能仍然映射到可执行文件.但并不要求变量始终位于同一"位置"(内存位置或寄存器).不同的编译器和不同的编译器版本在跟踪变量的当前位置方面做得更好或更差.但正如你所说,它可能在某个特定时刻并不存在.

如果出现异常或类似Segerror的信号,调试器可以指出与实际故障点无关的代码行.

即使对于未优化的代码,如果发生堆栈粉碎(通常在段错误之前发生),情况也可能是这样.否则,这实际上只是第一个问题的变体:"实际的故障点"不是一行代码,而是一条指令.因此,编译器必须决定指令与哪行代码(如果有的话)关系最密切.

编译器为优化代码输出合理且有用的调试信息的能力各不相同.到目前为止,IMO Visual C++是最好的,Clang比GCC好,但这真的取决于优化的类型和编译器的版本.

底线:您从优化代码的调试信息中获得的信息存在明显的缺陷.但就像有人曾经说过的那样:"我知道这是不公平的,但这是镇上唯一的游戏."在StringMuncher::chew()中看到段故障可能会也可能不会表明chew()功能是actually造成的,但它是probably造成的,这比只看到段故障位于地址0x6AE002E0好还是坏?

可以使用各种技术来帮助调试优化的代码,但这些技术不在本问题的讨论范围内,并且可能已经在另一个问题中进行了描述.

C++相关问答推荐

为什么信号量为空= 0,而不是阻塞?

ATTiny1606定时器TCA 0中断未触发

如何使用Python C API实现多线程程序?

使用NameSurname扫描到两个单独的字符串

在传统操作系统上可以在虚拟0x0写入吗?

C语言中的strstr问题

Clang:如何强制运行时错误的崩溃/异常由于-fsanitize=undefined

正确的TCP/IP数据包 struct

我在这里正确地解释了C操作顺序吗?

`#if`条件中是否允许`sizeof`?

使用sscanf获取零个或多个长度的字符串

等同于铁 rust 的纯C语言S未实现!()宏

C11/C17标准允许编译器清除复合文字内存吗?

-Wnonnull-Compare警告不是具有误导性吗?

Valgrind正在使用一个Fexecve电话报告不可能发生的事情

赋值两侧的后置增量,字符指针

可以';t从A9G模块拨打电话

共享内存未授予父进程权限

如何向 execl 创建的后台程序提供输入?

初始化时无法将‘float****’转换为‘float (*)[128][3][4]’