Win32下的堆损坏; 如何定位?

Jos*_*osh 59 c++ memory windows debugging multithreading

我正在研究破坏堆的多线程 C++应用程序.找到这种腐败的常用工具似乎不适用.源代码的旧版本(18个月之前)表现出与最新版本相同的行为,因此这已经存在了很长时间并且没有被注意到; 在缺点方面,源增量不能用于识别何时引入错误 - 存储库中存在大量代码更改.

崩溃行为的提示是在这个系统中产生吞吐量 - 数据的套接字传输,它被导入内部表示.我有一组测试数据会定期导致应用程序异常(各种地方,各种原因 - 包括堆分配失败,因此:堆损坏).

行为似乎与CPU功率或内存带宽有关; 机器越多,崩溃就越容易.禁用超线程核心或双核核心可降低(但不消除)损坏的速度.这表明与时间相关的问题.

现在这里有一个问题:
当它在一个轻量级的调试环境下运行时(比方说Visual Studio 98 / AKA MSVC6),堆损坏相当容易重现 - 在事情失败和异常失败之前经过十或十五分钟,就像alloc;在复杂的调试环境下运行时一样(Rational Purify,VS2008/MSVC9或者甚至是微软应用程序验证程序)系统变得内存速度受限并且不会崩溃(内存限制:CPU没有达到上面50%,磁盘指示灯不亮,程序运行速度快,盒子消耗1.3G2G RAM) .因此,我可以选择能够重现问题(但不能确定原因)或能够识别原因或我无法重现的问题.

我目前最好的猜测是下一步是:

  1. 得到一个疯狂的grunty框(以取代当前的开发框:2Gb RAM E6550 Core2 Duo); 这样可以在强大的调试环境下运行时重现崩溃导致错误行为; 要么
  2. 重写操作符newdelete使用VirtualAllocVirtualProtect在完成后将内存标记为只读.运行MSVC6并让操作系统捕获正在写入释放内存的坏人.是的,这是绝望的标志:谁是地狱重写newdelete?!我想知道这是否会使它像Purify等人一样慢.

而且,不是:内置Purify仪器的运输不是一种选择.

一位同事刚刚走过去问"Stack Overflow?我们现在收到堆栈溢出了吗?!?"

现在,问题是:我如何找到堆腐败者?


更新:平衡new[],delete[]似乎已经有很长的路要走解决问题.而不是15分钟,应用程序现在大约两个小时崩溃.还没有.还有什么建议?堆损坏仍然存在.

更新:Visual Studio 2008下的发布版本似乎要好得多; 目前的怀疑依赖于STL随船携带的实施VS98.


  1. 重现问题.Dr Watson将生成一个可能有助于进一步分析的转储.

我会注意到这一点,但我担心沃森博士只会在事后被绊倒,而不是当堆被踩到时.

另一个尝试可能是使用WinDebug作为调试工具,它同时也很轻巧,非常强大.

此刻再次发生这种情况:在出现问题之前没有多大帮助.我想抓住这个行为中的破坏者.

也许这些工具可以让您至少将问题缩小到某个组件.

我不抱太大希望,但绝望的时候要求......

并且您确定项目的所有组件都具有正确的运行时库设置(C/C++ tabVS 6.0项目设置中的代码生成类别)吗?

不,我不是,明天我将花费几个小时浏览工作区(其中有58个项目)并检查它们是否正在编译并链接相应的标志.


更新:这需要30秒.选择Settings对话框中的所有项目,取消选择,直到找到没有正确设置的项目(它们都具有正确的设置).

小智 28

我的第一选择是一个专用的堆工具,如pageheap.exe.

重写new和delete可能很有用,但是它不会捕获较低级代码提交的alloc.如果这是你想要的,最好low-level alloc API使用Microsoft Detours绕道而行.

还要进行健全性检查,例如:验证运行时库是否匹配(发布与调试,多线程与单线程,dll与静态库),查找错误删除(例如,删除删除[]应该是使用过),确保你没有混合和匹配你的分配.

还可以尝试选择性地关闭线程并查看问题何时消失.

在第一个异常时调用堆栈等是什么样的?


Mic*_*der 11

我的工作也有同样的问题(VC6有时我们也会使用).并没有简单的解决方案.我只有一些提示:

  • 尝试在生产计算机上使用自动故障转储(请参阅Process Dumper).我的经验表明沃森博士适合倾倒.
  • 从代码中删除所有catch(...).他们经常隐藏严重的内存异常.
  • 检查高级Windows调试 - 对于像您这样的问题,有很多很棒的技巧.我全心全意地推荐这个.
  • 如果您使用STLtry STLPort和checked版本.无效的迭代器是地狱.

祝好运.像你这样的问题花了我们几个月来解决.为此做好准备......


Tal*_*Tal 8

运行原始应用程序ADplus -crash -pn appnename.exe 当弹出内存问题时,您将获得一个很好的大转储.

您可以分析转储以确定哪个内存位置已损坏.如果幸运的话,覆盖内存是一个唯一的字符串,你可以弄清楚它来自哪里.如果你不幸运,你将需要挖掘win32堆并计算出原始记忆特征是什么.(堆-x可能有帮助)

在您知道什么是混乱后,您可以使用特殊堆设置缩小appverifier使用率.即,您可以指定DLL要监视的内容或要监视的分配大小.

希望这将加速监控,足以抓住罪魁祸首.

根据我的经验,我从不需要完整的堆验证模式,但我花了很多时间分析崩溃转储和浏览源.

PS: 您可以使用DebugDiag来分析转储.它可以指出DLL拥有损坏的堆,并为您提供其他有用的细节.


Gra*_*row 7

通过编写我们自己的malloc和免费函数,我们运气良好.在生产中,他们只是调用标准的malloc并且是免费的,但在调试中,他们可以做任何你想做的事情.我们还有一个简单的基类,除了覆盖new和delete操作符以使用这些函数之外什么都不做,然后你编写的任何类都可以简单地从该类继承.如果你有大量的代码,那么替换对malloc的调用并免费使用新的malloc并且免费(不要忘记realloc!)可能是一个很大的工作,但从长远来看它非常有用.

在Steve Maguire的书"强化建议书"(强烈推荐)中,您可以在这些例程中进行调试,例如:

  • 跟踪分配以发现泄漏
  • 分配比必要更多的内存并在记忆的开头和结尾放置标记 - 在自由例程中,您可以确保这些标记仍在那里
  • 在分配时使用标记(以查找未初始化内存的使用情况)和免费(查找free'd内存的使用情况)来记忆内存

另一个好主意是从来没有使用之类的东西strcpy,strcat或者sprintf-始终使用strncpy,strncatsnprintf.我们也编写了我们自己的这些版本,以确保我们不会写下缓冲区的末尾,这些也遇到了很多问题.