JavaScript 延伸模組中的原生偵錯工具物件
原生偵錯工具物件代表偵錯工具環境的各種建構和行為。 物件可以傳入 (,或在 javaScript 延伸模組) 取得,以操作偵錯工具的狀態。
偵錯工具物件的範例包括下列專案。
- 工作階段
- 執行緒/ 執行緒
- 進程/ 進程
- 堆疊框架/ 堆疊框架
- 區域變數
- 模組/ 模組
- 公用程式
- 狀態
- 設定
例如,host.namespace.Debugger.Utility.Control.ExecuteCommand 物件可用來使用下列兩行 JavaScript 程式碼,將 u 命令傳送至偵錯工具。
var ctl = host.namespace.Debugger.Utility.Control;
var outputLines = ctl.ExecuteCommand("u");
本主題描述如何使用一般物件,並提供其屬性和行為的參考資訊。
如需使用 JavaScript 的一般資訊,請參閱 JavaScript 偵錯工具腳本。 如需使用偵錯工具物件的 JavaScript 範例,請參閱 JavaScript 偵錯工具範例腳本。 如需使用設定物件的相關資訊,請參閱 .settings (設定偵錯設定) 。
若要探索偵錯工具會話中可用的物件,請使用 dx (Display NatVis Expression) 命令。 例如,您可以使用這個 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.內建:內建值 (序數、浮點數等...)
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 所定義的命名空間實作程式碼 snip 中所定義的內容。 所有這些詳細資料都可以隱藏在 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 延伸模組中的原生偵錯工具物件 - 偵錯工具物件詳細資料