Oggetti debugger nativi nelle estensioni JavaScript
Gli oggetti debugger nativi rappresentano vari costrutti e comportamenti dell'ambiente debugger. Gli oggetti possono essere passati a estensioni JavaScript (o acquisite in) per modificare lo stato del debugger.
Gli oggetti debugger di esempio includono quanto segue.
- sessione
- Thread/Thread
- Processi/Processi
- Frame stack /Stack Frame
- Variabili locali
- Moduli/Modulo
- Utilità
- State
- Impostazioni
Ad esempio, l'oggetto host.namespace.Debugger.Utility.Control.ExecuteCommand può essere usato per inviare il comando u al debugger con due righe di codice JavaScript.
var ctl = host.namespace.Debugger.Utility.Control;
var outputLines = ctl.ExecuteCommand("u");
Questo argomento descrive come usare oggetti comuni e fornisce informazioni di riferimento sugli attributi e sui comportamenti.
Per informazioni generali sull'uso di JavaScript, vedere Scripting del debugger JavaScript. Per esempi JavaScript che usano gli oggetti debugger, vedere Script di esempio del debugger JavaScript. Per informazioni sull'uso degli oggetti impostazioni, vedere .settings (Imposta impostazioni di debug).
Per esplorare gli oggetti disponibili in una sessione del debugger, usare il comando dx (Display NatVis Expression). Ad esempio, è possibile visualizzare alcuni degli oggetti debugger di primo livello con questo 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
Tutti gli elementi elencati sopra sono clickable DML e possono essere ripresi ulteriormente per visualizzare la struttura dell'oggetto debugger.
Estensione del debugger tramite il modello di dati
Il modello di dati del debugger consente la creazione di un'interfaccia per informazioni sulle applicazioni e sui driver in Windows con gli attributi seguenti.
- È individuabile e organizzato: è possibile eseguire query su uno spazio dei nomi logicamente strutturato usando il comando dx.
- Può essere eseguita una query usando LINQ: consente l'estrazione e l'ordinamento dei dati usando un linguaggio di query standard.
- Può essere logicamente e coerentemente esteso- Estendibile usando tecniche descritte in questo argomento con provider di script del debugger, ad esempio Natvis e JavaScript.
Estensione di un oggetto Debugger in JavaScript
Oltre ad essere in grado di creare un visualizzatore in JavaScript, le estensioni di script possono anche modificare i concetti principali del debugger- sessioni, processi, thread, stack, frame di stack, variabili locali e persino pubblicarsi come punti di estensione che altre estensioni possono usare.
Questa sezione descrive come estendere un concetto di base all'interno del debugger. Le estensioni compilate per essere condivise devono essere conformi alle linee guida presentate in Oggetti debugger nativi in Estensioni JavaScript - Considerazioni sulla progettazione e sui test.
Registrazione di un'estensione
Uno script può registrare il fatto che fornisce un'estensione tramite una voce nella matrice restituita dal metodo initializeScript.
function initializeScript()
{
return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process")];
}
La presenza di un oggetto host.namedModelParent all'interno della matrice restituita indica al debugger che una determinata classe prototipo o ES6 (comProcessExtension in questo caso) sarà un modello di dati padre al modello registrato sotto il nome Debugger.Models.Process.
Punti di estensione dell'oggetto debugger
I punti di estensione del debugger seguenti sono integrali al debugger e sono disponibili per essere usati dai provider di script, ad esempio JavaScript.
Debugger.Models.Sessions: elenco di sessioni (destinazioni) a cui è collegato il debugger
Debugger.Models.Session: sessione singola (destinazione) associata al debugger (modalità utente live, KD e così via...)
Debugger.Models.Processes: elenco di processi all'interno di una sessione
Debugger.Models.Threads: elenco di thread all'interno di un processo
Debugger.Models.Thread: un singolo thread all'interno di un processo (indipendentemente dalla modalità utente o kernel)
Debugger.Models.Stack: stack di un thread
Debugger.Models.StackFrames: raccolta di frame che costituiscono uno stack
Debugger.Models.StackFrame: un singolo frame stack all'interno di uno stack
Debugger.Models.LocalVariables: variabili locali all'interno di un frame dello stack
Debugger.Models.Parameters: parametri per una chiamata all'interno di un frame stack
Debugger.Models.Module: un singolo modulo all'interno dello spazio indirizzi di un processo
Altri oggetti modello di dati
Sono inoltre disponibili alcuni oggetti di modello di dati aggiuntivi definiti dal modello di dati di base.
DataModel.Models.Intrinseco: valore intrinseco (ordinali, float e così via...)
DataModel.Models.String: stringa
DataModel.Models.Array: matrice nativa
DataModel.Models.Guid: GUID
DataModel.Models.Error: oggetto error
DataModel.Models.Concepts.Iterable: applicato a ogni oggetto che è iterabile
DataModel.Models.Concepts.StringDisplayable: applicato a ogni oggetto con una conversione della stringa di visualizzazione
Panoramica dell'estensione dell'oggetto DEBUGGER COM di esempio
Si consideri un esempio. Si supponga di voler creare un'estensione del debugger per visualizzare informazioni specifiche per COM, ad esempio la tabella dell'interfaccia globale (GIT).
In passato, potrebbe esserci un'estensione del debugger esistente con un numero di comandi che forniscono un mezzo per accedere agli elementi relativi a COM. Un comando potrebbe visualizzare informazioni incentrate sul processo (la tabella dell'interfaccia globale per esempio). Un altro comando può fornire informazioni incentrate sul thread, ad esempio il codice dell'appartamento in esecuzione. Potrebbe essere necessario conoscere e caricare una seconda estensione del debugger per esplorare altri aspetti di COM.
Anziché avere un set di comandi difficili da individuare, un'estensione JavaScript può modificare il concetto di processo e un thread, per aggiungere queste informazioni in modo naturale, esplorabile e componibile con altre estensioni del debugger.
Estensione dell'oggetto Debugger in modalità utente o kernel
Il debugger e gli oggetti debugger hanno un comportamento diverso in modalità utente e kernel. Quando si creano gli oggetti modello del debugger, è necessario decidere in quali ambienti si funzionerà. Poiché si lavorerà con COM in modalità utente, verrà creata e testato questa estensione com in modalità utente. In altre situazioni, è possibile creare un debugger JavaScript che funzionerà sia nel debug in modalità utente che in modalità kernel.
Creazione di uno spazio dei nomi secondario
Tornare all'esempio, è possibile definire un prototipo o una classe ES6, comProcessExtension che contiene il set di elementi da aggiungere a un oggetto process.
Importante La finalità dello spazio dei nomi secondario consiste nel creare un paradigma logicamente strutturato e esplorabile. Ad esempio, evitare di scaricare elementi non correlati nello stesso spazio dei nomi secondario. Esaminare attentamente le informazioni descritte in Oggetti debugger nativi in Estensioni JavaScript - Considerazioni di progettazione e test prima di creare un sotto-spazio dei nomi.
In questo frammento di codice viene creato un sotto-spazio dei nomi denominato "COM" all'oggetto debugger di processo esistente.
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);
}
}
Implementazione dello spazio dei nomi
Creare quindi l'oggetto che implementa lo spazio dei nomi secondario COM in un processo.
Importante Possono essere presenti più processi (se collegati a tale in modalità utente o in KD). Questa estensione non può presupporre che lo stato presente del debugger sia quello che l'utente ha previsto. Qualcuno può acquisire <alcuniProcess.COM> in una variabile e modificarla, che può causare la presentazione di informazioni dal contesto del processo errato. La soluzione consiste nell'aggiungere codice nell'estensione in modo che ogni istanza tenga traccia del processo a cui è collegato. Per questo esempio di codice, queste informazioni vengono passate tramite il puntatore "this" della proprietà.
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);
}
}
Logica di implementazione per la tabella dell'interfaccia globale COM
Per separare la logica di implementazione per la tabella di interfaccia globale COM più chiaramente, si definirà una classe ES6, gipTable che astrae la tabella COM GIP e un'altra, globalObjects, ovvero ciò che verrà restituito dal getter GlobalObjects() definito nel snip del codice implementazione dello spazio dei nomi illustrato sopra. Tutti questi dettagli possono essere nascosti all'interno della chiusura di initializeScript per evitare di pubblicare uno di questi dettagli interni nello spazio dei nomi del debugger.
// 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;
}
}
Infine, usare host.namedModelRegistration per registrare la nuova funzionalità COM.
function initializeScript()
{
return [new host.namedModelParent(comProcessExtension, "Debugger.Models.Process"),
new host.namedModelRegistration(comNamespace, "Debugger.Models.ComProcess")];
}
Salvare il codice in GipTableAbstractor.js usando un'applicazione, ad esempio il blocco note.
Ecco le informazioni sul processo disponibili in modalità utente prima di caricare questa estensione.
0:000:x86> dx @$curprocess
@$curprocess : DataBinding.exe
Name : DataBinding.exe
Id : 0x1b9c
Threads
Modules
Caricare l'estensione JavaScript.
0:000:x86> .scriptload C:\JSExtensions\GipTableAbstractor.js
JavaScript script successfully loaded from 'C:\JSExtensions\GipTableAbstractor.js'
Usare quindi il comando dx per visualizzare informazioni sul processo usando l'oggetto @$curprocess predefinito.
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 *]
Questa tabella è anche accessibile a livello di codice tramite il 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]
Estensione dei concetti relativi a oggetti debugger con LINQ
Oltre a poter estendere oggetti come processo e thread, JavaScript può estendere anche i concetti associati al modello di dati. Ad esempio, è possibile aggiungere un nuovo metodo LINQ a ogni iterabile. Si consideri un'estensione di esempio, "DuplicateDataModel" che duplica ogni voce in un numero iterabile N volte. Il codice seguente illustra come è possibile implementare questa operazione.
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")];
}
Salvare il codice in DuplicateDataModel.js usando un'applicazione, ad esempio il blocco note.
Caricare il provider di script javaScript se necessario e quindi caricare l'estensione 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'
Usare il comando dx per testare la nuova funzione 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)
…
Vedi anche
Oggetti debugger nativi nelle estensioni JavaScript - Dettagli dell'oggetto debugger
Oggetti debugger nativi nelle estensioni JavaScript - Considerazioni sulla progettazione e sul test