Отладка перемещения по времени — автоматизация JavaScript
Автоматизация JavaScript позволяет работать с трассировками TTD несколькими способами, например автоматизацией команд или запросами для поиска данных событий из файла трассировки.
Общие сведения о работе с JavaScript см. в скриптах отладчика JavaScript. Также существуют примеры скриптов отладчика JavaScript.
Автоматизация команд JavaScript TTD
Одним из способов использования JavaScript для автоматизации TTD является отправка команд для автоматизации работы с файлами трассировки времени.
Перемещение в файл трассировки
В этом JavaScript показано, как перейти к началу трассировки перемещения по времени с помощью команды !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");
Мы можем сделать это в функцию ResetTrace и сохранить ее как MyTraceUtils.js с помощью пользовательского интерфейса JavaScript в 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");
}
После загрузки файла TTD в WinDbg вызовите функцию ResetTraceCmd() с помощью команды DX в окне команды отладчика.
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()
Ограничения отправки команд
Но для всех, кроме самых простых ситуаций, подход отправки команд имеет недостатки. Он зависит от использования текстовых выходных данных. И анализ выходных данных приводит к тому, что код является хрупким и трудным для поддержания. Лучший подход — использовать объекты TTD напрямую.
В следующем примере показано, как использовать объекты непосредственно для выполнения той же задачи с помощью объектов напрямую.
// 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");
}
При выполнении этого кода показано, что мы можем перейти к началу файла трассировки.
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
В этом примере функция ResetTraceEnd устанавливается в конец трассировки, а текущее и новое положение отображается с помощью объекта 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");
}
При выполнении этого кода отображается текущая и новая позиция.
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
В этом развернутом примере значения начальной и конечной позиции сравниваются, если позиция в трассировке изменилась.
// 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");
}
}
В этом примере отображается сообщение о том, что все готово в начале файла трассировки.
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
Чтобы протестировать скрипт, используйте команду !tt для перехода по полпути в файле трассировки.
0:000> !tt 50
Setting position to 50% into the trace
Setting position: 71:0
Теперь при выполнении скрипта отображается соответствующее сообщение, указывающее, что позиция была задана в начале трассировки 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
Индексирование файла трассировки перемещения по времени
Если только файл трассировки копируется на другой компьютер, он должен быть переиндексирован. Дополнительные сведения см. в статье "Отладка перемещения по времени" — работа с файлами трассировки.
В этом коде показан пример функции IndexTrace, которая показывает, сколько времени требуется для повторного индексирования файла трассировки.
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");
}
Ниже приведены выходные данные из небольшого файла трассировки.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.IndexTrace()
>>> Trace was indexed in 2 ms
Добавление инструкции try catch
Чтобы проверить, возникают ли ошибки при запуске индексирования, заключите код индексирования в инструкцию 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");
}
}
Ниже приведены выходные данные скрипта, если индексирование выполнено успешно.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.IndexTraceTry()
>>> Index Return Value: Loaded
>>> Trace was successfully indexed in 1 ms
Если трассировка не может быть индексирована, например если трассировка не загружена в отладчик, выполняется код цикла catch.
0:007> dx Debugger.State.Scripts.MyTraceUtils.Contents.IndexTraceTry()
>>> Index Failed!
>>> Index Return Value: undefined
>>> Returned error: TypeError
Запросы объектов JavaScript TTD
Более расширенное использование JavaScript и TTD — запрос объектов перемещения по времени для поиска определенных вызовов или событий, произошедших в трассировке. Дополнительные сведения о объектах TTD см. в следующих статье:
Общие сведения о объектах отладки временных путешествий
Собственные объекты отладчика в расширениях JavaScript — сведения об объекте отладчика
Команда dx отображает сведения из модели данных отладчика и поддерживает запросы с помощью синтаксиса LINQ. Dx очень полезно запрашивать объекты в режиме реального времени. Это позволяет создать прототип требуемого запроса, который затем можно автоматизировать с помощью JavaScript. Команда dx предоставляет завершение вкладки, которое может быть полезно при изучении объектной модели. Общие сведения о работе с запросами LINQ и объектами отладчика см. в разделе "Использование LINQ С объектами отладчика".
Эта команда dx подсчитывает все вызовы определенного API в этом примере GetLastError.
0:000> dx @$cursession.TTD.Calls("kernelbase!GetLastError").Count()
@$cursession.TTD.Calls("kernelbase! GetLastError").Count() : 0x12
Эта команда выглядит во всей трассировке перемещения по времени, чтобы увидеть, когда был вызван 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]
Сравнение строк для TTD. Вызовы объекта для поиска вызовов
В этом примере команды показано, как использовать сравнения строк для поиска конкретных вызовов. В этом примере запрос ищет строку OLE в параметре lpFileName функции CreateFileW.
dx -r2 @$cursession.TTD.Calls("kernelbase!CreateFileW").Where(x => x.Parameters.lpFileName.ToDisplayString("su").Contains("OLE"))
Добавьте . Оператор Select для печати timestart и значения параметра 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 })
Это создает выходные данные, если TTD. Объект вызовов найден, содержащий целевую информацию.
[0x0]
TimeStart : 6E37:590
lpFileName : 0x346a78be90 : "C:\WINDOWS\SYSTEM32\OLEACCRC.DLL" [Type: wchar_t *]
Отображение количества вызовов в трассировке
После использования команды dx для изучения объектов, с которыми вы хотите работать, вы можете автоматизировать их использование с JavaScript. В этом простом примере TTD. Объект "Вызовы" используется для подсчета вызовов к базе ядра! GetLastError.
function CountLastErrorCalls()
{
var LastErrorCalls = host.currentSession.TTD.Calls("kernelbase!GetLastError");
host.diagnostics.debugLog(">>> GetLastError calls in this TTD recording: " + LastErrorCalls.Count() +" \n");
}
Сохраните скрипт в файле TTDUtils.js и вызовите его с помощью команды dx, чтобы отобразить количество ядровой базы! GetLastError в файле трассировки.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.CountLastErrorCalls()
>>> GetLastError calls in this TTD recording: 18
Отображение кадров в стеке
Для отображения кадров в стеке используется массив.
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");
}
}
В этом примере трассировки отображается одна запись стека.
0:000> dx Debugger.State.Scripts.MyTraceUtils.Contents.DisplayStack()
>>> Printing stack
>>> Stack Entry -> 0: ntdll!LdrInitializeThunk + 0x21
Поиск события и отображение стека
В этом коде находятся все события исключений, а цикл используется для перемещения к каждому из них. Затем currentThread.ID объектов потока TTD используется для отображения идентификатора потока и currentThread.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");
}
}
Выходные данные показывают расположение события исключения, TID и кадров стека.
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
Поиск события и отправка двух команд
При необходимости можно объединить запросы к объектам TTD и отправлять команды. Этот пример находит каждое событие в трассировке TTD типа ThreadCreated, перемещается в эту позицию и отправляет команды ~Thread Status и !runaway для отображения состояния потока.
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);
}
}
При выполнении кода отображается состояние потока в момент возникновения исключения.
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
Объединение функций служебной программы
В этом последнем примере можно вызвать функции служебной программы, созданные ранее. Сначала мы индексируем трассировку с помощью IndexTraceTry , а затем вызовем ThreadCreateThreadStatus. Затем мы используем ResetTrace для перехода к началу трассировки и последнего вызова HardwareExceptionDisplayStack.
function ProcessTTDFiles()
{
try
{
IndexTraceTry()
ThreadCreateThreadStatus()
ResetTrace()
HardwareExceptionDisplayStack()
}
catch(err)
{
host.diagnostics.debugLog("\n >>> Processing of TTD file failed \n");
}
}
Выполнение этого скрипта в файле трассировки, содержащего исключение оборудования, создает эти выходные данные.
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
См. также
Отладка временных путешествий — обзор
Общие сведения о объектах отладки временных путешествий
Собственные объекты отладчика в расширениях JavaScript — сведения об объекте отладчика