Compartir a través de


Objetos de depurador nativo en extensiones de JavaScript

Los objetos del depurador nativo representan diversas construcciones y comportamientos del entorno del depurador. Los objetos se pueden pasar a extensiones de JavaScript (o adquirirse en ellas) para manipular el estado del depurador.

Los objetos del depurador de ejemplo incluyen lo siguiente.

  • Sesión
  • Hilos / Hilo
  • Procesos y procesos
  • Marcos de pila/Marco de pila
  • Variables locales
  • Módulos / Módulo
  • Utilidad
  • Estado
  • Configuración

Por ejemplo, el objeto host.namespace.Debugger.Utility.Control.ExecuteCommand se puede usar para enviar el comando u al depurador con dos líneas de código JavaScript siguientes.

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

En este tema se describe cómo trabajar con objetos comunes y se proporciona información de referencia sobre sus atributos y comportamientos.

Para obtener información general sobre cómo trabajar con JavaScript, consulte Scripting del depurador de JavaScript. Para ver ejemplos de JavaScript que usan los objetos del depurador, consulte Scripts de ejemplo del depurador de JavaScript. Para obtener información sobre cómo trabajar con los objetos de configuración, vea .settings (Establecer configuración de depuración) .

Para explorar los objetos disponibles en una sesión del depurador, use el comando dx (Display NatVis Expression). Por ejemplo, puede mostrar algunos de los objetos del depurador de nivel superior con este 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 los elementos enumerados anteriormente son DML en los que se puede hacer clic y se pueden explorar en profundidad para ver la estructura de objetos del depurador.

Extensión del depurador a través del modelo de datos

El modelo de datos del depurador permite la creación de una interfaz para obtener información sobre las aplicaciones y los controladores de Windows que tienen los siguientes atributos.

  • Es reconocible y organizado: se puede consultar un espacio de nombres estructurado lógicamente mediante el comando dx.
  • Se puede consultar mediante LINQ: esto permite la extracción y ordenación de datos mediante un lenguaje de consulta estándar.
  • Puede extenderse de forma lógica y coherente: extensible mediante técnicas descritas en este tema con proveedores de scripting del depurador, como Natvis y JavaScript.

Extensión de un objeto Debugger en JavaScript

Además de poder crear un visualizador en JavaScript, las extensiones de script también pueden modificar los conceptos básicos del depurador: sesiones, procesos, subprocesos, pilas, marcos de pila, variables locales e incluso publicarse como puntos de extensión que pueden consumir otras extensiones.

En esta sección se describe cómo ampliar un concepto básico dentro del depurador. Las extensiones que se compilan para compartirse deben cumplir las directrices presentadas en Objetos de depurador nativo en extensiones de JavaScript: consideraciones de diseño y pruebas.

Registro de una extensión

Un script puede registrar el hecho de que proporciona una extensión a través de una entrada en la matriz devuelta desde el método initializeScript.

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

La presencia de un objeto host.namedModelParent dentro de la matriz devuelta indica al depurador que un objeto de prototipo determinado o una clase ES6 (comProcessExtension en este caso) va a ser un modelo de datos primario al modelo que se registra bajo el nombre Debugger.Models.Process.

Puntos de extensión del objeto depurador

Los siguientes puntos de extensión del depurador son integrales para el mismo y están disponibles para que los proveedores de scripts, como JavaScript, puedan usarlos.

Debugger.Models.Sessions: la lista de sesiones (destinos) a las que está asociado el depurador.

Debugger.Models.Session: una sesión individual (destino) a la que está asociado el depurador (modo de usuario activo, KD, etc....)

Debugger.Models.Processes: la lista de procesos dentro de una sesión

Debugger.Models.Threads: la lista de subprocesos dentro de un proceso

Debugger.Models.Thread: un subproceso individual dentro de un proceso (independientemente de si es modo usuario o modo kernel)

Debugger.Models.Stack: pila de un subproceso

Debugger.Models.StackFrames: la colección de marcos que componen una pila

Debugger.Models.StackFrame: un marco de pila individual dentro de una pila

Debugger.Models.LocalVariables: variables locales dentro de un marco de pila

Debugger.Models.Parameters: los parámetros de una llamada dentro de un marco de pila

Debugger.Models.Module: un módulo individual dentro del espacio de direcciones de un proceso

Objetos de modelo de datos adicionales

Además, hay algunos objetos de modelo de datos adicionales definidos por el modelo de datos principal.

DataModel.Models.Intrinsic: un valor intrínseco (ordinales, floats, etc...).

DataModel.Models.String: una cadena

DataModel.Models.Array: una matriz nativa

DataModel.Models.Guid: UN GUID

DataModel.Models.Error: un objeto de error

DataModel.Models.Concepts.Iterable: se aplica a cada objeto que se puede iterar

DataModel.Models.Concepts.StringDisplayable: se aplica a cada objeto que tiene una conversión de cadena para mostrar

Descripción general de la extensión de objeto del depurador COM de ejemplo

Veamos un ejemplo. Imagine que desea crear una extensión del depurador para mostrar información específica de COM, como la tabla de interfaz global (GIT).

En el pasado, podría haber habido una extensión del depurador existente con una serie de comandos que proporcionan un medio para acceder a aspectos de COM. Un comando podría mostrar información centrada en el proceso (la tabla de interfaz global por ejemplo). Otro comando puede proporcionar información centrada en subprocesos, como qué código de apartamento se está ejecutando dentro. Es posible que necesite conocer y cargar una segunda extensión del depurador para explorar otros aspectos de COM.

En lugar de tener un conjunto de comandos difíciles de detectar, una extensión de JavaScript puede modificar el concepto del depurador de lo que es un proceso y un subproceso, para agregar esta información de una manera natural, explorable y composable con otras extensiones del depurador.

Extensión de objeto del depurador de modo de usuario o kernel

El depurador y los objetos del depurador tienen un comportamiento diferente en el modo de usuario y kernel. Al crear los objetos del modelo del depurador, debe decidir en qué entornos va a trabajar. Dado que trabajaremos con COM en modo de usuario, crearemos y probaremos esta extensión com en modo de usuario. En otras situaciones, puede crear un JavaScript de depuración que funcionará en la depuración tanto en modo de usuario como de núcleo.

Creación de un subespacio de nombres

Volviendo a nuestro ejemplo, podemos definir un prototipo o una clase ES6, comProcessExtension que contiene el conjunto de cosas que queremos agregar a un objeto de proceso.

Importante La intención con el subespacio de nombres es crear un paradigma lógicamente estructurado y explorable de forma natural. Por ejemplo, evite colocar elementos no relacionados en el mismo subespacio de nombres. Revise cuidadosamente la información que se describe en Objetos de depurador nativo en extensiones de JavaScript: consideraciones de diseño y pruebas antes de crear un subespacio de nombres.

En este fragmento de código, añadimos un subespacio de nombres denominado "COM" al objeto del depurador de procesos 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);
    }
}

Implementación del espacio de nombres

A continuación, cree el objeto que implementa el subespacio de nombres COM en un proceso.

Importante Puede haber varios procesos (ya sea conectados a este tipo en modo de usuario o bajo KD). Esta extensión no puede suponer que el estado actual del depurador es el que el usuario pretendía. Alguien puede capturar <someProcess>.COM en una variable y modificarla, lo que puede provocar la presentación de información desde el contexto de proceso incorrecto. La solución consiste en agregar código en la extensión para que cada instanciación haga un seguimiento de a qué proceso está adjunta. Para este ejemplo de código, esta información se pasa a través del puntero "this" de la propiedad .

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 implementación para la tabla de interfaz global COM

Para separar esta lógica de implementación para la tabla de interfaz global COM más claramente, definiremos una clase ES6, gipTable que abstrae la tabla GIP COM y otra, globalObjects, que es lo que se devolverá del captador GlobalObjects() definido en el snip del código de implementación del espacio de nombres mostrado anteriormente. Todos estos detalles se pueden ocultar dentro de la clausura de initializeScript para evitar la publicación de estos detalles internos en el espacio de nombres del 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 último, use host.namedModelRegistration para registrar la nueva funcionalidad COM.

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

Guarde el código en GipTableAbstractor.js mediante una aplicación como el Bloc de notas.

Esta es la información del proceso disponible en modo de usuario antes de cargar esta extensión.

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

Cargue la extensión de JavaScript.

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

A continuación, use el comando dx para mostrar información sobre el proceso mediante el @$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 tabla también es accesible mediante programación a través de la cookie de 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]

Ampliando los conceptos de objetos del depurador con LINQ

Además de poder ampliar objetos como el proceso y el subproceso, JavaScript también puede ampliar los conceptos asociados al modelo de datos. Por ejemplo, es posible agregar un nuevo método LINQ a cada iterable. Considere una extensión de ejemplo, "DuplicateDataModel", que duplica cada elemento en un iterable N veces. En el código siguiente se muestra cómo se puede implementar.

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

Guarde el código en DuplicateDataModel.js mediante una aplicación como el Bloc de notas.

Cargue el proveedor de scripts de JavaScript si es necesario y luego cargue la extensión 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 el comando dx para probar la nueva función 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 también

Objetos de depurador nativo en extensiones de JavaScript: detalles del objeto debugger

Objetos de depurador nativo en extensiones de JavaScript: consideraciones de diseño y pruebas

Guionización del depurador de JavaScript

Ejemplos de scripts del depurador de JavaScript