크래시 덤프 분석

릴리스 전에 모든 버그를 찾을 수 있는 것은 아닙니다. 즉, 릴리스 전에 예외를 throw하는 모든 버그를 찾을 수 있는 것은 아닙니다. 다행히 Microsoft는 개발자가 사용자가 검색한 예외에 대한 정보를 수집하는 데 도움이 되는 함수를 플랫폼 SDK에 포함시켰습니다. MiniDumpWriteDump 함수는 전체 프로세스 공간을 저장하지 않고 필요한 크래시 덤프 정보를 파일에 씁니다. 이 크래시 덤프 정보 파일을 미니덤프라고합니다. 이 기술 문서에서는 미니덤프를 작성하고 사용하는 방법에 대한 정보를 제공합니다.

미니덤프 작성

미니덤프를 작성하기 위한 기본 옵션은 다음과 같습니다.

  • 아무 작업도 수행하지 않습니다. 프로그램이 처리되지 않은 예외를 throw할 때마다 Windows에서 미니덤프를 자동으로 생성합니다. Windows XP부터 미니덤프 자동 생성을 사용할 수 있습니다. 사용자가 허용하는 경우 미니덤프는 WER(Windows 오류 보고)을 통해 개발자가 아닌 Microsoft로 전송됩니다. 개발자는 Windows 데스크톱 애플리케이션 프로그램을 통해 이러한 미니덤프에 액세스할 수 있습니다.

    WER을 사용하려면 다음이 필요합니다.

    • Authenticode를 사용하여 애플리케이션에 서명하는 개발자
    • 애플리케이션에는 모든 실행 파일 및 DLL에 유효한 VERSIONINFO 리소스가 있습니다.

    처리되지 않은 예외에 대한 사용자 지정 루틴을 구현하는 경우 예외 처리기에서 ReportFault 함수를 사용하여 자동화된 미니덤프를 WER에 보내는 것이 좋습니다. ReportFault 함수는 미니덤프를 WER에 연결하고 보내는 모든 문제를 처리합니다. WER에 미니덤프를 보내지 않는 것은 Windows용 게임의 요구 사항을 위반합니다.

    WER에 대한 자세한 내용은 Windows 오류 보고 참조하세요.

  • Microsoft Visual Studio Team System의 제품을 사용합니다. 디버그 메뉴에서 덤프 다른 이름으로 저장을 클릭하여 덤프의 복사본을 저장합니다. 로컬로 저장된 덤프를 사용하는 것은 사내 테스트 및 디버깅을 위한 옵션일 뿐입니다.

  • 프로젝트에 코드를 추가합니다. MiniDumpWriteDump 함수와 적절한 예외 처리 코드를 추가하여 미니덤프를 저장하고 개발자에게 직접 보냅니다. 이 문서에서는 이 옵션을 구현하는 방법을 보여 줍니다. 그러나 MiniDumpWriteDump 는 현재 관리 코드에서 작동하지 않으며 Windows XP, Windows Vista, Windows 7에서만 사용할 수 있습니다.

스레드로부터의 안전성

MiniDumpWriteDump 는 DBGHELP 라이브러리의 일부입니다. 이 라이브러리는 스레드로부터 안전하지 않으므로 MiniDumpWriteDump를 사용하는 모든 프로그램은 MiniDumpWriteDump를 호출하기 전에 모든 스레드를 동기화해야 합니다.

코드로 Minidump 작성

실제 구현은 간단합니다. 다음은 MiniDumpWriteDump를 사용하는 방법의 간단한 예입니다.

#include <dbghelp.h>
#include <shellapi.h>
#include <shlobj.h>

int GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{
    BOOL bMiniDumpSuccessful;
    WCHAR szPath[MAX_PATH]; 
    WCHAR szFileName[MAX_PATH]; 
    WCHAR* szAppName = L"AppName";
    WCHAR* szVersion = L"v1.0";
    DWORD dwBufferSize = MAX_PATH;
    HANDLE hDumpFile;
    SYSTEMTIME stLocalTime;
    MINIDUMP_EXCEPTION_INFORMATION ExpParam;

    GetLocalTime( &stLocalTime );
    GetTempPath( dwBufferSize, szPath );

    StringCchPrintf( szFileName, MAX_PATH, L"%s%s", szPath, szAppName );
    CreateDirectory( szFileName, NULL );

    StringCchPrintf( szFileName, MAX_PATH, L"%s%s\\%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp", 
               szPath, szAppName, szVersion, 
               stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, 
               stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond, 
               GetCurrentProcessId(), GetCurrentThreadId());
    hDumpFile = CreateFile(szFileName, GENERIC_READ|GENERIC_WRITE, 
                FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);

    ExpParam.ThreadId = GetCurrentThreadId();
    ExpParam.ExceptionPointers = pExceptionPointers;
    ExpParam.ClientPointers = TRUE;

    bMiniDumpSuccessful = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), 
                    hDumpFile, MiniDumpWithDataSegs, &ExpParam, NULL, NULL);

    return EXCEPTION_EXECUTE_HANDLER;
}


void SomeFunction()
{
    __try
    {
        int *pBadPtr = NULL;
        *pBadPtr = 0;
    }
    __except(GenerateDump(GetExceptionInformation()))
    {
    }
}

이 예제에서는 MiniDumpWriteDump기본 사용량과 이를 호출하는 데 필요한 최소 정보를 보여 줍니다. 덤프 파일의 이름은 개발자에게 달려 있습니다. 그러나 파일 이름 충돌을 방지하려면 애플리케이션의 이름 및 버전 번호, 프로세스 및 스레드 ID, 날짜 및 시간에서 파일 이름을 생성하는 것이 좋습니다. 이렇게 하면 미니덤프를 애플리케이션 및 버전별로 그룹화할 수도 있습니다. 미니덤프 파일 이름을 구분하는 데 사용되는 정보의 양을 결정하는 것은 개발자의 달려 있습니다.

이전 예제의 경로 이름은 GetTempPath 함수를 호출하여 임시 파일에 대해 지정된 디렉터리의 경로를 검색하여 생성되었다는 점에 유의해야 합니다. 이 디렉터리의 사용은 최소 권한의 사용자 계정에서도 작동하며 더 이상 필요하지 않은 후 미니덤프가 하드 드라이브 공간을 차지하지 못하게 합니다.

일일 빌드 프로세스 중에 제품을 보관하는 경우 필요한 경우 이전 버전의 제품을 디버그할 수 있도록 빌드에 대한 기호도 포함해야 합니다. 또한 기호를 생성하는 동안 전체 컴파일러 최적화를 기본 단계를 수행해야 합니다. 이 작업은 개발 환경에서 프로젝트의 속성을 열고 릴리스 구성에 대해 다음을 수행하여 수행할 수 있습니다.

  1. 프로젝트 속성 페이지의 왼쪽에서 C/C++를 클릭합니다. 기본적으로 일반 설정이 표시됩니다. 프로젝트 속성 페이지의 오른쪽에서 디버그 정보 형식을 Program Database(/Zi)로 설정합니다.
  2. 속성 페이지의 왼쪽에서 링커를 확장한 다음 디버깅을 클릭합니다. 속성 페이지의 오른쪽에서 디버그 정보 생성을 예(/DEBUG)로 설정합니다.
  3. 최적화를 클릭하고 참조를 E리미네이트 미참조 데이터(/OPT:REF)로 설정합니다.
  4. COMDAT 접기를 사용하도록 설정하여 중복 COMDAT(/OPT:ICF)를 제거합니다.

MSDN에는 MINIDUMP_EXCEPTION_INFORMATION 구조 및 MiniDumpWriteDump 함수에 대한 자세한 정보가 있습니다.

Dumpchk.exe 사용

Dumpchk.exe는 덤프 파일이 올바르게 생성되었는지 확인하는 데 사용할 수 있는 명령줄 유틸리티입니다. Dumpchk.exe에서 오류가 발생하면 덤프 파일이 손상되어 분석할 수 없습니다. Dumpchk.exe 사용에 대한 자세한 내용은 Dumpchk.exe를 사용하여 메모리 덤프 파일을 확인하는 방법을 참조하세요.

Dumpchk.exe는 Windows XP 제품 CD에 포함되어 있으며 Windows XP 제품 CD의 Support\Tools\ 폴더에서 Setup.exe를 실행하여 System Drive\Program Files\Support Tools\에 설치할 수 있습니다. Windows 하드웨어 개발자 중앙의 Windows 디버깅 도구에서 사용할 수 있는 디버깅 도구를 다운로드하고 설치하여 최신 버전의 Dumpchk.exe를가져올 수도 있습니다.

미니덤프 분석

분석을 위해 미니덤프를 여는 것은 만드는 것만큼 쉽습니다.

미니덤프를 분석하려면

  1. Visual Studio를 엽니다.
  2. 파일 메뉴에서 프로젝트 열기를 클릭합니다.
  3. 형식의 파일을 덤프 파일설정하고 덤프 파일로 이동하여 선택한 다음 열기를 클릭합니다.
  4. 디버거를 실행합니다.

디버거는 시뮬레이션된 프로세스를 만듭니다. 시뮬레이트된 프로세스는 충돌을 일으킨 명령에서 중단됩니다.

Microsoft 공용 기호 서버 사용

드라이버 또는 시스템 수준 크래시에 대한 스택을 얻으려면 Microsoft 공용 기호 서버를 가리키도록 Visual Studio를 구성해야 할 수 있습니다.

Microsoft 기호 서버에 대한 경로를 설정하려면

  1. 디버그 메뉴에서 옵션을 클릭합니다.
  2. 옵션 대화 상자에서 디버깅 노드를 열고 기호를 클릭합니다.
  3. 디버그할 때 기호를 수동으로 로드하지 않으려면 기호가 수동으로 로드되지 않은 경우에만 위의 위치를 검색해야 합니다.
  4. 원격 기호 서버에서 기호를 사용하는 경우 기호를 복사할 수 있는 로컬 디렉터리를 지정하여 성능을 향상시킬 수 있습니다. 이렇게 하려면 기호 서버에서 이 디렉터리로 캐시 기호의 경로를 입력합니다. Microsoft 공용 기호 서버에 연결하려면 이 설정을 사용하도록 설정해야 합니다. 원격 컴퓨터에서 프로그램을 디버깅하는 경우 캐시 디렉터리가 원격 컴퓨터의 디렉터리를 참조합니다.
  5. 확인을 클릭합니다.
  6. Microsoft 공용 기호 서버를 사용 중이므로 최종 사용자 사용권 계약 대화 상자가 나타납니다. 규약에 동의하고 기호를 로컬 캐시에 다운로드하려면 [예]를 클릭하십시오.

WinDbg를 사용하여 Minidump 디버깅

Windows 디버깅 도구의 일부인 디버거인 WinDbg를 사용하여 미니덤프를 디버그할 수도 있습니다. WinDbg를 사용하면 Visual Studio를 사용하지 않고도 디버그할 수 있습니다. Windows 디버깅 도구를 다운로드하려면 Windows 하드웨어 개발자 중앙Windows 디버깅 도구를 참조하세요.

Windows 디버깅 도구를 설치한 후 WinDbg에 기호 경로를 입력해야 합니다.

WinDbg에 기호 경로를 입력하려면

  1. 파일 메뉴에서 기호 경로를 클릭합니다.

  2. 기호 검색 경로 창에서 다음을 입력합니다.

    "srv\*c:\\cache\*https://msdl.microsoft.com/download/symbols;"

미니덤프와 함께 복사 보호 도구 사용

또한 개발자는 복사 보호 체계가 미니덤프에 어떤 영향을 미칠 수 있는지 알고 있어야 합니다. 대부분의 복사 보호 체계에는 자체의 descramble 도구가 있으며, MiniDumpWriteDump와 함께 이러한 도구를 사용하는 방법은 개발자에게 달려 있습니다.

요약

MiniDumpWriteDump 함수는 제품이 출시된 후 버그를 수집하고 해결하는 데 매우 유용한 도구가 될 수 있습니다. MiniDumpWriteDump를 사용하는 사용자 지정 예외 처리기를 작성하면 개발자가 정보 수집을 사용자 지정하고 디버깅 프로세스를 개선할 수 있습니다. 이 함수는 C++기반 프로젝트에서 사용할 수 있을 만큼 유연하며 프로젝트의 안정성 프로세스의 일부로 간주되어야 합니다.