Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Si vous déboguez un programme MFC, ces techniques de débogage peuvent être utiles.
AfxDebugBreak
MFC fournit une fonction AfxDebugBreak spéciale pour les points d’arrêt de codage en dur dans le code source :
AfxDebugBreak( );
Sur les plateformes Intel, AfxDebugBreak produit le code suivant, qui s’interrompt dans le code source plutôt que dans le code noyau :
_asm int 3
Sur d'autres plateformes, AfxDebugBreak se contente d'appeler DebugBreak.
Veillez à supprimer les déclarations AfxDebugBreak lorsque vous réalisez une version de production, ou à les entourer avec #ifdef _DEBUG.
Macro TRACE
Pour afficher les messages de votre programme dans la fenêtre Sortie du débogueur, vous pouvez utiliser la macroATLTRACE ou la macro TRACE MFC. Comme les assertions, les macros de trace sont actives uniquement dans la version de débogage de votre programme et disparaissent lorsqu’elles sont compilées dans la version release.
Les exemples suivants montrent quelques-unes des façons dont vous pouvez utiliser la macro TRACE . Comme printf, la macro TRACE peut gérer un certain nombre d’arguments.
int x = 1;
int y = 16;
float z = 32.0;
TRACE( "This is a TRACE statement\n" );
TRACE( "The value of x is %d\n", x );
TRACE( "x = %d and y = %d\n", x, y );
TRACE( "x = %d and y = %x and z = %f\n", x, y, z );
La macro TRACE gère correctement les paramètres char* et wchar_t*. Les exemples suivants illustrent l’utilisation de la macro TRACE avec différents types de paramètres de chaîne.
TRACE( "This is a test of the TRACE macro that uses an ANSI string: %s %d\n", "The number is:", 2);
TRACE( L"This is a test of the TRACE macro that uses a UNICODE string: %s %d\n", L"The number is:", 2);
TRACE( _T("This is a test of the TRACE macro that uses a TCHAR string: %s %d\n"), _T("The number is:"), 2);
Pour plus d’informations sur la macro TRACE , consultez Diagnostic Services.
Détection des fuites de mémoire dans MFC
MFC fournit des classes et des fonctions pour détecter la mémoire allouée, mais jamais libérée.
Suivi des allocations de mémoire
Dans MFC, vous pouvez utiliser la macro DEBUG_NEW à la place du nouvel opérateur pour aider à localiser les fuites de mémoire. Dans la version de débogage de votre programme, DEBUG_NEW effectue le suivi du nom de fichier et du numéro de ligne pour chaque objet qu’il alloue. Lorsque vous compilez une version Release de votre programme, DEBUG_NEW cela se traduit par une opération new simple sans les informations de nom de fichier et de numéro de ligne. Ainsi, vous ne payez aucune pénalité de vitesse dans la version Release de votre programme.
Si vous ne souhaitez pas réécrire l’intégralité de votre programme pour utiliser DEBUG_NEW à la place de new, vous pouvez définir cette macro dans vos fichiers sources :
#define new DEBUG_NEW
Lorsque vous faites un dump d'objets, chaque objet alloué avec DEBUG_NEW affiche le fichier et le numéro de ligne où il a été alloué, ce qui vous permet de localiser l'origine des fuites de mémoire.
La version de débogage de l’infrastructure MFC utilise DEBUG_NEW automatiquement, mais votre code ne le fait pas. Si vous souhaitez bénéficier des avantages de DEBUG_NEW, vous devez utiliser DEBUG_NEW explicitement ou #define nouveau comme indiqué ci-dessus.
Activation des diagnostics de mémoire
Avant de pouvoir utiliser les installations de diagnostic de mémoire, vous devez activer le suivi des diagnostics.
Pour activer ou désactiver les diagnostics de mémoire
Appelez la fonction globale AfxEnableMemoryTracking pour activer ou désactiver l’allocateur de mémoire de diagnostic. Étant donné que les diagnostics de mémoire sont activés par défaut dans la bibliothèque de débogage, vous utiliserez généralement cette fonction pour les désactiver temporairement, ce qui augmente la vitesse d’exécution du programme et réduit la sortie de diagnostic.
Pour sélectionner des fonctionnalités de diagnostic de mémoire spécifiques avec afxMemDF
Si vous souhaitez un contrôle plus précis sur les fonctionnalités de diagnostic de mémoire, vous pouvez activer et désactiver sélectivement les fonctionnalités de diagnostic de mémoire individuelles en définissant la valeur de la variable globale MFC afxMemDF. Cette variable peut avoir les valeurs suivantes, comme spécifié par le type énuméré afxMemDF.
Valeur Descriptif allocMemDF Activez l’allocateur de mémoire de diagnostic (valeur par défaut). delayFreeMemDF Retarder la libération de la mémoire lors de l’appel deleteoufreejusqu’à ce que le programme se termine. Cela entraîne l’allocation de la quantité maximale de mémoire possible par votre programme.checkAlwaysMemDF Appelez AfxCheckMemory chaque fois que la mémoire est allouée ou libérée. Ces valeurs peuvent être utilisées en combinaison en effectuant une opération logique-OR, comme illustré ici :
afxMemDF = allocMemDF | delayFreeMemDF | checkAlwaysMemDF;
Captures instantanées de la mémoire
Créez un objet CMemoryState et appelez la fonction membre CMemoryState ::Checkpoint . Cela crée le premier instantané de mémoire.
Une fois que le programme a effectué ses opérations d'allocation et de libération de mémoire, créez un autre objet
CMemoryStateet appelezCheckpointpour cet objet. Cette opération obtient un deuxième instantané de l’utilisation de la mémoire.Créez un troisième objet
CMemoryStateet appelez sa fonction membre CMemoryState::Difference, en fournissant les deux objets précédentsCMemoryStatecomme arguments. S’il existe une différence entre les deux états de mémoire, laDifferencefonction retourne une valeur différente de zéro. Cela indique que certains blocs de mémoire n'ont pas été libérés.Cet exemple montre à quoi ressemble le code :
// Declare the variables needed #ifdef _DEBUG CMemoryState oldMemState, newMemState, diffMemState; oldMemState.Checkpoint(); #endif // Do your memory allocations and deallocations. CString s("This is a frame variable"); // The next object is a heap object. CPerson* p = new CPerson( "Smith", "Alan", "581-0215" ); #ifdef _DEBUG newMemState.Checkpoint(); if( diffMemState.Difference( oldMemState, newMemState ) ) { TRACE( "Memory leaked!\n" ); } #endifNotez que les instructions de vérification de la mémoire sont délimitées par des blocs #ifdef _DEBUG / #endif afin qu'elles soient compilées uniquement dans les versions Debug de votre programme.
Maintenant que vous savez qu’une fuite de mémoire existe, vous pouvez utiliser une autre fonction membre, CMemoryState ::D umpStatistics qui vous aidera à le localiser.
Affichage des statistiques de mémoire
La fonction CMemoryState::Difference examine deux objets d’état de mémoire et détecte les objets qui n’ont pas été libérés du tas entre les états de début et de fin. Après avoir pris des captures instantanées de mémoire et les avoir comparées à l’aide de CMemoryState::Difference, vous pouvez appeler CMemoryState::DumpStatistics pour obtenir des informations sur les objets qui n’ont pas été libérés.
Prenons l’exemple suivant :
if( diffMemState.Difference( oldMemState, newMemState ) )
{
TRACE( "Memory leaked!\n" );
diffMemState.DumpStatistics();
}
Un exemple de dump de cet exemple aura l'aspect suivant :
0 bytes in 0 Free Blocks
22 bytes in 1 Object Blocks
45 bytes in 4 Non-Object Blocks
Largest number used: 67 bytes
Total allocations: 67 bytes
Les blocs libres sont des blocs dont la désallocation est retardée si afxMemDF est définie sur delayFreeMemDF.
Les blocs Object ordinaires, affichés sur la deuxième ligne, restent alloués sur le tas.
Les blocs non-objets incluent des tableaux et des structures alloués avec new. Dans le cas présent, quatre blocs non objet ont été alloués sur le tas, mais pas libérés.
Largest number used donne la mémoire maximale utilisée par le programme à tout moment.
Total allocations donne la quantité totale de mémoire utilisée par le programme.
Création de dumps d'objets
Dans un programme MFC, vous pouvez utiliser CMemoryState::DumpAllObjectsSince pour effectuer un dump d’une description de tous les objets sur le tas qui n’ont pas été désalloués.
DumpAllObjectsSince vide tous les objets alloués depuis le dernier CMemoryState ::Checkpoint. Si aucun appel n’a Checkpoint eu lieu, DumpAllObjectsSince vide tous les objets et non-objets actuellement en mémoire.
Remarque
Avant de pouvoir utiliser le dump d'objets MFC, vous devez activer le traçage de diagnostic.
Remarque
MFC fait automatiquement un dump de tous les objets qui ont été perdus lorsque votre programme s'arrête ; il est donc inutile de créer du code pour faire un dump des objets en ce point.
Le code suivant teste une fuite de mémoire en comparant deux états de mémoire et vide tous les objets si une fuite est détectée.
if( diffMemState.Difference( oldMemState, newMemState ) )
{
TRACE( "Memory leaked!\n" );
diffMemState.DumpAllObjectsSince();
}
Le contenu du dump ressemble à ceci :
Dumping objects ->
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
{1} strcore.cpp(80) : non-object block at $00A7516E, 25 bytes long
Les nombres dans les accolades au début de la plupart des lignes spécifient l’ordre dans lequel les objets ont été alloués. Le dernier objet alloué, auquel correspond le numéro le plus élevé, apparaît en au début du dump.
Pour obtenir la quantité d'informations maximale d'un dump d'objets, vous pouvez remplacer la fonction membre Dump de n'importe quel objet dérivé de CObjectpour personnaliser le dump d'objets.
Vous pouvez définir un point d’arrêt sur une allocation de mémoire particulière en définissant la variable _afxBreakAlloc globale sur le nombre indiqué dans les accolades. Si vous réexécutez le programme, le débogueur arrêtera l'exécution lorsque cette allocation aura lieu. Vous pouvez ensuite examiner la pile des appels pour voir comment votre programme est arrivé à ce stade.
La bibliothèque runtime C a une fonction similaire, _CrtSetBreakAlloc, que vous pouvez utiliser pour les allocations d’exécution C.
Interprétation des vidages mémoire
Examinons ce dump d'objets plus en détail :
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
{1} strcore.cpp(80) : non-object block at $00A7516E, 25 bytes long
Le programme qui a généré ce dump n'avait que deux allocations explicites : une sur la pile, l'autre sur le tas :
// Do your memory allocations and deallocations.
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
Le constructeur CPerson prend trois arguments qui sont des pointeurs vers char, utilisés pour initialiser les variables membres CString. Dans le dump mémoire, vous pouvez voir l'objet CPerson avec trois blocs non-objets (3, 4 et 5). Celles-ci contiennent les caractères des CString variables membres et ne seront pas supprimées lorsque le CPerson destructeur d’objet est appelé.
Le nombre de blocs 2 est l’objet CPerson lui-même.
$51A4 représente l’adresse du bloc et est suivie du contenu de l’objet, qui a été produit par CPerson::Dump lorsqu’il est appelé par DumpAllObjectsSince.
Vous pouvez deviner que le numéro de bloc 1 est associé à la CString variable frame en raison de son nombre de séquences et de sa taille, qui correspond au nombre de caractères dans la variable d’image CString . Les variables allouées sur le frame sont automatiquement désallouées lorsque le frame est hors de portée.
Variables frame
En général, vous n'avez pas à vous soucier des objets de tas associés à des variables frame, car ils sont automatiquement désalloués lorsque celles-ci sont hors de portée. Afin d'éviter tout encombrement dans les dumps des diagnostics de la mémoire, vous devez positionner vos appels à Checkpoint de telle sorte qu'ils se trouvent hors de la portée des variables frame. Par exemple, placez des crochets de portée autour du code d’allocation précédent, comme illustré ici :
oldMemState.Checkpoint();
{
// Do your memory allocations and deallocations ...
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
}
newMemState.Checkpoint();
Une fois la portée mise entre parenthèses, le dump mémoire, pour cet exemple, se présente de la façon suivante :
Dumping objects ->
{5} strcore.cpp(80) : non-object block at $00A7521A, 9 bytes long
{4} strcore.cpp(80) : non-object block at $00A751F8, 5 bytes long
{3} strcore.cpp(80) : non-object block at $00A751D6, 6 bytes long
{2} a CPerson at $51A4
Last Name: Smith
First Name: Alan
Phone #: 581-0215
Allocations non-objets
Notez que certaines allocations sont des objets (par exemple CPerson) et qu’elles ne sont pas des allocations d’objets. Les « allocations nonobject » sont des allocations pour les objets non dérivés de CObject ou les allocations de types C primitifs tels que char, int ou long. Si la classe dérivée de CObject alloue de l’espace supplémentaire, par exemple pour les mémoires tampons internes, ces objets affichent à la fois les allocations d’objets et de non-objets.
Prévention des fuites de mémoire
Notez que dans le code ci-dessus, le bloc de mémoire associé à la CString variable frame a été désalloué automatiquement et ne s’affiche pas comme une fuite de mémoire. La désallocation automatique associée aux règles de portée se charge de la plupart des fuites de mémoire associées aux variables frame.
Toutefois, pour les objets alloués sur le tas, vous devez supprimer explicitement l’objet pour empêcher une fuite de mémoire. Pour nettoyer la dernière fuite de mémoire dans l'exemple précédent, supprimez l'objet CPerson alloué sur le tas, comme indiqué ci-après :
{
// Do your memory allocations and deallocations.
CString s("This is a frame variable");
// The next object is a heap object.
CPerson* p = new CPerson( "Smith", "Alan", "581-0215" );
delete p;
}
Personnalisation des dumps d'objets
Lorsque vous dérivez une classe de CObject, vous pouvez substituer la fonction membre Dump pour fournir des informations supplémentaires lorsque vous utilisez DumpAllObjectsSince pour faire un dump des objets dans la fenêtre Sortie.
La fonction Dump écrit une représentation textuelle des variables de membre de l'objet dans un contexte de dump (CDumpContext). Le contexte de dump est similaire à un flux d'E/S. Vous pouvez utiliser l’opérateur append (<<) pour envoyer des données à un CDumpContext.
Lorsque vous substituez la fonction Dump , vous devez d'abord appeler la version classe de base de la fonction Dump pour faire un dump du contenu de l'objet de classe de base. Ensuite, sortiez une description textuelle et une valeur pour chaque variable membre de votre classe dérivée.
La déclaration de la Dump fonction ressemble à ceci :
class CPerson : public CObject
{
public:
#ifdef _DEBUG
virtual void Dump( CDumpContext& dc ) const;
#endif
CString m_firstName;
CString m_lastName;
// And so on...
};
Étant donné que le vidage d’objets n’est logique que lorsque vous déboguez votre programme, la déclaration de la Dump fonction est entre crochets avec un bloc #ifdef _DEBUG/#endif .
Dans l’exemple suivant, la Dump fonction appelle d’abord la Dump fonction pour sa classe de base. Il écrit ensuite une brève description de chaque variable membre, ainsi que la valeur du membre dans le flux de diagnostic.
#ifdef _DEBUG
void CPerson::Dump( CDumpContext& dc ) const
{
// Call the base class function first.
CObject::Dump( dc );
// Now do the stuff for our specific class.
dc << "last name: " << m_lastName << "\n"
<< "first name: " << m_firstName << "\n";
}
#endif
Vous devez fournir un argument CDumpContext pour spécifier la destination de la sortie du dump. La version de débogage de MFC fournit un objet prédéfini CDumpContext nommé afxDump qui envoie la sortie au débogueur.
CPerson* pMyPerson = new CPerson;
// Set some fields of the CPerson object.
//...
// Now dump the contents.
#ifdef _DEBUG
pMyPerson->Dump( afxDump );
#endif
Réduction de la taille d'une version Debug MFC
Les informations de débogage d’une grande application MFC peuvent prendre beaucoup d’espace disque. Vous pouvez utiliser l’une de ces procédures pour réduire la taille :
Régénérez les bibliothèques MFC à l’aide de l’option /Z7, /Zi, /ZI (Debug Information Format) au lieu de /Z7. Ces options créent un fichier de base de données de programme unique (PDB) qui contient des informations de débogage pour l’ensemble de la bibliothèque, ce qui réduit la redondance et permet une économie d'espace.
Régénérez les bibliothèques MFC sans informations de débogage (pas d’option /Z7, /Zi, /ZI (Format des informations de débogage)). Dans ce cas, l’absence d’informations de débogage vous empêchera d’utiliser la plupart des installations du débogueur dans le code de la bibliothèque MFC, mais étant donné que les bibliothèques MFC sont déjà déboguées soigneusement, cela peut ne pas être un problème.
Créez votre propre application avec uniquement des informations de débogage pour les modules sélectionnés, comme décrit ci-dessous.
Création d’une application MFC avec des informations de débogage pour les modules sélectionnés
La création de modules sélectionnés avec les bibliothèques de débogage MFC vous permet d'utiliser l'exécution pas à pas et d'autres fonctionnalités de débogage dans ces modules. Cette procédure utilise à la fois les configurations de Debug et de Release du projet, ce qui nécessite les modifications décrites dans les étapes suivantes (et rend également une "reconstruction complète" nécessaire lorsqu'une compilation Release complète est requise).
Dans l’Explorateur de solutions, sélectionnez le projet .
Dans le menu Affichage , sélectionnez Pages de propriétés.
Tout d’abord, vous allez créer une configuration de projet.
Dans la boîte de dialogue Pages de propriétés du< projet>, cliquez sur le bouton Configuration Manager.
Dans la boîte de dialogue Configuration Manager, recherchez votre projet dans la grille. Dans la colonne Configuration , sélectionnez <Nouveau...>.
Dans la boîte de dialogue Nouvelle configuration du projet, tapez un nom pour votre nouvelle configuration, par exemple « Débogage partiel », dans la zone Nom de la configuration du projet .
Dans la liste Copier les paramètres à partir de , cliquez sur Release.
Cliquez sur OK pour fermer la boîte de dialogue Nouvelle configuration du projet .
Fermez la boîte de dialogue Configuration Manager .
À présent, vous allez définir des options pour l’ensemble du projet.
Dans la boîte de dialogue Pages de propriétés , sous le dossier Propriétés de configuration , sélectionnez la catégorie Général .
Dans la grille des paramètres du projet, développez Project Defaults (si nécessaire).
Sous Valeurs par défaut du projet, recherchez l’utilisation de MFC. Le paramètre actuel apparaît dans la colonne droite de la grille. Cliquez sur le paramètre actuel et modifiez-le pour utiliser MFC dans une bibliothèque statique.
Dans le volet gauche de la boîte de dialogue Pages de propriétés , ouvrez le dossier C/C++ et sélectionnez Préprocesseur. Dans la grille des propriétés, recherchez les définitions de préprocesseur et remplacez « NDEBUG » par « _DEBUG ».
Dans le volet gauche de la boîte de dialogue Pages de propriétés, ouvrez le dossier Linker et sélectionnez la catégorie D’entrée. Dans la grille des propriétés, recherchez des dépendances supplémentaires. Dans le paramètre Dépendances supplémentaires, tapez « NAFXCWD.LIB » et « LIBCMT ».
Cliquez sur OK pour enregistrer les nouvelles options de build et fermer la boîte de dialogue Pages de propriétés .
Dans le menu Générer , sélectionnez Reconstruire. Cela supprime toutes les informations de débogage de vos modules, mais n’affecte pas la bibliothèque MFC.
Vous devez à présent ajouter de nouveau les informations de débogage aux modules sélectionnés dans votre application. N’oubliez pas que vous pouvez définir des points d’arrêt et exécuter d’autres fonctions de débogueur uniquement dans les modules que vous avez compilés avec des informations de débogage. Pour chaque fichier projet dans lequel vous souhaitez inclure des informations de débogage, effectuez les étapes suivantes :
Dans l’Explorateur de solutions, ouvrez le dossier Fichiers sources situé sous votre projet.
Sélectionnez le fichier pour lequel vous souhaitez définir les informations de débogage.
Dans le menu Affichage , sélectionnez Pages de propriétés.
Dans la boîte de dialogue Pages de propriétés , sous le dossier Paramètres de configuration , ouvrez le dossier C/C++ , puis sélectionnez la catégorie Général .
Dans la grille des propriétés, recherchez Format des informations de débogage.
Cliquez sur les paramètres Debug Information Format et sélectionnez l’option souhaitée (généralement /ZI) pour obtenir des informations de débogage.
Si vous utilisez une application générée par l’Assistant application ou que vous avez des en-têtes précompilés, vous devez désactiver les en-têtes précompilés ou les recompiler avant de compiler les autres modules. Sinon, vous recevrez l’avertissement C4650 et le message d’erreur C2855. Vous pouvez désactiver les en-têtes précompilés en modifiant le paramètre Créer/utiliser des en-têtes précompilés dans la <boîte de dialogue Propriétés du projet> (dossier Propriétés de configuration, sous-dossier C/C++, catégorie En-têtes précompilés).
Dans le menu Générer , sélectionnez Générer pour reconstruire les fichiers projet obsolètes.
En guise d’alternative à la technique décrite dans cette rubrique, vous pouvez utiliser un makefile externe pour définir des options individuelles pour chaque fichier. Dans ce cas, pour établir un lien avec les bibliothèques de débogage MFC, vous devez définir l’indicateur _DEBUG pour chaque module. Si vous souhaitez utiliser des bibliothèques de mise en production MFC, vous devez définir NDEBUG. Pour plus d’informations sur l’écriture de makefiles externes, consultez la référence NMAKE.