Partilhar 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 para extensões JavaScript (ou adquiridas em) para manipular o estado do depurador.

Os objetos de depurador de exemplo incluem o seguinte.

  • Session
  • Threads/Thread
  • Processos/Processo
  • Stack Frames / Stack Frame
  • Variáveis locais
  • Módulos/Módulo
  • Utilitário
  • 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 de código JavaScript a seguir.

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 Scripts 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 (Exibir 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 do objeto 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 obter informações sobre aplicativos e drivers no Windows que têm os seguintes atributos.

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

Estendendo um objeto de depurador em JavaScript

Além de poder criar um visualizador no 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 principal dentro do depurador. As extensões criadas para serem compartilhadas devem estar em conformidade com as diretrizes apresentadas em Objetos Depuradores 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 neste caso) será um modelo de dados pai para o modelo que está registrado sob o nome Debugger.Models.Process.

Pontos de extensão de objeto do depurador

Os pontos de extensão do depurador a seguir são integrais ao 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 quadros 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 quadro 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.Intrínseco: um valor intrínseco (ordinais, floats etc...)

DataModel.Models.String: uma cadeia de caracteres

DataModel.Models.Array: uma matriz nativa

DataModel.Models.Guid: um 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 tem 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, pode haver uma extensão de depurador existente com vários comandos que fornecem um meio de acessar coisas 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 apartment está sendo executado. Talvez seja necessário saber 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ê estará trabalhando. 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 depurador que funcionará na depuração do modo de usuário e kernel.

Criando um subpasta

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 subpasta é criar um paradigma logicamente estruturado e naturalmente explorável. Por exemplo, evite despejar itens não relacionados no mesmo subpaspa. Examine cuidadosamente as informações discutidas em Objetos de Depurador Nativos em Extensões javaScript – Considerações de design e teste antes de criar um subpasta.

Neste snippet de código, criamos adicionar um subpasta 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 de namespace

Em seguida, crie o objeto que implementa o SUB-namespace COM em um processo.

Importante Pode haver vários processos (sejam anexados a eles no modo de usuário ou em KD). Essa extensão não pode assumir que o estado atual do depurador é o que o usuário pretendia. Alguém pode capturar <someProcess.COM> em uma variável e modificá-la, 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, essas informações são passadas por meio do ponteiro 'this' da 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 da tabela de interface global COM com mais clareza, 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 implementação de 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 de 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 *]

Essa 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 conceitos de objeto 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 todas as entradas 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 Duplicar.

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

Confira também

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

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

Script do Depurador JavaScript

Scripts de exemplo do depurador JavaScript