Freigeben über


Einführung in Objekte zum Debuggen von Zeitreisen

Zeitreise-Debugging-Logo mit einer Uhr.

In diesem Abschnitt wird beschrieben, wie das Datenmodell für die Abfrage von Ablaufverfolgungen für Zeitreisen verwendet werden kann. Dies kann ein nützliches Tool sein, um Fragen wie diesen zum Code zu beantworten, der in einer Zeitreiseablaufverfolgung 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.

0:000> dx @$curprocess.TTD
@$curprocess.TTD                
    Index           
    Threads         
    Events          
    DebugOutput     
    Lifetime         : [2C8:0, 16EC:98A]
    DefaultMemoryPolicy : InFragmentAggressive
    SetPosition      [Sets the debugger to point to the given position on this process.]
    GatherMemoryUse  [0]
    RecordClients   
    PrevMemoryAccess [(accessMask, address, size [, address, size, ...]) - Find the previous matching memory access before current position.]
    NextMemoryAccess [(accessMask, address, size [, address, size, ...]) - Find the next matching memory access after current position.]
    Recorder

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                
    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"])]
    MemoryForPositionRange [Returns memory access information for specified address range and position range: TTD.MemoryForPositionRange(startAddress, endAddress [, "rwec"], minPosition, maxPosition)]
    PinObjectPosition [Pins an object to the given time position: TTD.PinObjectPosition(obj, pos)]
    AsyncQueryEnabled : false
    RichQueryTypesEnabled : true
    DefaultParameterCount : 0x4
    Data             : Normalized data sources based on the contents of the time travel trace
    Utility          : Methods that can be useful when analyzing time travel traces
    Analyzers        : Methods that perform code analysis on the time travel trace
    Bookmarks        : Bookmark collection
    Checkers         : Checkers (scripts for detection of common issues recorded in a time travel trace)

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)                
    [0x0]           
        EventType        : 0x0
        ThreadId         : 0x2d98
        UniqueThreadId   : 0x2
        TimeStart        : 718:7D7 [Time Travel]
        TimeEnd          : 718:7DA [Time Travel]
        Function         : KERNELBASE!GetLastError
        FunctionAddress  : 0x7ff996cf20f0
        ReturnAddress    : 0x7ff99855ac5a
        ReturnValue      : 0x0 [Type: unsigned long]
        Parameters      
        SystemTimeStart  : Friday, January 12, 2024 21:18:40.862
        SystemTimeEnd    : Friday, January 12, 2024 21:18:40.862
    [0x1]           
        EventType        : 0x0
        ThreadId         : 0x2d98
        UniqueThreadId   : 0x2
        TimeStart        : 72D:1B3 [Time Travel]
        TimeEnd          : 72D:1B6 [Time Travel]
        Function         : KERNELBASE!GetLastError
        FunctionAddress  : 0x7ff996cf20f0
        ReturnAddress    : 0x7ff9961538df
        ReturnValue      : 0x57 [Type: unsigned long]
        Parameters      
        SystemTimeStart  : Friday, January 12, 2024 21:18:40.862
        SystemTimeEnd    : Friday, January 12, 2024 21:18:40.862
...        

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        : 0x0
    ThreadId         : 0x2d98
    UniqueThreadId   : 0x2
    TimeStart        : 718:7D7 [Time Travel]
    TimeEnd          : 718:7DA [Time Travel]
    Function         : KERNELBASE!GetLastError
    FunctionAddress  : 0x7ff996cf20f0
    ReturnAddress    : 0x7ff99855ac5a
    ReturnValue      : 0x0 [Type: unsigned long]
    Parameters      
    SystemTimeStart  : Friday, January 12, 2024 21:18:40.862
    SystemTimeEnd    : Friday, January 12, 2024 21:18:40.862

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 })                
    [0x1]           
        Time             : 72D:1B3 [Time Travel]
        Error            : 0x57 [Type: unsigned long]
    [0x2]           
        Time             : 72D:1FC [Time Travel]
        Error            : 0x2af9 [Type: unsigned long]
    [0x3]           
        Time             : 72D:26E [Time Travel]
        Error            : 0x2af9 [Type: unsigned long]

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

Ein Lernprogramm zum Debuggen von C++-Code mithilfe einer Time Travel Debugging-Aufzeichnung mithilfe von Abfragen, um Informationen zur Ausführung des fraglichen problematischen Codes zu finden, finden Sie unter WinDbg-Samples - Time Travel Debugging und Abfragen.

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 nichts für Aufrufe einer DLL zurückgibt.

  • Die Syntax für den Aufruf ist nicht ganz richtig. Überprüfen Sie die Aufrufsyntax mithilfe von x (Prüfsymbole). 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