Sdílet prostřednictvím


Techniky ladění CRT

Při ladění programu, který používá knihovnu runtime jazyka C, mohou být tyto techniky ladění užitečné.

Použití knihovny ladění CRT

Knihovna C runtime (CRT) poskytuje rozsáhlou podporu ladění. Chcete-li použít některou z ladicí knihovny CRT, musíte propojit a zkompilovat pomocí /DEBUG /MDd, /MTdnebo ./LDd

Hlavní definice a makra pro ladění CRT najdete v<crtdbg.h> souboru hlaviček.

Funkce v knihovnách ladění CRT se kompilují s informacemi o ladění (/Z7, /Zd, /Zi, /ZI (formát informací o ladění)) a bez optimalizace. Některé funkce obsahují kontrolní výrazy k ověření parametrů, které jsou jim předány, a je k dispozici zdrojový kód. Pomocí tohoto zdrojového kódu můžete přejít k funkcím CRT, abyste potvrdili, že funkce fungují podle očekávání, a zkontrolovat chybné parametry nebo stavy paměti. (Některé technologie CRT jsou proprietární a neposkytují zdrojový kód pro zpracování výjimek, plovoucí desetinou čárku a několik dalších rutin.)

Další informace o různých knihovnách za běhu, které můžete použít, najdete v tématu Knihovny runtime jazyka C.

Makra pro hlášení

K ladění můžete použít _RPTn makra definovaná _RPTFn v<crtdbg.h> a nahradit použití printf příkazů. Nemusíte je uzavřít do #ifdef direktiv, protože automaticky zmizí v sestavení vydané verze, pokud _DEBUG není definován.

Makro Popis
_RPT0, _RPT1, _RPT2, , _RPT3_RPT4 Vypíše řetězec zprávy a nula až čtyři argumenty. _RPT4Řetězec _RPT1 zprávy slouží jako formátovací řetězec ve stylu printf pro argumenty.
_RPTF0, _RPTF1, _RPTF2, , _RPTF3_RPTF4 Stejné jako _RPTn, ale tato makra také vypíše název souboru a číslo řádku, kde se makro nachází.

Představte si následující příklad:

#ifdef _DEBUG
    if ( someVar > MAX_SOMEVAR )
        printf( "OVERFLOW! In NameOfThisFunc( ),
               someVar=%d, otherVar=%d.\n",
               someVar, otherVar );
#endif

Tento kód vypíše hodnoty a someVar otherVar do stdout. Pomocí následujícího volání _RPTF2 můžete nahlásit stejné hodnoty a navíc název souboru a číslo řádku:

if (someVar > MAX_SOMEVAR) _RPTF2(_CRT_WARN, "In NameOfThisFunc( ), someVar= %d, otherVar= %d\n", someVar, otherVar );

Některé aplikace můžou potřebovat ladit sestavy, které makra dodávaná s knihovnou runtime jazyka C neposkytují. V těchto případech můžete napsat makro navržené speciálně tak, aby vyhovovalo vašim požadavkům. Do některého ze souborů hlaviček můžete například zahrnout kód podobný následujícímu, který definuje makro s názvem ALERT_IF2:

#ifndef _DEBUG                  /* For RELEASE builds */
#define  ALERT_IF2(expr, msg, arg1, arg2)  do {} while (0)
#else                           /* For DEBUG builds   */
#define  ALERT_IF2(expr, msg, arg1, arg2) \
    do { \
        if ((expr) && \
            (1 == _CrtDbgReport(_CRT_ERROR, \
                __FILE__, __LINE__, msg, arg1, arg2))) \
            _CrtDbgBreak( ); \
    } while (0)
#endif

Jedno volání ALERT_IF2 by mohlo provádět všechny funkce printf kódu:

ALERT_IF2(someVar > MAX_SOMEVAR, "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n", someVar, otherVar );

Vlastní makro můžete snadno změnit tak, aby hlásilo více nebo méně informací do různých cílů. Tento přístup je užitečný při vývoji požadavků na ladění.

Zápis funkce volání pro ladění

Můžete napsat několik druhů vlastních funkcí háku ladění, které umožňují vložit kód do některých předdefinovaných bodů uvnitř normálního zpracování ladicího programu.

Funkce volání bloku klienta

Pokud chcete ověřit nebo hlásit obsah dat uložených v _CLIENT_BLOCK blocích, můžete pro tento účel napsat funkci. Funkce, kterou napíšete, musí mít prototyp podobný následujícímu, jak je definováno v<crtdbg.h>:

void YourClientDump(void *, size_t)

Jinými slovy, funkce háku by měla přijmout void ukazatel na začátek bloku přidělení spolu s size_t hodnotou typu označující velikost přidělení a vrátit void. V opačném případě je jeho obsah na vás.

Jakmile funkci hooku nainstalujete pomocí _CrtSetDumpClient, bude volána při každém výpisu _CLIENT_BLOCK bloku. Potom můžete pomocí _CrtReportBlockType získat informace o typu nebo podtypu bloků s výpisem paměti.

Ukazatel na funkci, kterou _CrtSetDumpClient předáte, je typu _CRT_DUMP_CLIENT, jak je definováno v<crtdbg.h>:

typedef void (__cdecl *_CRT_DUMP_CLIENT)
   (void *, size_t);

Funkce volání přidělení

Funkce háku přidělení, nainstalovaná pomocí _CrtSetAllocHook, se volá při každém přidělení paměti, přerozdělení nebo uvolnění. Tento typ háku můžete použít pro mnoho různých účelů. Používá se k otestování, jak aplikace zpracovává nedostatečné situace v paměti, jako je například prozkoumání vzorů přidělení nebo informace o přidělování protokolů pro pozdější analýzu.

Poznámka:

Mějte na paměti omezení týkající se použití funkcí knihovny modulu runtime jazyka C ve funkci háku přidělení, které jsou popsány v hookech přidělení a přidělení paměti crt.

Funkce háku přidělení by měla mít prototyp jako v následujícím příkladu:

int YourAllocHook(int nAllocType, void *pvData,
        size_t nSize, int nBlockUse, long lRequest,
        const unsigned char * szFileName, int nLine )

Ukazatel, ke _CrtSetAllocHook kterému předáte, je typu _CRT_ALLOC_HOOK, jak je definováno v<crtdbg.h>:

typedef int (__cdecl * _CRT_ALLOC_HOOK)
    (int, void *, size_t, int, long, const unsigned char *, int);

Když knihovna za běhu volá váš háček, argument označuje, nAllocType jaká operace přidělení se má provést (_HOOK_ALLOC, _HOOK_REALLOCnebo _HOOK_FREE). Ve volném nebo v reálném umístění pvData má ukazatel na článek uživatele bloku, který se má uvolnit. U přidělení je však tento ukazatel null, protože k přidělení nedošlo. Zbývající argumenty obsahují velikost přidělení, jeho typ bloku, sekvenční číslo požadavku a ukazatel na název souboru. Pokud jsou k dispozici, argumenty zahrnují také číslo řádku, ve kterém bylo přidělení provedeno. Jakmile funkce háku provede jakoukoli analýzu a další úkoly, které chce autor, musí vrátit buď TRUE, indikující, že operace přidělení může pokračovat, nebo FALSE, označující, že operace by měla selhat. Jednoduchý háček tohoto typu může zkontrolovat množství paměti přidělené doposud a vrátit FALSE se, pokud tato částka překročila malý limit. Aplikace by pak měla zaznamenat druh chyb přidělení, ke kterým by obvykle docházelo pouze v případě, že je dostupná paměť nízká. Složitější háky můžou sledovat vzorce přidělování, analyzovat využití paměti nebo hlásit, když dojde k určitým situacím.

Přidělení háků a přidělení paměti CRT

Důležitým omezením funkcí háku přidělení je, že musí explicitně ignorovat _CRT_BLOCK bloky. Tyto bloky jsou přidělení paměti provedené interně funkcemi knihovny runtime jazyka C, pokud provádějí jakákoli volání funkcí knihovny runtime jazyka C, které přidělují interní paměť. Bloky můžete ignorovat _CRT_BLOCK zahrnutím následujícího kódu na začátek funkce háku přidělení:

if ( nBlockUse == _CRT_BLOCK )
    return( TRUE );

Pokud háček přidělení neignoruje _CRT_BLOCK bloky, všechny funkce knihovny runtime jazyka C volané v háku můžou program v nekonečné smyčce zachytit. printf Například provede interní přidělení. Pokud váš kód háku volá printf, výsledná přidělení způsobí opětovné volání vašeho háku, který bude znovu volat printf atd. až do přetečení zásobníku. Pokud potřebujete hlásit _CRT_BLOCK operace přidělení, jedním ze způsobů, jak toto omezení obejít, je použít funkce rozhraní API systému Windows místo funkcí za běhu jazyka C pro formátování a výstup. Vzhledem k tomu, že rozhraní API systému Windows nepoužívají haldu knihovny runtime jazyka C, nebudou v nekonečné smyčce zachytávání přidělení zachytávány.

Pokud prozkoumáte zdrojové soubory knihovny runtime, uvidíte, že výchozí funkce háku přidělení ( _CrtDefaultAllocHook která se jednoduše vrátíTRUE) je umístěna v samostatném souboru, který vlastní . debug_heap_hook.cpp Pokud chcete, aby se volání háku přidělení volala i pro přidělení provedená spouštěcím kódem za běhu, který se spustí před funkcí vaší aplikace main , můžete tuto výchozí funkci nahradit vlastní funkcí namísto použití _CrtSetAllocHook.

Funkce volání sestavy

Funkce háku sestavy, která je nainstalována pomocí _CrtSetReportHook, je volána při _CrtDbgReport každém vygenerování sestavy ladění. Můžete ho použít mimo jiné k filtrování sestav, abyste se mohli zaměřit na konkrétní typy přidělení. Funkce háku sestavy by měla mít prototyp podobný tomuto příkladu:

int AppReportHook(int nRptType, char *szMsg, int *retVal);

Ukazatel, ke _CrtSetReportHook kterému předáte, je typu _CRT_REPORT_HOOK, jak je definováno v <crtdbg.h>:

typedef int (__cdecl *_CRT_REPORT_HOOK)(int, char *, int *);

Když knihovna za běhu volá funkci háku, nRptType argument obsahuje kategorii sestavy (_CRT_WARN_CRT_ERRORnebo _CRT_ASSERT), szMsg obsahuje ukazatel na plně sestavený řetězec zprávy sestavy a retVal určuje, zda _CrtDbgReport má pokračovat normální spuštění po vygenerování sestavy nebo spuštění ladicího programu. (Hodnota retVal nuly pokračuje v provádění, hodnota 1 spustí ladicí program.)

Pokud háček zprávu zpracuje zcela, takže se nevyžaduje žádné další hlášení, měla by se vrátit TRUE. Pokud se vrátí FALSE, _CrtDbgReport ohlásí zprávu normálně.

V této části

  • Ladění verzí funkcí přidělení haldy

    Popisuje speciální ladicí verze funkcí přidělení haldy, včetně: jak CRT mapuje volání, výhody jejich explicitního volání, jak se vyhnout převodu, sledování samostatných typů přidělení v klientských blocích a výsledky nedefinovat _DEBUG.

  • Podrobnosti haldy ladění CRT

    Popisuje správu paměti a haldu ladění, typy bloků v haldě ladění, funkce generování sestav stavu haldy a způsob použití haldy ladění ke sledování žádostí o přidělení.

  • Vyhledání nevrácené paměti pomocí knihovny CRT

    Popisuje techniky pro detekci a izolování nevracení paměti pomocí ladicího programu a knihovny runtime jazyka C.

Viz také