c++ - Inverse Heisenbug - 单元测试仅在附加调试器时失败

标签 c++ unit-testing debugging debug-build

我最近修复了我们产品中的一个缺陷,其症状是访问悬空指针导致的访问冲突。

为了良好的实践,我添加了一个单元测试以确保错误不会再次出现。在编写单元测试时,我总是会撤消我的缺陷修复并确保单元测试失败,否则我知道它没有正确完成它的工作。

取消缺陷修复后,我发现我的单元测试仍然通过(不好)。当我将调试器附加到单元测试以查看它通过的原因时,测试失败(即抛出异常)并且我可以中断并观察调用堆栈与我修复的原始缺陷中的调用堆栈匹配。

我没有修改 Visual Studio 2005 中的“异常中断”设置,这确实是导致测试工具终止的关键 Win32 异常(即没有正常的异常处理程序)。

异常的文本是:

Unhandled exception at 0x0040fc59 in _testcase.exe: 0xC0000005:
Access violation reading location 0xcdcdcdcd.

注意:位置并不总是 0xcdcdcdcd ( allocated but unwritten Win32 heap memory )。有时是0x00000000,有时是另一个地址。

这似乎与传统的 Heisenbug 相反,当通过调试器观察它时,问题就会消失。在我的例子中,通过调试器观察它会使问题出现!

我最初的想法是,这是由于调试器中的时间差异而暴露的竞争条件。但是,当我向代码添加跟踪并独立于调试器运行它时,打印出的数据向我表明应用程序应该以与在调试器下运行时类似的方式中止。但事实并非如此!

对于可能导致此问题的原因有什么建议吗?


更新:我正在缩小这个问题的原因。参见 this question更多细节。如果找到答案,我会更新这个问题。

最佳答案

通常,当您删除指向该内存的指针时,VC++ 调试器会用一些已知值填充堆分配的内存。自从我使用 Visual Studio 以来已经有一段时间了,但对我来说 0xcdcdcdcd 可能是这样一个值似乎是合理的。在我看来,应用程序在调试器中运行时最有可能正常崩溃。在 Release模式下运行时,运行时不会浪费时间覆盖已释放的内存,因此有时您会“幸运”并且存储在该内存中的数据仍然有效。

您可以修改您的build设置以打开在 Release模式下使用已知值填充释放内存的选项(不要忘记在完成后再次将其关闭)。我猜如果您这样做,您的应用程序会在 Release模式下崩溃。

我知道该值并不总是 0xcdcdcdcd,这可能意味着我错了,或者可能意味着您有不止一条路径指向悬空指针。

关于c++ - Inverse Heisenbug - 单元测试仅在附加调试器时失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4272939/

相关文章:

debugging - 配合使用带有Rust的vscode调试控制台

android - gradle 重复条目 FloatProperty

unit-testing - 在虚拟机中自动安装设置

c++ - VC++ : Pass multiple arrays between functions

.net - 互操作结构内存应该在调用后释放吗?

c++ - 函数模板和缩写函数模板之间的等价关系

c# - 对基于时间的组件进行单元测试?

unit-testing - 如何为一个测试方法运行多个测试用例

asp.net - 当开发环境不在域上时,如何在域上测试 ASP 应用程序?

c++ - 友好度和派生类