共用方式為


JavaScript 延伸模組中的原生調試程序物件

原生調試程序物件代表調試程式環境的各種建構和行為。 物件可以傳遞至 (或取得) JavaScript 延伸模組,以操作偵錯工具的狀態。

調試程式物件的範例包括下列專案。

  • 會期
  • 線程/討論串
  • 程序處理/ 程序
  • 堆疊框架/堆疊框架
  • 局部變數
  • 模組/ 模組
  • 效用
  • 國家
  • 設定

例如,host.namespace.Debugger.Utility.Control.ExecuteCommand 物件可用來將 u 命令傳送至偵錯工具,並搭配下列兩行 JavaScript 程式碼。

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

本主題說明如何使用一般物件,並提供其屬性和行為的參考資訊。

如需使用 JavaScript 的一般資訊,請參閱 JavaScript 偵錯工具腳本。 如需使用偵錯工具物件的 JavaScript 範例,請參閱 JavaScript 偵錯工具範例腳本。 如需使用設定物件的相關資訊,請參閱 .settings (設定偵錯設定)。

若要探索偵錯工具會話中可用的物件,請使用 dx (顯示 NatVis 運算式) 命令。 例如,您可以使用此 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   

上面列出的所有項目都是可以點擊的 DML,並且可以進一步展開以檢視偵錯器物件結構。

透過資料模型擴充偵錯工具

偵錯工具資料模型允許建立介面,以取得 Windows 中具有下列屬性的應用程式和驅動程式相關資訊。

  • 可探索且有組織 - 可以使用 dx 指令查詢邏輯結構的名稱空間。
  • 可以使用 LINQ 查詢 - 這允許使用標準查詢語言提取和排序數據。
  • 可以邏輯且一致地擴充 - 使用本主題中所述的技術與偵錯工具腳本提供者 (例如 Natvis 和 JavaScript) 進行擴充。

在 JavaScript 中擴充偵錯工具物件

除了能夠在 JavaScript 中建立視覺化工具之外,腳本延伸模組還可以修改偵錯器的核心概念 - 會話、進程、執行緒、堆疊、堆疊框架、區域變數 - 甚至將自己發佈為其他延伸模組可以使用的擴充點。

本節說明如何在偵錯工具內擴充核心概念。 建置為共用的延伸模組應該符合 JavaScript 延伸模組 - 設計和測試考慮中的原生偵錯工具物件中呈現的指導方針。

註冊延伸模組

腳本可以透過 initializeScript 方法回傳的陣列中的項目來註冊其提供擴展功能的資訊。

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

傳回的陣列內存在 host.namedModelParent 物件,會向偵錯工具指出指定的原型物件或 ES6 類別 (在此案例中為 comProcessExtension) 將會是以名稱 Debugger.Models.Process 註冊之模型的父資料模型。

偵錯工具物件延伸點

下列偵錯工具延伸點是偵錯工具不可或缺的一部分,可供腳本提供者使用 ,例如 JavaScript。

Debugger.Models.Sessions:偵錯工具所附加的工作階段(目標)清單

Debugger.Models.Session:偵錯工具附加至的個別會話 (目標) (即時使用者模式、KD 等)

Debugger.Models.Processes:會話內的進程清單

Debugger.Models.Threads:進程內的執行緒清單

Debugger.Models.Thread:進程內的個別執行緒 (不論使用者或核心模式)

Debugger.Models.Stack:執行緒的堆疊

Debugger.Models.StackFrames:組成堆疊的框架集合

Debugger.Models.StackFrame:堆疊內的個別堆疊框架

Debugger.Models.LocalVariables:堆疊框架內的區域變數

Debugger.Models.Parameters:堆疊框架內呼叫的參數

Debugger.Models.Module:進程位址空間內的個別模組

其他資料模型物件

此外,還有一些由核心資料模型定義的其他資料模型物件。

DataModel.Models.Intrinsic:內在值(序數、浮點數等...

DataModel.Models.String:字串

DataModel.Models.Array:原生陣列

DataModel.Models.Guid:GUID

DataModel.Models.Error:錯誤物件

DataModel.Models.Concepts.Iterable:套用至每個可迭代的物件

DataModel.Models.Concepts.StringDisplayable:套用至具有顯示字串轉換的每個物件

範例 COM 偵錯工具物件延伸模組概觀

我們來看一個範例。 假設您想要建立偵錯工具延伸模組,以顯示 COM 特有的資訊,例如全域介面資料表 (GIT) 。

過去,可能會有現有的偵錯工具延伸模組,其中包含許多命令,可提供存取 COM 相關專案的方法。 一個命令可能會顯示以進程為中心的資訊(例如廣域介面表)。 另一個命令可能會提供以執行緒為中心的資訊,例如在其中執行的 Apartment 程式碼。 您可能需要瞭解並載入第二個偵錯工具延伸模組,才能探索 COM 的其他層面。

JavaScript 延伸模組可以修改偵錯工具的進程和執行緒概念,以自然、可探索且可與其他偵錯工具延伸模組組合的方式新增此資訊,而不是有一組難以探索的命令。

使用者或核心模式偵錯工具物件延伸模組

偵錯工具和偵錯工具物件在使用者和核心模式中具有不同的行為。 當您建立偵錯工具模型物件時,您必須決定要在哪些環境中工作。 因為我們將在使用者模式中使用 COM,所以我們將在使用者模式中建立和測試此 com 延伸模組。 在其他情況下,您可以建立偵錯工具 JavaScript,以同時適用於使用者和核心模式偵錯。

建立子命名空間

回到我們的範例,我們可以定義一個原型或 ES6 類別 comProcessExtension ,其中包含我們想要新增至流程物件的一組內容。

重要 子命名空間的目的是建立邏輯結構化且自然可探索的範式。 例如,避免將不相關的項目放置到相同的子命名空間中。 在建立子命名空間之前,請仔細檢閱 JavaScript 延伸模組中的原生偵錯工具物件 - 設計和測試考量 中所討論的資訊。

在此程式碼片段中,我們會建立將名為 '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);
    }
}

命名空間實作

接下來,建立在進程上實作子命名空間 COM 的物件。

重要 可以有多個程序(無論是在使用者模式中附加到此程序,還是在 KD 下附加到此程序)。 此延伸模組無法假設偵錯工具的目前狀態是使用者想要的狀態。 有人可以在變數中擷取 <someProcess>.COM 並對其進行修改,這可能會導致從錯誤的進程內容呈現資訊。 解決方案是在擴充套件中新增程式碼,以便每個實例化都會追蹤它附加到哪個程序。 針對此程式碼範例,此資訊會透過屬性的 'this' 指標傳遞。

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

COM 全域介面數據表的實作邏輯

為了更清楚地區分 COM 全域介面表的實作邏輯,我們將定義一個 ES6 類別 gipTable ,它抽象化了 COM GIP 表,以及另一個 globalObjects,這是從上面所示的命名空間實作程式碼片段中定義的 GlobalObjects() getter 傳回的內容。 所有這些詳細數據都可以隱藏在 initializeScript 的閉包內,以避免將任何這些內部詳細數據發佈到偵錯工具命名空間中。

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

最後,使用 host.namedModelRegistration 來註冊新的 COM 功能。

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

使用記事本等應用程式將程式碼儲存到 GipTableAbstractor.js。

以下是載入此擴充功能之前在使用者模式中可用的進程資訊。

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

載入 JavaScript 擴充功能。

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

然後使用 dx 命令使用預先定義的 @$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 *]

此表格也可透過 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]

使用 LINQ 擴充偵錯工具物件概念

除了能夠擴展進程和執行緒等物件之外,JavaScript 還可以擴展與資料模型相關的概念。 例如,可以將新的 LINQ 方法新增至每個可疊代專案。 考慮一個示例擴展“DuplicateDataModel”,它會在可迭代的 N 次內複製每個條目。 以下程式碼顯示如何實現這一點。

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

使用記事本等應用程式將程式碼儲存到 DuplicateDataModel.js。

如有必要,請載入 JavaScript 腳本提供者,然後載入 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'

使用 dx 命令來測試新的 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) 
…

另請參閱

JavaScript 延伸模組中的原生偵錯工具物件 - 偵錯工具物件詳細資料

JavaScript 延伸模組中的原生偵錯工具物件 - 設計和測試考量

JavaScript 偵錯工具指令碼

JavaScript 偵錯工具範例腳本