Delen via


Details van CRT-foutopsporing

De CRT-foutopsporingsfuncties en gerelateerde functies bieden veel manieren om problemen met geheugenbeheer in uw code bij te houden en op te sporen. U kunt deze gebruiken om bufferoverschrijdingen te vinden en om geheugentoewijzingen en geheugenstatus bij te houden en te rapporteren. Het biedt ook ondersteuning voor het maken van uw eigen toewijzingsfuncties voor foutopsporing voor uw unieke app-behoeften.

Bufferoverschrijdingen zoeken met heap voor foutopsporing

Twee van de meest voorkomende en ontraceerbare problemen die programmeurs tegenkomen, overschrijven het einde van een toegewezen buffer en geheugenlekken (het niet meer nodig is om toewijzingen vrij te maken). De heap voor foutopsporing biedt krachtige hulpprogramma's voor het oplossen van problemen met geheugentoewijzing van dit type.

De foutopsporingsversies van de heap-functies roepen de standaard- of basisversies aan die worden gebruikt in Release-builds. Wanneer u een geheugenblok aanvraagt, wijst de heap-manager voor foutopsporing een iets groter geheugenblok toe vanuit de basis heap dan u hebt aangevraagd en retourneert een aanwijzer naar uw gedeelte van dat blok. Stel dat uw toepassing de aanroep bevat: malloc( 10 ). In een release-build malloc wordt de basis-heap-toewijzingsroutine aangeroepen die een toewijzing van 10 bytes aanvraagt. In een foutopsporingsbuild zou echter een malloc aanroep worden aangeroepen _malloc_dbg, die vervolgens de basis-heap-toewijzingsroutine aanroept die een toewijzing van 10 bytes plus ongeveer 36 bytes extra geheugen aanvraagt. Alle resulterende geheugenblokken in de heap voor foutopsporing zijn verbonden in één gekoppelde lijst, gerangschikt op basis van het moment dat ze zijn toegewezen.

Het extra geheugen dat is toegewezen door de foutopsporingsroutines wordt gebruikt voor het bewaren van informatie. Er worden aanwijzers weergegeven die foutopsporingsblokken aan elkaar koppelen en kleine buffers aan beide zijden van uw gegevens om overschrijven van de toegewezen regio te ondervangen.

Momenteel wordt de blokheaderstructuur die wordt gebruikt voor het opslaan van de logboekregistratiegegevens van de heap gedeclareerd in de <crtdbg.h> header en gedefinieerd in het <debug_heap.cpp> CRT-bronbestand. Conceptueel is het vergelijkbaar met deze structuur:

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;

De no_mans_land buffers aan beide zijden van het gegevensgebied van de gebruiker van het blok zijn momenteel 4 bytes groot en worden gevuld met een bekende bytewaarde die wordt gebruikt door de foutopsporingsroutines om te controleren of de limieten van het geheugenblok van de gebruiker niet zijn overschreven. De heap voor foutopsporing vult ook nieuwe geheugenblokken met een bekende waarde. Als u ervoor kiest om vrije blokken in de gekoppelde lijst van de heap te bewaren, worden deze vrijgemaakte blokken ook gevuld met een bekende waarde. Momenteel zijn de werkelijke bytewaarden die worden gebruikt als volgt:

Code Beschrijving
no_mans_land (0xFD) De buffers 'no_mans_land' aan beide zijden van het geheugen dat door een toepassing wordt gebruikt, worden momenteel gevuld met 0xFD.
Vrije blokken (0xDD) De vrijgemaakte blokken die niet worden gebruikt in de gekoppelde lijst voor foutopsporing van heap wanneer de _CRTDBG_DELAY_FREE_MEM_DF vlag is ingesteld, worden momenteel gevuld met 0xDD.
Nieuwe objecten (0xCD) Nieuwe objecten worden gevuld met 0xCD wanneer ze worden toegewezen.

Typen blokken op de heap voor foutopsporing

Elk geheugenblok in de heap voor foutopsporing wordt toegewezen aan een van de vijf toewijzingstypen. Deze typen worden bijgehouden en verschillend gerapporteerd voor doeleinden van lekdetectie en statusrapportage. U kunt het type van een blok opgeven door het toe te wijzen met behulp van een directe aanroep naar een van de foutopsporingsfuncties voor heap-toewijzing, zoals _malloc_dbg. De vijf typen geheugenblokken in de heap voor foutopsporing (ingesteld in het nBlockUse lid van de _CrtMemBlockHeader structuur) zijn als volgt:

_NORMAL_BLOCK
Een aanroep naar malloc of calloc maakt een normaal blok. Als u alleen normale blokken wilt gebruiken en geen clientblokken nodig hebt, kunt u het volgende definiëren _CRTDBG_MAP_ALLOC. _CRTDBG_MAP_ALLOC zorgt ervoor dat alle heap-toewijzingsaanroepen worden toegewezen aan hun foutopsporingsequivalenten in foutopsporingsversies. Hiermee kunt u informatie over bestandsnaam en regelnummer opslaan over elke toewijzingsoproep in de bijbehorende blokheader.

_CRT_BLOCK
De geheugenblokken die intern worden toegewezen door veel runtimebibliotheekfuncties, worden gemarkeerd als CRT-blokken, zodat ze afzonderlijk kunnen worden verwerkt. Als gevolg hiervan kunnen lekdetectie en andere bewerkingen niet worden beïnvloed door deze bewerkingen. Een toewijzing mag nooit een blok crt-type toewijzen, opnieuw toewijzen of vrijmaken.

_CLIENT_BLOCK
Een toepassing kan een specifieke groep toewijzingen bijhouden voor foutopsporing door ze toe te wijzen als dit type geheugenblok, met behulp van expliciete aanroepen naar de heap-functies voor foutopsporing. MFC wijst bijvoorbeeld alle CObject objecten toe als clientblokken; andere toepassingen kunnen verschillende geheugenobjecten in clientblokken behouden. Subtypen van clientblokken kunnen ook worden opgegeven voor een grotere traceringsgranulariteit. Als u subtypen van clientblokken wilt opgeven, verschuift u het getal naar links met 16 bits en OR met _CLIENT_BLOCK. Voorbeeld:

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

Een door de client geleverde hook-functie voor het dumpen van de objecten die zijn opgeslagen in clientblokken, kan worden geïnstalleerd met behulp van _CrtSetDumpClienten wordt vervolgens aangeroepen wanneer een clientblok wordt gedumpt door een foutopsporingsfunctie. _CrtDoForAllClientObjects Kan ook worden gebruikt om een bepaalde functie aan te roepen die door de toepassing wordt geleverd voor elk clientblok in de heap voor foutopsporing.

_FREE_BLOCK
Normaal gesproken worden blokken verwijderd uit de lijst. Als u wilt controleren of vrijgemaakt geheugen niet naar wordt geschreven of om weinig geheugen te simuleren, kunt u vrije blokken in de gekoppelde lijst behouden, gemarkeerd als Vrij en gevuld met een bekende bytewaarde (momenteel 0xDD).

_IGNORE_BLOCK
Het is mogelijk om de heap-bewerkingen voor foutopsporing gedurende een bepaald interval uit te schakelen. Gedurende deze periode worden geheugenblokken opgeslagen in de lijst, maar worden gemarkeerd als Blokken negeren.

Als u het type en subtype van een bepaald blok wilt bepalen, gebruikt u de functie _CrtReportBlockType en de macro's _BLOCK_TYPE en _BLOCK_SUBTYPE. De macro's worden als volgt gedefinieerd <crtdbg.h> :

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

Controleren op heap-integriteit en geheugenlekken

Veel van de functies van heap voor foutopsporing moeten worden geopend vanuit uw code. In de volgende sectie worden enkele van de functies beschreven en hoe u deze kunt gebruiken.

_CrtCheckMemory
U kunt bijvoorbeeld een aanroep gebruiken om _CrtCheckMemoryde integriteit van de heap op elk moment te controleren. Met deze functie wordt elk geheugenblok in de heap geïnspecteerd. Er wordt gecontroleerd of de headergegevens van het geheugenblok geldig zijn en bevestigt dat de buffers niet zijn gewijzigd.

_CrtSetDbgFlag
U kunt bepalen hoe de heap voor foutopsporing toewijzingen bijhoudt met behulp van een interne vlag, _crtDbgFlagdie kan worden gelezen en ingesteld met behulp van de _CrtSetDbgFlag functie. Door deze vlag te wijzigen, kunt u de heap voor foutopsporing instrueren om te controleren op geheugenlekken wanneer het programma wordt afgesloten en eventuele gedetecteerde lekken rapporteert. Op dezelfde manier kunt u de heap vertellen dat er vrije geheugenblokken in de gekoppelde lijst moeten worden achtergelaten om situaties met weinig geheugen te simuleren. Wanneer de heap wordt gecontroleerd, worden deze vrijgemaakte blokken volledig geïnspecteerd om ervoor te zorgen dat ze niet zijn gestoord.

De _crtDbgFlag vlag bevat de volgende bitvelden:

Bitveld Standaardwaarde Beschrijving
_CRTDBG_ALLOC_MEM_DF Op Schakelt foutopsporingstoewijzing in. Wanneer deze bit is uitgeschakeld, blijven toewijzingen aan elkaar gekoppeld, maar hun bloktype is _IGNORE_BLOCK.
_CRTDBG_DELAY_FREE_MEM_DF Uit Voorkomt dat geheugen daadwerkelijk wordt vrijgemaakt, zoals voor het simuleren van omstandigheden met weinig geheugen. Wanneer deze bit is ingeschakeld, worden vrije blokken bewaard in de gekoppelde lijst voor foutopsporing in heap, maar zijn gemarkeerd als _FREE_BLOCK en gevuld met een speciale bytewaarde.
_CRTDBG_CHECK_ALWAYS_DF Uit Oorzaken _CrtCheckMemory worden aangeroepen bij elke toewijzing en deallocatie. De uitvoering is langzamer, maar er worden snel fouten onderschept.
_CRTDBG_CHECK_CRT_DF Uit Zorgt ervoor dat blokken die als type _CRT_BLOCK zijn gemarkeerd, worden opgenomen in bewerkingen voor lekdetectie en statusverschil. Wanneer deze bit is uitgeschakeld, wordt het geheugen dat intern wordt gebruikt door de runtimebibliotheek genegeerd tijdens dergelijke bewerkingen.
_CRTDBG_LEAK_CHECK_DF Uit Zorgt ervoor dat lekcontrole wordt uitgevoerd bij het afsluiten van het programma via een aanroep naar _CrtDumpMemoryLeaks. Er wordt een foutenrapport gegenereerd als de toepassing niet alle toegewezen geheugen vrijgeeft.

De heap voor foutopsporing configureren

Alle aanroepen naar heap-functies, zoals malloc, free, calloc, realloc, en newdelete oplossen om foutopsporingsversies van die functies op te sporen die in de heap voor foutopsporing werken. Wanneer u een geheugenblok vrijgeeft, controleert de heap voor foutopsporing automatisch de integriteit van de buffers aan beide zijden van uw toegewezen gebied en geeft een foutenrapport uit als er overschrijven is opgetreden.

De heap voor foutopsporing gebruiken

Koppel de foutopsporingsbuild van uw toepassing aan een foutopsporingsversie van de C-runtimebibliotheek.

Een of meer _crtDbgFlag bitvelden wijzigen en een nieuwe status voor de vlag maken

  1. Roep _CrtSetDbgFlag aan met de newFlag parameter die is ingesteld op _CRTDBG_REPORT_FLAG (om de huidige _crtDbgFlag status te verkrijgen) en sla de geretourneerde waarde op in een tijdelijke variabele.

  2. Schakel bits in met behulp van een bitsgewijze | operator ('of') op de tijdelijke variabele met de bijbehorende bitmaskers (weergegeven in de toepassingscode door manifestconstanten).

  3. Schakel de andere bits uit met behulp van een bitwise operator ("en") op de variabele met een bitwise &~ operator ('not' of complement) van de juiste bitmaskers.

  4. Roep _CrtSetDbgFlag aan met de newFlag parameter die is ingesteld op de waarde die is opgeslagen in de tijdelijke variabele om de nieuwe status voor _crtDbgFlagte maken.

    Met de volgende regels code kunt u bijvoorbeeld automatische lekdetectie inschakelen en controles voor blokken van het type _CRT_BLOCKuitschakelen:

    // 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, deleteen _CLIENT_BLOCK toewijzingen in de heap voor foutopsporing in C++

De foutopsporingsversies van de C-runtimebibliotheek bevatten foutopsporingsversies van de C++ new en delete operators. Als u het _CLIENT_BLOCK toewijzingstype gebruikt, moet u de foutopsporingsversie van de new operator rechtstreeks aanroepen of macro's maken die de new operator vervangen in de foutopsporingsmodus, zoals wordt weergegeven in het volgende voorbeeld:

/* 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 );
}

De foutopsporingsversie van de delete operator werkt met alle bloktypen en vereist geen wijzigingen in uw programma wanneer u een releaseversie compileert.

Heap-statusrapportagefuncties

Als u een samenvattingsmomentopname van de status van de heap op een bepaald moment wilt vastleggen, gebruikt u de _CrtMemState structuur die is gedefinieerd in <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;

Met deze structuur wordt een aanwijzer opgeslagen in het eerste (meest recent toegewezen) blok in de gekoppelde lijst voor foutopsporing in heap. Vervolgens wordt in twee matrices vastgelegd hoeveel van elk type geheugenblok (_NORMAL_BLOCK_CLIENT_BLOCK_FREE_BLOCK, enzovoort) in de lijst staan en het aantal bytes dat is toegewezen in elk type blok. Ten slotte wordt het hoogste aantal bytes geregistreerd dat in de heap tot dat moment is toegewezen en het aantal bytes dat momenteel is toegewezen.

Andere CRT-rapportagefuncties

De volgende functies rapporteren de status en inhoud van de heap en gebruiken de informatie om geheugenlekken en andere problemen te detecteren.

Functie Beschrijving
_CrtMemCheckpoint Hiermee wordt een momentopname van de heap opgeslagen in een _CrtMemState structuur die door de toepassing wordt geleverd.
_CrtMemDifference Vergelijkt twee geheugenstatusstructuren, slaat het verschil tussen deze structuren op in een derde statusstructuur en retourneert TRUE als de twee statussen verschillen.
_CrtMemDumpStatistics Dumpt een bepaalde _CrtMemState structuur. De structuur kan een momentopname bevatten van de status van de heap voor foutopsporing op een bepaald moment of het verschil tussen twee momentopnamen.
_CrtMemDumpAllObjectsSince Dumpt informatie over alle objecten die zijn toegewezen sinds een bepaalde momentopname is gemaakt van de heap of vanaf het begin van de uitvoering. Telkens wanneer een _CLIENT_BLOCK blok wordt gedumpt, wordt een hook-functie aangeroepen die door de toepassing wordt geleverd, als er een is geïnstalleerd met behulp van _CrtSetDumpClient.
_CrtDumpMemoryLeaks Bepaalt of er geheugenlekken zijn opgetreden sinds het begin van de uitvoering van het programma en, als dat het geval is, alle toegewezen objecten dumpt. Telkens wanneer _CrtDumpMemoryLeaks een _CLIENT_BLOCK blok wordt gedumpt, wordt een hook-functie aangeroepen die door de toepassing wordt geleverd, als er een is geïnstalleerd met behulp van _CrtSetDumpClient.

Heap-toewijzingsaanvragen bijhouden

Als u de naam van het bronbestand en het regelnummer van een assertie of rapportagemacro kent, is het vaak handig om de oorzaak van een probleem op te sporen. Hetzelfde geldt niet voor heap-toewijzingsfuncties. Hoewel u macro's op veel geschikte punten in de logische structuur van een toepassing kunt invoegen, wordt een toewijzing vaak begraven in een functie die op veel verschillende plaatsen op verschillende tijdstippen wordt aangeroepen. De vraag is niet welke coderegel een onjuiste toewijzing heeft gemaakt. In plaats daarvan was een van de duizenden toewijzingen die door die coderegel zijn gemaakt, slecht en waarom.

Unieke toewijzingsaanvraagnummers en _crtBreakAlloc

Er is een eenvoudige manier om de specifieke heap-toewijzingsaanroep te identificeren die slecht is gegaan. Het maakt gebruik van het unieke toewijzingsaanvraagnummer dat is gekoppeld aan elk blok in de heap voor foutopsporing. Wanneer informatie over een blok wordt gerapporteerd door een van de dumpfuncties, wordt dit toewijzingsaanvraagnummer tussen accolades weergegeven. Bijvoorbeeld: {36}.

Zodra u het toewijzingsaanvraagnummer van een onjuist toegewezen blok weet, kunt u dit nummer doorgeven om een onderbrekingspunt te _CrtSetBreakAlloc maken. De uitvoering wordt verbroken vlak voordat u het blok toedeelt en u kunt teruggaan om te bepalen welke routine verantwoordelijk was voor de slechte oproep. Als u opnieuw wilt compileren, kunt u hetzelfde doen in het foutopsporingsprogramma door _crtBreakAlloc het instellingsnummer van de toewijzingsaanvraag waarin u geïnteresseerd bent.

Foutopsporingsversies van uw toewijzingsroutines maken

Een complexere benadering is het maken van foutopsporingsversies van uw eigen toewijzingsroutines, vergelijkbaar met de _dbg versies van de heap-toewijzingsfuncties. Vervolgens kunt u argumenten voor bronbestands- en regelnummers doorgeven aan de onderliggende heap-toewijzingsroutines en kunt u onmiddellijk zien waar een ongeldige toewijzing vandaan komt.

Stel dat uw toepassing een veelgebruikte routine bevat die vergelijkbaar is met het volgende voorbeeld:

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

In een headerbestand kunt u code toevoegen, zoals het volgende voorbeeld:

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

Vervolgens kunt u de toewijzing in uw routine voor het maken van records als volgt wijzigen:

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 worden de naam van het bronbestand en het regelnummer waarnaar addNewRecord is aangeroepen, opgeslagen in elk resulterend blok dat is toegewezen in de foutopsporings-heap en wordt gerapporteerd wanneer dat blok wordt onderzocht.

Zie ook

Systeemeigen code voor foutopsporing