堆Win32下的腐败; 如何定位?
我正在研究一个正在破坏堆的多线程 C ++应用程序。 找到这种腐败的常用工具似乎不适用。 源代码的旧版本(18个月)显示与最新版本相同的行为,所以这已经存在了很长时间,并且没有被注意到; 在缺点方面,源代码增量不能用于识别何时引入了错误 - 存储库中有很多代码更改。
崩溃行为的提示是在这个系统中产生吞吐量 - 数据的套接字传输被转化为内部表示。 我有一组测试数据会定期导致应用程序异常(各种地方,各种原因 - 包括堆分配失败,因此:堆损坏)。
这种行为似乎与CPU功率或内存带宽有关; 每台机器越多,崩溃越容易。 禁用超线程核心或双核核心会降低(但不会消除)损坏的速度。 这表明与时间有关的问题。
现在,这是一个难题:
当它在一个轻量级的调试环境下运行时(比如说Visual Studio 98 / AKA MSVC6
),堆损坏是相当容易重现的 - 十分钟或十五分钟之后就会出现可怕的异常和异常,例如alloc;
当在复杂的调试环境(Rational Purify, VS2008/MSVC9
甚至Microsoft应用程序验证程序)下运行时,系统会变成内存速度限制,并且不会崩溃(内存限制:CPU未达到50%
,磁盘指示灯不亮,该程序的速度可以更快,消耗2G内存的1.3G
内存)。 所以, 我可以在能够重现问题(但不能识别原因)或能够识别原因或我无法重现的问题之间做出选择。
我现在最好的猜测是接下来的地方是:
E6550 Core2 Duo
中的2Gb RAM); 这将使得在强大的调试环境下运行时可以重新引发崩溃导致错误行为; 要么 new
和delete
以使用VirtualAlloc
和VirtualProtect
将内存尽快标记为只读。 在MSVC6
下MSVC6
,让操作系统抓住正在写入释放内存的坏人。 是的,这是一个绝望的迹象:谁将重写new
和delete
? 我想知道这是否会像Purify等人那样缓慢。 并且,否:内置Purify仪器的运输不是一种选择。
一位同事刚刚走过去,问道:“堆栈溢出?我们现在是否在堆栈溢出?!?”
现在,问题是: 如何找到堆腐败者?
更新:平衡new[]
和delete[]
似乎已经得到了解决这个问题很长的路要走。 现在,应用程序现在需要两个小时才能崩溃,而不是15分钟。 还没有。 还有什么建议? 堆腐败持续存在。
更新:Visual Studio 2008下的发布版本似乎好得多; 目前的怀疑依赖于VS98
附带的STL
实现。
Dr Watson
将产生一个可能有助于进一步分析的转储。 我会记下这一点,但我担心沃森医生只会在事实发生后才会被绊倒,而不是堆积如山。
另一个尝试可能是使用WinDebug
作为一个调试工具,它非常强大,同时也是轻量级的。
现在还有一件事:在出现问题之前没什么帮助。 我想赶上行为中的破坏者。
也许这些工具将允许您至少将问题缩小到某个组件。
我没有太多的希望,但绝望的时刻呼唤着......
您确定项目的所有组件都有正确的运行时库设置( C/C++ tab
,VS 6.0项目设置中的代码生成类别)吗?
不,我不会,明天我会花费几个小时通过工作区(其中的58个项目),并检查他们是否正在编译和链接适当的标志。
更新:这花了30秒。 在“
Settings
对话框中选择所有项目,取消选择,直到找到没有正确设置的项目(它们都具有正确的设置)。 我的第一个选择将是一个专门的堆工具,如pageheap.exe。
重写新的和删除可能是有用的,但是这并不能捕获由低级代码提交的分配。 如果这是你想要的,那么最好使用Microsoft Detours来绕开low-level alloc API
。
还有一些理智的检查,例如:验证你的运行时库匹配(释放与调试,多线程与单线程,DLL与静态库),寻找坏的删除(例如,删除删除[]应该已经使用),确保你没有混合和匹配你的分配。
也可以尝试有选择地关闭线程并查看问题何时消失。
调用堆栈在第一次异常时的样子是什么样的?
我在工作中遇到同样的问题(我们也有时会使用VC6
)。 并没有简单的解决方案。 我只有一些提示:
STL
尝试STLPort
并检查构建。 无效的迭代器是地狱。 祝你好运。 类似你的问题需要我们几个月的时间来解决。 准备好这个...
使用ADplus -crash -pn appnename.exe
运行原始应用程序当内存问题弹出时,您将获得一个不错的大转储。
您可以分析转储以确定哪些内存位置已损坏。 如果幸运的话,覆盖内存是一个唯一的字符串,你可以找出它来自哪里。 如果你不幸运的话,你将需要深入研究win32
堆并确定什么是原始记忆特征。 (堆-x可能有帮助)
在知道发生了什么后,您可以使用特殊的堆设置来缩小验证用法。 即你可以指定你监视的DLL
,或者监视什么分配大小。
希望这会加速监控,足以追上罪魁祸首。
根据我的经验,我从不需要完整的堆校验器模式,但我花了大量时间分析故障转储和浏览源。
PS:您可以使用DebugDiag分析转储。 它可以指出DLL
拥有损坏的堆,并给你其他有用的细节。