Partager via


WinDbg : Chronologies

logo WinDbg avec une loupe inspectant les bits.

Débogage temporel (TTD) permet aux utilisateurs d’enregistrer des traces, qui sont des enregistrements de l’exécution d’un programme. Les chronologies sont une représentation visuelle des événements qui se produisent pendant l’exécution. Ces événements peuvent être des emplacements de points d’arrêt, de lectures/écritures de mémoire, d’appels de fonction et de retours et d’exceptions.

Capture d’écran des chronologies dans le débogueur affichant les exceptions, l’accès à la mémoire, les points d’arrêt et les appels de fonction.

Utilisez la fenêtre Chronologies pour afficher les événements importants, comprendre leur position relative et accéder facilement à leur emplacement dans votre fichier de trace TTD. Utilisez plusieurs chronologies pour explorer visuellement les événements dans la trace de voyage de temps et découvrir la corrélation des événements.

La fenêtre Chronologie s’affiche lorsque vous ouvrez un fichier de trace TTD. Il affiche les événements clés sans avoir à créer manuellement des requêtes de modèle de données. En même temps, tous les objets de voyage de temps sont disponibles pour permettre des requêtes de données plus complexes.

Pour plus d’informations sur la création et l’utilisation des fichiers de trace de voyage temporel, consultez Time Travel Debugging : Vue d'ensemble.

Types de chronologies

La fenêtre Chronologies affiche les événements dans les chronologies suivantes :

  • Exceptions : vous pouvez filtrer sur un code d’exception spécifique.
  • Points d’arrêt : vous pouvez voir quand les points d’arrêt sont atteints sur une chronologie.
  • Accès à la mémoire : vous pouvez lire, écrire et exécuter entre deux adresses mémoire.
  • Appels de fonction : vous pouvez effectuer une recherche sous la forme .module!function

Pointez sur chaque événement pour afficher plus d’informations dans une info-bulle. La sélection d’un événement exécute la requête pour l’événement et affiche plus d’informations. Le double-clic d’un événement amène à cet emplacement dans le fichier de traçage TTD.

Exceptions

Lorsque vous chargez un fichier de trace et que la chronologie est active, elle affiche automatiquement toutes les exceptions dans l’enregistrement.

Lorsque vous pointez sur un point d’arrêt, des informations telles que le type d’exception et le code d’exception s’affichent.

Capture d’écran d’une chronologie dans le débogueur affichant des exceptions avec des informations sur un code d’exception spécifique.

Vous pouvez filtrer davantage sur un code d’exception spécifique à l’aide du champ de code d’exception facultatif.

Capture d’écran d’une boîte de dialogue d’exception du débogueur de chronologie avec type de chronologie défini sur Exceptions et Code d’exception défini sur 0xC0000004.

Vous pouvez également ajouter une nouvelle chronologie pour un type d’exception spécifique.

Points d’arrêt

Après avoir ajouté un point d’arrêt, les positions de la chronologie vous indiquent quand ce point d’arrêt est atteint. Par exemple, vous pouvez utiliser la commande bp Set Breakpoint. Lorsque vous pointez sur un point d’arrêt, l’adresse et le pointeur d’instruction associés au point d’arrêt s’affichent.

Capture d’écran d’une chronologie dans le débogueur affichant environ 30 indicateurs de point d’arrêt.

Lorsque le point d’arrêt est effacé, la chronologie du point d’arrêt associée est automatiquement supprimée.

Appels de fonction

Vous pouvez voir les positions des appels de fonction sur la chronologie. Pour effectuer cette étape, fournissez la recherche sous la forme .module!function par exemple TimelineTestCode!multiplyTwo. Vous pouvez également spécifier des caractères génériques, par exemple TimelineTestCode!m*.

Capture d’écran de l’ajout d’une chronologie dans le débogueur avec un nom d’appel de fonction entré.

Lorsque vous pointez sur un appel de fonction, le nom de la fonction, les paramètres d’entrée, leurs valeurs et la valeur de retour s’affichent. Cet exemple montre la mémoire tampon et la taille, car elles sont les paramètres pour DisplayGreeting!GetCppConGreeting.

Capture d’écran d’une chronologie dans le débogueur affichant les appels de fonction et la fenêtre Registres.

Accès à la mémoire

Utilisez la chronologie des accès à la mémoire pour voir quand une plage spécifique de mémoire a été lue ou écrite, ou où l’exécution du code a eu lieu. Les adresses de démarrage et d’arrêt sont utilisées pour définir une plage entre deux adresses mémoire.

Capture d’écran de l’ajout d’une boîte de dialogue Accès à la mémoire avec l’option Écriture sélectionnée.

Lorsque vous pointez sur un élément d’accès à la mémoire, la valeur et le pointeur d’instruction s’affichent.

Capture d’écran d’une chronologie dans le débogueur affichant les événements d’accès à la mémoire.

Utiliser des chronologies

Une ligne grise verticale suit le curseur lorsque vous pointez sur la chronologie. La ligne bleue verticale indique la position actuelle dans la trace.

Sélectionnez les icônes de loupe pour effectuer un zoom avant et arrière sur la chronologie.

Dans la zone de contrôle de chronologie supérieure, utilisez le rectangle pour faire défiler l’affichage de la chronologie. Faites glisser les délimiteurs externes du rectangle pour redimensionner l’affichage chronologie actuel.

Capture d’écran d’une chronologie dans le débogueur montrant la zone supérieure utilisée pour sélectionner l’affichage actif.

Mouvements de la souris

Pour effectuer un zoom avant et arrière, sélectionnez Ctrl et utilisez la roulette de défilement.

Pour faire un panoramique de côté à côté, maintenez la touche Maj et utilisez la roulette de défilement.

Techniques de débogage de chronogramme

Pour illustrer les techniques de chronologie de débogage, le guide de débogage Time Travel est ici réutilisé. Cette démonstration suppose que vous avez terminé les deux premières étapes pour générer l’exemple de code et créer l’enregistrement TTD à l’aide des deux premières étapes décrites ici.

Dans ce scénario, la première étape consiste à rechercher l’exception dans la trace de voyage dans le temps. Double-cliquez sur la seule exception que vous voyez dans la chronologie.

Dans la fenêtre Commande , vous pouvez voir que la commande suivante a été émise lorsque vous avez sélectionné l’exception.

(2dcc.6600): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: CC:0
@$curprocess.TTD.Events.Where(t => t.Type == "Exception")[0x0].Position.SeekTo()

Sélectionnez Afficher>les registres pour afficher les registres à ce stade dans la chronologie pour commencer votre investigation.

Capture d’écran d’une chronologie dans le débogueur affichant l’exception du labo de démonstration et la fenêtre Registres.

Dans la sortie de commande, la pile (esp) et le pointeur de base (ebp) pointent vers différentes adresses. Cette différence peut indiquer une altération de la pile. Il est possible qu'une fonction ait terminé son exécution puis ait endommagé la pile. Pour valider ce problème, retournez à un moment avant que l'état du processeur soit corrompu et vérifiez si vous pouvez déterminer quand l'altération de la pile s'est produite.

À mesure que vous effectuez ce processus, examinez les valeurs des variables locales et de la pile :

  • Sélectionnez Afficher>les variables locales pour afficher les valeurs locales.
  • Sélectionnez Vue>Pile pour voir la pile d’exécution de code.

Au moment de l’échec, dans la trace, il est courant d’arriver quelques étapes après la véritable cause dans le code de traitement des erreurs. Avec le voyage dans le temps, vous pouvez revenir en arrière une instruction à la fois pour localiser la véritable cause racine.

Dans le ruban Accueil , utilisez la commande Step Into Back pour revenir à trois instructions. À mesure que vous effectuez ce processus, continuez à examiner les fenêtres Stack, Locals et Registers.

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

0:000> t-
Time Travel Position: CB:41
eax=00000000 ebx=00564000 ecx=c0d21d62 edx=7a1e4a6c esi=00061299 edi=00061299
eip=00540020 esp=003cf7d0 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: CB:40
eax=00000000 ebx=00564000 ecx=c0d21d62 edx=7a1e4a6c esi=00061299 edi=00061299
eip=00061767 esp=003cf7cc 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+0x57:
00061767 c3              ret
0:000> t-
Time Travel Position: CB:3A
eax=0000004c ebx=00564000 ecx=c0d21d62 edx=7a1e4a6c esi=00061299 edi=00061299
eip=0006175f esp=003cf718 ebp=003cf7c8 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
DisplayGreeting!main+0x4f:
0006175f 33c0            xor     eax,eax

À ce stade de la trace, votre pile et votre pointeur de base ont des valeurs qui sont plus logiques. Il semble que vous vous rapprochez du point dans le code où l’altération s’est produite.

esp=003cf718 ebp=003cf7c8

La fenêtre Locals contient des valeurs de votre application cible. La fenêtre Code source met en surbrillance la ligne de code prête à être exécutée dans votre code source à ce stade de la trace.

Pour examiner plus en détail, ouvrez une fenêtre Mémoire pour afficher le contenu près de l’adresse mémoire du pointeur de pile (esp). Dans cet exemple, il a la valeur 003cf7c8. Sélectionnez Mémoire>Texte>ASCII pour afficher le texte ASCII stocké à cette adresse.

Capture d’écran du débogueur affichant les fenêtres Registres, Pile et Mémoire.

Chronologie de l’accès à la mémoire

Une fois qu’un emplacement de mémoire intéressant est identifié, utilisez cette valeur pour ajouter une chronologie d’accès à la mémoire. Sélectionnez + Ajouter une chronologie et renseignez l’adresse de départ. Examinez 4 octets de sorte que lorsque vous les ajoutez à la valeur de d'adresse de début003cf7c8, vous avez une valeur de d'adresse de fin003cf7cb. La valeur par défaut consiste à examiner toutes les écritures de mémoire, mais vous pouvez également examiner uniquement les écritures ou l’exécution du code à cette adresse.

Capture d’écran de l’ajout d’une boîte de dialogue d’accès à la mémoire de chronologie avec l’option Écriture sélectionnée et une valeur de début de 003cf7c8.

Vous pouvez maintenant parcourir la chronologie en sens inverse pour examiner à quel moment dans cette trace de voyage dans le temps cet emplacement mémoire a été modifié, pour voir ce que cela révèle. Lorsque vous sélectionnez cette position dans la chronologie, vous pouvez voir que les valeurs locales sont différentes pour la chaîne copiée. La valeur de destination semble incomplète, comme si la longueur de votre chaîne n’est pas correcte.

Capture d’écran de la chronologie d’accès à la mémoire et de la fenêtre Locals affichant des valeurs locales avec des valeurs sources et de destination différentes.

Chronologie des points d’arrêt

L’utilisation de points d’arrêt est une approche courante pour suspendre l’exécution du code à un événement d’intérêt. Avec TTD, vous pouvez définir un point d’arrêt et revenir en arrière jusqu’à ce que ce point d’arrêt soit atteint après l’enregistrement de la trace. La possibilité d’examiner l’état du processus une fois qu’un problème se produit, afin de déterminer le meilleur emplacement pour un point d’arrêt, active davantage de flux de travail de débogage uniques au TTD.

Pour explorer une autre technique de débogage de chronologie, sélectionnez l’exception dans la chronologie et revenez à nouveau trois étapes en arrière à l’aide de la commande Retour en arrière sur le ruban Accueil.

Dans ce petit exemple, il est facile d’examiner le code. Si vous avez des centaines de lignes de code et des dizaines de sous-routines, utilisez les techniques décrites ici pour réduire le temps nécessaire pour localiser le problème.

Comme mentionné précédemment, le pointeur de base (esp) pointe vers le texte de votre message au lieu de pointer vers une instruction.

Utilisez la commande ba pour définir un point d’arrêt sur l’accès à la mémoire. Vous définissez un point d’arrêt d’écriture w - pour voir quand cette zone de mémoire est écrite.

0:000> ba w4 003cf7c8

Bien que vous utilisiez un simple point d'arrêt d'accès mémoire, vous pouvez créer des points d'arrêt sous forme d'instructions conditionnelles plus complexes. Pour plus d’informations, consultez bp, bu, bm (Définir le point d’arrêt).

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

À ce stade, vous pouvez examiner la pile des programmes pour voir quel code est actif.

Capture d’écran d’une chronologie dans le débogueur affichant les chronologies d’accès à la mémoire et les fenêtres de pile.

Étant donné qu'il est peu probable que la fonction Microsoft fournie par wscpy_s() ait un bogue de code de ce type, recherchez plus loin dans la stack. La pile montre qu'Greeting!main appelle Greeting!GetCppConGreeting. Dans votre petit exemple de code, vous pouvez ouvrir le code à ce stade et trouver l’erreur facilement. Toutefois, pour illustrer les techniques que vous pouvez utiliser avec un programme plus volumineux et plus complexe, vous ajoutez une chronologie des appels de fonction.

Chronologie des appels de fonction

Sélectionnez + Ajouter une chronologie et saisissez DisplayGreeting!GetCppConGreeting dans le champ chaîne de recherche de fonction.

Les cases d’emplacement de début et d’emplacement de fin indiquent le début et la fin d’un appel de fonction dans la trace.

Vous pouvez utiliser le dx commande pour afficher l’objet d’appel de fonction et voir les champs TimeStart et TimeEnd associés, qui correspondent à l’emplacement de début et de fin de l’appel de fonction.

dx @$cursession.TTD.Calls("DisplayGreeting!GetCppConGreeting")[0x0]
    EventType        : 0x0
    ThreadId         : 0x6600
    UniqueThreadId   : 0x2
    TimeStart        : 6D:BD [Time Travel]
    SystemTimeStart  : Thursday, October 31, 2019 23:36:05
    TimeEnd          : 6D:742 [Time Travel]
    SystemTimeEnd    : Thursday, October 31, 2019 23:36:05
    Function         : DisplayGreeting!GetCppConGreeting
    FunctionAddress  : 0x615a0
    ReturnAddress    : 0x61746
    Parameters  

Vous devez sélectionner l’une des cases Emplacement de début ou Emplacement de fin , ou les deux cases à cocher Emplacement de début et Emplacement de fin .

Capture d’écran de la boîte de dialogue Ajouter une nouvelle chronologie affichant l’ajout d’une chronologie d’appels de fonction avec une chaîne de recherche de fonction 'DisplayGreeting!GetCppConGreeting'.

Votre code n’est pas récursif ou réinitif, il est donc facile de localiser sur la chronologie lorsque la GetCppConGreeting méthode est appelée. L’appel à GetCppConGreeting se produit également en même temps que votre point d’arrêt et l’événement d’accès à la mémoire que vous avez défini. Il semble donc que vous avez ciblé une zone spécifique du code pour examiner attentivement la cause première de l’incident de l’application.

Capture d’écran d’une chronologie dans le débogueur affichant la chronologie d’accès à la mémoire et la fenêtre Locals avec un message et une mémoire tampon contenant différentes valeurs de chaîne.

Explorer l’exécution du code en affichant plusieurs chronologies

Bien que notre exemple de code soit petit, la technique d’utilisation de plusieurs chronologies permet l’exploration visuelle d’une trace de voyage de temps. Vous pouvez consulter le fichier de trace pour poser des questions, telles que « Quand une zone de mémoire est accessible avant qu’un point d’arrêt soit atteint ? ».

Capture d’écran d’une chronologie dans le débogueur affichant une chronologie d’accès à la mémoire et la fenêtre Locals.

La possibilité d’afficher davantage de corrélations et de trouver des éléments inattendus différencie l’outil de chronologie de l’interaction avec la trace de trajet du temps à l’aide de commandes de ligne de commande.

Marqueurs de chronologie

Marquez les positions temporelles importantes dans WinDbg au lieu de copier et coller manuellement la position dans Notepad. Les signets facilitent l’affichage en un clin d’œil des positions différentes dans la trace par rapport à d’autres événements et pour les annoter.

Vous pouvez fournir un nom descriptif pour les marque-pages.

Capture d’écran de la boîte de dialogue Nouveau signet montrant un exemple de nom pour le premier appel d’API dans l’application Afficher le message d’accueil.

Sélectionnez Afficher> lachronologie pour ouvrir la fenêtre Chronologie afin de pouvoir accéder à la chronologie des signets. Lorsque vous pointez sur un signet, le nom du signet s’affiche.

Capture d’écran de la chronologie affichant trois signets avec le curseur pointant sur un pour afficher le nom du signet.

Cliquez avec le bouton droit sur le signet pour atteindre la position du signet, renommez le signet ou supprimez-le.

Capture d’écran du menu contextuel des Signets, accessible par un clic droit, affichant les options pour naviguer vers la position, modifier et supprimer.

Remarque

La fonctionnalité de signet n’est pas disponible dans la version 1.2402.24001.0 de WinDbg.