Depuración de viajes en el tiempo - Automatización de JavaScript
Puede usar la automatización de JavaScript para trabajar con rastreos TTD de varias maneras, como la automatización de comandos o el uso de consultas para buscar datos de eventos del archivo de seguimiento.
Para obtener información general sobre cómo trabajar con JavaScript, consulte JDepurador de script de JavaScript. Consulte también Scripts de ejemplo del depurador de JavaScript.
Automatización de comandos TTD de JavaScript
Una manera de usar JavaScript para la automatización de TTD es enviar comandos para automatizar el trabajo con archivos de rastreo de viajes en tiempo.
Mover un archivo de rastreo
En este ejemplo de JavaScript se muestra cómo pasar al inicio de un rastreo de viajes en el tiempo mediante el 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");
Esto se puede convertir en una función ResetTrace y guardarla como MyTraceUtils.js, mediante la interfaz de usuario de JavaScript en WinDbg.
// My Trace Utils
// WinDbg TTD JavaScript MyTraceUtilsCmd Sample
"use strict";
function MyTraceUtilsCmd()
{
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");
}
Después de cargar un archivo TTD en WinDbg, llame a la función ResetTraceCmd() mediante el comando dx en la ventana de comandos del depurador.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.ResetTraceCmd()
>>> Sent command to move to the start of the TTD file
Debugger.State.Scripts.MyTraceUtils.Contents.ResetTraceCmd()
Limitaciones del envío de comandos
Pero, excepto en las situaciones más sencillas, el método de enviar comandos tiene sus inconvenientes. Se basa en el uso del resultado de texto. Y analizar ese resultado conduce un código frágil y difícil de mantener. Un mejor enfoque es usar directamente los objetos TTD.
En el ejemplo siguiente se muestra cómo usar los objetos directamente para completar la misma tarea mediante los objetos directamente.
// My Trace Utils
// 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");
}
La ejecución de este código muestra que podemos pasar al inicio del archivo de rastreo.
0:000> dx Debugger.State.Scripts.MyTraceUtils.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
En esta función ResetTraceEnd de ejemplo, la posición se establece al final del rastreo y la posición actual y nueva se muestra mediante el objeto de posición currentThread.TTD.
// 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");
}
Al ejecutar este código se muestra la posición actual y nueva.
0:000> dx Debugger.State.Scripts.MyTraceUtils.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
En este ejemplo expandido, se comparan los valores de posición inicial y final para ver si la posición del rastreo ha cambiado.
// 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");
}
}
En esta ejecución de ejemplo, se muestra un mensaje que indica que estábamos listos al inicio del archivo de rastreo.
0:000> dx Debugger.State.Scripts.MyTraceUtils.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 probar el script, use el comando !tt para navegar a la mitad del archivo de rastreo.
0:000> !tt 50
Setting position to 50% into the trace
Setting position: 71:0
Al ejecutar el script ahora se muestra el mensaje adecuado que indica que la posición se estableció en el inicio del rastreo TTD.
0:000> dx Debugger.State.Scripts.MyTraceUtils.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
Indexación de un archivo de rastreo de viajes en el tiempo
Si solo se copia un archivo de rastreo en un equipo diferente, deberá volver a indexarse. Para obtener más información, consulte Depuración de viajes en el tiempo: trabajo con archivos de rastreo.
Este código muestra una función IndexTrace de ejemplo que muestra cuánto tiempo se tarda en volver a indexar un archivo de rastreo.
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");
}
Este es el resultado de un pequeño archivo de rastreo.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.IndexTrace()
>>> Trace was indexed in 2 ms
Adición de una instrucción try catch
Para comprobar si se generaron errores cuando se ejecutó la indexación, incluya el código de indexación en una instrucción 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");
}
}
Este es el resultado del script si la indexación se realiza correctamente.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.IndexTraceTry()
>>> Index Return Value: Loaded
>>> Trace was successfully indexed in 1 ms
Si el rastreo no se puede indexar, por ejemplo, si el rastreo no se carga en el depurador, se ejecuta el código del bucle catch.
0:007> dx Debugger.State.Scripts.MyTraceUtils.Contents.IndexTraceTry()
>>> Index Failed!
>>> Index Return Value: undefined
>>> Returned error: TypeError
Consultas de objetos TTD de JavaScript
Un uso más avanzado de JavaScript y TTD es consultar los objetos de viajes en el tiempo para localizar llamadas o eventos específicos que se han producido en el rastreo. Para obtener más información sobre los objetos TTD, consulte:
Introducción a los objetos de depuración de viajes en el tiempo
Objetos de depurador nativos en extensiones de JavaScript: detalles del objeto del depurador
El comando dx muestra información del modelo de datos del depurador y admite consultas mediante la sintaxis LINQ. Dx es muy útil para consultar los objetos en tiempo real. Esto permite la creación de prototipos de la consulta deseada que luego se puede automatizar mediante JavaScript. El comando dx proporciona la función de completar tabulaciones, lo que puede resultar útil al explorar el modelo de objetos. Para obtener información general sobre cómo trabajar con consultas LINQ y objetos del depuradore, consulte Utilización de LINQ con los objetos del depurador.
Este comando dx cuenta todas las llamadas a una determinada API, en este ejemplo GetLastError.
0:000> dx @$cursession.TTD.Calls("kernelbase!GetLastError").Count()
@$cursession.TTD.Calls("kernelbase! GetLastError").Count() : 0x12
Este comando busca en todo el rastreo del viaje en el tiempo para comprobar cuándo se llamó a 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]
Comparaciones de cadenas para el objeto TTD.Calls para buscar llamadas
En este comando de ejemplo se muestra cómo usar comparaciones de cadenas para buscar llamadas específicas. En este ejemplo, la consulta busca la cadena "OLE" en el parámetro lpFileName de la función CreateFileW.
dx -r2 @$cursession.TTD.Calls("kernelbase!CreateFileW").Where(x => x.Parameters.lpFileName.ToDisplayString("su").Contains("OLE"))
Agregue una instrucción .Select para imprimir Timestart y el valor del 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 })
Esto genera este resultado, si se encuentra un objeto TTD.Calls que contiene la información de destino.
[0x0]
TimeStart : 6E37:590
lpFileName : 0x346a78be90 : "C:\WINDOWS\SYSTEM32\OLEACCRC.DLL" [Type: wchar_t *]
Mostrar el número de llamadas en un rastreo
Después de haber usado el comando dx para explorar los objetos con los que desea trabajar, puede automatizar su uso con JavaScript. En este ejemplo sencillo, el objeto TTD.Calls se usa para contar las llamadas 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");
}
Guarde el script en un archivo TTDUtils.js y llámelo con el comando dx para mostrar un recuento de la cantidad de kernelbase!GetLastError en el archivo de rastreo.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.CountLastErrorCalls()
>>> GetLastError calls in this TTD recording: 18
Mostrar los marcos en una pila
Para mostrar los marcos en una pila, se usa una matriz.
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");
}
}
En este rastreo de ejemplo, se muestra la entrada de una pila.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.DisplayStack()
>>> Printing stack
>>> Stack Entry -> 0: ntdll!LdrInitializeThunk + 0x21
Búsqueda de un evento y visualización de la pila
En este código se encuentran todos los eventos de excepción y se usa un bucle para moverse a cada uno. A continuación, el currentThread.ID de los objetos de subproceso TTD se usa para mostrar el ID de subproceso y currentThread.Stack se usa para mostrar todos los fotogramas de la pila.
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");
}
}
El resultado muestra la ubicación del evento de excepción, el TID y los marcos de pila.
0:000> dx Debugger.State.Scripts.MyTraceUtils.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
Búsqueda de un evento y envío de dos comandos
La consulta de objetos TTD y el envío de comandos se pueden combinar según sea necesario. En este ejemplo se busca cada evento en el rastreo TTD de tipo ThreadCreated, se mueve a esa posición y se envían los comandos ~ Thread Status y !runaway para mostrar el estado del subproceso.
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);
}
}
Al ejecutar el código se muestra el estado del subproceso en el momento en que se produjo la excepción.
0:000> dx Debugger.State.Scripts.MyTraceUtils.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
Encadenamiento de funciones de utilidad
En este ejemplo final, podemos llamar a las funciones de utilidad que creamos anteriormente. En primer lugar, indizamos el seguimiento mediante IndexTraceTry y, a continuación, llamamos a ThreadCreateThreadStatus. Después, usamos ResetTrace para pasar al inicio del seguimiento y, por último, llamamos a HardwareExceptionDisplayStack.
function ProcessTTDFiles()
{
try
{
IndexTraceTry()
ThreadCreateThreadStatus()
ResetTrace()
HardwareExceptionDisplayStack()
}
catch(err)
{
host.diagnostics.debugLog("\n >>> Processing of TTD file failed \n");
}
}
Al ejecutar este script en un archivo de rastreo que contiene una excepción de hardware, se genera este resultado.
0:000> dx Debugger.State.Scripts.MyTraceUtils.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 también
Depuración de viajes en el tiempo - Información general
Introducción a los objetos de depuración de viajes en el tiempo
Objetos de depurador nativos en extensiones de JavaScript: detalles del objeto del depurador