Jeśli narzędzia zautomatyzowane (takie jak ogrodzenie elektryczne lub valgrind) nie rozwiązują problemu, i uważnie wpatrując się w kod, aby spróbować określić, gdzie mogło dojść do zła, nie pomaga i wyłącza/umożliwia różne operacje (dopóki nie uzyskasz korelacji między obecnością zepsucia sterty a operacjami, które wykonały lub nie wykonałeś wcześniej) aby zawęzić to nie działa, zawsze możesz spróbować tej techniki, która próbuje znaleźć korupcję wcześniej niż później tak, aby łatwiej wyśledzić źródło:
tworzyć własne nowe i usunąć operatorów, które wystawią obszary ochronne korupcyjnych oczywiste wokół przydzielonych obszarów pamięci, coś takiego:
#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);}
... powyższe elementy wystarczą, aby uzyskać funkcjonalność w stylu ogrodzenia elektrycznego, pod warunkiem, że cokolwiek napisze na jednym z dwóch 64-bajtowych "pasm ochronnych" na początku lub na końcu każdej nowej/usuwanej pamięci -alokacja, a następnie po usunięciu alokacji, MyCustomDelete() zauważy uszkodzenie i awarię programu.
Jeśli to nie jest wystarczająco dobre (np. Ponieważ do czasu usunięcia pojawiło się tak wiele od czasu korupcji, że trudno powiedzieć, co spowodowało uszkodzenie), możesz pójść jeszcze dalej, dodając przydzielone MyCustomAlloc() bufor do pojedynczej/globalnej podwójnie połączonej listy przydziałów i niech MyCustomDelete() usunie ją z tej samej listy (upewnij się, że serializuje te operacje, jeśli twój program jest wielowątkowy!). Zaletą takiego działania jest możliwość dodania innej funkcji zwanej np. CheckForHeapCorruption(), które będzie iterować na tej połączonej liście i sprawdzać zakresy zabezpieczeń dla każdej alokacji na połączonej liście i raportować, czy którykolwiek z nich został uszkodzony. Następnie możesz spryskiwać wywołania funkcji CheckForHeapCorruption() w całym kodzie, tak aby po wystąpieniu uszkodzenia sterty zostanie wykryte podczas następnego wywołania funkcji CheckForHeapCorruption(), a nie później. W końcu okaże się, że jedno wywołanie CheckForHeapCorruption() przeszło z kolorami latającymi, a następnie następne wywołanie CheckForHeapCorruption(), zaledwie kilka linii później, wykryło uszkodzenie, w którym to momencie wiesz, że uszkodzenie było spowodowane przez kod wykonany między dwa wywołania CheckForHeapCorruption(), a następnie możesz przestudiować ten kod, aby dowiedzieć się, co robi źle, i/lub dodać więcej wywołań CheckForHeapCorruption() do tego kodu, jeśli to konieczne.
Powtarzaj, aż błąd stanie się oczywisty. Powodzenia!
Dlaczego '256 + 1' w' memset', kiedy przydzielono tylko '256' bajtów? –
W swojej większej aplikacji, skąd wiesz, że sterty zostały uszkodzone? Jakie narzędzie cię o tym poinformowało? – PaulMcKenzie
@ T.Z. Aby zademonstrować rodzaj korupcji, która mogłaby wystąpić ... – StoryTeller