我正在调试Visual Studio 2008下的(本机)多线程C ++应用程序。在看似随机的场合,我得到一个“Windows触发了一个断点......”错误,注意这可能是由于堆中的腐败。这些错误并不总是会立即使应用程序崩溃,尽管它可能会在之后崩溃。
这些错误的一个大问题是它们只在实际发生损坏后弹出,这使得它们很难跟踪和调试,尤其是在多线程应用程序上。
什么样的事情会导致这些错误?
如何调试它们?
欢迎提示,工具,方法,启发......
答案 0 :(得分:125)
Application Verifier结合Debugging Tools for Windows是一个惊人的设置。你可以将它们作为Windows Driver Kit or the lighter Windows SDK的一部分。 (在研究earlier question about a heap corruption issue时发现了应用程序验证程序。)我过去也使用过BoundsChecker和Insure ++(在其他答案中提到过),尽管我对Application Verifier中有多少功能感到惊讶。
电围栏(又名“efence”),dmalloc,valgrind等等都值得一提,但大多数都比* nix更容易在Windows下运行。 Valgrind非常灵活:我调试了大型服务器软件,使用它有很多堆问题。
当所有其他方法都失败时,您可以提供自己的全局运算符new / delete和malloc / calloc / realloc重载 - 如何执行此操作会因编译器和平台而有所不同 - 这将有点像投资 - 但从长远来看可能会得到回报。 dmalloc和electricfence应该看起来很熟悉的特征列表,以及令人惊讶的优秀书籍Writing Solid Code:
请注意,在我们的本地自制软件系统中(对于嵌入式目标),我们将跟踪与大多数其他内容分开,因为运行时开销要高得多。
如果您对更多重置这些分配函数/运算符的理由感兴趣,请查看my answer to "Any reason to overload global operator new and delete?";除了无耻的自我推销外,它还列出了其他有助于跟踪堆损坏错误的技术,以及其他适用的工具。
因为我在搜索MS使用的alloc / free / fence值时会一直找到我自己的答案,这里是another answer that covers Microsoft dbgheap fill values。
答案 1 :(得分:35)
您可以通过为应用程序启用Page Heap来检测大量堆损坏问题。为此,您需要使用作为Debugging Tools For Windows
一部分的gflags.exe运行Gflags.exe并在可执行文件的图像文件选项中,选中“启用页面堆”选项。
现在重新启动exe并附加到调试器。启用页面堆后,只要发生任何堆损坏,应用程序就会进入调试器。
答案 2 :(得分:13)
答案 3 :(得分:12)
要真正减慢速度并执行大量运行时检查,请尝试在main()
的顶部添加以下内容或在Microsoft Visual Studio C ++中等效
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
答案 4 :(得分:8)
什么样的事情会导致这些错误?
用记忆做顽皮的事情,例如:在缓冲区结束后写入,或者在缓冲区被释放回堆之后写入缓冲区。
如何调试它们?
使用一种工具,为您的可执行文件添加自动边界检查:即Unix上的valgrind,或Windows上的BoundsChecker工具(维基百科也建议使用Purify和Insure ++)。
请注意这些会降低您的应用程序速度,因此如果您的应用程序是软实时应用程序,它们可能无法使用。
另一种可能的调试辅助工具/工具可能是MicroQuill的HeapAgent。
答案 5 :(得分:8)
我从Detecting access to freed memory获得的一个快速提示是:
如果要查找错误 很快,没有检查每一个 访问内存的语句 阻止,您可以设置内存指针 释放后的无效值 块:
#ifdef _DEBUG // detect the access to freed memory #undef free #define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666; #endif
答案 6 :(得分:5)
我发现每次都有用和工作的最佳工具是代码审查(有很好的代码审查员)。
除了代码审核之外,我首先尝试Page Heap。页面堆需要几秒钟的时间来设置,运气好的话可能会确定您的问题。
如果页面堆没有运气,请从Microsoft下载Debugging Tools for Windows并学习使用WinDbg。抱歉无法为您提供更具体的帮助,但是多线程堆损坏更多的是艺术而不是科学。谷歌的“WinDbg堆腐败”,你应该找到很多关于这个主题的文章。
答案 7 :(得分:4)
您可能还想检查是否要链接动态或静态C运行时库。如果您的DLL文件链接到静态C运行时库,则DLL文件具有单独的堆。
因此,如果您要在一个DLL中创建一个对象并尝试在另一个DLL中释放它,您将获得上面看到的相同消息。另一个Stack Overflow问题引用了此问题, Freeing memory allocated in a different DLL 。
答案 8 :(得分:3)
您使用什么类型的分配功能?我最近使用Heap *样式分配函数遇到了类似的错误。
事实证明我错误地使用HEAP_NO_SERIALIZE
选项创建了堆。这实质上使Heap函数在没有线程安全的情况下运行。如果使用得当可以提高性能,但如果在多线程程序中使用HeapAlloc,则不应该使用它[1]。我只提到这一点,因为你的帖子提到你有一个多线程的应用程序。如果您在任何地方使用HEAP_NO_SERIALIZE,请删除它,它可能会解决您的问题。
[1]在某些情况下,这是合法的,但它要求您序列化对Heap *的调用,而多线程程序通常不是这种情况。
答案 9 :(得分:3)
如果这些错误随机发生,则很有可能遇到数据争用。请检查:你是否修改了不同线程的共享内存指针?英特尔®线程检测器可能有助于在多线程程序中检测此类问题。
答案 10 :(得分:1)
除了寻找工具之外,还要考虑寻找可能的罪魁祸首。是否有您正在使用的组件,可能不是您编写的,可能未经过设计和测试以在多线程环境中运行?或者只是一个你知道的人已经在这样的环境中运行。
最后一次发生在我身上,这是一个本地包,多年来已成功用于批处理作业。但这是该公司第一次使用.NET Web服务(多线程)。就是这样 - 他们谎称代码是线程安全的。
答案 11 :(得分:0)
您可以对_CrtSetDbgFlag使用VC CRT堆检查宏: _CRTDBG_CHECK_ALWAYS_DF 或 _CRTDBG_CHECK_EVERY_16_DF .. _CRTDBG_CHECK_EVERY_1024_DF 。
答案 12 :(得分:0)
我想补充一点经验。在过去的几天里,我在我的应用程序中解决了这个错误的实例。在我的特定情况下,代码中的错误是:
Control.Invoke
并处置一个托管对象,该托管对象包含回调所属的本机对象。Control.Invoke
结束之前,它将在回调调用中保持阻塞状态)。我应该澄清一下,我使用boost::thread
,所以我使用成员函数作为线程函数。Control.BeginInvoke
(我的GUI使用Winforms),以便本机线程可以在对象被销毁之前结束(回调的目的正是通知线程结束并且可以销毁对象。)答案 13 :(得分:0)
我遇到了类似的问题 - 它随机弹出。也许在构建文件中有些东西是腐败的,但我最后通过先清理项目然后重建来修复它。
所以除了给出的其他回复:
什么样的事情会导致这些错误? 构建文件中的某些内容已损坏。
如何调试它们? 清理项目并重建。如果它已修复,这可能就是问题所在。
答案 14 :(得分:0)
我也遇到了这个问题。就我而言,我分配了x大小的内存,并为x + n大小附加了数据。因此,释放时显示堆溢出。只需确保分配的内存足够,然后检查内存中添加了多少字节即可。