哪些优化编译器开关使调试变得非常困难?

Posted

技术标签:

【中文标题】哪些优化编译器开关使调试变得非常困难?【英文标题】:Which optimization compiler switches make debugging very hard? 【发布时间】:2016-11-24 08:36:09 【问题描述】:

我参与了一个完全没有 PDB 的调试情况(不幸的是,这种情况仍然会发生)。在特定情况下,我正在研究堆栈损坏,并尝试进行手动堆栈遍历。但是,ESP 和 EBP 寄存器之间存在严重的不匹配,因此我认为代码是在打开/Oy 优化(帧指针省略)的情况下编译的。在这种情况下,我不得不放弃。

我现在的问题是:从 Visual Studio 2015 C++ 编译器切换到优化,哪些会使调试变得困难?一个简短的解释为什么变得困难会很棒。

为了限制问题的范围,答案应仅考虑 x86(32 位)。

可用的选项可以是found on MSDN,因为有:

O1 - 创建小代码 Os - 喜欢小代码 O2 - 创建快速代码 Ot - 支持快速代码 Ob - 控制内联扩展 Oi - 生成内在函数

以下几个不需要考虑:

Od - 禁用优化。这显然会造成最少的麻烦 Og - 已弃用 Ox - 只是其他人的组合。这显然会导致各个开关的故障总和 Oy - 省略帧指针。我已经知道了。这使得堆栈行走非常困难。主要是靠猜测。

【问题讨论】:

内联和帧指针遗漏是您应该担心的。文档说明了您的第一个列表如何映射到详细选项。 作为 cmets 下面的 Sombrero Kid,只需使用 -Zo 标志。它会生成一个更大的 PDB 文件,但使零售调试几乎与调试未优化的调试版本一样好。 除了 /Od 之外的任何东西都很难。您仍然会在叶函数中省略帧指针。换句话说,任何不进行任何调用的函数。从 ESP 中找到堆栈帧,根据您在函数序言中找到的值进行调整。 FPO 是使调试极其困难的一种。内联也使旧版本的windbg难以调试,现代版本显示内联。 【参考方案1】:

哇,有很多不同类型的代码优化,比我详细了解的要多,但我会尝试详细介绍显着影响调试体验的不同优化,优化知识可以帮助指导哪个编译器开关将启用它。

重新排序指令以防止 cpu 空闲,通常会产生更多代码,调试器会出现在代码周围跳转而不是线性执行。 将代码减少到编译时间常数,代码更小,代码更快,调试时将跳过此代码。 省略帧指针,生成更小的代码,但更难以遍历堆栈。 高效的寄存器使用,这将导致变量在超出范围之前不可读或出错,因为编译器已决定可以安全地提前停用该变量,并将其寄存器释放以供其他地方使用,而不是不必要地将其值推到堆栈。这样可以生成更小更快的代码。 内联,内联产生更胖的代码更快,内联函数不会出现在调用堆栈中。

这些天来,所有的优化标志都不受欢迎,配置文件引导优化是迄今为止在发布中使用优化的更可取的方式,如果你想调试发布式代码,你应该使用 -Zo 标志,这将产生更好的 pdb可以为您提供有关寄存器中内联函数和变量的更多信息。

GCC 和 clang 有一组优化标志,-O 标志只是这些标志的合并,查看 GCC 如何分配优化以及每个优化的详细信息将有助于进一步了解所有不同的优化编译器,在一般,做。

编辑:另外,如果您想打开单个编译器标志并查看它对 GCC 和 clang 的编译代码的作用,那么上帝螺栓真的很有用https://gcc.godbolt.org

【讨论】:

绝对是-Zo flag 的粉丝。我很惊讶它没有被设为默认值。 有趣的是,我提供的链接中没有列出 /Zo 它在侧边栏中列出,在 VS2013 Update 3 之前它是一个未记录的实验功能,他们可能只是忘记将它添加到主列表中。【参考方案2】:

据我所知,调试问题可能主要是编译后的二进制文件有信息遗漏造成的。 一些原因是:

帧指针遗漏:/Oy,正如您已经发现的那样 内联:/Ob2 在使用 /O1、/O2 或 /Ox 时生效 代码缩减:很难知道这如何准确映射到 /O VC++ 编译器选项 更好的寄存器使用 (在某种程度上)内在函数:/Oi

我个人的看法是,没有调试信息的二进制文件(.PDB 文件)通常“足够难”来调试,至少不值得我花时间 :-)

【讨论】:

以上是关于哪些优化编译器开关使调试变得非常困难?的主要内容,如果未能解决你的问题,请参考以下文章

C#编译器优化那点事

JLinkGDBServer调试ARM linux内核

JLinkGDBServer调试ARM linux内核

我应该使用哪些优化/设置在 Xcode for MacOS 的发布模式下进行编译?

模型压缩+编译器优化,使AI算法在移动端性能超越专用硬件

c语言编译