JavaScript デバッガーのスクリプト
このトピックでは、JavaScript を使用して、デバッガー オブジェクトを理解し、デバッガーの機能を拡張およびカスタマイズするスクリプトを作成する方法について説明します。
JavaScript デバッガー スクリプトの概要
スクリプト プロバイダーは、スクリプト言語をデバッガーの内部オブジェクト モデルにブリッジします。 JavaScript デバッガー スクリプト プロバイダーでは、デバッガーで JavaScript を使用できます。
.scriptload コマンドを介して JavaScript がロードされると、スクリプトのルート コードが実行され、スクリプト内に存在する名前がデバッガ (dx デバッガ) のルート名前空間にブリッジされ、スクリプトはメモリ内に常駐します。 はアンロードされ、そのオブジェクトへのすべての参照が解放されます。 スクリプトは、デバッガーの式エバリュエーターに新しい関数を提供したり、デバッガーのオブジェクト モデルを変更したり、NatVis ビジュアライザーとほぼ同じ方法でビジュアライザーとして機能したりできます。
このトピックでは、JavaScript デバッガー スクリプトで実行できるいくつかの操作について説明します。
これら 2 つのトピックでは、デバッガーでの JavaScript の操作に関する追加情報を提供します。
JavaScript スクリプトのビデオ
デフラグツール #170 - Andy と Bill は、デバッガーでの JavaScript の拡張性とスクリプト機能を示しています。
デバッガー JavaScript プロバイダー
デバッガーに含まれる JavaScript プロバイダーは、最新の ECMAScript6 オブジェクトとクラスの機能強化を最大限に活用します。 詳細については ECMAScript 6 — 新機能: 概要 & と比較を参照してください。
JsProvider.dll
JsProvider.dllは、JavaScript デバッガー スクリプトをサポートするために読み込まれる JavaScript プロバイダーです。
要件
JavaScript デバッガー スクリプトは、サポートされているすべてのバージョンの Windows で動作するように設計されています。
JavaScript スクリプト プロバイダーの読み込み
.scriptコマンドを使用する前に、スクリプト・プロバイダをロードする必要がある。 .scriptprovidersコマンドを使用して、JavaScriptプロバイダがロードされていることを確認します。
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
JavaScript スクリプトのメタ コマンド
JavaScript デバッガー スクリプトを使用するには、次のコマンドを使用できます。
- .scriptproviders (スクリプト プロバイダーの一覧表示)
- .scriptload (スクリプトの読み込み)
- .scriptunload (スクリプトのアンロード)
- .scriptrun (スクリプトの実行)
- .scriptlist (読み込まれたスクリプトの一覧表示)
要件
.scriptコマンドを使用する前に、スクリプト・プロバイダをロードする必要がある。 .scriptprovidersコマンドを使用して、JavaScriptプロバイダがロードされていることを確認します。
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
.scriptproviders (スクリプト プロバイダーの一覧表示)
.scriptprovidersコマンドは、現在デバッガが理解しているすべてのスクリプト言語と、それらが登録されている拡張子を一覧表示します。
以下の例では、JavaScriptとNatVisプロバイダーがロードされている。
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
".NatVis "で終わるファイルはNatVisスクリプトとして理解され、".js "で終わるファイルはJavaScriptスクリプトとして理解されます。 どちらのタイプのスクリプトも、.scriptloadコマンドでロードできる。
詳細については .scriptproviders (スクリプト プロバイダーの一覧表示) を参照してください 。
.scriptload (スクリプトの読み込み)
.scriptload コマンドはスクリプトを読み込み、スクリプトのルート コードと initializeScript 関数を実行します。 スクリプトの初期読み込みと実行にエラーがある場合は、エラーがコンソールに表示されます。 次のコマンドは、TestScript.jsのロードに成功したことを示している。
0:000> .scriptload C:\WinDbg\Scripts\TestScript.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\TestScript.js'
スクリプトによって行われたオブジェクトモデルの操作は、スクリプトがその後にアンロードされるか、別の内容で再度実行されるまで、そのまま残ります。
詳細については .scriptload (スクリプトの読み込み) を参照してください 。
.scriptrun
.scriptrun コマンドはスクリプトを読み込み、スクリプトのルート コード、 initializeScript および invokeScript 関数を実行します。 スクリプトの初期読み込みと実行にエラーがある場合は、エラーがコンソールに表示されます。
0:000> .scriptrun C:\WinDbg\Scripts\helloWorld.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\helloWorld.js'
Hello World! We are in JavaScript!
スクリプトによって行われたデバッガー オブジェクト モデルの操作は、その後スクリプトがアンロードされるか、別の内容で再度実行されるまで、そのまま残ります。
詳細については .scriptrun (スクリプトの実行)を参照してください。
.scriptunload (スクリプトのアンロード)
.scriptunload コマンドは、読み込まれたスクリプトをアンロードし uninitializeScript 関数を呼び出します。 スクリプトをアンロードするには、次のコマンド構文を使用します。
0:000:x86> .scriptunload C:\WinDbg\Scripts\TestScript.js
JavaScript script unloaded from 'C:\WinDbg\Scripts\TestScript.js'
詳細については .scriptunload (アンロード スクリプト) を参照してください。
.scriptlist (読み込まれたスクリプトの一覧表示)
scriptlist コマンドは、.scriptload または .scriptrun コマンドを介してロードされたスクリプトを一覧表示します。 .scriptloadを使用してTestScriptが正常にロードされた場合、.scriptlistコマンドはロードされたスクリプトの名前を表示します。
0:000> .scriptlist
Command Loaded Scripts:
JavaScript script from 'C:\WinDbg\Scripts\TestScript.js'
詳細については .scriptlist (読み込まれたスクリプトの一覧表示) を参照してください。
JavaScript デバッガー スクリプトの概要
HelloWorld サンプル スクリプト
このセクションでは、Hello World を出力する単純な JavaScript デバッガー スクリプトを作成して実行する方法について説明します。
// WinDbg JavaScript sample
// Prints Hello World
function initializeScript()
{
host.diagnostics.debugLog("***> Hello World! \n");
}
メモ帳などのテキスト エディターを使用して、上記の JavaScript コードを含む HelloWorld.js という名前のテキスト ファイルを作成します。
.scriptload コマンドを使用して、スクリプトを読み込んで実行します。 関数名 initializeScript を使用したため、スクリプトの読み込み時に関数内のコードが実行されます。
0:000> .scriptload c:\WinDbg\Scripts\HelloWorld.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\HelloWorld.js'
***> Hello World!
スクリプトが読み込まれた後、デバッガーで追加の機能を使用できます。 dx (NatVis 式の表示) コマンドを使用して Debugger.State.Scripts を表示し、スクリプトが常駐していることを確認します。
0:000> dx Debugger.State.Scripts
Debugger.State.Scripts
HelloWorld
次の例では、名前付き関数を追加して呼び出します。
2 つの値を追加するサンプル スクリプト
このセクションでは、入力を受け取り、2 つの数値を追加する単純な JavaScript デバッガー スクリプトを作成して実行する方法について説明します。
この単純なスクリプトは、addTwoValues という 1 つの関数を提供します。
// WinDbg JavaScript sample
// Adds two functions
function addTwoValues(a, b)
{
return a + b;
}
メモ帳などのテキスト エディターを使用して FirstSampleFunction.js という名前のテキスト ファイルを作成する
.scriptload コマンドを使用してスクリプトを読み込みます。
0:000> .scriptload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'
スクリプトが読み込まれた後、デバッガーで追加の機能を使用できます。 dx (NatVis 式の表示) コマンドを使用して Debugger.State.Scripts を表示し、スクリプトが常駐していることを確認します。
0:000> dx Debugger.State.Scripts
Debugger.State.Scripts
FirstSampleFunction
FirstSampleFunction を選択して、提供される関数を確認できます。
0:000> dx -r1 -v Debugger.State.Scripts.FirstSampleFunction.Contents
Debugger.State.Scripts.FirstSampleFunction.Contents : [object Object]
host : [object Object]
addTwoValues
...
スクリプトを操作しやすくするには、dx コマンドを使用して、スクリプトの内容を保持する変数をデバッガーに割り当てます。
0:000> dx @$myScript = Debugger.State.Scripts.FirstSampleFunction.Contents
dx 式エバリュエーターを使用して addTwoValues 関数を呼び出します。
0:000> dx @$myScript.addTwoValues(10, 41),d
@$myScript.addTwoValues(10, 41),d : 51
また、$@scriptContents 組み込みのエイリアスを使用してスクリプトを操作することもできます。 $@scriptContents エイリアスは、読み込まれるすべてのスクリプトの .Content をマージします。
0:001> dx @$scriptContents.addTwoValues(10, 40),d
@$scriptContents.addTwoValues(10, 40),d : 50
スクリプトの操作が完了したら、.scriptunload コマンドを使用してスクリプトをアンロードします。
0:000> .scriptunload c:\WinDbg\Scripts\FirstSampleFunction.js
JavaScript script successfully unloaded from 'c:\WinDbg\Scripts\FirstSampleFunction.js'
デバッガー コマンドの自動化
このセクションでは u (組み立て) コマンド の送信を自動化する単純な JavaScript デバッガー スクリプトを作成して実行する方法について説明します。 このサンプルでは、ループ内でコマンド出力を収集して表示する方法も示します。
このスクリプトは、1 つの関数 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");
}
メモ帳などのテキスト エディターを使用して RunCommands.js という名前のテキスト ファイルを作成する
.scriptload コマンドを使用して、RunCommands スクリプトをロードします。
0:000> .scriptload c:\WinDbg\Scripts\RunCommands.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\RunCommands.js'
スクリプトが読み込まれた後、デバッガーで追加の機能を使用できます。 dx (Display NatVis Expression) コマンドを使用して Debugger.State.Scripts.RunCommands を表示し、スクリプトが常駐していることを確認します。
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]
dx コマンドを使用して、RunCommands スクリプトで 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
特殊な JavaScript デバッガー関数
JavaScript スクリプトには、スクリプト プロバイダー自体によって呼び出される特殊な関数がいくつかあります。
initializeScript
JavaScript スクリプトを読み込んで実行すると、スクリプト内の変数、関数、およびその他のオブジェクトがデバッガーのオブジェクト モデルに影響する前に、一連の手順を実行します。
- スクリプトがメモリに読み込まれ、解析されます。
- スクリプト内のルート コードが実行されます。
- スクリプトに initializeScript というメソッドがある場合、そのメソッドが呼び出されます。
- initializeScript からの戻り値は、デバッガーのオブジェクト モデルを自動的に変更する方法を決定するために使用されます。
- スクリプト内の名前は、デバッガーの名前空間にブリッジされます。
メンションしたように、スクリプトのルート コードが実行された直後に initializeScript が呼び出されます。 そのジョブは、デバッガーのオブジェクト モデルを変更する方法を示す登録オブジェクトの JavaScript 配列をプロバイダーに返します。
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
invokeScript メソッドはプライマリ スクリプト メソッドであり、.scriptload と .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
uninitializeScript メソッドは、initializeScript とは逆の動作です。 スクリプトのリンクが解除され、アンロードの準備が整っているときに呼び出されます。 そのジョブは、実行中にスクリプトが命令的に行ったオブジェクト モデルに対する変更を元に戻したり、スクリプトがキャッシュしたオブジェクトを破棄したりすることです。
スクリプトがオブジェクト モデルに対する命令型操作も結果のキャッシュも行わない場合は、uninitializeScript メソッドを使用する必要はありません。 initializeScript の戻り値によって示されるように実行されたオブジェクト モデルに対する変更は、プロバイダーによって自動的に元に戻されます。 このような変更には、明示的な 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");
}
スクリプト コマンドによって呼び出される関数の概要
次の表は、スクリプト コマンドによって呼び出される関数をまとめたものです。
コマンド | .scriptload | .scriptrun (スクリプトの実行) | .scriptunload (スクリプトのアンロード) |
---|---|---|---|
root | はい | はい | |
initializeScript | はい | はい | |
invokeScript | はい | ||
uninitializeScript | はい |
このサンプル コードを使用して、スクリプトの読み込み、実行、アンロード時に各関数が呼び出されるタイミングを確認します。
// 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");
}
JavaScript でのデバッガー ビジュアライザーの作成
カスタム視覚化ファイルを使用すると、データリレーションシップとコンテンツをより適切に反映する視覚化構造でデータをグループ化および整理できます。 JavaScript デバッガー拡張機能を使用して、NatVis とよく似た方法で動作するデバッガー ビジュアライザーを記述できます。 これは、特定のデータ型のビジュアライザーとして機能する JavaScript プロトタイプ オブジェクト (または ES6 クラス) を作成することによって実現されます。 NatVis とデバッガの詳細については、 dx (NatVis 式の表示)を参照してください。
クラスの例 - Simple1DArray
1 次元配列を表す C++ クラスの例を考えてみましょう。 このクラスには 2 つのメンバーがあります。m_size配列の全体的なサイズと、m_size フィールドと等しいメモリ内の int の数へのポインターであるm_pValues。
class Simple1DArray
{
private:
ULONG64 m_size;
int *m_pValues;
};
dx コマンドを使用して、既定のデータ構造のレンダリングを見ることができます。
0:000> dx g_array1D
g_array1D [Type: Simple1DArray]
[+0x000] m_size : 0x5 [Type: unsigned __int64]
[+0x008] m_pValues : 0x8be32449e0 : 0 [Type: int *]
JavaScript ビジュアライザー
この型を視覚化するには、デバッガーに表示するすべてのフィールドとプロパティを含むプロトタイプ (または ES6) クラスを作成する必要があります。 また、initializeScript メソッドで、指定された型のビジュアライザーとしてプロトタイプをリンクするように JavaScript プロバイダーに指示するオブジェクトを返す必要があります。
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")];
}
スクリプトを arrayVisualizer.js という名前のファイルに保存します。
.load (拡張 DLL のロード) コマンドを使用して、JavaScript プロバイダーをロードします。
0:000> .load C:\ScriptProviders\jsprovider.dll
.scriptload を使用して、配列ビジュアライザー スクリプトを読み込みます。
0:000> .scriptload c:\WinDbg\Scripts\arrayVisualizer.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\arrayVisualizer.js'
これで、dx コマンドを使用すると、スクリプト ビジュアライザーに配列コンテンツの行が表示されます。
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
さらに、この JavaScript 視覚化には、[選択] などの LINQ 機能が用意されています。
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
視覚エフェクトに影響するもの
initializeScript から host.typeSignatureRegistration オブジェクトを返してネイティブ型のビジュアライザーを作成するプロトタイプまたはクラスには、JavaScript 内のすべてのプロパティとメソッドがネイティブ型に追加されます。 さらに、次のセマンティクスが適用されます。
2 つのアンダースコア (__) で始まらない名前は、視覚化で使用できます。
標準の JavaScript オブジェクトの一部である名前、または JavaScript プロバイダーが作成するプロトコルの一部である名前は、視覚化には表示されません。
オブジェクトは、[Symbol.iterator] のサポートを介して反復可能にすることができます。
オブジェクトは、getDimensionality、getValueAt、および必要に応じて setValueAt という複数の関数で構成されるカスタム プロトコルをサポートすることで、インデックスを作成できます。
ネイティブ オブジェクト ブリッジと JavaScript オブジェクト ブリッジ
JavaScript とデバッガーのオブジェクト モデルの間のブリッジは双方向です。 ネイティブ オブジェクトは JavaScript に渡すことができます。JavaScript オブジェクトはデバッガーの式エバリュエーターに渡すことができます。 この例として、スクリプトに次のメソッドを追加することを検討してください。
function multiplyBySeven(val)
{
return val * 7;
}
このメソッドは、上記の LINQ クエリの例で使用できるようになりました。 まず、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
次に示すように、multiplyBySeven 関数をインラインで使用できます。
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
JavaScript を使用した条件付きブレークポイント
ブレークポイントにヒットした後、JavaScript を使用して補足処理を実行できます。 たとえば、スクリプトを使用して他の実行時の値を調べ、コードの実行を自動的に続行するか、停止して追加の手動デバッグを実行するかを決定できます。
ブレークポイントの操作に関する一般的な情報については ブレークポイントを制御する方法 を参照してください。
ブレークポイント処理スクリプトDebugHandler.js例
この例では、メモ帳の開いて保存するダイアログを評価します: notepad!ShowOpenSaveDialog。 このスクリプトは、pszCaption 変数を評価して、現在のダイアログが "開く" ダイアログであるか、または "名前を付けて保存" ダイアログかを判断します。 開いているダイアログの場合は、コードの実行が続行されます。 [名前を付けて保存] ダイアログの場合、コードの実行が停止し、デバッガーが中断します。
// 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");
}
}
このコマンドは、メモ帳にブレークポイントを設定します。ShowOpenSaveDialog。ブレークポイントにヒットするたびに上記のスクリプトが実行されます。
bp notepad!ShowOpenSaveDialog ".scriptrun C:\\WinDbg\\Scripts\\DebugHandler.js"
次に、メモ帳で [ファイル > の保存] オプションを選択すると、スクリプトが実行され、g コマンドが送信されず、コードの実行が中断されます。
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
JavaScript 拡張機能で 64 ビット値を操作する
このセクションでは、JavaScript デバッガー拡張機能に渡される 64 ビット値の動作について説明します。 この問題は、JavaScript には 53 ビットを使用して数値を格納する機能しかない場合に発生します。
64 ビットおよび JavaScript 53 ビット ストレージ
JavaScript に渡される序数の値は、通常、JavaScript 番号としてマーシャリングされます。 この問題は、JavaScript の数値が 64 ビット倍精度浮動小数点値であるという点です。 53 ビットを超える序数では、JavaScript への入力の精度が失われます。 これにより、64 ビット ポインターとその他の 64 ビットの序数値に関する問題が発生します。この値には、最大バイト数のフラグが含まれている可能性があります。 これに対処するために、JavaScript を入力する 64 ビットのネイティブ値 (ネイティブ コードまたはデータ モデル) は、JavaScript 番号ではなくライブラリ型として入力されます。 このライブラリの種類は、数値の精度を失うことなくネイティブ コードにラウンド トリップします。
自動変換
64 ビットの序数値のライブラリ型では、標準の JavaScript valueOf 変換がサポートされています。 値の変換を必要とする算術演算またはその他のコンストラクトでオブジェクトが使用されている場合は、自動的に JavaScript 番号に変換されます。 精度の損失が発生した場合 (値が 53 ビットを超える序数精度を利用する場合)、JavaScript プロバイダーは例外をスローします。
JavaScript でビット演算子を使用する場合は、さらに 32 ビットの序数精度に制限されることに注意してください。
このサンプル コードでは、2 つの数値を合計し、64 ビット値の変換をテストするために使用します。
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);
}
メモ帳などのテキスト エディターを使用して PlayWith64BitValues.js という名前のテキスト ファイルを作成する
.scriptload コマンドを使用してスクリプトを読み込みます。
0:000> .scriptload c:\WinDbg\Scripts\PlayWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\PlayWith64BitValues.js'
スクリプトを操作しやすくするには、dx コマンドを使用して、スクリプトの内容を保持する変数をデバッガーに割り当てます。
0:000> dx @$myScript = Debugger.State.Scripts.PlayWith64BitValues.Contents
dx 式エバリュエーターを使用して addTwoValues 関数を呼び出します。
最初に、2^53 =9007199254740992 (16 進0x20000000000000) の値を計算します。
まず、(2^53) - 2 を使用して、合計の正しい値が返されることを確認します。
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740990)
Sum >> 18014398509481980
次に、(2^53) -1 =9007199254740991 を計算します。 これにより、変換プロセスの精度が失われることを示すエラーが返されるため、これは JavaScript コードの sum メソッドで使用できる最大値です。
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number
64 ビット値を渡すデータ モデル メソッドを呼び出します。 ここでは精度の損失はありません。
0:001> dx @$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, (x, y) => x + y)
@$myScript.performOp64BitValues( 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, (x, y) => x + y) : 0xfffffffffffffffe
比較
64 ビット ライブラリ型は JavaScript オブジェクトであり、JavaScript 番号などの値型ではありません。 これは、比較操作にいくつかの影響を与えます。 通常、オブジェクトの等値 (==) は、オペランドが同じ値ではなく同じオブジェクトを参照していることを示します。 JavaScript プロバイダーは、64 ビット値へのライブ参照を追跡し、収集されていない 64 ビット値に対して同じ "不変" オブジェクトを返すことで、これを軽減します。 これは、比較のために、次のことが発生することを意味します。
// 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");
メモ帳などのテキスト エディターを使用して ComparisonWith64BitValues.js という名前のテキスト ファイルを作成する
.scriptload コマンドを使用してスクリプトを読み込みます。
0:000> .scriptload c:\WinDbg\Scripts\ComparisonWith64BitValues.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\ComparisonWith64BitValues.js'
スクリプトを操作しやすくするには、dx コマンドを使用して、スクリプトの内容を保持する変数をデバッガーに割り当てます。
0:000> dx @$myScript = Debugger.State.Scripts.comparisonWith64BitValues.Contents
最初にテストするために、(2^53) - 2 を使用し、期待される値が返されることを確認します。
0:001> dx @$myScript.comparisonWith64BitValues(9007199254740990, 9007199254740990)
areEqual >> true
areNotEqual >> false
isEqualTo42 >> false
isLess >> false
また、比較演算子が必要に応じて動作していることを検証するために、最初の値として数値 42 を試します。
0:001> dx @$myScript.comparisonWith64BitValues(42, 9007199254740990)
areEqual >> false
areNotEqual >> true
isEqualTo42 >> true
isLess >> true
次に、(2^53) -1 =9007199254740991 を計算します。 この値は、変換プロセスで精度が失われることを示すエラーを返すため、これは JavaScript コードの比較演算子で使用できる最大値です。
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740991)
Error: 64 bit value loses precision on conversion to number
操作の精度の維持
デバッガー拡張機能で有効桁数をメインできるようにするために、64 ビット ライブラリ型の上に一連の数学関数が投影されます。 拡張で 64 ビットの受信値に対して 53 ビットを超える精度が必要な場合 (または可能な場合)、標準演算子に依存するのではなく、次のメソッドを使用する必要があります。
メソッド名 | Signature | 説明 |
---|---|---|
asNumber | .asNumber() | 64 ビット値を JavaScript 番号に変換します。 有効桁数の損失が発生した場合、**例外がスローされます** |
convertToNumber | .convertToNumber() | 64 ビット値を JavaScript 番号に変換します。 精度の損失が発生した場合、**例外はスローされません** |
getLowPart | .getLowPart() | 64 ビット値の下位 32 ビットを JavaScript 番号に変換します |
getHighPart | .getHighPart() | 64 ビット値の上位 32 ビットを JavaScript 数値に変換します。 |
add | .add(価値) | 64 ビット値に値を追加し、結果を返します |
subtract | .subtract(価値) | 64 ビット値から値を減算し、結果を返します。 |
multiply | .multiply(価値) | 64 ビット値に指定された値を乗算し、結果を返します。 |
divide | .divide(価値) | 64 ビット値を指定された値で除算し、結果を返します。 |
bitwiseAnd | .bitwiseAnd(価値) | 指定された値でビットごとの 64 ビット値を計算し、結果を返します。 |
bitwiseOr | .bitwiseOr(価値) | 指定された値を使用して 64 ビット値のビット単位の論理和を計算し、結果を返します。 |
bitwiseXor | .bitwiseXor(価値) | 64 ビット値と指定された値のビットごとの XOR を計算し、結果を返します。 |
bitwiseShiftLeft | .bitwiseShiftLeft(価値) | 指定した量だけ 64 ビット値をシフトし、結果を返します。 |
bitwiseShiftRight | .bitwiseShiftRight(value) | 64 ビット値を指定された量だけ右にシフトし、結果を返します。 |
toString | .toString([基数]) | 64 ビット値を既定の基数 (または必要に応じて指定された基数) の表示文字列に変換します。 |
この方法も利用可能です。
メソッド名 | Signature | 説明 |
---|---|---|
compareTo | .compareTo(価値) | 64 ビット値を別の 64 ビット値と比較します。 |
JavaScript のデバッグ
このセクションでは、デバッガーのスクリプト デバッグ機能を使用する方法について説明します。 デバッガーには .scriptdebug (JavaScript のデバッグ) コマンドを使用した JavaScript スクリプトのデバッグのサポートが統合されています。
Note
WinDbg で JavaScript デバッグを使用するには、管理者としてデバッガーを実行します。
JavaScript のデバッグを調べるには、このサンプル コードを使用します。 このチュートリアルでは、DebuggableSample.js名前を付け、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
//
}
サンプルスクリプトをロードします。
.scriptload C:\MyScripts\DebuggableSample.js
.scriptdebug コマンドを使用して、スクリプトのアクティブなデバッグを開始します。
0:000> .scriptdebug C:\MyScripts\DebuggableSample.js
>>> ****** DEBUGGER ENTRY DebuggableSample ******
No active debug event!
>>> Debug [DebuggableSample <No Position>] >
プロンプトが表示されたら >>> Debug [DebuggableSample <No Position>] >
と入力要求があれば、スクリプト・デバッガーの中にいることになる。
.help コマンドを使用して、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
sx スクリプト デバッガー コマンドを使用して、トラップできるイベントのリストを表示します。
>>> 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
sxe スクリプト デバッガ コマンドを使用してエントリでブレークをオンにし、スクリプト内のコードが実行されるとすぐにスクリプトがスクリプト デバッガにトラップされるようにします。
>>> Debug [DebuggableSample <No Position>] >sxe en
sxe en
Event filter 'en' is now active
スクリプト デバッガーを終了し、デバッガーにトラップするスクリプトに関数呼び出しを行います。
>>> Debug [DebuggableSample <No Position>] >q
この時点で、通常のデバッガーに戻ります。 次のコマンドを実行してスクリプトを呼び出します。
dx @$scriptContents.outermost()
これで、スクリプト デバッガーに戻り、最も外側の JavaScript 関数の最初の行に分割しました。
>>> ****** SCRIPT BREAK DebuggableSample [BreakIn] ******
Location: line = 73, column = 5
Text: var x = 99
>>> Debug [DebuggableSample 73:5] >
デバッガーでブレークを確認するだけでなく、ブレークが発生した行 (73) と列 (5) に関する情報、およびソース コードの関連するスニペットも取得できます: var x = 99。
数回ステップを実行して、スクリプト内の別の場所に移動してみましょう。
p
t
p
t
p
p
この時点で、34 行目の throwAndCatch メソッドに分割する必要があります。
...
>>> ****** SCRIPT BREAK DebuggableSample [Step Complete] ******
Location: line = 34, column = 5
Text: var curProc = host.currentProcess
これを確認するには、スタック トレースを実行します。
>>> 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())
ここから、変数の値を調査できます。
>>> 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
現在のコード行にブレークポイントを設定し、現在設定されているブレークポイントを確認しましょう。
>>> 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
ここから sxd スクリプト デバッガー コマンドを使用してエントリ (en) イベントを無効にします。
>>> Debug [DebuggableSample 34:5] >sxd en
sxd en
Event filter 'en' is now inactive
次に、スクリプトを最後まで続けます。
>>> Debug [DebuggableSample 34:5] >g
g
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test
...
スクリプト メソッドをもう一度実行し、ブレークポイントにヒットするのを確認します。
0:000> dx @$scriptContents.outermost()
inside outer!
>>> ****** SCRIPT BREAK DebuggableSample [Breakpoint 1] ******
Location: line = 34, column = 5
Text: var curProc = host.currentProcess
コールスタックを表示します。
>>> 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())
この時点で、このスクリプトのデバッグを停止するため、デタッチします。
>>> Debug [DebuggableSample 34:5] >.detach
.detach
Debugger has been detached from script!
次に、「q」と入力して終了します。
q
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test
関数をもう一度実行してもデバッガーが中断されなくなりました。
0:007> dx @$scriptContents.outermost()
inside outer!
This is a fun test
Of the script debugger
foo.a = 99
Caught and returned!
Test
VSCode の JavaScript - IntelliSense の追加
VSCode でデバッガー データ モデル オブジェクトを操作する場合は、Windows 開発キットで使用できる定義ファイルを使用できます。 IntelliSense 定義ファイルは、すべての host.* デバッガー オブジェクト API をサポートします。 キットを 64 ビット PC の既定のディレクトリにインストールした場合は、次の場所にあります。
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\JsProvider.d.ts
VSCode で IntelliSense 定義ファイルを使用するには:
定義ファイルを見つける - JSProvider.d.ts
定義ファイルをスクリプトと同じフォルダーにコピーします。
JavaScript スクリプト ファイルの先頭に追加
/// <reference path="JSProvider.d.ts" />
します。
この参照を JavaScript ファイルで使用すると、VS Code によって、スクリプト内の構造に加えて JSProvider によって提供されるホスト API で IntelliSense が自動的に提供されます。 たとえば、「ホスト」と入力します。 使用可能なすべてのデバッガー モデル API の IntelliSense が表示されます。
JavaScript リソース
JavaScript デバッグ拡張機能の開発に役立つ可能性がある JavaScript リソースを次に示します。