Sdílet prostřednictvím


Nativní objekty ladicího programu v rozšířeních JavaScriptu

Nativní objekty ladicího programu představují různé konstrukty a chování prostředí ladicího programu. Objekty lze předat (nebo získat v) javascriptových rozšířeních pro manipulaci se stavem ladicího programu.

Mezi příklady objektů ladicího programu patří následující.

  • Sezení
  • Vlákna / vlákno
  • Procesy / Proces
  • Zásobníkové rámce / Zásobníkový rámec
  • Místní proměnné
  • Moduly / Moduly
  • Užitnost
  • Stát
  • Nastavení

Například host.namespace.Debugger.Utility.Control.ExecuteCommand objekt lze použít k odeslání příkazu u do ladicího programu s následujícími dvěma řádky javascriptového kódu.

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

Toto téma popisuje, jak pracovat s běžnými objekty a poskytuje referenční informace o jejich atributech a chování.

Obecné informace o práci s JavaScriptem najdete v tématu Skriptování ladicího programu JavaScriptu. Příklady Jazyka JavaScript, které používají objekty ladicího programu, naleznete v části Ukázkové skripty ladicího programu Jazyka JavaScript. Informace o práci s objekty nastavení naleznete v části .settings (Nastavení ladicích nastavení).

Pokud chcete prozkoumat objekty dostupné v relaci ladicího programu, použijte příkaz dx (Display NatVis Expression). Pomocí tohoto příkazu dx můžete například zobrazit některé objekty ladicího programu nejvyšší úrovně.

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   

Všechny výše uvedené položky jsou kliknutelné DML a lze je dále procházet, aby se zobrazila struktura objektu ladicího programu.

Rozšíření ladicího programu prostřednictvím datového modelu

Datový model ladicího programu umožňuje vytvořit rozhraní pro informace o aplikacích a ovladačích ve Windows, které mají následující atributy.

  • Je zjistitelný a uspořádaný – logicky strukturovaný prostor názvů lze dotazovat pomocí příkazu dx.
  • Lze dotazovat pomocí LINQ- To umožňuje extrakci a řazení dat pomocí standardního dotazovacího jazyka.
  • Lze logicky a konzistentně rozšířit – rozšiřitelné pomocí technik popsaných v tomto tématu pomocí zprostředkovatelů skriptování ladicího programu, jako jsou Natvis a JavaScript.

Rozšíření objektu ladicího programu v JavaScriptu

Kromě vytváření vizualizérů v JavaScriptu mohou skriptová rozšíření také upravovat základní koncepty ladicího programu – relace, procesy, vlákna, zásobníky, snímky zásobníku, místní proměnné – a dokonce se publikovat jako rozšiřovací body, které mohou používat další rozšíření.

Tato část popisuje, jak rozšířit základní koncept v rámci ladicího programu. Rozšíření vytvořená tak, aby se sdílela, by měla odpovídat pokynům uvedeným v nativních objektech ladicího programu v rozšířeních JavaScriptu – Aspekty návrhu a testování.

Registrace rozšíření

Skript může zaregistrovat skutečnost, že poskytuje rozšíření jako položku pole vráceného metodou initializeScript.

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

Přítomnost objektu host.namedModelParent ve vráceném poli oznámí ladicímu programu, že prototyp objektu nebo třída ES6 (v tomto případě comProcessExtension) bude nadřazeným datovým modelem, který je registrován pod názvem Debugger.Models.Process.

Rozšíření objektu debuggeru

Následující body rozšíření ladicího programu jsou nedílnou součástí ladicího programu a jsou k dispozici pro použití poskytovateli skriptů, jako je JavaScript.

Debugger.Models.Sessions: Seznam relací (cílů), ke kterým je ladicí program připojený

Debugger.Models.Session: Jednotlivá relace (cíl), ke které je ladicí program připojený (živý uživatelský režim, KD atd.)

Debugger.Models.Processes: Seznam procesů v rámci relace

Debugger.Models.Threads: Seznam vláken v rámci procesu

Debugger.Models.Thread: Jednotlivé vlákno v rámci procesu (bez ohledu na to, jestli uživatelský nebo režim jádra)

Debugger.Models.Stack: Zásobník vlákna

Debugger.Models.StackFrames: Kolekce rámečků, které tvoří zásobník

Debugger.Models.StackFrame: Samostatný rámec zásobníku v rámci zásobníku

Debugger.Models.LocalVariables: Místní proměnné v zásobníkovém rámci

Debugger.Models.Parameters: Parametry volání v rámci zásobníkového rámce

Debugger.Models.Module: Jednotlivé moduly v adresních prostorech procesu

Další objekty datového modelu

Kromě toho existují další objekty datového modelu, které jsou definované základním datovým modelem.

DataModel.Models.Intrinsic: Vnitřní hodnota (ordinaly, float atd.)

DataModel.Models.String: Řetězec

DataModel.Models.Array: Nativní pole

DataModel.Models.Guid: GUID

DataModel.Models.Error: Objekt chyby

DataModel.Models.Concepts.Iterable: Používá se u každého objektu, který je iterovatelný.

DataModel.Models.Concepts.StringDisplayable: Používá se u každého objektu, který má převod zobrazovaného řetězce.

Přehled rozšíření objektu ladicího programu příkladu COM

Podívejme se na příklad. Představte si, že chcete vytvořit rozšíření ladicího programu pro zobrazení informací specifických pro com, jako je například globální tabulka rozhraní (GIT).

V minulosti mohlo existovat rozšíření ladicího programu s řadou příkazů, které poskytují prostředky pro přístup k informacím o COM. Jeden příkaz může zobrazit informace zaměřené na procesy (například globální tabulka rozhraní). Jiný příkaz může poskytnout informace zaměřené na vlákno, jako je například apartmánový kód, který se spouští uvnitř. Možná budete potřebovat seznámit se s druhým rozšířením ladicího programu a načíst ho, abyste mohli prozkoumat další aspekty modelu COM.

Místo toho, aby byly k dispozici těžko objevitelná příkazy, může rozšíření JavaScriptu změnit pojetí, co je proces a vlákno v ladicím programu, aby tyto informace přidalo způsobem, který je přirozený, přehledný a kombinovatelný s dalšími rozšířeními ladicího programu.

Rozšíření objektu ladicího programu v režimu uživatele nebo jádra

Ladicí program a objekty ladicího programu mají odlišné chování v režimu uživatele a jádra. Při vytváření objektů modelu ladicího programu musíte rozhodnout, ve kterých prostředích budete pracovat. Protože budeme pracovat s modelem COM v uživatelském režimu, vytvoříme a otestujeme toto rozšíření com v uživatelském režimu. V jiných situacích můžete vytvořit ladicí JavaScript, který bude pracovat v uživatelském i kernelovém režimu.

Vytvoření dílčího oboru názvů

Vrátíme se k našemu příkladu, můžeme definovat prototyp nebo třídu ES6 comProcessExtension , která obsahuje sadu věcí, které chceme přidat do objektu procesu.

Důležité Záměrem s podprostorem jmen je vytvořit logicky strukturované a přirozeně prozkoumatelné paradigma. Vyhněte se například dumpingu nesouvisejících položek do stejného dílčího oboru názvů. Pečlivě si projděte informace popisované v nativních objektech ladicího programu v rozšířeních JavaScriptu – Aspekty návrhu a testování před vytvořením dílčího oboru názvů.

V tomto fragmentu kódu vytvoříme do existujícího objektu ladicího programu procesu dílčí obor názvů s názvem COM.

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

Implementace oboru názvů

Dále vytvořte objekt, který v procesu implementuje dílčí obor názvů COM.

Důležité V uživatelském režimu nebo pod KD může existovat více procesů (ať už jsou k nim připojeny nebo ne). Toto rozšíření nemůže předpokládat, že současný stav ladicího programu je takový, jaký si uživatel přál. Někdo může zachytit <someProcess>.COM v proměnné a upravit ji, což může vést k prezentaci informací z kontextu nesprávného procesu. Řešením je přidat do rozšíření kód, aby každá instance sledovala, ke kterému procesu je připojený. Pro tento vzorový kód se tyto informace předávají prostřednictvím ukazatele "this" vlastnosti.

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

Logika implementace pro tabulku globálního rozhraní modelu COM

Abychom jasněji oddělili logiku implementace pro globální rozhraní tabulky COM, definujeme jednu třídu ES6, gipTable, která abstrahuje tabulku COM GIP, a další globalObjects, což je to, co se vrátí z getteru GlobalObjects() definovaného v části kódu implementace oboru názvů uvedené výše. Všechny tyto podrobnosti mohou být skryty uvnitř blokování funkce initializeScript, aby se zabránilo publikování jakýchkoli z těchto interních podrobností do jmenného prostoru ladicího programu.

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

Nakonec pomocí host.namedModelRegistration zaregistrujte novou funkci MODELU COM.

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

Uložte kód do GipTableAbstractor.js pomocí aplikace, jako je poznámkový blok.

Tady jsou informace o procesu, které jsou k dispozici v uživatelském režimu před načtením tohoto rozšíření.

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

Načtěte rozšíření JavaScriptu.

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

Potom pomocí příkazu dx zobrazte informace o procesu pomocí předdefinované @$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 *]

Tato tabulka je také prostřednictvím kódu programu přístupná prostřednictvím souboru 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]

Rozšíření konceptů objektů ladicího programu pomocí LINQ

Kromě možnosti rozšířit objekty, jako je proces a vlákno, může JavaScript také rozšířit koncepty spojené s datovým modelem. Můžete například přidat novou metodu LINQ ke každé iterovatelné. Představte si příklad rozšíření "DuplicateDataModel", které duplikuje každou položku v iterovatelném N krát. Následující kód ukazuje, jak to lze implementovat.

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

Uložte kód do DuplicateDataModel.js pomocí aplikace, jako je poznámkový blok.

V případě potřeby načtěte poskytovatele skriptů JavaScriptu a následně načtěte rozšíření 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'

Pomocí příkazu dx otestujte novou funkci 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) 
…

Viz také

Nativní objekty ladicího programu v rozšířeních JavaScriptu – Podrobnosti o objektu ladicího programu

Nativní objekty ladicího programu v rozšířeních JavaScriptu – Aspekty návrhu a testování

Skriptování ladicího programu JavaScriptu

Ukázkové skripty ladicího programu JavaScriptu