Partager via


WinDbg - Chronologies

Logo de WinDbg avec une loupe inspectant des octets.

Le débogage TTD (Time Travel Debugging) 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, lectures/écritures en mémoire, appels de fonctions et retours, et exceptions.

Chronologie dans le débogueur affichant les exceptions, les accès mémoire, les points d’arrêt et les appels de fonctions.

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

La fenêtre des chronologies s’affiche lors de l’ouverture d’un fichier de trace TTD et montre les événements clés sans que vous ayez à créer manuellement des requêtes de modèle de données. Dans le même temps, tous les objets de voyage dans le 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 Time Travel, veuillez consulter la rubrique Débogage TTD - Vue d’ensemble.

Types de chronologies

La fenêtre chronologies peut afficher les événements suivants :

  • Exceptions (vous pouvez également filtrer sur un code d’exception spécifique)
  • Points d’arrêt
  • Appels de fonctions (recherche sous la forme module!fonction)
  • Accès Mémoire (lecture/écriture/exécution entre deux adresses mémoire)

Pointez le curseur sur chaque événement pour obtenir plus d’informations via une infobulle. Cliquer sur un événement exécutera la requête pour l’événement et affichera plus d’informations. Double-cliquer sur un événement vous mènera à cet emplacement dans le fichier de trace TTD.

Exceptions

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

Lorsque vous passez le curseur sur un point d’arrêt, des informations telles que le type d’exception et le code d’exception sont affichées.

Chronologie dans le débogueur affichant des exceptions avec des informations sur un code d’exception spécifique.

Vous pouvez également filtrer sur un code d’exception spécifique en utilisant le champ de code d’exception facultatif.

La boîte de dialogue d’exception du débogueur de la chronologie avec le type de chronologie défini sur exception et le 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, vous pouvez afficher les positions où ce point d’arrêt est atteint sur une chronologie. Cela peut être fait, par exemple, en utilisant la commande bp Définir le point d’arrêt. Lorsque vous passez le curseur sur un point d’arrêt, l’adresse et le pointeur d’instruction associés au point d’arrêt sont affichés.

Chronologie dans le débogueur affichant environ 30 indicateurs de points d’arrêt.

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

Appels de fonction

Vous pouvez afficher les positions des appels de fonctions sur la chronologie. Pour ce faire, fournissez la recherche sous la forme de module!function, par exemple TimelineTestCode!multiplyTwo. Vous pouvez également spécifier des jokers, par exemple TimelineTestCode!m*.

Ajout d’une chronologie dans le débogueur avec le nom de l’appel de fonction saisi.

Lorsque vous passez le curseur sur un appel de fonction, le nom de la fonction, les paramètres d’entrée, leurs valeurs et la valeur de retour sont affichés. Cet exemple montre la mémoire tampon et la taille , car il s’agit des paramètres de DisplayGreeting ! GetCppConGreeting.

Chronologie dans le débogueur affichant les appels de fonctions et la fenêtre des registres.

Accès à la mémoire

Utilisez la chronologie d’accès mémoire pour afficher quand une plage spécifique de mémoire a été lue ou écrite, ou où l’exécution du code a eu lieu. Une adresse de début et une adresse de fin sont utilisées pour définir une plage entre deux adresses mémoire.

Ajout d’une chronologie d’accès mémoire avec le bouton d’écriture sélectionné.

Lorsque vous passez le curseur sur un élément d’accès mémoire, la valeur et le pointeur d’instruction sont affichés.

Chronologie dans le débogueur affichant des événements d’accès mémoire.

Travailler avec les chronologies

Une ligne verticale grise suit le curseur lors du survol de la chronologie. La ligne verticale bleue indique la position actuelle dans la trace.

Cliquez sur les icônes loupe pour zoomer sur la chronologie.

Dans la zone de contrôle des chronologies en haut, utilisez le rectangle pour faire défiler la vue de la chronologie. Faites glisser les délimiteurs extérieurs du rectangle pour redimensionner la vue actuelle de la chronologie.

Chronologie dans le débogueur montrant la zone supérieure utilisée pour sélectionner le point de vue actif.

Mouvements de la souris

Zoom avant et arrière en utilisant Ctrl + Molette de la souris.

Faites défiler de gauche à droite en utilisant Shift + Molette de la souris.

Techniques de débogage de la chronologie

Pour illustrer les techniques de débogage de la chronologie, la Procédure pas à pas du Débogage TTD est réutilisée ici. Cette démonstration suppose que vous avez terminé les deux premières étapes pour construire le code d’exemple et créé l’enregistrement TTD en utilisant les deux premières étapes décrites là-bas.

Section 1: Compiler le code d’exemple

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

Dans ce scénario, la première étape consiste à trouver l’exception dans la trace de voyage dans le temps. Cela peut être fait en double-cliquant sur la seule exception qui est sur la chronologie.

En regardant dans la fenêtre de commande, nous voyons la commande suivante qui a été émise lorsque nous avons cliqué sur 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 Affichage>>Registres pour afficher les registres à ce moment dans la chronologie pour commencer notre investigation.

Chronologie dans le débogueur affichant l’exception demolab et la fenêtre des registres.

Dans la sortie de commande, notez que la pile (esp) et le pointeur de base (ebp) pointent vers deux adresses très différentes. 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.

Pendant que nous faisons cela, nous examinerons les valeurs des variables locales et de la pile.

Sélectionnez Affichage>>Variables Locales pour afficher les valeurs locales.

Sélectionnez Affichage>>Pile pour afficher la pile d’exécution du 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.

À 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, de variables locales et de registres.

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: 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, 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=003cf718 ebp=003cf7c8

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 dans notre code source à ce stade de la trace.

Pour enquêter plus avant, nous pouvons ouvrir une fenêtre de mémoire pour afficher le contenu près du pointeur de pile (esp). Dans cet exemple, il a une valeur de 003cf7c8. Sélectionnez Mémoire>>Texte>>ASCII pour afficher le texte ASCII stocké à cette adresse.

Débogueur affichant les fenêtres des registres, de la pile et de la mémoire.

Ligne temporelle d’accès mémoire

Une fois qu’un emplacement mémoire d’intérêt a été identifié, ajoutez une chronologie d’accès mémoire en utilisant cette valeur. Cliquez sur + Ajouter une chronologie (Add Timeline) et renseignez l’adresse de départ. Nous examinerons 4 octets, donc en ajoutant cela à l’adresse de départ de 003cf7c8, nous avons 003cf7cb. Par défaut, on examine toutes les écritures en mémoire, mais vous pouvez également regarder uniquement les écritures ou l’exécution de code à cette adresse.

Ajout d’une boîte de dialogue de chronologie d’accès mémoire avec le bouton d’écriture sélectionné et une valeur de départ de 003cf7c8.

Nous pouvons 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é écrit pour voir ce que nous pouvons trouver. En cliquant sur cette position dans la chronologie, nous voyons que les valeurs locales ont des valeurs différentes pour la chaîne copiée. La valeur de destination semble ne pas être complète, comme si la longueur de notre chaîne n’était pas correcte.

Chronologie d’accès mémoire et fenêtre des variables locales affichant des valeurs de variables locales avec des valeurs de source et de destination différentes.

Chronologie des 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.

Pour explorer une technique de débogage de chronologie alternative, cliquez sur l’exception dans la chronologie et revenez une fois de plus trois étapes en arrière, en utilisant la commande Step Into Back sur le ruban Accueil.

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.

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

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

0:000> ba w4 003cf7c8

Bien que nous utiliserons un simple point d’arrêt d’accès mémoire, les points d’arrêt peuvent être construits pour être des énoncés conditionnels plus complexes. Pour plus d’informations, veuillez consulter la section bp, bu, bm (Définir le point d’arrêt).

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

À ce stade, nous pouvons examiner la pile du programme pour voir quel code est actif.

Chronologie dans le débogueur affichant la chronologie d’accès mémoire et les fenêtres de 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 un programme plus grand et plus complexe, nous allons ajouter une chronologie d’appel de fonction.

Appel de fonction chronologie

Cliquez sur + Ajouter une ligne temporelle et remplissez la DisplayGreeting!GetCppConGreeting pour la chaîne de recherche de fonction.

Les cases à cocher Début et Fin de la localisation indiquent le début et la fin d’un appel de fonction dans la trace.

Nous pouvons utiliser la commande dx pour afficher l’objet d’appel de fonction pour voir les champs TimeStart et TimeEnd associés qui correspondent au Début de localisation et à la Fin de localisation 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  

Soit la case Début, soit la case Fin, ou les deux cases Début et Fin de localisation doivent être cochées.

Ajout de la nouvelle boîte de dialogue de chronologie affichant l’ajout d’une chronologie d’appel de fonction avec une chaîne de recherche de fonction de DisplayGreeting!GetCppConGreeting.

Comme notre code n’est ni récursif ni ré-entrant, il est assez facile de repérer sur la chronologie quand la méthode GetCppConGreeting est appelée. L’appel à GetCppConGreeting se produit également en même temps que notre point d’arrêt ainsi que l’événement d’accès mémoire que nous avons défini. Il semble donc que nous nous sommes rapprochés d’une zone de code à examiner attentivement pour trouver la cause fondamentale de notre plantage d’application.

Chronologie dans le débogueur affichant la chronologie d’accès mémoire et les fenêtres de variables locales avec un message et un tampon contenant des valeurs de chaîne différentes.

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

Bien que notre exemple de code soit petit, la technique d’utilisation de plusieurs chronologies permet une exploration visuelle d’une trace de voyage dans le temps. Vous pouvez regarder à travers le fichier de trace pour poser des questions comme : « quand une zone de mémoire est-elle accédée avant qu’un point d’arrêt ne soit atteint ? ».

Chronologie dans le débogueur affichant la chronologie d’accès mémoire et les fenêtres de variables locales.

La capacité à voir des corrélations supplémentaires et à trouver des éléments auxquels vous ne vous attendiez pas, différencie l’outil de la chronologie de l’interaction avec la trace de voyage dans le temps en utilisant des commandes en ligne de commande.

Marques Pages de la chronologie

Marquer les positions importantes de Voyage dans le Temps dans WinDbg au lieu de copier-coller manuellement la position dans le bloc-notes. Les marque-pages facilitent la visualisation en un coup d’œil des différentes positions dans la trace par rapport à d’autres événements, et permettent de les annoter.

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

Nouvelle boîte de dialogue de marque-pages montrant un exemple de nom pour le premier appel API dans l’application Display Greeting.

Accédez aux marque-pages via la fenêtre des chronologies disponible dans Affichage > Chronologies. Lorsque vous survolez un marque-page, il affichera le nom du marque-page.

Chronologie affichant trois marque-pages avec le curseur survolant l’un d’eux, révélant le nom du marque-page.

Vous pouvez faire un clic droit sur le marque-page pour voyager à cette position, renommer ou supprimer le marque-page.

Menu contextuel clic droit du marque-page affichant des options pour voyager à la position, éditer et supprimer.

Remarque

Dans la version 1.2402.24001.0 du débogueur, la fonction de marque-page n’est pas disponible.

Voir aussi

Fonctionnalités de WinDbg

Procédure pas à pas du Débogage TTD