Penelusuran Kesalahan Perjalanan Waktu - Otomatisasi JavaScript
Anda dapat menggunakan otomatisasi JavaScript untuk bekerja dengan jejak TTD dengan sejumlah cara, seperti otomatisasi perintah atau menggunakan kueri untuk menemukan data peristiwa dari file pelacakan.
Untuk informasi umum tentang bekerja dengan JavaScript, lihat Pembuatan Skrip JavaScript Debugger. Ada juga Contoh Skrip JavaScript Debugger.
Otomatisasi Perintah JavaScript TTD
Salah satu cara menggunakan JavaScript untuk otomatisasi TTD, adalah dengan mengirim perintah untuk mengotomatiskan bekerja dengan file pelacakan perjalanan waktu.
Memindahkan dalam file pelacakan
JavaScript ini menunjukkan cara berpindah ke awal pelacakan perjalanan waktu menggunakan perintah !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");
Kita dapat membuat ini menjadi fungsi ResetTrace, dan menyimpannya sebagai ResetTrace.js, menggunakan UI JavaScript di 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");
}
Setelah file TTD dimuat di WinDbg, panggil fungsi ResetTraceCmd() menggunakan perintah dx di jendela perintah 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()
Batasan pengiriman perintah
Tetapi untuk semua kecuali situasi paling sederhana, pendekatan mengirim perintah memiliki kelemahan. Ini bergantung pada penggunaan output teks. Dan mengurai output itu mengarah ke kode yang rapuh dan sulit untuk dipertahankan. Pendekatan yang lebih baik adalah menggunakan objek TTD secara langsung.
Contoh berikut menunjukkan cara menggunakan objek secara langsung untuk menyelesaikan tugas yang sama menggunakan objek secara langsung.
// 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");
}
Menjalankan kode ini menunjukkan bahwa kita dapat pindah ke awal file pelacakan.
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
Dalam contoh ini fungsi ResetTraceEnd, posisi diatur ke akhir jejak dan posisi saat ini dan baru ditampilkan menggunakan objek PosisiThread.TTD saat ini.
// 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");
}
Menjalankan kode ini menampilkan posisi saat ini dan baru.
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
Dalam sampel yang diperluas ini, nilai posisi awal dan akhir dibandingkan dengan melihat apakah posisi dalam jejak berubah.
// 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");
}
}
Dalam contoh ini, pesan ditampilkan bahwa kita semua siap di awal file pelacakan.
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
Untuk menguji skrip, gunakan perintah !tt untuk menavigasi setengah jalan dalam file pelacakan.
0:000> !tt 50
Setting position to 50% into the trace
Setting position: 71:0
Menjalankan skrip sekarang menampilkan pesan yang tepat yang menunjukkan bahwa posisi diatur ke awal pelacakan 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
Mengindeks file pelacakan perjalanan waktu
Jika hanya file pelacakan yang disalin ke PC yang berbeda, file tersebut harus diindeks ulang. Untuk informasi selengkapnya, lihat Penelusuran Kesalahan Perjalanan Waktu - Bekerja dengan File Pelacakan.
Kode ini menunjukkan contoh fungsi IndexTrace yang menampilkan berapa lama waktu yang diperlukan untuk mengindeks ulang file pelacakan.
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");
}
Berikut adalah output dari file pelacakan kecil.
0:000> dx Debugger.State.Scripts.TTDUtils.Contents.IndexTrace()
>>> Trace was indexed in 2 ms
Menambahkan pernyataan coba tangkapan
Untuk memeriksa apakah kesalahan dimunculkan saat pengindeksan dijalankan, sertakan kode pengindeksan dalam pernyataan coba tangkap.
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");
}
}
Berikut adalah output skrip jika pengindeksan berhasil.
0:000> dx Debugger.State.Scripts.TTDUtils.Contents.IndexTraceTry()
>>> Index Return Value: Loaded
>>> Trace was successfully indexed in 1 ms
Jika pelacakan tidak dapat diindeks, misalnya jika jejak tidak dimuat dalam debugger, kode perulangan tangkapan dijalankan.
0:007> dx Debugger.State.Scripts.TTDUtils.Contents.IndexTraceTry()
>>> Index Failed!
>>> Index Return Value: undefined
>>> Returned error: TypeError
Kueri Objek TTD JavaScript
Penggunaan JavaScript dan TTD yang lebih canggih adalah mengkueri objek perjalanan waktu untuk menemukan panggilan atau peristiwa tertentu yang telah terjadi dalam pelacakan. Untuk informasi selengkapnya tentang objek TTD, lihat:
Pengantar objek Time Travel Debugging
Objek Debugger Asli di Ekstensi JavaScript - Detail Objek Debugger
Perintah dx menampilkan informasi dari model data debugger dan mendukung kueri menggunakan sintaks LINQ. Dx sangat berguna untuk mengkueri objek secara realtime. Ini memungkinkan prototipe kueri yang diinginkan yang kemudian dapat diotomatisasi menggunakan JavaScript. Perintah dx menyediakan penyelesaian tab, yang dapat membantu saat menjelajahi model objek. Untuk informasi umum tentang bekerja dengan kueri LINQ dan objek debugger, lihat Menggunakan LINQ Dengan objek debugger.
Perintah dx ini, menghitung semua panggilan ke API tertentu, dalam contoh ini GetLastError.
0:000> dx @$cursession.TTD.Calls("kernelbase!GetLastError").Count()
@$cursession.TTD.Calls("kernelbase! GetLastError").Count() : 0x12
Perintah ini terlihat di seluruh jejak perjalanan waktu untuk melihat kapan GetLastError dipanggil.
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]
Perbandingan string untuk TTD. Memanggil Objek untuk menemukan panggilan
Contoh perintah ini, menunjukkan cara menggunakan perbandingan string untuk menemukan panggilan tertentu. Dalam contoh ini, kueri mencari string "OLE" dalam parameter lpFileName dari fungsi CreateFileW.
dx -r2 @$cursession.TTD.Calls("kernelbase!CreateFileW").Where(x => x.Parameters.lpFileName.ToDisplayString("su").Contains("OLE"))
Tambahkan . Pilih pernyataan untuk mencetak Timestart dan nilai parameter 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 })
Ini menghasilkan output ini, jika TTD. Objek panggilan ditemukan yang berisi informasi target.
[0x0]
TimeStart : 6E37:590
lpFileName : 0x346a78be90 : "C:\WINDOWS\SYSTEM32\OLEACCRC.DLL" [Type: wchar_t *]
Menampilkan jumlah panggilan dalam pelacakan
Setelah Anda menggunakan perintah dx untuk menjelajahi objek yang ingin Anda kerjakan, Anda dapat mengotomatiskan penggunaannya dengan JavaScript. Dalam contoh sederhana ini, TTD. Objek panggilan digunakan untuk menghitung panggilan ke kernelbase! GetLastError.
function CountLastErrorCalls()
{
var LastErrorCalls = host.currentSession.TTD.Calls("kernelbase!GetLastError");
host.diagnostics.debugLog(">>> GetLastError calls in this TTD recording: " + LastErrorCalls.Count() +" \n");
}
Simpan skrip dalam file TTDUtils.js dan panggil menggunakan perintah dx untuk menampilkan hitungan jumlah kernelbase! GetLastError dalam file pelacakan.
0:000> dx Debugger.State.Scripts.TTDUtils.Contents.CountLastErrorCalls()
>>> GetLastError calls in this TTD recording: 18
Menampilkan bingkai dalam tumpukan
Untuk menampilkan bingkai dalam tumpukan, array digunakan.
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");
}
}
Dalam jejak sampel ini, satu entri tumpukan ditampilkan.
0:000> dx Debugger.State.Scripts.TTDUtils.Contents.DisplayStack()
>>> Printing stack
>>> Stack Entry -> 0: ntdll!LdrInitializeThunk + 0x21
Menemukan peristiwa dan menampilkan tumpukan
Dalam kode ini semua peristiwa pengecualian berada dan perulangan digunakan untuk berpindah ke masing-masing peristiwa. Kemudian currentThread.ID Objek Utas TTD digunakan untuk menampilkan ID utas dan currentThread.Stack digunakan untuk menampilkan semua bingkai dalam tumpukan.
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");
}
}
Output menunjukkan lokasi peristiwa pengecualian, TID, dan bingkai tumpukan.
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
Menemukan peristiwa dan mengirim dua perintah
Mengkueri objek TTD dan mengirim perintah dapat digabungkan seperlunya. Contoh ini menemukan setiap peristiwa dalam jejak TTD jenis ThreadCreated, berpindah ke posisi tersebut , dan mengirim Status ~ Thread dan perintah !runaway untuk menampilkan status utas.
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);
}
}
Menjalankan kode menampilkan status utas pada saat pengecualian terjadi.
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
Menautkan fungsi utilitas bersama-sama
Dalam sampel akhir ini, kita dapat memanggil fungsi utilitas yang kita buat sebelumnya. Pertama, kita mengindeks jejak menggunakan IndexTraceTry lalu memanggil ThreadCreateThreadStatus. Kami kemudian menggunakan ResetTrace untuk berpindah ke awal jejak dan terakhir memanggil HardwareExceptionDisplayStack.
function ProcessTTDFiles()
{
try
{
IndexTraceTry()
ThreadCreateThreadStatus()
ResetTrace()
HardwareExceptionDisplayStack()
}
catch(err)
{
host.diagnostics.debugLog("\n >>> Processing of TTD file failed \n");
}
}
Menjalankan skrip ini pada file pelacakan yang berisi pengecualian perangkat keras, menghasilkan output ini.
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
Lihat Juga
Penelusuran Kesalahan Perjalanan Waktu - Gambaran Umum
Pengantar objek Time Travel Debugging
Objek Debugger Asli di Ekstensi JavaScript - Detail Objek Debugger