Time Travel Debugging — Enregistrer et rembobiner les bogues qui ne se reproduisent pas dans les applications de longue durée
· Mis à jour le: · Go Komura · WinDbg, Time Travel Debugging, Débogage, Enquête de bogue, Windows, Développement Windows, .NET, C++, Conseil technique
Historique des révisions (première version, publiée le 2 Sep 2026)
- Première publication
« Ça plante une fois par mois, seulement au milieu de la nuit. » « La même opération ne le reproduit jamais sur ma machine. » « On a un dump, mais regarder le lieu du plantage ne nous dit pas pourquoi la valeur est devenue ce qu’elle est. » Parmi les enquêtes de bogues d’applications Windows de longue durée, ce sont les cas qui consomment le plus de temps. Dans un article précédent, « Lire un dump de plantage avec WinDbg + SOS », nous avons examiné comment lire un dump de plantage, qui est une photographie unique. Cet article en est la suite et couvre l’outil pour les situations où une photographie ne suffit pas : Time Travel Debugging (TTD).
TTD est une fonction de WinDbg qui enregistre l’exécution entière d’un processus et permet de la relire plus tard, en avant et en arrière. Au lieu d’essayer encore et encore de reproduire un bogue, vous pouvez « rembobiner » la session du débogueur.1 Les lecteurs visés sont les développeurs et les mainteneurs d’applications Windows, .NET et C++ qui ont déjà fait leur part d’enquête par dump et journaux et ont encore des cas dont ils n’arrivent pas à atteindre la cause. L’environnement requis est Windows 10/11 ou Windows Server 2016 ou ultérieur avec le WinDbg actuel, et l’enregistrement exige des privilèges d’administrateur.12 Le niveau de difficulté est intermédiaire.
Hypothèses de cet article
| Élément | Contenu |
|---|---|
| Lecteurs visés | Développeurs et mainteneurs d’applications Windows avec des bogues de longue durée ou intermittents dont les dumps et les journaux n’atteignent pas la cause |
| Connaissances préalables | Expérience d’ouvrir un dump dans WinDbg et d’exécuter !analyze -v ou !clrstack. Le contenu de l’article d’analyse SOS est supposé connu |
| Environnement requis | Windows 10/11 ou Windows Server 2016/2019/2022/2025, WinDbg (version actuelle), TTD.exe, privilèges d’administrateur2 |
| Hors périmètre | Intégration avec le Snapshot Debugger de Visual Studio Enterprise ; mode noyau (TTD est réservé au mode utilisateur3) |
1. L’essentiel d’abord
- Un dump conserve l’« état » ; TTD conserve le « chemin ». La documentation officielle indique que les dumps ont tendance à manquer l’état et le chemin d’exécution qui ont mené à l’échec.1 Si une photographie du moment du plantage ne vous dit pas la cause, ce qu’il vous faut ensuite n’est pas davantage de photographies, mais un enregistrement.
- Enregistrer est lourd. Pendant l’enregistrement, le processus cible tourne 5 à 20 fois plus lentement, voire davantage, et le fichier de trace grandit de 5 à 50 Mo par seconde tant que le processus est actif, sans plafond.24 Ce n’est pas un outil que l’on attache inconditionnellement à une application de longue durée.
- Pour les applications de longue durée, concevez « ce qu’il faut enregistrer ». Le chapitre 5 couvre les quatre points d’entrée que TTD.exe fournit :
-ring/-maxFile(ne garder que les N derniers Mo),-module(n’enregistrer que pendant l’exécution de votre propre module),-recordmode Manual(laisser l’application spécifier l’intervalle d’enregistrement) et-monitor(enregistrer chaque lancement).2 - La lecture s’articule autour de trois choses : les positions (
!tt), les événements (dx @$curprocess.TTD.Events) et les requêtes (TTD.Calls/TTD.Memory). Combinezba(un point d’arrêt sur accès) avecg-(exécution inverse), et le débogueur répond directement « qui a écrit cette valeur en dernier ? ».5 - Une trace contient le contenu de la mémoire. Elle peut inclure des informations personnelles et confidentielles telles que des chemins de fichiers, des données du Registre et le contenu de la mémoire et des fichiers.1 Concevez le partage et le stockage comme vous le feriez pour un fichier confidentiel.
- Sachez ce que TTD ne peut pas faire avant de commencer. Il ne peut pas enregistrer le mode noyau, il ne peut pas s’injecter dans les processus protégés (PPL), il ne peut pas se détacher lui-même une fois attaché, et la mémoire ne peut pas être modifiée pendant la lecture.32
Dans le diagramme, un trait continu marque une relation qui vaut toujours et un trait pointillé une relation conditionnelle (les conditions figurent dans l’explication de chaque relation sur la page de détail). La liste complète des relations (32 au total, avec preuve et niveau de certitude) et les définitions des concepts principaux sont rassemblées sur la page de détail de la carte des connaissances (en japonais). Données : JSON-LD / Turtle
2. Là où un dump ne suffit pas
Un dump de plantage est une copie de la mémoire et des registres au moment où le processus a planté (ou a été arrêté). Comme décrit dans l’article d’analyse SOS, !clrstack vous dit où ça a planté et !dumpheap -stat ce qui consomme le tas. Ce qu’il ne peut pas vous dire, c’est ce qui s’est passé sur le chemin vers cet état.
flowchart TB
accTitle: Ce qu'un dump capture et ce que TTD capture
accDescr: Un dump de plantage capture seulement l'état au moment du plantage, pas le chemin qui y a mené. TTD conserve l'exécution complète des instructions du début de l'enregistrement à la fin, de sorte que le chemin est préservé avec l'état
dump["Dump de plantage : état au moment du plantage"] --> q1["Ne montre pas pourquoi la valeur est devenue ce qu'elle est"]
ttd["Trace TTD : exécution des instructions de l'intervalle enregistré"] --> q2["Peut revenir à la position où la valeur a été écrite"]
q1 -.-> gap["C'est cet écart qui étire les enquêtes de longue durée"]
Figure 1 : Un dump est l’état ; TTD est le chemin. Dans les bogues de longue durée, c’est généralement le second que vous voulez.
Les cas typiques ressemblent à ceci.
- Un champ d’une structure sur le tas contient une valeur impossible. Le dump montre la valeur corrompue, mais pas qui l’a écrite ni quand.
- L’emplacement de l’exception est connu, mais pas pourquoi l’argument qui lui a été passé était invalide. En remontant les appelants, la fonction qui a produit la valeur en chemin n’est plus sur la pile.
- Les handles ou la mémoire grandissent au fil d’un mois. Un dump unique ne peut que dire « ça grandit » ; le chemin d’appels qui l’a fait grandir n’est pas là.
flowchart TB
accTitle: Ce qu'un dump manque dans les cas typiques de longue durée
accDescr: La valeur corrompue est capturée mais pas qui l'a écrite, la position de l'exception est capturée mais la fonction qui a produit l'argument n'est plus sur la pile, et la croissance de la ressource est capturée mais pas le chemin qui l'a fait grandir ; les trois types de cas partagent cela
c1["Champ corrompu"] --> m1["Aucun enregistrement de qui l'a écrit ni quand"]
c2["Exception sur un argument invalide"] --> m2["La fonction qui a produit la valeur n'est plus sur la pile"]
c3["Ressource qui grandit sur un mois"] --> m3["Aucun enregistrement du chemin d'appels qui l'a fait grandir"]
m1 --> same["Point commun : l'état existe, mais pas le chemin"]
m2 --> same
m3 --> same
Figure 2 : Les trois cas ont « un état mais pas de chemin ». Davantage de photographies ne comblent pas le chemin.
La documentation de Microsoft compare les forces et les faiblesses des méthodes d’enquête comme suit.1
| Méthode | Forces | Faiblesses |
|---|---|---|
| Débogage en direct | Interactif, montre le flux d’exécution et permet de changer l’état | Arrête le travail de l’utilisateur. Reproduire plusieurs fois demande de l’effort. Souvent inutilisable en production. Difficile de revenir du point d’échec à la cause |
| Dumps | Aucune modification de code au préalable. Faible intrusion, peuvent être collectés sur un déclencheur. Surcoût quasi nul lorsqu’ils ne sont pas utilisés | Même avec des instantanés consécutifs, la vue du « temps écoulé » est grossière |
| Télémétrie et journaux | Légers. Liés aux scénarios métier | Pas de journaux sur les chemins de code inattendus. Profondeur de données insuffisante, et figée statiquement dans le code |
| TTD | Fort sur les bogues complexes. Aucune modification de code au préalable. Peut être relu hors ligne autant de fois que l’on veut et enregistre tout | Surcoût important pendant l’enregistrement. Peut collecter plus de données que nécessaire. Les fichiers deviennent volumineux |
Il y a encore une propriété importante que le tutoriel officiel de TTD souligne. Lorsque le débogueur s’arrête au point d’échec, ce point se trouve souvent dans du code de gestion d’erreur plusieurs étapes après la cause réelle.5 Un dump est toujours pris à cette position « plusieurs étapes plus tard ». Avec TTD, vous pouvez reculer de là, instruction par instruction.
flowchart TB
accTitle: L'écart entre le point d'échec et la cause réelle
accDescr: Le point d'échec où un dump est pris se trouve souvent dans la gestion d'erreur plusieurs étapes après la cause réelle, et avec TTD vous pouvez rembobiner depuis ce point instruction par instruction jusqu'à la cause
cause["Cause réelle (l'instruction qui a corrompu la valeur)"] --> steps["Plusieurs étapes en avant"]
steps --> fail["Point d'échec (exception, gestion d'erreur)"]
fail -->|"Dump"| photo["Figé ici"]
fail -->|"TTD"| back["Revenir avec p- / t- / g-"]
back --> cause
Figure 3 : Un dump est figé au point d’échec. TTD peut remonter du point d’échec à la cause.
3. Comment TTD fonctionne et ce qu’il coûte
3.1 Ce qui est enregistré
TTD injecte un moteur d’enregistrement dans le processus cible et enregistre les instructions exécutées, instruction par instruction. Selon les termes de la documentation officielle, il « encode une trace complète au niveau des instructions à une moyenne de moins d’un octet par instruction » ; en pratique, cela se situe quelque part entre un bit et un octet par instruction. Les programmes qui exécutent peu de sortes de fonctions et traitent peu de données produisent des traces plus petites ; l’inverse en produit de plus grandes.24
Un enregistrement produit deux fichiers.1
| Fichier | Rôle | Taille approximative |
|---|---|---|
.run |
La trace elle-même. Stocke l’exécution des instructions pendant l’enregistrement | Grandit de 5 à 50 Mo par seconde en activité. Ne grandit pas au ralenti4 |
.idx |
L’index. Données auxiliaires qui permettent à WinDbg de relire et d’interroger la mémoire efficacement. Créé à l’arrêt de l’enregistrement, et aussi généré automatiquement lorsque WinDbg ouvre le fichier .run |
1 à 2 fois la taille de la trace4 |
flowchart TB
accTitle: Le flux de l'enregistrement TTD à la lecture
accDescr: Un moteur d'enregistrement est injecté dans le processus cible et l'exécution des instructions est enregistrée dans un fichier .run ; lorsque WinDbg ouvre le fichier .run, il crée un index .idx et relit en avant et en arrière à l'aide des positions, des événements et des requêtes
proc["Processus cible"] --> inj["Moteur d'enregistrement injecté (TTDRecordCPU)"]
inj --> run[".run (enregistrement de l'exécution des instructions)"]
run --> open["Ouvrir dans WinDbg"]
open --> idx["Générer .idx (index)"]
idx --> play["Relire en avant et en arrière avec les positions, les événements et les requêtes"]
Figure 4 : L’enregistrement se fait avec TTD.exe ou WinDbg ; la lecture se fait avec WinDbg. Partager uniquement le fichier .run suffit.
Le temps à l’intérieur d’un fichier .run s’exprime comme une « position ». Elle prend la forme de deux nombres hexadécimaux séparés par deux-points, tels que 12:0 ou 1A0:12F ; la première moitié est le numéro de séquençage (correspondant à un événement de séquençage) et la seconde moitié le nombre approximatif d’instructions depuis cet événement.6 FFFFFFFFFFFFFFFE:0 signifie la fin de la trace.7 Les positions occupent le premier plan au chapitre 6.
flowchart TB
accTitle: Comment les positions dans une trace s'expriment
accDescr: Une position est un numéro de séquençage hexadécimal et un nombre de pas séparés par deux-points ; le début est proche de 0, la fin s'exprime comme FFFFFFFFFFFFFFFE:0, et vous pouvez aussi vous déplacer vers une position approximative par pourcentage
pos["Position xx:yy (hexadécimal)"] --> seq["xx : numéro de séquençage"]
pos --> step["yy : instructions depuis cet événement"]
seq --> tail["La fin est FFFFFFFFFFFFFFFE:0"]
step --> pct["Les pourcentages tels que !tt 50 fonctionnent aussi"]
Figure 5 : Une position est « numéro d’événement:nombre d’instructions ». Ce n’est pas l’heure murale, mais elle peut être convertie en heure murale comme le montre le chapitre 6.
3.2 Le coût
La documentation officielle elle-même décrit TTD comme une « technologie invasive ».2 Voici le coût en chiffres.
| Élément | Contenu |
|---|---|
| Vitesse | Le processus cible tourne 5 à 20 fois plus lentement, voire davantage, pendant l’enregistrement (selon l’application et les options d’enregistrement). Cela peut passer inaperçu dans l’interface, mais c’est perceptible dans les opérations lourdes telles qu’une boîte de dialogue Ouvrir un fichier23 |
| Croissance du fichier | 5 à 50 Mo par seconde en activité. Quelques minutes d’enregistrement peuvent atteindre plusieurs Go. Aucun plafond n’est fixé4 |
| Disque saturé | Si le disque se remplit pendant l’enregistrement, TTD écrit la dernière page puis attend effectivement de pouvoir écrire à nouveau. WinDbg continue d’afficher la boîte de dialogue d’enregistrement et n’émet ni erreur ni avertissement. Le résultat est une trace incomplète4 |
| Mémoire | L’enregistrement ajoute le surcoût des processeurs virtuels à la mémoire du processus cible (par défaut 55 sur x64/ARM64, 32 sur x86). Réduisez-le avec -numVCpu uniquement lorsque la mémoire est insuffisante2 |
| Impossible de se détacher | Une fois attaché, TTD ne peut pas se détacher lui-même. Lorsque vous terminez l’enregistrement, fermez l’application ou terminez le processus. Si le processus est essentiel au système, le système d’exploitation doit être redémarré2 |
flowchart TB
accTitle: Le coût de l'enregistrement et son effet sur les applications de longue durée
accDescr: Le ralentissement pendant l'enregistrement, la croissance du fichier, l'attente silencieuse lorsque le disque est saturé et l'impossibilité de se détacher après l'attachement sont les quatre coûts qui rendent impossible d'attacher TTD inconditionnellement à une application de longue durée
cost["Enregistrement TTD"] --> slow["5 à 20 fois plus lent"]
cost --> grow["Grandit de 5 à 50 Mo par seconde, pas de plafond"]
cost --> disk["Attend silencieusement lorsque le disque est saturé"]
cost --> stuck["Ne peut pas se détacher après l'attachement"]
slow --> no["L'enregistrement continu inconditionnel n'est pas viable"]
grow --> no
disk --> no
stuck --> no
Figure 6 : N’importe lequel des quatre coûts écarte l’enregistrement continu. C’est pourquoi la « conception d’enregistrement » du chapitre 5 est nécessaire.
3.3 Ce qu’il ne peut pas faire
- Mode utilisateur uniquement. Il ne peut enregistrer que l’exécution en mode utilisateur d’un processus ; le code qui s’exécute en mode noyau, tel que les pilotes, ne peut pas être débogué.3
- Processus protégés. TTD ne peut pas s’injecter dans les processus protégés de Windows tels que Protected Process Light (PPL).3
- La lecture est en lecture seule. Vous pouvez revenir dans le temps, mais vous ne pouvez pas changer l’histoire. Les commandes qui lisent la mémoire fonctionnent ; celles qui la modifient, non.3
- Incompatibilité avec les logiciels antivirus et de surveillance de la mémoire. Parce que TTD s’accroche au processus, il entre en conflit avec les logiciels qui suivent ou mettent en miroir les appels mémoire du système. Si l’enregistrement produit une erreur qui ressemble à des autorisations insuffisantes, désactivez temporairement ces logiciels pour isoler le problème. Le framework Electron est également un conflit connu ; même si un enregistrement réussit, le processus cible peut se bloquer (interblocage) ou planter.3
- Les applications UWP ne peuvent pas être lancées et enregistrées (l’attachement à une application UWP déjà en cours d’exécution est possible). Les « processus inhabituels » qui s’exécutent dans une autre session ou un autre contexte de sécurité ne sont actuellement pas pris en charge non plus.8
flowchart TB
accTitle: Ce que TTD ne peut pas enregistrer ni faire
accDescr: Le code en mode noyau, les processus protégés, l'enregistrement au lancement des applications UWP et les processus dans une autre session ou un autre contexte de sécurité ne peuvent pas être enregistrés ; la mémoire ne peut pas être modifiée pendant la lecture ; et les logiciels antivirus et Electron peuvent entrer en conflit
no["Limitations de TTD"] --> g1["Ne peut pas être enregistré"]
no --> g2["Limitations et conflits"]
g1 --> k["Code en mode noyau (pilotes, etc.)"]
k --> ppl["Processus protégés (PPL)"]
ppl --> uwp["Enregistrement au lancement d'UWP (l'attachement est possible)"]
uwp --> sess["Autres sessions et contextes de sécurité"]
g2 --> ro["La lecture est en lecture seule"]
ro --> av["Peut entrer en conflit avec l'antivirus et Electron"]
Figure 7 : Les limitations viennent en deux sortes : « ne peut pas enregistrer » et « entre en conflit ». Cette dernière doit être isolée par environnement.
Le dernier point fait trébucher quiconque veut enregistrer un service. La documentation de TTD.exe décrit -attach comme destiné à « l’investigation de services et d’applications de longue durée » et -monitor comme enregistrant « chaque fois qu’un programme ou un service démarre »,2 ce qui ne s’accorde pas simplement avec l’affirmation de la page de dépannage. En pratique, l’approche sûre est de vérifier par environnement dans cet ordre : confirmer que ping.exe ou cmd.exe peuvent être enregistrés dans la même configuration que la production, puis essayer le processus cible.8
4. Enregistrer — L’interface WinDbg et TTD.exe
Il y a deux points d’entrée pour l’enregistrement : l’interface WinDbg, ou TTD.exe en ligne de commande.
4.1 Enregistrer depuis l’interface WinDbg
Exécutez WinDbg en tant qu’administrateur (l’élévation est obligatoire pour TTD1), choisissez File > Start debugging > Launch executable (advanced), spécifiez l’exécutable et cochez Record with Time Travel Debugging. Choisir Configure and Record permet de définir l’emplacement du fichier de trace et Record subset of execution (limiter les modules enregistrés avec une liste séparée par des virgules telle que notepad.exe,kernelbase.dll). Pour un processus déjà en cours d’exécution, choisissez File > Start debugging > Attach to process et cochez de même Record Process with Time Travel Debugging.9
Pendant l’enregistrement, une petite boîte de dialogue avec les boutons « Stop and Debug » et « Cancel » apparaît. Lorsque l’application se termine (ou plante), la trace est fermée et WinDbg l’ouvre automatiquement et construit l’index.5
flowchart TB
accTitle: Le flux de l'enregistrement depuis l'interface WinDbg
accDescr: Dans WinDbg démarré en tant qu'administrateur, choisir Launch executable (advanced) ou Attach to process, cocher Record with Time Travel Debugging, définir l'emplacement de sauvegarde et le filtre de module dans Configure and Record, passer par la boîte de dialogue d'enregistrement, et lorsque l'application se termine la trace est fermée et indexée automatiquement
adm["Démarrer WinDbg en tant qu'administrateur"] --> pick["Launch executable (advanced) / Attach to process"]
pick --> chk["Cocher Record with Time Travel Debugging"]
chk --> cfg["Configure and Record : emplacement de sauvegarde, filtre de module"]
cfg --> rec["Boîte de dialogue d'enregistrement (Stop and Debug)"]
rec --> fin["L'application se termine, la trace est fermée et indexée automatiquement"]
Figure 8 : L’enregistrement depuis l’interface prend cinq étapes. Sautez « démarrer en tant qu’administrateur » et vous vous arrêtez à la première boîte de dialogue.
4.2 Enregistrer avec TTD.exe
Lorsque vous devez enregistrer sur un PC où WinDbg ne peut pas être installé, ou pour automatiser l’enregistrement, utilisez TTD.exe seul. Il s’installe via App Installer depuis https://aka.ms/ttd/download, et après l’installation vous pouvez le vérifier avec ttd.exe -help. Pour les environnements hors ligne, Microsoft fournit aussi une procédure officielle (avec un script PowerShell) pour décompresser le bundle MSIX à la main et extraire uniquement les binaires.2 L’enregistrement exige des privilèges d’administrateur et s’exécute normalement depuis une invite de commandes administrateur.2
Il y a trois modes d’enregistrement.2
flowchart TB
accTitle: Les trois modes d'enregistrement de TTD.exe
accDescr: launch démarre un nouveau processus avec des arguments et l'enregistre, mais il s'exécute avec des privilèges élevés. attach s'attache à un processus en cours par PID. monitor enregistre chaque fois que le programme spécifié démarre, et le lancement se fait avec des privilèges normaux
m["Modes d'enregistrement de TTD.exe"] --> l["-launch : lancer et enregistrer"]
m --> a["-attach : s'attacher à un PID en cours"]
m --> mo["-monitor : enregistrer chaque lancement"]
l -.-> lp["Lancé avec des privilèges d'administrateur"]
a -.-> ap["Conserve les privilèges normaux"]
mo -.-> mp["Chemin de lancement normal, adapté à l'automatisation"]
Figure 9 : Seul -launch peut passer des arguments, mais il s’exécute avec des privilèges élevés. Pour enregistrer un comportement proche de la production, utilisez -attach ou -monitor.
:: Lancer et enregistrer (mode par défaut ; -launch peut être omis)
TTD.exe -out C:\traces MyApp.exe --config prod.json
:: S'attacher à un processus en cours (créer d'abord le répertoire de sortie)
TTD.exe -attach 21440 -out C:\traces\MyApp.run
:: Enregistrer chaque lancement (Ctrl+C termine la surveillance ; -out exige un chemin complet)
TTD.exe -out C:\traces\ -monitor MyApp.exe
-launchest le seul mode qui peut passer des arguments, mais le programme est lancé avec les mêmes privilèges (administrateur) que TTD.exe. Pour les applications dont le comportement change avec les privilèges, enregistrez avec-attachou-monitorpour conserver les privilèges normaux.2-attachexige que le répertoire de sortie existe déjà. Si vous spécifiez un nom de fichier, aucun fichier de ce nom ne doit exister.2-monitorinstalle un pilote de surveillance des lancements de processus et enregistre chaque fois que le programme spécifié (plusieurs peuvent être indiqués) démarre. Il reste en vigueur jusqu’au redémarrage et s’arrête avec Ctrl+C. Ses avantages sont que vous n’avez pas à assembler le lancement vous-même, que la cible s’exécute avec des privilèges normaux, et qu’il convient à l’automatisation par script. Ajouter-cmdLineFilter "string"n’enregistre que les lancements dont la ligne de commande contient cette chaîne.2-childrenenregistre aussi les processus enfants, mais chaque processus reçoit son propre fichier.run, et WinDbg ne peut en ouvrir qu’un à la fois.2
Pendant l’enregistrement, une petite interface à deux boutons apparaît : « Tracing Off » (arrêter l’enregistrement et laisser l’application continuer) et « Exit App » (fermer l’application et terminer l’enregistrement). Pour l’automatisation, masquez-la avec -noUI et acceptez le CLUF avec -accepteula.2 Le journal d’enregistrement est conservé dans un fichier .out au même emplacement que le fichier .run, où vous pouvez lire les heures murales de début et de fin de l’enregistrement, la durée de la session d’enregistrement (temps de simulation), s’il s’agissait d’un lancement ou d’un attachement, et la version du système d’exploitation. Lorsqu’un enregistrement échoue, certains messages d’erreur n’apparaissent que dans le fichier .out.2
5. Conception d’enregistrement pour les applications de longue durée
C’est le cœur de l’article. Compte tenu des coûts du chapitre 3, attraper un bogue qui survient à un moment inconnu dans un processus qui tourne pendant des jours exige une conception d’enregistrement. TTD.exe fournit quatre moyens, et vous choisissez selon la nature du symptôme.
flowchart TB
accTitle: Choix de la portée d'enregistrement selon le symptôme d'une application de longue durée
accDescr: Si vous ne savez pas quand cela se produira, ne gardez que la dernière partie avec un tampon circulaire ; si vous savez quel module est suspect, n'enregistrez que ce module ; si vous pouvez modifier l'application, spécifiez l'intervalle avec l'API d'enregistrement manuel ; s'il n'apparaît qu'au démarrage ou à certains lancements, enregistrez chaque lancement avec le mode surveillance
q{"Nature du symptôme ?"} -->|"Moment d'apparition inconnu"| ring["-ring / -maxFile : ne garder que la fin"]
q -->|"Seulement au démarrage ou à certains lancements"| mon["-monitor : enregistrer chaque lancement"]
ring -->|"Le module suspect est clair"| mod["Ajouter -module"]
ring -->|"L'application peut être modifiée"| man["Spécifier l'intervalle avec -recordmode Manual"]
Figure 10 : Les quatre ne s’excluent pas. Combiner -ring avec -module est la combinaison la plus pratique pour les applications de longue durée.
5.1 Tampon circulaire — Ne garder que les N derniers Mo
Avec -ring, la trace est écrite dans un tampon circulaire de la taille donnée par -maxFile, et le fichier ne grandit jamais au-delà de cette limite. Ce qui reste n’est que la dernière partie de l’enregistrement qui tient dans cette taille.2 L’unité de -maxFile est le Mo ; en mode tampon circulaire, la valeur par défaut est 2 048 Mo, le minimum 1 Mo et le maximum 32 768 Mo (la valeur par défaut pour l’anneau en mémoire d’un processus 32 bits est 256 Mo).2
:: S'attacher à une application de surveillance en cours et ne garder que les 4 Go d'exécution les plus récents
TTD.exe -accepteula -noUI -attach 21440 -ring -maxFile 4096 -out C:\traces\MyApp.run
:: Lorsque le symptôme est détecté, arrêter l'enregistrement (l'application continue)
TTD.exe -stop 21440
-stop accepte un nom de processus, un PID ou all, et arrête cet enregistrement. -wait <seconds> attend que chaque session d’enregistrement sur le système soit terminée (-1 pour indéfiniment), et s’utilise dans les scripts d’automatisation pour imposer l’ordre « arrêter, puis collecter le fichier ».2
flowchart TB
accTitle: Chronologie d'un enregistrement en tampon circulaire
accDescr: L'enregistrement commence à l'attachement, les parties plus anciennes sont poussées hors du tampon circulaire, et lorsque le symptôme apparaît et que stop est émis, seule la portion maxFile la plus récente reste comme trace
s["Démarrer l'enregistrement avec -attach -ring"] --> old["Les intervalles plus anciens sont poussés dehors"]
old --> sym["Le symptôme apparaît"]
sym --> stop["Arrêter l'enregistrement avec -stop"]
stop --> keep["Seule la portion maxFile la plus récente reste"]
keep -.-> note["Dimensionner pour que les instants avant le symptôme tiennent dans le tampon"]
Figure 11 : Le tampon circulaire est un dispositif pour garder « les instants avant le symptôme ». Calculez -maxFile à rebours à partir de « le temps entre le constat du symptôme et l’arrêt, multiplié par la croissance par seconde ».
Il y a deux points de conception.
- Rendez le tampon assez grand pour absorber le temps entre le constat et l’arrêt. Dans un processus actif, la trace grandit de 5 à 50 Mo par seconde,4 donc un anneau de 4 Go correspond à une à deux minutes sous charge lourde et un peu plus de dix minutes sous charge légère. Il vous faut un mécanisme qui maintienne le décalage entre la détection du symptôme (une ligne de journal précise, un seuil de compteur, une alerte de la surveillance) et
-stopdans cette fenêtre. - Arrêter ne détache pas.
-stoparrête l’enregistrement, mais TTD ne se détache pas lui-même du processus cible.2 Terminer complètement l’enregistrement exige de terminer le processus, donc incluez « redémarrer à la prochaine fenêtre de maintenance après la collecte de la trace » dans la procédure d’exploitation.
sequenceDiagram
accTitle: Arrêt d'un enregistrement en tampon circulaire en coordination avec la surveillance
accDescr: Lorsque le côté surveillance détecte le symptôme par les journaux ou les compteurs, il appelle TTD.exe stop, collecte le fichier .run finalisé et redémarre le processus à la prochaine fenêtre de maintenance pour retirer TTD
participant W as Surveillance (journaux, compteurs)
participant T as TTD.exe
participant P as Processus cible
W->>W: Détecter le symptôme
W->>T: -stop PID
T->>P: Arrêter l'enregistrement (le processus continue)
T-->>W: .run est finalisé
W->>W: Collecter .run, chiffrer et stocker
W->>P: Redémarrer à la prochaine fenêtre de maintenance
Figure 12 : Ce n’est que lorsque le décalage entre la détection et -stop tient dans le tampon que les instants avant le symptôme restent dans la trace. Le redémarrage fait partie de l’exploitation.
5.2 Filtre de module — N’enregistrer que pendant l’exécution de votre propre code
-module <module name> n’enregistre que le module spécifié (l’exécutable lui-même ou une DLL chargée ; plusieurs peuvent être indiqués) et le code que ce module appelle. Le processus cible tourne à pleine vitesse jusqu’à ce que du code du module spécifié s’exécute ; l’enregistrement commence lorsque l’exécution entre dans le module, s’arrête lorsqu’elle le quitte, et le processus revient à pleine vitesse. Parce que l’activation et la désactivation de l’enregistrement sont coûteuses, l’enregistrement reste actif tant que le module spécifié appelle d’autres modules du processus.2
:: N'enregistrer que pendant l'exécution de notre DLL de logique de mesure (combiné avec un anneau)
TTD.exe -accepteula -noUI -attach 21440 -module MeasureCore.dll -ring -maxFile 2048 -out C:\traces\
Une trace ainsi constituée saute simplement les intervalles où l’enregistrement était arrêté, traite « l’instruction suivante » comme la première instruction après la reprise de l’enregistrement, et vous la déboguez sans différence avec une trace du processus entier.2 Dans les applications de longue durée, la boucle idle de l’interface et le traitement interne du framework tendent à représenter la plus grande partie des instructions exécutées, donc restreindre l’enregistrement à votre propre module seul réduit considérablement le surcoût et la taille du fichier.
flowchart TB
accTitle: Comment se comporte l'enregistrement filtré par module
accDescr: Le processus cible tourne à pleine vitesse hors du module spécifié ; l'enregistrement commence lorsque le code du module spécifié est entré, continue tant que ce module appelle d'autres modules, et s'arrête lorsque l'exécution quitte le module
out1["Hors du module spécifié : pleine vitesse"] --> in1["Entrée dans le module spécifié : l'enregistrement commence"]
in1 --> callee["Autres modules qu'il appelle : l'enregistrement continue"]
callee --> out2["Sortie du module spécifié : l'enregistrement s'arrête"]
out2 --> out1
Figure 13 : Parce que les API Win32 et l’environnement d’exécution que votre DLL appelle sont enregistrés aussi, cela suffit pour retracer « ce que notre code a remis au système d’exploitation ».
5.3 Enregistrement manuel — Laisser l’application spécifier l’intervalle
Avec -recordmode Manual, le processus continue de tourner à pleine vitesse même après l’injection de TTD, et l’enregistrement n’a lieu que lorsque le programme appelle l’API d’enregistrement in-process de TTD (la valeur par défaut, Automatic, enregistre dès le moment de l’injection).2 La documentation de l’API et les exemples se trouvent dans le dépôt WinDbg-Samples sur GitHub.10
Si vous pouvez modifier l’application, c’est l’approche la moins gaspilleuse. Vous pouvez intégrer une logique qui démarre l’enregistrement lorsque l’application elle-même sait qu’une anomalie est imminente et s’arrête lorsque les choses reviennent à la normale : « trois tentatives de communication consécutives ont échoué », « l’arriéré de la file a dépassé le seuil », et ainsi de suite. Dans une configuration de processus superviseur comme celle couverte dans l’article sur les Job Objects, le superviseur peut décider de l’intervalle à la place.
5.4 Mode surveillance — Enregistrer chaque lancement
Pour les bogues qui n’apparaissent que juste après le démarrage ou à certains lancements, -monitor convient. Le tableau de comparaison officiel décrit aussi le mode surveillance comme destiné à « attraper les problèmes intermittents et les problèmes de démarrage ».2 Lorsque vous enregistrez le même programme de nombreuses fois, les noms de fichiers séquentiels par défaut (MyApp01.run, MyApp02.run, etc.) deviennent inefficaces parce que les fichiers existants doivent être parcourus, donc utilisez -timestampFilename pour des noms horodatés. Le nombre d’enregistrements simultanés peut être plafonné avec -maxConcurrentRecordings.2
flowchart TB
accTitle: Comment se comporte le mode surveillance
accDescr: L'option monitor installe un pilote de surveillance des lancements de processus, resserre la cible avec le filtre de ligne de commande chaque fois que le programme spécifié démarre, l'enregistre, crée un fichier de trace distinct par lancement, et continue jusqu'à Ctrl+C ou un redémarrage
drv["Installer le pilote de surveillance des lancements"] --> launch["Détecter un lancement du programme spécifié"]
launch --> filt{"Correspond à -cmdLineFilter ?"}
filt -->|"Oui"| rec["Enregistrer ce lancement (fichier distinct par lancement)"]
filt -->|"Non"| skip["Ne pas enregistrer"]
rec --> next["Attendre le prochain lancement (jusqu'à Ctrl+C ou redémarrage)"]
skip --> next
Figure 14 : Le mode surveillance « guette le lancement ». Il convient aux bogues qui apparaissent à chaque lancement et aux bogues que l’on peut resserrer par les conditions de lancement.
5.5 Où placer le disque
Pour les enregistrements de longue durée, placez les traces sur un volume dédié et incluez la croissance du fichier .run dans votre surveillance. Comme indiqué à la section 3.2, lorsque le disque se remplit, l’enregistrement attend simplement en silence, sans erreur. La solution de contournement officielle est tout aussi primitive : « vérifier l’espace libre dans l’Explorateur » et « vérifier que le fichier .run grandit régulièrement ».4 Sans surveillance de l’espace libre, vous vous retrouvez avec une trace incomplète dans laquelle l’instant que vous vouliez le plus n’a jamais été écrit.
flowchart TB
accTitle: Comment la saturation du disque produit une trace incomplète
accDescr: Lorsque le disque se sature pendant l'enregistrement, TTD écrit la dernière page et attend silencieusement sans erreur ni avertissement, de sorte que le symptôme qui survient ensuite n'est pas enregistré, laissant une trace incomplète qui s'ouvre mais à laquelle manque la partie cruciale. Prévenez-le avec un volume dédié et une surveillance de la croissance de .run
full["Le disque se sature"] --> wait["Écrit la dernière page et attend silencieusement"]
wait --> none["Pas d'erreur, pas d'avertissement"]
none --> sym["Le symptôme survient ensuite"]
sym --> inc["Trace incomplète sans le symptôme"]
guard["Volume dédié + surveillance de la croissance de .run"] -.->|"prévient"| full
Figure 15 : Une trace qui « s’ouvre mais à laquelle manque la partie cruciale » naît d’un trou dans la surveillance du disque.
6. Relire — Positions, événements et rembobinage
6.1 Ouvrir
Lorsque vous ouvrez un fichier .run dans WinDbg, s’il n’y a pas d’index, !index s’exécute automatiquement et construit le fichier .idx tout en comptant les images clés (positions dans la trace générées automatiquement pour l’indexation ; les traces plus grandes en ont davantage). Plus la trace est grande, plus cela prend de temps.5 L’état de l’index peut être vérifié avec !index -status ; s’il signale autre chose que « Index file loaded », reconstruisez-le avec !index -force. Si cela échoue encore, fermez le débogueur, supprimez le fichier .idx et rouvrez le fichier .run. Reconstruire l’index ne modifie pas le fichier .run, donc aucune donnée n’est perdue.8
Une mise en garde : lorsque l’indexation des traces volumineuses a été améliorée dans TTD 1.11.611, le format d’index a changé, et les traces existantes doivent être réindexées.11 Si vous promenez de vieux fichiers .idx, c’est ici que vous trébuchez.
flowchart TB
accTitle: Vérification et reconstruction de l'index
accDescr: Après l'ouverture de la trace, vérifier l'état avec !index -status ; s'il est autre chose que Index file loaded, reconstruire avec !index -force, et si cela échoue encore, fermer le débogueur, supprimer le fichier .idx et rouvrir le fichier .run. La reconstruction ne modifie pas le fichier .run
open["Ouvrir la trace"] --> st["!index -status"]
st -->|"Index file loaded"| ok["Passer à l'analyse"]
st -->|"Tout autre chose"| force["Reconstruire avec !index -force"]
force -->|"Échoue"| del["Fermer, supprimer .idx, rouvrir .run"]
del --> ok
force -->|"Réussit"| ok
Figure 16 : Reconstruire l’index ne touche pas le fichier .run. En cas de doute, supprimez-le et rouvrez.
6.2 Se déplacer par position
Passer une position à !tt se déplace à ce point dans le temps.6
!tt 0 ; début de la trace
!tt 50 ; approximativement la position 50 %
!tt 100 ; fin de la trace
!tt 1A0:12F ; vers la position 1A0:12F
Une position est une paire de nombres hexadécimaux, numéro de séquençage:nombre de pas.6 L’objet Position a les propriétés Percent (la fraction de la trace), Sequence et Steps, plus SeekTo(), qui se déplace à cette position, et ToSystemTime(), qui renvoie l’heure murale approximative (UTC).7 Dans les enquêtes de longue durée, ToSystemTime() fait la différence, parce qu’il permet d’apparier les horodatages du journal de l’application avec les positions de la trace. Les résultats de TTD.Calls portent aussi SystemTimeStart / SystemTimeEnd.12
flowchart TB
accTitle: Apparier les positions aux horodatages des journaux
accDescr: En partant d'un horodatage dans le journal de l'application, suivre l'heure murale approximative de l'objet Position pour identifier la position dans la trace, s'y déplacer avec SeekTo, et lire l'exécution autour
log["Journal de l'application : heure de l'anomalie"] --> match["Trouver une position dont ToSystemTime est proche"]
match --> seek["S'y déplacer avec SeekTo"]
seek --> read["Lire les appels et les valeurs environnants"]
Figure 17 : « Que faisait-il juste avant cette ligne de journal ? » se consulte par la correspondance entre positions et heure murale.
Les positions sont aussi utiles pour le partage. Lorsque vous remettez une trace à un collègue, joignez la position !tt x:y et il peut commencer à regarder au même instant. Écrire des plages de positions dans les rapports de bogue est aussi une pratique officiellement recommandée.2
6.3 Rembobiner
Ajouter - aux commandes de pas habituelles se déplace en arrière dans le temps.13
| Commande | Signification | Bouton du ruban |
|---|---|---|
p- |
Reculer d’une instruction (ou d’une ligne source). Un appel de fonction compte pour un pas | Step Over Back |
t- |
Reculer d’une instruction (ou d’une ligne source). Entre dans les appels de fonction | Step Into Back |
g- |
Exécuter en inverse. S’arrête lorsqu’un point d’arrêt est atteint, sur un événement, ou au début de la trace | Go Back |
Les événements qui arrêtent g- sont les mêmes que ceux qui arrêtent un g en avant.13 Autrement dit, posez un ba (point d’arrêt sur accès) ou un bp et exécutez g-, et vous revenez droit à « la dernière position où cette condition valait ». C’est le mouvement de base du chapitre 7.
flowchart TB
accTitle: Choix parmi les commandes inverses
accDescr: p- recule d'un pas en passant par-dessus les appels de fonction, t- recule d'une instruction dans les fonctions, et g- revient droit à un point d'arrêt, un événement ou le début de la trace. Ce qui arrête un g en avant arrête aussi g-
cur["Position actuelle"] -->|"p-"| over["Reculer d'un pas en passant par-dessus les appels"]
cur -->|"t-"| into["Reculer d'une instruction dans les fonctions"]
cur -->|"g-"| run["Revenir droit à la prochaine condition d'arrêt"]
run -.-> stop["ba / bp / événement / début de la trace"]
Figure 18 : Pour regarder soigneusement à proximité, utilisez t- ; pour sauter vers une cause lointaine, utilisez ba + g-.
6.4 Entrer par les événements
Si vous ne savez pas par où commencer la lecture, commencez par la liste des événements. @$curprocess.TTD.Events liste la création et la terminaison de threads, le chargement et le déchargement de modules, et les exceptions comme événements.14
dx -g @$curprocess.TTD.Events
dx @$curprocess.TTD.Events.Where(t => t.Type == "Exception").Select(e => e.Exception)
Un événement d’exception contient la position, le type (Software / Hardware), le code d’exception et le compteur de programme au moment, et cliquer sur le lien [Time Travel] de la sortie se déplace à cette position.15 Le tutoriel officiel suit ce flux : il saute à la position d’une violation d’accès (0xc0000005), soupçonne une corruption de pile parce que le pointeur de pile et le pointeur de base ne s’accordent pas, et recule de trois instructions avec t- pour vérifier les valeurs.5 La fenêtre Timelines de WinDbg visualise les exceptions, les points d’arrêt, les accès mémoire et les appels de fonction sous forme de chronologie, et un double-clic sur une exception émet le même SeekTo().16
6.5 Threads et positions
!positions montre chaque thread actif à la position actuelle avec la position de chaque thread dans la trace.17 Il y a un piège ici. Changer de thread avec ~<number>s ne déplace pas la position dans la trace. La position que le débogueur utilise pour lire la mémoire ne change pas, donc pour regarder la mémoire d’un autre thread « à cet instant », déplacez-vous avec le lien de position dans la sortie de !positions ou avec !tt x:y.13
flowchart TB
accTitle: Le chemin de base d'une lecture
accDescr: Ouvrir la trace et construire l'index, se déplacer de la liste des événements à la position de l'exception, reculer pas à pas jusqu'à la cause, et si besoin se déplacer à la position d'un autre thread avec positions
open["Ouvrir la trace et l'indexer"] --> ev["Trouver l'exception dans TTD.Events"]
ev --> seek["Se déplacer à la position avec [Time Travel]"]
seek --> back["Reculer avec t- / p- / g-"]
back --> th["Vérifier les positions des autres threads avec !positions"]
th -.-> caution["~s ne déplace pas la position de la trace"]
Figure 19 : « Entrer par un événement, marcher en arrière » est le chemin de base d’une lecture. Changer de thread n’est pas se déplacer en position.
7. Trouver « quand » avec des requêtes — TTD.Calls et TTD.Memory
La vraie force de la lecture est que vous pouvez interroger toute la trace. Les objets de TTD sont exposés via le modèle de données du débogueur (la commande dx), et vous pouvez les filtrer, les trier et les agréger à la manière LINQ.18
7.1 TTD.Calls — Chercher des appels de fonction
@$cursession.TTD.Calls("module!symbol") collecte les appels de la fonction spécifiée dans toute la trace. Les jokers sont autorisés, et chaque appel porte ses positions de début et de fin (TimeStart / TimeEnd), l’identifiant de thread (avec un UniqueThreadId qui n’est jamais réutilisé), les arguments (Parameters[]), la valeur de retour (ReturnValue) et l’adresse de retour (ReturnAddress).12
L’exemple de la documentation officielle est GetLastError. Agrégez les appels dont la valeur de retour est non nulle par code d’erreur, et vous obtenez une liste de quelles erreurs se sont produites combien de fois pendant la trace.18
dx -g @$cursession.TTD.Calls("kernelbase!GetLastError").Where(x => x.ReturnValue != 0).GroupBy(x => x.ReturnValue).Select(x => new { ErrorNumber = x.First().ReturnValue, ErrorCount = x.Count() }).OrderByDescending(p => p.ErrorCount),d
Pour « d’où MessageBox a-t-il été appelé en dernier ? », prenez le dernier appel avec OrderBy(c => c.TimeStart).Last() et déplacez-vous via le lien [Time Travel] de son TimeStart.18
flowchart TB
accTitle: Construction d'une requête TTD.Calls
accDescr: Collecter les appels par nom de fonction, filtrer par valeur de retour ou arguments, agréger par code d'erreur et analogues, trier par temps, et se déplacer à la position de l'appel voulu via le lien Time Travel
calls["TTD.Calls (nom de fonction, jokers)"] --> where["Where : filtrer par valeur de retour ou arguments"]
where --> group["GroupBy : agréger par code d'erreur, etc."]
group --> order["OrderBy : trier par temps"]
order --> jump["Se déplacer via [Time Travel] sur TimeStart"]
Figure 20 : La façon d’utiliser les requêtes est de partir non de « où » mais de « quand, combien de fois, et avec quels arguments ».
Un mot sur les symboles. TTD détermine le nombre et les types des arguments d’une fonction, son type de retour et sa convention d’appel à partir des informations de symboles PDB. Avec des private symbols, vous obtenez le nom de la fonction et les arguments corrects. Avec des public symbols seulement, vous obtenez le nom de la fonction et des arguments par défaut (quatre entiers non signés 64 bits). Un module sans aucun symbole reçoit le nom de fonction UnknownOrMissingSymbols.1218 Pour les sortes de PDB et comment les conserver, voir « Qu’est-ce qu’un PDB ? ».
flowchart TB
accTitle: Disponibilité des symboles et résultats de TTD.Calls
accDescr: Avec des private symbols vous obtenez le nom de la fonction et les arguments corrects ; avec des public symbols seulement le nom de la fonction et les quatre arguments entiers 64 bits par défaut ; sans symboles le nom de fonction devient UnknownOrMissingSymbols
sym{"Symboles pour le module ?"} -->|"private symbols"| full["Nom de fonction + arguments et valeur de retour corrects"]
sym -->|"public symbols"| pub["Nom de fonction + arguments par défaut (4 x entiers 64 bits)"]
sym -->|"aucun"| unk["UnknownOrMissingSymbols"]
Figure 21 : Avoir conservé les PDB de vos propres modules détermine directement l’utilité des requêtes.
Calls implique du calcul, donc plus la trace est grande, plus cela prend de temps et plus l’usage du processeur est élevé. Les résultats sont mis en cache en mémoire, donc la deuxième requête et les suivantes pour la même fonction sont plus rapides.12 Lorsqu’une requête ne renvoie rien, il y a quatre causes : la façon dont l’appel est écrit (vérifiez le nom du module avec la commande x ; s’il revient en majuscules, utilisez cela), la DLL cible n’est pas encore chargée à cette position (déplacez-vous vers une position après le chargement et relancez), la fonction est développée en ligne (ne peut pas être suivie), ou le joker est trop large (resserrez-le).18
7.2 TTD.Memory — Chercher des accès mémoire
@$cursession.TTD.Memory(start address, end address, "access type") collecte les accès à la plage mémoire spécifiée dans toute la trace. Les types sont r (lecture), w (écriture), rw, e (exécution), rwe et ec (exécution/changement).19 « Qui a écrit cette variable en dernier ? » se répond en collectant avec "w", en prenant .Last(), et en se déplaçant à cette position.5
dx -g @$cursession.TTD.Memory(0x00a4fca0, 0x00a4fca4, "w")
dx @$cursession.TTD.Memory(0x00a4fca0, 0x00a4fca4, "w").Last().TimeStart.SeekTo()
Si vous n’avez besoin de chercher qu’en avant ou en arrière depuis la position actuelle, @$curprocess.TTD.PrevMemoryAccess("w", address, size) / NextMemoryAccess sont plus légers et acceptent plusieurs plages à la fois. La position où un registre a changé se trouve avec @$curthread.TTD.PrevRegisterWrite("rcx").6
7.3 ba + g- — Faire répondre le débogueur « qui l’a corrompu ? »
Condenser la procédure montrée dans le tutoriel officiel en une forme utilisable pour les enquêtes de longue durée donne ce qui suit.5
- Se déplacer à la position de l’exception avec
TTD.Events - Reculer avec
t-et formuler l’hypothèse de quelle variable contient la valeur corrompue - Obtenir l’adresse de cette variable avec
dx &variable - Poser un point d’arrêt en écriture avec
ba w4 <address> - Exécuter
g-pour revenir droit à la position où cette variable a été écrite en dernier - Vérifier si ce point (ou quelques instructions plus tôt) est la cause. Si la valeur écrite venait d’une autre variable, poser
basur cette variable et exécuterg-à nouveau - Répéter jusqu’à atteindre l’instruction qui l’a corrompue
flowchart TB
accTitle: Retracer l'origine d'une valeur avec ba et g-
accDescr: Poser un point d'arrêt en écriture sur l'adresse de la valeur corrompue et exécuter en inverse, s'arrêter à l'instruction qui l'a écrite en dernier, et si cette valeur venait d'une autre variable répéter les mêmes étapes jusqu'à atteindre l'instruction qui l'a corrompue
bad["Identifier l'adresse de la valeur corrompue"] --> ba["Poser un point d'arrêt en écriture avec ba w"]
ba --> gb["Exécuter en inverse avec g-"]
gb --> writer["S'arrêter à l'instruction qui l'a écrite en dernier"]
writer --> q{"La valeur venait-elle d'une autre variable ?"}
q -->|"Oui"| bad
q -->|"Non"| found["L'instruction corruptrice = la cause"]
Figure 22 : Une enquête qui s’arrête à « c’est corrompu » avec un dump avance mécaniquement jusqu’à « qui l’a corrompu » avec TTD.
TTD 1.11.553 et ultérieur ajoutent aussi @$curframe.TTD.VariableHistory(), qui renvoie l’historique des valeurs des variables locales d’une frame. Il montre un tableau des noms de variables et des valeurs que chaque variable a tenues sur quelles plages de positions.11 Dans des situations comme la corruption de pile où vous voulez savoir « depuis quand la valeur est fausse », il est utile pour resserrer où poser ba en premier.
8. Appliquer cela aux bogues de longue durée
Nous appliquons maintenant ces outils aux trois types de cas listés au début.
flowchart TB
accTitle: Types de bogues de longue durée et le point d'entrée TTD pour chacun
accDescr: Pour les exceptions intermittentes et la corruption de données, revenir de l'événement avec ba et g- ; pour la croissance des ressources, apparier les appels d'acquisition et de libération avec TTD.Calls ; pour Ne répond pas et les chaînes d'attentes, suivre les positions et les heures murales des appels d'API d'attente
t1["Exceptions intermittentes, corruption de données"] --> a1["TTD.Events, puis ba + g-"]
t2["Croissance des handles et de la mémoire"] --> a2["Apparier acquisition et libération avec TTD.Calls"]
t3["Ne répond pas, chaînes d'attentes"] --> a3["!positions et heures murales des API d'attente"]
a2 -.-> pre["Enregistrer avec -module limité à votre propre DLL"]
Figure 23 : Le point d’entrée diffère selon le type. Ce qu’ils partagent, c’est de resserrer la portée d’enregistrement avant de commencer à lire.
Type 1 : Exceptions intermittentes et corruption de données. Gardez les instants avant le symptôme avec le tampon circulaire de la section 5.1, sautez à la position de l’exception depuis la liste d’événements de la section 6.4, et retracez l’origine de la valeur avec le ba + g- de la section 7.3. La différence avec une enquête par dump est que l’enquête ne s’arrête pas lorsque vous trouvez « la variable corrompue » ; de là vous continuez mécaniquement en arrière.
Type 2 : Croissance des handles et de la mémoire. C’est le type de cas disséqué dans « Enquête sur les plantages après un fonctionnement de longue durée d’une caméra industrielle - La fuite de handles (partie 1) ». Vous ne pouvez pas enregistrer un mois, donc restreignez l’enregistrement à votre propre DLL avec le -module de la section 5.2, combinez-le avec -ring, et gardez « les quelques minutes pendant lesquelles ça grandit ». Sur la trace, collectez TTD.Calls("kernelbase!CreateFileW") et TTD.Calls("kernelbase!CloseHandle") et appariez la valeur de retour de CreateFileW (la valeur de handle) avec le premier argument de CloseHandle (Parameters[0]). Le ReturnValue de CloseHandle est un drapeau booléen de succès, donc inutile pour l’appariement. Si une valeur de handle reste non fermée, le ReturnAddress (l’appelant) de cet appel CreateFileW vous donne l’appelant qui fuit.12 Cela dit, observer si une fuite existe et de quelle taille elle est est moins coûteux avec Application Verifier ou les compteurs de handles ; la bonne répartition du travail est de faire intervenir TTD au stade où l’on cloue quel chemin fuit. Notez qu’enregistrer avec Application Verifier activé dégrade nettement les performances de lecture à cause de l’usage de la mémoire, donc désactivez-le pendant l’enregistrement.8
Type 3 : « Ne répond pas » et chaînes d’attentes. Comme écrit dans « Ce qu’est vraiment « Ne répond pas » », un blocage est une question de qui attend qui. Une trace ne grandit pas au ralenti,4 donc le temps d’attente d’un thread qui attend dans le noyau n’apparaît pas comme des instructions. Ce qui apparaît, c’est l’appel juste avant d’entrer dans l’attente et les instructions après son retour. Regardez la position de chaque thread avec !positions,17 et alignez « quel thread a commencé à attendre quoi, et quand » à partir du SystemTimeStart / SystemTimeEnd de TTD.Calls("kernelbase!WaitForSingleObject") et analogues ; apparié aux horodatages des journaux, cela permet de reconstruire la chaîne d’attentes.12 Les « bogues dépendants de l’ordre » comme DllMain et le verrou du chargeur ou les réveils parasites des variables de condition sont un domaine où TTD, qui enregistre l’ordre lui-même, convient bien.
9. TTD avec les applications .NET
La documentation officielle de TTD indique que le code managé peut être débogué avec TTD dans WinDbg à l’aide de l’extension SOS (sos.dll) s’exécutant en mode 64 bits.1 Le chargement est le même qu’au chapitre 3 de l’article d’analyse SOS (.loadby sos coreclr ou chargement automatique), et !clrstack et !pe fonctionnent à chaque position de la trace. Le chemin de base est de se déplacer à la position de l’exception avec TTD.Events puis de lire la pile managée avec !clrstack.
flowchart TB
accTitle: Mise en place pour lire une trace TTD d'une application .NET
accDescr: Ouvrir la trace TTD dans WinDbg, charger l'extension SOS 64 bits, se déplacer à la position de l'événement d'exception, et lire l'état managé avec !clrstack et !pe. Utiliser TTD.Calls pour les appels aux frontières natives
run["Trace TTD (.run)"] --> wd["WinDbg"]
wd --> sos["Extension SOS (64 bits)"]
sos --> ev["Se déplacer à la position de l'exception avec TTD.Events"]
ev --> clr["Lire avec !clrstack / !pe"]
wd -.-> calls["TTD.Calls pour les appels aux frontières natives"]
Figure 24 : État managé via SOS, recherches d’appels aux frontières natives. Séparez les rôles et vous ne vous perdrez pas.
Deux mises en garde. Premièrement, ce que Microsoft garantit est « SOS en mode 64 bits ». Rien n’est dit des applications .NET compilées pour x86, donc exécutez la cible d’enquête en x64 si vous le pouvez. Deuxièmement, TTD.Calls s’appuie sur les informations de symboles PDB.12 Ne comptez pas chercher par nom des méthodes managées compilées JIT ; l’usage fiable est de suivre les appels aux frontières natives telles que les cibles P/Invoke dans des DLL natives, COM et les API Win32. Les problèmes de tas managé comme ceux de « Distinguer l’attente du GC d’une fuite mémoire en .NET » se poursuivent d’abord avec dotnet-counters / dotnet-gcdump / !gcroot sur un dump, et TTD intervient au stade où une frontière native est impliquée.
10. Choisir parmi dumps, journaux, ETW et TTD
TTD n’est pas une panacée, et il ne remplace pas les outils existants. Voici sa place face aux outils couverts dans nos articles.
| Ce que vous voulez savoir | Outil à utiliser en premier | Quand TTD intervient |
|---|---|---|
| L’état au moment du plantage | Dump de plantage (WER LocalDumps / ProcDump) | Lorsque le dump montre « c’est corrompu » mais pas qui l’a corrompu |
| Quel fichier ou quelle clé de Registre a échoué | Process Monitor | Lorsque vous avez aussi besoin de comment les arguments passés à l’API en échec ont été construits |
| Performances de tout le PC sur une longue durée | WPR/WPA, PerfView (ETW) | Lorsque c’est un problème de correction plutôt que de performance et que vous avez besoin de l’ordre au niveau des instructions |
| La séquence d’événements au niveau métier | Les journaux de l’application | Lorsque cela s’est produit sur un chemin de code sans journalisation (TTD enregistre tout sans modification de code préalable1) |
| Qui a écrit cette valeur, l’ordre des appels | TTD | — |
flowchart TB
accTitle: Choix d'une méthode d'enquête
accDescr: D'abord saisir l'état avec des dumps et des journaux légers, enquêter sur les API en échec avec ProcMon et les performances avec ETW, et ne passer à TTD que lorsque vous avez encore besoin de qui a passé quoi et quand
start["Symptôme"] --> light["Saisir l'état avec dumps et journaux (léger)"]
light --> api["API en échec : ProcMon"]
light --> perf["Performances : WPR/WPA, PerfView"]
light --> need{"Besoin de qui, quand, et ce qui a été passé ?"}
need -->|"Oui"| ttd["Concevoir la portée d'enregistrement et enregistrer avec TTD"]
need -->|"Non"| done["Réglé avec des outils légers"]
Figure 25 : Saisissez d’abord le « résultat » avec des outils légers, et sortez TTD seulement pour les cas où le « chemin » s’avère nécessaire.
L’ordre est « le plus léger d’abord ». Les dumps et les journaux ne coûtent presque rien à collecter, donc gardez-les en place en permanence ; sortez TTD, après la conception du chapitre 5, pour les cas où la lecture du dump a montré que le chemin est nécessaire. Compte tenu du surcoût de TTD et du fait que les traces contiennent des informations confidentielles, il n’y a aucune raison d’inverser cet ordre.
11. Notes d’exploitation
- Traitez les traces comme des fichiers confidentiels. Un enregistrement contient le contenu de la mémoire et peut inclure des informations personnelles et liées à la sécurité telles que des chemins de fichiers, des données du Registre et le contenu de la mémoire et des fichiers.12 Lorsque vous enregistrez dans un environnement client, comprenez à l’avance ce qui peut être inclus (chaînes de connexion, jetons, données clients) et décidez d’un canal de transfert chiffré, d’un emplacement de stockage et d’une durée de conservation.
- Ne partagez que le fichier
.run. Le fichier.idxest à peu près aussi grand que le fichier.runet est généré automatiquement lorsque WinDbg l’ouvre. Les fichiers.runse compressent bien. Lorsque vous signalez un bogue dans TTD lui-même, joignez aussi le fichier.out.2 - Gardez les versions alignées. TTD continue d’être mis à jour avec WinDbg ; 1.11.611 inclut une correction des plantages d’enregistrement dans les programmes qui utilisent AVX/AVX512 et un changement de format d’index.11 Mélanger une ancienne version du côté enregistrement ou du côté lecture signifie réindexer ou réenregistrer.
- Vérifiez
-replayCpuSupportlorsque vous relisez sur un processeur différent. La valeur par défaut favorise la portabilité, etMostConservativeest fourni pour les cas où le processeur d’enregistrement et le processeur de lecture diffèrent (par exemple relire une trace Intel sur arm64). Inversement, si vous savez que le processeur de lecture est égal ou meilleur, vous pouvez choisir un enregistrement plus petit et plus rapide.2 - Windows Server peut aussi être enregistré. TTD.exe prend en charge Windows Server 2016/2019/2022/2025.2
- Isoler un échec d’enregistrement. Essayez d’abord si
ping.exeoucmd.exepeuvent être enregistrés ; sinon, soupçonnez un conflit avec un logiciel invasif tel qu’un antivirus ou une virtualisation d’applications.8
flowchart TB
accTitle: Procédure de traitement des traces
accDescr: Parce qu'une trace enregistrée contient le contenu de la mémoire, comprendre quelles informations elle peut contenir, compresser uniquement le fichier .run et le remettre par un canal chiffré, décider de l'emplacement de stockage et de la durée de conservation, et du côté analyse l'ouvrir avec la même version de WinDbg et construire l'index
rec["Enregistrement terminé (.run / .idx / .out)"] --> know["Comprendre quelles informations elle peut contenir"]
know --> share["Compresser uniquement .run, chiffrer et remettre"]
share --> keep["Décider de l'emplacement de stockage et de la durée de conservation"]
keep --> open["Ouvrir avec la même version de WinDbg et générer .idx"]
Figure 26 : Remettre une trace, c’est remettre un fichier confidentiel. Décidez de la procédure avant d’enregistrer.
12. Résumé
- Un dump est l’« état » ; TTD est le « chemin ». Dans les cas qui ont besoin de l’origine d’une valeur corrompue ou de l’ordre des appels, TTD transforme l’enquête en travail mécanique
- L’enregistrement est 5 à 20 fois plus lent, grandit de 5 à 50 Mo par seconde, et ne peut pas se détacher. Pour les applications de longue durée, concevez d’abord la portée d’enregistrement avec
-ring/-maxFile,-module,-recordmode Manualet-monitor - La lecture est « entrer par un événement, marcher en arrière » :
TTD.Events, puis[Time Travel], puist-/g-, etba+g-pour faire répondre le débogueur « qui l’a corrompu » TTD.CallsetTTD.Memorysont des requêtes sur toute la trace. Appariez aux horodatages des journaux avecToSystemTime()etSystemTimeStart- Pour .NET, lisez l’état avec SOS 64 bits et utilisez
TTD.Callsaux frontières natives - Une trace est un fichier confidentiel. Ne partagez que le fichier
.run, et décidez d’abord du canal et du stockage
Les cas où un dump a été collecté mais la cause est hors de portée, ou où le travail est bloqué parce que le bogue ne se reproduit pas, se règlent assez souvent avec TTD une fois que la portée d’enregistrement peut être conçue. Nous pouvons aussi prendre en charge tout, de la conception d’enregistrement à l’analyse du fichier .run ; n’hésitez pas à nous contacter avec vos dumps et vos journaux.
Articles associés
- Lire un dump de plantage avec WinDbg + SOS — Guide pratique d’analyse après la collecte
- Introduction à la collecte des dumps de crash Windows - WER/ProcDump/WinDbg
- Qu’est-ce qu’un PDB (Program Database) ? — Comprendre les informations de débogage, les symboles et Source Link
- Enquête sur les plantages après un fonctionnement de longue durée d’une caméra industrielle - La fuite de handles (partie 1)
- Ce qu’est vraiment « Ne répond pas » — comment Windows décide qu’une application est bloquée, et comment concevoir des applications qui ne le sont pas
- Guide pratique de Process Monitor (ProcMon) — identifier en 10 minutes un « paramètre non pris en compte » ou un ACCESS DENIED
- WPR/WPA en pratique — Introduction à l’investigation des performances à l’échelle du système pour « tout le PC est lent »
- Identifier précisément les lenteurs avec PerfView et dotnet-trace — Introduction pratique à l’analyse des performances .NET
- Ce qui reste après la mort du parent — Garder les processus enfants en laisse avec les Job Objects
Domaines de conseil associés
KomuraSoft LLC prend en charge l’enquête de cause racine des bogues d’applications Windows qui n’apparaissent qu’après une longue durée de fonctionnement ou de façon intermittente, en combinant dumps de plantage, journaux et traces TTD ; la mise en place d’un dispositif d’enquête incluant la conception de la portée d’enregistrement ; et l’isolement des pannes qui impliquent des frontières natives (COM, P/Invoke, SDK de périphériques). Contactez-nous dès le stade « nous avons un dump mais n’arrivons pas à la cause ».
- Analyse de dysfonctionnements et des causes racines
- Conseil technique et revue de conception
- Développement d’applications Windows
- Nous contacter
Références
-
Microsoft Learn, Time Travel Debugging - Overview. Sur le fait que TTD enregistre l’exécution d’un processus et la relit en avant et en arrière, que les dumps ont tendance à manquer l’état et le chemin d’exécution qui ont mené à l’échec, que l’enregistrement exige des privilèges d’administrateur, que les enregistrements peuvent contenir des informations personnelles et liées à la sécurité, le tableau comparatif des méthodes d’enquête, les rôles de
.run/.idx, et le débogage du code managé avec l’extension SOS en mode 64 bits. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 -
Microsoft Learn, Time Travel Debugging - TTD.exe command line utility. Sur le ralentissement de 5 à 20 fois ou davantage, l’impossibilité de se détacher après l’attachement, la prise en charge de Windows Server 2016 à 2025, l’installation et le déploiement hors ligne, les trois modes
-launch/-attach/-monitor, les options-out/-noUI/-accepteula/-stop/-wait/-tracingOff/-children/-cmdLineFilter/-timestampFilename/-ring/-maxFile/-maxConcurrentRecordings/-numVCpu/-replayCpuSupport/-module/-recordmode, la lecture du fichier.out, et les conseils de partage des traces. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15 ↩16 ↩17 ↩18 ↩19 ↩20 ↩21 ↩22 ↩23 ↩24 ↩25 ↩26 ↩27 ↩28 ↩29 ↩30 ↩31 ↩32 ↩33 ↩34 -
Microsoft Learn, Time Travel Debugging - Overview - Things to look out for. Sur l’incompatibilité avec les logiciels antivirus et de surveillance de la mémoire et Electron, le mode utilisateur uniquement, le fait que la lecture est en lecture seule, l’impossibilité de s’injecter dans les processus protégés (PPL), et l’impact de performance d’environ 10 à 20 fois pendant l’enregistrement. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7
-
Microsoft Learn, Time Travel Debugging - Working with Trace Files. Sur les facteurs de taille de trace (un bit à un octet par instruction), la croissance de 5 à 50 Mo par seconde en activité et nulle au ralenti, l’absence de plafond de taille maximale, le fait que l’index fait 1 à 2 fois la trace, et le comportement de l’enregistrement et de l’indexation lorsque le disque se sature, ainsi que la solution de contournement. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9
-
Microsoft Learn, Time Travel Debugging - Sample App Walkthrough. Sur la procédure générale de se déplacer à la position d’un événement d’exception et de revenir avec
baetg-à la position où une valeur invalide a été écrite en dernier, le fait que le point d’échec se trouve souvent dans la gestion d’erreur plusieurs étapes après la cause réelle, le fait que la trace est fermée lors d’un plantage et que WinDbg l’indexe automatiquement, et l’usage deTTD.Memoryet.Last(). ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 -
Microsoft Learn, !tt (time travel). Sur la spécification des positions pour
!tt(pourcentage ouxx:yy), la signification des deux composantes d’une position (numéro de séquençage et nombre de pas), etTTD.PrevRegisterWrite/PrevMemoryAccess/NextMemoryAccess. ↩ ↩2 ↩3 ↩4 -
Microsoft Learn, TTD Position Objects. Sur
Percent/Sequence/Stepsde l’objet Position,SeekTo(), le fait queToSystemTime()renvoie l’heure murale approximative (UTC), et le fait queFFFFFFFFFFFFFFFE:0désigne la fin de la trace. ↩ ↩2 -
Microsoft Learn, Time Travel Debugging - Troubleshooting. Sur le fait que l’élévation est requise, que l’enregistrement au lancement des applications UWP n’est pas pris en charge, que les « processus inhabituels » dans une autre session ou un autre contexte de sécurité sont hors périmètre, l’isolement avec
ping.exe/cmd.exe, le ralentissement de la lecture lorsque Application Verifier est utilisé en même temps, et la reconstruction de l’index avec!index -status/!index -force. ↩ ↩2 ↩3 ↩4 ↩5 -
Microsoft Learn, Time Travel Debugging - Record a trace. Sur l’enregistrement depuis Launch executable (advanced) / Attach to process dans l’interface WinDbg, la case Record with Time Travel Debugging, le réglage de l’emplacement de sauvegarde avec Configure and Record, et la limitation des modules avec Record subset of execution. ↩
-
Microsoft, WinDbg-Samples - TTD in-process recording API (GitHub). Sur la documentation de l’API d’enregistrement in-process qui, combinée à
-recordmode Manual, laisse le programme contrôler le début et l’arrêt de l’enregistrement. ↩ -
Microsoft Learn, Time travel debugging release notes. Sur la correction d’enregistrement pour les programmes qui utilisent AVX/AVX512 et le changement de format d’index (réindexation requise) dans 1.11.611, et
@$curframe.TTD.VariableHistory()ajouté dans 1.11.553. ↩ ↩2 ↩3 -
Microsoft Learn, TTD Calls Objects. Sur les arguments de
TTD.Calls, les propriétésThreadId/UniqueThreadId/Function/ReturnValue/ReturnAddress/Parameters[]/TimeStart/TimeEnd/SystemTimeStart/SystemTimeEnd, les valeurs par défaut lorsque les informations de symboles PDB manquent (quatre arguments entiers non signés 64 bits,UnknownOrMissingSymbols), et le fait que le calcul prend du temps et que les résultats sont mis en cache. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 -
Microsoft Learn, Time Travel Debugging - Replay a trace. Sur l’exécution inverse avec
p-/t-/g-, le fait queg-s’arrête sur les mêmes événements que l’exécution en avant,!positions, et le fait que~sne change pas la position dans la trace. ↩ ↩2 ↩3 -
Microsoft Learn, TTD Event Objects. Sur les types d’événements (ThreadCreated/ThreadTerminated/ModuleLoaded/ModuleUnloaded/Exception) et les objets enfants Position, Module, Thread et Exception. ↩
-
Microsoft Learn, TTD Exception Objects. Sur
Type(Software/Hardware),ProgramCounter,Code,FlagsetPositionde l’objet exception. ↩ -
Microsoft Learn, WinDbg: Timelines. Sur le fait que la fenêtre Timelines visualise les exceptions, les points d’arrêt, les accès mémoire et les appels de fonction, et qu’un double-clic sur une exception émet
Position.SeekTo(). ↩ -
Microsoft Learn, !positions. Sur l’affichage de chaque thread actif et de la position de chaque thread dans la trace. ↩ ↩2
-
Microsoft Learn, Introduction to Time Travel Debugging objects. Sur les objets
@$curprocess.TTD/@$cursession.TTD, les requêtes avecOrderBy/Where/Select/GroupBy, les exemples d’agrégation des erreursGetLastErroret de recherche du dernier appel àMessageBoxW, la signification deUnknownOrMissingSymbols, et les quatre raisons pour lesquellesCallsne renvoie rien. ↩ ↩2 ↩3 ↩4 ↩5 -
Microsoft Learn, TTD Memory Objects. Sur les types d’accès de
TTD.Memory(r/w/rw/e/rwe/ec) et le déplacement vers une position via le lien[Time Travel]dans les résultats. ↩
Articles associés
Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.
Lire un dump de plantage avec WinDbg + SOS — Guide pratique d'analyse après la collecte
Explique comment lire concrètement un dump de plantage Windows collecté, à l'aide de WinDbg et de l'extension SOS. Couvre la configuratio...
Pourquoi les arguments se cassent — Les règles des arguments de ligne de commande Windows
Windows passe à CreateProcess une seule chaîne que le destinataire découpe. Traite les règles de CommandLineToArgvW, du CRT et de .NET, A...
Fin de la maintenance des pilotes d'imprimante Windows — Comment les applications métier doivent préparer l'impression des rapports et des étiquettes
Microsoft met progressivement fin aux pilotes d'imprimante v3/v4. Ce que Windows protected print mode retire, et comment inventorier et p...
Identifier précisément les lenteurs avec PerfView et dotnet-trace — Introduction pratique à l'analyse des performances .NET
Lorsqu'une application métier est « lente », « sature le processeur » ou « se fige parfois », quel outil utiliser et que faut-il observer...
Empêcher les lancements multiples d'une application Windows — Mutex nommé et activation de la fenêtre existante lors d'un second lancement
Cet article détaille comment implémenter la prévention des lancements multiples d'une application Windows métier à l'aide d'un Mutex nomm...
Sujets associés
Ces pages replacent le sujet dans un contexte plus large de services et de décisions.
Thèmes techniques Windows
Portail des sujets sur le développement Windows, l'analyse des incidents et la valorisation des actifs existants.
Analyse de bugs et incidents de longue durée
Pannes intermittentes, diagnostic des communications, crashs après longue exécution et tests des chemins d'échec.
Services liés à ce sujet
Cet article est directement lié aux services suivants.
Développement d'applications Windows
Applications métier, intégration d'équipements et outils de communication, des besoins au développement.
Questions fréquentes
Questions souvent posées lors d’une consultation sur le sujet de cet article.
- Quelle est la différence entre Time Travel Debugging (TTD) et un dump de plantage ?
- Un dump de plantage est une photographie de la mémoire au moment du plantage : il montre l'état à cet instant, mais pas le chemin qui y a mené. Une trace TTD est un enregistrement complet de l'exécution des instructions du processus, que l'on peut ensuite relire en avant et en arrière, de sorte que vous pouvez rembobiner et vérifier directement qui a écrit en dernier dans une variable ou ce qui a été appelé juste avant une exception. La documentation officielle de Microsoft indique aussi que les dumps ont tendance à manquer l'état et le chemin d'exécution qui ont mené à l'échec. Le prix est que le processus tourne 5 à 20 fois plus lentement pendant l'enregistrement, et que le fichier de trace grandit d'environ 5 à 50 Mo par seconde tant que le processus est actif.
- Puis-je laisser TTD attaché à une application de production qui tourne pendant des jours ?
- Pas tel quel. TTD ralentit considérablement le processus pendant l'enregistrement, la trace grandit de 5 à 50 Mo par seconde dans un processus actif, et il n'y a pas de plafond sur la taille du fichier. L'utiliser sur une application de longue durée suppose une conception d'enregistrement : le tampon circulaire de TTD.exe (-ring) avec -maxFile pour ne garder que les N derniers Mo, -module pour n'enregistrer que pendant l'exécution de votre propre module, ou -recordmode Manual pour laisser l'application choisir l'intervalle d'enregistrement. De plus, une fois attaché, TTD ne peut pas se détacher lui-même, il faut donc décider à l'avance comment vous terminerez l'enregistrement, ce qui signifie terminer (redémarrer) le processus.
- Peut-on enregistrer des services et des processus dans d'autres sessions ?
- La documentation de TTD.exe décrit -attach comme destiné à l'investigation de services et d'applications de longue durée, et -monitor comme enregistrant chaque fois qu'un programme ou un service démarre. D'un autre côté, la page de dépannage indique que les processus inhabituels qui s'exécutent dans une autre session ou un autre contexte de sécurité ne sont actuellement pas pris en charge pour l'enregistrement. Que cela fonctionne réellement dépend de l'environnement, donc enregistrez d'abord un processus simple tel que ping.exe ou cmd.exe dans la même configuration que la production, essayez ensuite le processus cible, et seulement alors intégrez-le à l'exploitation.
- Peut-on utiliser TTD sur des traces d'applications .NET ?
- Oui. La documentation officielle indique que l'extension SOS (sos.dll) s'exécutant en mode 64 bits peut être utilisée sur une trace TTD dans WinDbg pour déboguer du code managé. Le schéma de base consiste à exécuter des commandes SOS telles que !clrstack et !pe à chaque position de la trace, à se déplacer jusqu'à la position d'un événement d'exception, puis à lire la pile managée. La requête TTD.Calls, qui cherche des appels par nom de symbole, s'appuie sur les informations de symboles PDB, donc dans les applications .NET l'usage fiable est de suivre les appels aux frontières natives telles que P/Invoke, COM et les API Win32.
- Est-il prudent d'envoyer un fichier de trace (.run) à une autre entreprise ou hors de l'organisation ?
- Pas tel quel. Un enregistrement TTD contient le contenu de la mémoire du processus, et la documentation officielle indique explicitement qu'il peut inclure des informations personnelles ou confidentielles telles que des chemins de fichiers, des données du Registre et le contenu de la mémoire et des fichiers. Si vous en envoyez un, comprenez d'abord ce qui a été enregistré (chaînes de connexion, jetons, données clients, etc.), puis décidez d'un canal de transfert chiffré et d'un emplacement de stockage. Partager uniquement le fichier .run suffit ; le fichier d'index (.idx) est généré automatiquement lorsque WinDbg l'ouvre.
- TTD.Calls ne renvoie rien lorsque je cherche une fonction. Pourquoi ?
- Il y a quatre causes principales. Premièrement, les symboles : les fonctions d'un module sans PDB s'appellent UnknownOrMissingSymbols, et le nom du module peut être en majuscules, donc vérifiez le nom de symbole réel avec la commande x. Deuxièmement, la DLL cible peut ne pas encore être chargée à cette position ; déplacez-vous vers une position après le chargement de la DLL et interrogez à nouveau. Troisièmement, si la fonction est développée en ligne, le moteur de requête ne peut pas la suivre. Quatrièmement, le joker peut être trop large et correspondre à trop de fonctions ; resserrez le motif.
Profil de l’auteur
Page de présentation de l’auteur de l’article.
Go Komura
Représentant de KomuraSoft LLC
Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.