撰寫分析延伸模組外掛程式以擴充 !analyze
您可以藉由撰寫分析延伸模組外掛程式來擴充 !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
指定此外掛程式想要參與錯誤檢查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,告知我們專案包含錯誤檢查碼。 卷標是在 extsfns.h) 中定義的 DEBUG_FLR_PARAM_TYPE 列舉 (值,也稱為 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);