Condividi tramite


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

JavaScript Debugger Scripting

Script di esempio del debugger JavaScript