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
, /MTd
nebo ./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_REALLOC
nebo _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_ERROR
nebo _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
.-
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.