Скрипты отладчика JavaScript
В этом разделе описывается, как использовать JavaScript для создания скриптов, которые понимают объекты отладчика и расширяют и настраивают возможности отладчика.
Общие сведения о скриптах отладчика JavaScript
Поставщики скриптов мостите язык сценариев к внутренней объектной модели отладчика. Поставщик скриптов отладчика JavaScript позволяет использовать JavaScript с отладчиком.
При загрузке JavaScript с помощью команды .scriptload выполняется корневой код скрипта, имена, которые присутствуют в скрипте, мостятся в корневое пространство имен отладчика (dx Debugger) и скрипт остается в памяти, пока он не выгрузится и все ссылки на его объекты будут освобождены. Скрипт может предоставлять новые функции оценщику выражений отладчика, изменять объектную модель отладчика или действовать в качестве визуализатора так же, как визуализатор NatVis.
В этом разделе описано, что можно сделать с помощью скриптов отладчика JavaScript.
Эти два раздела содержат дополнительные сведения о работе с JavaScript в отладчике.
Примеры скриптов отладчика JavaScript
Собственные объекты в расширениях JavaScript
Видео сценариев JavaScript
Средства дефрагментации #170 — Энди и Билл демонстрируют расширяемость и возможности сценариев JavaScript в отладчике.
Поставщик JavaScript для отладчика
Поставщик JavaScript, включенный в отладчик, использует все преимущества последних улучшений объектов и классов ECMAScript6. Дополнительные сведения см. в разделе ECMAScript 6 — новые функции: обзор и сравнение.
JsProvider.dll
JsProvider.dll — это поставщик JavaScript, загруженный для поддержки скриптов отладчика JavaScript.
Требования
Скрипты отладчика JavaScript предназначены для работы со всеми поддерживаемыми версиями Windows.
Загрузка поставщика скриптов JavaScript
Перед использованием любой из команд скрипта необходимо загрузить поставщика сценариев. Используйте команду scriptproviders, чтобы убедиться, что поставщик JavaScript загружен.
0:000> .scriptproviders
Available Script Providers:
NatVis (extension '.NatVis')
JavaScript (extension '.js')
Метакоманды скриптов JavaScript
Следующие команды доступны для работы с скриптами отладчика JavaScript.
- .scriptproviders (поставщики скриптов списка)
- .scriptload (Load Script)
- .scriptunload (выгрузка скрипта)
- .scriptrun (запуск скрипта)
- .scriptlist (список загруженных скриптов)
Требования
Перед использованием любой из команд скрипта необходимо загрузить поставщика сценариев. Используйте команду 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 (Load Script)
Команда .scriptload загружает скрипт и выполняет корневой код скрипта и функцию initializeScript . Если в начальной загрузке и выполнении скрипта возникают ошибки, в консоли будут отображаться ошибки. Следующая команда показывает успешную загрузку TestScript.js.
0:000> .scriptload C:\WinDbg\Scripts\TestScript.js
JavaScript script successfully loaded from 'C:\WinDbg\Scripts\TestScript.js'
Любые манипуляции с объектной моделью, сделанные скриптом, будут оставаться на месте, пока скрипт не будет впоследствии выгружен или снова запущен с другим содержимым.
Дополнительные сведения см. в разделе .scriptload (Load Script)
.scriptrun
Команда .scriptrun загружает скрипт, выполняет корневой код скрипта, инициализациюScript и функцию 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 (Run Script).
.scriptunload (выгрузка скрипта)
Команда .scriptunload выгрузит загруженный скрипт и вызывает функцию uninitializeScript . Используйте следующий синтаксис команды для выгрузки скрипта
0:000:x86> .scriptunload C:\WinDbg\Scripts\TestScript.js
JavaScript script unloaded from 'C:\WinDbg\Scripts\TestScript.js'
Дополнительные сведения см. в разделе .scriptunload (Выгрузка скрипта).
.scriptlist (список загруженных скриптов)
Команда scriptlist выводит список всех скриптов, загруженных с помощью скриптов или команды .scriptrun. Если TestScript был успешно загружен с помощью .scriptload, команда .scriptlist будет отображать имя загруженного скрипта.
0:000> .scriptlist
Command Loaded Scripts:
JavaScript script from 'C:\WinDbg\Scripts\TestScript.js'
Дополнительные сведения см. в разделе .scriptlist (List Loaded Scripts).
Начало работы со скриптами отладчика JavaScript
Пример скрипта HelloWorld
В этом разделе описывается, как создать и выполнить простой скрипт отладчика JavaScript, который выводит приложение Hello World.
// WinDbg JavaScript sample
// Prints Hello World
function initializeScript()
{
host.diagnostics.debugLog("***> Hello World! \n");
}
Используйте текстовый редактор, например Блокнот, чтобы создать текстовый файл с именем HelloWorld.js, содержащий приведенный выше код JavaScript.
Используйте команду .scriptload для загрузки и выполнения скрипта. Так как мы использовали имя функции инициализироватьScript, код в функции выполняется при загрузке скрипта.
0:000> .scriptload c:\WinDbg\Scripts\HelloWorld.js
JavaScript script successfully loaded from 'c:\WinDbg\Scripts\HelloWorld.js'
***> Hello World!
После загрузки скрипта дополнительные функциональные возможности доступны в отладчике. Используйте команду dx (Display NatVis Expression) для отображения отладчика.State.Scripts, чтобы увидеть, что наш скрипт теперь является резидентом.
0:000> dx Debugger.State.Scripts
Debugger.State.Scripts
HelloWorld
В следующем примере мы добавим и вызовем именованную функцию.
Добавление двух значений примера скрипта
В этом разделе описывается создание и выполнение простого скрипта отладчика JavaScript, который добавляет входные данные и добавляет два числа.
Этот простой скрипт предоставляет одну функцию, addTwoValues.
// 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 (Display NatVis Expression) для отображения отладчика.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 объединяет все псевдонимы . Содержимое всех загруженных скриптов.
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'
Автоматизация команд отладчика
В этом разделе описывается создание и выполнение простого скрипта отладчика JavaScript, который автоматизирует отправку команды u (Unassemble). В примере также показано, как собирать и отображать выходные данные команды в цикле.
Этот скрипт предоставляет одну функцию 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) для отображения отладчика.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 используется для определения способа автоматического изменения объектной модели отладчика.
- Имена в скрипте мостятся к пространству имен отладчика.
Как упоминание, инициализацияScript будет вызываться сразу после выполнения корневого кода скрипта. Его задача — вернуть массив объектов регистрации 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 является основным методом скрипта и вызывается при выполнении скриптов и запуска .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 является поведением противоположности инициализацииScript. Он вызывается, когда скрипт не связан и готов к выгрузке. Его задача заключается в отмене любых изменений в объектной модели, которую скрипт сделал императивно во время выполнения и(или) уничтожить все объекты, кэшированные скриптом.
Если скрипт не выполняет императивные манипуляции с объектной моделью или кэширует результаты, он не должен иметь метод uninitializeScript. Любые изменения объектной модели, выполненные как указано возвращаемым значением инициализацииScript, автоматически удаляются поставщиком. Для таких изменений не требуется явный метод 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 | yes | yes | |
initializeScript | yes | yes | |
invokeScript | yes | ||
uninitializeScript | yes |
Используйте этот пример кода, чтобы узнать, когда каждая функция вызывается как скрипт загружается, выполняется и выгружается.
// 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 (Display NatVis Expression).
Пример класса — Simple1DArray
Рассмотрим пример класса C++, представляющего одинмерный массив. Этот класс имеет два члена, m_size который является общим размером массива, и m_pValues который является указателем на ряд инт в памяти, равных m_size полю.
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 (LOAD Extension 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, такие как 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
Что влияет на визуализацию
Прототип или класс, который делает визуализатор для собственного типа путем возврата объекта host.typeSignatureRegistration из initializeScript, будет содержать все свойства и методы в JavaScript, добавленные в собственный тип. Кроме того, применяются следующие семантики:
Любое имя, которое не начинается с двух символов подчеркивания (__), будет доступно в визуализации.
Имена, которые являются частью стандартных объектов JavaScript или являются частью протоколов, которые создает поставщик JavaScript, не будут отображаться в визуализации.
Объект можно сделать итерируемым с помощью поддержки [Symbol.iterator].
Объект можно индексировать с помощью поддержки пользовательского протокола, состоящего из нескольких функций: getDimensionality, getValueAt и при необходимости setValueAt.
Мост объектов Native и 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
Затем можно использовать встроенную функцию умноженияBySeven, как показано ниже.
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 пример скрипта обработки точек останова
В этом примере будет оцениваться диалоговое окно открытия и сохранения блокнота: блокнот! 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
Работа с 64-разрядными значениями в расширениях JavaScript
В этом разделе описывается, как 64-разрядные значения, передаваемые в расширение отладчика JavaScript, ведут себя. Эта проблема возникает, так как JavaScript имеет возможность хранить номера только с помощью 53-разрядных версий.
64-разрядная версия и JavaScript 53-разрядная служба хранилища
Порядковые значения, передаваемые в JavaScript, обычно маршалируются в виде чисел JavaScript. Проблема заключается в том, что числа JavaScript являются 64-разрядными значениями с плавающей запятой двойной точности. Любой порядковый номер более 53-разрядных разрядов потеряет точность при входе в JavaScript. Это представляет проблему для 64-разрядных указателей и других 64-разрядных порядковых значений, которые могут иметь флаги в самых высоких байтах. Чтобы справиться с этим, любое 64-разрядное собственное значение (будь то из машинного кода или модели данных), которое вводит JavaScript в качестве типа библиотеки, не как номер JavaScript. Этот тип библиотеки возвращается обратно в машинный код, не теряя числовую точность.
Автоматическое преобразование
Тип библиотеки для 64-разрядных порядковых значений поддерживает стандартное преобразование значений JavaScript. Если объект используется в математической операции или другой конструкции, для которой требуется преобразование значений, он автоматически преобразуется в номер JavaScript. Если произошла потеря точности (значение использует более 53 битов порядковой точности), поставщик JavaScript вызовет исключение.
Обратите внимание, что если в JavaScript используются побитовые операторы, вы также ограничиваетесь 32-разрядными порядковой точностью.
Этот пример кода суммирует два числа и будет использоваться для тестирования преобразования 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 (шестнадцатеричное 0x20000000000000).
Сначала для тестирования мы будем использовать (2^53) - 2 и видим, что он возвращает правильное значение для суммы.
0:000> dx @$myScript.playWith64BitValues(9007199254740990, 9007199254740990)
Sum >> 18014398509481980
Затем мы вычислим (2^53) -1 =9007199254740991. Это возвращает ошибку, указывающую, что процесс преобразования потеряет точность, поэтому это наибольшее значение, которое можно использовать с методом суммы в коде JavaScript.
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 битов), вместо использования стандартных операторов следует использовать следующие методы:
Имя метода | Подпись | Description |
---|---|---|
asNumber | .asNumber() | Преобразует 64-разрядное значение в номер JavaScript. Если возникает потеря точности, **ИСКЛЮЧЕНИЕ СОЗДАЕТСЯ** |
convertToNumber | .convertToNumber() | Преобразует 64-разрядное значение в номер JavaScript. Если возникает потеря точности, **НЕТ ИСКЛЮЧЕНИЯ СОЗДАЕТСЯ** |
getLowPart | .getLowPart() | Преобразует более низкие 32-разрядные значения 64-разрядного значения в число JavaScript |
getHighPart | .getHighPart() | Преобразует высокие 32-разрядные значения 64-разрядного значения в число JavaScript |
add | .add(value) | Добавляет значение в 64-разрядное значение и возвращает результат. |
вычитание | .subtract(value) | Вычитает значение из 64-разрядного значения и возвращает результат. |
multiply | .умножение(значение) | Умножает 64-разрядное значение на указанное значение и возвращает результат. |
divide | .divide(value) | Делит 64-разрядное значение на указанное значение и возвращает результат. |
bitwiseAnd | .bitwiseAnd(value) | Вычисляет битовое и 64-разрядное значение с заданным значением и возвращает результат. |
bitwiseOr | .bitwiseOr(value) | Вычисляет битовое или 64-разрядное значение с заданным значением и возвращает результат. |
bitwiseXor | BitwiseXor(value) | Вычисляет побитовый xor 64-разрядного значения с заданным значением и возвращает результат. |
bitwiseShiftLeftLeft | .bitwiseShiftLeftLeft(value) | Сдвигает 64-разрядное значение слева от заданной суммы и возвращает результат. |
bitwiseShiftRight | .bitwiseShiftRight(value) | Сдвигает 64-разрядное значение вправо по заданному объему и возвращает результат. |
toString | .toString([radix]) | Преобразует 64-разрядное значение в отображаемую строку в радиксе по умолчанию (или дополнительно предоставленном радиксе). |
Этот метод также доступен.
Имя метода | Подпись | Description |
---|---|---|
compareTo | .compareTo(value) | Сравнивает 64-разрядное значение с другим 64-разрядным значением. |
Отладка JavaScript
В этом разделе описывается, как использовать возможности отладки скрипта отладчика. Отладчик имеет встроенную поддержку отладки скриптов JavaScript с помощью команды .scriptdebug (Debug JavaScript).
Примечание.
Чтобы использовать отладку JavaScript с WinDbg, запустите отладчик как Администратор istrator.
Используйте этот пример кода для изучения отладки 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
На этом этапе необходимо разбить метод throwAndCatch в строке 34.
...
>>> ****** 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
Отсюда мы отключим событие записи (en) с помощью команды отладчика скрипта sxd .
>>> 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
JavaScript в VSCode— добавление IntelliSense
Если вы хотите работать с объектами модели данных отладчика в VSCode, можно использовать файл определения, доступный в комплектах средств разработки Windows. Файл определения IntelliSense обеспечивает поддержку всех API объектов отладчика host.* . Если вы установили комплект в каталоге по умолчанию на 64-разрядном компьютере, он находится здесь:
C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\winext\JsProvider.d.ts
Чтобы использовать файл определения IntelliSense в VSCode:
Поиск файла определения — JSProvider.d.ts
Скопируйте файл определения в ту же папку, что и сценарий.
Добавьте
/// <reference path="JSProvider.d.ts" />
в начало файла скрипта JavaScript.
С этой ссылкой в файле JavaScript VS Code автоматически предоставит IntelliSense в API узла, предоставляемых JSProvider, в дополнение к структурам в скрипте. Например, введите "host". и вы увидите IntelliSense для всех доступных API модели отладчика.
Ресурсы JavaScript
Ниже приведены ресурсы JavaScript, которые могут быть полезны при разработке расширений отладки JavaScript.