Depurador de script de JavaScript

En este tema se describe cómo usar JavaScript para crear scripts que comprendan los objetos del depurador y amplíen y personalicen las capacidades del depurador.

Información general sobre el scripting del depurador de JavaScript

Los proveedores de scripts puentean un lenguaje de scripting al modelo de objetos interno del depurador. El proveedor de scripting del depurador de JavaScript permite el uso de JavaScript con el depurador.

Cuando se carga un JavaScript a través del comando .scriptload, se ejecuta el código raíz del script, los nombres presentes en el script se puentean al espacio de nombres raíz del depurador (depurador dx) y el script permanece residente en memoria hasta que se descarga y se liberan todas las referencias a sus objetos. El script puede proporcionar nuevas funciones al evaluador de expresiones del depurador, modificar el modelo de objetos del depurador, o puede actuar como un visualizador de forma muy similar a como lo hace un visualizador NatVis.

En este tema se describen algunas de las acciones que puede hacer con el scripting del depurador de JavaScript.

Estos dos temas proporcionan información adicional sobre cómo trabajar con JavaScript en el depurador.

Scripts de ejemplo del depurador de JavaScript

Objetos nativos en extensiones de JavaScript

Vídeo de scripting de JavaScript

Herramientas de desfragmentación #170: Andy y Bill demuestran la extensibilidad de JavaScript y las capacidades de scripting en el depurador.

Proveedor de JavaScript del depurador

El proveedor de JavaScript incluido con el depurador aprovecha al máximo las últimas mejoras de objetos y clases de ECMAScript6. Para obtener más información, consulte ECMAScript 6 — Nuevas características: Información general y comparación.

JsProvider.dll

JsProvider.dll es el proveedor de JavaScript que se carga para admitir el scripting del depurador de JavaScript.

Requisitos

El scripting de depurador de JavaScript está diseñado para funcionar con todas las versiones compatibles de Windows.

Carga del proveedor de scripting de JavaScript

Antes de utilizar cualquiera de los comandos .script, es necesario cargar un proveedor de scripts. Utilice el comando .scriptproviders para confirmar que el proveedor de JavaScript está cargado.

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

Comandos meta de scripting de JavaScript

Los siguientes comandos están disponibles para trabajar con scripting del depurador de JavaScript.

Requisitos

Antes de utilizar cualquiera de los comandos .script, es necesario cargar un proveedor de scripts. Utilice el comando .scriptproviders para confirmar que el proveedor de JavaScript está cargado.

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

.scriptproviders (lista de proveedores de scripts)

El comando .scriptproviders enumerará todos los lenguajes de script que actualmente entiende el depurador y la extensión bajo la que están registrados.

En el ejemplo siguiente, se cargan los proveedores JavaScript y NatVis.

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

Cualquier archivo que termine en ".NatVis" se entiende como un script NatVis y cualquier archivo que termine en ".js" se entiende como un script JavaScript. Cualquier tipo de script puede cargarse con el comando .scriptload.

Para obtener más información, consulte .scriptproviders (Enumerar proveedores de scripts)

.scriptload (cargar script)

El comando .scriptload cargará un script y ejecutará el código raíz de un script y la función initializeScript. Si hay algún error en la carga inicial y la ejecución del script, se mostrarán los errores en la consola. El siguiente comando muestra la carga correcta de TestScript.js.

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

Cualquier manipulación del modelo de objeto realizada por el script permanecerá en su lugar hasta que el script se descargue posteriormente o se ejecute de nuevo con un contenido diferente.

Para obtener más información, consulte .scriptload (Cargar script)

.scriptrun

El comando .scriptrun cargará un script, ejecutará el código raíz del script, la función initializeScript e invokeScript. Si hay algún error en la carga inicial y la ejecución del script, se mostrarán los errores en la consola.

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

Cualquier manipulación del modelo de objeto del depurador realizada por el script permanecerá en su lugar hasta que el script se descargue posteriormente o se ejecute de nuevo con un contenido diferente.

Para obtener más información, consulte .scriptrun (Ejecutar script).

.scriptunload (Descargar script)

El comando .scriptunload descarga un script cargado y llama a la función uninitializeScript. Use la siguiente sintaxis de comando para descargar un script.

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

Para obtener más información, consulte .scriptload (Descargar script).

.scriptlist (Enumerar scripts cargados)

El comando .scriptlist enumerará todos los scripts que se hayan cargado mediante el comando .scriptload o .scriptrun. Si el TestScript se ha cargado correctamente utilizando .scriptload, el comando .scriptlist mostrará el nombre del script cargado.

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

Para obtener más información, consulte .scriptlist (Enumerar scripts cargados).

Introducción al scripting del depurador de JavaScript

Script de ejemplo HelloWorld

En esta sección se describe cómo crear y ejecutar un script sencillo del depurador de JavaScript que imprime Hola mundo.

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

Use un editor de texto como el Bloc de notas para crear un archivo de texto denominado HelloWorld.js que contenga el código JavaScript mostrado anteriormente.

Use el comando .scriptload para cargar y ejecutar el script. Dado que usamos el nombre de la función initializeScript, el código de la función se ejecuta cuando se carga el script.

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

Una vez cargado el script, la funcionalidad adicional está disponible en el depurador. Use el comando dx (Display NatVis Expression) para mostrar Debugger.State.Scripts para ver que nuestro script ahora es residente.

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

En el siguiente ejemplo, agregaremos y llamaremos a una función con nombre.

Adición de un script de ejemplo de dos valores

En esta sección se describe cómo crear y ejecutar un script simple del depurador de JavaScript que agrega entradas y dos números.

Este script simple proporciona una sola función, addTwoValues.

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

Use un editor de texto, como el Bloc de notas, para crear un archivo de texto denominado FirstSampleFunction.js

Use el comando .scriptload para cargar el script.

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

Una vez cargado el script, la funcionalidad adicional está disponible en el depurador. Use el comando dx (Display NatVis Expression) para mostrar Debugger.State.Scripts para ver que nuestro script ahora es residente.

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

Podemos seleccionar FirstSampleFunction para ver qué funciones proporciona.

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

Para que el script sea un poco más cómodo para trabajar, asigne una variable en el depurador para retener el contenido del script mediante el comando dx.

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

Use el evaluador de expresiones dx para llamar a la función addTwoValues.

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

También puede usar el alias integrado $@scriptContents para trabajar con los scripts. El alias $@scriptContents combina todos los elementos .Content de todos los scripts que se cargan.

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

Cuando termine de trabajar con el script, use el comando .scriptunload para descargar el script.

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

Automatización de comandos del depurador

En esta sección se describe cómo crear y ejecutar un script simple del depurador de JavaScript que automatiza el envío del comando u (Unassemble). En el ejemplo también se muestra cómo recopilar y mostrar la salida del comando en un bucle.

Este script proporciona una sola función, 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");

}

Use un editor de texto, como el Bloc de notas, para crear un archivo de texto denominado RunCommands.js

Use el comando .scriptload para cargar el script RunCommands.

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

Una vez cargado el script, la funcionalidad adicional está disponible en el depurador. Use el comando dx (Display NatVis Expression) para mostrar Debugger.State.Scripts.RunCommands y ver que nuestro script ahora es residente.

0:000>dx -r3 Debugger.State.Scripts.RunCommands
Debugger.State.Scripts.RunCommands                
    Contents         : [object Object]
        host             : [object Object]
            diagnostics      : [object Object]
            namespace       
            currentSession   : Live user mode: <Local>
            currentProcess   : notepad.exe
            currentThread    : ntdll!DbgUiRemoteBreakin (00007ffd`87f2f440) 
            memory           : [object Object]

Use el comando dx para llamar a la función RunCommands en el script RunCommands.

0:000> dx Debugger.State.Scripts.RunCommands.Contents.RunCommands()
  ***> Displaying command output
  ntdll!ExpInterlockedPopEntrySListEnd+0x17 [d:\rs1\minkernel\ntos\rtl\amd64\slist.asm @ 196]:
  00007ffd`87f06e67 cc              int     3
  00007ffd`87f06e68 cc              int     3
  00007ffd`87f06e69 0f1f8000000000  nop     dword ptr [rax]
  ntdll!RtlpInterlockedPushEntrySList [d:\rs1\minkernel\ntos\rtl\amd64\slist.asm @ 229]:
  00007ffd`87f06e70 0f0d09          prefetchw [rcx]
  00007ffd`87f06e73 53              push    rbx
  00007ffd`87f06e74 4c8bd1          mov     r10,rcx
  00007ffd`87f06e77 488bca          mov     rcx,rdx
  00007ffd`87f06e7a 4c8bda          mov     r11,rdx
***> Exiting RunCommands Function

Funciones especiales del depurador de JavaScript

Existen varias funciones especiales en un script JavaScript llamadas por el propio proveedor del script.

initializeScript

Cuando un script JavaScript se carga y se ejecuta, pasa por una serie de pasos antes de que las variables, funciones y otros objetos del script afecten al modelo de objetos del depurador.

  • El script se carga en la memoria y se analiza.
  • Se ejecuta el código raíz del script.
  • Si el script tiene un método denominado initializeScript, se llama a ese método.
  • El valor devuelto de initializeScript se usa para determinar cómo modificar automáticamente el modelo de objetos del depurador.
  • Los nombres del script se puentean al espacio de nombres del depurador.

Como se ha mencionado, se llamará a initializeScript inmediatamente después de ejecutar el código raíz del script. Su trabajo es devolver una matriz de JavaScript de objetos de registro al proveedor que indica cómo modificar el modelo de objetos del depurador.

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

El método invokeScript es el método principal de script y se llama cuando se ejecutan .scriptload y .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

El método uninitializeScript es el comportamiento opuesto a initializeScript. Se llama cuando un script está desvinculado y está listo para descargarse. Su trabajo consiste en deshacer los cambios en el modelo de objetos que el script realizó de forma imperativa durante la ejecución o destruir los objetos almacenados en la caché del script.

Si un script no realiza manipulaciones imperativas en el modelo de objetos ni almacena en la caché los resultados, no necesita tener un método uninitializeScript. El proveedor deshace automáticamente los cambios realizados en el modelo de objetos tal como se indica en el valor devuelto de initializeScript. Estos cambios no requieren un método uninitializeScript explícito.

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

Resumen de funciones llamadas por comandos de script

En esta tabla se resumen las funciones a las que llaman los comandos de script.

Get-Help .scriptload .scriptrun (Ejecutar script) .scriptunload (Descargar script)
root
initializeScript
invokeScript
uninitializeScript

Use este código de ejemplo para ver cuándo se llama a cada función a medida que se carga, ejecuta y descarga el script.

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

Creación de un visualizador de depurador en JavaScript

Los archivos de visualización personalizados permiten agrupar y organizar datos en una estructura de visualización que refleje mejor las relaciones de datos y el contenido. Puede usar las extensiones del depurador de JavaScript para escribir visualizadores del depurador que actúan de forma muy similar a NatVis. Esto se logra mediante la creación de un objeto prototipo de JavaScript (o una clase ES6) que actúa como visualizador para un tipo de datos determinado. Para obtener más información sobre NatVis y el depurador, consulte dx (Display NatVis Expression).

Clase de ejemplo: Simple1DArray

Consideremos un ejemplo de una clase C++ que representa una matriz unidimensional. Esta clase tiene dos miembros, m_size que es el tamaño total del array, y m_pValues que es un puntero a un número de ints en memoria igual al campo m_size.

class Simple1DArray
{
private:

    ULONG64 m_size;
    int *m_pValues;
};

Podemos usar el comando dx para ver la representación predeterminada de la estructura de datos.

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

Visualizador de JavaScript

Para visualizar este tipo, es necesario crear una clase prototipo (o ES6) que tenga todos los campos y propiedades que queremos que el depurador muestre. También es necesario que el método initializeScript devuelva un objeto que indique al proveedor de JavaScript que vincule nuestro prototipo como visualizador para el tipo especificado.

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

Guarde el script en un archivo denominado arrayVisualizer.js.

Use el comando .load (Cargar DLL de extensión) para cargar el proveedor de JavaScript.

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

Use .scriptload para cargar el script del visualizador de matriz.

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

Ahora, cuando se usa el comando dx, el visualizador de scripts mostrará filas de contenido de matriz.

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

Además, esta visualización de JavaScript proporciona funcionalidad LINQ, como 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

Qué afecta a la visualización

Un prototipo o clase que se convierte en el visualizador de un tipo nativo a través de una devolución de un objeto host.typeSignatureRegistration de initializeScript tendrá todas las propiedades y métodos de JavaScript agregados al tipo nativo. Además, se aplica la siguiente semántica:

  • Cualquier nombre que no comience con dos caracteres de subrayado (__) estará disponible en la visualización.

  • Los nombres que forman parte de objetos JavaScript estándar o forman parte de los protocolos que crea el proveedor de JavaScript no aparecerán en la visualización.

  • Un objeto puede hacerse iterable mediante [Symbol.iterator].

  • Un objeto se puede indexar a través de la compatibilidad de un protocolo personalizado que consta de varias funciones: getDimensionality, getValueAt y, opcionalmente, setValueAt.

Puente de objetos nativos y JavaScript

El puente entre JavaScript y el modelo de objetos del depurador es bidireccional. Los objetos nativos se pueden pasar a JavaScript y los objetos JavaScript se pueden pasar al evaluador de expresiones del depurador. Como ejemplo de esto, consideremos la adición del siguiente método en nuestro script:

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

Este método ahora se puede usar en la consulta LINQ de ejemplo anterior. En primer lugar, cargamos la visualización de 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

A continuación, podemos usar la función multiplyBySeven insertada como se muestra a continuación.

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

Puntos de interrupción condicionales con JavaScript

Puede usar JavaScript para realizar el procesamiento complementario después de que se alcance un punto de interrupción. Por ejemplo, el script se puede usar para examinar otros valores de tiempo de ejecución y, a continuación, determinar si desea continuar automáticamente la ejecución del código o detener y realizar una depuración manual adicional.

Para obtener información general sobre cómo trabajar con puntos de interrupción, consulte Métodos de control de puntos de interrupción.

Script de procesamiento de puntos de interrupción de ejemplo DebugHandler.js

En este ejemplo se evaluará el cuadro de diálogo abierto y guardado del Bloc de notas: notepad!ShowOpenSaveDialog. Este script evaluará la variable pszCaption para determinar si el cuadro de diálogo actual es un diálogo "Abrir" o es un cuadro de diálogo "Guardar como". Si se trata de un cuadro de diálogo Abrir, la ejecución del código continuará. Si se trata de un cuadro de diálogo Guardar, la ejecución del código se detendrá y el depurador se interrumpirá.

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

Este comando establece un punto de interrupción en notepad!ShowOpenSaveDialog, y ejecutará el script anterior cada vez que se alcance ese punto de interrupción.

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

A continuación, cuando se selecciona la opción Archivo > Guardar en el Bloc de notas, se ejecuta el script, no se envía el comando g y se produce un salto en la ejecución del código.

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

Trabajar con valores de 64 bits en extensiones de JavaScript

En esta sección se describe cómo se comportan los valores de 64 bits pasados a una extensión del depurador de JavaScript. Este problema se produce ya que JavaScript solo tiene la capacidad de almacenar números con 53 bits.

Almacenamiento de 64 bits y JavaScript de 53 bits

Los valores ordinales pasados a JavaScript normalmente se calculan como números de JavaScript. El problema es que los números de JavaScript son valores de punto flotante de precisión doble de 64 bits. Cualquier ordinal de más de 53 bits perdería precisión al entrar en JavaScript. Esto presenta un problema para punteros de 64 bits y otros valores ordinales de 64 bits que pueden tener marcas en los bytes más altos. Para hacer frente a esto, cualquier valor nativo de 64 bits (ya sea de código nativo o del modelo de datos) que entra en JavaScript, entra como un tipo de biblioteca, no como un número de JavaScript. Este tipo de biblioteca volverá a recorrer el código nativo sin perder precisión numérica.

Conversión automática

El tipo de biblioteca para los valores ordinales de 64 bits admite la conversión de JavaScript estándar. Si el objeto se usa en una operación matemática u otra construcción que requiere la conversión de valores, se convertirá automáticamente en un número de JavaScript. Si se produjera una pérdida de precisión (el valor utiliza más de 53 bits de precisión ordinal), el proveedor de JavaScript lanzará una excepción.

Tenga en cuenta que si utiliza operadores bit a bit en JavaScript, estará limitado a 32 bits de precisión ordinal.

Este código de ejemplo suma dos números y se usará para probar la conversión de valores de 64 bits.

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

Use un editor de texto, como el Bloc de notas, para crear un archivo de texto denominado PlayWith64BitValues.js

Use el comando .scriptload para cargar el script.

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

Para que el script sea un poco más cómodo para trabajar, asigne una variable en el depurador para retener el contenido del script mediante el comando dx.

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

Use el evaluador de expresiones dx para llamar a la función addTwoValues.

Primero, calcularemos el valor de 2^53 =9007199254740992 (hexadecimal 0x20000000000000).

Usaremos (2^53) - 2 y veremos que devuelve el valor correcto para la suma.

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

A continuación, calcularemos (2^53) -1 =9007199254740991. Esto devuelve el error que indica que el proceso de conversión perderá precisión, por lo que este es el valor más grande que se puede usar con el método sum en código JavaScript.

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

Llame a un método de modelo de datos que pase valores de 64 bits. No hay pérdida de precisión aquí.

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

De comparación

El tipo de biblioteca de 64 bits es un objeto JavaScript y no un tipo de valor como un número de JavaScript. Esto tiene algunas implicaciones para las operaciones de comparación. Normalmente, la igualdad (==) en un objeto indicaría que los operandos se refieren al mismo objeto y no al mismo valor. El proveedor de JavaScript lo mitiga realizando el seguimiento de las referencias dinámicas a valores de 64 bits y devolviendo el mismo objeto "inmutable" para el valor de 64 bits no recopilado. Esto significa que, a efectos comparativos, ocurriría lo siguiente.

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

Use un editor de texto, como el Bloc de notas, para crear un archivo de texto denominado ComparisonWith64BitValues.js

Use el comando .scriptload para cargar el script.

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

Para que el script sea un poco más cómodo para trabajar, asigne una variable en el depurador para retener el contenido del script mediante el comando dx.

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

Usaremos (2^53) - 2 y veremos que devuelve los valores esperados.

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

También probaremos el número 42 como primer valor para validar que el operador de comparación funciona como debería.

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

A continuación, calcularemos (2^53) -1 =9007199254740991. Este valor devuelve el error que indica que el proceso de conversión perderá precisión, por lo que este es el valor más grande que se puede usar con los operadores de comparación en código JavaScript.

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

Mantener la precisión en las operaciones

Para permitir que una extensión del depurador mantenga la precisión, se proyecta un conjunto de funciones matemáticas sobre el tipo de biblioteca de 64 bits. Si la extensión necesita (o puede necesitar) una precisión superior a 53 bits para valores entrantes de 64 bits, deben usarse los siguientes métodos en lugar de confiar en los operadores estándar:

Nombre de método Signature Descripción
asNumber .asNumber() Convierte el valor de 64 bits en un número de JavaScript. Si se produce una pérdida de precisión, **SE GENERA UNA EXCEPCIÓN**
convertToNumber .convertToNumber() Convierte el valor de 64 bits en un número de JavaScript. Si se produce una pérdida de precisión, **NO SE GENERA UNA EXCEPCIÓN**
getLowPart .getLowPart() Convierte los 32 bits inferiores del valor de 64 bits en un número de JavaScript.
getHighPart .getHighPart() Convierte los 32 bits altos del valor de 64 bits en un número JavaScript
add .add(value) Agrega un valor al valor de 64 bits y devuelve el resultado.
restar .subtract(value) Resta un valor del valor de 64 bits y devuelve el resultado.
multiply .multiply(value) Multiplica el valor de 64 bits por el valor proporcionado y devuelve el resultado.
divide .divide(value) Divide el valor de 64 bits por el valor proporcionado y devuelve el resultado.
bitwiseAnd .bitwiseAnd(value) Calcula el bit a bit and del valor de 64 bits con el valor proporcionado y devuelve el resultado.
bitwiseOr .bitwiseOr(value) Calcula el bit a bit or del valor de 64 bits con el valor proporcionado y devuelve el resultado.
bitwiseXor .bitwiseXor(value) Calcula el bit a bit xor del valor de 64 bits con el valor proporcionado y devuelve el resultado.
bitwiseShiftLeft .bitwiseShiftLeft(value) Desplaza el valor de 64 bits hacia la izquierda la cantidad especificada y devuelve el resultado.
bitwiseShiftRight .bitwiseShiftRight(value) Desplaza el valor de 64 bits hacia la derecha la cantidad especificada y devuelve el resultado.
toString .toString([radix]) Convierte el valor de 64 bits en una cadena de presentación en el radix predeterminado (o el radix proporcionado opcionalmente).

Este método también está disponible.

Nombre de método Signature Descripción
compareTo .compareTo(value) Compara el valor de 64 bits con otro valor de 64 bits.

Depuración de JavaScript

En esta sección se describe cómo usar las funcionalidades de depuración de scripts del depurador. El depurador tiene compatibilidad integrada con la depuración de scripts de JavaScript mediante el comando .scriptdebug (Depurar JavaScript).

Nota:

Para utilizar la depuración de JavaScript con WinDb, ejecute el depurador como administrador.

Use este código de ejemplo para explorar la depuración de javaScript. En este tutorial, se le asignará el nombre DebuggableSample.js y se guardará en el directorio 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
    //
}

Cargue el script de ejemplo.

.scriptload C:\MyScripts\DebuggableSample.js

Para iniciar la depuración activa del script, use el comando .scriptdebug.

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

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

Una vez que vea el aviso >>> Debug [DebuggableSample <No Position>] > y una solicitud de entrada, estará dentro del depurador de script.

Use el comando .help para visualizar una lista de comandos en el entorno de depuración de 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

Use el comando sx para ver la lista de eventos que podemos interceptar.

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

Use el comando del depurador de script sxe para activar la interrupción en la entrada de modo que el script se intercepte en el depurador de script en cuanto se ejecute cualquier código dentro de él, utilice este comando.

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

Salga del depurador de script y realizaremos una llamada de función al script que se interceptará en el depurador.

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

En este punto, está de vuelta en el depurador normal. Ejecute el comando siguiente para llamar al script.

dx @$scriptContents.outermost()

Ahora, está de vuelta en el depurador de scripts y se interrumpe en la primera línea de la función JavaScript más externa.

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

>>> Debug [DebuggableSample 73:5] >                         

Además de ver la interrupción en el depurador, obtendrá información sobre la línea (73) y la columna (5) donde tuvo lugar la interrupción, así como el fragmento de código fuente correspondiente: var x = 99.

Demos un paso más y lleguemos a otro lugar del script.

    p
    t
    p
    t
    p
    p

En este punto, se ha interrumpido en el método throwAndCatch en la línea 34.

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

Para comprobarlo, ejecute un seguimiento de la pila.

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

Desde aquí, puede investigar el valor de las variables.

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

Vamos a establecer un punto de interrupción en la línea de código actual y ver qué puntos de interrupción se establecen ahora.

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

Desde aquí, deshabilitaremos el evento de entrada (en) mediante el comando del depurador de script sxd.

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

Y luego solo tiene que dejar que el script continúe hasta el final.

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

Ejecute de nuevo el método de script y observe que se alcance el punto de interrupción.

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

Muestre la pila de llamadas.

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

En este punto, queremos detener la depuración de este script, por lo que nos desasociamos de él.

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

Y luego escriba q para salir.

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

La ejecución de la función de nuevo ya no se interrumpirá en el depurador.

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

JavaScript en VSCode: adición de IntelliSense

Si quiere trabajar con los objetos del modelo de datos del depurador en VSCode, puede usar un archivo de definición que está disponible en los kits de desarrollo de Windows. El archivo de definición de IntelliSense proporciona compatibilidad con todas las API de objeto host.* del depurador. Si instaló el kit en el directorio predeterminado en un equipo de 64 bits, reside aquí:

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

Para usar el archivo de definición de IntelliSense en VSCode:

  1. Busque el archivo de definición: JSProvider.d.ts

  2. Copie el archivo de definición en la misma carpeta que el script.

  3. Agregue /// <reference path="JSProvider.d.ts" /> a la parte superior del archivo de script de JavaScript.

Con esa referencia en el archivo JavaScript, VS Code le proporcionará automáticamente IntelliSense en las API de host proporcionadas por JSProvider además de las estructuras en su script. Por ejemplo, escriba "host." y verá IntelliSense para todas las API del modelo de depurador disponibles.

Recursos de JavaScript

A continuación se indican recursos de JavaScript que pueden ser útiles para desarrollar extensiones de depuración de JavaScript.

Vea también

Scripts de ejemplo del depurador de JavaScript

Objetos nativos en extensiones de JavaScript