Objetos de depurador nativos en extensiones de JavaScript
Los objetos de depurador nativos representan diversas construcciones y comportamientos del entorno del depurador. Los objetos se pueden pasar a extensiones de JavaScript (o adquiridas en) para manipular el estado del depurador.
Los objetos del depurador de ejemplo incluyen lo siguiente.
- Sesión
- Subprocesos/Subprocesos
- Procesos y procesos
- Marcos de pila/Marco de pila
- Variables locales
- Módulos o módulos
- Utilidad
- State
- 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 obtener 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 se pueden hacer clic en DML y se pueden recursar más abajo 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 ampliarse de forma lógica y coherente: extensible mediante técnicas descritas en este tema con proveedores de scripting de 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 otras extensiones pueden consumir.
En esta sección se describe cómo ampliar un concepto básico dentro del depurador. Las extensiones que se crean 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 de 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 prototipo determinado o una clase ES6 (comProcessExtension en este caso) va a ser un modelo de datos primario al modelo que está registrado bajo el nombre Debugger.Models.Process.
Puntos de extensión de objeto del depurador
Los siguientes puntos de extensión del depurador son integrales para el depurador y están disponibles para su uso por proveedores de scripts como JavaScript.
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 de un proceso
Debugger.Models.Thread: un subproceso individual dentro de un proceso (independientemente de si el usuario o el modo kernel)
Debugger.Models.Stack: la pila de un subproceso
Debugger.Models.StackFrames: 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 todos los objetos que se pueden iterar
DataModel.Models.Concepts.StringDisplayable: se aplica a cada objeto que tiene una conversión de cadena para mostrar
Introducción a 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 una extensión del depurador existente con una serie de comandos que proporcionan un medio para acceder a cosas sobre COM. Un comando podría mostrar información centrada en el proceso (por ejemplo, la tabla de interfaz global). Otro comando puede proporcionar información centrada en subprocesos, como qué código de apartamento se está ejecutando dentro. Es posible que tenga que 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 forma 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 de 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, es posible que pueda crear un JavaScript de depurador que funcione tanto en la depuración en modo de usuario como en modo kernel.
Creación de un subespacio de nombres
Volviendo al 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 volcar 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, se crea un subespacio de nombres denominado "COM" en el 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 (tanto si están conectados a este tipo en modo de usuario como en KD). Esta extensión no puede suponer que el estado actual del depurador es el que el usuario ha previsto. Alguien puede capturar <someProcess.COM> en una variable y modificarla, lo que puede provocar la presentación de información del contexto de proceso incorrecto. La solución consiste en agregar código en la extensión para que cada instancia realice un seguimiento del proceso al que está asociado. 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 de la tabla de interfaz global COM con mayor claridad, 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 de código de implementación de espacio de nombres que se muestra anteriormente. Todos estos detalles se pueden ocultar dentro del cierre de initializeScript para evitar la publicación de cualquiera 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]
Extensión de 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 con el 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 todas las entradas de una N veces iterable. 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 scripting de JavaScript si es necesario y, a continuación, 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 nativos en extensiones de JavaScript: detalles del objeto debugger
Objetos de depurador nativos en extensiones de JavaScript: consideraciones de diseño y pruebas