Compartir a través de


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 funcionalidades del depurador.

Información general sobre el scripting del depurador de JavaScript

Los proveedores de scripts conectan un lenguaje de secuencias de comandos 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 integran en el espacio de nombres raíz del depurador (dx Debugger) y el script permanece residente en la 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 actuar como visualizador de la misma manera que lo hace un visualizador NatVis.

En este tema se describen algunas de las acciones que se pueden realizar con el depurador de JavaScript.

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

Ejemplos de scripts del depurador de JavaScript

Objetos nativos en extensiones de JavaScript

Vídeo de scripting de JavaScript

Herramientas de desfragmentación #170 : Andy y Bill muestran las capacidades de extensibilidad y scripting de JavaScript 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 ECMAScript6. Para obtener más información, vea 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 secuencias de comandos de JavaScript

Antes de usar cualquiera de los comandos .script, es necesario cargar un proveedor de scripting. Use el comando .scriptproviders para confirmar que se ha cargado el proveedor de JavaScript.

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 usar cualquiera de los comandos .script, es necesario cargar un proveedor de scripting. Use el comando .scriptproviders para confirmar que se ha cargado el proveedor de JavaScript.

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

.scriptproviders (Enumerar proveedores de scripts)

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

En el ejemplo siguiente, se cargan los proveedores de 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 de JavaScript. Cualquier tipo de script se puede cargar con el comando .scriptload.

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

.scriptload (Load 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 comando siguiente muestra la carga exitosa de TestScript.js.

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

Las manipulaciones del modelo de objetos realizadas por el script permanecerán en su lugar hasta que el script se descargue posteriormente o se vuelva a ejecutar con contenido diferente.

Para obtener más información, consulte .scriptload (Load Script)

.scriptrun

El comando .scriptrun cargará un script, ejecutará el código raíz del script, el initializeScript y la función 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!

Las manipulaciones del modelo de objetos del depurador realizadas por el script permanecerán en su lugar hasta que el script se descargue posteriormente o se vuelva a ejecutar con contenido diferente.

Para obtener más información, vea .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 comandos 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, vea .scriptunload (Unload Script).

.scriptlist (Enumerar scripts cargados)

El comando .scriptlist enumerará los scripts que se han cargado a través del comando .scriptload o .scriptrun. Si TestScript se cargó correctamente mediante .scriptload, el comando .scriptlist mostraría 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, vea .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 sencillo script del depurador de JavaScript que imprime Hello World.

// 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 ejemplo siguiente, 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 sencillo de depuración en JavaScript que recibe entradas y suma 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 trabajar con el script sea un poco más cómodo, asigne una variable en el depurador para almacenar 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 . Contenido de todos los scripts que se cargan.

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

Cuando haya terminado 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 para ver que nuestro script ahora está 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

Hay varias funciones especiales en un script de JavaScript llamado por el propio proveedor de scripts.

initializeScript

Cuando se carga un script de JavaScript y se ejecuta, pasa por una serie de pasos antes de que las variables, las 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 vinculan al espacio de nombres del depurador.

Como se mencionó, 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 de script principal 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 caché del script.

Si un script no realiza manipulaciones imperativas en el modelo de objetos ni almacena en caché los resultados, no es necesario 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.

Comando .scriptload .scriptrun (Ejecutar script) .scriptunload (Descargar script)
raíz
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, vea dx (Display NatVis Expression).

Clase de ejemplo: Simple1DArray

Considere un ejemplo de una clase de C++ que representa una matriz unidimensional. Esta clase tiene dos miembros, m_size que es el tamaño general de la matriz 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 (Load Extension DLL) 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 Seleccionar.

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

Lo que 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 semántica siguiente:

  • 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 ser iterable a través del soporte de [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 al entorno JavaScript, y los objetos de JavaScript se pueden pasar al evaluador de expresiones del depurador. Como ejemplo de esto, considere 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 en línea como se muestra.

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, vea Métodos de control de puntos de interrupción.

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

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

 // 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 siempre 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 Guardar > archivo 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 almacenamiento de 53 bits en JavaScript

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 tratar con esto, cualquier valor nativo de 64 bits (ya sea desde código nativo o el modelo de datos) que entra en JavaScript entra como un tipo de biblioteca, no como un número de JavaScript. Este tipo de biblioteca se convertirá de nuevo a 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 estándar de valueOf de JavaScript. 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 producía una pérdida de precisión (el valor utiliza más de 53 bits de precisión ordinal), el proveedor de JavaScript producirá una excepción.

Tenga en cuenta que si utiliza operadores a nivel de bits en JavaScript, está aún más 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 trabajar con el script sea un poco más cómodo, asigne una variable en el depurador para almacenar 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.

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

En primer lugar, 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

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 hacen referencia al mismo objeto y no al mismo valor. El proveedor de JavaScript mitiga esto rastreando referencias en vivo a valores de 64 bits y devolviendo el mismo objeto "inmutable" para aquellos valores de 64 bits no recolectados. Esto significa que, para la comparación, se producirí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 trabajar con el script sea un poco más cómodo, asigne una variable en el depurador para almacenar el contenido del script mediante el comando dx.

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

En primer lugar para probar, 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 posiblemente) necesite precisión por encima de 53 bits para los valores entrantes de 64 bits, se deben usar los métodos siguientes en lugar de confiar en operadores estándar:

Nombre de método firma de 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 PRODUCE 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 PRODUCE NINGUNA EXCEPCIÓN**
getLowPart .getLowPart() Convierte los 32 bits inferiores del valor de 64 bits en un número de JavaScript.
obtenerParteAlta .getHighPart() Convierte los 32 bits más significativos del valor de 64 bits en un número de JavaScript.
añadir .add(value) Agrega un valor al valor de 64 bits y devuelve el resultado.
restar .subtract(value) Resta un valor al valor de 64 bits y devuelve el resultado.
multiply .multiply(value) // Multiplica un valor 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.
AND a nivel de bits .bitwiseAnd(value) Calcula el "y" bit a bit del valor de 64 bits con el valor proporcionado y devuelve el resultado.
bitwiseOr .bitwiseOr(value) Calcula el bit a bit o del valor de 64 bits con el valor proporcionado y devuelve el resultado.
bitwiseXor .bitwiseXor(value) Calcula el xor bit a bit del valor de 64 bits con el valor proporcionado y devuelve el resultado.
bitwiseShiftLeft .bitwiseShiftLeft(value) Desplaza hacia la izquierda el valor de 64 bits por la cantidad especificada y devuelve el resultado.
bitwiseShiftRight .bitwiseShiftRight(value) Desplaza el valor de 64 bits hacia la derecha por 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 firma de 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 usar la depuración de JavaScript con WinDbg, ejecute el depurador como administrador.

Use este código de ejemplo para aprender a depurar JavaScript. En este tutorial, le asignaremos el nombre DebuggableSample.js y lo guardaremos 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

Inicie la depuración activa del script mediante 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 mensaje >>> Debug [DebuggableSample <No Position>] > y una solicitud de entrada, está dentro del depurador de scripts.

Use el comando .help para mostrar 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 script debugger 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     

Utilice el comando de depuración de scripts sxe para activar la pausa en entrada, de modo que el script se detenga en el depurador de scripts tan pronto como se ejecute cualquier código dentro de él.

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

Salga del depurador de scripts 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 regreso en el depurador normal. Ejecute el siguiente comando para llamar al script.

dx @$scriptContents.outermost()

Ahora, estás de vuelta en el depurador de scripts y se detiene 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.

Vamos a avanzar unos cuantos pasos y alcanzar otro punto en el guion.

    p
    t
    p
    t
    p
    p

En este punto, debe dividirse 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 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 scripts sxd .

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

Luego, deje 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 script y observe cómo se alcanza 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                                    

Muestra 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 desconectamos de él.

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

A continuación, 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 VS Code: adición de IntelliSense

Si quiere trabajar con los objetos del modelo de datos del depurador en VS Code, 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, se encuentra aquí:

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

Para usar el archivo de definición de IntelliSense en VS Code:

  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 para las API del host proporcionadas por JSProvider además de las estructuras de 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 muestran los recursos de JavaScript que pueden resultar útiles a medida que desarrolla extensiones de depuración de JavaScript.

Consulte también

Ejemplos de scripts del depurador de JavaScript

Objetos nativos en extensiones de JavaScript