您可以撰寫分析延伸模組外掛程式,以擴充 !analyze 調試程式命令的功能。 藉由提供分析延伸模組外掛程式,您可以參與錯誤檢查的分析,或以您自己的元件或應用程式特有的方式參與例外狀況分析。
當您撰寫分析延伸模組外掛程式時,您也會撰寫元數據檔案,描述您想要呼叫外掛程式的情況。 當 !analyze 執行時,它會找出、載入並執行適當的分析延伸模組外掛程式。
若要撰寫分析延伸模組外掛程式,並將其提供給 !analyze,請遵循下列步驟。
- 建立導出 _EFN_Analyze函式 的 DLL。
- 建立元數據檔案,其名稱與您的 DLL 相同,且擴展名為 .alz。 例如,如果您的 DLL 名為 MyAnalyzer.dll,您的元數據檔案必須命名為 MyAnalyzer.alz。 如需如何建立元數據檔案的資訊,請參閱 Analysis Extensions 的元數據檔案。 將元數據檔案放在與 DLL 相同的目錄中。
- 在調試程式中,使用 .extpath 命令將目錄新增至擴展名檔案路徑。 例如,如果您的 DLL 和元數據檔案位於名為 c:\MyAnalyzer 的資料夾,請輸入命令 .extpath+ c:\MyAnalyzer。
當 !analyze 命令在調試程式中執行時,分析引擎會尋找擴展名為 .alz 擴展名的元數據檔案路徑。 分析引擎會讀取元數據檔案,以判斷應該載入哪些分析延伸模組外掛程式。 例如,假設分析引擎正在執行,以回應錯誤檢查代碼 0xA IRQL_NOT_LESS_OR_EQUAL,並讀取名為 MyAnalyzer.alz 的元數據檔案,其中包含下列條目。
PluginId MyPlugin
DebuggeeClass Kernel
BugCheckCode 0xA
BugCheckCode 0xE2
此項目 BugCheckCode 0x0A 指定該外掛想要參與 Bug Check 0xA 的分析,因此分析引擎會載入 MyAnalyzer.dll(必須與 MyAnalyzer.alz 位於相同的目錄中),並呼叫其 _EFN_Analyze 函式。
注意 元數據檔的最後一行必須以換行符結尾。
基本架構範例
以下是您可以做為起點的基本架構範例。
建置名為 MyAnalyzer.dll 的 DLL,以匯出這裏顯示的 _EFN_Analyze 函式。
#include <windows.h> #define KDEXT_64BIT #include <wdbgexts.h> #include <dbgeng.h> #include <extsfns.h> extern "C" __declspec(dllexport) HRESULT _EFN_Analyze(_In_ PDEBUG_CLIENT4 Client, _In_ FA_EXTENSION_PLUGIN_PHASE CallPhase, _In_ PDEBUG_FAILURE_ANALYSIS2 pAnalysis) { HRESULT hr = E_FAIL; PDEBUG_CONTROL pControl = NULL; hr = Client->QueryInterface(__uuidof(IDebugControl), (void**)&pControl); if(S_OK == hr && NULL != pControl) { IDebugFAEntryTags* pTags = NULL; pAnalysis->GetDebugFATagControl(&pTags); if(NULL != pTags) { if(FA_PLUGIN_INITILIZATION == CallPhase) { pControl->Output(DEBUG_OUTPUT_NORMAL, "My analyzer: initialization\n"); } else if(FA_PLUGIN_STACK_ANALYSIS == CallPhase) { pControl->Output(DEBUG_OUTPUT_NORMAL, "My analyzer: stack analysis\n"); } else if(FA_PLUGIN_PRE_BUCKETING == CallPhase) { pControl->Output(DEBUG_OUTPUT_NORMAL, "My analyzer: prebucketing\n"); } else if(FA_PLUGIN_POST_BUCKETING == CallPhase) { pControl->Output(DEBUG_OUTPUT_NORMAL, "My analyzer: post bucketing\n"); FA_ENTRY_TYPE entryType = pTags->GetType(DEBUG_FLR_BUGCHECK_CODE); pControl->Output(DEBUG_OUTPUT_NORMAL, "The data type for the DEBUG_FLR_BUGCHECK_CODE tag is 0x%x.\n\n", entryType); } } pControl->Release(); } return hr; }建立名為 MyAnalyzer.alz 的中繼資料檔案,其中包含下列條目。
PluginId MyPlugin DebuggeeClass Kernel BugCheckCode 0xE2注意 元數據檔的最後一行必須以換行符結尾。
在主電腦與目標計算機之間建立內核模式偵錯會話。
在主計算機上,將 MyAnalyzer.dll 和 MyAnalyzer.alz 放在 c:\MyAnalyzer 資料夾中。
在主電腦上,於調試程式中輸入這些命令。
.extpath+ c:\MyAnalyzer
。崩潰
.crash 命令會在目標計算機上產生錯誤檢查0xE2 MANUALLY_INITIATED_CRASH,這會導致主計算機上的調試程序中斷。 錯誤檢查分析引擎(在主計算機上的調試程式中執行)會讀取 MyAnalyzer.alz,並看到 MyAnalyzer.dll 能夠參與分析錯誤檢查0xE2。 因此,分析引擎會載入 MyAnalyzer.dll,並呼叫其 _EFN_Analyze函式 。
確認您在除錯程式中看到類似下列的輸出。
* Bugcheck Analysis * * * ******************************************************************************* Use !analyze -v to get detailed debugging information. BugCheck E2, {0, 0, 0, 0} My analyzer: initialization My analyzer: stack analysis My analyzer: prebucketing My analyzer: post bucketing The data type for the DEBUG_FLR_BUGCHECK_CODE tag is 0x1.
上述調試程式輸出顯示分析引擎呼叫 _EFN_Analyze 函式四次:一次用於分析的每個階段。 分析引擎會傳遞 _EFN_Analyze 函式兩個介面指標。
用戶端 是 IDebugClient4 介面, pAnalysis 是 IDebugFailureAnalysis2 介面。 上述基本架構範例中的程式代碼示範如何取得兩個以上的介面指標。
Client->QueryInterface 會取得 IDebugControl 介面,並 pAnalysis->GetDebugFATagControl 取得 IDebugFAEntryTags 介面。
失敗分析條目、標記和數據類型
分析引擎會建立 DebugFailureAnalysis 物件,以組織與特定程式代碼失敗相關的數據。 DebugFailureAnalysis 物件具有失敗分析專案集合(FA 專案),每個專案都以FA_ENTRY結構表示。 分析延伸模組外掛程式會使用 IDebugFailureAnalysis2 介面來存取這個FA專案集合。 每個FA專案都有一個標記,可識別專案所包含的資訊種類。 例如,FA 專案可能會有 標籤DEBUG_FLR_BUGCHECK_CODE,告知我們專案包含錯誤檢查碼。 標籤是 DEBUG_FLR_PARAM_TYPE 列舉類型中的值(定義於 extsfns.h),也被稱為 FA_TAG 列舉。
typedef enum _DEBUG_FLR_PARAM_TYPE {
...
DEBUG_FLR_BUGCHECK_CODE,
...
DEBUG_FLR_BUILD_VERSION_STRING,
...
} DEBUG_FLR_PARAM_TYPE;
typedef DEBUG_FLR_PARAM_TYPE FA_TAG;
大部分 FA條目 都有相關聯的數據區塊。 FA_ENTRY 結構的 DataSize 成員會保存數據區塊的大小。 某些FA項目沒有相關聯的數據區塊;所有信息都會由標記傳達。 在這些情況下, DataSize 成員的 值為 0。
typedef struct _FA_ENTRY
{
FA_TAG Tag;
USHORT FullSize;
USHORT DataSize;
} FA_ENTRY, *PFA_ENTRY;
每個標記都有一組屬性:例如名稱、描述和數據類型。 DebugFailureAnalysis 物件與 DebugFailureAnalysisTags 物件相關聯,其中包含標記屬性的集合。 下圖說明此關聯。
DebugFailureAnalysis 物件具有屬於特定分析會話的 FA 項目 集合。 相關聯的 DebugFailureAnalysisTags 物件具有標籤屬性集合,其中只包含該相同分析會話所使用的標記。 如上圖所示,分析引擎具有一個全域標籤表格,其中保存了一組大型標籤的有限信息,這些標籤通常可用於分析會話。
分析會話所使用的大部分標記通常是標準標籤;也就是說,標籤是 FA_TAG 列舉中的值。 不過,分析延伸模組外掛程式可以建立自定義標籤。 分析延伸模組外掛程式可以將 FA專案 新增至 DebugFailureAnalysis 物件,並指定專案的自定義標記。 在此情況下,自定義標記的屬性會新增至相關聯 DebugFailureAnalysisTags 物件中的標記屬性集合。
您可以透過 IDebugFAEntry 標記介面來存取 DebugFailureAnalysisTags 。 若要取得 IDebugFAEntry 介面的指標,請呼叫 IDebugFailureAnalysis2 介面的 GetDebugFATagControl 方法。
每個標記都有一個數據類型屬性,您可以檢查以判斷失敗分析項目中數據的數據類型。 數據類型是以 FA_ENTRY_TYPE 列舉中的值表示。
下列程式代碼行會取得 DEBUG_FLR_BUILD_VERSION_STRING 標記的數據類型。 在此情況下,數據類型是DEBUG_FA_ENTRY_ANSI_STRING。 在程序代碼中, pAnalysis 是 IDebugFailureAnalysis2 介面的指標。
IDebugFAEntryTags* pTags = pAnalysis->GetDebugFATagControl(&pTags);
if(NULL != pTags)
{
FA_ENTRY_TYPE entryType = pTags->GetType(DEBUG_FLR_BUILD_VERSION_STRING);
}
如果失敗分析項目沒有數據區塊,則相關聯標籤的數據類型會是DEBUG_FA_ENTRY_NO_TYPE。
回想一下DebugFailureAnalysis物件有一個FA項目的集合。 若要檢查集合中的所有FA條目,請使用 NextEntry 方法。 下列範例示範如何遍歷整個FA條目集合。 假設 pAnalysis 是 IDebugFailureAnalysis2 介面的指標。 請注意,我們會將 NULL 傳遞至 NextEntry 來取得第一個條目。
PFA_ENTRY entry = pAnalysis->NextEntry(NULL);
while(NULL != entry)
{
// Do something with the entry
entry = pAnalysis->NextEntry(entry);
}
標籤可以有名稱和描述。 在下列程式代碼中, pAnalysis 是 IDebugFailureAnalysis 介面的指標, pControl 是 IDebugControl 介面的指標,而 pTags 是 IDebugFAEntryTags 介面的指標。 此程式代碼示範如何使用 GetProperties 方法來取得與 FA專案相關聯的標記名稱和描述。
#define MAX_NAME_LENGTH 64
#define MAX_DESCRIPTION_LENGTH 512
CHAR name[MAX_NAME_LENGTH] = {0};
ULONG nameSize = MAX_NAME_LENGTH;
CHAR desc[MAX_DESCRIPTION_LENGTH] = {0};
ULONG descSize = MAX_DESCRIPTION_LENGTH;
PFA_ENTRY pEntry = pAnalysis->NextEntry(NULL);
pTags->GetProperties(pEntry->Tag, name, &nameSize, desc, &descSize, NULL);
pControl->Output(DEBUG_OUTPUT_NORMAL, "The name is %s\n", name);
pControl->Output(DEBUG_OUTPUT_NORMAL, "The description is %s\n", desc);