【问题标题】:How can I find a rare bug that seems to only occur in release builds?如何找到似乎只在发布版本中出现的罕见错误?
【发布时间】:2010-07-15 10:53:46
【问题描述】:

我有一个相当大的解决方案,偶尔会崩溃。可悲的是,这些崩溃似乎只发生在发布版本中。当我在崩溃时附加调试器时,我收到消息:

"没有为任何调用加载符号 堆栈帧。源代码不能 显示”

这使得很难找到崩溃的原因。我正在使用 Visual Studio 2008 的默认发布构建设置,其中“调试信息格式”设置为“程序数据库 (/Zi)”。

您有什么提示可以帮助我找到错误吗?例如,我是否可以更改我的项目中的某些设置,以便可能仍然发生崩溃但在调试器中获得更有意义的信息?

更新:问题是一个很少发生的逻辑错误,它本身不应该导致崩溃,但显然会导致其他地方崩溃。解决逻辑错误解决了崩溃行为。

致所有来这里寻找类似问题解决方案的人:祝你好运,你将经历一段艰难的旅程。最终帮助我找到问题的是在代码中添加了很多边界检查(我可以使用预处理器指令启用/禁用)并为 linux 编译并使用 gdb/valgrind 运行。

【问题讨论】:

    标签: c++ visual-studio visual-studio-2008 debugging


    【解决方案1】:

    首先确保您正在为发布构建构建符号(调试信息),并且调试器可以找到它们(这可能需要配置符号路径 - 符号服务器会更好)。

    在调试时再次使用“模块”视图确认您已加载符号。

    获取符号的最简单方法是将.pdb 文件放在与程序集相同的位置。

    查看John Robbins blog 了解更多详情。

    【讨论】:

      【解决方案2】:

      如果应用优化后代码崩溃(如在默认版本中),则很可能是您的代码存在某种缺陷,并且依赖于在发布和调试构建之间发生变化的未定义行为。

      尝试在发布版本中关闭优化以查看问题是否消失(或在调试版本中打开以查看是否发生)。如果是这样,您仍然应该致力于找到并修复错误,但您至少会知道要寻找未定义的行为。

      将编译器警告级别设置为最大 (/W4) 并将警告设置为错误 (/Wx) 并修复所有警告(而不是简单地将所有内容投射到眼前 - 想想吧!)。应用优化时,您可能会收到调试版本中未出现的警告,因为执行了更广泛的代码分析 - 这是有用的静态分析。

      如果您希望在优化的构建中打开调试,您可以这样做,但您不太可能能够跟踪正在发生的事情,因为优化器可能会重新排序代码,并删除代码和变量。

      【讨论】:

        【解决方案3】:

        在我看来,堆栈框架被炸毁了。与缓冲区溢出无关,例如,只需将一个大字符串复制到一个小 char[] 中。这消除了返回地址。代码一直运行直到返回,然后当它从堆栈中弹出一个错误的地址时会爆炸。或者更糟糕的是,如果地址恰好是有效的。

        调试器无法显示任何有意义的内容,因为它无法遍历堆栈以显示代码如何到达崩溃位置。实际的崩溃位置不会告诉您任何信息。

        凝灰岩作为调试的钉子。您必须使其可重现,并且您需要步进或跟踪以找到最后一个已知良好的功能。退出后产生崩溃的那个是有错误的那个。您实际上可以看到造成损害的语句,调试器调用堆栈突然变得紧张。如果您无法获得一致的重现,那么剩下的就是彻底的代码审查。您可以通过将其称为“安全审查”来证明时间的合理性。祝你好运。

        【讨论】:

          【解决方案4】:

          调试版本可能不允许缺陷表达的几个原因:

          • 某些调试配置会初始化所有变量。
          • 调试内存分配和释放可能更能容忍指针滥用。
          • 调试构建可能会以不同的速度执行,从而掩盖竞争条件。

          由于您使用的是 C++,您可能会考虑使用静态分析工具(如 valgrind)来指出可能的未初始化数据和指针错误处理。

          可以通过添加带有时间戳的日志输出来跟踪竞争条件。您首先必须通过观察崩溃之前发生的事情来缩小“大型解决方案”中问题发生的位置。请务必使用延迟日志记录机制 - 稍后或在另一个线程中执行字符串处理的机制,因此它本身不会对时间产生太大影响。

          【讨论】:

            【解决方案5】:

            您知道您仍然可以调试发布版本吗?只需按 F5(而不是 CTRL+F5)即可在调试中运行。

            它是否可重复,即当它发生时你是否在做特定的事情?

            如果是这样,请在崩溃前在代码中设置断点并按 F5 以在调试中运行(但请确保您使用的是发布版本)。然后逐步执行,直到您的应用程序崩溃。我通常发现这比添加日志记录和调试打印语句要快。

            如果没有,仅在调试模式下运行有时会捕获错误并在有问题的行上停止。

            否则,Richard 和 Amar 的答案很好:-)

            【讨论】:

              【解决方案6】:

              未初始化的变量(可能是指针)也可能导致问题。也许您应该对您的代码运行一个静态分析程序 - CppCheck 还不错。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2021-08-27
                • 1970-01-01
                • 2014-10-16
                • 2011-07-10
                • 2018-02-27
                • 1970-01-01
                相关资源
                最近更新 更多