Compartilhar via


Depuração de viagem no tempo – Automação javaScript

Logotipo de depuração de viagem no tempo com um relógio.

Você pode usar a automação javaScript para trabalhar com rastreamentos TTD de várias maneiras, como automação de comando ou usando consultas para localizar dados de evento do arquivo de rastreamento.

Para obter informações gerais sobre como trabalhar com JavaScript, consulte JavaScript Debugger Scripting. Também há scripts de exemplo do depurador JavaScript.

Automação de comandos TTD do JavaScript

Uma maneira de usar o JavaScript para automação TTD é enviar comandos para automatizar o trabalho com arquivos de rastreamento de viagem no tempo.

Movendo em um arquivo de rastreamento

Este JavaScript mostra como mover para o início de um rastreamento de viagem no tempo usando o 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");

Podemos transformar isso em uma função ResetTrace e salvá-la como ResetTrace.js, usando a interface do usuário do JavaScript no 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");
}

Depois que um arquivo TTD for carregado no WinDbg, chame a função ResetTraceCmd() usando o comando dx na janela de comando do depurador.

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()

Limitações do envio de comandos

Mas para todas as situações menos simples, a abordagem de enviar comandos tem desvantagens. Ele depende do uso da saída de texto. E analisar essa saída leva a um código que é frágil e difícil de manter. Uma abordagem melhor é usar os objetos TTD diretamente.

O exemplo a seguir mostra como usar os objetos diretamente para concluir a mesma tarefa usando os objetos diretamente.

// 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");
}

A execução desse código mostra que podemos mover para o início do arquivo de rastreamento.

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

Neste exemplo, função ResetTraceEnd, a posição é definida como o final do rastreamento e a posição atual e nova é exibida usando o objeto 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");
}

A execução desse código exibe a posição atual e a nova.

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

Neste exemplo expandido, os valores de posição inicial e final são comparados para ver se a posição no rastreamento foi alterada.

// 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");
    }
}

Nesta execução de exemplo, é exibida uma mensagem informando que estávamos todos prontos no início do arquivo de rastreamento.

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

Para testar o script, use o comando !tt para navegar pela metade no arquivo de rastreamento.

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

A execução do script agora exibe a mensagem adequada que indica que a posição foi definida como o início do rastreamento 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  

Indexando um arquivo de rastreamento de viagem no tempo

Se apenas um arquivo de rastreamento for copiado para um computador diferente, ele precisará ser indexado novamente. Para obter mais informações, consulte Depuração de viagem no tempo – Trabalhando com arquivos de rastreamento.

Este código mostra um exemplo de função IndexTrace que exibe quanto tempo leva para reindexar um arquivo de rastreamento.

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");
}

Aqui está a saída de um pequeno arquivo de rastreamento.

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

>>> Trace was indexed in 2 ms

Adicionando uma instrução try catch

Para marcar para ver se os erros foram gerados quando a indexação foi executada, coloque o código de indexação em uma instrução 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");
    }
}

Aqui está a saída do script se a indexação for bem-sucedida.

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

>>> Index Return Value: Loaded

>>> Trace was successfully indexed in 1 ms

Se o rastreamento não puder ser indexado, por exemplo, se o rastreamento não for carregado no depurador, o código do loop catch será executado.

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

>>> Index Failed!

>>> Index Return Value: undefined

>>> Returned error: TypeError

Consultas de objetos TTD do JavaScript

Um uso mais avançado de JavaScript e TTD é consultar os objetos de viagem no tempo para localizar chamadas ou eventos específicos que ocorreram no rastreamento. Para obter mais informações sobre os objetos TTD, consulte:

Introdução aos objetos de depuração de viagem no tempo

Objetos nativos do depurador em extensões JavaScript – Detalhes do objeto do depurador

O comando dx exibe informações do modelo de dados do depurador e dá suporte a consultas usando a sintaxe LINQ. O Dx é muito útil para consultar os objetos em tempo real. Isso permite a criação de protótipos da consulta desejada que pode ser automatizada usando JavaScript. O comando dx fornece a conclusão da guia, o que pode ser útil ao explorar o modelo de objeto. Para obter informações gerais sobre como trabalhar com consultas LINQ e objetos de depurador, consulte Usando LINQ com os objetos do depurador.

Esse comando dx conta todas as chamadas para uma determinada API, neste exemplo , GetLastError.

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

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

Esse comando examina todo o rastreamento de viagem no tempo para ver quando GetLastError foi chamado.

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]

Comparações de cadeia de caracteres para TTD. Chama o Objeto para localizar chamadas

Este comando de exemplo mostra como usar comparações de cadeia de caracteres para localizar chamadas específicas. Neste exemplo, a consulta procura a cadeia de caracteres "OLE" no parâmetro lpFileName da função CreateFileW.

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

Adicione um . Selecione a instrução para imprimir Timestart e o valor do parâmetro 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 })

Isso gera essa saída, se um TTD. O objeto Calls é encontrado que contém as informações de destino.

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

Exibindo o número de chamadas em um rastreamento

Depois de usar o comando dx para explorar os objetos com os quais deseja trabalhar, você pode automatizar o uso deles com JavaScript. Neste exemplo simples, o TTD. O objeto Calls é usado para contar chamadas para kernelbase! GetLastError.

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

Salve o script em um arquivo TTDUtils.js e chame-o usando o comando dx para exibir uma contagem do número de kernelbase! GetLastError no arquivo de rastreamento.


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

Exibindo os quadros em uma pilha

Para exibir os quadros em uma pilha, uma matriz é usada.

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");
    }
}

Neste rastreamento de exemplo, a entrada de uma pilha é exibida.

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

Localizando um evento e exibindo a pilha

Nesse código, todos os eventos de exceções estão localizados e um loop é usado para mover para cada um deles. Em seguida, o currentThread.ID dos Objetos de Thread TTD é usado para exibir a ID do thread e currentThread.Stack é usado para exibir todos os quadros na pilha.


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");
    }
}

A saída mostra o local do evento de exceção, o TID e os quadros de pilha.

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

Localizando um evento e enviando dois comandos

Consultar objetos TTD e enviar comandos pode ser combinado conforme necessário. Este exemplo localiza cada evento no rastreamento TTD do tipo ThreadCreated, move para essa posição e envia os comandos ~ Thread Status e !runaway para exibir o thread status.

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);
    }
}

A execução do código exibe o thread status no momento em que a exceção ocorreu.

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

Encadeamento de funções do utilitário juntos

Neste exemplo final, podemos chamar as funções de utilitário que criamos anteriormente. Primeiro indexamos o rastreamento usando IndexTraceTry e, em seguida, chamamos ThreadCreateThreadStatus. Em seguida, usamos ResetTrace para ir para o início do rastreamento e, por fim, chamar HardwareExceptionDisplayStack.

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

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

}

A execução desse script em um arquivo de rastreamento que contém uma exceção de hardware gera essa saída.

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


Consulte Também

Depuração de viagem no tempo – Visão geral

Introdução aos objetos de depuração de viagem no tempo

Objetos nativos do depurador em extensões JavaScript – Detalhes do objeto do depurador

Script do Depurador JavaScript

Scripts de exemplo do depurador JavaScript