我知道已经存在许多类似的问题和答案,但我无法解决我的问题。
在我的大应用程序中,堆在某处被破坏,我无法找到它。我也使用像gflags这样的工具,但没有运气。
我在下面的示例中尝试了gflags,它会破坏堆的目的:
char* pBuffer = new char[256];
memset(pBuffer, 0, 256 + 1);
delete[] pBuffer;
第2行会覆盖堆,但如何通过gflags,windbg等工具找到它。可能是我没有正确使用gflags。
答案 0 :(得分:2)
如果自动化工具(如电栅栏或valgrind)没有做到这一点,并专心盯着你的代码试图找出它可能出错的地方并没有帮助,并且禁用/启用各种操作(直到你得到堆腐败的存在和之前已执行或未执行的操作之间的关联)来缩小它似乎不起作用,你总是可以尝试这种技术,试图尽早发现腐败,以便更容易追查来源:
创建自己的自定义new和delete运算符,在分配的内存区域周围放置明显的防护区域,如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <new>
// make this however big you feel is "big enough" so that corrupted bytes will be seen in the guard bands
static int GUARD_BAND_SIZE_BYTES = 64;
static void * MyCustomAlloc(size_t userNumBytes)
{
// We'll allocate space for a guard-band, then space to store the user's allocation-size-value,
// then space for the user's actual data bytes, then finally space for a second guard-band at the end.
char * buf = (char *) malloc(GUARD_BAND_SIZE_BYTES+sizeof(userNumBytes)+userNumBytes+GUARD_BAND_SIZE_BYTES);
if (buf)
{
char * w = buf;
memset(w, 'B', GUARD_BAND_SIZE_BYTES); w += GUARD_BAND_SIZE_BYTES;
memcpy(w, &userNumBytes, sizeof(userNumBytes)); w += sizeof(userNumBytes);
char * userRetVal = w; w += userNumBytes;
memset(w, 'E', GUARD_BAND_SIZE_BYTES); w += GUARD_BAND_SIZE_BYTES;
return userRetVal;
}
else throw std::bad_alloc();
}
static void MyCustomDelete(void * p)
{
if (p == NULL) return; // since delete NULL is a safe no-op
// Convert the user's pointer back to a pointer to the top of our header bytes
char * internalCP = ((char *) p)-(GUARD_BAND_SIZE_BYTES+sizeof(size_t));
char * cp = internalCP;
for (int i=0; i<GUARD_BAND_SIZE_BYTES; i++)
{
if (*cp++ != 'B')
{
printf("CORRUPTION DETECTED at BEGIN GUARD BAND POSITION %i of allocation %p\n", i, p);
abort();
}
}
// At this point, (cp) should be pointing to the stored (userNumBytes) field
size_t userNumBytes = *((const size_t *)cp);
cp += sizeof(userNumBytes); // skip past the user's data
cp += userNumBytes;
// At this point, (cp) should be pointing to the second guard band
for (int i=0; i<GUARD_BAND_SIZE_BYTES; i++)
{
if (*cp++ != 'E')
{
printf("CORRUPTION DETECTED at END GUARD BAND POSITION %i of allocation %p\n", i, p);
abort();
}
}
// If we got here, no corruption was detected, so free the memory and carry on
free(internalCP);
}
// override the global C++ new/delete operators to call our
// instrumented functions rather than their normal behavior
void * operator new(size_t s) throw(std::bad_alloc) {return MyCustomAlloc(s);}
void * operator new[](size_t s) throw(std::bad_alloc) {return MyCustomAlloc(s);}
void operator delete(void * p) throw() {MyCustomDelete(p);}
void operator delete[](void * p) throw() {MyCustomDelete(p);}
......上面的内容足以让你获得Electric-Fence风格的功能,因为如果有任何内容写入两个64字节&#34;保护频带中的任何一个&#34;在任何新的/删除内存分配的开头或结尾,然后当分配被删除时,MyCustomDelete()会注意到损坏并使程序崩溃。
如果这还不够好(例如,因为在删除发生以来已经发生了很多事情,因为很难说出导致腐败的原因),你可以更进一步让MyCustomAlloc()将分配的缓冲区添加到单个/全局双向链接的分配列表中,并让MyCustomDelete()从同一个列表中删除它(如果你的程序是多线程的,请确保序列化这些操作!)。这样做的好处是,您可以添加另一个称为例如CheckForHeapCorruption()将迭代该链表并检查链表中每个分配的保护带,并报告其中是否有任何损坏。然后,您可以在整个代码中调用CheckForHeapCorruption(),这样当发生堆损坏时,将在下次调用CheckForHeapCorruption()时检测到它,而不是在稍后的某个时间。最终你会发现一个调用CheckForHeapCorruption()的调用以飞扬的颜色传递,然后下一次调用CheckForHeapCorruption(),几行之后就检测到了损坏,此时你就知道腐败是由执行之间执行的任何代码引起的。两次调用CheckForHeapCorruption(),然后你可以研究那个特定的代码来弄清楚它做错了什么,和/或在必要的时候将更多的CheckForHeapCorruption()调用添加到该代码中。
重复直到bug变得明显。祝你好运!
答案 1 :(得分:1)
如果同一个变量一直被破坏,数据断点是查找负责更改的代码的快速而简单的方法(如果您的IDE支持它们)。 (调试 - >新断点 - &gt; MS Visual Studio 2008中的新数据断点...)。如果您的堆损坏更随机,他们将无法帮助(但我认为,如果它有帮助,我会分享简单的答案)。
答案 2 :(得分:0)
我认为在Windows上也支持一种称为电围栏的工具。
基本上,它的作用是劫持malloc和co,使每个分配在页面边界处结束并标记下一页不可访问。
效果是在缓冲区溢出时出现seg错误。
它可能还有缓冲区欠载的选项。
答案 3 :(得分:0)
请阅读此链接 Visual Studio - how to find source of heap corruption errors
Is there a good Valgrind substitute for Windows?
它讲述了在Windows上查找堆问题的技巧。
但另一方面,您总是可以编写(如果您正在编写新代码)内存管理器。 要做的是:使用你的包装api,它将调用malloc / calloc等。
假设你有api myMalloc(size_t len); 然后在你的函数里面,你可以尝试配置HEADER + len + FOOTER。 在您的标题保存信息,如分配大小或可能是更多信息。在页脚,添加一些神奇的数字,如deadbeef。并从myMalloc返回ptr(来自malloc)+ HEADER。
当使用myfree(void * ptr)释放它时,然后只需执行ptr -HEADER,检查len,然后跳转到FOOTER = ptr-HEADER +真正的全部len。在这个偏移处,你应该找到deadbeef,如果你没找到,那么你知道,它已经被破坏了。