2010-03-18 206 views
19

我想知道是否有一种很好的方法来查找导致堆损坏错误的源代码,因为在分配的堆之外写入数据的内存地址在Visual Studio中阻止;Visual Studio - 如何找到堆损坏错误的来源

专线(0008)免费列表元素26F7F670是错误的大小(死)

(尝试写上如何找到内存错误的一些注意事项)提前

谢谢!

回答

1

您可以在写入内存地址时设置断点。然后调试器会向您显示写入该位置的代码,但仍需要确定哪些写入会导致此问题。

0

我假设C++为语言。

如果错误是可重现的并且损坏的地址总是相同的,那么可以在写入此地址时放置一个数据断点来停止程序。

+1

语言是C/C++混合。在每个调试会话中,被破坏的地址是不同的,所以我想这是不可能使用数据断点的 – Danne 2010-03-18 13:49:13

+1

你很遗憾是对的。 在这些情况下,我的做法是#define free/delete为无。如果问题消失,我将#define malloc/new/free/delete添加到记录每个调用的函数中,以查找没有分配的重复删除或删除操作。 – Timores 2010-03-19 07:51:41

3

也许你可以试试微软的应用程序验证器。 它通过打开对堆操作的额外检查为我解决了一次类似的问题。 在我看来,破坏地址的随机性是因为堆可能会“微妙地”受损,并且直到堆中发生大的事情(如大量分配/空闲)才会显示问题。

36

开始安装的WinDbg:

http://www.microsoft.com/whdc/Devtools/Debugging/default.mspx

然后打开这样的页堆:

gflags.exe –p /enable yourexecutable.exe /full 

这将每堆分配后插入非写页面。

在从windbg内部启动可执行文件之后,堆外的任何写入操作现在都会被此调试器捕获。事后关闭页堆的使用:如何使用页堆here

gflags.exe -p /disable yourexecutable.exe 

更多信息。

+3

最佳解决方案!救了我。另外,直接打开gflags.exe并使用GUI也可以。转到“图像文件”,填写exe文件名称,并选中“启用页面堆”。任何调试器都可以工作。 – Eliko 2015-02-27 12:08:12

+1

非常感谢。我发现这种技术需要两天时间。我只是检查“启用页堆”,并正常使用Visual Studio调试器。然后,它会在导致堆损坏错误的代码位置处完全中断。我的错误是由于一个线程不断地提供数据,而其他线程只分配足够用于旧数据的内存,这对于存储新数据是不够的。 – khanhhh89 2016-03-23 02:13:51

+0

我试图安装这个调试工具,但它要求我卸载最新的SDK。请建议 – 2016-08-31 04:53:24

2

它可能太晚了,但如果它编译与gcc,并可以在Linux上运行,你可以使用valgrind找到问题的根源(我不记得旗帜,我只用了一次,取得了巨大的成功)。关于GFLAGS和页堆(这帮助了很多)

+0

祝你好运,微软已经竭尽全力确保他们的字符串处理命令与尽可能多的标准不兼容。如果这很简单,你可能会逃避。 – Owl 2017-10-10 13:37:16

2

对于Windows 10,你可以启用PageHeap option in the GFlags Tool,该工具是作为Debugging Tools for Windows的一部分。

GFlags中的Page Heap选项允许您选择标准堆验证或整页堆验证。请注意,整个堆验证为每个分配使用一整页内存,因此可能导致系统内存不足。

要启用页堆在GFlags在:

•要启用标准页堆验证,标准版将在每个堆分配的最后写上图案,然后检查模式时的分配被释放。

要验证的所有进​​程使用:

GFLAGS/R + HPA

GFLAGS/K + HPA

为单个进程使用:

个GFLAGS/P /启用映像文件名称

•要启用一个过程整页堆验证,此选项在每次分配的结尾放置一个不可访问页面,这样的计划,如果它试图访问立即停止内存超出分配范围,由于内存消耗过多,因此只能在单个进程中使用。

GFLAGS/I映像文件名称+ HPA

GFLAGS/P /启用映像文件名称/全

上面的两个命令是可互换的。

注意:上面提到的所有页面堆设置都是存储在注册表中的系统范围设置(除了/ k)并且保持有效直到您更改它们。该/ K设置是核心标志设置是为这次会议设定并会丢失Windows关机时

另一个有用的工具是Application Verifier,但是这不是Windows调试工具的一部分,而它包含在Windows Software Development Kit (SDK)

0

确保所链接的所有库都与您正在运行的应用程序在相同的CLR版本中进行编译 - 全部在Release或全部在Debug中。

在Debug和Release中进行编译时,实际上是针对两个不同版本的C运行时库。这些版本完全不同,它们使用不同的策略分配内存,并使用不同的堆。但要知道的最重要的事情是它们彼此不兼容。

Release C运行时库按预期分配内存,而Debug将添加额外的信息,例如保护块来跟踪缓冲区溢出和调用分配函数的位置,并且反过来它会分配比Release更多的内存。

如果您将应用程序链接到在Release和Debug中构建的DLL组合,最有可能最终尝试删除在另一个CLR中创建的对象。这意味着你将试图释放比分配给对象更多或更少的内存,这会破坏堆。

您应该构建应用程序,并附加到在相同配置下构建的库(Release或Debug)。

特别是在使用不同编译器编译的模块中,可能会发生此问题。

有一种解决办法,我会提到但不建议。如果由于某种原因,您仍然需要以不同的模式构建,这个解决方案将允许从相同的共享堆中分配和释放所有内存。 API GetProcessHeap将允许您在整个不同模块中访问共享堆。通过使用HeapAlloc & HeapFree,您可以在共享堆中分配和释放内存。注意:HeapAlloc和HeapFree应该将您的应用程序中的所有调用都替换为malloc并释放。

+0

我在调试和发布版本中都针对相同版本的3D方库(例如OpenCV)进行编译。据我所知,这只意味着在调试时我不能进入任何三方代码,并且它在调试时也运行得更快。你认为我错了吗? – 2017-08-22 00:46:36

+0

@ILIABROUDNO通常分发的第三方库将分发其他人可以在发布或调试模式下使用的发布库。他们通过构建它们的DLL来包含它们的C运行时库拷贝来实现这一点,同时,它们确保不共享CRT资源,例如跨越库边界的堆,以确保动态分配的内存将在相同的时间释放边界的一侧。 总之,如果您链​​接的第三方库已经这样做了,那么在Release和Debug中使用它们应该没有问题。 – 2017-08-22 06:41:16

+0

@ILIABROUDNO如果这有帮助,请+1 :) – 2017-08-22 06:43:25