Condividi tramite


Debug di viaggi temporali - Automazione JavaScript

Logo di debug del tempo di viaggio con un orologio.

È possibile usare l'automazione JavaScript per usare le tracce TTD in diversi modi, ad esempio l'automazione dei comandi o l'uso di query per individuare i dati degli eventi dal file di traccia.

Per informazioni generali sull'uso di JavaScript, vedere Scripting del debugger JavaScript. Sono disponibili anche script di esempio del debugger JavaScript.

Automazione dei comandi TTD JavaScript

Un modo per usare JavaScript per l'automazione TTD consiste nell'inviare comandi per automatizzare l'uso dei file di traccia del viaggio nel tempo.

Spostamento in un file di traccia

Questo JavaScript illustra come passare all'inizio di una traccia di viaggio temporale usando il comando !tt .

var dbgControl = host.namespace.Debugger.Utility.Control;  
dbgControl.ExecuteCommand("!tt 0",false);
host.diagnostics.debugLog(">>> Sent command to move to the start of the TTD file \n");

È possibile impostarlo in una funzione ResetTrace e salvarlo come ResetTrace.js, usando l'interfaccia utente JavaScript in WinDbg.

// WinDbg TTD JavaScript ResetTraceCmd Sample

"use strict";

function ResetTraceCmd()
{
    var dbgControl = host.namespace.Debugger.Utility.Control;  
    dbgControl.ExecuteCommand("!tt 0",false);
    host.diagnostics.debugLog(">>> Sent command to move to the start of the TTD file \n");
}

Dopo il caricamento di un file TTD in WinDbg, chiamare la funzione ResetTraceCmd() usando il comando dx nella finestra di comando del debugger.

0:000> dx Debugger.State.Scripts.ResetTrace.Contents.ResetTraceCmd()
>>> Sent command to move to the start of the TTD file
Debugger.State.Scripts.ResetTrace.Contents.ResetTrace()

Limitazioni dell'invio di comandi

Ma per tutte le situazioni più semplici, l'approccio all'invio di comandi presenta svantaggi. Si basa sull'uso dell'output di testo. L'analisi dell'output comporta codice fragile e difficile da gestire. Un approccio migliore consiste nell'usare direttamente gli oggetti TTD.

Nell'esempio seguente viene illustrato come utilizzare gli oggetti direttamente per completare la stessa attività usando direttamente gli oggetti .

// WinDbg TTD JavaScript ResetTrace Sample

"use strict";

function ResetTrace()
{
    host.currentProcess.TTD.SetPosition(0);
    host.diagnostics.debugLog(">>> Set position to the start of the TTD file \n");
}

L'esecuzione di questo codice mostra che è possibile passare all'inizio del file di traccia.

0:000> dx Debugger.State.Scripts.ResetTrace.Contents.ResetTrace()
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: F:0
>>> Set position to the start of the TTD file

In questo esempio La funzione ResetTraceEnd, la posizione viene impostata sulla fine della traccia e la posizione corrente e nuova viene visualizzata utilizzando l'oggetto CurrentThread.TTD Position.


// WinDbg TTD JavaScript Sample to Reset Trace using objects directly
// and display current and new position

function ResetTraceEnd()
{
   var PositionOutputStart = host.currentThread.TTD.Position;
   host.diagnostics.debugLog(">>> Current position in trace file:  "+ PositionOutputStart +"\n");
   host.currentProcess.TTD.SetPosition(100);
   var PositionOutputNew = host.currentThread.TTD.Position;
   host.diagnostics.debugLog(">>> New position in trace file:  "+ PositionOutputNew +"\n");
}

L'esecuzione di questo codice visualizza la posizione corrente e quella nuova.

0:000> dx Debugger.State.Scripts.ResetTrace.Contents.ResetTraceEnd()
>>> Current position in trace file:  F:0
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: D3:1
>>> New position in trace file:  D3:1

In questo esempio espanso i valori della posizione iniziale e finale vengono confrontati per verificare se la posizione nella traccia è stata modificata.

// WinDbg TTD JavaScript ResetTraceEx Sample

"use strict";

function ResetTraceEx()
{
    const PositionOutputStart = host.currentThread.TTD.Position;
    host.diagnostics.debugLog(">>> Current position in trace file:  "+ PositionOutputStart +"\n");
  
    host.currentProcess.TTD.SetPosition(0);

    const PositionOutputNew = host.currentThread.TTD.Position;
    host.diagnostics.debugLog(">>> New position in trace file:  "+ PositionOutputNew +"\n");

    if (parseInt(PositionOutputStart,16) != parseInt(PositionOutputNew,16))
    {
        host.diagnostics.debugLog(">>> Set position to the start of the TTD file  \n");
    }
    else
    {
        host.diagnostics.debugLog(">>> Position was already set to the start of the TTD file \n");
    }
}

In questo esempio viene visualizzato un messaggio che indica che tutti erano pronti all'inizio del file di traccia.

0:000> dx Debugger.State.Scripts.ResetTrace.Contents.ResetTraceEx()
>>> Current position in trace file:  F:0
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: F:0
>>> New position in trace file:  F:0
>>> Position was already set to the start of the TTD file

Per testare lo script, usare il comando !tt per spostarsi a metà nel file di traccia.

0:000> !tt 50
Setting position to 50% into the trace
Setting position: 71:0

L'esecuzione dello script visualizza ora il messaggio appropriato che indica che la posizione è stata impostata sull'inizio della traccia TTD.

0:000> dx Debugger.State.Scripts.ResetTrace.Contents.ResetTraceEx()
>>> Current position in trace file:  71:0
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: F:0
>>> New position in trace file:  F:0
>>> Set position to the start of the TTD file  

Indicizzazione di un file di traccia di spostamento temporale

Se solo un file di traccia viene copiato in un PC diverso, sarà necessario reindicizzare. Per altre informazioni, vedere Time Travel Debugging - Working with Trace Files.For more information, see Time Travel Debugging - Working with Trace Files.

Questo codice mostra un esempio di funzione IndexTrace che visualizza il tempo necessario per reindicizzare un file di traccia.

function IndexTrace()
{
    var timeS = (new Date()).getTime();
    var output = host.currentProcess.TTD.Index.ForceBuildIndex();
    var timeE = (new Date()).getTime();
    host.diagnostics.debugLog("\n>>> Trace was indexed in " + (timeE - timeS) + " ms\n");
}

Ecco l'output di un piccolo file di traccia.

0:000> dx Debugger.State.Scripts.TTDUtils.Contents.IndexTrace()

>>> Trace was indexed in 2 ms

Aggiunta di un'istruzione try catch

Per verificare se sono stati generati errori durante l'esecuzione dell'indicizzazione, racchiudere il codice di indicizzazione in un'istruzione try catch.


function IndexTraceTry()
{
    var timeS = (new Date()).getTime();
    try
    {
         var IndexOutput =  host.currentProcess.TTD.Index.ForceBuildIndex();
         host.diagnostics.debugLog("\n>>> Index Return Value: " + IndexOutput + "\n");
         var timeE = (new Date()).getTime();
         host.diagnostics.debugLog("\n>>> Trace was successfully indexed in " + (timeE - timeS) + " ms\n");
     }

    catch(err)
    {
         host.diagnostics.debugLog("\n>>> Index Failed! \n");
         host.diagnostics.debugLog("\n>>> Index Return Value: " + IndexOutput + "\n");
         host.diagnostics.debugLog("\n>>> Returned error: " + err.name + "\n");
    }
}

Di seguito è riportato l'output dello script se l'indicizzazione ha esito positivo.

0:000> dx Debugger.State.Scripts.TTDUtils.Contents.IndexTraceTry()

>>> Index Return Value: Loaded

>>> Trace was successfully indexed in 1 ms

Se la traccia non può essere indicizzata, ad esempio se la traccia non viene caricata nel debugger, viene eseguito il codice del ciclo catch.

0:007> dx Debugger.State.Scripts.TTDUtils.Contents.IndexTraceTry()

>>> Index Failed!

>>> Index Return Value: undefined

>>> Returned error: TypeError

Query sugli oggetti TTD JavaScript

Un uso più avanzato di JavaScript e TTD consiste nell'eseguire una query sugli oggetti di spostamento temporale per individuare chiamate o eventi specifici che si sono verificati nella traccia. Per altre informazioni sugli oggetti TTD, vedere:

Introduzione agli oggetti debug di viaggi temporali

Oggetti debugger nativi nelle estensioni JavaScript - Dettagli oggetto debugger

Il comando dx visualizza informazioni dal modello di dati del debugger e supporta le query usando la sintassi LINQ. Dx è molto utile per eseguire query sugli oggetti in tempo reale. Ciò consente la creazione di prototipi della query desiderata che può quindi essere automatizzata usando JavaScript. Il comando dx fornisce il completamento della scheda, che può essere utile durante l'esplorazione del modello a oggetti. Per informazioni generali sull'uso di query LINQ e oggetti debugger, vedere Uso di LINQ Con gli oggetti debugger.

Questo comando dx conta tutte le chiamate a una determinata API, in questo esempio GetLastError.

0:000> dx @$cursession.TTD.Calls("kernelbase!GetLastError").Count()

@$cursession.TTD.Calls("kernelbase! GetLastError").Count() : 0x12

Questo comando cerca nell'intera traccia di viaggio temporale per vedere quando è stato chiamato GetLastError.

0:000> dx @$cursession.TTD.Calls("kernelbase!GetLastError").Where(c => c.ReturnValue != 0)

@$cursession.TTD.Calls("kernelbase!GetLastError").Where(c => c.ReturnValue != 0)
    [0x0]
    [0x1]
    [0x2]
    [0x3]

Confronti tra stringhe per TTD. Chiama oggetto per individuare le chiamate

Questo comando di esempio illustra come usare confronti di stringhe per individuare chiamate specifiche. In questo esempio la query cerca la stringa "OLE" nel parametro lpFileName della funzione CreateFileW.

dx -r2 @$cursession.TTD.Calls("kernelbase!CreateFileW").Where(x => x.Parameters.lpFileName.ToDisplayString("su").Contains("OLE"))

Aggiungere un oggetto . Selezionare l'istruzione per stampare Timestart e il valore del parametro lpFileName .

dx -r2 @$cursession.TTD.Calls("kernelbase!CreateFileW").Where(x => x.Parameters.lpFileName.ToDisplayString("su").Contains("OLE")).Select(x => new { TimeStart = x.TimeStart, lpFileName = x.Parameters.lpFileName })

Verrà generato questo output, se un TTD. Viene trovato l'oggetto Chiamate che contiene le informazioni di destinazione.

    [0x0]
        TimeStart        : 6E37:590
        lpFileName       : 0x346a78be90 : "C:\WINDOWS\SYSTEM32\OLEACCRC.DLL" [Type: wchar_t *]

Visualizzazione del numero di chiamate in una traccia

Dopo aver usato il comando dx per esplorare gli oggetti da usare, è possibile automatizzare l'uso con JavaScript. In questo semplice esempio, il TTD. L'oggetto Chiamate viene usato per contare le chiamate a kernelbase. GetLastError.

function CountLastErrorCalls()
{
    var LastErrorCalls = host.currentSession.TTD.Calls("kernelbase!GetLastError");
    host.diagnostics.debugLog(">>> GetLastError calls in this TTD recording: " +  LastErrorCalls.Count() +" \n");
}

Salvare lo script in un file TTDUtils.js e chiamarlo usando il comando dx per visualizzare un conteggio del numero di kernelbase. GetLastError nel file di traccia.


0:000> dx Debugger.State.Scripts.TTDUtils.Contents.CountLastErrorCalls()
>>> GetLastError calls in this TTD recording: 18

Visualizzazione dei fotogrammi in uno stack

Per visualizzare i fotogrammi in uno stack, viene usata una matrice.

function DisplayStack()
{
// Create an array of stack frames in the current thread
const Frames = Array.from(host.currentThread.Stack.Frames);
host.diagnostics.debugLog(">>> Printing stack \n");
// Print out all of the frame entries in the array
for(const [Idx, Frame] of Frames.entries())
    {
        host.diagnostics.debugLog(">>> Stack Entry -> " + Idx + ":  "+ Frame + " \n");
    }
}

In questa traccia di esempio viene visualizzata la voce di uno stack.

0:000> dx Debugger.State.Scripts.TTDUtils.Contents.DisplayStack()
>>> Printing stack
>>> Stack Entry -> 0:  ntdll!LdrInitializeThunk + 0x21

Individuazione di un evento e visualizzazione dello stack

In questo codice vengono individuati tutti gli eventi delle eccezioni e viene usato un ciclo per spostarsi in ognuno di essi. Viene quindi utilizzata la currentThread.ID degli oggetti thread TTD per visualizzare l'ID del thread e currentThread.Stack per visualizzare tutti i fotogrammi nello stack.


function HardwareExceptionDisplayStack()
{
var exceptionEvents = host.currentProcess.TTD.Events.Where(t => t.Type == "Exception");
    for (var curEvent of exceptionEvents)
    {
        // Move to the current event position
        curEvent.Position.SeekTo();
        host.diagnostics.debugLog(">>> The Thread ID (TID) is : " + host.currentThread.Id + "\n");
        // Create an array of stack frames in the current thread
        const Frames = Array.from(host.currentThread.Stack.Frames);
        host.diagnostics.debugLog(">>> Printing stack \n");
        // Print out all of the frame entries in the array
        for(const [Idx, Frame] of Frames.entries()) {
            host.diagnostics.debugLog(">>> Stack Entry -> " + Idx + ":  "+ Frame + " \n");
        }
    host.diagnostics.debugLog("\n");
    }
}

L'output mostra la posizione dell'evento di eccezione, il TID e gli stack frame.

0:000> dx Debugger.State.Scripts.TTDUtils.Contents.HardwareExceptionDisplayStack()
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 91:0
>>> The Thread ID (TID) is : 5260
>>> Printing stack
>>> Stack Entry -> 0:  0x540020
>>> Stack Entry -> 1:  0x4d0049
>>> Stack Entry -> 2:  DisplayGreeting!__CheckForDebuggerJustMyCode + 0x16d
>>> Stack Entry -> 3:  DisplayGreeting!mainCRTStartup + 0x8
>>> Stack Entry -> 4:  KERNEL32!BaseThreadInitThunk + 0x19
>>> Stack Entry -> 5:  ntdll!__RtlUserThreadStart + 0x2f
>>> Stack Entry -> 6:  ntdll!_RtlUserThreadStart + 0x1b

Individuazione di un evento e invio di due comandi

L'esecuzione di query su oggetti TTD e l'invio di comandi possono essere combinati in base alle esigenze. Questo esempio individua ogni evento nella traccia TTD di tipo ThreadCreated, si sposta in tale posizione e invia i comandi ~Thread Status e !runaway per visualizzare lo stato del thread.

function ThreadCreateThreadStatus()
{
var threadEvents = host.currentProcess.TTD.Events.Where(t => t.Type == "ThreadCreated");
    for (var curEvent of threadEvents)
    {
        // Move to the current event position
       curEvent.Position.SeekTo();
        // Display Information about threads
       host.namespace.Debugger.Utility.Control.ExecuteCommand("~", false);
       host.namespace.Debugger.Utility.Control.ExecuteCommand("!runaway 7", false);
    }
}

L'esecuzione del codice visualizza lo stato del thread nel momento in cui si è verificata l'eccezione.

0:000> dx Debugger.State.Scripts.TTDUtils.Contents.ThreadCreateThreadStatus()
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: F:0
.  0  Id: 948.148c Suspend: 4096 Teb: 00a33000 Unfrozen
User Mode Time
  Thread       Time
    0:148c     0 days 0:00:00.000
Kernel Mode Time
  Thread       Time
    0:148c     0 days 0:00:00.000
Elapsed Time
  Thread       Time
    0:148c     3474 days 2:27:43.000

Concatenamento delle funzioni dell'utilità

In questo esempio finale è possibile chiamare le funzioni di utilità create in precedenza. Prima di tutto si indicizza la traccia usando IndexTraceTry e quindi si chiama ThreadCreateThreadStatus. Viene quindi usato ResetTrace per passare all'inizio della traccia e infine chiamare HardwareExceptionDisplayStack.

function ProcessTTDFiles()
{
    try
    {
    IndexTraceTry()
    ThreadCreateThreadStatus()
    ResetTrace()
    HardwareExceptionDisplayStack()
    }

    catch(err)
    {
         host.diagnostics.debugLog("\n >>> Processing of TTD file failed \n");
    }

}

L'esecuzione di questo script in un file di traccia contenente un'eccezione hardware genera questo output.

0:000> dx Debugger.State.Scripts.TTDUtils.Contents.ProcessTTDFiles()

>>> Index Return Value: Loaded

>>> Trace was successfully indexed in 0 ms
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: F:0
.  0  Id: 948.148c Suspend: 4096 Teb: 00a33000 Unfrozen
User Mode Time
  Thread       Time
    0:148c     0 days 0:00:00.000
Kernel Mode Time
  Thread       Time
    0:148c     0 days 0:00:00.000
Elapsed Time
  Thread       Time
    0:148c     3474 days 2:27:43.000
>>> Printing stack
>>> Stack Entry -> 0:  ntdll!LdrInitializeThunk
>>> Current position in trace file:  F:0
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: F:0
>>> New position in trace file:  F:0
(948.148c): Break instruction exception - code 80000003 (first/second chance not available)
Time Travel Position: 91:0
>>> The Thread ID (TID) is : 5260
>>> Printing stack
>>> Stack Entry -> 0:  0x540020
>>> Stack Entry -> 1:  0x4d0049
>>> Stack Entry -> 2:  DisplayGreeting!__CheckForDebuggerJustMyCode + 0x16d
>>> Stack Entry -> 3:  DisplayGreeting!mainCRTStartup + 0x8
>>> Stack Entry -> 4:  KERNEL32!BaseThreadInitThunk + 0x19
>>> Stack Entry -> 5:  ntdll!__RtlUserThreadStart + 0x2f
>>> Stack Entry -> 6:  ntdll!_RtlUserThreadStart + 0x1b


Vedi anche

Debug di viaggi temporali - Panoramica

Introduzione agli oggetti debug di viaggi temporali

Oggetti debugger nativi nelle estensioni JavaScript - Dettagli oggetto debugger

JavaScript Debugger Scripting

Script di esempio del debugger JavaScript