Skript för JavaScript-felsökningsprogram

Det här avsnittet beskriver hur du använder JavaScript för att skapa skript som förstår felsökningsobjekt och utökar och anpassar funktionerna i felsökningsprogrammet.

Översikt över Skript för JavaScript-felsökningsprogram

Skriptprovidrar överbryggar ett skriptspråk till felsökarens interna objektmodell. JavaScript-skriptprovidern för felsökning tillåter användning av JavaScript med felsökningsprogrammet.

När ett JavaScript läses in via kommandot .scriptload körs rotkoden för skriptet, namnen som finns i skriptet bryggs till rotnamnområdet för felsökningsprogrammet (dx Debugger) och skriptet förblir kvar i minnet tills det tas bort och alla referenser till dess objekt släpps. Skriptet kan ge nya funktioner till felsökarens uttrycksutvärderare, ändra objektmodellen för felsökningsprogrammet eller fungera som visualiserare på ungefär samma sätt som en NatVis-visualiserare.

I det här avsnittet beskrivs en del av vad du kan göra med JavaScript-felsökningsskript.

De här två avsnitten innehåller ytterligare information om hur du arbetar med JavaScript i felsökningsprogrammet.

Exempelskript för JavaScript-felsökningsprogram

Inbyggda objekt i JavaScript-tillägg

JavaScript-skriptvideo

Defrag Tools #170 – Andy och Bill demonstrerar JavaScript-utökningsbarhet och skriptfunktioner i felsökningsprogrammet.

JavaScript-providern för felsökningsprogrammet

JavaScript-providern som ingår i felsökningsprogrammet drar full nytta av de senaste förbättringarna av ECMAScript6-objektet och -klassen. Mer information finns i ECMAScript 6 – Nya funktioner: Översikt och jämförelse.

JsProvider.dll

JsProvider.dll är JavaScript-leverantören som läses in för att stödja JavaScript Debugger Scripting.

Requirements

JavaScript Debugger Scripting är utformat för att fungera med alla versioner av Windows som stöds.

Laddar JavaScript-skriptleverantören

Innan du använder något av .script-kommandona behöver en skriptleverantör laddas in. Använd kommandot .scriptproviders för att bekräfta att JavaScript-providern har lästs in.

0:000> .scriptproviders
Available Script Providers:
    NatVis (extension '.NatVis')
    JavaScript (extension '.js')

Metakommandon för JavaScript-skript

Följande kommandon är tillgängliga för att använda JavaScript-felsökarskript.

Requirements

Innan du använder något av .script-kommandona måste en skriptleverantör läsas in. Använd kommandot .scriptproviders för att bekräfta att JavaScript-providern har lästs in.

0:000> .scriptproviders
Available Script Providers:
    NatVis (extension '.NatVis')
    JavaScript (extension '.js')

.scriptproviders (listskriptleverantörer)

Kommandot .scriptproviders visar en lista över alla skriptspråk som för närvarande tolkas av felsökaren och tillägget som de är registrerade under.

I exemplet nedan laddas JavaScript- och NatVis-leverantörerna.

0:000> .scriptproviders
Available Script Providers:
    NatVis (extension '.NatVis')
    JavaScript (extension '.js')

Alla filer som slutar på ". NatVis" förstås som ett NatVis-skript och alla filer som slutar med ".js" förstås som ett JavaScript-skript. Båda typerna av skript kan läsas in med kommandot .scriptload.

Mer information finns i .scriptproviders (List Script Providers)

.scriptload (Läs in skript)

Kommandot .scriptload läser in ett skript och kör rotkoden för ett skript och funktionen initializeScript . Om det finns fel i den första inläsningen och körningen av skriptet visas felen i konsolen. Följande kommando visar den lyckade laddningen av TestScript.js.

0:000> .scriptload C:\WinDbg\Scripts\TestScript.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\TestScript.js'

Alla objektmodellmanipuleringar som görs av skriptet finns kvar tills skriptet därefter tas bort eller körs igen med annat innehåll.

Mer information finns i .scriptload (Load Script)

.scriptrun

Kommandot .scriptrun läser in ett skript, kör rotkoden för skriptet, initieraScript och invokeScript-funktionen . Om det finns fel i den första inläsningen och körningen av skriptet visas felen i konsolen.

0:000> .scriptrun C:\WinDbg\Scripts\helloWorld.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\helloWorld.js'
Hello World!  We are in JavaScript!

Alla manipulationer av felsökningsobjektmodellen som görs av skriptet kommer att vara kvar tills skriptet därefter tas bort eller körs igen med annat innehåll.

Mer information finns i .scriptrun (Kör skript).

.scriptunload (Ta bort skript)

Kommandot .scriptunload tar bort ett inläst skript och anropar funktionen uninitializeScript . Använd följande kommandosyntax för att ta bort ett skript

0:000:x86> .scriptunload C:\WinDbg\Scripts\TestScript.js
JavaScript script unloaded from 'C:\WinDbg\Scripts\TestScript.js'

Mer information finns i .scriptunload (Ta bort skript).

.scriptlist (Lista inlästa skript)

Kommandot .scriptlist visar alla skript som har lästs in via kommandot .scriptload eller .scriptrun. Om TestScript har lästs in med hjälp av .scriptload visar kommandot .scriptlist namnet på det inlästa skriptet.

0:000> .scriptlist
Command Loaded Scripts:
    JavaScript script from 'C:\WinDbg\Scripts\TestScript.js'

Mer information finns i .scriptlist (List Loaded Scripts).

Kom igång med Skript för JavaScript-felsökningsprogram

HelloWorld-exempelskript

I det här avsnittet beskrivs hur du skapar och kör ett enkelt JavaScript-felsökningsskript som skriver ut Hello World.

// WinDbg JavaScript sample
// Prints Hello World
function initializeScript()
{
    host.diagnostics.debugLog("***> Hello World! \n");
}

Använd en textredigerare som Anteckningar för att skapa en textfil med namnet HelloWorld.js som innehåller JavaScript-koden som visas ovan.

Använd kommandot .scriptload för att läsa in och köra skriptet. Eftersom vi använde funktionsnamnet initializeScript körs koden i funktionen när skriptet läses in.

0:000> .scriptload c:\WinDbg\Scripts\HelloWorld.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\HelloWorld.js'
***> Hello World! 

När skriptet har lästs in är ytterligare funktioner tillgängliga i felsökningsprogrammet. Använd kommandot dx (Display NatVis Expression) för att visa Debugger.State.Scripts för att se att vårt skript nu är bosatt.

0:000> dx Debugger.State.Scripts
Debugger.State.Scripts                
    HelloWorld 

I nästa exempel lägger vi till och anropar en namngiven funktion.

Lägga till exempelskript för två värden

I det här avsnittet beskrivs hur du skapar och kör ett enkelt JavaScript-debuggerskript som tar indata och adderar två tal.

Det här enkla skriptet innehåller en enda funktion, addTwoValues.

// WinDbg JavaScript sample
// Adds two functions
function addTwoValues(a, b)
 {
     return a + b;
 }

Använd en textredigerare som Anteckningar för att skapa en textfil med namnet FirstSampleFunction.js

Använd kommandot .scriptload för att läsa in skriptet.

0:000> .scriptload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'

När skriptet har lästs in är ytterligare funktioner tillgängliga i felsökningsprogrammet. Använd kommandot dx (Display NatVis Expression) för att visa Debugger.State.Scripts för att se att vårt skript nu är bosatt.

0:000> dx Debugger.State.Scripts
Debugger.State.Scripts                
    FirstSampleFunction    

Vi kan välja FirstSampleFunction för att se vilka funktioner den tillhandahåller.

0:000> dx -r1 -v Debugger.State.Scripts.FirstSampleFunction.Contents
Debugger.State.Scripts.FirstSampleFunction.Contents                 : [object Object]
    host             : [object Object]
    addTwoValues    
 ... 

Om du vill göra skriptet lite enklare att arbeta med tilldelar du en variabel i felsökningsprogrammet för att lagra innehållet i skriptet med hjälp av dx-kommandot.

0:000> dx @$myScript = Debugger.State.Scripts.FirstSampleFunction.Contents

Använd dx-uttrycksutvärderingen för att anropa funktionen addTwoValues.

0:000> dx @$myScript.addTwoValues(10, 41),d
@$myScript.addTwoValues(10, 41),d : 51

Du kan också använda @$scriptContents inbyggda alias för att arbeta med skripten. Aliaset @$scriptContents sammanfogar alla . Innehållet i alla skript som läses in.

0:001> dx @$scriptContents.addTwoValues(10, 40),d
@$scriptContents.addTwoValues(10, 40),d : 50

När du är klar med att arbeta med skriptet använder du kommandot .scriptunload för att ta bort skriptet.

0:000> .scriptunload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully unloaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'

Felsökningskommandoautomatisering

I det här avsnittet beskrivs hur du skapar och kör ett enkelt JavaScript-debuggerscript som automatiserar sändningen av u (Unassemble)-kommandot. Exemplet visar också hur du samlar in och visar kommandoutdata i en loop.

Det här skriptet innehåller en enda funktion, RunCommands().

// WinDbg JavaScript sample
// Shows how to call a debugger command and display results
"use strict";

function RunCommands()
{
var ctl = host.namespace.Debugger.Utility.Control;   
var output = ctl.ExecuteCommand("u");
host.diagnostics.debugLog("***> Displaying command output \n");

for (var line of output)
   {
   host.diagnostics.debugLog("  ", line, "\n");
   }

host.diagnostics.debugLog("***> Exiting RunCommands Function \n");

}

Använd en textredigerare som Anteckningar för att skapa en textfil med namnet RunCommands.js

Använd kommandot .scriptload för att läsa in RunCommands-skriptet.

0:000> .scriptload c:\WinDbg\Scripts\RunCommands.js 
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\RunCommands.js'

När skriptet har lästs in är ytterligare funktioner tillgängliga i felsökningsprogrammet. Använd kommandot dx (Display NatVis Expression) för att visa Debugger.State.Scripts.RunCommands för att se att skriptet nu är bosatt.

0:000>dx -r3 Debugger.State.Scripts.RunCommands
Debugger.State.Scripts.RunCommands                
    Contents         : [object Object]
        host             : [object Object]
            diagnostics      : [object Object]
            namespace       
            currentSession   : Live user mode: <Local>
            currentProcess   : notepad.exe
            currentThread    : ntdll!DbgUiRemoteBreakin (00007ffd`87f2f440) 
            memory           : [object Object]

Använd dx-kommandot för att anropa funktionen RunCommands i RunCommands-skriptet.

0:000> dx Debugger.State.Scripts.RunCommands.Contents.RunCommands()
  ***> Displaying command output
  ntdll!ExpInterlockedPopEntrySListEnd+0x17 [d:\rs1\minkernel\ntos\rtl\amd64\slist.asm @ 196]:
  00007ffd`87f06e67 cc              int     3
  00007ffd`87f06e68 cc              int     3
  00007ffd`87f06e69 0f1f8000000000  nop     dword ptr [rax]
  ntdll!RtlpInterlockedPushEntrySList [d:\rs1\minkernel\ntos\rtl\amd64\slist.asm @ 229]:
  00007ffd`87f06e70 0f0d09          prefetchw [rcx]
  00007ffd`87f06e73 53              push    rbx
  00007ffd`87f06e74 4c8bd1          mov     r10,rcx
  00007ffd`87f06e77 488bca          mov     rcx,rdx
  00007ffd`87f06e7a 4c8bda          mov     r11,rdx
***> Exiting RunCommands Function

Särskilda JavaScript-felsökningsfunktioner

Det finns flera specialfunktioner i ett JavaScript-skript som anropas av själva skriptprovidern.

initializeScript

När ett JavaScript-skript läses in och körs går det igenom en serie steg innan variabler, funktioner och andra objekt i skriptet påverkar felsökarens objektmodell.

  • Skriptet läses in i minnet och parsas.
  • Rotkoden i skriptet körs.
  • Om skriptet har en metod som kallas initializeScript anropas den metoden.
  • Returvärdet från initializeScript används för att fastställa hur objektmodellen för felsökningsprogrammet ska ändras automatiskt.
  • Namnen i skriptet bryggs till felsökarens namnområde.

Som nämnts anropas initializeScript omedelbart efter att rotkoden för skriptet har körts. Dess jobb är att returnera en JavaScript-matris med registreringsobjekt till providern som anger hur du ändrar objektmodellen för felsökningsprogrammet.

function initializeScript()
{
    // Add code here that you want to run every time the script is loaded. 
    // We will just send a message to indicate that function was called.
    host.diagnostics.debugLog("***> initializeScript was called\n");
}

invokeScript

Metoden invokeScript är den primära skriptmetoden och anropas när .scriptload och .scriptrun körs.

function invokeScript()
{
    // Add code here that you want to run every time the script is executed. 
    // We will just send a message to indicate that function was called.
    host.diagnostics.debugLog("***> invokeScript was called\n");
}

uninitializeScript

Metoden uninitializeScript är den beteendemässiga motsatsen till initializeScript. Det anropas när ett skript är olänkat och gör sig redo att avlasta. Dess uppgift är att ångra alla ändringar i objektmodellen som skriptet gjorde imperativt under körningen och/eller att förstöra alla objekt som skriptet cachelagrade.

Om ett skript varken utför imperativa manipuleringar av objektmodellen eller cachelagrar resultat behöver det inte ha någon uninitializeScript-metod. Ändringar i objektmodellen som utförs enligt returvärdet för initializeScript ångras automatiskt av providern. Sådana ändringar kräver ingen explicit uninitializeScript-metod.

function uninitializeScript()
{
    // Add code here that you want to run every time the script is unloaded. 
    // We will just send a message to indicate that function was called.
    host.diagnostics.debugLog("***> uninitialize was called\n");
}

Sammanfattning av funktioner som anropas av skriptkommandon

Den här tabellen sammanfattar vilka funktioner som anropas av skriptkommandona

Command .scriptload .scriptrun (Kör skript) .scriptunload (Ta bort skript)
rot Ja Ja
initializeScript Ja Ja
invokeScript Ja
uninitializeScript Ja

Använd den här exempelkoden för att se när varje funktion anropas när skriptet läses in, körs och tas bort.

// Root of Script
host.diagnostics.debugLog("***>; Code at the very top (root) of the script is always run \n");


function initializeScript()
{
    // Add code here that you want to run every time the script is loaded. 
    // We will just send a message to indicate that function was called.
    host.diagnostics.debugLog("***>; initializeScript was called \n");
}

function invokeScript()
{
    // Add code here that you want to run every time the script is executed. 
    // We will just send a message to indicate that function was called.
    host.diagnostics.debugLog("***>; invokeScript was called \n");
}


function uninitializeScript()
{
    // Add code here that you want to run every time the script is unloaded. 
    // We will just send a message to indicate that function was called.
    host.diagnostics.debugLog("***>; uninitialize was called\n");
}


function main()
{
    // main is just another function name in JavaScript
    // main is not called by .scriptload or .scriptrun  
    host.diagnostics.debugLog("***>; main was called \n");
}

Skapa en felsökningsvisualiserare i JavaScript

Med anpassade visualiseringsfiler kan du gruppera och organisera data i en visualiseringsstruktur som bättre återspeglar datarelationerna och innehållet. Du kan använda JavaScript-felsökningstilläggen för att skriva felsökningsvisualiserare som fungerar på ett sätt som liknar NatVis. Detta görs genom redigering av ett JavaScript-prototypobjekt (eller en ES6-klass) som fungerar som visualiserare för en viss datatyp. Mer information om NatVis och felsökningsprogrammet finns i dx (Visa NatVis-uttryck).

Exempelklass – Simple1DArray

Tänk dig ett exempel på en C++-klass som representerar en endimensionell matris. Den här klassen har två medlemmar, m_size som är matrisens totala storlek och m_pValues som är en pekare till ett antal ints i minnet som är lika med det m_size fältet.

class Simple1DArray
{
private:

    ULONG64 m_size;
    int *m_pValues;
};

Vi kan använda dx-kommandot för att titta på standardåtergivningen av datastrukturen.

0:000> dx g_array1D
g_array1D                 [Type: Simple1DArray]
    [+0x000] m_size           : 0x5 [Type: unsigned __int64]
    [+0x008] m_pValues        : 0x8be32449e0 : 0 [Type: int *]

JavaScript Visualizer

För att visualisera den här typen måste vi skapa en prototypklass (eller ES6) som har alla fält och egenskaper som vi vill att felsökningsprogrammet ska visa. Vi måste också låta metoden initializeScript returnera ett objekt som instruerar JavaScript-providern att länka vår prototyp som visualiserare för den angivna typen.

function initializeScript()
{
    //
    // Define a visualizer class for the object.
    //
    class myVisualizer
    {
        //
        // Create an ES6 generator function which yields back all the values in the array.
        //
        *[Symbol.iterator]()
        {
            var size = this.m_size;
            var ptr = this.m_pValues;
            for (var i = 0; i < size; ++i)
            {
                yield ptr.dereference();

                //
                // Note that the .add(1) method here is effectively doing pointer arithmetic on
                // the underlying pointer.  It is moving forward by the size of 1 object.
                //
                ptr = ptr.add(1);
            }
        }
    }

    return [new host.typeSignatureRegistration(myVisualizer, "Simple1DArray")];
}

Spara skriptet i en fil med namnet arrayVisualizer.js.

Använd kommandot .load (Load Extension DLL) för att läsa in JavaScript-providern.

0:000> .load C:\ScriptProviders\jsprovider.dll

Använd .scriptload för att läsa in matrisvisualiserarskriptet.

0:000> .scriptload c:\WinDbg\Scripts\arrayVisualizer.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\arrayVisualizer.js'

Nu när dx-kommandot används visar skriptvisualiseraren rader med matrisinnehåll.

0:000> dx g_array1D
g_array1D                 : [object Object] [Type: Simple1DArray]
    [<Raw View>]     [Type: Simple1DArray]
    [0x0]            : 0x0
    [0x1]            : 0x1
    [0x2]            : 0x2
    [0x3]            : 0x3
    [0x4]            : 0x4

Dessutom tillhandahåller den här JavaScript-visualiseringen LINQ-funktioner, till exempel Välj.

0:000> dx g_array1D.Select(n => n * 3),d
g_array1D.Select(n => n * 3),d                
    [0]              : 0
    [1]              : 3
    [2]              : 6
    [3]              : 9
    [4]              : 12

Vad påverkar visualiseringen

En prototyp eller klass som görs till visualiserare för en intern typ via en retur av ett host.typeSignatureRegistration-objekt från initializeScript kommer att ha alla egenskaper och metoder i JavaScript tillagt till den inbyggda typen. Dessutom gäller följande semantik:

  • Alla namn som inte börjar med två understreck (__) kommer att vara tillgängliga i visualiseringen.

  • Namn som ingår i JavaScript-standardobjekt eller ingår i protokoll som JavaScript-providern skapar visas inte i visualiseringen.

  • Ett objekt kan göras iterbart via stöd för [Symbol.iterator].

  • Ett objekt kan göras indexerbart via stöd för ett anpassat protokoll som består av flera funktioner: getDimensionality, getValueAt och valfritt setValueAt.

Inbyggd objektbrygga och JavaScript-objektbrygga

Bryggan mellan JavaScript och objektmodellen för felsökningsprogrammet är dubbelriktad. Inbyggda objekt kan skickas till JavaScript och JavaScript-objekt kan skickas till felsökarens uttrycksutvärdering. Som ett exempel på detta bör du överväga att lägga till följande metod i skriptet:

function multiplyBySeven(val)
{
    return val * 7;
}

Den här metoden kan nu användas i linq-exempelfrågan ovan. Först läser vi in JavaScript-visualiseringen.

0:000> .scriptload c:\WinDbg\Scripts\arrayVisualizer2.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\arrayVisualizer2.js'

0:000> dx @$myScript = Debugger.State.Scripts.arrayVisualizer2.Contents

Sedan kan vi använda funktionen multiplyBySeven direkt enligt nedan.

0:000> dx g_array1D.Select(@$myScript.multiplyBySeven),d
g_array1D.Select(@$myScript.multiplyBySeven),d                
    [0]              : 0
    [1]              : 7
    [2]              : 14
    [3]              : 21
    [4]              : 28

Villkorsstyrda brytpunkter med JavaScript

Du kan använda JavaScript för att utföra kompletterande bearbetning när en brytpunkt har nåtts. Skript kan till exempel användas för att undersöka andra körningstidsvärden och sedan avgöra om du vill fortsätta kodkörningen automatiskt eller stoppa och utföra ytterligare manuell felsökning.

Allmän information om hur du arbetar med brytpunkter finns i Metoder för att kontrollera brytpunkter.

DebugHandler.js exempel på skript för brytpunktsbearbetning

Det här exemplet utvärderar anteckningsblockets öppna och sparande dialogruta: anteckningar! ShowOpenSaveDialog. Det här skriptet utvärderar variabeln pszCaption för att avgöra om den aktuella dialogrutan är en "Öppna"-dialogruta eller om det är en dialogruta för "Spara som". Om det är en öppen dialogruta fortsätter kodkörningen. Om det är en spara som-dialogruta stoppas kodkörningen och felsökningsprogrammet bryter sig in.

 // Use JavaScript strict mode 
"use strict";

// Define the invokeScript method to handle breakpoints

 function invokeScript()
 {
    var ctl = host.namespace.Debugger.Utility.Control;

    //Get the address of my string
    var address = host.evaluateExpression("pszCaption");

    // The open and save dialogs use the same function
    // When we hit the open dialog, continue.
    // When we hit the save dialog, break.
    if (host.memory.readWideString(address) == "Open") {
        // host.diagnostics.debugLog("We're opening, let's continue!\n");
        ctl.ExecuteCommand("gc");
    }
    else
    {
        //host.diagnostics.debugLog("We're saving, let's break!\n");
    }
  }

Det här kommandot anger en brytpunkt på notepad!ShowOpenSaveDialog och kör skriptet ovan när brytpunkten nås.

bp notepad!ShowOpenSaveDialog ".scriptrun C:\\WinDbg\\Scripts\\DebugHandler.js"

När alternativet Spara fil > väljs i anteckningar körs skriptet, g-kommandot skickas inte och en brytning i kodkörningen sker.

JavaScript script successfully loaded from 'C:\WinDbg\Scripts\DebugHandler.js'
notepad!ShowOpenSaveDialog:
00007ff6`f9761884 48895c2408      mov     qword ptr [rsp+8],rbx ss:000000db`d2a9f2f0=0000021985fe2060

Arbeta med 64-bitarsvärden i JavaScript-tillägg

I det här avsnittet beskrivs hur 64-bitarsvärden som skickas till ett JavaScript-felsökningstillägg fungerar. Det här problemet uppstår eftersom JavaScript bara har möjlighet att lagra nummer med hjälp av 53-bitars.

64-bitars och JavaScript 53-bitars lagring

Ordningsvärden som skickas till JavaScript hanteras normalt som JavaScript-nummer. Problemet med detta är att JavaScript-nummer är 64-bitars flyttal med dubbel precision. Alla ordningstal över 53 bitar skulle förlora precision vid inmatning i JavaScript. Det här är ett problem för 64-bitars pekare och andra 64-bitars ordinalvärden som kan ha flaggor i de högsta byten. För att hantera detta kommer alla 64-bitars interna värden (oavsett om de kommer från inbyggd kod eller datamodellen) in i JavaScript som en bibliotekstyp – inte som ett JavaScript-nummer. Den här bibliotekstypen går tillbaka till intern kod utan att förlora numerisk precision.

Automatisk konvertering

Bibliotekstypen för 64-bitars ordnala värden stöder standard JavaScript valueOf-konvertering. Om objektet används i en matematisk åtgärd eller en annan konstruktion som kräver värdekonvertering konverteras det automatiskt till ett JavaScript-nummer. Om precisionsförlust skulle inträffa (värdet använder mer än 53 bitars ordningstalsprecision) utlöser JavaScript-providern ett undantag.

Observera att om du använder bitvis operatorer i JavaScript är du ytterligare begränsad till 32-bitars ordningstalsprecision.

Den här exempelkoden summerar två tal och används för att testa konverteringen av 64-bitarsvärden.

function playWith64BitValues(a64, b64)
{
    // Sum two numbers to demonstrate 64-bit behavior.
    //
    // Imagine a64==100, b64==1000
    // The below would result in sum==1100 as a JavaScript number.  No exception is thrown.  The values auto-convert.
    //
    // Imagine a64==2^56, b64=1
    // The below will **Throw an Exception**.  Conversion to numeric results in loss of precision!
    //
    var sum = a64 + b64;
    host.diagnostics.debugLog("Sum   >> ", sum, "\n");

}

function performOp64BitValues(a64, b64, op)
{
    //
    // Call a data model method passing 64-bit value.  There is no loss of precision here.  This round trips perfectly.
    // For example:
    //  0:000> dx @$myScript.playWith64BitValues(0x4444444444444444ull, 0x3333333333333333ull, (x, y) => x + y)
    //  @$myScript.playWith64BitValues(0x4444444444444444ull, 0x3333333333333333ull, (x, y) => x + y) : 0x7777777777777777
    //
    return op(a64, b64);
}

Använd en textredigerare som Anteckningar för att skapa en textfil med namnet PlayWith64BitValues.js

Använd kommandot .scriptload för att läsa in skriptet.

0:000> .scriptload c:\WinDbg\Scripts\PlayWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\PlayWith64BitValues.js'

Om du vill göra skriptet lite enklare att arbeta med tilldelar du en variabel i felsökningsprogrammet för att lagra innehållet i skriptet med hjälp av dx-kommandot.

0:000> dx @$myScript = Debugger.State.Scripts.PlayWith64BitValues.Contents

Använd dx-uttrycksutvärderingen för att anropa funktionen addTwoValues.

Först beräknar vi värdet 2^53 =9007199254740992 (Hex 0x20000000000000).

Först ska vi testa (2^53) – 2 och se att det returnerar rätt värde för summan.

0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740990)
Sum   >> 18014398509481980

Sedan beräknar vi (2^53) -1 =9007199254740991. Detta returnerar felet som anger att konverteringsprocessen förlorar precision, så det här är det största värdet som kan användas med sum-metoden i JavaScript-kod.

0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number

Anropa en datamodellmetod som skickar 64-bitarsvärden. Det finns ingen förlust av precision här.

0:001> dx @$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF,  0x7FFFFFFFFFFFFFFF, (x, y) => x + y)
@$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF,  0x7FFFFFFFFFFFFFFF, (x, y) => x + y) : 0xfffffffffffffffe

Jämförelse

64-bitarsbibliotekstypen är ett JavaScript-objekt och inte en värdetyp, till exempel ett JavaScript-nummer. Detta har vissa konsekvenser för jämförelseåtgärder. Normalt skulle likhet (==) för ett objekt indikera att operander refererar till samma objekt och inte samma värde. JavaScript-providern minimerar detta genom att spåra livereferenser till 64-bitarsvärden och returnera samma "oföränderliga" objekt för icke-insamlade 64-bitarsvärden. Det innebär att följande för jämförelse skulle inträffa.

// Comparison with 64 Bit Values

function comparisonWith64BitValues(a64, b64)
{
    //
    // No auto-conversion occurs here.  This is an *EFFECTIVE* value comparison.  This works with ordinals with above 53-bits of precision.
    //
    var areEqual = (a64 == b64);
    host.diagnostics.debugLog("areEqual   >> ", areEqual, "\n");
    var areNotEqual = (a64 != b64);
    host.diagnostics.debugLog("areNotEqual   >> ", areNotEqual, "\n");

    //
    // Auto-conversion occurs here.  This will throw if a64 does not pack into a JavaScript number with no loss of precision.
    //
    var isEqualTo42 = (a64 == 42);
    host.diagnostics.debugLog("isEqualTo42   >> ", isEqualTo42, "\n");
    var isLess = (a64 < b64);
    host.diagnostics.debugLog("isLess   >> ", isLess, "\n");

Använd en textredigerare som Anteckningar för att skapa en textfil med namnet ComparisonWith64BitValues.js

Använd kommandot .scriptload för att läsa in skriptet.

0:000> .scriptload c:\WinDbg\Scripts\ComparisonWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\ComparisonWith64BitValues.js'

Om du vill göra skriptet lite enklare att arbeta med tilldelar du en variabel i felsökningsprogrammet för att lagra innehållet i skriptet med hjälp av dx-kommandot.

0:000> dx @$myScript = Debugger.State.Scripts.comparisonWith64BitValues.Contents

Först ska vi testa (2^53) – 2 och se att det returnerar de förväntade värdena.

0:001> dx @$myScript.comparisonWith64BitValues(9007199254740990, 9007199254740990)
areEqual   >> true
areNotEqual   >> false
isEqualTo42   >> false
isLess   >> false

Vi provar också siffran 42 som det första värdet för att verifiera att jämförelseoperatorn fungerar som den ska.

0:001> dx @$myScript.comparisonWith64BitValues(42, 9007199254740990)
areEqual   >> false
areNotEqual   >> true
isEqualTo42   >> true
isLess   >> true

Sedan beräknar vi (2^53) -1 =9007199254740991. Det här värdet returnerar felet som anger att konverteringsprocessen förlorar precision, så det här är det största värdet som kan användas med jämförelseoperatorerna i JavaScript-kod.

0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number

Upprätthålla precision i åtgärder

För att ett felsökningstillägg ska kunna upprätthålla precisionen projiceras en uppsättning matematiska funktioner ovanpå 64-bitarsbibliotekstypen. Om tillägget behöver (eller eventuellt) behöver precision över 53 bitar för inkommande 64-bitarsvärden bör följande metoder användas i stället för att förlita sig på standardoperatorer:

Metodnamn Underskrift Beskrivning
asNumber .asNumber() Konverterar 64-bitarsvärdet till ett JavaScript-nummer. Om precisionsförlust inträffar utlöses **ETT UNDANTAG**
convertToNumber .convertToNumber() Konverterar 64-bitarsvärdet till ett JavaScript-nummer. Om precisionsförlust inträffar utlöses **INGET UNDANTAG**
getLowPart .getLowPart() Konverterar de nedre 32 bitarna av ett 64-bitarsvärde till ett JavaScript-nummer
getHighPart .getHighPart() Konverterar de mest betydande 32 bitarna av 64-bitarsvärdet till ett JavaScript-nummer
lägg till .add(value) Lägger till ett värde i 64-bitarsvärdet och returnerar resultatet
subtrahera .subtract(value) Subtraherar ett värde från 64-bitarsvärdet och returnerar resultatet
multiplicera .multiply(value) Multiplicerar 64-bitarsvärdet med det angivna värdet och returnerar resultatet
divide .divide(value) Delar upp 64-bitarsvärdet med det angivna värdet och returnerar resultatet
bitwiseAnd .bitwiseAnd(value) Beräknar bitvis och av 64-bitarsvärdet med det tillhandahållna värdet och returnerar resultatet
bitwiseOr .bitwiseOr(value) Beräknar bitvis eller 64-bitarsvärdet med det angivna värdet och returnerar resultatet
bitwiseXor .bitwiseXor(value) Beräknar bitvis xor för 64-bitarsvärdet med det angivna värdet och returnerar resultatet
bitwiseShiftLeft .bitwiseShiftLeft(value) Flyttar det 64-bitars värde som lämnas av det angivna beloppet och returnerar resultatet
bitwiseShiftRight .bitwiseShiftRight(värde) Flyttar 64-bitarsvärdet till höger med det angivna beloppet och returnerar resultatet
toString .toString([radix]) Konverterar 64-bitarsvärdet till en visningssträng i standardradixet (eller radixet som eventuellt har angetts)

Den här metoden är också tillgänglig.

Metodnamn Underskrift Beskrivning
compareTo .compareTo(value) Jämför 64-bitarsvärdet med ett annat 64-bitarsvärde.

JavaScript-felsökning

I det här avsnittet beskrivs hur du använder felsökningsfunktionerna för skript i felsökningsprogrammet. Felsökningsprogrammet har integrerat stöd för felsökning av JavaScript-skript med hjälp av kommandot .scriptdebug (Debug JavaScript).

Anmärkning

Om du vill använda JavaScript-felsökning med WinDbg kör du felsökningsprogrammet som administratör.

Använd den här exempelkoden för att utforska felsökning av ett JavaScript. I den här genomgången ger vi den namnet DebuggableSample.js och sparar den i katalogen C:\MyScripts.

"use strict";

class myObj
{
    toString()
    {
        var x = undefined[42];
        host.diagnostics.debugLog("BOO!\n");
    }
}

class iterObj
{
    *[Symbol.iterator]()
    {
        throw new Error("Oopsies!");
    }
}

function foo()
{
    return new myObj();
}

function iter()
{
    return new iterObj();
}

function throwAndCatch()
{
    var outer = undefined;
    var someObj = {a : 99, b : {c : 32, d: "Hello World"} };
    var curProc = host.currentProcess;
    var curThread = host.currentThread;

    try
    {
        var x = undefined[42];
    } catch(e) 
    {
        outer = e;
    }

    host.diagnostics.debugLog("This is a fun test\n");
    host.diagnostics.debugLog("Of the script debugger\n");
    var foo = {a : 99, b : 72};
    host.diagnostics.debugLog("foo.a = ", foo.a, "\n");

    return outer;
}

function throwUnhandled()
{
    var proc = host.currentProcess;
    var thread = host.currentThread;
    host.diagnostics.debugLog("Hello...  About to throw an exception!\n");
    throw new Error("Oh me oh my!  This is an unhandled exception!\n");
    host.diagnostics.debugLog("Oh...  this will never be hit!\n");
    return proc;
}

function outer()
{
    host.diagnostics.debugLog("inside outer!\n");
    var foo = throwAndCatch();
    host.diagnostics.debugLog("Caught and returned!\n");
    return foo;
}

function outermost()
{
    var x = 99;
    var result = outer();
    var y = 32;
    host.diagnostics.debugLog("Test\n");
    return result;
}

function initializeScript()
{
    //
    // Return an array of registration objects to modify the object model of the debugger
    // See the following for more details:
    //
    //     https://aka.ms/JsDbgExt
    //
}

Läs in exempelskriptet.

.scriptload C:\MyScripts\DebuggableSample.js

Börja felsöka skriptet aktivt med hjälp av kommandot .scriptdebug .

0:000> .scriptdebug C:\MyScripts\DebuggableSample.js
>>> ****** DEBUGGER ENTRY DebuggableSample ******
           No active debug event!

>>> Debug [DebuggableSample <No Position>] >

När du ser uppmaningen >>> Debug [DebuggableSample <No Position>] > och en begäran om indata finns du i skriptfelsökaren.

Använd kommandot .help för att visa en lista över kommandon i JavaScript-felsökningsmiljön.

>>> Debug [DebuggableSample <No Position>] >.help
Script Debugger Commands (*NOTE* IDs are **PER SCRIPT**):
    ? .................................. Get help
    ? <expr>  .......................... Evaluate expression <expr> and display result
    ?? <expr>  ......................... Evaluate expression <expr> and display result
    |  ................................. List available scripts
    |<scriptid>s  ...................... Switch context to the given script
    bc \<bpid\>  ......................... Clear breakpoint by specified \<bpid\>
    bd \<bpid\>  ......................... Disable breakpoint by specified \<bpid\>
    be \<bpid\>  ......................... Enable breakpoint by specified \<bpid\>
    bl  ................................ List breakpoints
    bp <line>:<column>  ................ Set breakpoint at the specified line and column
    bp <function-name>  ................ Set breakpoint at the (global) function specified by the given name
    bpc  ............................... Set breakpoint at current location
    dv  ................................ Display local variables of current frame
    g  ................................. Continue script
    gu   ............................... Step out
    k  ................................. Get stack trace
    p  ................................. Step over
    q  ................................. Exit script debugger (resume execution)
    sx  ................................ Display available events/exceptions to break on
    sxe <event>  ....................... Enable break on <event>
    sxd <event>  ....................... Disable break on <event>
    t  ................................. Step in
    .attach <scriptId>  ................ Attach debugger to the script specified by <scriptId>
    .detach [<scriptId>]  .............. Detach debugger from the script specified by <scriptId>
    .frame <index>  .................... Switch to frame number <index>
    .f+  ............................... Switch to next stack frame
    .f-  ............................... Switch to previous stack frame
    .help  ............................. Get help

Använd kommandot sx för skriptfelsökning för att se listan över händelser som vi kan fånga.

>>> Debug [DebuggableSample <No Position>] >sx              
sx                                                          
    ab  [   inactive] .... Break on script abort            
    eh  [   inactive] .... Break on any thrown exception    
    en  [   inactive] .... Break on entry to the script     
    uh  [     active] .... Break on unhandled exception     

Använd kommandot sxe script debugger för att aktivera break on entry så att skriptet kommer att svälla in i skriptfelsökaren så snart någon kod i det körs.

>>> Debug [DebuggableSample <No Position>] >sxe en          
sxe en                                                      
Event filter 'en' is now active                             

Avsluta skriptfelsökaren så gör vi ett funktionsanrop till skriptet som kommer att svälla in i felsökningsprogrammet.

>>> Debug [DebuggableSample <No Position>] >q

Nu är du tillbaka i det normala felsökningsprogrammet. Kör följande kommando för att anropa skriptet.

dx @$scriptContents.outermost()

Nu är du tillbaka i skriptfelsökaren och har brutit dig in på den första raden i den yttersta JavaScript-funktionen.

>>> ****** SCRIPT BREAK DebuggableSample [BreakIn] ******   
           Location: line = 73, column = 5                  
           Text: var x = 99                                 

>>> Debug [DebuggableSample 73:5] >                         

Förutom att se inbrytningen i felsökningsprogrammet får du information på raden (73) och kolumnen (5) där pausen ägde rum samt relevant kodfragment av källkod: var x = 99.

Låt oss gå några gånger och komma till en annan plats i skriptet.

    p
    t
    p
    t
    p
    p

Vid denna punkt bör du vara inne i metoden throwAndCatch på rad 34.

...
>>> ****** SCRIPT BREAK DebuggableSample [Step Complete] ******                       
           Location: line = 34, column = 5                                            
           Text: var curProc = host.currentProcess                                    

Du kan verifiera detta genom att köra en stackspårning.

>>> Debug [DebuggableSample 34:5] >k                                                  
k                                                                                     
    ##  Function                         Pos    Source Snippet                        
-> [00] throwAndCatch                    034:05 (var curProc = host.currentProcess)   
   [01] outer                            066:05 (var foo = throwAndCatch())           
   [02] outermost                        074:05 (var result = outer())                

Härifrån kan du undersöka värdet för variabler.

>>> Debug [DebuggableSample 34:5] >??someObj                
??someObj                                                   
someObj          : {...}                                    
    __proto__        : {...}                                
    a                : 0x63                                 
    b                : {...}                                
>>> Debug [DebuggableSample 34:5] >??someObj.b              
??someObj.b                                                 
someObj.b        : {...}                                    
    __proto__        : {...}                                
    c                : 0x20                                 
    d                : Hello World                          

Nu ska vi ange en brytpunkt på den aktuella kodraden och se vilka brytpunkter som nu har angetts.

>>> Debug [DebuggableSample 34:5] >bpc                      
bpc                                                         
Breakpoint 1 set at 34:5                                    
>>> Debug [DebuggableSample 34:5] >bl                       
bl                                                          
      Id State    Pos                                       
       1 enabled  34:5                                      

Härifrån inaktiverar vi "entry (en)"-händelsen med hjälp av sxd-skriptfelsökarens kommando.

>>> Debug [DebuggableSample 34:5] >sxd en                                                                              
sxd en                                                                                                                 
Event filter 'en' is now inactive                                                                                      

Och sedan bara gå och låt skriptet fortsätta till slutet.

>>> Debug [DebuggableSample 34:5] >g                                                                                   
g                                                                                                                      
This is a fun test                                                                                                     
Of the script debugger                                                                                                 
foo.a = 99                                                                                                             
Caught and returned!                                                                                                   
Test                                                                                                                   
...

Kör skriptmetoden igen och observera när vår brytpunkt träffas.

0:000> dx @$scriptContents.outermost()                                                
inside outer!                                                                         
>>> ****** SCRIPT BREAK DebuggableSample [Breakpoint 1] ******                        
           Location: line = 34, column = 5                                            
           Text: var curProc = host.currentProcess                                    

Visa anropsstacken.

>>> Debug [DebuggableSample 34:5] >k                                                  
k                                                                                     
    ##  Function                         Pos    Source Snippet                        
-> [00] throwAndCatch                    034:05 (var curProc = host.currentProcess)   
   [01] outer                            066:05 (var foo = throwAndCatch())           
   [02] outermost                        074:05 (var result = outer())                

Nu vill vi sluta felsöka det här skriptet, så vi kopplar från det.

>>> Debug [DebuggableSample 34:5] >.detach                  
.detach                                                     
Debugger has been detached from script!                     

Och skriv sedan q för att avsluta.

q                                                           
This is a fun test                                          
Of the script debugger                                      
foo.a = 99                                                  
Caught and returned!                                        
Test                                                        

Om du kör funktionen igen bryts den inte längre in i felsökningsprogrammet.

0:007> dx @$scriptContents.outermost()
inside outer!
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test

JavaScript i VS Code – Lägga till IntelliSense

Om du vill arbeta med felsökningsdatamodellobjekten i VS Code kan du använda en definitionsfil som är tillgänglig i Windows-utvecklingspaketen. IntelliSense-definitionsfilen ger stöd för alla host.*-API:er för felsökningsobjektet. Om du har installerat satsen i standardkatalogen på en 64-bitarsdator finns den här:

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\JsProvider.d.ts

Så här använder du IntelliSense-definitionsfilen i VS Code:

  1. Leta upp definitionsfilen – JSProvider.d.ts

  2. Kopiera definitionsfilen till samma mapp som skriptet.

  3. Lägg till /// <reference path="JSProvider.d.ts" /> överst i JavaScript-skriptfilen.

Med den referensen i JavaScript-filen ger VS Code dig automatiskt IntelliSense på de värd-API:er som tillhandahålls av JSProvider utöver strukturerna i skriptet. Skriv till exempel "värd". och du ser IntelliSense för alla tillgängliga API:er för felsökningsmodellen.

JavaScript-resurser

Följande är JavaScript-resurser som kan vara användbara när du utvecklar JavaScript-felsökningstillägg.

Se även

Exempelskript för JavaScript-felsökningsprogram

Inbyggda objekt i JavaScript-tillägg