Einführung in Objekte zum Debuggen von Zeitreisen
In diesem Abschnitt wird beschrieben, wie das Datenmodell für die Abfrage von Ablaufverfolgungen für Zeitreisen verwendet werden kann. Dies kann ein leistungsfähiges Tool sein, um Fragen wie diese über den Code zu beantworten, der in einer Ablaufverfolgung für Zeitreisen erfasst wird.
- Welche Ausnahmen sind in der Ablaufverfolgung enthalten?
- Zu welchem Zeitpunkt in der Ablaufverfolgung wurde ein bestimmtes Code-Modul geladen?
- Wann wurden Threads in der Ablaufverfolgung erstellt/beendet?
- Welches sind die am längsten laufenden Threads in der Ablaufverfolgung?
Es gibt TTD-Erweiterungen, die Daten zu den Datenmodellobjekten Session und Process hinzufügen. Auf die TTD-Datenmodellobjekte kann über den Befehl dx (Display Debugger Object Model Expression), die Modellfenster von WinDbg, JavaScript und C++ zugegriffen werden. Die TTD-Erweiterungen werden beim Debuggen einer Ablaufverfolgung von Zeitreisen automatisch geladen.
Prozess-Objekte
Die primären Objekte, die zu Process-Objekten hinzugefügt werden, befinden sich im TTD-Namespace von jedem Process-Objekt. Beispiel: $@curprocess.TTD
.
:000> dx @$curprocess.TTD
@$curprocess.TTD
Threads
Events
Lifetime : [26:0, 464232:0]
SetPosition [Sets the debugger to point to the given position on this process.]
Allgemeine Informationen zur Arbeit mit LINQ-Abfragen und Debugger-Objekten finden Sie unter Verwendung von LINQ mit den Debugger-Objekten.
Eigenschaften
Object | Beschreibung |
---|---|
Lebensdauer | Ein TTD-Bereichsobjekt, das die Lebensdauer der gesamten Ablaufverfolgung beschreibt. |
Threads | Enthält eine Sammlung von TTD-Thread-Objekten, eines für jeden Thread während der gesamten Lebensdauer der Ablaufverfolgung. |
Ereignisse | Enthält eine Sammlung von TTD-Ereignisobjekten, eines für jedes Ereignis in der Ablaufverfolgung. |
Methoden
Methode | Beschreibung |
---|---|
SetPosition() | Nimmt eine Ganzzahl zwischen 0 und 100 oder eine Zeichenfolge im N:N-Format als Eingabe und springt in der Ablaufverfolgung an diese Stelle. Siehe !tt für weitere Informationen. |
Session-Objekte
Die primären Objekte, die zu Session-Objekten hinzugefügt werden, befinden sich im TTD-Namespace von jedem Session-Objekt. Beispiel: $@cursession.TTD
.
0:000> dx @$cursession.TTD
@$cursession.TTD : [object Object]
Calls [Returns call information from the trace for the specified set of methods: TTD.Calls("module!method1", "module!method2", ...) For example: dx @$cursession.TTD.Calls("user32!SendMessageA")]
Memory [Returns memory access information for specified address range: TTD.Memory(startAddress, endAddress [, "rwec"])]
DefaultParameterCount : 0x4
AsyncQueryEnabled : false
Resources
Data : Normalized data sources based on the contents of the time travel trace
Utility : Methods that can be useful when analyzing time travel traces
Hinweis
Es gibt einige von TTDAnalyze hinzugefügte Objekte und Methoden, die für interne Funktionen der Erweiterung verwendet werden. Nicht alle Namespaces sind dokumentiert, und die aktuellen Namespaces werden sich im Laufe der Zeit weiterentwickeln.
Methoden
Methode | Beschreibung |
---|---|
Data.Heap() | Eine Sammlung von Heap-Objekten, die während der Ablaufverfolgung zugewiesen wurden. Beachten Sie, dass es sich um eine Funktion handelt, die Berechnungen durchführt und daher eine gewisse Zeit in Anspruch nimmt. |
Calls() | Gibt eine Sammlung von calls objects zurück, die mit der Eingabezeichenkette übereinstimmen. Die Eingabezeichenfolge kann Platzhalter enthalten. Beachten Sie, dass es sich um eine Funktion handelt, die Berechnungen durchführt und daher eine gewisse Zeit in Anspruch nimmt. |
Memory() | Diese Methode nimmt die Parameter beginAddress, endAddress und dataAccessMask entgegen und gibt eine Sammlung von Speicherobjekten zurück. Beachten Sie, dass es sich um eine Funktion handelt, die Berechnungen durchführt und daher eine gewisse Zeit in Anspruch nimmt. |
Sortieren der Abfrageausgabe
Verwenden Sie die Methode OrderBy(), um die von der Abfrage zurückgegebenen Zeilen nach einer oder mehreren Spalten zu sortieren. Dieses Beispiel sortiert nach TimeStart in aufsteigender Reihenfolge.
0:000> dx -r2 @$cursession.TTD.Calls("kernelbase!GetLastError").OrderBy(c => c.TimeStart)
@$cursession.TTD.Calls("kernelbase!GetLastError").OrderBy(c => c.TimeStart)
[0xb]
EventType : Call
ThreadId : 0x3a10
UniqueThreadId : 0x2
TimeStart : 39:2DC [Time Travel]
TimeEnd : 39:2DF [Time Travel]
Function : UnknownOrMissingSymbols
FunctionAddress : 0x7561ccc0
ReturnAddress : 0x7593d24c
ReturnValue : 0x0
Parameters
[0xe]
EventType : Call
ThreadId : 0x3a10
UniqueThreadId : 0x2
TimeStart : AF:36 [Time Travel]
TimeEnd : AF:39 [Time Travel]
Function : UnknownOrMissingSymbols
FunctionAddress : 0x7561ccc0
ReturnAddress : 0x4723ef
ReturnValue : 0x0
Parameters
Um eine zusätzliche Tiefe der Datenmodellobjekte anzuzeigen, wird die Option -r2 Rekursionsebene verwendet. Weitere Informationen zu den Optionen des dx-Befehls finden Sie unter dx (Display Debugger Object Model Expression).
Dieses Beispiel sortiert nach TimeStart in absteigender Reihenfolge.
0:000> dx -r2 @$cursession.TTD.Calls("kernelbase!GetLastError").OrderByDescending(c => c.TimeStart)
@$cursession.TTD.Calls("kernelbase!GetLastError").OrderByDescending(c => c.TimeStart)
[0x1896]
EventType : Call
ThreadId : 0x3a10
UniqueThreadId : 0x2
TimeStart : 464224:34 [Time Travel]
TimeEnd : 464224:37 [Time Travel]
Function : UnknownOrMissingSymbols
FunctionAddress : 0x7561ccc0
ReturnAddress : 0x7594781c
ReturnValue : 0x0
Parameters
[0x18a0]
EventType : Call
ThreadId : 0x3a10
UniqueThreadId : 0x2
TimeStart : 464223:21 [Time Travel]
TimeEnd : 464223:24 [Time Travel]
Function : UnknownOrMissingSymbols
FunctionAddress : 0x7561ccc0
ReturnAddress : 0x7594781c
ReturnValue : 0x0
Parameters
Angabe von Elementen in einer Abfrage
Um ein bestimmtes Element auszuwählen, können der Abfrage eine Reihe von Qualifizierern hinzugefügt werden. Die Abfrage zeigt zum Beispiel den ersten Aufruf an, der „kernelbase!GetLastError“ enthält.
0:000> dx @$cursession.TTD.Calls("kernelbase!GetLastError").First()
@$cursession.TTD.Calls("kernelbase!GetLastError").First()
EventType : Call
ThreadId : 0x3a10
UniqueThreadId : 0x2
TimeStart : 77A:9 [Time Travel]
TimeEnd : 77A:C [Time Travel]
Function : UnknownOrMissingSymbols
FunctionAddress : 0x7561ccc0
ReturnAddress : 0x6cf12406
ReturnValue : 0x0
Parameters
Filterung in einer Abfrage
Verwenden Sie die Select()-Methode, um auszuwählen, welche Spalten angezeigt werden sollen, und um den Namen der Spaltenanzeige zu ändern.
Dieses Beispiel gibt Zeilen zurück, in denen ReturnValue ungleich Null ist, und wählt die Anzeige der Spalten TimeStart und ReturnValue mit den benutzerdefinierten Anzeigenamen Time und Error aus.
0:000> dx -r2 @$cursession.TTD.Calls("kernelbase!GetLastError").Where(c => c.ReturnValue != 0).Select(c => new { Time = c.TimeStart, Error = c.ReturnValue })
@$cursession.TTD.Calls("kernelbase!GetLastError").Where(c => c.ReturnValue != 0).Select(c => new { Time = c.TimeStart, Error = c.ReturnValue })
[0x13]
Time : 3C64A:834 [Time Travel]
Error : 0x36b7
[0x1c]
Time : 3B3E7:D6 [Time Travel]
Error : 0x3f0
[0x1d]
Time : 3C666:857 [Time Travel]
Error : 0x36b7
[0x20]
Time : 3C67E:12D [Time Travel]
Gruppierung
Verwenden Sie die Methode GroupBy(), um die von der Abfrage zurückgegebenen Daten zu gruppieren und so eine Analyse mit strukturierten Ergebnissen durchzuführen.
0:000> dx -r2 @$cursession.TTD.Calls("kernelbase!GetLastError").Where(c => c.ReturnValue != 0).Select(c => new { Time = c.TimeStart, Error = c.ReturnValue }).GroupBy(x => x.Error)
@$s = @$cursession.TTD.Calls("kernelbase!GetLastError").Where(c => c.ReturnValue != 0).Select(c => new { Time = c.TimeStart, Error = c.ReturnValue }).GroupBy(x => x.Error)
[0x36b7]
[0x0]
[0x1]
[0x2]
[0x3]
[...]
[0x3f0]
[0x0]
[0x1]
[0x2]
[0x3]
...
Zuweisung des Ergebnisses einer Abfrage an eine Variable
Verwenden Sie diese Syntax, um das Ergebnis einer Abfrage einer Variablen zuzuweisen dx $@var = <expression>
In diesem Beispiel werden die Ergebnisse einer Abfrage myResults zugewiesen
dx -r2 @$myResults = @$cursession.TTD.Calls("kernelbase!GetLastError").Where(c => c.ReturnValue != 0).Select(c => new { Time = c.TimeStart, Error = c.ReturnValue })
Verwenden Sie den Befehl dx, um die neu erstellte Variable mit der Option -g grid anzuzeigen. Weitere Informationen zu den Optionen des dx-Befehls finden Sie unter dx (Display Debugger Object Model Expression).
0:000> dx -g @$myResults
========================================
= = (+) Time = (+) Error =
========================================
= [0x13] - 3C64A:834 - 0x36b7 =
= [0x1c] - 3B3E7:D6 - 0x3f0 =
= [0x1d] - 3C666:857 - 0x36b7 =
= [0x20] - 3C67E:12D - 0x2 =
= [0x21] - 3C6F1:127 - 0x2 =
= [0x23] - 3A547:D6 - 0x3f0 =
= [0x24] - 3A59B:D0 - 0x3f0 =
Beispiele
Abfrage nach Ausnahmen
Diese LINQ-Abfrage verwendet das TTD.Event-Objekt, um alle Ausnahmen in der Ablaufverfolgung anzuzeigen.
0:000> dx @$curprocess.TTD.Events.Where(t => t.Type == "Exception").Select(e => e.Exception)
@$curprocess.TTD.Events.Where(t => t.Type == "Exception").Select(e => e.Exception)
[0x0] : Exception 0x000006BA of type Software at PC: 0X777F51D0
[0x1] : Exception 0x000006BA of type Software at PC: 0X777F51D0
[0x2] : Exception 0xE06D7363 of type CPlusPlus at PC: 0X777F51D0
Abfrage nach bestimmten API-Aufrufen
Verwenden Sie das Objekt TTD.Calls, um nach bestimmten API-Aufrufen zu suchen. In diesem Beispiel ist ein Fehler beim Aufruf von user32!MessageBoxW aufgetreten, der Windows-API zum Anzeigen einer Messagebox. Wir listen alle Aufrufe von MessageBoxW auf, ordnen sie nach der Startzeit der Funktion und wählen dann den letzten Aufruf aus.
0:000> dx @$cursession.TTD.Calls("user32!MessageBoxW").OrderBy(c => c.TimeStart).Last()
@$cursession.TTD.Calls("user32!MessageBoxW").OrderBy(c => c.TimeStart).Last()
EventType : Call
ThreadId : 0x3a10
UniqueThreadId : 0x2
TimeStart : 458310:539 [Time Travel]
TimeEnd : 45C648:61 [Time Travel]
Function : UnknownOrMissingSymbols
FunctionAddress : 0x750823a0
ReturnAddress : 0x40cb93
ReturnValue : 0x10a7000000000001
Parameters
Abfrage des Ladevorgangs für ein bestimmtes Modul
Verwenden Sie zunächst den Befehl lm (List Loaded Modules), um die geladenen Module anzuzeigen.
0:000> lm
start end module name
012b0000 012cf000 CDog_Console (deferred)
11570000 1158c000 VCRUNTIME140D (deferred)
11860000 119d1000 ucrtbased (deferred)
119e0000 11b63000 TTDRecordCPU (deferred)
11b70000 11cb1000 TTDWriter (deferred)
73770000 73803000 apphelp (deferred)
73ea0000 74062000 KERNELBASE (deferred)
75900000 759d0000 KERNEL32 (deferred)
77070000 771fe000 ntdll (private pdb symbols)
Verwenden Sie dann den folgenden dx-Befehl, um zu sehen, an welcher Position in der Ablaufverfolgung ein bestimmtes Modul geladen wurde, z. B. ntdll.
dx @$curprocess.TTD.Events.Where(t => t.Type == "ModuleLoaded").Where(t => t.Module.Name.Contains("ntdll.dll"))
@$curprocess.TTD.Events.Where(t => t.Type == "ModuleLoaded").Where(t => t.Module.Name.Contains("ntdll.dll"))
[0x0] : Module Loaded at position: A:0
Diese LINQ-Abfrage zeigt das/die Ladeereignis(e) eines bestimmten Moduls an.
0:000> dx @$curprocess.TTD.Events.Where(t => t.Type == "ModuleUnloaded").Where(t => t.Module.Name.Contains("ntdll.dll"))
@$curprocess.TTD.Events.Where(t => t.Type == "ModuleUnloaded").Where(t => t.Module.Name.Contains("ntdll.dll"))
[0x0] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0
Die Adresse FFFFFFFFFFFFFFFE:0 zeigt das Ende der Ablaufverfolgung an.
Abfrage nach allen Fehlerprüfungen in der Ablaufverfolgung
Verwenden Sie diesen Befehl, um alle Fehlerprüfungen in der Ablaufverfolgung nach Fehleranzahl zu sortieren.
0:000> 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
==================================================
= = (+) ErrorNumber = ErrorCount =
==================================================
= [1008] - 1008 - 8668 =
= [14007] - 14007 - 4304 =
= [2] - 2 - 1710 =
= [6] - 6 - 1151 =
= [1400] - 1400 - 385 =
= [87] - 87 - 383 =
Abfrage der Zeitposition in der Ablaufverfolgung, zu der Threads erstellt wurden
Verwenden Sie diesen dx-Befehl, um alle Ereignisse der Ablaufverfolgung im Rasterformat (-g) anzuzeigen.
0:000> dx -g @$curprocess.TTD.Events
==================================================================================================================================================================================================
= = (+) Type = (+) Position = (+) Module = (+) Thread =
==================================================================================================================================================================================================
= [0x0] : Module Loaded at position: 2:0 - ModuleLoaded - 2:0 - Module C:\Users\USER1\Documents\Visual Studio 2015\Proje... - =
= [0x1] : Module Loaded at position: 3:0 - ModuleLoaded - 3:0 - Module C:\WINDOWS\SYSTEM32\VCRUNTIME140D.dll at address 0... - =
= [0x2] : Module Loaded at position: 4:0 - ModuleLoaded - 4:0 - Module C:\WINDOWS\SYSTEM32\ucrtbased.dll at address 0X118... - =
= [0x3] : Module Loaded at position: 5:0 - ModuleLoaded - 5:0 - Module C:\Users\USER1\AppData\Local\Dbg\UI\Fast.20170907... - =
= [0x4] : Module Loaded at position: 6:0 - ModuleLoaded - 6:0 - Module C:\Users\USER1\AppData\Local\Dbg\UI\Fast.20170907... - =
= [0x5] : Module Loaded at position: 7:0 - ModuleLoaded - 7:0 - Module C:\WINDOWS\SYSTEM32\apphelp.dll at address 0X73770... - =
= [0x6] : Module Loaded at position: 8:0 - ModuleLoaded - 8:0 - Module C:\WINDOWS\System32\KERNELBASE.dll at address 0X73... - =
= [0x7] : Module Loaded at position: 9:0 - ModuleLoaded - 9:0 - Module C:\WINDOWS\System32\KERNEL32.DLL at address 0X7590... - =
= [0x8] : Module Loaded at position: A:0 - ModuleLoaded - A:0 - Module C:\WINDOWS\SYSTEM32\ntdll.dll at address 0X7707000... - =
= [0x9] : Thread created at D:0 - ThreadCreated - D:0 - - UID: 2, TID: 0x4C2C =
= [0xa] : Thread terminated at 64:0 - ThreadTerminated - 64:0 - - UID: 2, TID: 0x4C2C =
= [0xb] : Thread created at 69:0 - ThreadCreated - 69:0 - - UID: 3, TID: 0x4CFC =
= [0xc] : Thread created at 6A:0 - ThreadCreated - 6A:0 - - UID: 4, TID: 0x27B0 =
= [0xd] : Thread terminated at 89:0 - ThreadTerminated - 89:0 - - UID: 4, TID: 0x27B0 =
= [0xe] : Thread terminated at 8A:0 - ThreadTerminated - 8A:0 - - UID: 3, TID: 0x4CFC =
= [0xf] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\Users\USER1\Documents\Visual Studio 2015\Proje... - =
= [0x10] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\Users\USER1\AppData\Local\Dbg\UI\Fast.20170907... - =
= [0x11] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\WINDOWS\SYSTEM32\VCRUNTIME140D.dll at address 0... - =
= [0x12] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\Users\USER1\AppData\Local\Dbg\UI\Fast.20170907... - =
= [0x13] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\WINDOWS\SYSTEM32\ucrtbased.dll at address 0X118... - =
= [0x14] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\WINDOWS\SYSTEM32\apphelp.dll at address 0X73770... - =
= [0x15] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\WINDOWS\System32\KERNELBASE.dll at address 0X73... - =
= [0x16] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\WINDOWS\System32\KERNEL32.DLL at address 0X7590... - =
= [0x17] : Module Unloaded at position: FFFFFFFFFFFFFFFE:0 - ModuleUnloaded - FFFFFFFFFFFFFFFE:0 - Module C:\WINDOWS\SYSTEM32\ntdll.dll at address 0X7707000... - =
==================================================================================================================================================================================================
Wählen Sie eine der Spalten mit einem +-Zeichen aus, um die Ausgabe zu sortieren.
Verwenden Sie diese LINQ-Abfrage, um im Rasterformat die Zeitposition in der Ablaufverfolgung anzuzeigen, zu der Threads erstellt wurden (Typ == „ThreadCreated“).
dx -g @$curprocess.TTD.Events.Where(t => t.Type == "ThreadCreated").Select(t => t.Thread)
===========================================================================================================
= = (+) UniqueId = (+) Id = (+) Lifetime = (+) ActiveTime =
===========================================================================================================
= [0x0] : UID: 2, TID: 0x4C2C - 0x2 - 0x4c2c - [0:0, FFFFFFFFFFFFFFFE:0] - [D:0, 64:0] =
= [0x1] : UID: 3, TID: 0x4CFC - 0x3 - 0x4cfc - [0:0, 8A:0] - [69:0, 8A:0] =
= [0x2] : UID: 4, TID: 0x27B0 - 0x4 - 0x27b0 - [0:0, 89:0] - [6A:0, 89:0] =
===========================================================================================================
Verwenden Sie diese LINQ-Abfrage, um im Rasterformat die Zeitpositionen in der Ablaufverfolgung anzuzeigen, an denen Threads beendet wurden (Typ == „ThreadTerminated“).
0:000> dx -g @$curprocess.TTD.Events.Where(t => t.Type == "ThreadTerminated").Select(t => t.Thread)
===========================================================================================================
= = (+) UniqueId = (+) Id = (+) Lifetime = (+) ActiveTime =
===========================================================================================================
= [0x0] : UID: 2, TID: 0x4C2C - 0x2 - 0x4c2c - [0:0, FFFFFFFFFFFFFFFE:0] - [D:0, 64:0] =
= [0x1] : UID: 4, TID: 0x27B0 - 0x4 - 0x27b0 - [0:0, 89:0] - [6A:0, 89:0] =
= [0x2] : UID: 3, TID: 0x4CFC - 0x3 - 0x4cfc - [0:0, 8A:0] - [69:0, 8A:0] =
===========================================================================================================
Sortieren der Ausgabe zur Ermittlung der am längsten laufenden Threads
Verwenden Sie diese LINQ-Abfrage, um im Rasterformat die ungefähr am längsten laufenden Threads in der Ablaufverfolgung anzuzeigen.
0:000> dx -g @$curprocess.TTD.Events.Where(e => e.Type == "ThreadTerminated").Select(e => new { Thread = e.Thread, ActiveTimeLength = e.Thread.ActiveTime.MaxPosition.Sequence - e.Thread.ActiveTime.MinPosition.Sequence }).OrderByDescending(t => t.ActiveTimeLength)
=========================================================
= = (+) Thread = ActiveTimeLength =
=========================================================
= [0x0] - UID: 2, TID: 0x1750 - 0x364030 =
= [0x1] - UID: 3, TID: 0x420C - 0x360fd4 =
= [0x2] - UID: 7, TID: 0x352C - 0x35da46 =
= [0x3] - UID: 9, TID: 0x39F4 - 0x34a5b5 =
= [0x4] - UID: 11, TID: 0x4288 - 0x326199 =
= [0x5] - UID: 13, TID: 0x21C8 - 0x2fa8d8 =
= [0x6] - UID: 14, TID: 0x2188 - 0x2a03e3 =
= [0x7] - UID: 15, TID: 0x40E8 - 0x29e7d0 =
= [0x8] - UID: 16, TID: 0x124 - 0x299677 =
= [0x9] - UID: 4, TID: 0x2D74 - 0x250f43 =
= [0xa] - UID: 5, TID: 0x2DC8 - 0x24f921 =
= [0xb] - UID: 6, TID: 0x3B1C - 0x24ec8e =
= [0xc] - UID: 10, TID: 0x3808 - 0xf916f =
= [0xd] - UID: 12, TID: 0x26B8 - 0x1ed3a =
= [0xe] - UID: 17, TID: 0x37D8 - 0xc65 =
= [0xf] - UID: 8, TID: 0x45F8 - 0x1a2 =
=========================================================
Abfrage nach Lesezugriffen auf einen Speicherbereich
Verwenden Sie das TTD.Memory Objekt zur Abfrage von Lesezugriffen auf einen Speicherbereich.
Der Thread Environment Block (TEB) ist eine Struktur, die alle Informationen über den Zustand eines Threads enthält, einschließlich des von GetLastError() zurückgegebenen Ergebnisses. Sie können diese Datenstruktur abfragen, indem Sie dx $@teb
für den aktuellen Thread ausführen. Eines der Mitglieder von TEB ist die 4 Byte große Variable LastErrorValue. Wir können das LastErrorValue-Mitglied in der TEB mit dieser Syntax referenzieren. dx &$@teb->LastErrorValue
.
Die Beispielabfrage zeigt, wie man alle Lesevorgänge in diesem Bereich im Speicher findet, alle Lesevorgänge auswählt, die vor der Erstellung des Dialogs stattgefunden haben, und dann das Ergebnis sortiert, um den letzten Lesevorgang zu finden.
0:000> dx @$cursession.TTD.Memory(&@$teb->LastErrorValue, &@$teb->LastErrorValue + 0x4, "r")
@$cursession.TTD.Memory(&@$teb->LastErrorValue, &@$teb->LastErrorValue + 0x4, "r")
[0x0]
[0x1]
[0x2]
[0x3]
Wenn in unserer Ablaufverfolgung ein „Dialog“-Ereignis stattgefunden hat, können wir eine Abfrage starten, um alle Lesevorgänge in diesem Bereich im Speicher zu finden, alle Lesevorgänge auswählen, die vor der Erstellung des Dialogs stattgefunden haben, und dann das Ergebnis sortieren, um den letzten Lesevorgang zu finden. Dann reist man zu diesem Zeitpunkt, indem man SeekTo() für die resultierende Zeitposition aufruft.
:000> dx @$cursession.TTD.Memory(&@$teb->LastErrorValue, &@$teb->LastErrorValue + 0x4, "r").Where(m => m.TimeStart < @$dialog).OrderBy(m => m.TimeStart).Last().TimeEnd.SeekTo()
Setting position: 458300:37
ModLoad: 6cee0000 6cf5b000 C:\WINDOWS\system32\uxtheme.dll
ModLoad: 75250000 752e6000 C:\WINDOWS\System32\OLEAUT32.dll
ModLoad: 76320000 7645d000 C:\WINDOWS\System32\MSCTF.dll
ModLoad: 76cc0000 76cce000 C:\WINDOWS\System32\MSASN1.dll
GitHub TTD Query Lab
Eine Anleitung zum Debuggen von C++-Code mit Hilfe einer Time Travel Debugging-Aufzeichnung unter Verwendung von Abfragen, um Informationen über die Ausführung des fraglichen problematischen Codes zu finden, finden Sie unter https://github.com/Microsoft/WinDbg-Samples/blob/master/TTDQueries/tutorial-instructions.md.
Der gesamte im Lab verwendete Code ist hier verfügbar: https://github.com/Microsoft/WinDbg-Samples/tree/master/TTDQueries/app-sample.
Fehlersuche bei TTD-Abfragen
„UnknownOrMissingSymbols“ als Funktionsnamen
Die Datenmodellerweiterung benötigt vollständige Symbolinformationen, um Funktionsnamen, Parameterwerte usw. bereitstellen zu können. Wenn keine vollständigen Symbolinformationen verfügbar sind, verwendet der Debugger „UnknownOrMissingSymbols“ als Funktionsnamen.
- Wenn Sie über private Symbole verfügen, erhalten Sie den Funktionsnamen und die korrekte Liste der Parameter.
- Wenn Sie öffentliche Symbole haben, erhalten Sie den Funktionsnamen und einen Standardsatz von Parametern – vier vorzeichenlose 64-Bit-Ints.
- Wenn Sie keine Symbolinformationen für das abgefragte Modul haben, wird „UnknownOrMissingSymbols“ als Name verwendet.
TTD-Abfragen für Aufrufe
Es kann mehrere Gründe geben, warum eine Abfrage bei Aufrufen einer DLL nichts zurückgibt.
- Die Syntax für den Aufruf ist nicht ganz richtig. Versuchen Sie, die Aufrufsyntax mit dem Befehl x zu überprüfen: „x <call>“. Wenn der von x zurückgegebene Modulname in Großbuchstaben geschrieben ist, verwenden Sie diesen.
- Die DLL ist noch nicht geladen und wird später in der Ablaufverfolgung geladen. Um dies zu umgehen, reisen Sie zu einem Zeitpunkt, nachdem die DLL geladen wurde, und wiederholen Sie die Abfrage.
- Der Aufruf ist inlined, was die Abfragesoftware nicht nachvollziehen kann.
- Das Abfragemuster verwendet Platzhalter, die zu viele Funktionen zurückgeben. Versuchen Sie, das Abfragemuster spezifischer zu gestalten, damit die Anzahl der übereinstimmenden Funktionen klein genug ist.
Weitere Informationen
Verwendung von LINQ mit den Debugger-Objekten
dx (Display Debugger Object Model Expression)
Zeitreise-Debugging – Überblick
Zeitreise-Debugging – JavaScript-Automatisierung