我的程序使用第三方库,在某些时候会引发分段错误。我尝试用调试符号编译库,没有编译器优化,崩溃消失了。我怀疑是编译器优化揭示了这个错误。调试此类案例的最佳做法是什么?
编辑 - (更正了上述声明:“显示”而非“造成”)
我想我被误解了。我没有打算责怪编译器,或类似的东西。我只是要求最佳实践在这种情况下找到错误,我在第三方库中没有调试符号(崩溃回溯导致第三方库)。
答案 0 :(得分:8)
您的怀疑是优化导致了一个错误。我怀疑您的代码具有导致未定义行为的构造,并且当优化器打开时,此未定义行为表现为错误行为或崩溃。不要责怪优化器。在你的代码中找到UB ......可能会很棘手。可能的罪魁祸首:
答案 1 :(得分:8)
你所描述的很常见。它几乎从来都不是编译器优化中的错误。优化为您的代码做了很多事情。变量被重新排序/优化等等。如果你有一个缓冲区溢出,它可能只是溢出内存,这在调试版本中没什么大不了的,但是这个内存在优化构建中非常重要。
使用valgrind来追踪记忆错误 - 它们几乎总是导致您看到症状的原因。
答案 2 :(得分:4)
使用调试符号和编译器优化进行编译,它也会“希望”失败。允许系统生成核心文件(ulimit -c unlimited
,然后重新运行程序)。将核心文件加载到gdb中以查看发生的情况。
另一个强大的工具是valgrind,使用选项--db-attatch=yes
在valgrind中运行你的程序,它会在检测到无效的读或写时立即停止并运行调试器。无效的读/写可能会引发Segfault,即使它们没有,也应该删除它们。
答案 3 :(得分:2)
继续将调试语句或消息框放在您认为代码崩溃的位置。崩溃将发生在两个消息框之间,只要代码没有太多改变,这将帮助您找到错误的代码。
同时注释掉代码块,直到崩溃停止。继续评论,直到崩溃返回。您上次评论的内容必须直接或间接导致崩溃。
这两种方法对于一般调试都很有用,如果你能够可靠地重现崩溃,你的工作已经完成了一半。
我没有给出调试编译器优化的具体建议,因为崩溃的可能性很小。通常会非常稳健地测试优化,以确保它们不会以任何方式更改代码的功能或语义。
答案 4 :(得分:2)
如果回溯通向第三方库,请在库调用之前使用gdb
中断。验证您传递给库的参数是否有效(即,不是未初始化的指针,不是指向freed内存的指针,不是超出范围等)。
您可以使用strace
跟踪函数调用,然后尝试确定第三方库中的执行路径吗?在失败的库调用之前使用printf
或其他系统调用,以便在strace输出中有一个起点。
如果你真的认为这是第三方库中的一个错误,你必须通过优化来编译它,这样你才能重现失败。您是说您的编译器只能包含非优化构建的调试符号吗? gdb
仍应适用于优化版本。
答案 5 :(得分:0)
好吧,通过编译后的二进制文件无济于事。
这样就可以通过代码查找导致段错误的部分。我会手动处理你的代码并开始评论。一旦找到导致错误的原因,您就可以确定如何处理错误。可能值得在选定位置添加printf
以查看程序确切失败的位置。
将其视为二进制搜索错误;)
答案 6 :(得分:0)
如果只在打开优化时它会爆炸,那么这是一个强烈的提示,你已经在某处调用了未定义的行为。不幸的是,UB可能远不及实际产生段错误的代码(正如我过去多次发现的那样)。
每次发生这种情况(经常没有这种情况),原因是代码中的其他地方发生了缓冲区溢出。尽管如此,我从未开发出一种可重复的,通常适用的技术来解决问题(除非你想通过调试器调用小时数并咒骂一般适用的技术)。