Compartilhar via


Script do Depurador JavaScript

Este tópico descreve como usar o JavaScript para criar scripts que entendam os objetos do depurador e estenda e personalize os recursos do depurador.

Visão geral do Script do Depurador javaScript

Os provedores de scripts conectam uma linguagem de script ao modelo de objeto interno do depurador. O provedor de scripts do depurador JavaScript permite o uso de JavaScript com o depurador.

Quando um JavaScript é carregado por meio do comando .scriptload, o código raiz do script é executado, os nomes presentes no script são conectados ao namespace raiz do depurador (dx Depurador) e o script permanece residente na memória até que ele seja descarregado e todas as referências a seus objetos sejam liberadas. O script pode fornecer novas funções para o avaliador de expressão do depurador, modificar o modelo de objeto do depurador ou pode atuar como um visualizador da mesma maneira que um visualizador NatVis.

Este tópico descreve algumas das coisas que você pode fazer com o script do depurador JavaScript.

Esses dois tópicos fornecem informações adicionais sobre como trabalhar com JavaScript no depurador.

Exemplos de Scripts para Depurador JavaScript

Objetos nativos em extensões JavaScript

Vídeo de Script JavaScript

Defrag Tools #170 – Andy e Bill demonstram as habilidades de extensibilidade e de script em JavaScript no depurador.

O provedor JavaScript do depurador

O provedor JavaScript incluído com o depurador aproveita ao máximo os mais recentes aprimoramentos de objeto e classe do ECMAScript6. Para obter mais informações, consulte ECMAScript 6 — Novos recursos: visão geral e comparação.

JsProvider.dll

JsProvider.dll é o provedor JavaScript que é carregado para dar suporte ao Script do Depurador javaScript.

Requisitos

O Script do Depurador javaScript foi projetado para funcionar com todas as versões com suporte do Windows.

Carregando o provedor de scripts JavaScript

Antes de usar qualquer um dos comandos .script, um provedor de scripts precisa ser carregado. Use o comando .scriptproviders para confirmar se o provedor JavaScript está carregado.

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

Comandos Meta de Script JavaScript

Os comandos a seguir estão disponíveis para trabalhar com o Script do Depurador javaScript.

Requisitos

Antes de usar qualquer um dos comandos .script, um provedor de scripts precisa ser carregado. Use o comando .scriptproviders para confirmar se o provedor JavaScript está carregado.

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

.scriptproviders (listar provedores de script)

O comando .scriptproviders listará todos os idiomas de script que atualmente são compreendidos pelo depurador e pela extensão sob a qual estão registrados.

No exemplo a seguir, os provedores JavaScript e NatVis são carregados.

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

Qualquer arquivo que termine em ". NatVis" é entendido como um script NatVis e qualquer arquivo que termina em ".js" é entendido como um script JavaScript. Qualquer tipo de script pode ser carregado com o comando .scriptload.

Para obter mais informações, consulte .scriptproviders (Listar provedores de script)

.scriptload (Load Script)

O comando .scriptload carregará um script e executará o código raiz de um script e a função initializeScript . Se houver erros na carga inicial e na execução do script, os erros serão exibidos no console. O comando a seguir mostra a carga bem-sucedida de TestScript.js.

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

Todas as manipulações de modelo de objeto feitas pelo script permanecerão em vigor até que o script seja descarregado posteriormente ou seja executado novamente com conteúdo diferente.

Para obter mais informações, consulte .scriptload (Load Script)

.scriptrun

O comando .scriptrun carregará um script, executará o código raiz do script, o initializeScript e a função invokeScript . Se houver erros na carga inicial e na execução do script, os erros serão exibidos no console.

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

Todas as manipulações do modelo de objeto do depurador feitas pelo script permanecerão em vigor até que o script seja descarregado posteriormente ou seja executado novamente com conteúdo diferente.

Para obter mais informações, consulte .scriptrun (Executar Script).

.scriptunload (Descarregar Script)

O comando .scriptunload descarrega um script carregado e chama a função uninitializeScript . Use a sintaxe de comando a seguir para descarregar um script

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

Para obter mais informações, consulte .scriptunload (Descarregar Script).

.scriptlist (Lista de Scripts Carregados)

O comando .scriptlist listará todos os scripts que foram carregados por meio do .scriptload ou do comando .scriptrun. Se o TestScript tiver sido carregado com êxito usando .scriptload, o comando .scriptlist exibirá o nome do script carregado.

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

Para obter mais informações, consulte .scriptlist (Listar Scripts Carregados).

Introdução ao Script do Depurador javaScript

Script de exemplo helloworld

Esta seção descreve como criar e executar um script de depurador JavaScript simples que imprime, Hello World.

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

Use um editor de texto, como o Bloco de Notas, para criar um arquivo de texto chamado HelloWorld.js que contenha o código JavaScript mostrado acima.

Use o comando .scriptload para carregar e executar o script. Como usamos o nome da função initializeScript, o código na função é executado quando o script é carregado.

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

Depois que o script é carregado, a funcionalidade adicional está disponível no depurador. Use o comando dx (Exibir Expressão NatVis) para exibir Debugger.State.Scripts para ver que nosso script agora está residente.

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

No próximo exemplo, adicionaremos e chamaremos uma função nomeada.

Adicionar script de exemplo de dois valores

Esta seção descreve como criar e executar um script de depuração JavaScript simples que recebe entrada e soma dois números.

Esse script simples fornece uma única função, addTwoValues.

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

Use um editor de texto, como o Bloco de Notas, para criar um arquivo de texto chamado FirstSampleFunction.js

Use o comando .scriptload para carregar o script.

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

Depois que o script é carregado, a funcionalidade adicional está disponível no depurador. Use o comando dx (Exibir Expressão NatVis) para exibir Debugger.State.Scripts para ver que nosso script agora está residente.

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

Podemos selecionar o FirstSampleFunction para ver quais funções ele fornece.

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

Para facilitar um pouco o trabalho com o script, atribua uma variável no depurador para armazenar o conteúdo do script usando o comando dx.

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

Use o avaliador de expressão dx para chamar a função addTwoValues.

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

Você também pode usar o @$scriptContents alias interno para trabalhar com os scripts. O alias @$scriptContents mescla todo o .Content de todos os scripts que foram carregados.

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

Quando terminar de trabalhar com o script, use o comando .scriptunload para descarregar o script.

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

Automação de comando do depurador

Esta seção descreve como criar e executar um script de depurador JavaScript simples que automatiza o envio do comando u (Unassemble ). O exemplo também mostra como coletar e exibir a saída do comando em um loop.

Esse script fornece uma única função, 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 um editor de texto, como o Bloco de Notas, para criar um arquivo de texto chamado RunCommands.js

Use o comando .scriptload para carregar o script RunCommands.

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

Depois que o script é carregado, a funcionalidade adicional está disponível no depurador. Use o comando dx (Exibir Expressão NatVis) para exibir Debugger.State.Scripts.RunCommands para ver que nosso script agora 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 o comando dx para chamar a função RunCommands no 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

Funções especiais do depurador JavaScript

Há várias funções especiais em um script JavaScript chamado pelo próprio provedor de script.

initializeScript

Quando um script JavaScript é carregado e executado, ele passa por uma série de etapas antes que as variáveis, as funções e outros objetos no script afetem o modelo de objeto do depurador.

  • O script é carregado na memória e analisado.
  • O código raiz dentro do script é executado.
  • Se o script tiver um método chamado initializeScript, esse método será chamado.
  • O valor retornado de initializeScript é usado para determinar como modificar automaticamente o modelo de objeto do depurador.
  • Os nomes no script são conectados ao namespace do depurador.

Conforme mencionado, initializeScript será chamado imediatamente após a execução do código raiz do script. Seu trabalho é retornar uma matriz JavaScript de objetos de registro para o provedor que indica como modificar o modelo de objeto do 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

O método invokeScript é o método de script primário e é chamado quando .scriptload e .scriptrun são executados.

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

O método uninitializeScript é o oposto comportamental de initializeScript. Ele é chamado quando um script é desvinculado e está se preparando para descarregar. Seu trabalho é desfazer as alterações no modelo de objeto que o script fez imperativamente durante a execução e/ou destruir quaisquer objetos que o script armazenasse em cache.

Se um script não fizer manipulações imperativas para o modelo de objeto nem armazenar em cache os resultados, ele não precisará ter um método uninitializeScript. Todas as alterações no modelo de objeto executadas conforme indicado pelo valor retornado de initializeScript são desfeitas automaticamente pelo provedor. Essas alterações não exigem um 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");
}

Resumo das funções chamadas por comandos de script

Esta tabela resume quais funções são chamadas pelos comandos de script

Comando .scriptload .scriptrun (Executar Script) .scriptunload (Descarregar Script)
raiz Sim Sim
initializeScript Sim Sim
invokeScript Sim
uninitializeScript Sim

Use este código de exemplo para ver quando cada função é chamada à medida que o script é carregado, executado e descarregado.

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

Criando um visualizador de depurador em JavaScript

Os arquivos de visualização personalizados permitem agrupar e organizar dados em uma estrutura de visualização que reflita melhor as relações de dados e o conteúdo. Você pode usar as extensões do depurador JavaScript para escrever visualizadores de depurador que atuam de maneira muito semelhante ao NatVis. Isso é feito por meio da criação de um objeto de protótipo JavaScript (ou uma classe ES6) que atua como visualizador para um determinado tipo de dados. Para obter mais informações sobre NatVis e o depurador, consulte dx (Expressão NatVis de exibição).

Classe de exemplo – Simple1DArray

Considere um exemplo de uma classe C++ que representa uma única matriz dimensional. Essa classe tem dois membros, m_size que é o tamanho geral da matriz e m_pValues que é um ponteiro para um número de ints na memória igual ao campo m_size.

class Simple1DArray
{
private:

    ULONG64 m_size;
    int *m_pValues;
};

Podemos usar o comando dx para examinar a renderização da estrutura de dados padrão.

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

Visualizador JavaScript

Para visualizar esse tipo, precisamos criar uma classe de protótipo (ou ES6) que tenha todos os campos e propriedades que queremos que o depurador mostre. Também precisamos que o método initializeScript retorne um objeto que informe ao provedor JavaScript associar nosso protótipo como um visualizador para o tipo fornecido.

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

Salve o script em um arquivo chamado arrayVisualizer.js.

Use o comando .load (Carregar Extensão DLL) para carregar o provedor JavaScript.

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

Use o .scriptload para carregar o script do visualizador de matriz.

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

Agora, quando o comando dx é usado, o visualizador de script exibirá linhas de conteúdo da 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

Além disso, essa visualização javaScript fornece funcionalidade LINQ, como Selecionar.

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

O que afeta a visualização

Um protótipo ou classe que é designado como visualizador para um tipo nativo por meio do retorno de um objeto host.typeSignatureRegistration de initializeScript terá todas as propriedades e métodos em JavaScript adicionados ao tipo nativo. Além disso, a seguinte semântica se aplica:

  • Qualquer nome que não comece com dois sublinhados (__) estará disponível na visualização.

  • Os nomes que fazem parte dos objetos JavaScript padrão ou fazem parte dos protocolos que o provedor JavaScript cria não aparecerão na visualização.

  • Um objeto pode ser iterável por meio do suporte de [Symbol.iterator].

  • Um objeto pode ser indexável por meio do suporte de um protocolo personalizado que consiste em várias funções: getDimensionality, getValueAt e, opcionalmente, setValueAt.

Ponte de objeto Nativo e JavaScript

A ponte entre JavaScript e o modelo de objeto do depurador é bidirecional. Objetos nativos podem ser passados para JavaScript e objetos JavaScript podem ser passados para o avaliador de expressão do depurador. Como exemplo disso, considere a adição do seguinte método em nosso script:

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

Esse método agora pode ser utilizado na consulta LINQ de exemplo acima. Primeiro, carregamos a visualização do 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

Em seguida, podemos usar a função multiplyBySeven embutida, conforme mostrado abaixo.

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

Pontos de interrupção condicionais com JavaScript

Você pode usar o JavaScript para fazer o processamento suplementar após um ponto de interrupção ser atingido. Por exemplo, o script pode ser usado para examinar outros valores de tempo de execução e, em seguida, determinar se você deseja continuar automaticamente a execução ou parar o código e fazer depuração manual adicional.

Para obter informações gerais sobre como trabalhar com pontos de interrupção, consulte Métodos de controle de pontos de interrupção.

DebugHandler.js Exemplo de Script de Processamento de Ponto de Interrupção

Este exemplo avaliará a caixa de diálogo abrir e salvar do bloco de notas: bloco de notas! ShowOpenSaveDialog. Esse script avaliará a variável pszCaption para determinar se a caixa de diálogo atual é uma caixa de diálogo "Abrir" ou se é uma caixa de diálogo "Salvar como". Se for uma caixa de diálogo aberta, a execução do código continuará. Se for uma caixa de diálogo Salvar como, a execução do código será interrompida e o depurador entrará em modo de interrupção.

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

Esse comando define um ponto de interrupção no bloco de notas! ShowOpenSaveDialog e executará o script acima sempre que esse ponto de interrupção for atingido.

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

Em seguida, quando a opção Salvar Arquivo > é selecionada no bloco de notas, o script é executado, o comando g não é enviado e ocorre uma interrupção na execução do 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

Trabalhar com valores de 64 bits em extensões JavaScript

Esta seção descreve como os valores de 64 bits passados para uma extensão de depurador JavaScript se comportam. Esse problema surge porque o JavaScript só tem a capacidade de armazenar números usando 53 bits.

64 bits e armazenamento de 53 bits em JavaScript

Os valores ordinais passados para JavaScript normalmente são empacotados como números JavaScript. O problema é que os números JavaScript são números de ponto flutuante de precisão dupla de 64 bits. Qualquer ordinal com mais de 53 bits perderia a precisão na entrada no JavaScript. Isso apresenta um problema para ponteiros de 64 bits e outros valores ordinais de 64 bits que podem ter sinalizadores nos bytes mais altos. Para lidar com isso, qualquer valor nativo de 64 bits (seja do código nativo ou do modelo de dados) que entra no JavaScript entra como um tipo de dado da biblioteca, não como um número JavaScript. Esse tipo de biblioteca fará uma viagem de ida e volta para o código nativo sem perder a precisão numérica.

Conversão automática

O tipo de biblioteca para valores ordinais de 64 bits oferece suporte à conversão padrão valueOf do JavaScript. Se o objeto for usado em uma operação matemática ou em outro constructo que exija conversão de valor, ele será convertido automaticamente em um número JavaScript. Se a perda de precisão ocorrer (o valor utilizará mais de 53 bits de precisão ordinal), o provedor JavaScript lançará uma exceção.

Observe que, se você usar operadores bit a bit em JavaScript, estará ainda mais limitado a 32 bits de precisão ordinal.

Este código de exemplo soma dois números e será usado para testar a conversão 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 um editor de texto, como o Bloco de Notas, para criar um arquivo de texto chamado PlayWith64BitValues.js

Use o comando .scriptload para carregar o script.

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

Para facilitar o trabalho com o script, atribua uma variável no depurador para manter seu conteúdo, utilizando o comando dx.

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

Use o avaliador de expressão dx para chamar a função addTwoValues.

Primeiro, calcularemos o valor de 2^53 =9007199254740992 (Hex 0x20000000000000).

Primeiro para testar, usaremos (2^53) – 2 e veremos se ele retorna o valor correto para a soma.

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

Em seguida, calcularemos (2^53) -1 =9007199254740991. Isso retorna o erro que indica que o processo de conversão perderá a precisão, portanto, esse é o maior valor que pode ser usado com o método de soma no código JavaScript.

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

Chame um método de modelo de dados passando valores de 64 bits. Não há perda de precisão aqui.

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

Comparação

O tipo de biblioteca de 64 bits é um objeto JavaScript e não um tipo de valor, como um número JavaScript. Isso tem algumas implicações para operações de comparação. Normalmente, a igualdade (==) em um objeto indicaria que operandos se referem ao mesmo objeto e não ao mesmo valor. O provedor JavaScript atenua isso rastreando referências ativas a valores de 64 bits e retornando o mesmo objeto "imutável" para os valores de 64 bits não coletados. Isso significa que, para comparação, o seguinte ocorreria.

// 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 um editor de texto, como o Bloco de Notas, para criar um arquivo de texto chamado ComparisonWith64BitValues.js

Use o comando .scriptload para carregar o script.

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

Para facilitar o trabalho com o script, atribua uma variável no depurador que armazene o conteúdo do script usando o comando dx.

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

Primeiro para testar, usaremos (2^53) - 2 e veremos se ele retorna os valores esperados.

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

Também tentaremos o número 42 como o primeiro valor para validar se o operador de comparação está funcionando como deveria.

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

Em seguida, calcularemos (2^53) -1 =9007199254740991. Esse valor retorna o erro que indica que o processo de conversão perderá a precisão, portanto, esse é o maior valor que pode ser usado com os operadores de comparação no código JavaScript.

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

Mantendo a precisão em operações

Para permitir que uma extensão de depurador mantenha a precisão, um conjunto de funções matemáticas é projetado sobre o tipo de biblioteca de 64 bits. Se a extensão precisar (ou possivelmente) precisar de precisão acima de 53 bits para valores de 64 bits de entrada, os seguintes métodos deverão ser utilizados em vez de depender de operadores padrão:

Nome do Método Assinatura Descrição
comoNúmero .asNumber() Converte o valor de 64 bits em um número JavaScript. Se ocorrer perda de precisão, **UMA EXCEÇÃO É LANÇADA**
convertToNumber .convertToNumber() Converte o valor de 64 bits em um número JavaScript. Se ocorrer perda de precisão, **NENHUMA EXCEÇÃO É LANÇADA**
obterParteInferior .getLowPart() Converte os 32 bits inferiores do valor de 64 bits em um número JavaScript
getHighPart .getHighPart() Converte os 32 bits mais altos do valor de 64 bits em um número JavaScript
adicionar .add(value) Adiciona um valor ao valor de 64 bits e retorna o resultado
extrair substrato .subtract(value) Subtrai um valor do valor de 64 bits e retorna o resultado
multiply .multiply(value) Multiplica o valor de 64 bits pelo valor fornecido e retorna o resultado
dividir .divide(value) Divide o valor de 64 bits pelo valor fornecido e retorna o resultado
bitwiseAnd .bitwiseAnd(value) Calcula o bit a bit e o valor de 64 bits com o valor fornecido e retorna o resultado
bitwiseOr .bitwiseOr(value) Calcula o bit a bit ou o valor de 64 bits com o valor fornecido e retorna o resultado
bitwiseXor .bitwiseXor(value) Calcula o xor bit a bit do valor de 64 bits com o valor fornecido e retorna o resultado
bitwiseShiftLeft .bitwiseShiftLeft(value) Desloca o valor de 64 bits deixado pelo valor fornecido e retorna o resultado
bitwiseShiftRight .bitwiseShiftRight(value) Desloca o valor de 64 bits para a direita pela quantidade especificada e retorna o resultado
toString .toString([radix]) Converte o valor de 64 bits em uma cadeia de caracteres de exibição no radix padrão (ou no radix fornecido opcionalmente)

Esse método também está disponível.

Nome do Método Assinatura Descrição
compareTo .compareTo(value) Compara o valor de 64 bits com outro valor de 64 bits.

Depuração de JavaScript

Esta seção descreve como usar os recursos de depuração de scripts do depurador. O depurador tem suporte integrado para depurar scripts JavaScript usando o comando .scriptdebug (Depurar JavaScript ).

Observação

Para usar a Depuração do JavaScript com o WinDbg, execute o depurador como Administrador.

Use este código de exemplo para explorar a depuração de JavaScript. Para este passo a passo, vamos nomeá-lo DebuggableSample.js e salvá-lo no diretório 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
    //
}

Carregue o script de exemplo.

.scriptload C:\MyScripts\DebuggableSample.js

Comece a depurar ativamente o script usando o comando .scriptdebug .

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

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

Depois de ver o prompt >>> Debug [DebuggableSample <No Position>] > e uma solicitação de entrada, você está dentro do depurador de script.

Use o comando .help para exibir uma lista de comandos no ambiente de Depuração do 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 o comando de depuração sx script para ver a 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 o comando do depurador de script sxe para ativar "break on entry", de modo que o script seja interceptado no depurador de script assim que qualquer código dentro dele for executado.

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

Encerre o depurador de script e faremos uma chamada de função no script que será capturada pelo depurador.

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

Neste ponto, você voltou ao depurador normal. Execute o comando a seguir para chamar o script.

dx @$scriptContents.outermost()

Agora, você está de volta ao depurador de script e interrompido na primeira linha da função JavaScript mais externa.

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

>>> Debug [DebuggableSample 73:5] >                         

Além de ver a quebra no depurador, você obtém informações sobre a linha (73) e a coluna (5) em que a interrupção ocorreu, bem como o snippet de código-fonte relevante: var x = 99.

Vamos avançar alguns passos e chegar a outro lugar no script.

    p
    t
    p
    t
    p
    p

Neste ponto, você deve ser dividido no método throwAndCatch na linha 34.

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

Você pode verificar isso executando um rastreamento de pilha.

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

A partir daqui, você pode investigar o valor das variáveis.

>>> 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 definir um ponto de interrupção na linha de código atual e ver quais pontos de interrupção agora estão definidos.

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

A partir deste ponto, desativaremos o evento de entrada (en) com o comando do depurador de script sxd.

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

Em seguida, apenas deixe o script continuar até o final.

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

Execute o método de script novamente e observe nosso ponto de interrupção ser atingido.

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

Exiba a pilha de chamadas.

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

Neste ponto, queremos parar de depurar esse script, então nos desanexamos dele.

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

Em seguida, digite 'q' para sair.

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

A execução da função novamente não será mais interrompida no 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 no VS Code – Adicionando o IntelliSense

Se você quiser trabalhar com os objetos do modelo de dados do depurador no VS Code, poderá usar um arquivo de definição disponível nos kits de desenvolvimento do Windows. O arquivo de definição do IntelliSense fornece suporte para todas as APIs de objeto do depurador host.* . Se você instalou o kit no diretório padrão em um computador de 64 bits, ele está localizado aqui:

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

Para usar o arquivo de definição do IntelliSense no VS Code:

  1. Localizar o arquivo de definição – JSProvider.d.ts

  2. Copie o arquivo de definição para a mesma pasta que o script.

  3. Adicione /// <reference path="JSProvider.d.ts" /> à parte superior do arquivo de script JavaScript.

Com essa referência em seu arquivo JavaScript, o VS Code fornecerá automaticamente o IntelliSense nas APIs de host, que são fornecidas pelo JSProvider, e além disso, nas estruturas em seu script. Por exemplo, digite "host". e você verá o IntelliSense para todas as APIs do modelo de depurador disponíveis.

Recursos JavaScript

Veja a seguir os recursos JavaScript que podem ser úteis à medida que você desenvolve extensões de depuração javaScript.

Consulte também

Exemplos de Scripts para Depurador JavaScript

Objetos nativos em extensões JavaScript