Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
In diesem Thema wird beschrieben, wie Sie JavaScript zum Erstellen von Skripts verwenden, die Debuggerobjekte verstehen und die Funktionen des Debuggers erweitern und anpassen.
Übersicht über javaScript-Debuggerskripting
Skriptanbieter verbinden eine Skriptsprache mit dem internen Objektmodell des Debuggers. Der JavaScript-Debuggerskriptinganbieter ermöglicht die Verwendung von JavaScript mit dem Debugger.
Wenn ein JavaScript über den .scriptload-Befehl geladen wird, wird der Hauptcode des Skripts ausgeführt, die Namen, die im Skript enthalten sind, in den Stammnamensraum des Debuggers (dx Debugger) eingebunden, und das Skript bleibt im Speicher, bis es entladen wird und alle Verweise auf seine Objekte freigegeben werden. Das Skript kann dem Ausdrucksauswerter des Debuggers neue Funktionen bereitstellen, das Objektmodell des Debuggers ändern oder auf ähnliche Weise wie eine NatVis-Visualisierung fungieren.
In diesem Thema wird beschrieben, was Sie mit javaScript-Debuggerskripting tun können.
Diese beiden Themen enthalten zusätzliche Informationen zum Arbeiten mit JavaScript im Debugger.
JavaScript-Debuggerbeispielskripts
Native Objekte in JavaScript-Erweiterungen
JavaScript-Script-Video
Defrag Tools #170 – Andy und Bill veranschaulichen die JavaScript-Erweiterbarkeit und Skriptfunktionen im Debugger.
Der Debugger-JavaScript-Anbieter
Der im Debugger enthaltene JavaScript-Anbieter nutzt die neuesten ECMAScript6-Objekt- und Klassenverbesserungen. Weitere Informationen finden Sie unter ECMAScript 6 – Neue Features: Übersicht & Vergleich.
JsProvider.dll
JsProvider.dll ist der JavaScript-Anbieter, der geladen wird, um JavaScript Debugger Scripting zu unterstützen.
Anforderungen
JavaScript-Debuggerskripting ist für die Arbeit mit allen unterstützten Versionen von Windows konzipiert.
Laden des JavaScript-Skriptinganbieters
Bevor Sie einen der Skriptbefehle verwenden, muss ein Skriptanbieter geladen werden. Verwenden Sie den Befehl ".scriptproviders", um zu bestätigen, dass der JavaScript-Anbieter geladen wurde.
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
JavaScript-Skripting-Metabefehle
Die folgenden Befehle stehen für die Verwendung von JavaScript-Debuggerskripting zur Verfügung.
- .scriptproviders (Liste der Skriptanbieter)
- .scriptload (Skript laden)
- .scriptunload (Unload Script)
- .scriptrun (Run Script)
- .scriptlist (Liste geladener Skripts)
Anforderungen
Bevor Sie einen der Skriptbefehle verwenden, muss ein Skriptanbieter geladen werden. Verwenden Sie den Befehl ".scriptproviders", um zu bestätigen, dass der JavaScript-Anbieter geladen wurde.
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
.skriptanbieter (Liste von Skriptanbietern)
Der Befehl ".scriptproviders" listet alle Skriptsprachen auf, die derzeit vom Debugger und der Erweiterung verstanden werden, unter der sie registriert sind.
Im folgenden Beispiel werden die JavaScript- und NatVis-Anbieter geladen.
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
Jede Datei, die mit ".NatVis" endet, wird als NatVis-Skript verstanden, und jede Datei, die mit ".js" endet, wird als JavaScript-Skript verstanden. Jeder Skripttyp kann mit dem Befehl ".scriptload" geladen werden.
Weitere Informationen finden Sie unter .scriptproviders (List Script Providers)
.scriptload (Skript laden)
Der Befehl ".scriptload" lädt ein Skript und führt den Stammcode eines Skripts und die InitializeScript-Funktion aus. Wenn beim anfänglichen Laden und Ausführen des Skripts Fehler auftreten, werden die Fehler in der Konsole angezeigt. Der folgende Befehl zeigt das erfolgreiche Laden von TestScript.js.
0:000> .scriptload C:\WinDbg\Scripts\TestScript.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\TestScript.js'
Alle Vom Skript vorgenommenen Objektmodellmanipulationen bleiben erhalten, bis das Skript anschließend entladen oder erneut mit unterschiedlichem Inhalt ausgeführt wird.
Weitere Informationen finden Sie unter .scriptload (Load Script)
.scriptrun
Der Befehl ".scriptrun" lädt ein Skript, führt den Stammcode des Skripts, das InitializeScript und die InvokeScript-Funktion aus. Wenn beim anfänglichen Laden und Ausführen des Skripts Fehler auftreten, werden die Fehler in der Konsole angezeigt.
0:000> .scriptrun C:\WinDbg\Scripts\helloWorld.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\helloWorld.js'
Hello World! We are in JavaScript!
Alle Vom Skript vorgenommenen Debuggerobjektmodellmanipulationen bleiben erhalten, bis das Skript anschließend entladen oder erneut mit unterschiedlichem Inhalt ausgeführt wird.
Weitere Informationen finden Sie unter .scriptrun (Run Script).
.scriptunload (Skript entladen)
Der Befehl ".scriptunload" entlädt ein geladenes Skript und ruft die UninitializeScript-Funktion auf. Verwenden der folgenden Befehlssyntax zum Entladen eines Skripts
0:000:x86> .scriptunload C:\WinDbg\Scripts\TestScript.js
JavaScript script unloaded from 'C:\WinDbg\Scripts\TestScript.js'
Weitere Informationen finden Sie unter .scriptunload (Unload Script).
.scriptlist (Liste geladener Skripts)
Der Befehl ".scriptlist" listet alle Skripts auf, die über das SKRIPTload oder den .scriptrun-Befehl geladen wurden. Wenn testScript erfolgreich mit .scriptload geladen wurde, zeigt der Befehl ".scriptlist" den Namen des geladenen Skripts an.
0:000> .scriptlist
Command Loaded Scripts:
JavaScript script from 'C:\WinDbg\Scripts\TestScript.js'
Weitere Informationen finden Sie unter ".scriptlist" (Liste geladener Skripts).For more information, see .scriptlist (List Loaded Scripts).
Erste Schritte mit JavaScript-Debuggerskripting
HelloWorld-Beispielskript
In diesem Abschnitt wird beschrieben, wie Sie ein einfaches JavaScript-Debuggerskript erstellen und ausführen, das ausdruckt, Hello World.
// WinDbg JavaScript sample
// Prints Hello World
function initializeScript()
{
host.diagnostics.debugLog("***> Hello World! \n");
}
Verwenden Sie einen Text-Editor wie Editor, um eine Textdatei namens HelloWorld.js zu erstellen, die den oben gezeigten JavaScript-Code enthält.
Verwenden Sie den Befehl ".scriptload", um das Skript zu laden und auszuführen. Da wir den Funktionsnamen initializeScript verwendet haben, wird der Code in der Funktion ausgeführt, wenn das Skript geladen wird.
0:000> .scriptload c:\WinDbg\Scripts\HelloWorld.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\HelloWorld.js'
***> Hello World!
Nachdem das Skript geladen wurde, ist die zusätzliche Funktionalität im Debugger verfügbar. Verwenden Sie den Dx-Befehl (NatVis-Ausdruck anzeigen), um Debugger.State.Scripts anzuzeigen, um zu sehen, dass unser Skript jetzt vorhanden ist.
0:000> dx Debugger.State.Scripts
Debugger.State.Scripts
HelloWorld
Im nächsten Beispiel fügen wir eine benannte Funktion hinzu und rufen sie auf.
Hinzufügen von zwei Werten (Beispielskript)
In diesem Abschnitt wird beschrieben, wie Sie ein einfaches JavaScript-Debuggerskript erstellen und ausführen, das Eingaben hinzufügt und zwei Zahlen hinzufügt.
Dieses einfache Skript bietet eine einzelne Funktion, addTwoValues.
// WinDbg JavaScript sample
// Adds two functions
function addTwoValues(a, b)
{
return a + b;
}
Verwenden Sie einen Text-Editor wie Notepad, um eine Textdatei mit dem Namen FirstSampleFunction.js zu erstellen.
Verwenden Sie den Befehl ".scriptload", um das Skript zu laden.
0:000> .scriptload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'
Nachdem das Skript geladen wurde, ist die zusätzliche Funktionalität im Debugger verfügbar. Verwenden Sie den Dx-Befehl (NatVis-Ausdruck anzeigen), um Debugger.State.Scripts anzuzeigen, um zu sehen, dass unser Skript jetzt vorhanden ist.
0:000> dx Debugger.State.Scripts
Debugger.State.Scripts
FirstSampleFunction
Wir können die FirstSampleFunction auswählen, um zu sehen, welche Funktionen sie bereitstellt.
0:000> dx -r1 -v Debugger.State.Scripts.FirstSampleFunction.Contents
Debugger.State.Scripts.FirstSampleFunction.Contents : [object Object]
host : [object Object]
addTwoValues
...
Um das Arbeiten mit dem Skript etwas bequemer zu gestalten, weisen Sie im Debugger eine Variable zu, die den Inhalt des Skripts mithilfe des dx-Befehls speichert.
0:000> dx @$myScript = Debugger.State.Scripts.FirstSampleFunction.Contents
Verwenden Sie den Dx-Ausdrucks-Evaluator, um die addTwoValues-Funktion aufzurufen.
0:000> dx @$myScript.addTwoValues(10, 41),d
@$myScript.addTwoValues(10, 41),d : 51
Sie können auch die @ $scriptContents integrierten Alias verwenden, um mit den Skripts zu arbeiten. Der @$scriptContents-Alias kombiniert den Inhalt aller geladenen Skripte.
0:001> dx @$scriptContents.addTwoValues(10, 40),d
@$scriptContents.addTwoValues(10, 40),d : 50
Wenn Sie mit dem Skript fertig sind, verwenden Sie den Befehl ".scriptunload" zum Entladen des Skripts.
0:000> .scriptunload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully unloaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'
Debuggerbefehlsautomatisierung
In diesem Abschnitt wird beschrieben, wie Sie ein einfaches JavaScript-Debuggerskript erstellen und ausführen, das das Senden des Befehls "u" (Unassemble) automatisiert. Im Beispiel wird auch gezeigt, wie Befehlsausgabe in einer Schleife erfasst und angezeigt wird.
Dieses Skript stellt eine einzelne Funktion bereit, 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");
}
Verwenden Sie einen Text-Editor wie Editor, um eine Textdatei mit dem Namen RunCommands.js zu erstellen.
Verwenden Sie den Befehl ".scriptload", um das RunCommands-Skript zu laden.
0:000> .scriptload c:\WinDbg\Scripts\RunCommands.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\RunCommands.js'
Nachdem das Skript geladen wurde, ist die zusätzliche Funktionalität im Debugger verfügbar. Verwenden Sie den Dx-Befehl (NatVis-Ausdruck anzeigen), um Debugger.State.Scripts.RunCommands anzuzeigen, um zu sehen, dass unser Skript jetzt ansässig ist.
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]
Verwenden Sie den Dx-Befehl, um die RunCommands-Funktion im RunCommands-Skript aufzurufen.
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
Spezielle JavaScript-Debuggerfunktionen
Es gibt mehrere spezielle Funktionen in einem JavaScript-Skript, das vom Skriptanbieter selbst aufgerufen wird.
SkriptInitialisieren
Wenn ein JavaScript-Skript geladen und ausgeführt wird, durchläuft es eine Reihe von Schritten, bevor sich die Variablen, Funktionen und andere Objekte im Skript auf das Objektmodell des Debuggers auswirken.
- Das Skript wird in den Arbeitsspeicher geladen und analysiert.
- Der Stammcode im Skript wird ausgeführt.
- Wenn das Skript über eine Methode mit dem Namen InitializeScript verfügt, wird diese Methode aufgerufen.
- Der Rückgabewert aus initializeScript wird verwendet, um zu bestimmen, wie das Objektmodell des Debuggers automatisch geändert wird.
- Die Namen im Skript werden in den Namespace des Debuggers eingebunden.
Wie bereits erwähnt, wird InitializeScript unmittelbar aufgerufen, nachdem der Stammcode des Skripts ausgeführt wurde. Der Auftrag besteht darin, ein JavaScript-Array von Registrierungsobjekten an den Anbieter zurückzugeben, der angibt, wie das Objektmodell des Debuggers geändert wird.
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
Die invokeScript-Methode ist die primäre Skriptmethode und wird aufgerufen, wenn .scriptload und .scriptrun ausgeführt werden.
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
Die uninitializeScript-Methode ist das Verhaltens-Gegenteil der initializeScript-Methode. Es wird aufgerufen, wenn eine Verknüpfung eines Skripts aufgehoben wird und bereit zum Entladen ist. Der Auftrag besteht darin, alle Änderungen am Objektmodell rückgängig zu machen, die das Skript während der Ausführung zwingend vorgenommen hat, und/oder alle Objekte zu zerstören, die das Skript zwischengespeichert hat.
Wenn ein Skript weder imperative Manipulationen an das Objektmodell vornimmt noch Ergebnisse zwischenspeichert, muss keine nicht initializeScript-Methode vorhanden sein. Alle Änderungen am Objektmodell, die durch den Rückgabewert von initializeScript angegeben werden, werden automatisch vom Anbieter rückgängig gemacht. Für solche Änderungen ist keine explizite uninitializeScript-Methode erforderlich.
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");
}
Zusammenfassung der von Skriptbefehlen aufgerufenen Funktionen
In dieser Tabelle wird zusammengefasst, welche Funktionen von den Skriptbefehlen aufgerufen werden.
| Befehl | .scriptload | .scriptrun (Run Script) | .scriptunload (Unload Script) |
|---|---|---|---|
| wurzel | ja | ja | |
| initializeScript | ja | ja | |
| invokeScript | ja | ||
| uninitializeScript | ja |
Verwenden Sie diesen Beispielcode, um zu sehen, wann jede Funktion aufgerufen wird, während das Skript geladen, ausgeführt und entladen wird.
// 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");
}
Erstellen eines Debugger-Visualizers in JavaScript
Mit benutzerdefinierten Visualisierungsdateien können Sie Daten in einer Visualisierungsstruktur gruppieren und organisieren, die die Datenbeziehungen und Inhalte besser widerspiegelt. Sie können die JavaScript-Debuggererweiterungen verwenden, um Debuggerschnellansichten zu schreiben, die ähnlich wie NatVis funktionieren. Dies erfolgt über die Erstellung eines JavaScript-Prototypobjekts (oder einer ES6-Klasse), das als Visualizer für einen bestimmten Datentyp fungiert. Weitere Informationen zu NatVis und dem Debugger finden Sie unter dx (Display NatVis Expression).For more information about NatVis and the debugger see dx (Display NatVis Expression).
Beispielklasse – Simple1DArray
Betrachten Sie ein Beispiel für eine C++-Klasse, die ein eindimensionales Array darstellt. Diese Klasse verfügt über zwei Member: m_size, die Gesamtgröße des Arrays, und m_pValues, ein Zeiger auf eine Anzahl von Ganzzahlen im Arbeitsspeicher, die dem Feld m_size entsprechen.
class Simple1DArray
{
private:
ULONG64 m_size;
int *m_pValues;
};
Wir können den Dx-Befehl verwenden, um das Standardmäßige Datenstrukturrendering zu betrachten.
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
Um diesen Typ zu visualisieren, müssen wir eine Prototypklasse (oder ES6) erstellen, die alle Felder und Eigenschaften enthält, die der Debugger anzeigen soll. Außerdem muss die initializeScript-Methode ein Objekt zurückgeben, das dem JavaScript-Anbieter angibt, unseren Prototyp als Visualizer für den angegebenen Typ zu verknüpfen.
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")];
}
Speichern Sie das Skript in einer Datei mit dem Namen arrayVisualizer.js.
Verwenden Sie den Befehl .load (Load Extension DLL), um den JavaScript-Anbieter zu laden.
0:000> .load C:\ScriptProviders\jsprovider.dll
Verwenden Sie .scriptload, um das Array-Visualizer-Skript zu laden.
0:000> .scriptload c:\WinDbg\Scripts\arrayVisualizer.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\arrayVisualizer.js'
Wenn nun der Dx-Befehl verwendet wird, wird der Skriptvisualisierer Zeilen der Arrayinhalte darstellen.
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
Darüber hinaus stellt diese JavaScript-Visualisierung LINQ-Funktionen bereit, z. B. 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
Auswirkungen auf die Visualisierung
Ein Prototyp oder eine Klasse, der die Visualisierung für einen systemeigenen Typ über eine Rückgabe eines host.typeSignatureRegistration-Objekts aus initializeScript erstellt, verfügt über alle Eigenschaften und Methoden innerhalb von JavaScript, die dem nativen Typ hinzugefügt werden. Darüber hinaus gelten die folgenden Semantiken:
Jeder Name, der nicht mit zwei Unterstrichen (__) beginnt, ist in der Visualisierung verfügbar.
Namen, die Teil von Standard-JavaScript-Objekten sind oder Teil von Protokollen sind, die vom JavaScript-Anbieter erstellt werden, werden in der Visualisierung nicht angezeigt.
Ein Objekt kann über die Unterstützung von [Symbol.iterator] iterierbar gemacht werden.
Ein Objekt kann über die Unterstützung eines benutzerdefinierten Protokolls indiziert werden, das aus mehreren Funktionen besteht: getDimensionality, getValueAt und optional setValueAt.
Native und JavaScript-Objektbrücke
Die Brücke zwischen JavaScript und dem Objektmodell des Debuggers ist bidirektional. Systemeigene Objekte können an JavaScript übergeben werden, und JavaScript-Objekte können an den Ausdrucksauswerter des Debuggers übergeben werden. Betrachten Sie als Beispiel die Hinzufügung der folgenden Methode in unserem Skript:
function multiplyBySeven(val)
{
return val * 7;
}
Diese Methode kann jetzt in der obigen LINQ-Beispielabfrage verwendet werden. Zuerst laden wir die JavaScript-Visualisierung.
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
Dann können wir die multiplyBySeven-Funktion inline verwenden, wie unten dargestellt.
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
Bedingte Haltepunkte mit JavaScript
Sie können JavaScript verwenden, um eine zusätzliche Verarbeitung durchzuführen, nachdem ein Haltepunkt erreicht wurde. Beispielsweise kann ein Skript verwendet werden, um andere Laufzeitwerte zu untersuchen und dann festzustellen, ob Sie die Codeausführung automatisch fortsetzen oder beenden möchten, und zusätzliches manuelles Debuggen ausführen möchten.
Allgemeine Informationen zum Arbeiten mit Haltepunkten finden Sie unter Methoden zum Steuern von Haltepunkten.
DebugHandler.js Beispielskript für die Haltepunktverarbeitung
In diesem Beispiel wird das Öffnen- und Speichern-Dialogfenster von Notepad ausgewertet: notepad!ShowOpenSaveDialog. Dieses Skript wertet die pszCaption-Variable aus, um zu ermitteln, ob das aktuelle Dialogfeld ein Dialogfeld "Öffnen" ist oder ob es sich um ein Dialogfeld "Speichern unter" handelt. Wenn es sich um ein geöffnetes Dialogfeld handelt, wird die Codeausführung fortgesetzt. Wenn es sich um ein "Speichern unter"-Dialog handelt, wird die Programmausführung gestoppt, und der Debugger hält an.
// 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");
}
}
Mit diesem Befehl wird ein Haltepunkt bei notepad!ShowOpenSaveDialog festgelegt und das obige Skript wird jedes Mal ausgeführt, wenn dieser Haltepunkt erreicht wird.
bp notepad!ShowOpenSaveDialog ".scriptrun C:\\WinDbg\\Scripts\\DebugHandler.js"
Wenn dann die Option "Datei > speichern" im Editor ausgewählt ist, wird das Skript ausgeführt, der Befehl g nicht gesendet, und eine Unterbrechung der Codeausführung tritt auf.
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
Arbeiten mit 64-Bit-Werten in JavaScript-Erweiterungen
In diesem Abschnitt wird beschrieben, wie sich 64-Bit-Werte, die in eine JavaScript-Debuggererweiterung übergeben werden, verhalten. Dieses Problem tritt auf, da JavaScript nur die Möglichkeit hat, Zahlen mit 53-Bits zu speichern.
64-Bit- und JavaScript-53-Bit-Speicher
Ordinalwerte, die in JavaScript übergeben werden, werden normalerweise als JavaScript-Zahlen geparsed. Das Problem besteht darin, dass JavaScript-Zahlen 64-Bit-Gleitkommawerte mit doppelter Genauigkeit sind. Alle Ordinalzahlen über 53 Bit verlieren ihre Genauigkeit beim Übertragen an JavaScript. Dies stellt ein Problem für 64-Bit-Zeiger und andere 64-Bit-Ordinalwerte dar, die Flags in den höchsten Bytes aufweisen können. Um dies zu bewältigen, wird jeder 64-Bit-native Wert (ob aus systemeigenem Code oder dem Datenmodell), der in JavaScript eingeht, als Bibliothekstyp und nicht als JavaScript-Zahl dargestellt. Dieser Bibliothekstyp wird zurück zum systemeigenen Code zurückwechseln, ohne dass die numerische Genauigkeit verloren geht.
Automatische Konvertierung
Der Bibliothekstyp für 64-Bit-Ordinalwerte unterstützt die Standardmäßige JavaScript-WertOf-Konvertierung. Wenn das Objekt in einem mathematischen Vorgang oder einem anderen Konstrukt verwendet wird, das eine Wertkonvertierung erfordert, wird es automatisch in eine JavaScript-Zahl konvertiert. Wenn ein Genauigkeitsverlust auftritt (der Wert verwendet mehr als 53-Bit-Ordnungsgenauigkeit), löst der JavaScript-Anbieter eine Ausnahme aus.
Beachten Sie, dass Sie bei Verwendung bitweiser Operatoren in JavaScript weiter auf 32-Bit-Genauigkeit beschränkt sind.
Dieser Beispielcode summiert zwei Zahlen und wird verwendet, um die Konvertierung von 64-Bit-Werten zu testen.
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);
}
Verwenden Sie einen Text-Editor wie Editor, um eine Textdatei mit dem Namen PlayWith64BitValues.js zu erstellen.
Verwenden Sie den Befehl ".scriptload", um das Skript zu laden.
0:000> .scriptload c:\WinDbg\Scripts\PlayWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\PlayWith64BitValues.js'
Um das Arbeiten mit dem Skript etwas bequemer zu gestalten, weisen Sie im Debugger eine Variable zu, die den Inhalt des Skripts mithilfe des dx-Befehls speichert.
0:000> dx @$myScript = Debugger.State.Scripts.PlayWith64BitValues.Contents
Verwenden Sie den Dx-Ausdrucks-Evaluator, um die addTwoValues-Funktion aufzurufen.
Zunächst berechnen wir den Wert 2^53 =9007199254740992 (Hex 0x20000000000000).
Zunächst werden wir (2^53) - 2 verwenden und sehen, dass der richtige Wert für die Summe zurückgegeben wird.
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740990)
Sum >> 18014398509481980
Anschließend berechnen wir (2^53) -1 =9007199254740991. Dadurch wird der Fehler zurückgegeben, der angibt, dass der Konvertierungsprozess die Genauigkeit verliert, sodass dies der größte Wert ist, der mit der Summenmethode im JavaScript-Code verwendet werden kann.
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number
Rufen Sie eine Datenmodellmethode auf, die 64-Bit-Werte übergibt. Hier gibt es keinen Genauigkeitsverlust.
0:001> dx @$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, (x, y) => x + y)
@$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, (x, y) => x + y) : 0xfffffffffffffffe
Vergleich
Der 64-Bit-Bibliothekstyp ist ein JavaScript-Objekt und kein Werttyp wie eine JavaScript-Nummer. Dies hat einige Auswirkungen auf Vergleichsvorgänge. Normalerweise würde die Gleichheit (==) eines Objekts darauf hinweisen, dass Operanden auf dasselbe Objekt und nicht auf denselben Wert verweisen. Der JavaScript-Provider mindert das, indem er Live-Referenzen auf 64-Bit-Werte nachverfolgt und dasselbe "unveränderliche" Objekt für nicht eingesammelte 64-Bit-Werte zurückgibt. Dies bedeutet, dass im Vergleich folgendes auftreten würde.
// 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");
Verwenden Sie einen Text-Editor wie Editor, um eine Textdatei mit dem Namen ComparisonWith64BitValues.js zu erstellen.
Verwenden Sie den Befehl ".scriptload", um das Skript zu laden.
0:000> .scriptload c:\WinDbg\Scripts\ComparisonWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\ComparisonWith64BitValues.js'
Um das Arbeiten mit dem Skript etwas bequemer zu gestalten, weisen Sie im Debugger eine Variable zu, die den Inhalt des Skripts mithilfe des dx-Befehls speichert.
0:000> dx @$myScript = Debugger.State.Scripts.comparisonWith64BitValues.Contents
Zunächst werden wir (2^53) - 2 verwenden und sehen, dass sie die erwarteten Werte zurückgibt.
0:001> dx @$myScript.comparisonWith64BitValues(9007199254740990, 9007199254740990)
areEqual >> true
areNotEqual >> false
isEqualTo42 >> false
isLess >> false
Wir werden auch die Zahl 42 als ersten Wert testen, um zu überprüfen, ob der Vergleichsoperator wie erwartet funktioniert.
0:001> dx @$myScript.comparisonWith64BitValues(42, 9007199254740990)
areEqual >> false
areNotEqual >> true
isEqualTo42 >> true
isLess >> true
Anschließend berechnen wir (2^53) -1 =9007199254740991. Dieser Wert gibt den Fehler zurück, der angibt, dass der Konvertierungsprozess die Genauigkeit verliert. Dies ist der größte Wert, der mit den Vergleichsoperatoren im JavaScript-Code verwendet werden kann.
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number
Aufrechterhaltung der Genauigkeit in Vorgängen
Um die Präzision einer Debugger-Erweiterung zu gewährleisten, wird eine Reihe von mathematischen Funktionen auf dem 64-Bit-Bibliothekstyp abgebildet. Wenn die Erweiterung möglicherweise eine Präzision über 53 Bit für eingehende 64-Bit-Werte benötigt, sollten sie die folgenden Methoden verwenden, anstatt sich auf die üblichen Operatoren zu verlassen.
| Methodenname | Signatur- | Beschreibung |
|---|---|---|
| asNumber | .asNumber() | Konvertiert den 64-Bit-Wert in eine JavaScript-Zahl. Wenn der Genauigkeitsverlust auftritt, wird **EINE AUSNAHME AUSGEWORFEN** |
| convertToNumber | .convertToNumber() | Konvertiert den 64-Bit-Wert in eine JavaScript-Zahl. Wenn der Genauigkeitsverlust auftritt, wird **KEINE AUSNAHME GEWORFEN**. |
| ErhalteUnterenTeil | .getLowPart() | Konvertiert die niedrigeren 32-Bit-Werte des 64-Bit-Werts in eine JavaScript-Zahl. |
| getHighPart | .getHighPart() | Konvertiert die höherwertigen 32 Bits des 64-Bit-Werts in eine JavaScript-Zahl. |
| hinzufügen | .add(value) | Fügt dem 64-Bit-Wert einen Wert hinzu und gibt das Ergebnis zurück. |
| Subtrahieren | .subtrahieren(Wert) | Subtrahiert einen Wert vom 64-Bit-Wert und gibt das Ergebnis zurück. |
| multiplizieren | .multiplizieren(wert) | Multipliziert den 64-Bit-Wert mit dem angegebenen Wert und gibt das Ergebnis zurück. |
| divide | .divide(value) | Dividiert den 64-Bit-Wert durch den angegebenen Wert und gibt das Ergebnis zurück. |
| bitwiseAnd | .bitwiseAnd(value) | Berechnet den bitweisen und des 64-Bit-Werts mit dem angegebenen Wert und gibt das Ergebnis zurück. |
| bitwiseOr | .bitwiseOr(value) | Berechnet den bitweisen oder des 64-Bit-Werts mit dem angegebenen Wert und gibt das Ergebnis zurück. |
| bitwiseXor | .bitwiseXor(value) | Berechnet den bitweisen xor des 64-Bit-Werts mit dem angegebenen Wert und gibt das Ergebnis zurück. |
| bitwiseShiftLeft | .bitwiseShiftLeft(value) | Verschiebt den 64-Bit-Wert um den angegebenen Betrag nach links und gibt das Ergebnis zurück. |
| bitwiseShiftRight | .bitwiseShiftRight(value) | Verschiebt den 64-Bit-Wert um den angegebenen Betrag nach rechts und gibt das Ergebnis zurück. |
| toString | .toString([radix]) | Wandelt den 64-Bit-Wert in eine Darstellungszeichenfolge im Standardradix (oder im optional bereitgestellten Radix) um. |
Diese Methode ist auch verfügbar.
| Methodenname | Signatur- | Beschreibung |
|---|---|---|
| compareTo | .compareTo(value) | Vergleicht den 64-Bit-Wert mit einem anderen 64-Bit-Wert. |
JavaScript-Debugging
In diesem Abschnitt wird beschrieben, wie Sie die Skriptdebuggingfunktionen des Debuggers verwenden. Der Debugger unterstützt das Debuggen von JavaScript-Skripts mithilfe des Befehls ".scriptdebug( Debug JavaScript) ".
Hinweis
Um das JavaScript-Debugging mit WinDbg zu verwenden, führen Sie den Debugger als Administrator aus.
Verwenden Sie diesen Beispielcode, um das Debuggen eines JavaScript zu untersuchen. Für diese exemplarische Vorgehensweise benennen wir sie DebuggableSample.js und speichern sie im Verzeichnis "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
//
}
Laden Sie das Beispielskript.
.scriptload C:\MyScripts\DebuggableSample.js
Starten Sie aktiv das Debuggen des Skripts mithilfe des Befehls ".scriptdebug" .
0:000> .scriptdebug C:\MyScripts\DebuggableSample.js
>>> ****** DEBUGGER ENTRY DebuggableSample ******
No active debug event!
>>> Debug [DebuggableSample <No Position>] >
Sobald die Eingabeaufforderung >>> Debug [DebuggableSample <No Position>] > und eine Eingabeanforderung angezeigt werden, befinden Sie sich im Skriptdebugger.
Verwenden Sie den BEFEHL ".help ", um eine Liste von Befehlen in der JavaScript-Debugumgebung anzuzeigen.
>>> 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
Verwenden Sie den Befehl für den sx-Skriptdebugger , um die Liste der Ereignisse anzuzeigen, die wir abfangen können.
>>> 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
Verwenden Sie den Skriptdebuggerbefehl sxe, um Unterbrechung beim Eintritt zu aktivieren, damit das Skript in den Skriptdebugger wechselt, sobald irgendein Code darin ausgeführt wird.
>>> Debug [DebuggableSample <No Position>] >sxe en
sxe en
Event filter 'en' is now active
Beenden Sie den Skriptdebugger, und wir führen einen Funktionsaufruf in das Skript durch, das in den Debugger fällt.
>>> Debug [DebuggableSample <No Position>] >q
An diesem Punkt befinden Sie sich wieder im normalen Debugger. Führen Sie den folgenden Befehl aus, um das Skript aufzurufen.
dx @$scriptContents.outermost()
Jetzt befinden Sie sich wieder im Skriptdebugger und haben in der ersten Zeile der äußersten JavaScript-Funktion haltgemacht.
>>> ****** SCRIPT BREAK DebuggableSample [BreakIn] ******
Location: line = 73, column = 5
Text: var x = 99
>>> Debug [DebuggableSample 73:5] >
Zusätzlich zur Anzeige des Haltepunkts im Debugger erhalten Sie Informationen zu der Zeile (73) und der Spalte (5), in der der Haltepunkt stattgefunden hat, sowie den relevanten Codeausschnitt des Quellcodes: var x = 99.
Lassen Sie uns ein paar Schritte machen und an eine andere Stelle im Skript gelangen.
p
t
p
t
p
p
An diesem Punkt sollten Sie in die throwAndCatch-Methode in Zeile 34 unterteilt werden.
...
>>> ****** SCRIPT BREAK DebuggableSample [Step Complete] ******
Location: line = 34, column = 5
Text: var curProc = host.currentProcess
Sie können dies überprüfen, indem Sie eine Stapelablaufverfolgung ausführen.
>>> 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())
Von hier aus können Sie den Wert von Variablen untersuchen.
>>> 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
Lassen Sie uns einen Haltepunkt in der aktuellen Codezeile festlegen und sehen, welche Haltepunkte jetzt festgelegt sind.
>>> 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
Von hier aus deaktivieren wir das Eintragsereignis (en) mithilfe des Skriptdebuggerbefehls sxd .
>>> Debug [DebuggableSample 34:5] >sxd en
sxd en
Event filter 'en' is now inactive
Und dann einfach loslegen und das Skript bis zum Ende fortsetzen lassen.
>>> Debug [DebuggableSample 34:5] >g
g
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test
...
Führen Sie die Skriptmethode erneut aus, und beobachten Sie, wie unser Haltepunkt erreicht wird.
0:000> dx @$scriptContents.outermost()
inside outer!
>>> ****** SCRIPT BREAK DebuggableSample [Breakpoint 1] ******
Location: line = 34, column = 5
Text: var curProc = host.currentProcess
Zeigen Sie den Aufrufstapel an.
>>> 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())
An diesem Punkt möchten wir das Debuggen dieses Skripts beenden, daher lösen wir die Verbindung dazu.
>>> Debug [DebuggableSample 34:5] >.detach
.detach
Debugger has been detached from script!
Geben Sie dann q ein, um zu beenden.
q
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test
Durch das erneute Ausführen der Funktion wird der Debugger nicht mehr ausgelöst.
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 VS-Code – Hinzufügen von IntelliSense
Wenn Sie mit den Debugger-Datenmodellobjekten in VS Code arbeiten möchten, können Sie eine Definitionsdatei verwenden, die in den Windows-Entwicklungskits verfügbar ist. Die IntelliSense-Definitionsdatei bietet Unterstützung für alle Host.*-Debuggerobjekt-APIs. Wenn Sie das Kit im Standardverzeichnis auf einem 64-Bit-PC installiert haben, befindet es sich hier:
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\JsProvider.d.ts
So verwenden Sie die IntelliSense-Definitionsdatei in VS Code:
Suchen der Definitionsdatei – JSProvider.d.ts
Kopieren Sie die Definitionsdatei in denselben Ordner wie Ihr Skript.
Fügen Sie
/// <reference path="JSProvider.d.ts" />am Anfang der JavaScript-Skriptdatei hinzu.
Mit dieser Referenz in Ihrer JavaScript-Datei gibt VS Code Ihnen automatisch IntelliSense auf den host-APIs, die von JSProvider bereitgestellt werden, zusätzlich zu den Strukturen in Ihrem Skript. Geben Sie z. B. "Host" ein. Und Sie werden IntelliSense für alle verfügbaren APIs des Debuggermodells sehen.
JavaScript-Ressourcen
Im Folgenden finden Sie JavaScript-Ressourcen, die beim Entwickeln von JavaScript-Debuggingerweiterungen hilfreich sein können.