[MFC] C++ MiniDumpWriteDump

반응형

프로그램 개발 중 프로그램 에러는 컴파일 에러와 런타임 에러가 있습니다. 이번 포스팅에서는 런타임 에러 발생 시 디버깅에 필요한 정보를 파일로 저장하는 방법을 알아보겠습니다.

 

1. 런타임 에러의 종류

구문오류는 컴파일 타임에 컴파일러가 알려주는데 아래와 같은 런타임 에러는 프로그램이 그냥 꺼져버립니다.

그래서 별도로 그 순간의 스냅샷 정보를 저장하지 않으면 디버깅이 어렵습니다.

런타임 에러 종류

런타임 에러의 분석과 해결을 위해서는 당시 메모리와 레지스터 등의 디버깅에 필요한 정보를 파일로 저장할 필요가 있습니다. 

https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis의 Crash Dump Analysis가 그 방법을 알려주고 있습니다. 프로젝트에 Minidump.h 와 Minidump.cpp를 추가하고 소스코드를 복사 후 붙여넣기한 뒤 약간의 수정 후 결과를 확인해 보았습니다.

 

2. 크래시 덤프 분석 코드 작성

CALLBACK 등록하기

SetUnhandledExceptionFilter() 를 호출하여 직접호출이 아닌 exception 발생 시 콜백으로 동작하도록 수정합니다.

 

 .dmp 파일 생성 경로 수정

실행파일과 같은 경로에 생성되도록 수정하였습니다.

App의 Initinstance()에 콜백함수를 등록하고

콜백 함수 등록

프로젝트 코드에 아래와 같이 0으로 나누는 코드를 삽입 후 실행해 보았습니다.(retCode 값이 0)

작성한 테스트 코드

테스트코드가 실행되자 프로그램이 멈추고 그냥 종료되었는데 아래와 같은. dmp 파일이 생성되었습니다.

생성된 덤프 파일

.dmp 파일을 더블클릭으로 실행하면 Dump summary의 Exception Information에 crash 원인이 정확하게 나오고 있습니다.

우측 Actions의 "Debug with Native Only"를 클릭하면 비주얼스튜디오가 디버깅모드로 실행되면서 에러 위치를 정확하게 표기해 줍니다. 이를 위해서는 실행파일 경로에 디버그 데이터베이스파일 .pdb가 있어야 합니다.

Dump Summary
디버깅에서 확인된 에러

 

3. 덤프파일 생성 예제 코드

제가 사용한 예제 코드를 붙입니다. 참고하시기 바랍니다.

 

Minidump.h

#pragma once

LONG CALLBACK GenerateDump(EXCEPTION_POINTERS* pExceptionPointers);
void Register_MiniDumpWriteDump(void);

Minidump.cpp

#include "pch.h"
#include "Minidump.h"

/* https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis */

#include <strsafe.h>
#include <DbgHelp.h>
#pragma comment (lib, "DbgHelp")

static BOOL _boIsRegistered = FALSE;

LONG CALLBACK GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{
    BOOL bMiniDumpSuccessful;
    CString cstrFilePath;
    TCHAR szPath[MAX_PATH];
    TCHAR szFileName[MAX_PATH];
    TCHAR* szAppName = _T("Noyecube");
    TCHAR* szVersion = _T("v1.0");
    DWORD dwBufferSize = MAX_PATH;
    HANDLE hDumpFile;
    SYSTEMTIME stLocalTime;
    MINIDUMP_EXCEPTION_INFORMATION ExpParam;

    GetLocalTime(&stLocalTime);

#if 0
    GetTempPath(dwBufferSize, szPath);

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

#
    StringCchPrintf(szFileName, MAX_PATH, _T("%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());

#else
    GetModuleFileName(NULL, szPath, dwBufferSize);
    cstrFilePath.Format("%s", szPath);
    cstrFilePath = cstrFilePath.Left(cstrFilePath.ReverseFind(_T('\\')));
   
    StringCchPrintf(szFileName, MAX_PATH, _T("%s\\%s-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp"),
        (LPCTSTR)cstrFilePath, szAppName, szVersion,
        stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay,
        stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond,
        GetCurrentProcessId(), GetCurrentThreadId());
#endif
    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 Register_MiniDumpWriteDump(void)
{
    if (TRUE == _boIsRegistered)
        return;

    _boIsRegistered = TRUE;

    SetUnhandledExceptionFilter(GenerateDump);
}

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

/* https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170 */
#if 0
// C2707.cpp
#include <windows.h>
#include <stdio.h>

LONG MyFilter(LONG excode)
{
    return (excode == EXCEPTION_ACCESS_VIOLATION ?
        EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);   // OK
}

LONG func(void)
{
    int x, y;
    return(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ?  // C2707
        EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);

    __try
    {
        y = 0;
        x = 4 / y;
        return 0;
    }

    __except (MyFilter(GetExceptionCode()))
    {
        return(GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ? // ok
            EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);
    }
}

int main()
{
    __try
    {
        func();
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf_s("Caught exception\n");
    }
}
#endif

 

MS의 공식 자료는 아래 링크에서 확인 가능합니다.

https://docs.microsoft.com/en-us/windows/win32/dxtecharts/crash-dump-analysis

 

 

Crash Dump Analysis - Win32 apps

This technical article provides info about how to write and use a minidump.

docs.microsoft.com

https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2707?view=msvc-170

 

Compiler Error C2707

Learn more about: Compiler Error C2707

learn.microsoft.com

 

이상으로 런타임 에러 발생시 에러 발생 위치를 디버깅 모드에서 확인할 수 있도록 Crash Dump를 생성하는 방법과 사용 방법에 대하여 알아보았습니다.

반응형

'프로그래밍 > C | C++' 카테고리의 다른 글

[MFC] Modeless Dialog 생성하기  (0) 2022.09.12
[MFC] Build Openssl Statically Linked Against Windows  (0) 2022.08.27
[MFC] 로그(Log) 출력 Logger  (0) 2022.08.12
[MFC] simdjson library 설치  (0) 2022.08.01
[MFC] TA-LIB 설치  (0) 2022.08.01