4

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

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

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

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

例外的文本是:

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

注意:位置并不总是0xcdcdcdcd(已分配但未写入的 Win32 堆内存)。有时是0x00000000,有时是另一个地址。

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

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

关于可能导致这种情况的任何建议?


更新: 我正在缩小这个问题的原因。有关更多详细信息,请参阅此问题。如果我找到它,将用答案更新这个问题。

4

4 回答 4

3

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

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

我很欣赏该值并不总是 0xcdcdcdcd,这可能意味着我错了,或者可能意味着您有多个指向悬空指针的路径。

于 2010-11-25T03:45:14.630 回答
2

几年前我遇到了相反的情况:问题仅在附加调试器时发生。

事实证明,代码破坏了先前方法激活的堆栈帧,并且使用调试器引入了一个中间堆栈帧。

你可能有类似的情况。

于 2010-11-25T01:30:24.220 回答
0

我不知道这是否对您有任何帮助,但我曾经遇到过一个错误,如果程序在 Visual Studio 调试器下运行,或者程序在外部运行,然后附加了调试器,它的表现会有所不同。

于 2010-11-25T01:21:07.943 回答
0

我已经隔离了这个问题的原因 - 有关详细信息,请参阅此问题

在调试器下运行我的测试工具时,调试环境消耗的内存意味着同一对象的后续分配/释放总是分配在内存的不同部分。这意味着当我的测试工具试图访问一个悬空指针时,它使测试崩溃(从技术上讲,这是未定义的行为,但这是测试代码,它似乎在做我需要它做的事情)。

从命令行运行我的测试工具时,同一对象的后续分配/释放总是重新使用一块内存。这种巧合的行为意味着,当我在测试用例中访问实际上是一个悬空指针时,碰巧悬空指针仍然指向一个有效对象。这就是为什么我没有看到崩溃。

于 2010-11-25T06:13:43.053 回答