我最近修复了我们产品中的一个缺陷,其症状是访问一个悬空指针所造成的访问冲突。
为了提供良好的实践,我添加了一个单元测试,以确保bug不会再次出现。在编写单元测试时,我将始终退出缺陷修复,并确保单元测试失败,否则我知道它没有正确地完成它的工作。
退出缺陷修复后,我发现我的单元测试仍然通过(不太好)。当我将调试器附加到单元测试以查看它为什么通过时,测试失败(即抛出异常),我可以中断并观察到调用堆栈与我修复的原始缺陷中的调用堆栈匹配。
我没有修改Visual 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。如果我找到答案就会更新这个问题。
发布于 2010-11-24 22:13:43
我已经隔离了这个问题的原因--详见this question。
当在调试器下运行我的测试工具时,调试环境消耗的内存意味着相同对象的后续分配/释放总是在内存的不同的部分中分配。这意味着当我的测试工具试图访问一个悬空指针时,它会使测试崩溃(从技术上讲,这是未定义的行为,但这是测试代码,它似乎完成了我需要它做的事情)。
当从命令行运行我的测试工具时,相同对象的后续分配/释放总是重复使用相同的内存块。这种奇怪的行为意味着当我访问实际上是测试用例中的一个悬空指针时,这个悬空指针仍然指向一个有效的对象。所以我才没看到车祸。
发布于 2010-11-24 19:45:14
通常,当您删除指向该内存的指针时,VC++调试器将用一些已知值填充堆分配的内存。自从我使用Visual以来已经有很长一段时间了,但是在我看来,0xcddcdcd可能是这样一个值是合理的。在我看来,应用程序在调试器中运行时很可能会崩溃。在发布模式下运行时,运行时不会浪费时间来覆盖已释放的内存,因此有些时候您会感到“幸运”,而存储在该内存中的数据仍然有效。
您可以修改构建设置,打开在发布模式下使用已知值填充已分配内存的选项(完成后不要忘记再次关闭它)。我猜如果您这样做,您的应用程序将在发布模式下崩溃。
我理解这个值并不总是0xcdcddcd,这可能意味着我错了,或者可能意味着您有多条指向悬空指针的路径。
发布于 2010-11-24 17:30:24
几年前,我遇到了相反的情况:问题只是在调试器没有附加时才出现。
结果,代码破坏了以前方法激活的堆栈帧,并使用调试器引入了中间堆栈帧。
你可能也有类似的情况。
https://stackoverflow.com/questions/4272939
复制