다음을 통해 공유


!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 함수를 호출합니다.

참고 메타데이터 파일의 마지막 줄은 줄 바꿈 문자로 끝나야 합니다.

스켈레톤 예제

다음은 시작점으로 사용할 수 있는 기본 예제입니다.

  1. 여기에 표시된 _EFN_Analyze 함수를 내보내는 MyAnalyzer.dll 라는 DLL을 빌드합니다.

    #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;
    }
    
  2. 다음 항목이 있는 MyAnalyzer.alz라는 메타데이터 파일을 만듭니다.

    PluginId      MyPlugin
    DebuggeeClass Kernel
    BugCheckCode  0xE2
    

    참고 메타데이터 파일의 마지막 줄은 줄 바꿈 문자로 끝나야 합니다.

  3. 호스트와 대상 컴퓨터 간에 커널 모드 디버깅 세션을 설정합니다.

  4. 호스트 컴퓨터에서 c:\MyAnalyzer 폴더에 MyAnalyzer.dll 및 MyAnalyzer.alz를 배치합니다.

  5. 호스트 컴퓨터의 디버거에서 다음 명령을 입력합니다.

    .extpath+ c:\MyAnalyzer

    .충돌

  6. .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 인터페이스이고 pAnalysisIDebugFailureAnalysis2 인터페이스입니다. 앞의 기본 예제의 코드는 두 개의 인터페이스 포인터를 더 가져오는 방법을 보여 줍니다. Client->QueryInterface는 IDebugControl 인터페이스를 가져오고 pAnalysis->GetDebugFATagControlIDebugFAEntryTags 인터페이스를 가져옵니다.

오류 분석 항목, 태그 및 데이터 형식

분석 엔진은 DebugFailureAnalysis 개체를 만들어 특정 코드 오류와 관련된 데이터를 구성합니다. DebugFailureAnalysis 개체에는 각각 FA_ENTRY 구조로 표시되는 실패 분석 항목(FA 항목)의 컬렉션이 있습니다. 분석 확장 플러그 인은 IDebugFailureAnalysis2 인터페이스를 사용하여 이 FA 항목 컬렉션에 액세스합니다. 각 FA 항목에는 항목에 포함된 정보의 종류를 식별하는 태그가 있습니다. 예를 들어 FA 항목에는 태그 DEBUG_FLR_BUGCHECK_CODE 있을 수 있습니다. 이 태그는 항목에 버그 검사 코드가 포함되어 있음을 알려줍니다. 태그는 FA_TAG 열거형이라고도 하는 DEBUG_FLR_PARAM_TYPE 열거형(extsfns.h에 정의됨)의 값입니다.

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 개체 및 DebugFailureAnalysisTags 개체를 보여 주는 다이어그램

DebugFailureAnalysis 개체에는 특정 분석 세션에 속하는 FA 항목 컬렉션이 있습니다. 연결된 DebugFailureAnalysisTags 개체에는 동일한 분석 세션에서 사용하는 태그만 포함하는 태그 속성 컬렉션이 있습니다. 앞의 다이어그램에서 알 수 있듯이 분석 엔진에는 분석 세션에서 일반적으로 사용할 수 있는 대규모 태그 집합에 대한 제한된 정보를 포함하는 전역 태그 테이블이 있습니다.

일반적으로 분석 세션에서 사용되는 대부분의 태그는 표준 태그입니다. 즉, 태그는 FA_TAG 열거형의 값입니다. 그러나 분석 확장 플러그 인은 사용자 지정 태그를 만들 수 있습니다. 분석 확장 플러그 인은 DebugFailureAnalysis 개체에 FA 항목을 추가하고 항목에 대한 사용자 지정 태그를 지정할 수 있습니다. 이 경우 사용자 지정 태그의 속성이 연결된 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 항목의 전체 컬렉션을 반복하는 방법을 보여줍니다. pAnalysisIDebugFailureAnalysis2 인터페이스에 대한 포인터라고 가정합니다. NULLNextEntry에 전달하여 첫 번째 항목을 가져옵니다.

PFA_ENTRY entry = pAnalysis->NextEntry(NULL);

while(NULL != entry)
{
   // Do something with the entry

   entry = pAnalysis->NextEntry(entry);
}

태그에는 이름과 설명이 있을 수 있습니다. 다음 코드에서 pAnalysisIDebugFailureAnalysis 인터페이스에 대한 포인터이고 pControlIDebugControl 인터페이스에 대한 포인터이며 pTagsIDebugFAEntryTags 인터페이스에 대한 포인터입니다. 이 코드는 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);

추가 정보

사용자 지정 분석 디버거 확장 작성

_EFN_Analyze

분석 확장 플러그 인에 대한 메타데이터 파일

IDebugFailureAnalysis2

IDebugFAEntryTags