Time Travel Debugging — Langlaufende Fehler, die sich nicht reproduzieren, aufzeichnen und zurückspulen

· Aktualisiert am: · · WinDbg, Time Travel Debugging, Debugging, Fehleruntersuchung, Windows, Windows-Entwicklung, .NET, C++, Technische Beratung

Änderungsverlauf (Erstfassung, veröffentlicht am 2. Sep 2026)
Erstveröffentlichung

„Es stürzt einmal im Monat ab, nur mitten in der Nacht.“ „Derselbe Vorgang reproduziert es auf meinem Rechner nie.“ „Wir haben einen Dump, aber der Absturzort sagt uns nicht, warum der Wert so geworden ist.“ Unter den Fehleruntersuchungen langlaufender Windows-Apps sind das die Fälle, die am meisten Zeit fressen. In einem früheren Artikel, „Absturz-Dumps mit WinDbg + SOS lesen“, haben wir betrachtet, wie man einen Absturz-Dump liest, der eine einzelne Fotografie ist. Dieser Artikel setzt dort an und behandelt das Werkzeug für die Situationen, in denen eine Fotografie nicht reicht: Time Travel Debugging (TTD).

TTD ist eine WinDbg-Funktion, die die gesamte Ausführung eines Prozesses aufzeichnet und sie später vorwärts und rückwärts wiedergeben lässt. Statt immer wieder zu versuchen, einen Fehler zu reproduzieren, können Sie die Debuggersitzung „zurückspulen“.1 Die vorgesehenen Leser sind Entwickler und Betreuer von Windows-, .NET- und C++-Anwendungen, die Dump- und Log-Untersuchungen bereits hinter sich haben und trotzdem Fälle haben, deren Ursache sie nicht erreichen. Die Voraussetzungsumgebung ist Windows 10/11 oder Windows Server 2016 oder höher mit dem aktuellen WinDbg, und die Aufzeichnung erfordert Administratorrechte.12 Der Schwierigkeitsgrad ist Mittelstufe.

Voraussetzungen dieses Artikels

Punkt Inhalt
Vorgesehene Leser Entwickler und Betreuer von Windows-Apps mit langlaufenden oder intermittierenden Fehlern, deren Ursache Dumps und Logs nicht erreichen
Vorausgesetztes Wissen Erfahrung, einen Dump in WinDbg zu öffnen und !analyze -v oder !clrstack auszuführen. Der Inhalt des SOS-Analyseartikels wird vorausgesetzt
Voraussetzungsumgebung Windows 10/11 oder Windows Server 2016/2019/2022/2025, WinDbg (aktuelle Version), TTD.exe, Administratorrechte2
Nicht behandelt Integration mit dem Visual Studio Enterprise Snapshot Debugger; Kernelmodus (TTD ist nur Benutzermodus3)

1. Zuerst die Kurzfassung

  • Ein Dump bewahrt den „Zustand“; TTD bewahrt den „Pfad“. Die offizielle Dokumentation stellt fest, dass Dumps den Zustand und den Ausführungspfad, die zum Fehler geführt haben, oft verfehlen.1 Wenn eine Fotografie des Absturzmoments die Ursache nicht sagt, brauchen Sie als Nächstes nicht mehr Fotografien, sondern eine Aufzeichnung.
  • Aufzeichnen ist schwer. Während der Aufzeichnung läuft der Zielprozess 5- bis 20-mal langsamer oder schlimmer, und die Trace-Datei wächst bei aktivem Prozess um 5 bis 50 MB pro Sekunde, ohne Obergrenze.24 Es ist kein Werkzeug, das Sie bedingungslos an eine langlaufende App hängen.
  • Für langlaufende Apps entwerfen Sie, „was aufgezeichnet wird“. Kapitel 5 behandelt die vier Einstiege, die TTD.exe bereitstellt: -ring/-maxFile (nur die letzten N MB behalten), -module (nur aufzeichnen, während das eigene Modul ausgeführt wird), -recordmode Manual (die App legt das Aufzeichnungsintervall fest) und -monitor (jeden Start aufzeichnen).2
  • Die Wiedergabe dreht sich um drei Dinge: Positionen (!tt), Ereignisse (dx @$curprocess.TTD.Events) und Abfragen (TTD.Calls / TTD.Memory). Kombinieren Sie ba (einen Break-on-Access-Haltepunkt) mit g- (Rückwärtsausführung), und der Debugger beantwortet direkt „wer hat diesen Wert zuletzt geschrieben?“.5
  • Eine Trace enthält den Inhalt des Speichers. Sie kann personenbezogene und vertrauliche Informationen enthalten, etwa Dateipfade, Registrierungsdaten und den Inhalt von Speicher und Dateien.1 Entwerfen Sie Weitergabe und Speicherung so, wie Sie es für eine vertrauliche Datei tun würden.
  • Wissen Sie vorher, was TTD nicht kann. Es kann den Kernelmodus nicht aufzeichnen, es kann sich nicht in geschützte Prozesse (PPL) injizieren, es kann sich nach dem Anhängen nicht selbst lösen, und der Speicher lässt sich während der Wiedergabe nicht ändern.32

In der Abbildung kennzeichnet eine durchgezogene Linie eine stets geltende Beziehung und eine gestrichelte Linie eine bedingte (die Bedingungen stehen bei jeder Beziehung auf der Detailseite). Die vollständige Liste der Beziehungen (32 insgesamt, mit Beleg und Sicherheitsgrad) und die Definitionen der wichtigsten Konzepte sind auf der Detailseite der Wissenskarte (auf Japanisch) zusammengestellt. Daten: JSON-LD / Turtle

2. Wo ein Dump nicht ausreicht

Ein Absturz-Dump ist eine Kopie von Speicher und Registern im Moment, in dem der Prozess abgestürzt ist (oder angehalten wurde). Wie im SOS-Analyseartikel beschrieben, sagt !clrstack, wo es abgestürzt ist, und !dumpheap -stat, was den Heap verbraucht. Was er nicht sagen kann, ist was auf dem Weg zu diesem Zustand geschehen ist.

Was ein Dump erfasst und was TTD erfasstEin Absturz-Dump erfasst nur den Zustand im Moment des Absturzes, nicht den Weg, der dorthin geführt hat. TTD behält die gesamte Befehlsausführung vom Beginn der Aufzeichnung bis zum Ende, sodass der Pfad zusammen mit dem Zustand erhalten bleibtAbsturz-Dump: Zustand im Moment des AbsturzesZeigt nicht, warum der Wert so geworden istTTD-Trace: Befehlsausführung des aufgezeichneten IntervallsKann zur Position zurückgehen, an der der Wert geschrieben wurdeDiese Lücke ist es, die langlaufende Untersuchungen in die Länge zieht

Abbildung 1: Ein Dump ist Zustand; TTD ist der Pfad. Bei langlaufenden Fehlern ist in der Regel Letzteres das, was Sie wollen.

Typische Fälle sehen so aus.

  • Ein Feld einer Struktur auf dem Heap hält einen unmöglichen Wert. Der Dump zeigt den zerstörten Wert, aber nicht, wer ihn geschrieben hat oder wann.
  • Der Ort der Ausnahme ist bekannt, aber nicht, warum das übergebene Argument ungültig war. Geht man die Aufrufer weiter hinauf, steht die Funktion, die den Wert unterwegs erzeugt hat, nicht mehr auf dem Stapel.
  • Handles oder Speicher wachsen im Lauf eines Monats. Ein einzelner Dump kann nur sagen „es wächst“; der Aufrufpfad, der es hat wachsen lassen, ist nicht da.
Was ein Dump in typischen Langzeitfällen verfehltDer zerstörte Wert ist erfasst, aber nicht wer ihn geschrieben hat, die Ausnahmeposition ist erfasst, aber die Funktion, die das Argument erzeugt hat, steht nicht mehr auf dem Stapel, und das Ressourcenwachstum ist erfasst, aber nicht der Pfad, der es hat wachsen lassen; die drei Fallarten teilen dasZerstörtes FeldKeine Aufzeichnung, wer es wann geschrieben hatAusnahme bei ungültigem ArgumentFunktion, die den Wert erzeugt hat, steht nicht mehr auf dem StapelRessource, die über einen Monat wächstKeine Aufzeichnung des Aufrufpfads, der sie hat wachsen lassenGemeinsamer Punkt: Zustand vorhanden, aber kein Pfad

Abbildung 2: Alle drei Fälle haben „Zustand, aber keinen Pfad“. Mehr Fotografien füllen den Pfad nicht.

Die Dokumentation von Microsoft vergleicht Stärken und Schwächen der Untersuchungsmethoden wie folgt.1

Methode Stärken Schwächen
Live-Debugging Interaktiv, zeigt den Ausführungsfluss und lässt den Zustand ändern Hält die Arbeit des Benutzers an. Wiederholtes Reproduzieren kostet Aufwand. In der Produktion oft unbrauchbar. Vom Fehlerpunkt zur Ursache zurückzugehen ist schwer
Dumps Keine Codeänderungen im Voraus nötig. Geringe Aufdringlichkeit, lassen sich bei einem Auslöser erfassen. Nahezu kein Aufwand, wenn sie nicht genutzt werden Selbst mit aufeinanderfolgenden Schnappschüssen ist die Sicht auf „vergangene Zeit“ grob
Telemetrie und Logs Leichtgewichtig. An Geschäftsszenarien gebunden Keine Logs auf unerwarteten Codepfaden. Unzureichende Datentiefe, und statisch im Code verankert
TTD Stark bei komplexen Fehlern. Keine Codeänderungen im Voraus nötig. Lässt sich offline beliebig oft wiedergeben und zeichnet alles auf Großer Aufwand während der Aufzeichnung. Kann mehr Daten erfassen als nötig. Dateien werden groß

Es gibt noch eine wichtige Eigenschaft, die der offizielle TTD-Walkthrough hervorhebt. Wenn der Debugger am Fehlerpunkt anhält, liegt dieser Punkt oft in Fehlerbehandlungscode mehrere Schritte hinter der eigentlichen Ursache.5 Ein Dump wird immer an dieser Position „mehrere Schritte später“ genommen. Mit TTD können Sie von dort Befehl für Befehl zurückgehen.

Die Lücke zwischen Fehlerpunkt und eigentlicher UrsacheDer Fehlerpunkt, an dem ein Dump genommen wird, liegt oft in der Fehlerbehandlung mehrere Schritte hinter der eigentlichen Ursache, und mit TTD können Sie von diesem Punkt Befehl für Befehl zur Ursache zurückspulenDumpTTDEigentliche Ursache (die Anweisung, die den Wert zerstört hat)Mehrere Schritte vorwärtsFehlerpunkt (Ausnahme, Fehlerbehandlung)Hier eingefrorenMit p- / t- / g- zurückgehen

Abbildung 3: Ein Dump ist am Fehlerpunkt eingefroren. TTD kann vom Fehlerpunkt zur Ursache zurückgehen.

3. Wie TTD funktioniert und was es kostet

3.1 Was aufgezeichnet wird

TTD injiziert eine Aufzeichnungsengine in den Zielprozess und zeichnet die ausgeführten Anweisungen Anweisung für Anweisung auf. In den Worten der offiziellen Dokumentation „kodiert sie eine vollständige Trace auf Anweisungsebene mit durchschnittlich weniger als einem Byte pro Anweisung“; in der Praxis landet sie irgendwo zwischen einem Bit und einem Byte pro Anweisung. Programme, die wenige Arten von Funktionen ausführen und wenig Daten verarbeiten, erzeugen kleinere Traces; das Gegenteil erzeugt größere.24

Eine Aufzeichnung erzeugt zwei Dateien.1

Datei Rolle Ungefähre Größe
.run Die Trace selbst. Speichert die Befehlsausführung während der Aufzeichnung Wächst bei Aktivität um 5 bis 50 MB pro Sekunde. Wächst im Leerlauf nicht4
.idx Der Index. Hilfsdaten, mit denen WinDbg effizient wiedergeben und Speicher abfragen kann. Wird erzeugt, wenn die Aufzeichnung stoppt, und auch automatisch, wenn WinDbg die .run-Datei öffnet 1- bis 2-mal so groß wie die Trace4
Der Ablauf von der TTD-Aufzeichnung zur WiedergabeEine Aufzeichnungsengine wird in den Zielprozess injiziert und die Befehlsausführung in einer .run-Datei aufgezeichnet; wenn WinDbg die .run-Datei öffnet, erzeugt sie einen .idx-Index und gibt mit Positionen, Ereignissen und Abfragen vorwärts und rückwärts wiederZielprozessAufzeichnungsengine injiziert (TTDRecordCPU).run (Aufzeichnung der Befehlsausführung)In WinDbg öffnen.idx (Index) erzeugenMit Positionen, Ereignissen und Abfragen vorwärts und rückwärts wiedergeben

Abbildung 4: Aufgezeichnet wird mit TTD.exe oder WinDbg; gelesen wird mit WinDbg. Es reicht, nur die .run-Datei zu teilen.

Die Zeit in einer .run-Datei wird als „Position“ ausgedrückt. Sie hat die Form zweier hexadezimaler Zahlen, getrennt durch einen Doppelpunkt, etwa 12:0 oder 1A0:12F; die erste Hälfte ist die Sequenznummer (entsprechend einem Sequenzereignis) und die zweite Hälfte die ungefähre Anzahl der Anweisungen seit diesem Ereignis.6 FFFFFFFFFFFFFFFE:0 bedeutet das Ende der Trace.7 Positionen stehen in Kapitel 6 im Mittelpunkt.

Wie Positionen in einer Trace ausgedrückt werdenEine Position ist eine hexadezimale Sequenznummer und Schrittzahl, getrennt durch einen Doppelpunkt; der Anfang liegt nahe 0, das Ende wird als FFFFFFFFFFFFFFFE:0 ausgedrückt, und Sie können auch zu einer ungefähren Position per Prozent gehenPosition xx:yy (hexadezimal)xx: Sequenznummeryy: Anweisungen seit diesem EreignisDas Ende ist FFFFFFFFFFFFFFFE:0Prozentangaben wie !tt 50 funktionieren ebenfalls

Abbildung 5: Eine Position ist „Ereignisnummer:Anweisungsanzahl“. Sie ist keine Wanduhrzeit, lässt sich aber, wie in Kapitel 6 gezeigt, in Wanduhrzeit umrechnen.

3.2 Die Kosten

Die offizielle Dokumentation selbst beschreibt TTD als „invasive Technologie“.2 Hier sind die Kosten in Zahlen.

Punkt Inhalt
Geschwindigkeit Der Zielprozess läuft während der Aufzeichnung 5- bis 20-mal langsamer oder schlimmer (abhängig von der App und den Aufzeichnungsoptionen). In der Oberfläche kann es unbemerkt bleiben, bei schweren Vorgängen wie einem Datei-öffnen-Dialog ist es spürbar23
Dateiwachstum 5 bis 50 MB pro Sekunde bei Aktivität. Einige Minuten Aufzeichnung können mehrere GB erreichen. Es ist keine Obergrenze gesetzt4
Festplatte voll Wenn die Festplatte während der Aufzeichnung voll wird, schreibt TTD die letzte Seite und wartet dann faktisch, bis es wieder schreiben kann. WinDbg zeigt weiter den Aufzeichnungsdialog und gibt weder einen Fehler noch eine Warnung aus. Das Ergebnis ist eine unvollständige Trace4
Speicher Die Aufzeichnung fügt dem Speicher des Zielprozesses den Aufwand virtueller CPUs hinzu (standardmäßig 55 auf x64/ARM64, 32 auf x86). Verringern Sie ihn mit -numVCpu nur, wenn der Speicher knapp ist2
Lässt sich nicht lösen Einmal angehängt, kann TTD sich nicht selbst lösen. Wenn Sie die Aufzeichnung beenden, schließen Sie die App oder beenden Sie den Prozess. Ist der Prozess für das System unverzichtbar, muss das Betriebssystem neu gestartet werden2
Die Kosten der Aufzeichnung und ihre Wirkung auf langlaufende AppsDie Verlangsamung während der Aufzeichnung, das Dateiwachstum, das stille Warten bei voller Festplatte und die Unmöglichkeit, sich nach dem Anhängen zu lösen, sind die vier Kosten, die es unmöglich machen, TTD bedingungslos an eine langlaufende App zu hängenTTD-Aufzeichnung5- bis 20-mal langsamerWächst 5 bis 50 MB pro Sekunde, keine ObergrenzeWartet still, wenn die Festplatte voll istLässt sich nach dem Anhängen nicht lösenBedingungslose Daueraufzeichnung ist nicht tragfähig

Abbildung 6: Jede einzelne der vier Kosten schließt Daueraufzeichnung aus. Deshalb braucht es den „Aufzeichnungsentwurf“ von Kapitel 5.

3.3 Was es nicht kann

  • Nur Benutzermodus. Es kann nur die Benutzermodus-Ausführung eines Prozesses aufzeichnen; Code, der im Kernelmodus läuft, etwa Treiber, lässt sich nicht debuggen.3
  • Geschützte Prozesse. TTD kann sich nicht in geschützte Windows-Prozesse wie Protected Process Light (PPL) injizieren.3
  • Wiedergabe ist schreibgeschützt. Sie können in der Zeit zurückgehen, aber die Geschichte nicht ändern. Befehle, die Speicher lesen, funktionieren; Befehle, die ihn ändern, nicht.3
  • Unverträglichkeit mit Antivirus- und Speicherüberwachungssoftware. Weil TTD in den Prozess einhakt, kollidiert es mit Software, die Systemspeicheraufrufe verfolgt oder spiegelt. Wenn die Aufzeichnung einen Fehler erzeugt, der wie unzureichende Rechte aussieht, deaktivieren Sie solche Software vorübergehend, um das Problem einzugrenzen. Das Electron-Framework ist ebenfalls ein bekannter Konflikt; selbst wenn eine Aufzeichnung gelingt, kann der Zielprozess in einem Deadlock hängen oder abstürzen.3
  • UWP-Apps lassen sich nicht starten und aufzeichnen (Anhängen an eine bereits laufende UWP-App ist möglich). „Ungewöhnliche Prozesse“, die in einer anderen Sitzung oder einem anderen Sicherheitskontext laufen, werden derzeit ebenfalls nicht unterstützt.8
Was TTD nicht aufzeichnen oder tun kannKernelmodus-Code, geschützte Prozesse, Startaufzeichnung von UWP-Apps und Prozesse in einer anderen Sitzung oder einem anderen Sicherheitskontext lassen sich nicht aufzeichnen; der Speicher lässt sich während der Wiedergabe nicht ändern; und Antivirussoftware und Electron können kollidierenEinschränkungen von TTDLässt sich nicht aufzeichnenEinschränkungen und KonflikteKernelmodus-Code (Treiber usw.)Geschützte Prozesse (PPL)Startaufzeichnung von UWP (Anhängen ist möglich)Andere Sitzungen und SicherheitskontexteWiedergabe ist schreibgeschütztKann mit Antivirus und Electron kollidieren

Abbildung 7: Die Einschränkungen kommen in zwei Arten: „lässt sich nicht aufzeichnen“ und „kollidiert“. Letzteres muss je Umgebung eingegrenzt werden.

Der letzte Punkt stolpert jeden, der einen Dienst aufzeichnen will. Die Dokumentation von TTD.exe beschreibt -attach als für die „Untersuchung von Diensten und langlaufenden Apps“ gedacht und -monitor als Aufzeichnung „jedes Mal, wenn ein Programm oder Dienst startet“,2 was sich nicht einfach mit der Aussage auf der Seite zur Fehlerbehebung deckt. In der Praxis ist der sichere Weg, je Umgebung in dieser Reihenfolge zu prüfen: bestätigen, dass ping.exe oder cmd.exe in derselben Konfiguration wie in der Produktion aufgezeichnet werden können, dann den Zielprozess versuchen.8

4. Aufzeichnen — Die WinDbg-Oberfläche und TTD.exe

Es gibt zwei Einstiege für die Aufzeichnung: die WinDbg-Oberfläche oder das Kommandozeilen-TTD.exe.

4.1 Aufzeichnen aus der WinDbg-Oberfläche

Starten Sie WinDbg als Administrator (eine Erhöhung ist für TTD verpflichtend1), wählen Sie File > Start debugging > Launch executable (advanced), geben Sie die ausführbare Datei an und aktivieren Sie Record with Time Travel Debugging. Mit Configure and Record können Sie den Speicherort der Trace-Datei und Record subset of execution festlegen (die aufgezeichneten Module mit einer kommagetrennten Liste wie notepad.exe,kernelbase.dll einschränken). Für einen bereits laufenden Prozess wählen Sie File > Start debugging > Attach to process und aktivieren ebenso Record Process with Time Travel Debugging.9

Während der Aufzeichnung erscheint ein kleiner Dialog mit den Schaltflächen „Stop and Debug“ und „Cancel“. Wenn die App beendet (oder abstürzt), wird die Trace geschlossen, und WinDbg öffnet sie automatisch und baut den Index.5

Der Ablauf der Aufzeichnung aus der WinDbg-OberflächeIn als Administrator gestartetem WinDbg Launch executable (advanced) oder Attach to process wählen, Record with Time Travel Debugging aktivieren, Speicherort und Modulfilter in Configure and Record festlegen, den Aufzeichnungsdialog durchlaufen, und wenn die App beendet, wird die Trace geschlossen und automatisch indiziertWinDbg als Administrator startenLaunch executable (advanced) / Attach to processRecord with Time Travel Debugging aktivierenConfigure and Record: Speicherort, ModulfilterAufzeichnungsdialog (Stop and Debug)App beendet, Trace wird geschlossen und automatisch indiziert

Abbildung 8: Die Aufzeichnung aus der Oberfläche hat fünf Schritte. Überspringen Sie „als Administrator starten“, bleiben Sie am ersten Dialog stehen.

4.2 Aufzeichnen mit TTD.exe

Wenn Sie auf einem PC aufzeichnen müssen, auf dem WinDbg nicht installiert werden kann, oder um die Aufzeichnung zu automatisieren, verwenden Sie TTD.exe allein. Es wird über den App Installer von https://aka.ms/ttd/download installiert, und nach der Installation können Sie es mit ttd.exe -help prüfen. Für Offline-Umgebungen stellt Microsoft außerdem ein offizielles Verfahren (mit einem PowerShell-Skript) bereit, um das MSIX-Bundle von Hand zu entpacken und nur die Binärdateien zu extrahieren.2 Die Aufzeichnung erfordert Administratorrechte und wird normalerweise von einer Administrator-Eingabeaufforderung aus ausgeführt.2

Es gibt drei Aufzeichnungsmodi.2

Die drei Aufzeichnungsmodi von TTD.exelaunch startet einen neuen Prozess mit Argumenten und zeichnet ihn auf, läuft aber mit erhöhten Rechten. attach hängt an einen laufenden Prozess per PID. monitor zeichnet jedes Mal auf, wenn das angegebene Programm startet, und der Start geschieht mit normalen RechtenAufzeichnungsmodi von TTD.exe-launch: starten und aufzeichnen-attach: an eine laufende PID anhängen-monitor: jeden Start aufzeichnenWird mit Administratorrechten gestartetBehält normale RechteNormaler Startpfad, geeignet für Automatisierung

Abbildung 9: Nur -launch kann Argumente übergeben, läuft aber mit erhöhten Rechten. Um Verhalten nahe der Produktion aufzuzeichnen, verwenden Sie -attach oder -monitor.

:: Starten und aufzeichnen (Standardmodus; -launch kann weggelassen werden)
TTD.exe -out C:\traces MyApp.exe --config prod.json

:: An einen laufenden Prozess anhängen (Ausgabeverzeichnis vorher anlegen)
TTD.exe -attach 21440 -out C:\traces\MyApp.run

:: Jeden Start aufzeichnen (Ctrl+C beendet die Überwachung; -out erfordert einen vollständigen Pfad)
TTD.exe -out C:\traces\ -monitor MyApp.exe
  • -launch ist der einzige Modus, der Argumente übergeben kann, aber das Programm wird mit denselben (Administrator-)Rechten wie TTD.exe gestartet. Bei Apps, deren Verhalten sich mit den Rechten ändert, zeichnen Sie mit -attach oder -monitor auf, um normale Rechte zu behalten.2
  • -attach setzt voraus, dass das Ausgabeverzeichnis bereits existiert. Wenn Sie einen Dateinamen angeben, darf keine Datei dieses Namens existieren.2
  • -monitor installiert einen Treiber zur Überwachung von Prozessstarts und zeichnet jedes Mal auf, wenn das angegebene Programm (mehrere können angegeben werden) startet. Es bleibt bis zum Neustart wirksam und wird mit Ctrl+C gestoppt. Die Vorteile sind, dass Sie den Start nicht selbst zusammenbauen müssen, das Ziel mit normalen Rechten läuft und es sich für skriptgesteuerte Automatisierung eignet. Mit -cmdLineFilter "string" werden nur Starts aufgezeichnet, deren Kommandozeile diese Zeichenfolge enthält.2
  • -children zeichnet auch Kindprozesse auf, aber jeder Prozess erhält eine eigene .run-Datei, und WinDbg kann jeweils nur eine öffnen.2

Während der Aufzeichnung erscheint eine kleine Oberfläche mit zwei Schaltflächen: „Tracing Off“ (Aufzeichnung stoppen und die App weiterlaufen lassen) und „Exit App“ (die App schließen und die Aufzeichnung beenden). Für Automatisierung blenden Sie sie mit -noUI aus und akzeptieren die EULA mit -accepteula.2 Das Aufzeichnungsprotokoll wird in einer .out-Datei am selben Ort wie die .run-Datei gehalten, wo Sie die Wanduhr-Start- und -Endzeiten der Aufzeichnung, die Länge der Aufzeichnungssitzung (Simulationszeit), ob es ein Start oder ein Anhängen war, und die Betriebssystemversion lesen können. Wenn eine Aufzeichnung fehlschlägt, erscheinen manche Fehlermeldungen nur in der .out-Datei.2

5. Aufzeichnungsentwurf für langlaufende Apps

Das ist das Herzstück des Artikels. Angesichts der Kosten in Kapitel 3 braucht das Fangen eines Fehlers, der zu einem unbekannten Zeitpunkt in einem Prozess auftritt, der tagelang läuft, einen Aufzeichnungsentwurf. TTD.exe stellt vier Mittel bereit, und Sie wählen nach der Art des Symptoms.

Wahl des Aufzeichnungsumfangs nach dem Symptom einer langlaufenden AppWenn Sie nicht wissen, wann es auftritt, behalten Sie nur den letzten Teil mit einem Ringpuffer; wenn Sie wissen, welches Modul verdächtig ist, zeichnen Sie nur dieses Modul auf; wenn Sie die App ändern können, legen Sie das Intervall mit der API für manuelle Aufzeichnung fest; wenn es nur beim Start oder bei bestimmten Starts erscheint, zeichnen Sie jeden Start mit dem Überwachungsmodus aufUnbekannt, wann es auftrittNur beim Start oder bei bestimmten StartsVerdächtiges Modul ist klarApp kann geändert werdenArt des Symptoms?-ring / -maxFile: nur das Ende behalten-monitor: jeden Start aufzeichnen-module hinzufügenIntervall mit -recordmode Manual festlegen

Abbildung 10: Die vier schließen einander nicht aus. Die Kombination von -ring mit -module ist die praktischste Kombination für langlaufende Apps.

5.1 Ringpuffer — Nur die letzten N MB behalten

Mit -ring wird die Trace in einen Ringpuffer der mit -maxFile angegebenen Größe geschrieben, und die Datei wächst nie über diese Grenze hinaus. Was bleibt, ist nur der letzte Teil der Aufzeichnung, der in diese Größe passt.2 Die Einheit von -maxFile ist MB; im Ringpuffermodus ist der Standard 2048 MB, das Minimum 1 MB und das Maximum 32768 MB (der Standard für den In-Memory-Ring eines 32-Bit-Prozesses ist 256 MB).2

:: An eine laufende Überwachungs-App anhängen und nur die letzten 4 GB Ausführung behalten
TTD.exe -accepteula -noUI -attach 21440 -ring -maxFile 4096 -out C:\traces\MyApp.run

:: Wenn das Symptom erkannt wird, die Aufzeichnung stoppen (die App läuft weiter)
TTD.exe -stop 21440

-stop akzeptiert einen Prozessnamen, eine PID oder all und stoppt diese Aufzeichnung. -wait <seconds> wartet, bis jede Aufzeichnungssitzung auf dem System beendet ist (-1 für unbegrenzt), und wird in Automatisierungsskripten verwendet, um die Reihenfolge „stoppen, dann die Datei einsammeln“ durchzusetzen.2

Zeitlinie einer Ringpuffer-AufzeichnungDie Aufzeichnung beginnt beim Anhängen, ältere Teile werden aus dem Ringpuffer verdrängt, und wenn das Symptom erscheint und stop ausgegeben wird, bleibt nur der jüngste maxFile-Anteil als TraceAufzeichnung mit -attach -ring startenÄltere Intervalle werden verdrängtSymptom erscheintAufzeichnung mit -stop stoppenNur der jüngste maxFile-Anteil bleibtSo dimensionieren, dass die Momente vor dem Symptom in den Puffer passen

Abbildung 11: Der Ringpuffer ist eine Vorrichtung, um „die Momente vor dem Symptom“ zu behalten. Rechnen Sie -maxFile rückwärts aus „Zeit vom Bemerken des Symptoms bis zum Stoppen, multipliziert mit Wachstum pro Sekunde“.

Es gibt zwei Entwurfspunkte.

  1. Machen Sie den Puffer groß genug, um die Zeit vom Bemerken bis zum Stoppen aufzunehmen. In einem aktiven Prozess wächst die Trace um 5 bis 50 MB pro Sekunde,4 sodass ein 4-GB-Ring unter Last einer bis zwei Minuten und unter leichter Last etwas über zehn Minuten entspricht. Sie brauchen einen Mechanismus, der die Verzögerung zwischen dem Erkennen des Symptoms (eine bestimmte Logzeile, ein Zählerschwellenwert, ein Alarm der Überwachung) und -stop in diesem Fenster hält.
  2. Stoppen löst nicht. -stop stoppt die Aufzeichnung, aber TTD löst sich nicht selbst vom Zielprozess.2 Die Aufzeichnung vollständig zu beenden erfordert das Beenden des Prozesses, also nehmen Sie „Neustart im nächsten Wartungsfenster nach dem Einsammeln der Trace“ in das Betriebsverfahren auf.
Stoppen einer Ringpuffer-Aufzeichnung in Abstimmung mit der ÜberwachungWenn die Überwachungsseite das Symptom über Logs oder Zähler erkennt, ruft sie TTD.exe stop auf, sammelt die abgeschlossene .run-Datei ein und startet den Prozess im nächsten Wartungsfenster neu, um TTD zu entfernenZielprozessTTD.exeÜberwachung (Logs, Zähler)ZielprozessTTD.exeÜberwachung (Logs, Zähler)Symptom erkennen-stop PIDAufzeichnung stoppen (Prozess läuft weiter).run ist abgeschlossen.run einsammeln, verschlüsseln und speichernIm nächsten Wartungsfenster neu starten

Abbildung 12: Nur wenn die Verzögerung zwischen Erkennung und -stop in den Puffer passt, bleiben die Momente vor dem Symptom in der Trace. Der Neustart ist Teil des Betriebs.

5.2 Modulfilter — Nur aufzeichnen, während der eigene Code läuft

-module <module name> zeichnet nur das angegebene Modul (die ausführbare Datei selbst oder eine geladene DLL; mehrere können angegeben werden) und den Code, den dieses Modul aufruft, auf. Der Zielprozess läuft mit voller Geschwindigkeit, bis Code im angegebenen Modul ausgeführt wird; die Aufzeichnung beginnt, wenn die Ausführung ins Modul eintritt, stoppt, wenn sie es verlässt, und der Prozess kehrt zur vollen Geschwindigkeit zurück. Weil das Ein- und Ausschalten der Aufzeichnung teuer ist, bleibt die Aufzeichnung an, während das angegebene Modul andere Module im Prozess aufruft.2

:: Nur aufzeichnen, während unsere Messlogik-DLL läuft (kombiniert mit einem Ring)
TTD.exe -accepteula -noUI -attach 21440 -module MeasureCore.dll -ring -maxFile 2048 -out C:\traces\

Eine so erzeugte Trace überspringt einfach die Intervalle, in denen die Aufzeichnung aus war, behandelt „die nächste Anweisung“ als die erste Anweisung nach Wiederaufnahme der Aufzeichnung, und Sie debuggen sie nicht anders als eine Trace des ganzen Prozesses.2 In langlaufenden Apps machen die UI-Leerlaufschleife und die interne Verarbeitung des Frameworks tendenziell den Großteil der ausgeführten Anweisungen aus, sodass die Beschränkung der Aufzeichnung auf das eigene Modul allein Aufwand und Dateigröße erheblich senkt.

Wie modulgefilterte Aufzeichnung sich verhältDer Zielprozess läuft außerhalb des angegebenen Moduls mit voller Geschwindigkeit; die Aufzeichnung beginnt, wenn Code im angegebenen Modul betreten wird, läuft weiter, während dieses Modul andere Module aufruft, und stoppt, wenn die Ausführung das Modul verlässtAußerhalb des angegebenen Moduls: volle GeschwindigkeitEintritt ins angegebene Modul: Aufzeichnung beginntAndere Module, die es aufruft: Aufzeichnung läuft weiterVerlassen des angegebenen Moduls: Aufzeichnung stoppt

Abbildung 13: Weil die Win32-APIs und die Laufzeit, die Ihre DLL aufruft, ebenfalls aufgezeichnet werden, reicht das, um zu verfolgen, „was unser Code dem Betriebssystem übergeben hat“.

5.3 Manuelle Aufzeichnung — Die App legt das Intervall fest

Mit -recordmode Manual läuft der Prozess auch nach der Injektion von TTD mit voller Geschwindigkeit weiter, und die Aufzeichnung geschieht nur, wenn das Programm die In-Prozess-Aufzeichnungs-API von TTD aufruft (der Standard, Automatic, zeichnet ab dem Moment der Injektion auf).2 Die API-Dokumentation und Beispiele liegen im WinDbg-Samples-Repository auf GitHub.10

Wenn Sie die App ändern können, ist das der sparsamste Ansatz. Sie können Logik einbauen, die die Aufzeichnung startet, wenn die App selbst weiß, dass eine Anomalie unmittelbar bevorsteht, und stoppt, wenn sich die Lage normalisiert: „drei aufeinanderfolgende Kommunikationsversuche sind fehlgeschlagen“, „der Rückstau der Warteschlange hat den Schwellenwert überschritten“ und so weiter. In einer Supervisor-Prozess-Konfiguration wie der im Job-Object-Artikel behandelten kann der Supervisor das Intervall stattdessen entscheiden.

5.4 Überwachungsmodus — Jeden Start aufzeichnen

Für Fehler, die nur unmittelbar nach dem Start oder bei bestimmten Starts erscheinen, passt -monitor. Die offizielle Vergleichstabelle beschreibt den Überwachungsmodus ebenfalls als für das „Fangen intermittierender Probleme und Startprobleme“ gedacht.2 Wenn Sie dasselbe Programm viele Male aufzeichnen, werden die standardmäßigen fortlaufenden Dateinamen (MyApp01.run, MyApp02.run und so weiter) ineffizient, weil vorhandene Dateien durchsucht werden müssen; verwenden Sie daher -timestampFilename für Namen mit Zeitstempel. Die Zahl gleichzeitiger Aufzeichnungen lässt sich mit -maxConcurrentRecordings deckeln.2

Wie der Überwachungsmodus sich verhältDie Option monitor installiert einen Treiber zur Überwachung von Prozessstarts, grenzt das Ziel mit dem Kommandozeilenfilter jedes Mal ein, wenn das angegebene Programm startet, zeichnet es auf, erzeugt eine eigene Trace-Datei je Start und läuft weiter bis Ctrl+C oder einem NeustartJaNeinTreiber zur Startüberwachung installierenStart des angegebenen Programms erkennenStimmt mit -cmdLineFilter überein?Diesen Start aufzeichnen (eigene Datei je Start)Nicht aufzeichnenAuf den nächsten Start warten (bis Ctrl+C oder Neustart)

Abbildung 14: Der Überwachungsmodus „lauert auf den Start“. Er passt zu Fehlern, die bei jedem Start erscheinen, und zu Fehlern, die sich nach Startbedingungen eingrenzen lassen.

5.5 Wohin mit der Festplatte

Für langlaufende Aufzeichnungen legen Sie die Traces auf ein eigenes Volume und nehmen das Wachstum der .run-Datei in Ihre Überwachung auf. Wie in Abschnitt 3.2 vermerkt, wartet die Aufzeichnung bei voller Festplatte einfach still, ohne Fehler. Der offizielle Workaround ist ebenso primitiv: „freien Speicherplatz im Explorer prüfen“ und „prüfen, dass die .run-Datei regelmäßig wächst“.4 Ohne Überwachung des freien Speichers enden Sie mit einer unvollständigen Trace, in der der Moment, den Sie am meisten wollten, nie geschrieben wurde.

Wie eine volle Festplatte eine unvollständige Trace erzeugtWenn die Festplatte während der Aufzeichnung voll wird, schreibt TTD die letzte Seite und wartet still ohne Fehler und ohne Warnung, sodass das danach auftretende Symptom nicht aufgezeichnet wird und eine unvollständige Trace bleibt, die sich öffnet, der aber der entscheidende Teil fehlt. Verhindern Sie das mit einem eigenen Volume und Überwachung des .run-WachstumsverhindertFestplatte wird vollSchreibt die letzte Seite und wartet stillKein Fehler, keine WarnungSymptom tritt danach aufUnvollständige Trace ohne das SymptomEigenes Volume + Überwachung des .run-Wachstums

Abbildung 15: Eine Trace, die „sich öffnet, der aber der entscheidende Teil fehlt“, entsteht aus einer Lücke in der Festplattenüberwachung.

6. Wiedergeben — Positionen, Ereignisse und Zurückspulen

6.1 Öffnen

Wenn Sie eine .run-Datei in WinDbg öffnen und es keinen Index gibt, läuft !index automatisch und baut die .idx-Datei, während es Keyframes zählt (Positionen in der Trace, die automatisch für die Indizierung erzeugt werden; größere Traces haben mehr davon). Je größer die Trace, desto länger dauert es.5 Der Zustand des Index lässt sich mit !index -status prüfen; wenn etwas anderes als „Index file loaded“ gemeldet wird, bauen Sie ihn mit !index -force neu. Wenn das immer noch fehlschlägt, schließen Sie den Debugger, löschen Sie die .idx-Datei und öffnen Sie die .run-Datei erneut. Das Neubauen des Index ändert die .run-Datei nicht, sodass keine Daten verloren gehen.8

Ein Vorbehalt: Als die Indizierung großer Traces in TTD 1.11.611 verbessert wurde, hat sich das Indexformat geändert, und vorhandene Traces müssen neu indiziert werden.11 Wenn Sie alte .idx-Dateien mitführen, stolpern Sie hier.

Prüfen und Neubauen des IndexNach dem Öffnen der Trace den Zustand mit !index -status prüfen; wenn er etwas anderes als Index file loaded ist, mit !index -force neu bauen, und wenn das immer noch fehlschlägt, den Debugger schließen, die .idx-Datei löschen und die .run-Datei erneut öffnen. Das Neubauen ändert die .run-Datei nichtIndex file loadedAlles andereSchlägt fehlGelingtDie Trace öffnen!index -statusZur Analyse übergehenMit !index -force neu bauenSchließen, .idx löschen, .run erneut öffnen

Abbildung 16: Das Neubauen des Index berührt die .run-Datei nicht. Im Zweifel löschen und erneut öffnen.

6.2 Nach Position gehen

Wenn Sie !tt eine Position übergeben, geht es zu diesem Zeitpunkt.6

!tt 0          ; Anfang der Trace
!tt 50         ; ungefähr die 50%-Position
!tt 100        ; Ende der Trace
!tt 1A0:12F    ; zur Position 1A0:12F

Eine Position ist ein Paar hexadezimaler Zahlen, Sequenznummer:Schrittzahl.6 Das Position-Objekt hat die Eigenschaften Percent (der Anteil der Trace), Sequence und Steps, dazu SeekTo(), das zu dieser Position geht, und ToSystemTime(), das die ungefähre Wanduhrzeit (UTC) zurückgibt.7 In langlaufenden Untersuchungen macht ToSystemTime() den Unterschied, weil es die Zeitstempel im App-Log mit Positionen in der Trace abgleichen lässt. Ergebnisse von TTD.Calls tragen auch SystemTimeStart / SystemTimeEnd.12

Positionen mit Log-Zeitstempeln abgleichenAusgehend von einem Zeitstempel im App-Log der ungefähren Wanduhrzeit des Position-Objekts folgen, die Position in der Trace identifizieren, mit SeekTo dorthin gehen und die Ausführung in der Umgebung lesenApp-Log: Zeitpunkt der AnomalieEine Position finden, deren ToSystemTime nahe liegtMit SeekTo dorthin gehenDie umliegenden Aufrufe und Werte lesen

Abbildung 17: „Was hat es unmittelbar vor dieser Logzeile getan?“ lässt sich über die Entsprechung zwischen Positionen und Wanduhrzeit nachschlagen.

Positionen sind auch zum Teilen nützlich. Wenn Sie eine Trace an einen Kollegen übergeben, hängen Sie die Position !tt x:y an, und er kann vom selben Zeitpunkt aus zu lesen beginnen. Das Schreiben von Positionsbereichen in Fehlerberichten ist ebenfalls eine offiziell empfohlene Praxis.2

6.3 Zurückspulen

Ein angehängtes - an den üblichen Schrittbefehlen bewegt sich in der Zeit rückwärts.13

Befehl Bedeutung Schaltfläche im Menüband
p- Eine Anweisung (oder eine Quellzeile) zurück. Ein Funktionsaufruf zählt als ein Schritt Step Over Back
t- Eine Anweisung (oder eine Quellzeile) zurück. Tritt in Funktionsaufrufe ein Step Into Back
g- Rückwärts ausführen. Stoppt, wenn ein Haltepunkt getroffen wird, bei einem Ereignis oder am Anfang der Trace Go Back

Die Ereignisse, die g- stoppen, sind dieselben, die ein vorwärts gerichtetes g stoppen.13 Anders gesagt: Setzen Sie einen ba (Break-on-Access-Haltepunkt) oder bp und führen Sie g- aus, und Sie gehen geradewegs zurück zur „letzten Position, an der diese Bedingung galt“. Das ist der Grundzug von Kapitel 7.

Wahl unter den Rückwärtsbefehlenp- geht einen Schritt über Funktionsaufrufe hinweg zurück, t- geht eine Anweisung in Funktionen hinein zurück, und g- geht geradewegs zu einem Haltepunkt, einem Ereignis oder dem Anfang der Trace zurück. Was ein vorwärts gerichtetes g stoppt, stoppt auch g-p-t-g-Aktuelle PositionEinen Schritt über Aufrufe hinweg zurückEine Anweisung in Funktionen hinein zurückGeradewegs zur nächsten Stoppbedingung zurückba / bp / Ereignis / Anfang der Trace

Abbildung 18: Um die Nähe sorgfältig zu betrachten, verwenden Sie t-; um zu einer fernen Ursache zu springen, ba + g-.

6.4 Über Ereignisse einsteigen

Wenn Sie unsicher sind, wo Sie zu lesen beginnen sollen, beginnen Sie mit der Ereignisliste. @$curprocess.TTD.Events listet Thread-Erzeugung und -Beendigung, Modulladen und -entladen sowie Ausnahmen als Ereignisse auf.14

dx -g @$curprocess.TTD.Events
dx @$curprocess.TTD.Events.Where(t => t.Type == "Exception").Select(e => e.Exception)

Ein Ausnahmeereignis enthält die Position, den Typ (Software / Hardware), den Ausnahmecode und den Programmzähler zu dem Zeitpunkt, und ein Klick auf den Link [Time Travel] in der Ausgabe geht zu dieser Position.15 Der offizielle Walkthrough folgt diesem Ablauf: Er springt zur Position einer Zugriffsverletzung (0xc0000005), vermutet Stapelzerstörung, weil Stapelzeiger und Basiszeiger nicht übereinstimmen, und geht mit t- drei Anweisungen zurück, um die Werte zu prüfen.5 Das Fenster Timelines von WinDbg visualisiert Ausnahmen, Haltepunkte, Speicherzugriffe und Funktionsaufrufe als Zeitlinie, und ein Doppelklick auf eine Ausnahme löst dasselbe SeekTo() aus.16

6.5 Threads und Positionen

!positions zeigt jeden aktiven Thread an der aktuellen Position zusammen mit der Position jedes Threads in der Trace.17 Hier gibt es eine Falle. Das Umschalten von Threads mit ~<number>s bewegt die Position in der Trace nicht. Die Position, die der Debugger zum Lesen des Speichers verwendet, ändert sich nicht; um den Speicher eines anderen Threads „zu diesem Zeitpunkt“ zu betrachten, gehen Sie mit dem Positionslink in der Ausgabe von !positions oder mit !tt x:y dorthin.13

Der Grundweg durch eine WiedergabeDie Trace öffnen und den Index bauen, von der Ereignisliste zur Position der Ausnahme gehen, schrittweise rückwärts zur Ursache gehen und bei Bedarf mit positions zur Position eines anderen Threads gehenDie Trace öffnen und indizierenDie Ausnahme in TTD.Events findenMit [Time Travel] zur Position gehenMit t- / p- / g- zurückgehenPositionen anderer Threads mit !positions prüfen~s bewegt die Trace-Position nicht

Abbildung 19: „Über ein Ereignis einsteigen, rückwärts gehen“ ist der Grundweg durch eine Wiedergabe. Threads umzuschalten ist nicht, Positionen zu bewegen.

7. „Wann“ mit Abfragen finden — TTD.Calls und TTD.Memory

Die eigentliche Stärke der Wiedergabe ist, dass Sie die ganze Trace abfragen können. Die Objekte von TTD werden über das Debugger-Datenmodell (den Befehl dx) bereitgestellt, und Sie können sie LINQ-artig filtern, sortieren und aggregieren.18

7.1 TTD.Calls — Nach Funktionsaufrufen suchen

@$cursession.TTD.Calls("module!symbol") sammelt die Aufrufe der angegebenen Funktion aus der ganzen Trace. Platzhalter sind erlaubt, und jeder Aufruf trägt seine Start- und Endpositionen (TimeStart / TimeEnd), die Thread-ID (mit einer UniqueThreadId, die nie wiederverwendet wird), die Argumente (Parameters[]), den Rückgabewert (ReturnValue) und die Rücksprungadresse (ReturnAddress).12

Das Beispiel in der offiziellen Dokumentation ist GetLastError. Aggregieren Sie die Aufrufe, deren Rückgabewert ungleich null ist, nach Fehlercode, und Sie erhalten eine Liste, welche Fehler wie oft während der Trace aufgetreten sind.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

Für „wo wurde MessageBox zuletzt aufgerufen?“ nehmen Sie den letzten Aufruf mit OrderBy(c => c.TimeStart).Last() und gehen über den Link [Time Travel] seines TimeStart dorthin.18

Aufbau einer TTD.Calls-AbfrageAufrufe nach Funktionsnamen sammeln, nach Rückgabewert oder Argumenten filtern, nach Fehlercode und dergleichen aggregieren, nach Zeit sortieren und über den Time-Travel-Link zur Position des gewünschten Aufrufs gehenTTD.Calls (Funktionsname, Platzhalter)Where: nach Rückgabewert oder Argumenten filternGroupBy: nach Fehlercode usw. aggregierenOrderBy: nach Zeit sortierenÜber [Time Travel] auf TimeStart gehen

Abbildung 20: Die Art, Abfragen zu nutzen, ist, nicht von „wo“ auszugehen, sondern von „wann, wie oft und mit welchen Argumenten“.

Ein Wort zu Symbolen. TTD bestimmt Anzahl und Typen der Argumente einer Funktion, ihren Rückgabetyp und ihre Aufrufkonvention aus den PDB-Symbolinformationen. Mit private symbols erhalten Sie den Funktionsnamen und die korrekten Argumente. Nur mit public symbols erhalten Sie den Funktionsnamen und Standardargumente (vier 64-Bit-Ganzzahlen ohne Vorzeichen). Ein Modul ganz ohne Symbole erhält den Funktionsnamen UnknownOrMissingSymbols.1218 Zu den Arten von PDB und wie man sie aufbewahrt, siehe „Was ist eine PDB?“.

Verfügbarkeit von Symbolen und Ergebnisse von TTD.CallsMit private symbols erhalten Sie den Funktionsnamen und die korrekten Argumente; nur mit public symbols den Funktionsnamen und die vier Standard-64-Bit-Ganzzahlargumente; ohne Symbole wird der Funktionsname UnknownOrMissingSymbolsprivate symbolspublic symbolskeineSymbole für das Modul?Funktionsname + korrekte Argumente und RückgabewertFunktionsname + Standardargumente (4 x 64-Bit-Ganzzahlen)UnknownOrMissingSymbols

Abbildung 21: Ob Sie die PDBs Ihrer eigenen Module aufbewahrt haben, bestimmt unmittelbar, wie nützlich die Abfragen sind.

Calls bedeutet Berechnung, daher dauert es bei größerer Trace länger und die CPU-Auslastung steigt. Ergebnisse werden im Speicher zwischengespeichert, sodass die zweite und spätere Abfrage derselben Funktion schneller ist.12 Wenn eine Abfrage nichts liefert, gibt es vier Ursachen: wie der Aufruf geschrieben ist (prüfen Sie den Modulnamen mit dem Befehl x; kommt er in Großbuchstaben zurück, verwenden Sie das), die Zieldll ist an dieser Position noch nicht geladen (gehen Sie zu einer Position nach dem Laden und führen Sie sie erneut aus), die Funktion ist inline expandiert (lässt sich nicht verfolgen), oder der Platzhalter ist zu weit (engen Sie ihn ein).18

7.2 TTD.Memory — Nach Speicherzugriffen suchen

@$cursession.TTD.Memory(start address, end address, "access type") sammelt Zugriffe auf den angegebenen Speicherbereich aus der ganzen Trace. Die Typen sind r (Lesen), w (Schreiben), rw, e (Ausführen), rwe und ec (Ausführen/Ändern).19 „Wer hat diese Variable zuletzt geschrieben?“ beantworten Sie, indem Sie mit "w" sammeln, .Last() nehmen und zu dieser Position gehen.5

dx -g @$cursession.TTD.Memory(0x00a4fca0, 0x00a4fca4, "w")
dx @$cursession.TTD.Memory(0x00a4fca0, 0x00a4fca4, "w").Last().TimeStart.SeekTo()

Wenn Sie nur vorwärts oder rückwärts von der aktuellen Position suchen müssen, sind @$curprocess.TTD.PrevMemoryAccess("w", address, size) / NextMemoryAccess leichter und akzeptieren mehrere Bereiche auf einmal. Die Position, an der sich ein Register geändert hat, lässt sich mit @$curthread.TTD.PrevRegisterWrite("rcx") finden.6

7.3 ba + g- — Den Debugger „wer hat es zerstört?“ beantworten lassen

Verdichtet man das im offiziellen Walkthrough gezeigte Verfahren zu einer für langlaufende Untersuchungen nutzbaren Form, ergibt sich Folgendes.5

  1. Mit TTD.Events zur Position der Ausnahme gehen
  2. Mit t- zurückgehen und hypothetisieren, welche Variable den zerstörten Wert hält
  3. Die Adresse dieser Variable mit dx &variable holen
  4. Einen Schreibhaltepunkt mit ba w4 <address> setzen
  5. g- ausführen, um geradewegs zur Position zu gehen, an der diese Variable zuletzt geschrieben wurde
  6. Prüfen, ob dieser Punkt (oder einige Anweisungen früher) die Ursache ist. Wenn der geschriebene Wert von einer anderen Variable stammte, ba auf diese Variable setzen und g- erneut ausführen
  7. Wiederholen, bis Sie die Anweisung erreichen, die es zerstört hat
Den Ursprung eines Werts mit ba und g- verfolgenEinen Schreibhaltepunkt auf die Adresse des zerstörten Werts setzen und rückwärts ausführen, an der Anweisung anhalten, die ihn zuletzt geschrieben hat, und wenn dieser Wert von einer anderen Variable stammte, dieselben Schritte wiederholen, bis Sie die Anweisung erreichen, die ihn zerstört hatJaNeinDie Adresse des zerstörten Werts identifizierenEinen Schreibhaltepunkt mit ba w setzenMit g- rückwärts ausführenAn der Anweisung anhalten, die ihn zuletzt geschrieben hatStammte der Wert von einer anderen Variable?Die zerstörende Anweisung = die Ursache

Abbildung 22: Eine Untersuchung, die mit einem Dump bei „es ist zerstört“ endet, geht mit TTD mechanisch zu „wer hat es zerstört“ weiter.

TTD 1.11.553 und später fügen außerdem @$curframe.TTD.VariableHistory() hinzu, das die Geschichte der lokalen Variablenwerte eines Frames zurückgibt. Es zeigt eine Tabelle der Variablennamen und, welche Werte jede Variable über welche Positionsbereiche gehalten hat.11 In Situationen wie Stapelzerstörung, in denen Sie wissen wollen, „seit wann der Wert falsch ist“, ist es nützlich, um einzugrenzen, wo Sie ba zuerst setzen.

8. Anwendung auf langlaufende Fehler

Nun wenden wir diese Werkzeuge auf die drei am Anfang aufgeführten Fallarten an.

Arten langlaufender Fehler und der TTD-Einstieg für jedeBei intermittierenden Ausnahmen und Datenzerstörung vom Ereignis mit ba und g- zurückgehen; bei Ressourcenwachstum Beschaffungs- und Freigabeaufrufe mit TTD.Calls abgleichen; bei Keine Rückmeldung und Warteketten Positionen und Wanduhrzeiten von Warte-API-Aufrufen verfolgenIntermittierende Ausnahmen, DatenzerstörungTTD.Events, dann ba + g-Handle- und SpeicherwachstumBeschaffung und Freigabe mit TTD.Calls abgleichenKeine Rückmeldung, Warteketten!positions und Wanduhrzeiten von Warte-APIsMit -module auf die eigene DLL beschränkt aufzeichnen

Abbildung 23: Der Einstieg unterscheidet sich nach Art. Gemeinsam ist, den Aufzeichnungsumfang einzugrenzen, bevor Sie zu lesen beginnen.

Art 1: Intermittierende Ausnahmen und Datenzerstörung. Behalten Sie die Momente vor dem Symptom mit dem Ringpuffer von Abschnitt 5.1, springen Sie von der Ereignisliste von Abschnitt 6.4 zur Position der Ausnahme und verfolgen Sie den Ursprung des Werts mit dem ba + g- von Abschnitt 7.3. Der Unterschied zu einer Dump-Untersuchung ist, dass die Untersuchung nicht endet, wenn Sie „die zerstörte Variable“ gefunden haben; von dort gehen Sie mechanisch rückwärts weiter.

Art 2: Handle- und Speicherwachstum. Das ist die Art von Fall, die in „Untersuchung eines Absturzes einer Industriekamera-Anwendung nach Langzeitbetrieb - Der Handle-Leak (Teil 1)“ seziert wird. Sie können keinen Monat aufzeichnen, also beschränken Sie die Aufzeichnung mit dem -module von Abschnitt 5.2 auf Ihre eigene DLL, kombinieren Sie sie mit -ring und behalten Sie „die wenigen Minuten, während es wächst“. Auf der Trace sammeln Sie TTD.Calls("kernelbase!CreateFileW") und TTD.Calls("kernelbase!CloseHandle") und gleichen den Rückgabewert von CreateFileW (den Handle-Wert) mit dem ersten Argument von CloseHandle (Parameters[0]) ab. Der ReturnValue von CloseHandle ist ein boolesches Erfolgsflag, also für den Abgleich nutzlos. Bleibt ein Handle-Wert ungeschlossen, gibt die ReturnAddress (der Aufrufer) dieses CreateFileW-Aufrufs Ihnen den Aufrufer, der leakt.12 Gleichwohl ist das Beobachten, ob ein Leak existiert und wie groß es ist, mit Application Verifier oder Handle-Zählern günstiger; die richtige Arbeitsteilung ist, TTD in der Phase einzusetzen, in der Sie festnageln, welcher Pfad leakt. Beachten Sie, dass eine Aufzeichnung mit aktiviertem Application Verifier die Wiedergabeleistung wegen der Speicherverwendung spürbar verschlechtert; deaktivieren Sie es daher während der Aufzeichnung.8

Art 3: „Keine Rückmeldung“ und Warteketten. Wie in „Was „Keine Rückmeldung“ wirklich ist“ geschrieben, ist ein Hänger eine Frage, wer auf wen wartet. Eine Trace wächst im Leerlauf nicht,4 sodass die Wartezeit eines Threads, der im Kernel wartet, nicht als Anweisungen erscheint. Was erscheint, ist der Aufruf unmittelbar vor dem Eintritt in das Warten und die Anweisungen, nachdem er zurückgekehrt ist. Betrachten Sie die Position jedes Threads mit !positions,17 und reihen Sie „welcher Thread hat wann worauf zu warten begonnen“ aus dem SystemTimeStart / SystemTimeEnd von TTD.Calls("kernelbase!WaitForSingleObject") und dergleichen auf; abgeglichen mit den Log-Zeitstempeln lässt sich so die Wartekette rekonstruieren.12 „Reihenfolgeabhängige Fehler“ wie DllMain und die Ladersperre oder Scheinwecken von Bedingungsvariablen sind ein Bereich, in dem TTD, das die Reihenfolge selbst aufzeichnet, gut passt.

9. TTD mit .NET-Apps

Die offizielle TTD-Dokumentation stellt fest, dass verwalteter Code mit TTD in WinDbg unter Verwendung der SOS-Erweiterung (sos.dll) im 64-Bit-Modus debuggt werden kann.1 Das Laden ist dasselbe wie in Kapitel 3 des SOS-Analyseartikels (.loadby sos coreclr oder automatisches Laden), und !clrstack und !pe funktionieren an jeder Position der Trace. Der Grundweg ist, mit TTD.Events zur Position der Ausnahme zu gehen und dann den verwalteten Stapel mit !clrstack zu lesen.

Einrichtung zum Lesen einer TTD-Trace einer .NET-AppDie TTD-Trace in WinDbg öffnen, die 64-Bit-SOS-Erweiterung laden, zur Position des Ausnahmeereignisses gehen und den verwalteten Zustand mit !clrstack und !pe lesen. TTD.Calls für Aufrufe über native Grenzen verwendenTTD-Trace (.run)WinDbgSOS-Erweiterung (64-Bit)Mit TTD.Events zur Ausnahmeposition gehenMit !clrstack / !pe lesenTTD.Calls für Aufrufe über native Grenzen

Abbildung 24: Verwalteter Zustand über SOS, Aufrufsuchen an nativen Grenzen. Trennen Sie die Rollen, und Sie verirren sich nicht.

Zwei Vorbehalte. Erstens: Was Microsoft zusichert, ist „SOS im 64-Bit-Modus“. Über für x86 gebaute .NET-Apps steht nichts, also führen Sie das Untersuchungsziel wenn möglich als x64 aus. Zweitens stützt sich TTD.Calls auf PDB-Symbolinformationen.12 Verlassen Sie sich nicht darauf, JIT-kompilierte verwaltete Methoden nach Namen zu suchen; die zuverlässige Nutzung ist, Aufrufe über native Grenzen wie P/Invoke-Ziele in nativen DLLs, COM und Win32-APIs zu verfolgen. Probleme des verwalteten Heaps wie in „GC-Wartezeit von einem Speicherleck in .NET unterscheiden“ werden zuerst mit dotnet-counters / dotnet-gcdump / !gcroot an einem Dump verfolgt, und TTD kommt in der Phase, in der eine native Grenze beteiligt ist.

10. Wahl zwischen Dumps, Logs, ETW und TTD

TTD ist kein Allheilmittel und ersetzt die vorhandenen Werkzeuge nicht. Hier steht es neben den in unseren Artikeln behandelten Werkzeugen.

Was Sie wissen wollen Zuerst zu verwendendes Werkzeug Wann TTD dazukommt
Der Zustand im Moment des Absturzes Absturz-Dump (WER LocalDumps / ProcDump) Wenn der Dump „es ist zerstört“ zeigt, aber nicht, wer es zerstört hat
Welche Datei oder welcher Registrierungsschlüssel fehlgeschlagen ist Process Monitor Wenn Sie auch brauchen, wie die an die fehlschlagende API übergebenen Argumente gebaut wurden
Leistungsaufnahme des ganzen PCs über lange Dauer WPR/WPA, PerfView (ETW) Wenn es ein Korrektheitsproblem statt Leistung ist und Sie die Reihenfolge auf Anweisungsebene brauchen
Die geschäftliche Ereignisfolge Die Logs der App Wenn es auf einem Codepfad ohne Logging geschehen ist (TTD zeichnet alles ohne vorherige Codeänderungen auf1)
Wer diesen Wert geschrieben hat, die Reihenfolge der Aufrufe TTD
Wahl einer UntersuchungsmethodeZuerst den Zustand mit leichten Dumps und Logs erfassen, fehlschlagende APIs mit ProcMon und Leistung mit ETW untersuchen, und nur dann zu TTD übergehen, wenn Sie noch brauchen, wer was wann übergeben hatJaNeinSymptomDen Zustand mit Dumps und Logs erfassen (leicht)Fehlschlagende APIs: ProcMonLeistung: WPR/WPA, PerfViewBrauchen Sie wer, wann und was übergeben wurde?Den Aufzeichnungsumfang entwerfen und mit TTD aufzeichnenMit leichten Werkzeugen erledigt

Abbildung 25: Erfassen Sie zuerst das „Ergebnis“ mit leichten Werkzeugen, und holen Sie TTD nur für die Fälle heraus, in denen sich der „Pfad“ als nötig erweist.

Die Reihenfolge ist „zuerst das Leichteste“. Dumps und Logs kosten fast nichts zu erfassen, also halten Sie sie dauerhaft bereit; holen Sie TTD nach dem Entwurf von Kapitel 5 für die Fälle heraus, in denen das Lesen des Dumps gezeigt hat, dass der Pfad nötig ist. Angesichts des Aufwands von TTD und der Tatsache, dass Traces vertrauliche Informationen enthalten, gibt es keinen Grund, diese Reihenfolge umzukehren.

11. Betriebliche Hinweise

  • Behandeln Sie Traces als vertrauliche Dateien. Eine Aufzeichnung enthält Speicherinhalte und kann personenbezogene und sicherheitsbezogene Informationen enthalten, etwa Dateipfade, Registrierungsdaten und den Inhalt von Speicher und Dateien.12 Wenn Sie in einer Kundenumgebung aufzeichnen, verstehen Sie im Voraus, was enthalten sein kann (Verbindungszeichenfolgen, Token, Kundendaten), und legen Sie einen verschlüsselten Übertragungsweg, einen Speicherort und eine Aufbewahrungsfrist fest.
  • Teilen Sie nur die .run-Datei. Die .idx-Datei ist etwa so groß wie die .run-Datei und wird automatisch erzeugt, wenn WinDbg sie öffnet. .run-Dateien komprimieren sich gut. Wenn Sie einen Fehler in TTD selbst melden, hängen Sie auch die .out-Datei an.2
  • Halten Sie Versionen abgestimmt. TTD wird zusammen mit WinDbg weiter aktualisiert; 1.11.611 enthält eine Korrektur für Aufzeichnungsabstürze in Programmen, die AVX/AVX512 verwenden, und eine Änderung des Indexformats.11 Eine alte Version auf der Aufzeichnungs- oder der Wiedergabeseite zu mischen bedeutet Neuindizierung oder Neuaufzeichnung.
  • Prüfen Sie -replayCpuSupport, wenn Sie auf einer anderen CPU wiedergeben. Der Standard bevorzugt Übertragbarkeit, und MostConservative ist für Fälle vorgesehen, in denen sich die Aufzeichnungs-CPU und die Wiedergabe-CPU unterscheiden (etwa die Wiedergabe einer Intel-Trace auf arm64). Umgekehrt können Sie, wenn Sie wissen, dass die Wiedergabe-CPU gleich oder besser ist, eine kleinere, schnellere Aufzeichnung wählen.2
  • Windows Server lässt sich ebenfalls aufzeichnen. TTD.exe unterstützt Windows Server 2016/2019/2022/2025.2
  • Eine fehlschlagende Aufzeichnung eingrenzen. Versuchen Sie zuerst, ob ping.exe oder cmd.exe aufgezeichnet werden können; wenn nicht, verdächtigen Sie einen Konflikt mit aufdringlicher Software wie Antivirus oder Anwendungsvirtualisierung.8
Verfahren für den Umgang mit TracesWeil eine aufgezeichnete Trace Speicherinhalte enthält, verstehen, welche Informationen sie enthalten kann, nur die .run-Datei komprimieren und über einen verschlüsselten Kanal übergeben, Speicherort und Aufbewahrungsfrist festlegen und auf der Analyseseite mit derselben WinDbg-Version öffnen und den Index bauenAufzeichnung abgeschlossen (.run / .idx / .out)Verstehen, welche Informationen sie enthalten kannNur .run komprimieren, verschlüsseln und übergebenSpeicherort und Aufbewahrungsfrist festlegenMit derselben WinDbg-Version öffnen und .idx erzeugen

Abbildung 26: Eine Trace zu übergeben heißt, eine vertrauliche Datei zu übergeben. Legen Sie das Verfahren fest, bevor Sie aufzeichnen.

12. Zusammenfassung

  • Ein Dump ist „Zustand“; TTD ist der „Pfad“. In Fällen, die den Ursprung eines zerstörten Werts oder die Reihenfolge von Aufrufen brauchen, macht TTD die Untersuchung zur mechanischen Arbeit
  • Die Aufzeichnung ist 5- bis 20-mal langsamer, wächst 5 bis 50 MB pro Sekunde und lässt sich nicht lösen. Für langlaufende Apps entwerfen Sie zuerst den Aufzeichnungsumfang mit -ring/-maxFile, -module, -recordmode Manual und -monitor
  • Die Wiedergabe ist „über ein Ereignis einsteigen, rückwärts gehen“: TTD.Events, dann [Time Travel], dann t-/g-, und ba + g-, damit der Debugger „wer hat es zerstört?“ beantwortet
  • TTD.Calls und TTD.Memory sind Abfragen über die ganze Trace. Gleichen Sie mit ToSystemTime() und SystemTimeStart gegen Log-Zeitstempel ab
  • Für .NET lesen Sie den Zustand mit 64-Bit-SOS und verwenden Sie TTD.Calls an nativen Grenzen
  • Eine Trace ist eine vertrauliche Datei. Teilen Sie nur die .run-Datei und legen Sie Kanal und Speicherung zuerst fest

Fälle, in denen ein Dump erfasst wurde, die Ursache aber außer Reichweite ist, oder in denen die Arbeit stockt, weil der Fehler sich nicht reproduziert, werden recht oft mit TTD erledigt, sobald der Aufzeichnungsumfang entworfen werden kann. Wir können auch alles vom Aufzeichnungsentwurf bis zur Analyse der .run-Datei übernehmen; wenden Sie sich gern mit Ihren Dumps und Logs an uns.

Weiterführende Artikel

Zugehörige Beratungsfelder

KomuraSoft LLC übernimmt Ursachenuntersuchungen von Windows-App-Fehlern, die erst nach langem Lauf oder intermittierend erscheinen, unter Kombination von Absturz-Dumps, Logs und TTD-Traces; den Aufbau einer Untersuchungsaufstellung einschließlich des Entwurfs des Aufzeichnungsumfangs; und die Eingrenzung von Störungen, die native Grenzen (COM, P/Invoke, Geräte-SDKs) einbeziehen. Wenden Sie sich schon in der Phase „wir haben einen Dump, erreichen die Ursache aber nicht“ an uns.

Quellen

  1. Microsoft Learn, Time Travel Debugging - Overview. Dazu, dass TTD die Ausführung eines Prozesses aufzeichnet und sie vorwärts und rückwärts wiedergeben lässt, dass Dumps den Zustand und den Ausführungspfad, die zum Fehler geführt haben, oft verfehlen, dass die Aufzeichnung Administratorrechte erfordert, dass Aufzeichnungen personenbezogene und sicherheitsbezogene Informationen enthalten können, zur Vergleichstabelle der Untersuchungsmethoden, zu den Rollen von .run/.idx und zum Debuggen verwalteten Codes mit der SOS-Erweiterung im 64-Bit-Modus.  2 3 4 5 6 7 8 9 10

  2. Microsoft Learn, Time Travel Debugging - TTD.exe command line utility. Zur 5- bis 20-fachen oder stärkeren Verlangsamung, dazu, dass es sich nach dem Anhängen nicht selbst lösen kann, zur Unterstützung von Windows Server 2016 bis 2025, zu Installation und Offline-Bereitstellung, zu den drei Modi -launch/-attach/-monitor, zu den Optionen -out/-noUI/-accepteula/-stop/-wait/-tracingOff/-children/-cmdLineFilter/-timestampFilename/-ring/-maxFile/-maxConcurrentRecordings/-numVCpu/-replayCpuSupport/-module/-recordmode, zum Lesen der .out-Datei und zu Ratschlägen zum Teilen von 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

  3. Microsoft Learn, Time Travel Debugging - Overview - Things to look out for. Zur Unverträglichkeit mit Antivirus- und Speicherüberwachungssoftware und Electron, dazu, dass nur der Benutzermodus möglich ist, dass die Wiedergabe schreibgeschützt ist, dass eine Injektion in geschützte Prozesse (PPL) nicht möglich ist, und zur etwa 10- bis 20-fachen Leistungsauswirkung während der Aufzeichnung.  2 3 4 5 6 7

  4. Microsoft Learn, Time Travel Debugging - Working with Trace Files. Zu den Faktoren der Trace-Größe (ein Bit bis ein Byte pro Anweisung), zum Wachstum von 5 bis 50 MB pro Sekunde bei Aktivität und keinem im Leerlauf, zum Fehlen einer maximalen Größenobergrenze, dazu, dass der Index 1- bis 2-mal so groß wie die Trace ist, und zum Verhalten von Aufzeichnung und Indizierung bei voller Festplatte samt Workaround.  2 3 4 5 6 7 8 9

  5. Microsoft Learn, Time Travel Debugging - Sample App Walkthrough. Zum allgemeinen Verfahren, zur Position eines Ausnahmeereignisses zu gehen und mit ba und g- zur Position zurückzugehen, an der ein ungültiger Wert zuletzt geschrieben wurde, dazu, dass der Fehlerpunkt oft in der Fehlerbehandlung mehrere Schritte hinter der eigentlichen Ursache liegt, dazu, dass die Trace bei einem Absturz geschlossen und von WinDbg automatisch indiziert wird, und zur Nutzung von TTD.Memory und .Last() 2 3 4 5 6 7

  6. Microsoft Learn, !tt (time travel). Zur Positionsangabe für !tt (Prozent oder xx:yy), zur Bedeutung der zwei Bestandteile einer Position (Sequenznummer und Schrittzahl) und zu TTD.PrevRegisterWrite/PrevMemoryAccess/NextMemoryAccess 2 3 4

  7. Microsoft Learn, TTD Position Objects. Zu den Eigenschaften Percent/Sequence/Steps des Position-Objekts, zu SeekTo(), dazu, dass ToSystemTime() die ungefähre Wanduhrzeit (UTC) zurückgibt, und dazu, dass FFFFFFFFFFFFFFFE:0 das Ende der Trace bezeichnet.  2

  8. Microsoft Learn, Time Travel Debugging - Troubleshooting. Dazu, dass eine Erhöhung erforderlich ist, dass die Startaufzeichnung von UWP-Apps nicht unterstützt wird, dass „ungewöhnliche Prozesse“ in einer anderen Sitzung oder einem anderen Sicherheitskontext außerhalb des Rahmens liegen, zur Eingrenzung mit ping.exe/cmd.exe, dazu, dass die Wiedergabe langsamer wird, wenn Application Verifier gleichzeitig verwendet wird, und zum Neubauen des Index mit !index -status/!index -force 2 3 4 5

  9. Microsoft Learn, Time Travel Debugging - Record a trace. Zur Aufzeichnung aus Launch executable (advanced) / Attach to process in der WinDbg-Oberfläche, zum Kontrollkästchen Record with Time Travel Debugging, zum Festlegen des Speicherorts mit Configure and Record und zum Einschränken von Modulen mit Record subset of execution

  10. Microsoft, WinDbg-Samples - TTD in-process recording API (GitHub). Zur Dokumentation der In-Prozess-Aufzeichnungs-API, die zusammen mit -recordmode Manual dem Programm die Steuerung von Start und Stopp der Aufzeichnung überlässt. 

  11. Microsoft Learn, Time travel debugging release notes. Zur Aufzeichnungskorrektur für Programme, die AVX/AVX512 verwenden, und zur Änderung des Indexformats (Neuindizierung erforderlich) in 1.11.611 sowie zu @$curframe.TTD.VariableHistory(), hinzugefügt in 1.11.553.  2 3

  12. Microsoft Learn, TTD Calls Objects. Zu den Argumenten von TTD.Calls, zu den Eigenschaften ThreadId/UniqueThreadId/Function/ReturnValue/ReturnAddress/Parameters[]/TimeStart/TimeEnd/SystemTimeStart/SystemTimeEnd, zu den Standardwerten, wenn PDB-Symbolinformationen fehlen (vier 64-Bit-Ganzzahlargumente ohne Vorzeichen, UnknownOrMissingSymbols), und dazu, dass die Berechnung Zeit braucht und Ergebnisse zwischengespeichert werden.  2 3 4 5 6 7

  13. Microsoft Learn, Time Travel Debugging - Replay a trace. Zur Rückwärtsausführung mit p-/t-/g-, dazu, dass g- bei denselben Ereignissen stoppt wie die Vorwärtsausführung, zu !positions und dazu, dass ~s die Position in der Trace nicht ändert.  2 3

  14. Microsoft Learn, TTD Event Objects. Zu den Ereignistypen (ThreadCreated/ThreadTerminated/ModuleLoaded/ModuleUnloaded/Exception) und den Kindobjekten Position, Module, Thread und Exception. 

  15. Microsoft Learn, TTD Exception Objects. Zu Type (Software/Hardware), ProgramCounter, Code, Flags und Position des Ausnahmeobjekts. 

  16. Microsoft Learn, WinDbg: Timelines. Dazu, dass das Fenster Timelines Ausnahmen, Haltepunkte, Speicherzugriffe und Funktionsaufrufe visualisiert und ein Doppelklick auf eine Ausnahme Position.SeekTo() auslöst. 

  17. Microsoft Learn, !positions. Zur Anzeige jedes aktiven Threads und der Position jedes Threads in der Trace.  2

  18. Microsoft Learn, Introduction to Time Travel Debugging objects. Zu den Objekten @$curprocess.TTD / @$cursession.TTD, zu Abfragen mit OrderBy/Where/Select/GroupBy, zu den Beispielen, GetLastError-Fehler zu aggregieren und den letzten Aufruf von MessageBoxW zu finden, zur Bedeutung von UnknownOrMissingSymbols und zu den vier Gründen, warum Calls nichts liefert.  2 3 4 5

  19. Microsoft Learn, TTD Memory Objects. Zu den Zugriffstypen von TTD.Memory (r/w/rw/e/rwe/ec) und zum Gehen zu einer Position über den Link [Time Travel] in den Ergebnissen. 

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Worin unterscheiden sich Time Travel Debugging (TTD) und ein Absturz-Dump?
Ein Absturz-Dump ist eine Fotografie des Speichers im Moment des Absturzes: Er zeigt den Zustand in diesem Augenblick, aber nicht den Weg, der dorthin geführt hat. Eine TTD-Trace ist eine vollständige Aufzeichnung der Befehlsausführung des Prozesses, die sich später vorwärts und rückwärts wiedergeben lässt, sodass Sie zurückspulen und direkt prüfen können, wer eine Variable zuletzt geschrieben hat oder was unmittelbar vor einer Ausnahme aufgerufen wurde. Die offizielle Dokumentation von Microsoft stellt außerdem fest, dass Dumps den Zustand und den Ausführungspfad, die zum Fehler geführt haben, oft verfehlen. Der Preis ist, dass der Prozess während der Aufzeichnung 5- bis 20-mal langsamer läuft und die Trace-Datei bei aktivem Prozess um etwa 5 bis 50 MB pro Sekunde wächst.
Kann ich TTD an einer Produktions-App belassen, die tagelang läuft?
Nicht so, wie sie ist. TTD verlangsamt den Prozess während der Aufzeichnung erheblich, die Trace wächst in einem aktiven Prozess um 5 bis 50 MB pro Sekunde, und es gibt keine Obergrenze für die Dateigröße. Der Einsatz an einer langlaufenden App setzt einen Aufzeichnungsentwurf voraus: den Ringpuffer von TTD.exe (-ring) mit -maxFile, um nur die letzten N MB zu behalten, -module, um nur aufzuzeichnen, während das eigene Modul läuft, oder -recordmode Manual, damit die App das Aufzeichnungsintervall wählt. Außerdem kann TTD sich nach dem Anhängen nicht selbst lösen, sodass Sie im Voraus festlegen müssen, wie Sie die Aufzeichnung beenden, was die Beendigung (den Neustart) des Prozesses bedeutet.
Lassen sich Dienste und Prozesse in anderen Sitzungen aufzeichnen?
Die Dokumentation von TTD.exe beschreibt -attach als für die Untersuchung von Diensten und langlaufenden Apps gedacht und -monitor als Aufzeichnung bei jedem Start eines Programms oder Dienstes. Andererseits stellt die Seite zur Fehlerbehebung fest, dass ungewöhnliche Prozesse, die in einer anderen Sitzung oder einem anderen Sicherheitskontext laufen, derzeit nicht für die Aufzeichnung unterstützt werden. Ob es tatsächlich funktioniert, hängt von der Umgebung ab; zeichnen Sie daher zuerst einen einfachen Prozess wie ping.exe oder cmd.exe in derselben Konfiguration wie in der Produktion auf, versuchen Sie dann den Zielprozess und bauen Sie es erst danach in den Betrieb ein.
Lässt sich TTD auf Traces von .NET-Apps verwenden?
Ja. Die offizielle Dokumentation stellt fest, dass die SOS-Erweiterung (sos.dll) im 64-Bit-Modus auf einer TTD-Trace in WinDbg verwendet werden kann, um verwalteten Code zu debuggen. Das Grundmuster ist, SOS-Befehle wie !clrstack und !pe an jeder Position der Trace auszuführen, zur Position eines Ausnahmeereignisses zu gehen und dann den verwalteten Stapel zu lesen. Die TTD.Calls-Abfrage, die Aufrufe nach Symbolnamen sucht, stützt sich auf PDB-Symbolinformationen; in .NET-Apps ist die zuverlässige Nutzung daher, Aufrufe über native Grenzen wie P/Invoke, COM und Win32-APIs zu verfolgen.
Ist es unbedenklich, eine Trace-Datei (.run) an ein anderes Unternehmen oder nach außen zu senden?
Nicht so, wie sie ist. Eine TTD-Aufzeichnung enthält die Speicherinhalte des Prozesses, und die offizielle Dokumentation stellt ausdrücklich fest, dass sie personenbezogene oder vertrauliche Informationen enthalten kann, etwa Dateipfade, Registrierungsdaten und den Inhalt von Speicher und Dateien. Wenn Sie eine senden, verstehen Sie zuerst, was aufgezeichnet wurde (Verbindungszeichenfolgen, Token, Kundendaten und so weiter), und legen Sie dann einen verschlüsselten Übertragungsweg und einen Speicherort fest. Es reicht, nur die .run-Datei zu teilen; die Indexdatei (.idx) wird automatisch erzeugt, wenn WinDbg sie öffnet.
TTD.Calls liefert nichts, wenn ich nach einer Funktion suche. Warum?
Es gibt vier Hauptursachen. Erstens Symbole: Funktionen in einem Modul ohne PDB heißen UnknownOrMissingSymbols, und der Modulname kann in Großbuchstaben stehen; prüfen Sie den tatsächlichen Symbolnamen mit dem Befehl x. Zweitens ist die Zieldll an dieser Position möglicherweise noch nicht geladen; gehen Sie zu einer Position nach dem Laden der DLL und fragen Sie erneut ab. Drittens kann die Abfrage-Engine die Funktion nicht verfolgen, wenn sie inline expandiert ist. Viertens ist der Platzhalter vielleicht zu weit und trifft zu viele Funktionen; engen Sie das Muster ein.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog