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, /MTdou /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 _CrtSetAllocHookde , 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_REALLOCou _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 TRUEsimplement ) 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 _CrtSetReportHookde , 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_ERRORou _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.

Voir aussi