Interna felsökningsobjekt i JavaScript-tillägg

Interna felsökningsobjekt representerar olika konstruktioner och beteenden i felsökningsmiljön. Objekten kan skickas till (eller hämtas i) JavaScript-tillägg för att ändra tillståndet för felsökningsprogrammet.

Exempel på felsökningsobjekt är följande.

  • Sittning
  • Trådar/tråd
  • Processer/process
  • Stackramar/Stackram
  • Lokala variabler
  • Moduler/modul
  • Nyttighet
  • Stat/län
  • Inställningar

Till exempel kan objektet host.namespace.Debugger.Utility.Control.ExecuteCommand användas för att skicka u-kommandot till felsökningsprogrammet med följande två rader JavaScript-kod.

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

Det här avsnittet beskriver hur du arbetar med vanliga objekt och innehåller referensinformation om deras attribut och beteenden.

Allmän information om hur du arbetar med JavaScript finns i Skript för JavaScript-felsökningsprogram. JavaScript-exempel som använder felsökningsobjekten finns i Exempelskript för JavaScript-felsökningsprogram. Information om hur du arbetar med inställningsobjekten finns i .settings (Set Debug Settings).

Om du vill utforska de objekt som är tillgängliga i en felsökningssession använder du kommandot dx (Display NatVis Expression). Du kan till exempel visa några av felsökningsobjekten på den översta nivån med det här dx-kommandot.

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   

Alla objekt som anges ovan är klickbara DML och kan rekurseras längre ned för att visa objektstrukturen för felsökningsprogrammet.

Utöka felsökningsprogrammet via datamodellen

Datamodellen för felsökning gör det möjligt att skapa ett gränssnitt för information om program och drivrutiner i Windows som har följande attribut.

  • Kan identifieras och organiseras – ett logiskt strukturerat namnutrymme kan efterfrågas med hjälp av dx-kommandot.
  • Kan frågas med LINQ – Detta möjliggör extrahering och sortering av data med hjälp av ett standardfrågespråk.
  • Kan utökas logiskt och konsekvent – Utökningsbar med hjälp av tekniker som beskrivs i det här avsnittet med skriptprovider för felsökningsprogram som Natvis och JavaScript.

Utöka ett felsökningsobjekt i JavaScript

Förutom att kunna skapa en visualiserare i JavaScript kan skripttillägg också ändra huvudbegreppen i felsökningsprogrammet – sessioner, processer, trådar, staplar, stackramar, lokala variabler – och till och med publicera sig själva som tilläggspunkter som andra tillägg kan använda.

I det här avsnittet beskrivs hur du utökar ett kärnkoncept i felsökningsprogrammet. Tillägg som har skapats för att delas bör följa riktlinjerna som presenteras i interna felsökningsobjekt i JavaScript-tillägg – Design- och testningsöverväganden.

Registrering av ett tillägg

Ett skript kan registrera det faktum att det tillhandahåller ett tillägg via en post i matrisen som returneras från metoden initializeScript.

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

Förekomsten av ett host.namedModelParent-objekt i den returnerade matrisen anger för felsökaren att ett visst prototypobjekt eller en ES6-klass (comProcessExtension i det här fallet) kommer att vara en överordnad datamodell till modellen som är registrerad under namnet Debugger.Models.Process.

Tilläggspunkter för felsökningsobjekt

Följande tilläggspunkter för felsökningsprogrammet är integrerade i felsökningsprogrammet och är tillgängliga för användning av skriptproviders som JavaScript.

Debugger.Models.Sessions: Listan över sessioner (mål) som felsökningsprogrammet är kopplat till

Debugger.Models.Session: En enskild session (mål) som felsökningsprogrammet är kopplat till (live-användarläge, KD osv.)

Debugger.Models.Processes: Listan över processer i en session

Debugger.Models.Threads: Listan över trådar i en process

Debugger.Models.Thread: En enskild tråd i en process (oavsett om användarläge eller kärnläge)

Debugger.Models.Stack: Trådens stack

Debugger.Models.StackFrames: Samlingen med bildrutor som utgör en stack

Debugger.Models.StackFrame: En enskild stack frame i en stack

Debugger.Models.LocalVariables: De lokala variablerna i en stackram

Debugger.Models.Parameters: Parametrarna för ett anrop inom en stackram

Debugger.Models.Module: En enskild modul inom adressutrymmet för en process

Ytterligare datamodellobjekt

Dessutom finns det några ytterligare datamodellobjekt som definieras av kärndatamodellen.

DataModel.Models.Intrinsic: Ett intrinsiskt värde (ordinaltal, flyttal osv.)

DataModel.Models.String: En sträng

DataModel.Models.Array: En intern matris

DataModel.Models.Guid: Ett GUID

DataModel.Models.Error: Ett felobjekt

DataModel.Models.Concepts.Iterable: Tillämpas på alla objekt som kan itereras

DataModel.Models.Concepts.StringDisplayable: Tillämpas på varje objekt som har en visningssträngkonvertering

Exempel på översikt över COM-felsökningsobjekttillägg

Låt oss ta ett exempel. Anta att du vill skapa ett felsökningstillägg för att visa information som är specifik för COM, till exempel den globala gränssnittstabellen (GIT).

Tidigare kan det finnas ett befintligt tillägg för felsökningsprogram med ett antal kommandon som ger ett sätt att komma åt saker om COM. Ett kommando kan visa processcentrerad information (till exempel den globala gränssnittstabellen). Ett annat kommando kan ge trådcentrerad information, till exempel vilken komponentkod som exekveras i. Du kan behöva känna till och läsa in ett andra debugger-tillägg för att utforska ytterligare aspekter av COM.

I stället för att ha en uppsättning svåra att identifiera kommandon kan ett JavaScript-tillägg ändra felsökningsprogrammets koncept för vad en process och en tråd är, för att lägga till den här informationen på ett sätt som är naturligt, utforskande och komposterbart med andra tillägg för felsökningsprogram.

Objekttillägg för felsökning i användar- eller kärnläge

Felsökningsprogrammet och felsökningsobjekten har olika beteende i användar- och kernelläge. När du skapar dina felsökningsmodellobjekt måste du bestämma vilka miljöer du ska arbeta i. Eftersom vi kommer att arbeta med COM i användarläge skapar och testar vi det här com-tillägget i användarläge. I andra situationer kan du kanske skapa ett JavaScript-felsökningsprogram som fungerar i både användar- och kernellägesfelsökning.

Skapa ett undernamnområde

När vi går tillbaka till vårt exempel kan vi definiera en prototyp eller ES6-klass, comProcessExtension som innehåller den uppsättning saker som vi vill lägga till i ett processobjekt.

Viktig Avsikten med undernamnområdet är att skapa ett logiskt strukturerat och naturligt utforskande paradigm. Undvik till exempel att dumpa orelaterade objekt i samma undernamnområde. Granska noggrant informationen som beskrivs i interna felsökningsobjekt i JavaScript-tillägg – Design- och testningsöverväganden innan du skapar ett undernamnområde.

I det här kodfragmentet skapar vi lägga till ett undernamnområde med namnet "COM" på det befintliga processfelsökarobjektet.

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

Implementering av namnområde

Skapa sedan objektet som implementerar undernamnområdet COM i en process.

Viktig Det kan finnas flera processer (oavsett om de är kopplade till sådana i användarläge eller under KD). Det här tillägget kan inte förutsätta att det aktuella tillståndet för felsökningsprogrammet är det som användaren avsåg. Någon kan fånga <vissaProcess>.COM i en variabel och ändra den, vilket kan leda till att presentera information från fel processkontext. Lösningen är att lägga till kod i tillägget så att varje instansiering håller reda på vilken process den är kopplad till. För det här kodexemplet skickas informationen via "this"-pekaren för egenskapen.

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

Implementeringslogik för den globala COM-gränssnittstabellen

För att avgränsa implementeringslogik för den globala COM-gränssnittstabellen tydligare definierar vi en ES6-klass, gipTable som abstraherar bort COM GIP-tabellen och en annan, globalObjects, vilket är vad som returneras från Den GlobalObjects() getter som definieras i kodfragmentet För namnområdesimplementering som visas ovan. All den här informationen kan döljas i stängningen av InitializeScript för att undvika att publicera någon av dessa interna uppgifter i namnområdet för felsökningsprogrammet.

// 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;
    }
}

Slutligen använder du host.namedModelRegistration för att registrera de nya COM-funktionerna.

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

Spara koden till GipTableAbstractor.js med hjälp av ett program, till exempel Anteckningar.

Här är processinformationen som är tillgänglig i användarläge innan du läser in det här tillägget.

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

Läs in JavaScript-tillägget.

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

Använd sedan dx-kommandot för att visa information om processen med hjälp av den fördefinierade @$curprocess.

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 *]

Den här tabellen är också programmatiskt tillgänglig via GIT-cookie.

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]

Utöka begrepp för felsökningsobjekt med LINQ

Förutom att kunna utöka objekt som process och tråd kan JavaScript även utöka begrepp som är associerade med datamodellen. Det går till exempel att lägga till en ny LINQ-metod till varje iterabel. Överväg ett exempeltillägg, "DuplicateDataModel" som duplicerar varje post i en iterabel N gånger. Följande kod visar hur detta kan implementeras.

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

Spara koden till DuplicateDataModel.js, använd ett program som Anteckningar.

Ladda skriptleverantören för JavaScript om det behövs och ladda sedan in tillägget 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'

Använd dx-kommandot för att testa den nya duplicerade funktionen.

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) 
…

Se även

Interna felsökningsobjekt i JavaScript-tillägg – Information om felsökningsobjekt

Interna felsökningsobjekt i JavaScript-tillägg – Design- och testningsöverväganden

Skript för JavaScript-felsökningsprogram

Exempelskript för JavaScript-felsökningsprogram