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.
- .scriptproviders (list script providers)
- .scriptload (Carica script)
- .scriptunload (Scarica script)
- .scriptrun (Esegui script)
- .scriptlist (elenca gli script caricati)
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 (aggiungi) | .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 WinDbg, 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:
Individuare il file di definizione - JSProvider.d.ts
Copiare il file di definizione nella stessa cartella dello script.
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.