Compartilhar via


Objetos nativos do depurador em extensões JavaScript

Objetos de depurador nativos representam vários constructos e comportamentos do ambiente do depurador. Os objetos podem ser passados em (ou adquiridos em) extensões JavaScript para manipular o estado do depurador.

Os objetos de depurador de exemplo incluem o seguinte.

  • Session
  • Tarefas/Thread
  • Processos/Processo
  • Quadros de pilha/quadro de pilha
  • Variáveis locais
  • Módulos/Módulo
  • Utilidade
  • Estado
  • Configurações

Por exemplo, o objeto host.namespace.Debugger.Utility.Control.ExecuteCommand pode ser usado para enviar o comando u para o depurador com as duas linhas a seguir do código JavaScript.

var ctl = host.namespace.Debugger.Utility.Control;   
var outputLines = ctl.ExecuteCommand("u");

Este tópico descreve como trabalhar com objetos comuns e fornece informações de referência sobre seus atributos e comportamentos.

Para obter informações gerais sobre como trabalhar com JavaScript, consulte o Script do Depurador javaScript. Para obter exemplos de JavaScript que usam os objetos do depurador, consulte scripts de exemplo do depurador javaScript. Para obter informações sobre como trabalhar com os objetos de configurações, consulte .settings (Definir configurações de depuração).

Para explorar os objetos disponíveis em uma sessão de depurador, use o comando dx (Exibição de Expressão NatVis). Por exemplo, você pode exibir alguns dos objetos de depurador de nível superior com esse comando dx.

0: kd> dx -r2 Debugger
Debugger                
    Sessions         : [object Object]
        [0x0]            : Remote KD: KdSrv:Server=@{<Local>},Trans=@{NET:Port=50000,Key=1.2.3.4,Target}
    Settings        
        Debug           
        Display         
        EngineInitialization
        Extensions      
        Input           
        Sources         
        Symbols         
        AutoSaveSettings : false
    State           
        DebuggerVariables
        PseudoRegisters 
        Scripts         
        UserVariables   
    Utility         
        Collections     
        Control         
        Objects   

Todos os itens listados acima são DML clicáveis e podem ser recursados mais abaixo para exibir a estrutura de objetos do depurador.

Estendendo o depurador por meio do modelo de dados

O modelo de dados do depurador permite a criação de uma interface para informações sobre aplicativos e drivers no Windows que tem os seguintes atributos.

  • É detectável e organizado: um espaço de nome estruturado logicamente pode ser consultado usando o comando dx.
  • Pode ser consultado usando LINQ- Isso permite extração e classificação de dados usando uma linguagem de consulta padrão.
  • Pode ser estendido de maneira lógica e consistente. É extensível usando técnicas descritas neste tópico, com provedores de script para depuradores, como Natvis e JavaScript.

Estendendo um objeto de depurador em JavaScript

Além de poder criar um visualizador em JavaScript, as extensões de script também podem modificar os principais conceitos do depurador - sessões, processos, threads, pilhas, quadros de pilha, variáveis locais - e até mesmo publicar-se como pontos de extensão que outras extensões podem consumir.

Esta seção descreve como estender um conceito fundamental dentro do depurador. As extensões criadas para serem compartilhadas devem estar em conformidade com as diretrizes apresentadas em objetos de depurador nativos em extensões JavaScript – considerações de design e teste.

Registrando uma extensão

Um script pode registrar o fato de que ele fornece uma extensão por meio de uma entrada na matriz retornada do método initializeScript.

function initializeScript()
{
    return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process")];
}

A presença de um objeto host.namedModelParent dentro da matriz retornada indica ao depurador que um determinado objeto protótipo ou classe ES6 (comProcessExtension, nesse caso) será um modelo de dados pai para o modelo que é registrado sob o nome Debugger.Models.Process.

Pontos de Extensão do Objeto de Depuração

Os pontos de extensão do depurador a seguir são parte integrante do depurador e estão disponíveis para serem usados por provedores de script, como JavaScript.

Debugger.Models.Sessions: a lista de sessões (destinos) às quais o depurador está anexado

Debugger.Models.Session: uma sessão individual (destino) à qual o depurador está anexado (modo de usuário dinâmico, KD etc...)

Debugger.Models.Processes: a lista de processos em uma sessão

Debugger.Models.Threads: a lista de threads em um processo

Debugger.Models.Thread: um thread individual dentro de um processo (independentemente do modo de usuário ou kernel)

Debugger.Models.Stack: a pilha de um thread

Debugger.Models.StackFrames: a coleção de frames que compõem uma pilha

Debugger.Models.StackFrame: um quadro de pilha individual dentro de uma pilha

Debugger.Models.LocalVariables: as variáveis locais dentro de um frame de pilha

Debugger.Models.Parameters: os parâmetros de uma chamada dentro de um quadro de pilha

Debugger.Models.Module: um módulo individual dentro do espaço de endereço de um processo

Objetos de modelo de dados adicionais

Além disso, há alguns objetos de modelo de dados adicionais definidos pelo modelo de dados principal.

DataModel.Models.Intrinsic: um valor intrínseco (ordinais, pontos flutuantes, etc...)

DataModel.Models.String: uma cadeia de caracteres

DataModel.Models.Array: uma matriz nativa

DataModel.Models.Guid: um Identificador Único Global (GUID)

DataModel.Models.Error: um objeto de erro

DataModel.Models.Concepts.Iterable: aplicado a todos os objetos que podem ser iteráveis

DataModel.Models.Concepts.StringDisplayable: aplicado a cada objeto que tenha uma conversão de cadeia de caracteres de exibição

Visão geral da extensão de objeto de depurador COM de exemplo

Vamos considerar um exemplo. Imagine que você deseja criar uma extensão de depurador para exibir informações específicas do COM, como a tabela de interface global (GIT).

No passado, poderia ter havido uma extensão de depurador existente com vários comandos que forneciam um meio de acessar informações sobre COM. Um comando pode exibir informações centradas no processo (a tabela de interface global, por exemplo). Outro comando pode fornecer informações centradas em thread, como em qual código de apartamento está sendo executado. Pode ser necessário conhecer e carregar uma segunda extensão de depurador para explorar outros aspectos do COM.

Em vez de ter um conjunto de comandos difíceis de descobrir, uma extensão JavaScript pode modificar o conceito do depurador do que é um processo e um thread, para adicionar essas informações de uma maneira natural, explorável e composável com outras extensões de depurador.

Extensão de objeto de depurador do modo de usuário ou kernel

O depurador e os objetos do depurador têm um comportamento diferente no modo de usuário e kernel. Ao criar seus objetos de modelo de depurador, você precisa decidir em quais ambientes você trabalhará. Como trabalharemos com COM no modo de usuário, criaremos e testaremos essa extensão com no modo de usuário. Em outras situações, você poderá criar um JavaScript de debugger que funcionará tanto na depuração do modo de usuário quanto no modo kernel.

Criando um sub-namespace

Voltando ao nosso exemplo, podemos definir um protótipo ou classe ES6, comProcessExtension que contém o conjunto de coisas que queremos adicionar a um objeto de processo.

Importante A intenção com o sub-namespace é criar um paradigma logicamente estruturado e naturalmente explorável. Por exemplo, evite colocar itens não relacionados no mesmo subnamespace. Examine cuidadosamente as informações discutidas em Objetos Nativos do Depurador em Extensões JavaScript – Considerações de Design e Teste antes de criar um subnamespace.

Neste snippet de código, criamos um subnamespace chamado 'COM' no objeto de depurador de processo existente.

var comProcessExtension =
{
    //
    // Add a sub-namespace called 'COM' on process.
    //
    get COM()
    {
        //
        // What is 'this' below...?  It's the debugger's process object.  Yes -- this means that there is a cross-language
        // object hierarchy here.  A C++ object implemented in the debugger has a parent model (prototype) which is
        // implemented in JavaScript.
        //
        return new comNamespace(this);
    }
}

Implementação do namespace

Em seguida, crie o objeto que implementa o subnamespace COM em um processo.

Importante Pode haver vários processos (seja ligados a eles no modo de usuário ou sob KD). Essa extensão não pode assumir que o estado atual do depurador é o que o usuário pretendia. Alguém pode capturar <algumProcess>.COM em uma variável e modificá-lo, o que pode levar à apresentação de informações do contexto de processo errado. A solução é adicionar código na extensão para que cada instanciação acompanhe o processo ao qual está anexada. Para este exemplo de código, esta informação é passada por meio do ponteiro 'this' associado à propriedade.

this.__process = process;

class comNamespace
{
    constructor(process)
    {
        //
        // This is an entirely JavaScript object.  Each instantiation of a comNamespace will keep track
        // of what process it is attached to (passed via the ''this'' pointer of the property getter
        // we authored above.
        //
        this.__process = process;
    }
    
    get GlobalObjects()
    {
        return new globalObjects(this.__process);
    }
}

Lógica de implementação para a tabela de interface global COM

Para separar essa lógica de implementação para a tabela de interface global COM mais claramente, definiremos uma classe ES6, gipTable que abstrai a tabela COM GIP e outra globalObjects, que é o que será retornado do getter GlobalObjects() definido no snip de código da Implementação do Namespace mostrado acima. Todos esses detalhes podem ser ocultos dentro do fechamento de initializeScript para evitar a publicação de qualquer um desses detalhes internos no namespace do depurador.

// gipTable:
//
// Internal class which abstracts away the GIP Table.  It iterates objects of the form
// {entry : GIPEntry, cookie : GIT cookie}
//
class gipTable
{
    constructor(gipProcess)
    {
        //
        // Windows 8 through certain builds of Windows 10, it's in CGIPTable::_palloc.  In certain builds
        // of Windows 10 and later, this has been moved to GIPEntry::_palloc.  We need to check which.
        //
        var gipAllocator = undefined;
        try
        {
            gipAllocator = host.getModuleSymbol("combase.dll", "CGIPTable::_palloc", "CPageAllocator", gipProcess)._pgalloc;
        }
        catch(err)
        {
        }

        if (gipAllocator == undefined)
        {
            gipAllocator = host.getModuleSymbol("combase.dll", "GIPEntry::_palloc", "CPageAllocator", gipProcess)._pgalloc;
        }

        this.__data = {
            process : gipProcess,
            allocator : gipAllocator,
            pageList : gipAllocator._pPageListStart,
            pageCount : gipAllocator._cPages,
            entriesPerPage : gipAllocator._cEntriesPerPage,
            bytesPerEntry : gipAllocator._cbPerEntry,
            PAGESHIFT : 16,
            PAGEMASK : 0x0000FFFF,
            SEQNOMASK : 0xFF00
        };
    }

    *[Symbol.iterator]()
    {
        for (var pageNum = 0; pageNum < this.__data.pageCount; ++pageNum)
        {
            var page = this.__data.pageList[pageNum];
            for (var entryNum = 0; entryNum < this.__data.entriesPerPage; ++entryNum)
            {
                var entryAddress = page.address.add(this.__data.bytesPerEntry * entryNum);
                var gipEntry = host.createPointerObject(entryAddress, "combase.dll", "GIPEntry *", this.__data.process);
                if (gipEntry.cUsage != -1 && gipEntry.dwType != 0)
                {
                    yield {entry : gipEntry, cookie : (gipEntry.dwSeqNo | (pageNum << this.__data.PAGESHIFT) | entryNum)};
                }
            }
        }
    }

    entryFromCookie(cookie)
    {
        var sequenceNo = (cookie & this.__data.SEQNOMASK);
        cookie = cookie & ~sequenceNo;
        var pageNum = (cookie >> this.__data.PAGESHIFT);
        if (pageNum < this.__data.pageCount)
        {
            var page = this.__data.pageList[pageNum];
            var entryNum = (cookie & this.__data.PAGEMASK);
            if (entryNum < this.__data.entriesPerPage)
            {
                var entryAddress = page.address.add(this.__data.bytesPerEntry * entryNum);
                var gipEntry = host.createPointerObject(entryAddress, "combase.dll", "GIPEntry *", this.__data.process);
                if (gipEntry.cUsage != -1 && gipEntry.dwType != 0 && gipEntry.dwSeqNo == sequenceNo)
                {
                    return {entry : gipEntry, cookie : (gipEntry.dwSeqNo | (pageNum << this.__data.PAGESHIFT) | entryNum)};
                }
            }
        }

        //
        // If this exception flows back to C/C++, it will be a failed HRESULT (according to the type of error -- here E_BOUNDS)
        // with the message being encapsulated by an error object.
        //
        throw new RangeError("Unable to find specified value");
    }
}
// globalObjects:
//
// The class which presents how we want the GIP table to look to the data model.  It iterates the actual objects
// in the GIP table indexed by their cookie.
//
class globalObjects
{
    constructor(process)
    {
        this.__gipTable = new gipTable(process);
    }

    *[Symbol.iterator]()
    {
        for (var gipCombo of this.__gipTable)
        {
            yield new host.indexedValue(gipCombo.entry.pUnk, [gipCombo.cookie]);
        }
    }

    getDimensionality()
    {
        return 1;
    }

    getValueAt(cookie)
    {
        return this.__gipTable.entryFromCookie(cookie).entry.pUnk;
    }
}

Por fim, use host.namedModelRegistration para registrar a nova funcionalidade COM.

function initializeScript()
{
    return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process"),
            new host.namedModelRegistration(comNamespace, "Debugger.Models.ComProcess")];
}

Salve o código para GipTableAbstractor.js usando um aplicativo, como o bloco de notas.

Aqui estão as informações do processo disponíveis no modo de usuário antes de carregar essa extensão.

0:000:x86> dx @$curprocess
@$curprocess                 : DataBinding.exe
    Name             : DataBinding.exe
    Id               : 0x1b9c
    Threads         
    Modules  

Carregue a extensão JavaScript.

0:000:x86> .scriptload C:\JSExtensions\GipTableAbstractor.js
JavaScript script successfully loaded from 'C:\JSExtensions\GipTableAbstractor.js'

Em seguida, use o comando dx para exibir informações sobre o processo usando o @$curprocess predefinido.

0:000:x86> dx @$curprocess
@$curprocess                 : DataBinding.exe
    Name             : DataBinding.exe
    Id               : 0x1b9c
    Threads         
    Modules         
    COM              : [object Object]
0:000:x86> dx @$curprocess.COM
@$curprocess.COM                 : [object Object]
    GlobalObjects    : [object Object]
0:000:x86> dx @$curprocess.COM.GlobalObjects
@$curprocess.COM.GlobalObjects                 : [object Object]
    [0x100]          : 0x12f4fb0 [Type: IUnknown *]
    [0x201]          : 0x37cfc50 [Type: IUnknown *]
    [0x302]          : 0x37ea910 [Type: IUnknown *]
    [0x403]          : 0x37fcfe0 [Type: IUnknown *]
    [0x504]          : 0x12fe1d0 [Type: IUnknown *]
    [0x605]          : 0x59f04e8 [Type: IUnknown *]
    [0x706]          : 0x59f0eb8 [Type: IUnknown *]
    [0x807]          : 0x59f5550 [Type: IUnknown *]
    [0x908]          : 0x12fe340 [Type: IUnknown *]
    [0xa09]          : 0x5afcb58 [Type: IUnknown *]

Esta tabela também é acessível programaticamente por meio de cookie GIT.

0:000:x86> dx @$curprocess.COM.GlobalObjects[0xa09]
@$curprocess.COM.GlobalObjects[0xa09]                 : 0x5afcb58 [Type: IUnknown *]
    [+0x00c] __abi_reference_count [Type: __abi_FTMWeakRefData]
    [+0x014] __capture        [Type: Platform::Details::__abi_CapturePtr]

Estendendo os conceitos de objetos do Depurador com LINQ

Além de poder estender objetos como processo e thread, o JavaScript também pode estender conceitos associados ao modelo de dados. Por exemplo, é possível adicionar um novo método LINQ a cada iterável. Considere uma extensão de exemplo, "DuplicateDataModel", que duplica cada entrada em um N vezes iterável. O código a seguir mostra como isso pode ser implementado.

function initializeScript()
{
    var newLinqMethod =
    {
        Duplicate : function *(n)
        {
            for (var val of this)
            {
                for (var i = 0; i < n; ++i)
                {
                    yield val;
                }
            };
        }
    };

    return [new host.namedModelParent(newLinqMethod, "DataModel.Models.Concepts.Iterable")];
}

Salve o código para DuplicateDataModel.js usando um aplicativo, como o bloco de notas.

Carregue o provedor de scripts JavaScript, se necessário, e carregue a extensão DuplicateDataModel.js.

0:000:x86> !load jsprovider.dll
0:000:x86> .scriptload C:\JSExtensions\DuplicateDataModel.js
JavaScript script successfully loaded from 'C:\JSExtensions\DuplicateDataModel.js'

Use o comando dx para testar a nova função Duplicate.

0: kd> dx -r1 Debugger.Sessions.First().Processes.First().Threads.Duplicate(2),d
Debugger.Sessions.First().Processes.First().Threads.Duplicate(2),d                 : [object Generator]
    [0]              : nt!DbgBreakPointWithStatus (fffff800`9696ca60) 
    [1]              : nt!DbgBreakPointWithStatus (fffff800`9696ca60) 
    [2]              : intelppm!MWaitIdle+0x18 (fffff805`0e351348) 
    [3]              : intelppm!MWaitIdle+0x18 (fffff805`0e351348) 
…

Consulte também

Objetos nativos do depurador em extensões JavaScript – Detalhes do objeto de depurador

Objetos nativos do depurador em extensões JavaScript – Considerações sobre design e teste

Script do Depurador javaScript

Exemplos de Scripts para Depurador JavaScript