Partager via


Débogage de voyage dans le temps - Parcours d’application d’exemple

Logo de débogage avec voyage dans le temps mettant en vedette une horloge.

Ce laboratoire présente le Débogage avec Voyage dans le Temps (TTD), en utilisant un petit programme d’exemple contenant une erreur de code. TTD est utilisé pour déboguer, identifier et déterminer la cause première du problème. Bien que le problème dans ce petit programme soit facile à trouver, la procédure générale peut être utilisée sur un code plus complexe. Cette procédure générale peut être résumée comme suit :

  1. Capturer une trace de voyage dans le temps du programme en échec.
  2. Utiliser la commande dx (Afficher l’expression du modèle de débogueur) pour trouver l’événement d’exception stocké dans l’enregistrement.
  3. Utiliser la commande !tt (voyage dans le temps) pour se déplacer jusqu’à la position de l’événement d’exception dans la trace.
  4. À partir de ce point dans la trace, revenir en arrière pas à pas jusqu’à ce que le code fautif en question entre dans le champ d’application.
  5. Avec le code fautif en vue, examiner les valeurs locales et développer une hypothèse sur une variable qui pourrait contenir une valeur incorrecte.
  6. Déterminer l’adresse mémoire de la variable avec la valeur incorrecte.
  7. Définir un point d’arrêt d’accès à la mémoire (ba) sur l’adresse de la variable suspecte en utilisant la commande ba (Pause sur Accès).
  8. Utiliser g- pour revenir au dernier point d’accès à la mémoire de la variable suspecte.
  9. Vérifiez si cet emplacement, ou quelques instructions avant, est le point de l’erreur de code. Si c’est le cas, vous avez terminé. Si la valeur incorrecte provient d’une autre variable, définir un autre point d’arrêt d’accès à la mémoire sur la deuxième variable suspecte.
  10. Utiliser g- pour revenir au dernier point d’accès à la mémoire de la deuxième variable suspecte. Voir si cet emplacement ou quelques instructions avant contiennent l’erreur de code. Si c’est le cas, vous avez terminé.
  11. Répéter ce processus en remontant jusqu’à ce que le code qui a défini la valeur incorrecte à l’origine de l’erreur soit localisé.

Bien que les techniques générales décrites dans cette procédure s’appliquent à un large ensemble de problèmes de code, il existe des problèmes de code uniques qui nécessiteront une approche unique. Les techniques illustrées dans le guide devraient permettre d’élargir votre ensemble d’outils de débogage et illustrer une partie de ce qui est possible avec une trace TTD.

Objectifs du labo

Après avoir terminé ce laboratoire, vous serez en mesure d’utiliser la procédure générale avec une trace de voyage dans le temps pour localiser les problèmes de code.

Configuration du laboratoire

Vous aurez besoin du matériel suivant pour pouvoir effectuer le laboratoire :

  • Un ordinateur portable ou de bureau (hôte) exécutant Windows 10 ou Windows 11

Vous aurez besoin du logiciel suivant pour pouvoir effectuer le laboratoire :

  • Le WinDbg. Pour obtenir des informations sur l’installation de WinDbg, veuillez consulter la rubrique Installation de WinDbg.
  • Visual Studio pour compiler le code C++ d’exemple.

Le laboratoire est composé des trois sections suivantes.

Section 1: Compiler le code d’exemple

Dans la Section 1, vous allez compiler le code d’exemple en utilisant Visual Studio.

Créer l’application d’exemple dans Visual Studio

  1. Dans Microsoft Visual Studio, cliquez sur Fichier>Nouveau>Projet/Solution... et cliquez sur les modèles Visual C++.

    Sélectionnez l’Application Console Win32.

    Fournissez un nom de projet DisplayGreeting et cliquez sur OK.

  2. Décochez les vérifications du cycle de vie du développement sécurisé (SDL).

    Paramètres de l’Assistant d’Application Win32 dans Visual Studio.

  3. Cliquez sur Terminer.

  4. Collez le texte suivant dans le volet DisplayGreeting.cpp de Visual Studio.

    // DisplayGreeting.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <array>
    #include <stdio.h>
    #include <string.h>
    
    void GetCppConGreeting(wchar_t* buffer, size_t size)
    {
        wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!";
    
        wcscpy_s(buffer, size, message);
    }
    
    int main()
    {
         std::array <wchar_t, 50> greeting{};
         GetCppConGreeting(greeting.data(), sizeof(greeting));
    
         wprintf(L"%ls\n", greeting.data());
    
         return 0;
    }
    
  5. Dans Visual Studio, cliquez sur Projet>Propriétés DisplayGreeting. Cliquez ensuite sur C/C++ et Génération de code.

    Définissez les propriétés suivantes.

    Paramètre Valeur
    Vérification de la sécurité Désactivez la vérification de sécurité (/GS-).
    Vérifications de base à l’exécution Par défaut

    Remarque

    Bien que ces paramètres ne soient pas recommandés, il est possible d’imaginer un scénario où quelqu’un conseillerait d’utiliser ces paramètres pour accélérer le codage ou faciliter certains environnements de test.

  6. Dans Visual Studio, cliquez sur Build>Build Solution.

    Si tout se passe bien, la fenêtre de build devrait afficher un message indiquant que la build a réussi.

  7. Localisez les fichiers de l’application d’exemple compilée

    Dans l’Explorateur de solutions, faites un clic droit sur le projet DisplayGreeting et sélectionnez Ouvrir le dossier dans l’Explorateur de fichiers.

    Accédez au dossier Debug qui contient l’exécutable compilé et le fichier de symboles pdb pour l’exemple. Par exemple, vous accéderiez à C:\Projets\DisplayGreeting\Debug, si c’est le dossier où vos projets sont stockés.

  8. Exécutez l’application d’exemple avec l’erreur de code

    Double-cliquez sur le fichier exe pour exécuter l’application d’exemple.

    Capture d’écran de la console exécutant le fichier DisplayGreeting.exe.

    Si cette boîte de dialogue s’affiche, sélectionnez Fermer le programme

    Capture d’écran de la boîte de dialogue affichant « DisplayGreeting.exe a cessé de fonctionner ».

    Dans la prochaine section du guide, nous enregistrerons l’exécution de l’application d’exemple pour voir si nous pouvons déterminer pourquoi cette exception se produit.

Section 2: Enregistrer une trace de l’application d’exemple « DisplayGreeting »

Dans la Section 2, vous enregistrerez une trace de l’application d’exemple « DisplayGreeting » qui ne se comporte pas correctement

Pour lancer l’application d’exemple et enregistrer une trace TTD, suivez ces étapes. Pour obtenir des informations générales sur l’enregistrement de traces TTD, veuillez consulter la rubrique Enregistrement d’une trace avec Voyage dans le temps.

  1. Exécutez WinDbg en tant qu’administrateur, afin de pouvoir enregistrer des traces de Voyage dans le temps.

  2. Dans WinDbg, sélectionnez Fichier>Démarrer le débogage>Lancer l’exécutable (avancé).

  3. Saisissez le chemin de l’exécutable en mode utilisateur que vous souhaitez enregistrer ou sélectionnez Parcourir pour naviguer jusqu’à l’exécutable. Pour obtenir plus d’informations sur l’utilisation du menu lancer l’exécutable dans WinDbg, veuillez consulter la section WinDbg - Démarrer une session en mode utilisateur.

    Capture d’écran de WinDbg avec la case à cocher « Enregistrement avec Débogage Time Travel » dans l’écran Lancer l’exécutable (avancé).

  4. Cochez la case Enregistrer avec le débogage de voyage dans le temps pour enregistrer une trace lorsque l’exécutable est lancé.

  5. Cliquez sur Configurer et Enregistrer pour démarrer l’enregistrement.

  6. Lorsque la boîte de dialogue « Configurer l’enregistrement » apparaît, cliquez sur Enregistrer pour lancer l’exécutable et commencer l’enregistrement.

    Capture d’écran de WinDbg affichant la boîte de dialogue Configurer l’enregistrement avec le chemin défini sur temp.

  7. La boîte de dialogue d’enregistrement apparaît, indiquant que la trace est en cours d’enregistrement. Peu de temps après, l’application plante.

  8. Cliquez sur Fermer le programme pour fermer la boîte de dialogue « DisplayGreeting ne répond pas ».

    Boîte de dialogue affichant l’application DisplayGreeting a cessé de fonctionner.

  9. Lorsque le programme plante, le fichier de trace est fermé et écrit sur le disque.

    Capture d’écran de la sortie WinDbg affichant 1/1 images clés indexées.

  10. Le débogueur ouvrira automatiquement le fichier de trace et l’indexera. L’indexation est un processus qui permet un débogage efficace du fichier de trace. Ce processus d’indexation prendra plus de temps pour les fichiers de trace plus volumineux.

    (5120.2540): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: D:0 [Unindexed] Index
    !index
    Indexed 10/22 keyframes
    Indexed 20/22 keyframes
    Indexed 22/22 keyframes
    Successfully created the index in 755ms.
    

Remarque

Une image clé est un emplacement dans une trace utilisé pour l’indexation. Les images clés sont générées automatiquement. Les traces plus volumineuses contiendront plus d’images clés.

  1. À ce stade, vous êtes au début du fichier de trace et êtes prêt à voyager vers l’avant et vers l’arrière dans le temps.

    Maintenant que vous avez enregistré une trace TTD, vous pouvez rejouer la trace ou travailler avec le fichier de trace, par exemple le partager avec un collègue. Pour plus d’informations sur le travail avec les fichiers de trace, veuillez consulter la rubrique Débogage Time Travel - Travailler avec les fichiers de trace.

Dans la prochaine section de ce laboratoire, nous analyserons le fichier de trace pour localiser le problème de notre code.

Section 3: Analyser l’enregistrement de la trace pour identifier le problème de code

Dans la Section 3, vous analyserez l’enregistrement du fichier de trace pour identifier le problème de code.

Configurez l’environnement WinDbg

  1. Ajoutez votre emplacement de symbole local au chemin des symboles et rechargez les symboles, en tapant les commandes suivantes.

    .sympath+ C:\MyProjects\DisplayGreeting\Debug
    .reload
    
  2. Ajoutez votre emplacement de code local au chemin des sources en tapant la commande suivante.

    .srcpath+ C:\MyProjects\DisplayGreeting\DisplayGreeting
    
  3. Pour pouvoir afficher l’état de la pile et des variables locales, sur le ruban WinDbg, sélectionnez Affichage puis Variables locales et Affichage puis Pile. Organisez les fenêtres pour vous permettre de les voir, ainsi que le code source et les fenêtres de commande en même temps.

  4. Sur le ruban WinDbg, sélectionnez Source puis Ouvrir le fichier source. Localisez le fichier DisplayGreeting.cpp et ouvrez-le.

Examinez l’exception

  1. Lorsque le fichier de trace est chargé, il affiche des informations indiquant qu’une exception s’est produite.

    2fa8.1fdc): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68ef8100 ebx=00000000 ecx=77a266ac edx=69614afc esi=6961137c edi=004da000
    eip=77a266ac esp=0023f9b4 ebp=0023fc04 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:0023fac0=00000000
    
  2. Utilisez la commande dx pour répertorier tous les événements de l’enregistrement. L’événement d’exception est répertorié dans les événements.

    0:000> dx -r1 @$curprocess.TTD.Events
    ...
    [0x2c]           : Module Loaded at position: 9967:0
    [0x2d]           : Exception at 9BDC:0
    [0x2e]           : Thread terminated at 9C43:0
    ...
    
    

    Remarque

    Dans ce guide, trois points sont utilisés pour indiquer que la sortie superflue a été supprimée.

  3. Cliquez sur l’événement Exception pour afficher des informations sur cet événement TTD.

    0:000> dx -r1 @$curprocess.TTD.Events[17]
    @$curprocess.TTD.Events[17]                 : Exception at 68:0
        Type             : Exception
        Position         : 68:0 [Time Travel]
        Exception        : Exception of type Hardware at PC: 0X540020
    
  4. Cliquez sur le champ Exception pour approfondir les données de l’exception.

    0:000> dx -r1 @$curprocess.TTD.Events[17].Exception
    @$curprocess.TTD.Events[17].Exception                 : Exception of type Hardware at PC: 0X540020
        Position         : 68:0 [Time Travel]
        Type             : Hardware
        ProgramCounter   : 0x540020
        Code             : 0xc0000005
        Flags            : 0x0
        RecordAddress    : 0x0
    

    Les données de l’exception indiquent qu’il s’agit d’une erreur matérielle générée par le processeur. Il fournit également le code d’exception 0xc0000005 qui indique qu’il s’agit d’une violation d’accès. Cela indique généralement que nous avons essayé d’écrire dans une zone mémoire à laquelle nous n’avons pas accès.

  5. Cliquez sur le lien [Voyage dans le temps] dans l’événement d’exception pour vous déplacer à cette position dans la trace.

    0:000> dx @$curprocess.TTD.Events[17].Exception.Position.SeekTo()
    Setting position: 68:0
    
    @$curprocess.TTD.Events[17].Exception.Position.SeekTo()
    (16c8.1f28): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 68:0
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00540020 esp=00effe4c ebp=00520055 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    00540020 ??
    

    À noter dans cette sortie est que la pile et le pointeur de base pointent vers deux adresses très différentes.

    esp=00effe4c ebp=00520055
    

    Cela pourrait indiquer une corruption de la pile - peut-être qu’une fonction a retourné puis corrompu la pile. Pour valider cela, nous devons revenir en arrière avant que l’état du CPU ne soit corrompu et voir si nous pouvons déterminer quand la corruption de la pile s’est produite.

Examinez les variables locales et définissez un point d’arrêt de code

Au point de défaillance dans la trace, il est courant de se retrouver quelques étapes après la vraie cause dans le code de gestion des erreurs. Avec le voyage dans le temps, nous pouvons revenir en arrière une instruction à la fois, pour localiser la véritable cause fondamentale.

  1. À partir du ruban Accueil, utilisez la commande Step Into Back pour revenir en arrière de trois instructions. Pendant que vous faites cela, continuez à examiner les fenêtres de pile et de mémoire.

    La fenêtre de commande affichera la position de voyage dans le temps et les registres au fur et à mesure que vous revenez en arrière de trois instructions.

    0:000> t-
    Time Travel Position: 67:40
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00540020 esp=00effe4c ebp=00520055 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    00540020 ??              ???
    
    0:000> t-
    Time Travel Position: 67:3F
    eax=00000000 ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=0019193d esp=00effe48 ebp=00520055 iopl=0         nv up ei pl zr na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
    DisplayGreeting!main+0x4d:
    0019193d c3
    
    0:000> t-
    Time Travel Position: 67:39
    eax=0000004c ebx=00cf8000 ecx=99da9203 edx=69cf1a6c esi=00191046 edi=00191046
    eip=00191935 esp=00effd94 ebp=00effe44 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    DisplayGreeting!main+0x45:
    

    Remarque

    Dans ce guide, la sortie de la commande montre les commandes qui peuvent être utilisées au lieu des options de menu UI pour permettre aux utilisateurs préférant une utilisation en ligne de commande d’utiliser des commandes en ligne de commande.

  2. À ce stade de la trace, notre pile et notre pointeur de base ont des valeurs qui ont plus de sens, il semble donc que nous nous approchons du point dans le code où la corruption s’est produite.

    esp=00effd94 ebp=00effe44
    

    Il est également intéressant de noter que la fenêtre Données Locales contient des valeurs de notre application cible et que la fenêtre du code source met en surbrillance la ligne de code qui est prête à être exécutée à ce stade de la trace.

    Capture d’écran de WinDbg affichant la fenêtre Données Locales avec la sortie ASCII de mémoire et la fenêtre Code source.

  3. Pour approfondir, nous pouvons ouvrir une fenêtre de mémoire pour afficher le contenu près de l’adresse mémoire du pointeur de base de 0x00effe44.

  4. Pour afficher les caractères ASCII associés, sur le ruban Mémoire, sélectionnez Texte puis ASCII.

    Capture d’écran de l’aperçu de WinDbg affichant la sortie ASCII de la mémoire et la fenêtre du code source.

  5. Au lieu que le pointeur de base pointe vers une instruction, il pointe vers notre texte de message. Donc quelque chose ne va pas ici, cela pourrait être proche du moment où nous avons corrompu la pile. Pour approfondir, nous allons définir un point d’arrêt.

Remarque

Dans cet exemple très simple, il serait assez facile de simplement regarder dans le code, mais s’il y a des centaines de lignes de code et des dizaines de sous-routines, les techniques décrites ici peuvent être utilisées pour réduire le temps nécessaire pour localiser le problème.

TTD et les points d’arrêt

L’utilisation de points d’arrêt est une approche courante pour mettre en pause l’exécution du code à un événement d’intérêt. TTD vous permet de définir un point d’arrêt et de revenir en arrière dans le temps jusqu’à ce que ce point d’arrêt soit atteint après que la trace ait été enregistrée. La possibilité d’examiner l’état du processus après qu’un problème s’est produit, pour déterminer le meilleur emplacement pour un point d’arrêt, permet des flux de travail de débogage supplémentaires uniques à TTD.

Points d’arrêt d’accès mémoire

Vous pouvez définir des points d’arrêt qui se déclenchent lorsque l’emplacement mémoire est accédé. Utilisez la commande ba (Pause sur Accès), avec la syntaxe suivante :

ba <access> <size> <address> {options}
Option Description

e

execute (lorsque le CPU récupère une instruction à partir de l’adresse)

r

read/write (lorsque le CPU lit ou écrit à l’adresse)

w

write (lorsque le CPU écrit à l’adresse)

Notez que vous ne pouvez définir que quatre points d’arrêt de données à un moment donné et c’est à vous de vous assurer que vous alignez correctement vos données sinon le point d’arrêt ne se déclenchera pas (les mots doivent se terminer à des adresses divisibles par 2, les dwords doivent être divisibles par 4 et les quadwords par 0 ou 8).

Définir le point d’arrêt d’accès à la mémoire pour le pointeur de base

  1. À ce stade de la trace, nous aimerions définir un point d’arrêt sur l’accès à la mémoire en écriture au pointeur de base - ebp qui dans notre exemple est 00effe44. Pour ce faire, utilisez la commande ba en utilisant l’adresse que nous voulons surveiller. Nous voulons surveiller les écritures pour quatre octets, donc nous spécifions w4.

    0:000> ba w4 00effe44
    
  2. Sélectionnez Affichage puis Points d’arrêt pour confirmer qu’ils sont définis comme prévu.

    Capture d’écran de la fenêtre Points d’arrêt WinDbg affichant un point d’arrêt unique.

  3. Dans le menu Accueil, sélectionnez Revenir (Go Back) en arrière jusqu’à ce que le point d’arrêt soit atteint.

    0:000> g-
    Breakpoint 0 hit
    Time Travel Position: 5B:92
    eax=0000000f ebx=003db000 ecx=00000000 edx=00cc1a6c esi=00d41046 edi=0053fde8
    eip=00d4174a esp=0053fcf8 ebp=0053fde8 iopl=0         nv up ei pl nz ac pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000216
    DisplayGreeting!DisplayGreeting+0x3a:
    00d4174a c745e000000000  mov     dword ptr [ebp-20h],0 ss:002b:0053fdc8=cccccccc
    
  4. Sélectionnez Affichage , puis Variables Locales. Dans la fenêtre Variables Locales, nous pouvons voir que la variable destination contient seulement une partie du message, tandis que la source contient tout le texte. Cette information supporte l’idée que la pile a été corrompue.

    Capture d’écran de WinDbg affichant la fenêtre Variables Locales

  5. À ce stade, nous pouvons examiner la pile du programme pour voir quel code est actif. Dans le ruban Affichage , sélectionnez Pile.

    Capture d’écran de WinDbg affichant la fenêtre Pile

Comme il est très peu probable que la fonction wscpy_s() fournie par Microsoft ait un bug de code comme celui-ci, nous regardons plus loin dans la pile. La pile montre que Greeting!main appelle Greeting!GetCppConGreeting. Dans notre petit exemple de code, nous pourrions simplement ouvrir le code à ce moment-là et probablement trouver l’erreur assez facilement. Mais pour illustrer les techniques qui peuvent être utilisées avec des programmes plus grands et plus complexes, nous allons définir un nouveau point d’arrêt pour enquêter plus avant.

Définir le point d’arrêt d’accès pour la fonction GetCppConGreeting

  1. Utilisez la fenêtre des points d’arrêt pour effacer le point d’arrêt existant en faisant un clic droit sur le point d’arrêt existant et en sélectionnant Supprimer.

  2. Déterminez l’adresse de la fonction DisplayGreeting!GetCppConGreeting en utilisant la commande dx.

    0:000> dx &DisplayGreeting!GetCppConGreeting
    &DisplayGreeting!GetCppConGreeting                 : 0xb61720 [Type: void (__cdecl*)(wchar_t *,unsigned int)]
        [Type: void __cdecl(wchar_t *,unsigned int)]
    
  3. Utilisez la commande ba pour définir un point d’arrêt sur l’accès à la mémoire. Parce que la fonction sera simplement lue depuis la mémoire pour l’exécution, nous devons définir un point d’arrêt de lecture.

    0:000> ba r4 b61720
    
  4. Confirmez qu’un point d’arrêt de lecture matériel est actif dans la fenêtre des points d’arrêt.

    Capture d’écran de la fenêtre Points d’arrêt WinDbg affichant un point d’arrêt de lecture matériel unique.

  5. Comme nous nous interrogeons sur la taille de la chaîne de salutation, nous allons définir une fenêtre de surveillance pour afficher la valeur de sizeof(greeting). Dans ruban Affichage, sélectionnez Espion et fournissez sizeof(greeting).

    Capture d’écran de WinDbg affichant une fenêtre Espion Variables Locales

  6. Sur le menu Voyage dans le temps, utilisez le démarrage du Voyage dans le temps ou utilisez la commande !tt 0 pour vous déplacer au début de la trace.

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  7. Sur le menu Accueil, cliquez sur Aller ou utilisez la commande g pour avancer dans le code jusqu’à ce que le point d’arrêt soit atteint.

    0:000> g
    Breakpoint 2 hit
    Time Travel Position: 4B:1AD
    eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046
    eip=00b61721 esp=00ddf7a4 ebp=00ddf864 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!GetCppConGreeting+0x1:
    00b61721 8bec            mov     ebp,esp
    
  8. Sur le menu Accueil, sélectionnez Pas en arrière pour ou utilisez g-u revenir en arrière d’une étape.

    0:000> g-u
    Time Travel Position: 4B:1AA
    eax=00ddf800 ebx=00fa2000 ecx=00ddf800 edx=00b61046 esi=00b61046 edi=00b61046
    eip=00b61917 esp=00ddf7ac ebp=00ddf864 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!main+0x27:
    00b61917 e8def7ffff      call    DisplayGreeting!ILT+245(?GetCppConGreetingYAXPA_WIZ) (00b610fa)
    
  9. Il semble que nous ayons trouvé la cause première. Le tableau greeting que nous avons déclaré a une longueur de 50 caractères, tandis que sizeof(greeting) que nous passons à GetCppConGreeting est de 0x64, 100).

    Capture d’écran de WinDbg affichant le code DisplayGreeting avec une fenêtre Watch Locals montrant 0x64.

    En examinant plus en détail le problème de taille, nous remarquons également que le message fait 75 caractères de long, 76 avec le caractère de fin de chaîne.

    HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!
    
  10. Une façon de corriger le code serait d’augmenter la taille du tableau de caractères à 100.

    std::array <wchar_t, 100> greeting{};
    

    Et nous devons également changer sizeof(greeting) en size(greeting) dans cette ligne de code.

     GetCppConGreeting(greeting.data(), size(greeting));
    
  11. Pour valider ces corrections, nous pourrions recompiler le code et confirmer qu’il s’exécute sans erreur.

Définir un point d’arrêt en utilisant la fenêtre source

  1. Une autre façon de procéder à cette investigation serait de définir un point d’arrêt en cliquant sur n’importe quelle ligne de code. Par exemple, le fait de cliquer sur le côté droit de la ligne de définition std :array dans la fenêtre source définit un point d’arrêt.

    Capture d’écran de la fenêtre Source dans WinDbg avec un point d’arrêt défini sur std::array.

  2. Sur le menu Voyage dans le temps, utilisez la commande Démarrage du Voyage dans le temps pour vous déplacer au début de la trace.

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  3. Sur le ruban Accueil, cliquez sur Aller pour revenir en arrière jusqu’à ce que le point d’arrêt soit atteint.

    Breakpoint 0 hit
    Time Travel Position: 5B:AF
    eax=0000000f ebx=00c20000 ecx=00000000 edx=00000000 esi=013a1046 edi=00effa60
    eip=013a17c1 esp=00eff970 ebp=00effa60 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!DisplayGreeting+0x41:
    013a17c1 8bf4            mov     esi,esp
    

Définir le point d’arrêt d’accès pour la variable greeting

Une autre façon alternative d’effectuer cette investigation serait de définir un point d’arrêt sur les variables suspectes et d’examiner quel code les modifie. Par exemple, pour définir un point d’arrêt sur la variable greeting dans la méthode GetCppConGreeting, utilisez cette procédure.

Cette partie du guide suppose que vous êtes toujours situé sur le point d’arrêt de la section précédente.

  1. Depuis Affichage, puis Variables locales. Dans la fenêtre Variables Locales, greeting est disponible dans le contexte actuel, nous pourrons donc déterminer son emplacement mémoire.

  2. Utilisez la commande dx pour examiner le tableau greeting.

    0:000> dx &greeting
    &greeting                 : 0xddf800 [Type: std::array<wchar_t,50> *]
       [+0x000] _Elems           : "꽘棶檙瞝???" [Type: wchar_t [50]]
    

    Dans cette trace, greeting est situé en mémoire à ddf800.

  3. Utilisez la fenêtre des points d’arrêt pour effacer tout point d’arrêt existant en cliquant avec le bouton droit sur le point d’arrêt existant et en sélectionnant Supprimer.

  4. Définissez le point d’arrêt avec la commande ba en utilisant l’adresse mémoire que nous voulons surveiller pour l’accès en écriture.

    ba w4 ddf800
    
  5. Sur le menu Voyage dans le temps, utilisez la commande Démarrage du Voyage dans le temps pour vous déplacer au début de la trace.

    0:000> !tt 0
    Setting position to the beginning of the trace
    Setting position: 15:0
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 15:0
    eax=68e28100 ebx=00000000 ecx=77a266ac edx=69e34afc esi=69e3137c edi=00fa2000
    eip=77a266ac esp=00ddf3b8 ebp=00ddf608 iopl=0         nv up ei pl nz na pe nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
    ntdll!LdrpInitializeProcess+0x1d1c:
    77a266ac 83bdbcfeffff00  cmp     dword ptr [ebp-144h],0 ss:002b:00ddf4c4=00000000
    
  6. Sur le menu Accueil, sélectionnez Aller pour avancer jusqu’au premier point d’accès à la mémoire du tableau greeting.

    0:000> g-
    Breakpoint 0 hit
    Time Travel Position: 5B:9C
    eax=cccccccc ebx=002b1000 ecx=00000000 edx=68d51a6c esi=013a1046 edi=001bf7d8
    eip=013a1735 esp=001bf6b8 ebp=001bf7d8 iopl=0         nv up ei pl nz na po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
    DisplayGreeting!GetCppConGreeting+0x25:
    013a1735 c745ec04000000  mov     dword ptr [ebp-14h],4 ss:002b:001bf7c4=cccccccc
    

    Alternativement, nous aurions pu nous déplacer jusqu’à la fin de la trace et travailler à rebours dans le code pour trouver le dernier point dans la trace où l’emplacement mémoire du tableau a été écrit.

Utilisez les objets TTD.Memory pour visualiser l’accès à la mémoire

Une autre façon de déterminer à quels points de la trace la mémoire a été accédée est d’utiliser les objets TTD.Memory et la commande dx.

  1. Utilisez la commande dx pour examiner le tableau greeting.

    0:000> dx &greeting
    &greeting                 : 0xddf800 [Type: std::array<wchar_t,50> *]
       [+0x000] _Elems           : "꽘棶檙瞝???" [Type: wchar_t [50]]
    

    Dans cette trace, greeting est situé en mémoire à ddf800.

  2. Utilisez la commande dx pour regarder les quatre octets en mémoire à partir de cette adresse avec l’accès en lecture/écriture.

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")
    @$cursession.TTD.Memory(0x1bf7d0,0x1bf7d4, "rw")                
        [0x0]           
        [0x1]           
        [0x2]           
        [0x3]           
        [0x4]           
        [0x5]           
        [0x6]           
        [0x7]           
        [0x8]           
        [0x9]           
        [0xa]           
        ...         
    
  3. Cliquez sur l’une des occurrences pour afficher plus d’informations sur cette occurrence d’accès à la mémoire.

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5]
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5]                
        EventType        : MemoryAccess
        ThreadId         : 0x710
        UniqueThreadId   : 0x2
        TimeStart        : 27:3C1 [Time Travel]
        TimeEnd          : 27:3C1 [Time Travel]
        AccessType       : Write
        IP               : 0x6900432f
        Address          : 0xddf800
        Size             : 0x4
        Value            : 0xddf818
    
  4. Cliquez sur [Voyage dans le temps] pour positionner la trace au point dans le temps.

    0:000> dx @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5].TimeStart.SeekTo()
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw")[5].TimeStart.SeekTo()
    (1e5c.710): Break instruction exception - code 80000003 (first/second chance not available)
    Time Travel Position: 27:3C1
    eax=00ddf81c ebx=00fa2000 ecx=00ddf818 edx=ffffffff esi=00000000 edi=00b61046
    eip=6900432f esp=00ddf804 ebp=00ddf810 iopl=0         nv up ei pl nz ac po nc
    cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
    ucrtbased!_register_onexit_function+0xf:
    6900432f 51              push    ecx
    
  5. Si nous sommes intéressés par la dernière occurrence d’accès à la mémoire en lecture/écriture dans la trace, nous pouvons cliquer sur le dernier élément de la liste ou ajouter la fonction .Last() à la fin de la commande dx.

    0:000> dx -r1 @$cursession.TTD.Memory(0xddf800,0xddf804, "rw").Last()
    @$cursession.TTD.Memory(0xddf800,0xddf804, "rw").Last()                
        EventType        : MemoryAccess
        ThreadId         : 0x710
        UniqueThreadId   : 0x2
        TimeStart        : 53:100E [Time Travel]
        TimeEnd          : 53:100E [Time Travel]
        AccessType       : Read
        IP               : 0x690338e4
        Address          : 0xddf802
        Size             : 0x2
        Value            : 0x45
    
  6. Nous pourrions ensuite cliquer sur [Voyage dans le temps] pour nous déplacer à cette position dans la trace et examiner plus en détail l’exécution du code à ce moment-là, en utilisant les techniques décrites plus tôt dans ce laboratoire.

Pour plus d’informations sur les objets TTD.Memory, voir Objet TTD.Memory.

Résumé

Dans cet exemple très simple, le problème aurait pu être déterminé en examinant les quelques lignes de code, mais dans des programmes plus grands, les techniques présentées ici peuvent être utilisées pour réduire le temps nécessaire pour localiser un problème dans un code complexe.

Une fois qu’une trace est enregistrée, les étapes de trace et de reproduction peuvent être partagées, et le problème sera reproductible à la demande sur n’importe quel PC.

Voir aussi

Débogage de voyage dans le temps - Vue d’ensemble

Débogage de voyage dans le temps - Enregistrement

Débogage de voyage dans le temps - Rejouer une trace

Débogage de voyage dans le temps - Travail avec les fichiers de trace