共用方式為


時間移動偵錯 - 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 函式,並使用 WinDbg 中的 JavaScript UI 將其儲存為 ResetTrace.js。

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

在 WinDbg 中載入 TTD 檔案之後,請在除錯程式命令視窗中使用 dx 命令呼叫 Function ResetTraceCmd () 函式。

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

傳送命令的限制

但對於最簡單的情況而言,傳送命令的方法有缺點。 它依賴文字輸出的使用。 而剖析該輸出會導致程式代碼變得很困難且難以維護。 更好的方法是直接使用TTD物件。

下列範例示範如何直接使用 物件,直接使用 物件來完成相同的工作。

// 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.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

在此範例 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.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

在此展開的範例中,開始和結束位置值會進行比較,以查看追蹤中的位置是否已變更。

// 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.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

若要測試文稿,請使用 !tt 命令在追蹤檔案中流覽一半。

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

執行腳本現在會顯示適當的訊息,指出位置已設定為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  

編製時間移動追蹤檔案的索引

如果只將追蹤檔案複製到不同的計算機,則必須重新編製索引。 如需詳細資訊,請參閱 時間移動偵錯 - 使用追蹤檔案

此程式代碼顯示一個範例 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.TTDUtils.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.TTDUtils.Contents.IndexTraceTry()

>>> Index Return Value: Loaded

>>> Trace was successfully indexed in 1 ms

如果追蹤無法編製索引,例如,如果在調試程式中未載入追蹤,則會執行 catch 循環程序代碼。

0:007> dx Debugger.State.Scripts.TTDUtils.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 的字串比較。呼叫 物件以尋找呼叫

這個範例命令示範如何使用字串比較來找出特定呼叫。 在此範例中,查詢會在 CreateFileW 函式的 lpFileName 參數中尋找字串 “OLE”。

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

新增 。選取語句以列印 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。呼叫 物件可用來計算 對 kernelbase 的呼叫!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.TTDUtils.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.TTDUtils.Contents.DisplayStack()
>>> Printing stack
>>> Stack Entry -> 0:  ntdll!LdrInitializeThunk + 0x21

尋找事件並顯示堆疊

在此程式代碼中,所有例外狀況事件都位於 ,並使用循環來移動至每一個事件。 然後, TTD 線程物件的 currentThread.ID 會用來顯示線程標識碼,並使用 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.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

尋找事件並傳送兩個命令

您可以視需要查詢 TTD 對象和傳送命令。 本範例會在 ThreadCreated 類型的 TTD 追蹤中找出每個事件,移至該位置,並傳送 ~ 線程狀態!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.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

將公用程式函式鏈結在一起

在此最後一個範例中,我們可以呼叫我們稍早建立的公用程式函式。 首先,我們會使用 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.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


另請參閱

時間移動偵錯 - 概觀

時間移動偵錯對象的簡介

JavaScript 延伸模組中的原生調試程序物件 - 調試程式對象詳細數據

JavaScript 調試程式腳本

JavaScript 調試程式範例腳本