Dela via


Information om CRT-felsöknings-heap

CRT-felsöknings heap och relaterade funktioner ger många sätt att spåra och felsöka problem med minneshantering i koden. Du kan använda den för att hitta buffertöverskridningar och för att spåra och rapportera minnesallokeringar och minnestillstånd. Den har också stöd för att skapa egna felsökningsallokeringsfunktioner för dina unika appbehov.

Hitta buffertöverskridningar med felsöknings-heap

Två av de vanligaste och svårlösta problem som programmerare stöter på skriver över slutet på en allokerad buffert och minnesläckor (misslyckas med att frigöra allokeringar när de inte längre behövs). Felsöknings-heapen innehåller kraftfulla verktyg för att lösa problem med minnesallokering av den här typen.

Felsökningsversionerna av heapfunktionerna anropar standardversionerna eller basversionerna som används i Versionsversioner. När du begär ett minnesblock allokerar felsökningshanteraren från bashögen ett något större minnesblock än du begärde och returnerar en pekare till din del av blocket. Anta till exempel att programmet innehåller anropet: malloc( 10 ). I en Versionsversion malloc anropar du den grundläggande heap-allokeringsrutinen och begär en allokering på 10 byte. I en felsökningsversion anropar mallocdock _malloc_dbg , som sedan anropar den grundläggande heapallokeringsrutinen och begär en allokering på 10 byte plus cirka 36 byte extra minne. Alla resulterande minnesblock i felsöknings-heapen är anslutna i en enda länkad lista, ordnade enligt när de allokerades.

Det extra minne som allokeras av felsöknings-heaprutinerna används för bokföringsinformation. Den har pekare som länkar samman felsökningsminnesblock och små buffertar på båda sidor av dina data för att fånga överskrivningar av den allokerade regionen.

För närvarande deklareras blockhuvudstrukturen som används för att lagra felsöknings heapens bokföringsinformation i <crtdbg.h> rubriken och definieras i <debug_heap.cpp> CRT-källfilen. Konceptuellt liknar den den här strukturen:

typedef struct _CrtMemBlockHeader
{
// Pointer to the block allocated just before this one:
    _CrtMemBlockHeader* _block_header_next;
// Pointer to the block allocated just after this one:
    _CrtMemBlockHeader* _block_header_prev;
    char const*         _file_name;
    int                 _line_number;

    int                 _block_use;      // Type of block
    size_t              _data_size;      // Size of user block

    long                _request_number; // Allocation number
// Buffer just before (lower than) the user's memory:
    unsigned char       _gap[no_mans_land_size];

    // Followed by:
    // unsigned char    _data[_data_size];
    // unsigned char    _another_gap[no_mans_land_size];
} _CrtMemBlockHeader;

Buffertarna no_mans_land på vardera sidan av användardataområdet i blocket är för närvarande 4 byte i storlek och är fyllda med ett känt bytevärde som används av felsöknings-heaprutinerna för att verifiera att gränserna för användarens minnesblock inte har skrivits över. Felsöknings-heapen fyller också nya minnesblock med ett känt värde. Om du väljer att behålla frigjorda block i heapens länkade lista fylls även dessa frigjorda block med ett känt värde. För närvarande används de faktiska bytevärdena på följande sätt:

Kod Beskrivning
no_mans_land (0xFD) Buffertarna "no_mans_land" på båda sidor av minnet som används av ett program är för närvarande fyllda med 0xFD.
Frigjorda block (0xDD) De frigjorda blocken som inte används i felsöknings heapens länkade lista när _CRTDBG_DELAY_FREE_MEM_DF flaggan har angetts är för närvarande fylld med 0xDD.
Nya objekt (0xCD) Nya objekt fylls med 0xCD när de allokeras.

Typer av block på felsöknings-heapen

Varje minnesblock i felsöknings heapen tilldelas till en av fem allokeringstyper. Dessa typer spåras och rapporteras på olika sätt i syfte att identifiera läckor och rapportera tillstånd. Du kan ange ett blocktyp genom att allokera den med hjälp av ett direktanrop till någon av funktionerna för felsökning av heapallokering, till exempel _malloc_dbg. De fem typerna av minnesblock i felsöknings-heapen nBlockUse (som anges i strukturens medlem _CrtMemBlockHeader ) är följande:

_NORMAL_BLOCK
Ett anrop till malloc eller calloc skapar ett normalblock. Om du endast tänker använda normalblock och inte behöver klientblock kanske du vill definiera _CRTDBG_MAP_ALLOC. _CRTDBG_MAP_ALLOC gör att alla heap-allokeringsanrop mappas till deras felsökningsekvivalenter i felsökningsversioner. Det tillåter lagring av filnamn och radnummerinformation om varje allokeringsanrop i motsvarande blockrubrik.

_CRT_BLOCK
Minnesblocken som allokeras internt av många körningsbiblioteksfunktioner markeras som CRT-block så att de kan hanteras separat. Därför kan läckageidentifiering och andra åtgärder förbli opåverkade av dem. En allokering får aldrig allokera, omallokera eller frigöra något block av CRT-typ.

_CLIENT_BLOCK
Ett program kan hålla särskild koll på en viss grupp med allokeringar i felsökningssyfte genom att allokera dem som den här typen av minnesblock med hjälp av explicita anrop till felsöknings-heapfunktionerna. MFC allokerar till exempel alla CObject objekt som klientblock. Andra program kan behålla olika minnesobjekt i klientblock. Undertyper av klientblock kan också anges för större spårningskornighet. Om du vill ange undertyper av klientblock flyttar du antalet kvar med 16 bitar och OR det med _CLIENT_BLOCK. Till exempel:

#define MYSUBTYPE 4
freedbg(pbData, _CLIENT_BLOCK|(MYSUBTYPE<<16));

En hook-funktion som tillhandahålls av klienten för dumpning av objekt som lagras i klientblock kan installeras med , _CrtSetDumpClientoch anropas sedan när ett klientblock dumpas av en felsökningsfunktion. _CrtDoForAllClientObjects Kan också användas för att anropa en viss funktion som tillhandahålls av programmet för varje klientblock i felsöknings-heapen.

_FREE_BLOCK
Normalt tas block som frigörs bort från listan. Om du vill kontrollera att frigjort minne inte skrivs till eller för att simulera låg minnesanvändning kan du behålla frigjorda block i den länkade listan, markerad som Kostnadsfri och fylld med ett känt bytevärde (för närvarande 0xDD).

_IGNORE_BLOCK
Det går att inaktivera felsöknings-heapåtgärderna under ett visst intervall. Under den här tiden sparas minnesblock i listan, men markeras som Ignorera block.

Om du vill fastställa typen och undertypen för ett visst block använder du funktionen _CrtReportBlockType och makrona _BLOCK_TYPE och _BLOCK_SUBTYPE. Makrona definieras på <crtdbg.h> följande sätt:

#define _BLOCK_TYPE(block)          (block & 0xFFFF)
#define _BLOCK_SUBTYPE(block)       (block >> 16 & 0xFFFF)

Kontrollera heapintegritet och minnesläckor

Många av funktionerna i felsökningshögen måste nås inifrån koden. I följande avsnitt beskrivs några av funktionerna och hur du använder dem.

_CrtCheckMemory
Du kan använda ett anrop till till _CrtCheckMemoryexempel för att kontrollera heapens integritet när som helst. Den här funktionen inspekterar varje minnesblock i heapen. Den verifierar att minnesblockets rubrikinformation är giltig och bekräftar att buffertarna inte har ändrats.

_CrtSetDbgFlag
Du kan styra hur felsöknings-heapen håller reda på allokeringar med hjälp av en intern flagga, _crtDbgFlag, som kan läsas och ställas in med hjälp av _CrtSetDbgFlag funktionen. Genom att ändra den här flaggan kan du instruera felsöknings-heapen att söka efter minnesläckor när programmet avslutas och rapportera eventuella läckor som identifieras. På samma sätt kan du be heapen att lämna frigjorda minnesblock i den länkade listan för att simulera situationer med lågt minne. När heapen kontrolleras inspekteras dessa frigjorda block i sin helhet för att säkerställa att de inte har störts.

Flaggan _crtDbgFlag innehåller följande bitfält:

Bitfält Standardvärde Beskrivning
_CRTDBG_ALLOC_MEM_DF Aktiverar felsökningsallokering. När den här biten är inaktiverad förblir allokeringarna sammanlänkade, men deras blocktyp är _IGNORE_BLOCK.
_CRTDBG_DELAY_FREE_MEM_DF Av Förhindrar att minnet faktiskt frigörs, precis som för att simulera lågminnesförhållanden. När den här biten är på sparas frigjorda block i felsöknings heapens länkade lista men markeras som _FREE_BLOCK och fylls med ett särskilt bytevärde.
_CRTDBG_CHECK_ALWAYS_DF Av Orsaker _CrtCheckMemory som ska anropas vid varje allokering och frigöring. Körningen går långsammare, men den fångar upp fel snabbt.
_CRTDBG_CHECK_CRT_DF Av Orsakar block som markerats som typ _CRT_BLOCK som ska ingå i åtgärder för läckageidentifiering och tillståndsskillnad. När den här biten är avstängd ignoreras det minne som används internt av körningsbiblioteket under sådana åtgärder.
_CRTDBG_LEAK_CHECK_DF Av Gör att läckagekontroll utförs vid programavslut via ett anrop till _CrtDumpMemoryLeaks. En felrapport genereras om programmet inte har lyckats frigöra allt minne som det allokerade.

Konfigurera felsöknings-heapen

Alla anrop till heap-funktioner som malloc, free, calloc, realloc, newoch delete matcha för att felsöka versioner av de funktioner som fungerar i felsöknings-heapen. När du frigör ett minnesblock kontrollerar felsöknings-heapen automatiskt integriteten för buffertarna på båda sidor av det allokerade området och utfärdar en felrapport om överskrivning har inträffat.

Så här använder du felsöknings-heapen

Länka felsökningsversionen av ditt program med en felsökningsversion av C-körningsbiblioteket.

Ändra ett eller flera _crtDbgFlag bitfält och skapa ett nytt tillstånd för flaggan

  1. Anropa _CrtSetDbgFlag med parametern inställd newFlag_CRTDBG_REPORT_FLAG (för att hämta aktuellt _crtDbgFlag tillstånd) och lagra det returnerade värdet i en tillfällig variabel.

  2. Aktivera eventuella bitar med hjälp av en bitvis | operator ("eller") på den tillfälliga variabeln med motsvarande bitmasker (representeras i programkoden av manifestkonstanter).

  3. Inaktivera de andra bitarna med hjälp av en bitvis & operator ("och") på variabeln med en bitvis ~ operator ("inte" eller komplement) för lämpliga bitmasker.

  4. Anropa _CrtSetDbgFlag med parametern newFlag inställd på värdet som lagras i den tillfälliga variabeln för att skapa det nya tillståndet för _crtDbgFlag.

    Följande kodrader aktiverar till exempel automatisk läckageidentifiering och inaktiverar kontroller av block av typen _CRT_BLOCK:

    // Get current flag
    int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
    
    // Turn on leak-checking bit.
    tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
    
    // Turn off CRT block checking bit.
    tmpFlag &= ~_CRTDBG_CHECK_CRT_DF;
    
    // Set flag to the new value.
    _CrtSetDbgFlag( tmpFlag );
    

new, deleteoch _CLIENT_BLOCK allokeringar i felsöknings-heapen C++

Felsökningsversionerna av C-körningsbiblioteket innehåller felsökningsversioner av C++ new och delete operatorer. Om du använder _CLIENT_BLOCK allokeringstypen måste du anropa felsökningsversionen av operatorn new direkt eller skapa makron som ersätter operatorn new i felsökningsläge, enligt följande exempel:

/* MyDbgNew.h
 Defines global operator new to allocate from
 client blocks
*/

#ifdef _DEBUG
   #define DEBUG_CLIENTBLOCK   new( _CLIENT_BLOCK, __FILE__, __LINE__)
#else
   #define DEBUG_CLIENTBLOCK
#endif // _DEBUG

/* MyApp.cpp
        Use a default workspace for a Console Application to
 *      build a Debug version of this code
*/

#include "crtdbg.h"
#include "mydbgnew.h"

#ifdef _DEBUG
#define new DEBUG_CLIENTBLOCK
#endif

int main( )   {
    char *p1;
    p1 =  new char[40];
    _CrtMemDumpAllObjectsSince( NULL );
}

Operatörens delete felsökningsversion fungerar med alla blocktyper och kräver inga ändringar i programmet när du kompilerar en versionsversion.

Rapporteringsfunktioner för heaptillstånd

Om du vill samla in en sammanfattningsögonblicksbild av heapens tillstånd vid en viss tidpunkt använder du strukturen _CrtMemState som definieras i <crtdbg.h>:

typedef struct _CrtMemState
{
    // Pointer to the most recently allocated block:
    struct _CrtMemBlockHeader * pBlockHeader;
    // A counter for each of the 5 types of block:
    size_t lCounts[_MAX_BLOCKS];
    // Total bytes allocated in each block type:
    size_t lSizes[_MAX_BLOCKS];
    // The most bytes allocated at a time up to now:
    size_t lHighWaterCount;
    // The total bytes allocated at present:
    size_t lTotalCount;
} _CrtMemState;

Den här strukturen sparar en pekare till det första (senast allokerade) blocket i felsöknings heapens länkade lista. I två matriser registrerar den sedan hur många av varje typ av minnesblock (_NORMAL_BLOCK, , _CLIENT_BLOCK_FREE_BLOCKoch så vidare) som finns i listan och antalet byte som allokeras i varje typ av block. Slutligen registrerar den det högsta antalet byte som allokerats i heapen som helhet fram till den punkten och antalet byte som för närvarande allokeras.

Andra CRT-rapporteringsfunktioner

Följande funktioner rapporterar heapens tillstånd och innehåll och använder informationen för att identifiera minnesläckor och andra problem.

Funktion Beskrivning
_CrtMemCheckpoint Sparar en ögonblicksbild av heapen i en _CrtMemState struktur som tillhandahålls av programmet.
_CrtMemDifference Jämför två minnestillståndsstrukturer, sparar skillnaden mellan dem i en tredje tillståndsstruktur och returnerar TRUE om de två tillstånden skiljer sig åt.
_CrtMemDumpStatistics Dumpar en viss _CrtMemState struktur. Strukturen kan innehålla en ögonblicksbild av tillståndet för felsöknings heapen vid en viss tidpunkt eller skillnaden mellan två ögonblicksbilder.
_CrtMemDumpAllObjectsSince Dumpar information om alla objekt som allokerats sedan en viss ögonblicksbild togs av heapen eller från körningens början. Varje gång det dumpar ett _CLIENT_BLOCK block anropas en hook-funktion som tillhandahålls av programmet, om en har installerats med ._CrtSetDumpClient
_CrtDumpMemoryLeaks Avgör om några minnesläckor har inträffat sedan programkörningen startade och i så fall dumpar alla allokerade objekt. Varje gång _CrtDumpMemoryLeaks ett _CLIENT_BLOCK block dumpas anropas en hook-funktion som tillhandahålls av programmet, om en har installerats med ._CrtSetDumpClient

Spåra heapallokeringsbegäranden

Att känna till källfilens namn och radnummer för ett kontroll- eller rapporteringsmakron är ofta användbart för att hitta orsaken till ett problem. Detsamma gäller förmodligen inte för heapallokeringsfunktioner. Du kan infoga makron på många lämpliga punkter i ett programs logikträd, men en allokering är ofta begravd i en funktion som anropas från många olika platser vid många olika tidpunkter. Frågan är inte vilken kodrad som gjorde en felaktig allokering. I stället är det vilken av de tusentals allokeringar som gjorts av den kodraden var dålig och varför.

Unika nummer för allokeringsbegäran och _crtBreakAlloc

Det finns ett enkelt sätt att identifiera det specifika heapallokeringsanropet som gick dåligt. Den drar nytta av det unika allokeringsbegärandenumret som är associerat med varje block i felsöknings-heapen. När information om ett block rapporteras av en av dumpfunktionerna omges det här allokeringsbegärandenumret i klammerparenteser. Till exempel {36}.

När du känner till allokeringsbegärandenumret för ett felaktigt allokerat block kan du skicka det här numret till _CrtSetBreakAlloc för att skapa en brytpunkt. Körningen avbryts precis innan blocket allokeras och du kan backa för att avgöra vilken rutin som var ansvarig för det felaktiga anropet. För att undvika omkompilering kan du göra samma sak i felsökningsprogrammet genom att ange _crtBreakAlloc det nummer för allokeringsbegäran som du är intresserad av.

Skapa felsökningsversioner av dina allokeringsrutiner

En mer komplex metod är att skapa felsökningsversioner av dina egna allokeringsrutiner, jämförbara med versionerna _dbg av heap-allokeringsfunktionerna. Du kan sedan skicka källfils- och radnummerargument till de underliggande heapallokeringsrutinerna, och du kommer omedelbart att kunna se var en felaktig allokering har sitt ursprung.

Anta till exempel att ditt program innehåller en vanlig rutin som liknar följande exempel:

int addNewRecord(struct RecStruct * prevRecord,
                 int recType, int recAccess)
{
    // ...code omitted through actual allocation...
    if ((newRec = malloc(recSize)) == NULL)
    // ... rest of routine omitted too ...
}

I en rubrikfil kan du lägga till kod, till exempel följande exempel:

#ifdef _DEBUG
#define  addNewRecord(p, t, a) \
            addNewRecord(p, t, a, __FILE__, __LINE__)
#endif

Därefter kan du ändra allokeringen i din postskapanderutin enligt följande:

int addNewRecord(struct RecStruct *prevRecord,
                int recType, int recAccess
#ifdef _DEBUG
               , const char *srcFile, int srcLine
#endif
    )
{
    /* ... code omitted through actual allocation ... */
    if ((newRec = _malloc_dbg(recSize, _NORMAL_BLOCK,
            srcFile, scrLine)) == NULL)
    /* ... rest of routine omitted too ... */
}

Nu lagras källfilens namn och radnummer där addNewRecord anropades i varje resulterande block som allokerats i felsöknings heapen och rapporteras när blocket granskas.

Se även

Felsöka intern kod