!analyze を拡張するための分析拡張機能プラグインの記述
分析拡張機能プラグインを記述することにより、!analyze デバッガー コマンドの機能を拡張できます。 分析拡張機能プラグインを提供することにより、独自のコンポーネントまたはアプリケーションに固有の方法でバグ チェックまたは例外の分析に参加できます。
分析拡張機能プラグインを記述するときは、プラグインを呼び出す状況を記述するメタデータ ファイルも記述します。 !analyze を実行すると、適切な分析拡張機能プラグインが検索、読み込み、実行されます。
分析拡張機能プラグインを記述し、!analyze で使用できるようにするには、次の手順に従います。
- _EFN_Analyze 関数をエクスポートする DLL を作成します。
- DLL と同じ名前と .alz 拡張子を持つメタデータ ファイルを作成します。 たとえば、DLL の名前が MyAnalyzer.dll の場合、メタデータ ファイルには MyAnalyzer.alz という名前を付ける必要があります。 メタデータ ファイルを作成する方法については、「分析拡張機能のメタデータ ファイル」をご覧ください。 メタデータ ファイルを 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
.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 関数を 4 回呼び出したことを示しています (分析の各フェーズに対して 1 回)。 分析エンジンは、_EFN_Analyze 関数の 2 つのインターフェイス ポインターを渡します。 Client は IDebugClient4 インターフェイスであり、pAnalysis は IDebugFailureAnalysis2 インターフェイスです。 前のスケルトンの例のコードは、さらに 2 つのインターフェイス ポインターを取得する方法を示しています。 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 インターフェイスへのポインターであるとします。 NextEntry に NULL を渡すことにより、最初のエントリを取得していることに注意してください。
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);