JavaScript Debugger Scripting

Questo argomento descrive come usare JavaScript per creare script che comprendano gli oggetti debugger ed estendono e personalizzano le funzionalità del debugger.

Panoramica dello scripting del debugger JavaScript

I provider di script consentono di collegare un linguaggio di scripting al modello a oggetti interno del debugger. Il provider di scripting del debugger JavaScript consente l'uso di JavaScript con il debugger.

Quando un javaScript viene caricato tramite il comando .scriptload, viene eseguito il codice radice dello script, i nomi presenti nello script vengono inseriti nello spazio dei nomi radice del debugger (debugger dx) e lo script rimane residente in memoria finché non viene scaricato e tutti i riferimenti ai relativi oggetti vengono rilasciati. Lo script può fornire nuove funzioni all'analizzatore di espressioni del debugger, modificare il modello a oggetti del debugger o fungere da visualizzatore nello stesso modo in cui un visualizzatore NatVis esegue.

Questo argomento descrive alcune delle operazioni che è possibile eseguire con lo scripting del debugger JavaScript.

Questi due argomenti forniscono informazioni aggiuntive sull'uso di JavaScript nel debugger.

Script di esempio del debugger JavaScript

Oggetti nativi nelle estensioni JavaScript

JavaScript Scripting Video

Strumenti di deframmentazione #170 - Andy e Bill illustrano le capacità di estendibilità e scripting di JavaScript nel debugger.

Provider JavaScript del debugger

Il provider JavaScript incluso nel debugger sfrutta appieno i miglioramenti più recenti dell'oggetto ECMAScript6 e della classe. Per altre informazioni, vedere ECMAScript 6 - Nuove funzionalità: Panoramica e confronto.

JsProvider.dll

JsProvider.dll è il provider JavaScript caricato per supportare script del debugger JavaScript.

Requisiti

JavaScript Debugger Scripting è progettato per funzionare con tutte le versioni supportate di Windows.

Caricamento del provider di script JavaScript

Prima di usare uno dei comandi con estensione script, è necessario caricare un provider di script. Usare il comando .scriptproviders per verificare che il provider JavaScript sia caricato.

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

Comandi meta di scripting JavaScript

I comandi seguenti sono disponibili per l'uso con scripting del debugger JavaScript.

Requisiti

Prima di usare uno dei comandi con estensione script, è necessario caricare un provider di script. Usare il comando .scriptproviders per verificare che il provider JavaScript sia caricato.

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

.scriptproviders (list script providers)

Il comando .scriptproviders elenca tutti i linguaggi di script attualmente compresi dal debugger e l'estensione in cui sono registrati.

Nell'esempio seguente vengono caricati i provider JavaScript e NatVis.

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

Qualsiasi file che termina con ". NatVis" viene interpretato come script NatVis e qualsiasi file che termina con ".js" viene interpretato come uno script JavaScript. Entrambi i tipi di script possono essere caricati con il comando .scriptload.

Per altre informazioni, vedere .scriptproviders (List Script Providers)

.scriptload (Carica script)

Il comando .scriptload caricherà uno script ed eseguirà il codice radice di uno script e la funzione initializeScript . Se si verificano errori nel caricamento iniziale e nell'esecuzione dello script, gli errori verranno visualizzati nella console. Il comando seguente mostra il caricamento corretto di TestScript.js.

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

Tutte le modifiche apportate dallo script del modello a oggetti rimarranno invariate fino a quando lo script non viene successivamente scaricato o viene eseguito di nuovo con contenuto diverso.

Per altre informazioni, vedere .scriptload (Load Script)

.scriptrun

Il comando .scriptrun caricherà uno script, eseguirà il codice radice dello script, l'initializeScript e la funzione invokeScript. Se si verificano errori nel caricamento iniziale e nell'esecuzione dello script, gli errori verranno visualizzati nella console.

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

Tutte le manipolazioni del modello a oggetti del debugger eseguite dallo script rimarranno invariate fino a quando lo script non viene successivamente scaricato o eseguito di nuovo con contenuto diverso.

Per altre informazioni, vedere .scriptrun (Run Script).

.scriptunload (Scarica script)

Il comando .scriptunload scarica uno script caricato e chiama la funzione uninitializeScript . Usare la sintassi di comando seguente per scaricare uno script

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

Per altre informazioni, vedere .scriptunload (Scarica script).

.scriptlist (elenca gli script caricati)

Il comando .scriptlist elenca tutti gli script che sono stati caricati tramite .scriptload o il comando .scriptrun. Se TestScript è stato caricato correttamente con .scriptload, il comando .scriptlist visualizzerà il nome dello script caricato.

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

Per altre informazioni, vedere .scriptlist (List Loaded Scripts).

Introduzione allo scripting del debugger JavaScript

Script di esempio HelloWorld

Questa sezione descrive come creare ed eseguire un semplice script del debugger JavaScript che stampa, Hello World.

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

Usare un editor di testo, ad esempio Blocco note per creare un file di testo denominato HelloWorld.js contenente il codice JavaScript illustrato in precedenza.

Usare il comando .scriptload per caricare ed eseguire lo script. Poiché è stato usato il nome della funzione initializeScript, il codice nella funzione viene eseguito quando viene caricato lo script.

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

Dopo il caricamento dello script, nel debugger è disponibile la funzionalità aggiuntiva. Usare il comando dx (Display NatVis Expression) per visualizzare Debugger.State.Scripts per vedere che lo script è ora residente.

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

Nell'esempio successivo si aggiungerà e si chiamerà una funzione denominata.

Aggiunta di due valori script di esempio

Questa sezione descrive come creare ed eseguire un semplice script del debugger JavaScript che aggiunge accetta input e aggiunge due numeri.

Questo semplice script fornisce una singola funzione, addTwoValues.

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

Usare un editor di testo, ad esempio Blocco note per creare un file di testo denominato FirstSampleFunction.js

Usare il comando .scriptload per caricare lo script.

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

Dopo il caricamento dello script, nel debugger è disponibile la funzionalità aggiuntiva. Usare il comando dx (Display NatVis Expression) per visualizzare Debugger.State.Scripts per vedere che lo script è ora residente.

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

È possibile selezionare FirstSampleFunction per vedere quali funzioni fornisce.

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

Per rendere lo script un po' più pratico da usare, assegnare una variabile nel debugger per contenere il contenuto dello script usando il comando dx.

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

Usare l'analizzatore di espressioni dx per chiamare la funzione addTwoValues.

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

È anche possibile usare l'alias predefinito @$scriptContents per lavorare con gli script. L'alias @$scriptContents unisce tutti gli elementi . Contenuto di tutti gli script caricati.

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

Al termine dell'uso dello script, usare il comando .scriptunload per scaricare lo script.

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

Automazione dei comandi del debugger

Questa sezione descrive come creare ed eseguire un semplice script del debugger JavaScript che automatizza l'invio del comando u (Unassemble). L'esempio mostra anche come raccogliere e visualizzare l'output dei comandi in un ciclo.

Questo script fornisce una singola funzione, 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");

}

Usare un editor di testo, ad esempio Blocco note per creare un file di testo denominato RunCommands.js

Usare il comando .scriptload per caricare lo script RunCommands.

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

Dopo il caricamento dello script, nel debugger è disponibile la funzionalità aggiuntiva. Usare il comando dx (Display NatVis Expression) per visualizzare Debugger.State.Scripts.RunCommands per vedere che lo script è ora residente.

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]

Usare il comando dx per chiamare la funzione RunCommands nello script RunCommands.

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

Funzioni speciali del debugger JavaScript

Esistono diverse funzioni speciali in uno script JavaScript chiamato dal provider di script stesso.

initializeScript

Quando uno script JavaScript viene caricato ed eseguito, esegue una serie di passaggi prima delle variabili, delle funzioni e degli altri oggetti nello script influiscono sul modello a oggetti del debugger.

  • Lo script viene caricato in memoria e analizzato.
  • Viene eseguito il codice radice nello script.
  • Se lo script ha un metodo denominato initializeScript, questo metodo viene chiamato.
  • Il valore restituito da initializeScript viene usato per determinare come modificare automaticamente il modello a oggetti del debugger.
  • I nomi nello script vengono collegati allo spazio dei nomi del debugger.

Come accennato, initializeScript verrà chiamato immediatamente dopo l'esecuzione del codice radice dello script. Il processo consiste nel restituire una matrice JavaScript di oggetti di registrazione al provider che indica come modificare il modello a oggetti del debugger.

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

Il metodo invokeScript è il metodo script primario e viene chiamato quando vengono eseguiti .scriptload e .scriptrun.

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

Il metodo uninitializeScript è l'opposto comportamentale di initializeScript. Viene chiamato quando uno script viene scollegato e si prepara a scaricare. Il processo consiste nell'annullare le modifiche apportate al modello a oggetti che lo script ha apportato in modo imperativo durante l'esecuzione e/o per eliminare definitivamente tutti gli oggetti memorizzati nella cache dello script.

Se uno script non apporta modifiche imperative al modello a oggetti né memorizza nella cache i risultati, non deve avere un metodo uninitializeScript. Tutte le modifiche apportate al modello a oggetti eseguite come indicato dal valore restituito di initializeScript vengono annullate automaticamente dal provider. Tali modifiche non richiedono un metodo esplicito uninitializeScript.

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

Riepilogo delle funzioni chiamate dai comandi script

Questa tabella riepiloga le funzioni chiamate dai comandi script

Comando .scriptload .scriptrun (Esegui script) .scriptunload (Scarica script)
root yes yes
initializeScript yes yes
invokeScript yes
uninitializeScript yes

Usare questo codice di esempio per verificare quando ogni funzione viene chiamata come script viene caricato, eseguito e scaricato.

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

Creazione di un visualizzatore di debugger in JavaScript

I file di visualizzazione personalizzati consentono di raggruppare e organizzare i dati in una struttura di visualizzazione che riflette meglio le relazioni e il contenuto dei dati. È possibile usare le estensioni del debugger JavaScript per scrivere visualizzatori del debugger che agiscono in modo molto simile a NatVis. Questa operazione viene eseguita tramite la creazione di un oggetto prototipo JavaScript (o una classe ES6) che funge da visualizzatore per un determinato tipo di dati. Per altre informazioni su NatVis e sul debugger, vedere dx (Display NatVis Expression).

Classe di esempio - Simple1DArray

Si consideri un esempio di classe C++ che rappresenta una matrice unidimensionale. Questa classe ha due membri, m_size che corrisponde alla dimensione complessiva della matrice e m_pValues che è un puntatore a un numero di input in memoria uguale al campo m_size.

class Simple1DArray
{
private:

    ULONG64 m_size;
    int *m_pValues;
};

È possibile usare il comando dx per esaminare il rendering predefinito della struttura dei dati.

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

Visualizzatore JavaScript

Per visualizzare questo tipo, è necessario creare un prototipo (o ES6) classe che include tutti i campi e le proprietà da visualizzare nel debugger. È anche necessario che il metodo initializeScript restituisca un oggetto che indica al provider JavaScript di collegare il prototipo come visualizzatore per il tipo specificato.

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

Salvare lo script in un file denominato arrayVisualizer.js.

Usare il comando .load (Load Extension DLL) per caricare il provider JavaScript.

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

Usare .scriptload per caricare lo script del visualizzatore di matrici.

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

Ora, quando viene usato il comando dx, il visualizzatore di script visualizzerà le righe di contenuto della matrice.

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

Inoltre, questa visualizzazione JavaScript offre funzionalità LINQ, ad esempio Select.

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

Cosa influisce sulla visualizzazione

Un prototipo o una classe che viene reso visualizzatore per un tipo nativo tramite una restituzione di un oggetto host.typeSignatureRegistration da initializeScript avrà tutte le proprietà e i metodi all'interno di JavaScript aggiunti al tipo nativo. Inoltre, si applicano le semantiche seguenti:

  • Qualsiasi nome che non inizia con due caratteri di sottolineatura (__) sarà disponibile nella visualizzazione.

  • I nomi che fanno parte di oggetti JavaScript standard o fanno parte di protocolli creati dal provider JavaScript non verranno visualizzati nella visualizzazione.

  • Un oggetto può essere reso iterabile tramite il supporto di [Symbol.iterator].

  • Un oggetto può essere reso indicizzato tramite il supporto di un protocollo personalizzato costituito da diverse funzioni: getDimensionality, getValueAt e facoltativamente setValueAt.

Bridge di oggetti Native e JavaScript

Il bridge tra JavaScript e il modello a oggetti del debugger è bidirezionale. Gli oggetti nativi possono essere passati agli oggetti JavaScript e JavaScript all'analizzatore di espressioni del debugger. Ad esempio, prendere in considerazione l'aggiunta del metodo seguente nello script:

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

Questo metodo può ora essere utilizzato nella query LINQ di esempio precedente. Prima di tutto, si carica la visualizzazione JavaScript.

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

È quindi possibile usare la funzione multiplyBySeven inline, come illustrato di seguito.

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

Punti di interruzione condizionali con JavaScript

È possibile usare JavaScript per eseguire un'elaborazione supplementare dopo che viene raggiunto un punto di interruzione. Ad esempio, lo script può essere usato per esaminare altri valori di runtime e quindi determinare se si vuole continuare automaticamente l'esecuzione del codice o arrestare ed eseguire ulteriori operazioni di debug manuale.

Per informazioni generali sull'uso dei punti di interruzione, vedere Metodi di controllo dei punti di interruzione.

DebugHandler.js esempio di script di elaborazione del punto di interruzione

Questo esempio valuterà la finestra di dialogo aperta e salvata del Blocco note: Blocco note! ShowOpenSaveDialog. Questo script valuterà la variabile pszCaption per determinare se la finestra di dialogo corrente è una finestra di dialogo "Apri" o se si tratta di una finestra di dialogo "Salva con nome". Se si tratta di una finestra di dialogo aperta, l'esecuzione del codice continuerà. Se si tratta di una finestra di dialogo salva come , l'esecuzione del codice verrà arrestata e il debugger si interromperà.

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

Questo comando imposta un punto di interruzione nel Blocco note. ShowOpenSaveDialog e eseguirà lo script precedente ogni volta che viene raggiunto il punto di interruzione.

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

Quindi, quando l'opzione Salva file > è selezionata nel Blocco note, lo script viene eseguito, il comando g non viene inviato e si verifica un'interruzione nell'esecuzione del codice.

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

Usare valori a 64 bit nelle estensioni JavaScript

Questa sezione descrive il comportamento dei valori a 64 bit passati in un'estensione del debugger JavaScript. Questo problema si verifica perché JavaScript ha solo la possibilità di archiviare i numeri usando 53 bit.

Archiviazione a 64 bit e JavaScript a 53 bit

I valori ordinali passati in JavaScript vengono in genere sottoposto a marshalling come numeri JavaScript. Il problema è che i numeri JavaScript sono valori a virgola mobile a precisione doppia a 64 bit. Qualsiasi ordinale su 53 bit perde la precisione all'ingresso in JavaScript. Questo problema presenta un problema per i puntatori a 64 bit e altri valori ordinali a 64 bit che possono avere flag nei byte più alti. Per gestire questo problema, qualsiasi valore nativo a 64 bit (dal codice nativo o dal modello di dati) che immette JavaScript come tipo di libreria, non come numero JavaScript. Questo tipo di libreria eseguirà il round trip al codice nativo senza perdere precisione numerica.

Conversione automatica

Il tipo di libreria per i valori ordinali a 64 bit supporta la conversione standard del valore JavaScriptOf. Se l'oggetto viene usato in un'operazione matematica o in un altro costrutto che richiede la conversione di valori, verrà convertito automaticamente in un numero JavaScript. Se si verifica una perdita di precisione (il valore utilizza più di 53 bit di precisione ordinale), il provider JavaScript genererà un'eccezione.

Si noti che se si usano operatori bit per bit in JavaScript, si è ulteriormente limitati a 32 bit di precisione ordinale.

Questo codice di esempio somma due numeri e verrà usato per testare la conversione di valori a 64 bit.

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

Usare un editor di testo, ad esempio Blocco note per creare un file di testo denominato PlayWith64BitValues.js

Usare il comando .scriptload per caricare lo script.

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

Per rendere lo script un po' più pratico da usare, assegnare una variabile nel debugger per contenere il contenuto dello script usando il comando dx.

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

Usare l'analizzatore di espressioni dx per chiamare la funzione addTwoValues.

Prima di tutto si calcolerà il valore di 2^53 =9007199254740992 (hex 0x20000000000000).

Per prima cosa, si userà (2^53) - 2 e si noterà che restituisce il valore corretto per la somma.

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

Si calcolerà quindi (2^53) -1 =9007199254740991. Viene restituito l'errore che indica che il processo di conversione perderà precisione, quindi questo è il valore più grande che può essere usato con il metodo sum nel codice JavaScript.

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

Chiamare un metodo del modello di dati passando valori a 64 bit. Non c'è alcuna perdita di precisione qui.

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

Confronto

Il tipo di libreria a 64 bit è un oggetto JavaScript e non un tipo di valore, ad esempio un numero JavaScript. Ciò ha alcune implicazioni per le operazioni di confronto. In genere, l'uguaglianza (==) in un oggetto indica che gli operandi fanno riferimento allo stesso oggetto e non allo stesso valore. Il provider JavaScript riduce questo problema monitorando i riferimenti in tempo reale ai valori a 64 bit e restituendo lo stesso oggetto "non modificabile" per il valore a 64 bit non raccolto. Ciò significa che per il confronto si verificherebbe quanto segue.

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

Usare un editor di testo, ad esempio Blocco note per creare un file di testo denominato ComparisonWith64BitValues.js

Usare il comando .scriptload per caricare lo script.

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

Per rendere lo script un po' più pratico da usare, assegnare una variabile nel debugger per contenere il contenuto dello script usando il comando dx.

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

Prima di tutto, si userà (2^53) - 2 e si noterà che restituisce i valori previsti.

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

Si proverà anche il numero 42 come primo valore per verificare che l'operatore di confronto funzioni come dovrebbe.

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

Si calcolerà quindi (2^53) -1 =9007199254740991. Questo valore restituisce l'errore che indica che il processo di conversione perderà la precisione, quindi questo è il valore più grande che può essere usato con gli operatori di confronto nel codice JavaScript.

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

Mantenimento della precisione nelle operazioni

Per consentire a un'estensione del debugger di mantenere la precisione, viene proiettato un set di funzioni matematiche sopra il tipo di libreria a 64 bit. Se l'estensione richiede (o eventualmente) una precisione superiore a 53 bit per i valori a 64 bit in ingresso, è consigliabile usare i metodi seguenti anziché basarsi sugli operatori standard:

Nome metodo Firma Descrizione
asNumber .asNumber() Converte il valore a 64 bit in un numero JavaScript. Se si verifica una perdita di precisione, **AN EXCEPTION IS THROWN**
convertToNumber .convertToNumber() Converte il valore a 64 bit in un numero JavaScript. Se si verifica una perdita di precisione, **NO EXCEPTION IS THROWN**
getLowPart .getLowPart() Converte i 32 bit inferiori del valore a 64 bit in un numero JavaScript
getHighPart .getHighPart() Converte i 32 bit alti del valore a 64 bit in un numero JavaScript
add .add(value) Aggiunge un valore al valore a 64 bit e restituisce il risultato
sottrarre .subtract(value) Sottrae un valore dal valore a 64 bit e restituisce il risultato
Moltiplicare .multiply(value) Moltiplica il valore a 64 bit per il valore fornito e restituisce il risultato
Dividere .divide(value) Divide il valore a 64 bit per il valore fornito e restituisce il risultato
bitwiseAnd .bitwiseAnd(value) Calcola il bit per bit e il valore a 64 bit con il valore fornito e restituisce il risultato
bitwiseOr .bitwiseOr(value) Calcola il bit per bit o il valore a 64 bit con il valore fornito e restituisce il risultato
bitwiseXor .bitwiseXor(value) Calcola l'xor bit per bit del valore a 64 bit con il valore fornito e restituisce il risultato
bitwiseShiftLeft .bitwiseShiftLeft(value) Sposta il valore a 64 bit a sinistra della quantità specificata e restituisce il risultato
bitwiseShiftRight .bitwiseShiftRight(value) Sposta il valore a 64 bit direttamente in base alla quantità specificata e restituisce il risultato
toString .toString([radix]) Converte il valore a 64 bit in una stringa di visualizzazione nel radix predefinito (o il radix fornito facoltativamente)

Questo metodo è disponibile anche.

Nome metodo Firma Descrizione
compareTo .compareTo(value) Confronta il valore a 64 bit con un altro valore a 64 bit.

Debug javaScript

Questa sezione descrive come usare le funzionalità di debug dello script del debugger. Il debugger ha integrato il supporto per il debug di script JavaScript usando il comando .scriptdebug (Debug JavaScript).

Nota

Per usare il debug JavaScript con WinDb, eseguire il debugger come Amministrazione istrator.

Usare questo codice di esempio per esplorare il debug di un codice JavaScript. Per questa procedura dettagliata, la DebuggableSample.js verrà denominata e salvata nella directory 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
    //
}

Caricare lo script di esempio.

.scriptload C:\MyScripts\DebuggableSample.js

Avviare attivamente il debug dello script usando il comando .scriptdebug .

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

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

Dopo aver visualizzato il prompt >>> Debug [DebuggableSample <No Position>] > e una richiesta di input, si è all'interno del debugger di script.

Usare il comando .help per visualizzare un elenco di comandi nell'ambiente di debug JavaScript.

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

Usare il comando sx script debugger per visualizzare l'elenco degli eventi che è possibile intercettare.

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

Usare il comando sxe script debugger per attivare l'interruzione alla voce in modo che lo script si intercettare nel debugger di script non appena viene eseguito qualsiasi codice all'interno di esso.

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

Uscire dal debugger di script e si eseguirà una chiamata di funzione allo script che eseguirà l'intercettazione nel debugger.

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

A questo punto, si torna al debugger normale. Eseguire il comando seguente per chiamare lo script.

dx @$scriptContents.outermost()

A questo punto, si è di nuovo nel debugger di script e si è interrotti nella prima riga della funzione JavaScript più esterna.

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

>>> Debug [DebuggableSample 73:5] >                         

Oltre a visualizzare l'interruzione nel debugger, si ottengono informazioni sulla riga (73) e sulla colonna (5) in cui si è svolta l'interruzione, nonché sul frammento di codice sorgente pertinente: var x = 99.

Passiamo un paio di volte e passiamo a un altro posto nello script.

    p
    t
    p
    t
    p
    p

A questo punto, si dovrebbe essere suddivisi nel metodo throwAndCatch alla riga 34.

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

È possibile verificarlo eseguendo un'analisi dello stack.

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

Da qui è possibile esaminare il valore delle variabili.

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

Impostare un punto di interruzione nella riga di codice corrente e verificare quali punti di interruzione sono ora impostati.

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

Da qui si disabiliterà l'evento entry (en) usando il comando del debugger di script sxd .

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

E poi basta andare e lasciare che lo script continui fino alla fine.

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

Eseguire di nuovo il metodo script e osservare che il punto di interruzione viene raggiunto.

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

Visualizzare lo stack di chiamate.

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

A questo punto, si vuole interrompere il debug di questo script, in modo da scollegarlo.

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

Quindi digitare q per uscire.

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

L'esecuzione della funzione non verrà più interrotta nel debugger.

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

JavaScript in VSCode - Aggiunta di IntelliSense

Se si vuole usare gli oggetti del modello di dati del debugger in VSCode, è possibile usare un file di definizione disponibile nei kit di sviluppo Windows. Il file di definizione di IntelliSense fornisce supporto per tutte le API dell'oggetto debugger host.*. Se il kit è stato installato nella directory predefinita in un PC a 64 bit, si trova qui:

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

Per usare il file di definizione in IntelliSense in VSCode:

  1. Individuare il file di definizione - JSProvider.d.ts

  2. Copiare il file di definizione nella stessa cartella dello script.

  3. Aggiungere /// <reference path="JSProvider.d.ts" /> all'inizio del file di script JavaScript.

Con tale riferimento nel file JavaScript, VS Code fornirà automaticamente IntelliSense sulle API host fornite da JSProvider oltre alle strutture nello script. Ad esempio, digitare "host". e vedrai IntelliSense per tutte le API del modello di debugger disponibili.

Risorse JavaScript

Di seguito sono riportate le risorse JavaScript che possono essere utili durante lo sviluppo di estensioni di debug JavaScript.

Vedere anche

Script di esempio del debugger JavaScript

Oggetti nativi nelle estensioni JavaScript