2015-11-24 2 views
8

Я знаю, что уже существует много подобных вопросов и ответов, но я не могу решить свою проблему.Узнайте, где память кучи повреждена

В моем большом приложении куча где-то развращается, и я не могу его найти. Я использовал инструмент как gflags, но не повезло.

Я попытался GFLAGS на следующем примере, который развращает кучи по назначению:

char* pBuffer = new char[256]; 
memset(pBuffer, 0, 256 + 1); 
delete[] pBuffer; 

В строке # 2 кучи перезаписаны, но как найти его с помощью таких инструментов, как GFlags, WinDbg и т.д. Может быть, я не правильно используя gflags.

+3

Почему '256 + 1' в' memset', когда вы выделили только '256' байт? –

+3

В вашем более крупном приложении, как вы узнали, что куча повреждена? Какой инструмент проинформировал вас об этом? – PaulMcKenzie

+3

@ T.Z. Чтобы продемонстрировать вид коррупции, которая может произойти ... – StoryTeller

ответ

1

Если одна и та же переменная постоянно повреждена, точки прерывания данных - это быстрый и простой способ найти код, ответственный за изменение (если ваша IDE поддерживает их). (Debug-> New Break Point-> Новая точка останова данных ... в MS Visual Studio 2008). Они не помогут, если ваше повреждение кучи более случайное (но я решил бы поделиться простым ответом, если это поможет).

0

Существует инструмент под названием электрический забор, который, как мне кажется, поддерживается также и в Windows.

По существу, это то, что он делает, - это захват malloc и co, чтобы каждый конец выделения на границе страницы и отмечал следующую недоступную страницу.

Эффект заключается в том, что вы получаете ошибку seg при переполнении буфера.

Возможно, у него также есть опция для переполнения буфера.

0

Пожалуйста, прочтите эту ссылку Visual Studio - how to find source of heap corruption errors

Is there a good Valgrind substitute for Windows?

Это говорит метод для поиска проблем кучи на окнах.

Но, с другой стороны, вы всегда можете написать (если вы пишете новый код) менеджеров памяти. Способ использования: использовать вашу обертку apis, которая будет вызывать malloc/calloc и т. Д.

Предположим, у вас есть api myMalloc (size_t len); , то внутри вашей функции вы можете попробовать allocg HEADER + len + FOOTER. На вашем заголовке сохраняйте информацию как размер выделения или может быть больше информации. В нижнем колонтитуле добавьте магическое число, подобное deadbeef. И верните ptr (из malloc) + HEADER из myMalloc.

Когда вы освобождаете его, используя myfree (void * ptr), тогда просто сделайте ptr -HEADER, проверьте len, а затем прыгайте на FOOTER = ptr-HEADER + на самом деле с защелкой. При таком зачете вы должны найти deadbeef, и если вы не найдете его, то вы знаете, что он был поврежден.

0

Если автоматические инструменты (например, электрический забор или 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() в этот код по мере необходимости.

Повторяйте до тех пор, пока ошибка не станет очевидной. Удачи!

Смежные вопросы