Time Travel Hibakeresés – JavaScript Automatizálás

Time Travel hibakeresési emblémája, amely egy órát tartalmaz.

A JavaScript automation használatával többféleképpen is használhat TTD-nyomkövetéseket, például parancsautomatizálást vagy lekérdezéseket a nyomkövetési fájl eseményadatainak megkereséséhez.

A JavaScript használatával kapcsolatos általános információkért lásd a JavaScript Hibakereső szkriptkészítést. Vannak JavaScript Hibakereső példaszkriptek is.

JavaScript TTD parancsautomatizálás

A JavaScript TTD-automatizáláshoz való használatának egyik módja, ha parancsokat küld az időutazási nyomkövetési fájlok használatának automatizálásához.

Mozgás egy nyomkövetési fájlban

Ez a JavaScript bemutatja, hogyan válthat az időutazási nyomkövetés elejére a !tt paranccsal.

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

Ezt egy ResetTrace függvénybe állíthatjuk be, és MyTraceUtils.jsformátumban menthetjük a WinDbg JavaScript felhasználói felületével.

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

Miután betöltött egy TTD-fájlt a WinDbg-be, hívja meg a ResetTraceCmd() függvényt a hibakereső parancsablak dx parancsával.

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

A parancsok küldésének korlátozásai

A legegyszerűbb helyzeteken kívül azonban a parancsok küldésének megközelítése hátrányokat jelent. A szövegkimenet használatára támaszkodik. A kimenet elemzése pedig törékeny és nehezen karbantartható kódhoz vezet. Jobb módszer a TTD-objektumok közvetlen használata.

Az alábbi példa bemutatja, hogyan használhatja közvetlenül az objektumokat ugyanazon feladat közvetlen végrehajtására az objektumok használatával.

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

A kód futtatása azt mutatja, hogy át tudunk lépni a nyomkövetési fájl elejére.

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

Ebben a példában a ResetTraceEnd függvény a nyomkövetés végére állítja a pozíciót, az aktuális és az új pozíció pedig a currentThread.TTD Position objektummal jelenik meg.


// 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 kód futtatása az aktuális és az új pozíciót jeleníti meg.

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

Ebben a kibontott mintában a kezdő és a záró pozíció értékeit hasonlítjuk össze, hogy kiderüljön, változott-e a nyomkövetési pozíció.

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

A példafuttatás során egy olyan üzenet jelenik meg, amely szerint a nyomkövetési fájl elején mindannyian készen voltunk.

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

A szkript teszteléséhez a !tt paranccsal navigáljon félúton a nyomkövetési fájlban.

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

A szkript futtatása ekkor megjeleníti a megfelelő üzenetet, amely azt jelzi, hogy a pozíció a TTD-nyomkövetés kezdetére lett beállítva.

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  

Időutazási nyomkövetési fájl indexelése

Ha csak egy nyomkövetési fájlt másol át egy másik számítógépre, újra kell indexelni. További információ: Time Travel Hibakeresés – Nyomkövetési fájlok használata.

Ez a kód egy példa IndexTrace függvényt mutat be, amely megjeleníti, hogy mennyi ideig tart egy nyomkövetési fájl újraindexelése.

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

Íme egy kis nyomkövetési fájl kimenete.

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

>>> Trace was indexed in 2 ms

Try catch utasítás hozzáadása

Ha ellenőrizni szeretné, hogy az indexelés futtatásakor hibák jelentkeztek-e, helyezze az indexelési kódot egy try-catch blokkba.


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

Ha az indexelés sikeres, az alábbi parancsprogram kimenete jelenik meg.

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

>>> Index Return Value: Loaded

>>> Trace was successfully indexed in 1 ms

Ha a nyomkövetés nem indexelhető, például ha a nyomkövetés nincs betöltve a hibakeresőbe, futtatják az elkapási ciklus kódját.

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

>>> Index Failed!

>>> Index Return Value: undefined

>>> Returned error: TypeError

JavaScript TTD-objektumok lekérdezései

A JavaScript és a TTD fejlettebb használata az időutazási objektumok lekérdezése a nyomkövetésben történt hívások vagy események megkereséséhez. A TTD-objektumokkal kapcsolatos további információkért lásd:

Bevezetés a Time Travel hibakeresési objektumaiba

Natív hibakereső objektumok JavaScript-bővítményekben – Hibakereső objektum részletei

A dx parancs megjeleníti a hibakereső adatmodellből származó információkat, és támogatja a LINQ szintaxist használó lekérdezéseket. A Dx nagyon hasznos az objektumok valós idejű lekérdezéséhez. Ez lehetővé teszi a kívánt lekérdezés prototípusát, amelyet aztán a JavaScript használatával automatizálhat. A dx parancs lapkiegészítést biztosít, ami hasznos lehet az objektummodell feltárásakor. A LINQ-lekérdezések és hibakereső objektumok használatával kapcsolatos általános információkért tekintse meg a LINQ használata a hibakereső objektumokkal című témakört.

Ez a dx parancs megszámolja egy adott API összes hívását ebben a példában : GetLastError.

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

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

Ez a parancs a teljes időutazási nyomkövetésben megtekinti, hogy mikor lett meghívva 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]

Szövegösszehasonlítások a TTD.Calls objektumhoz a hívások megtalálásához

Ez a példaparancs bemutatja, hogyan használhat sztring-összehasonlításokat adott hívások megkereséséhez. Ebben a példában a lekérdezés az "OLE" sztringet keresi a CreateFileW függvény lpFileName paraméterében.

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

Adjon hozzá egy .Select utasítást a Timestart és az lpFileName paraméter értékének nyomtatásához.

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

Ez generálja ezt a kimenetet, ha található egy TTD.Calls objektum, amely tartalmazza a céladatokat.

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

A hívások számának megjelenítése nyomkövetésben

Miután a dx paranccsal feltárta a használni kívánt objektumokat, automatizálhatja a JavaScripttel való használatukat. Ebben az egyszerű példában a TTD.Calls objektumot a kernelbase!GetLastError hívások számolására használjuk.

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

Mentse a szkriptet egy TTDUtils.js fájlba, és hívja meg a dx paranccsal, hogy megjelenítse a kernelbase!GetLastError előfordulásainak számát a nyomkövetési fájlban.


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

A keretek megjelenítése halmazban

A keretek veremben való megjelenítéséhez tömböt használunk.

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

Ebben a mintakövetésben megjelenik az egyetlen verembejegyzés.

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

Esemény megkeresése és a verem megjelenítése

Ebben a kódban az összes kivételesemény található, és a rendszer egy hurkot használ az egyes eseményekre való áttéréshez. A TTD thread objektumok currentThread.ID a szálazonosító megjelenítésére szolgál, míg a currentThread.Stack a verem összes képkockájának megjelenítésére szolgál.


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 kimenet a kivételi esemény helyét, a TID-t és a veremkereteket jeleníti meg.

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

Esemény keresése és két parancs küldése

A TTD-objektumok lekérdezése és a parancsok küldése szükség szerint kombinálható. Ez a példa megkeresi az egyes eseményeket a ThreadCreated típusú TTD-nyomkövetésben, erre a pozícióra lép, és elküldi a ~ Szál állapota és a !runaway parancsokat a szál állapotának megjelenítéséhez.

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 kód futtatása megjeleníti a szál állapotát abban a pillanatban, amikor a kivétel történt.

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

A segédfüggvények összefűzése

Ebben az utolsó mintában meghívhatjuk a korábban létrehozott segédprogramfüggvényeket. Először indexeljük a nyomkövetést az IndexTraceTry használatával, majd meghívjuk a ThreadCreateThreadStatus parancsot. Ezután a ResetTrace használatával lépünk a nyomkövetés elejére, és végül meghívjuk a HardwareExceptionDisplayStack parancsot.

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

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

}

Egy nyomkövetési fájlon futtatva, amely hardverkivételt tartalmaz, ez a script ezt a kimenetet hozza létre.

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

Lásd még:

Time Travel Hibakeresés – Áttekintés

Bevezetés a Time Travel hibakeresési objektumaiba

Natív hibakereső objektumok JavaScript-bővítményekben – Hibakereső objektum részletei

JavaScript-hibakereső szkriptelése

JavaScript-hibakereső példaszkriptjei