Если автоматические инструменты (например, электрический забор или valgrind) не делают трюка и пристально смотрят на ваш код, чтобы попытаться выяснить, куда он поступил неправильно, это не помогает, а также отключает/разрешает различные операции (пока вы не получите корреляцию между наличием кучи-повреждения и тем, какие операции выполнялись или не выполнялись заранее), чтобы сузить его, похоже, не работает, вы всегда можете попробовать эту технику, которая пытается найти коррупцию раньше, чем позже , таким образом, чтобы сделать его легче отследить источник:
Создайте свой собственный новый и удалять операторы, которые ставят коррупционные разумеющееся области охраны вокруг выделенных областей памяти, что-то вроде этого:
#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-байтных «защитных полос» в начале или в конце любой новой/удаленной памяти -распределение, то, когда выделение будет удалено, MyCustomDelete() увидит повреждение и потерпит крах программы.
Если это не достаточно хорошо (например, потому что к моменту удаления произошло так много с момента коррупции, что трудно сказать, что послужило причиной коррупции), вы можете пойти еще дальше, добавив MyCustomAlloc() буфера в одноточечный/глобальный дважды связанный список распределений, и MyCustomDelete() удаляет его из того же списка (обязательно, чтобы сериализовать эти операции, если ваша программа многопоточная!). Преимущество этого заключается в том, что вы можете добавить еще одну функцию, например, например. CheckForHeapCorruption(), который будет перебирать этот связанный список и проверять защитные полосы каждого распределения в связанном списке и сообщать, если какой-либо из них был поврежден. Затем вы можете посыпать вызовы CheckForHeapCorruption() по всему вашему коду, так что, когда происходит куча коррупции, он будет обнаружен при следующем вызове CheckForHeapCorruption(), а не через некоторое время. В конце концов вы обнаружите, что один вызов CheckForHeapCorruption() передан с летающими цветами, а затем следующий вызов CheckForHeapCorruption(), всего несколько строк позже, обнаружил повреждение, после чего вы знаете, что повреждение было вызвано тем, что код был выполнен между два вызова CheckForHeapCorruption(), и вы можете изучить этот конкретный код, чтобы выяснить, что он делает неправильно, и/или добавить дополнительные вызовы в CheckForHeapCorruption() в этот код по мере необходимости.
Повторяйте до тех пор, пока ошибка не станет очевидной. Удачи!
Почему '256 + 1' в' memset', когда вы выделили только '256' байт? –
В вашем более крупном приложении, как вы узнали, что куча повреждена? Какой инструмент проинформировал вас об этом? – PaulMcKenzie
@ T.Z. Чтобы продемонстрировать вид коррупции, которая может произойти ... – StoryTeller