Condividi tramite


Oggetti debugger nativi nelle estensioni JavaScript

Gli oggetti debugger nativi rappresentano vari costrutti e comportamenti dell'ambiente del debugger. Gli oggetti possono essere passati o acquisiti nelle estensioni JavaScript per modificare lo stato del debugger.

Gli oggetti debugger di esempio includono quanto segue.

  • Sessione
  • Thread/Thread
  • Processi/Processo
  • Frame dello Stack/Frame dello Stack
  • Variabili locali
  • Moduli/Modulo
  • Utilità
  • stato
  • 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 seguenti.

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

In questo argomento viene descritto come utilizzare oggetti comuni e vengono fornite informazioni di riferimento sui relativi attributi e 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 delle impostazioni, consultare .settings (Set Debug Settings).

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 in precedenza sono DML cliccabili e possono essere esplorati ulteriormente per visualizzare la struttura degli oggetti del debugger.

Estensione del debugger tramite il modello di dati

Il modello di dati del debugger consente la creazione di un'interfaccia per informazioni su applicazioni e driver in Windows con gli attributi seguenti.

  • È individuabile e organizzato: è possibile eseguire query su uno spazio dei nomi strutturato logicamente usando il comando dx.
  • È possibile eseguire query usando LINQ: consente l'estrazione e l'ordinamento dei dati usando un linguaggio di query standard.
  • Può essere esteso in modo logico e coerente: 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 a poter creare un visualizzatore in JavaScript, le estensioni di script possono anche modificare i concetti di base del debugger, ovvero sessioni, processi, thread, stack, stack frame, variabili locali e persino pubblicarsi come punti di estensione utilizzabili da altre estensioni.

Questa sezione descrive come estendere un concetto di base all'interno del debugger. Le estensioni create per essere condivise devono essere conformi alle linee guida presentate in Native Debugger Objects in JavaScript Extensions - Design and Testing Considerations ( Considerazioni sulla progettazione e sul 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 un determinato oggetto prototipo o classe ES6 (comProcessExtension in questo caso) sarà un modello di dati padre al modello registrato con 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 l'uso da parte di provider di script come JavaScript.

Debugger.Models.Sessions: elenco di sessioni (destinazioni) a cui è collegato il debugger

Debugger.Models.Session: una singola sessione (destinazione) a cui è collegato il debugger (modalità utente attivo, 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: lo stack di un thread

Debugger.Models.StackFrames: raccolta di frame che costituiscono uno stack

Debugger.Models.StackFrame: Un singolo stack frame all'interno di uno stack

Debugger.Models.LocalVariables: variabili locali all'interno di uno stack frame

Debugger.Models.Parameters: parametri per una chiamata all'interno di uno stack frame

Debugger.Models.Module: un singolo modulo all'interno dello spazio indirizzi di un processo

Oggetti modello di dati aggiuntivi

Sono inoltre disponibili alcuni oggetti del modello di dati aggiuntivi definiti dal modello di dati di base.

DataModel.Models.Intrinsic: valore intrinseco (ordinali, float e così via...)

DataModel.Models.String: Una stringa

DataModel.Models.Array: matrice nativa

DataModel.Models.Guid: GUID (Identificatore Globale Unico)

DataModel.Models.Error: oggetto errore

DataModel.Models.Concepts.Iterable: applicato a ogni oggetto iterabile

DataModel.Models.Concepts.StringDisplayable: applicato a ogni oggetto con una conversione di stringhe di visualizzazione

Panoramica dell'estensione dell'oggetto debugger COM di esempio

Di seguito è riportato un esempio. Si supponga di voler creare un'estensione del debugger per visualizzare informazioni specifiche di COM, ad esempio la tabella dell'interfaccia globale (GIT).

In passato, potrebbe esserci un'estensione del debugger esistente con un certo 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, ad esempio). Un altro comando potrebbe fornire informazioni incentrate sui thread, ad esempio il codice apartment in esecuzione all'interno. Potrebbe essere necessario conoscere e caricare una seconda estensione del debugger per esplorare altri aspetti di COM.

Invece di avere un set di comandi difficili da individuare, un'estensione JavaScript può modificare il concetto di processo e thread del debugger, 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 lavorerà. Poiché si lavorerà con COM in modalità utente, verrà creata e testata 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

Tornando all'esempio, è possibile definire un prototipo o una classe ES6, comProcessExtension che contiene il set di elementi da aggiungere a un oggetto processo.

Importante Lo scopo dello spazio dei nomi secondario consiste nel creare un paradigma logicamente strutturato e esplorabile naturalmente. Ad esempio, evitare di inserire elementi non correlati nello stesso sotto-namespace. Esaminare attentamente le informazioni descritte in Native Debugger Objects in JavaScript Extensions - Design and Testing Considerations (Considerazioni sulla progettazione e sul test ) prima di creare uno spazio dei nomi secondario.

In questo frammento di codice viene aggiunto uno spazio dei nomi secondario 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 (collegati a tale in modalità utente o in KD). Questa estensione non può presupporre che lo stato attuale del debugger sia quello previsto dall'utente. Qualcuno può catturare <someProcess>.COM in una variabile e modificarlo, il che può portare a presentare informazioni dal contesto di processo errato. La soluzione consiste nell'aggiungere codice nell'estensione in modo che ogni istanza creata tenga traccia di quale processo è associato. 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 in modo più chiaro la logica di implementazione per la tabella dell'interfaccia globale COM, verrà definita una classe ES6, gipTable che astrae la tabella COM GIP e un altro globalObjects, ovvero ciò che verrà restituito dal getter GlobalObjects() definito nel snip del codice di implementazione dello spazio dei nomi illustrato in precedenza. 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 il file @$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 è accessibile anche 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 all'oggetto 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 elemento in un iterabile N volte. Nel codice seguente viene illustrato come 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 come Notepad.

Caricare il provider di scripting 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) 
…

Vedere anche

Oggetti debugger nativi nelle estensioni JavaScript - Dettagli oggetto debugger

Oggetti debugger nativi nelle estensioni JavaScript - Considerazioni sulla progettazione e sul test

JavaScript Debugger Scripting

Script di esempio del debugger JavaScript