Techniques de débogage CRT
Lorsque vous déboguez un programme qui utilise la bibliothèque runtime C, ces techniques de débogage peuvent être utiles.
Utilisation de la bibliothèque de débogage CRT
La bibliothèque CRT (C Runtime) fournit une prise en charge approfondie du débogage. Pour utiliser l’une des bibliothèques de débogage CRT, vous devez établir un lien avec /DEBUG
et compiler avec /MDd
, /MTd
ou /LDd
.
Les principales définitions et macros pour le débogage CRT sont disponibles dans le fichier d’en-tête<crtdbg.h>
.
Les fonctions dans les bibliothèques de débogage CRT sont compilées avec des informations de débogage (/Z7, /Zd, /Zi, /ZI (Format des informations de débogage)) et sans optimisation. Certaines fonctions contiennent des assertions pour vérifier les paramètres qui leur sont passés ; en outre, le code source est fourni. Avec ce code source, vous pouvez effectuer un pas à pas détaillé dans les fonctions CRT pour vérifier qu'elles opèrent comme prévu et rechercher les paramètres ou les états de mémoire incorrects. (Certaines technologies CRT sont propriétaires et ne fournissent pas de code source pour la gestion des exceptions, le point flottant et quelques autres routines.)
Pour plus d’informations sur les différentes bibliothèques Runtime que vous pouvez utiliser, consultez Bibliothèques Runtime C.
Macros pour la création de rapports
Pour le débogage, vous pouvez utiliser les macros et les _RPTn
macros, définies dans<crtdbg.h>
, pour remplacer l’utilisation d’instructionsprintf
._RPTFn
Vous n’avez pas besoin de les placer dans #ifdef
des directives, car elles disparaissent automatiquement dans votre build de mise en production quand _DEBUG
elles ne sont pas définies.
Macro | Description |
---|---|
_RPT0 , , _RPT1 _RPT2 , , _RPT3 _RPT4 |
Sort une chaîne de message et zéro à quatre arguments. Pour _RPT1 l’intermédiaire _RPT4 , la chaîne de message sert de chaîne de mise en forme de style printf pour les arguments. |
_RPTF0 , , _RPTF1 _RPTF2 , , _RPTF3 _RPTF4 |
Identique à _RPTn , mais ces macros affichent également le nom de fichier et le numéro de ligne où se trouve la macro. |
Prenons l’exemple suivant :
#ifdef _DEBUG
if ( someVar > MAX_SOMEVAR )
printf( "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n",
someVar, otherVar );
#endif
Ce code génère les valeurs de someVar
et otherVar
vers stdout
. Vous pouvez utiliser l'appel suivant à _RPTF2
pour reporter ces mêmes valeurs et, en outre, le nom de fichier et le numéro de ligne:
if (someVar > MAX_SOMEVAR) _RPTF2(_CRT_WARN, "In NameOfThisFunc( ), someVar= %d, otherVar= %d\n", someVar, otherVar );
Certaines applications peuvent avoir besoin de rapports de débogage que les macros fournies avec la bibliothèque d’exécution C ne fournissent pas. Dans ce cas, vous pouvez écrire une macro conçue spécifiquement pour répondre à vos propres besoins. Dans l’un de vos fichiers d’en-tête, par exemple, vous pouvez inclure du code comme suit pour définir une macro appelée 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
Un appel à ALERT_IF2
effectuer toutes les fonctions du printf
code :
ALERT_IF2(someVar > MAX_SOMEVAR, "OVERFLOW! In NameOfThisFunc( ),
someVar=%d, otherVar=%d.\n", someVar, otherVar );
Vous pouvez facilement modifier une macro personnalisée pour signaler différentes informations sur différentes destinations. Cette approche est utile à mesure que vos exigences de débogage évoluent.
Écriture de fonctions de raccordement de débogage
Vous pouvez écrire plusieurs types de fonctions de hook de débogage personnalisées qui vous permettent d’insérer votre code dans certains points prédéfinis à l’intérieur du traitement normal du débogueur.
Fonctions de raccordement de bloc client
Si vous voulez valider ou reporter le contenu des données stockées dans des blocs _CLIENT_BLOCK
, vous pouvez écrire une fonction spécialement dans ce but. La fonction que vous écrivez doit avoir un prototype similaire à ce qui suit, comme défini dans<crtdbg.h>
:
void YourClientDump(void *, size_t)
En d’autres termes, votre fonction de hook doit accepter un void
pointeur vers le début du bloc d’allocation, ainsi qu’une size_t
valeur de type indiquant la taille de l’allocation et le retour void
. Sinon, son contenu est à vous.
Une fois que vous avez installé votre fonction de hook à l’aide de _CrtSetDumpClient, elle est appelée chaque fois qu’un _CLIENT_BLOCK
bloc est vidé. Vous pouvez alors utiliser _CrtReportBlockType pour obtenir des informations sur le type ou le sous-type des blocs ayant fait l’objet d’un vidage.
Le pointeur vers votre fonction que vous passez _CrtSetDumpClient
est de type _CRT_DUMP_CLIENT
, tel que défini dans<crtdbg.h>
:
typedef void (__cdecl *_CRT_DUMP_CLIENT)
(void *, size_t);
Fonctions de raccordement d’allocation
Une fonction de raccordement d’allocation, installée à l’aide _CrtSetAllocHook
de , est appelée chaque fois que la mémoire est allouée, réaffectée ou libérée. Ce type de raccordement a de multiples applications. Utilisez-la, par exemple, pour tester la façon dont une application gère les situations de mémoire insuffisante, pour examiner les modèles d’allocation ou pour enregistrer les informations des allocations en vue d’une analyse ultérieure.
Remarque
Tenez compte de la restriction sur l’utilisation des fonctions de bibliothèque runtime C dans une fonction de hook d’allocation, décrite dans Les hooks d’allocation et les allocations de mémoire crt.
Une fonction de raccordement d’allocation doit avoir un prototype similaire à l’exemple suivant :
int YourAllocHook(int nAllocType, void *pvData,
size_t nSize, int nBlockUse, long lRequest,
const unsigned char * szFileName, int nLine )
Le pointeur auquel vous passez _CrtSetAllocHook
est de type _CRT_ALLOC_HOOK
, tel que défini dans<crtdbg.h>
:
typedef int (__cdecl * _CRT_ALLOC_HOOK)
(int, void *, size_t, int, long, const unsigned char *, int);
Lorsque la bibliothèque d’exécution appelle votre hook, l’argument nAllocType
indique quelle opération d’allocation est sur le point d’être effectuée (_HOOK_ALLOC
, _HOOK_REALLOC
ou _HOOK_FREE
). Dans le cas d’une libération ou d’une réallocation, pvData
contient un pointeur vers l’article utilisateur du bloc qui va être libéré. Toutefois, dans le cas d’une allocation, ce pointeur est null, car l’allocation n’a pas encore eu lieu. Les arguments restants contiennent la taille de l’allocation, son type de bloc, un numéro de requête séquentiel et un pointeur vers le nom de fichier. S’ils sont disponibles, les arguments incluent également le numéro de ligne dans lequel l’allocation a été effectuée. Une fois que la fonction de raccordement effectue l’analyse et d’autres tâches souhaitées par son auteur, elle doit retourner soit TRUE
, indiquant que l’opération d’allocation peut continuer, ou FALSE
, indiquant que l’opération doit échouer. Un simple crochet de ce type peut vérifier la quantité de mémoire allouée jusqu’à présent et retourner FALSE
si cette quantité a dépassé une petite limite. L’application rencontrerait ensuite le type d’erreurs d’allocation qui se produisaient normalement uniquement lorsque la mémoire disponible était faible. Des raccordements plus complexes permettraient d'assurer le suivi des modèles d'allocation, d'analyser l'utilisation de la mémoire ou de reporter des situations spécifiques.
Crochets d’allocation et allocations de mémoire CRT
Une restriction importante sur les fonctions de hook d’allocation est qu’elles doivent ignorer explicitement les _CRT_BLOCK
blocs. Ces blocs consistent en des allocations de mémoire effectuées en interne par les fonctions de la bibliothèque Runtime C qui allouent la mémoire interne si elles sont appelées. Pour ignorer les blocs _CRT_BLOCK
, incluez le code suivant au début de votre fonction de hook d’allocation :
if ( nBlockUse == _CRT_BLOCK )
return( TRUE );
Si votre hook d’allocation n’ignore pas les blocs _CRT_BLOCK
, toute fonction de la bibliothèque Runtime C appelée dans votre hook peut enfermer le programme dans une boucle sans fin. Par exemple, printf
effectue une allocation interne. Si votre code de raccordement appelle printf
, l’allocation résultante entraîne l’appel de votre hook, qui appelle printf
à nouveau, et ainsi de suite, jusqu’à ce que la pile dépasse. Si vous avez besoin de reporter les opérations d'allocation de _CRT_BLOCK
, un moyen de contourner cette restriction consiste à utiliser pour la mise en forme et la sortie les fonctions API Windows, plutôt que les fonctions Runtime C. Les API Windows n’utilisent pas le tas de la bibliothèque Runtime C. Elles n’enferment donc pas votre hook d’allocation dans une boucle sans fin.
Si vous examinez les fichiers sources de la bibliothèque d’exécution, vous verrez que la fonction de hook d’allocation par défaut ( _CrtDefaultAllocHook
qui retourne TRUE
simplement ) se trouve dans un fichier distinct de son propre fichier. debug_heap_hook.cpp
Si vous souhaitez que votre hook d’allocation soit appelé même pour les allocations effectuées par le code de démarrage au moment de l’exécution qui est exécuté avant la fonction de main
votre application, vous pouvez remplacer cette fonction par défaut par l’une de vos propres, au lieu d’utiliser _CrtSetAllocHook
.
Fonctions de raccordement de rapport
Une fonction de raccordement de rapport, installée à l’aide _CrtSetReportHook
de , est appelée chaque fois _CrtDbgReport
qu’un rapport de débogage est généré. Vous pouvez vous en servir, entre autres, pour filtrer les rapports de façon à vous concentrer sur des types d'allocations spécifiques. Une fonction de hook de rapport doit avoir un prototype comme cet exemple :
int AppReportHook(int nRptType, char *szMsg, int *retVal);
Le pointeur auquel vous passez _CrtSetReportHook
est de type _CRT_REPORT_HOOK
, tel que défini dans <crtdbg.h>
:
typedef int (__cdecl *_CRT_REPORT_HOOK)(int, char *, int *);
Lorsque la bibliothèque d’exécution appelle votre fonction de hook, l’argument nRptType
contient la catégorie du rapport (_CRT_WARN
, _CRT_ERROR
ou _CRT_ASSERT
), szMsg
contient un pointeur vers une chaîne de message de rapport entièrement assemblée et retVal
spécifie s’il _CrtDbgReport
doit continuer l’exécution normale après avoir généré le rapport ou démarrer le débogueur. (La retVal
valeur zéro continue l’exécution, la valeur 1 démarre le débogueur.)
Si le hook gère complètement le message en question, de sorte qu’aucun autre rapport n’est requis, il doit retourner TRUE
. Si elle retourne FALSE
, _CrtDbgReport
signale normalement le message.
Contenu de cette section
Versions de débogage des fonctions d’allocation du tas
Décrit les versions spéciales de débogage des fonctions d’allocation de tas, notamment : comment le CRT mappe les appels, les avantages de les appeler explicitement, comment éviter la conversion, suivre les types distincts d’allocations dans les blocs clients et les résultats de ne pas définir
_DEBUG
.Détails du tas de débogage CRT
Décrit la gestion de la mémoire et le tas de débogage, les types de blocs sur le tas de débogage, les fonctions de création de rapports d’état de tas et comment utiliser le tas de débogage pour suivre les demandes d’allocation.
Rechercher des fuites de mémoire à l’aide de la bibliothèque CRT
Décrit les techniques de détection et d'identification des fuites de mémoire à l'aide du débogueur et de la bibliothèque runtime C.