我正在用C ++ / Qt编写一个包含图形文件解析器的程序。我使用g++
来编译项目。
在开发过程中,我不断比较不同编译器标志之间关于优化和调试信息的低级解析器层的性能,以及Qt的调试标志(打开/关闭qDebug()和Q_ASSERT())。
现在我遇到了一个问题,唯一正常运行的构建是没有任何优化的构建。所有其他版本,即使使用-O1
,似乎也以另一种方式工作。它们由于未满足的断言而崩溃,在没有-O...
标志的情况下编译时会满足这些断言。即使使用-Wall
,代码也不会产生任何编译器警告。
我非常确定我的程序中存在一个错误,这似乎只对启用优化有害。问题是:即使在调试程序时我也找不到它。解析器似乎从文件中读取错误的数据。当我运行一些简单的测试用例时,它们运行得很好。当我运行一个更大的测试用例(直接从文件读取的图形上的路由计算)时,文件中的读取不正确,我无法解释。
我应该从哪里开始追踪这种未定义行为的问题? 在这种不同的行为中可能涉及哪些优化方法?(我可以一个接一个地启用所有标志,但我不知道那么多编译器标志但是-O...
我知道那里它们很多,所以这需要很长时间。)一旦我知道这个类型的错误类型,我相信我迟早会发现它。
如果你能告诉我哪些编译器优化方法可能成为这类问题的候选者,你可以帮助我很多。
答案 0 :(得分:16)
在优化版本中通常会出现一些类错误,这些错误通常不会出现在调试版本中。
未初始化的变量。编译器可以捕获一些但不是全部。查看所有构造函数,查看全局变量。特别是寻找未初始化的指针。在调试构建内存中重置为零,但在发布版本中它不是。
使用超出范围的临时工。例如,当您在函数中返回对本地临时的引用时。这些通常在调试版本中工作,因为堆栈被填充更多。临时工作者往往会在堆叠上存活一段时间。
阵列覆盖临时写作。例如,如果在函数中创建一个数组作为临时数,然后在结尾之外写入一个元素。同样,堆栈在调试时会有额外的空间(用于调试信息),并且你的溢出不会命中程序数据。
答案 1 :(得分:2)
您可以从优化版本中禁用优化,以帮助简化调试优化版本。
-g -O1 -fno-inline -fno-loop-optimize -fno-if-conversion -fno-if-conversion2 \
-fno-delayed-branch
这应该使调试器中的代码更容易理解。
另一个建议是,如果您所拥有的断言没有提供有关导致问题的原因的足够信息,则应考虑添加更多断言。如果您害怕性能问题或断言混乱,可以将它们包装在宏中。这使您可以区分调试断言与最初添加的断言,因此可以在以后禁用它们或从代码中删除它们。
答案 2 :(得分:1)
1)在破碎的版本上使用valgrind。 (就此而言,在工作版本上尝试valgrind,也许你会很幸运。)
2)使用“-O1 -g”构建系统,并使用gdb逐步执行程序。在崩溃时,哪个变量的值不正确?重新运行你的程序并注意何时写入该变量(或者当它变为不存在且应该存在时)。