4 min read

효율적으로 디버깅하기 SEH와 minidump

다음과 같은 소스 코드가있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <Windows.h>
#include <chrono>
#include <mutex>
#include <DbgHelp.h>
int main() {
    try {
        int* A = NULL;
        *= 100;
    }
    catch (… ) {
        std::cout << “hello world” << std::endl;
    }
}
cs

auto;”>이 예제는  access violation의 한 예이다.

해당 예제를 실행시키면 catch에서 hello world를 출력 시킬거같다.

하지만 이 프로그램을 실행 시키면..

우리가 생각대로 예외처리가 되지 않았다.

왜 이런문제가 발생할까? 이는 C++문법에서는 자극히 정상적인 문법이여서 발생한다.

해당 예외는 운영체계 레벨의 예외이지실제적으로 C++레벨에서 매우 정상적인 문법체계이다 그래서 나온게 (SEH)Structured Exception Handling이다.

SEH의 문법은 자극적 이해하기 쉽다.

다음과같이 try에 컴파일러 확장 문법인 __ 붙이면 끝난다.

EXCEPTION하는 방법에는 총 3가지가 있다. 하지만 이는 MSDN에 찾아보기 바란다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <Windows.h>
#include <chrono>
#include <mutex>
#include <DbgHelp.h>
int main() {
    __try {
        int* A = NULL;
        *= 100;
    }
    __except (EXCEPTION_EXECUTE_HANDLER) {
        std::cout << “hello world” << std::endl;
    }
}
cs

이를 실행시켜주면 매우 정상적으로 예외처리가 되는걸 볼수 있다.

하지만 문제 생기는 코드를 전부 __try __except를 담아두면 실제 프로그램내에서는

__try,__except로 떡칠 될것이고 성능도 다운된다.

또한 운영체계레벨의 코드는 어디서 예외가 빠르게 고쳐야 하는 코드이기때문에 다음과 같은 방법을 쓴다.

그래서 toplevel에서 Filter를 나의 필터로 교체한후에 예외처리가 불혀서 강제로 프로그램을 종료 시켜 DUMP를 남긴다.

Filter를 교체하는 법은 SetUnhandledExceptionFilter라는 함수가 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <Windows.h>
#include <chrono>
#include <mutex>
#include <DbgHelp.h>
LONG __stdcall topLvFilter(PEXCEPTION_POINTERS exception) {
    std::cout << “hello world” << std::endl;
    ::TerminateProcess(GetCurrentProcess(), 0);
    return 0;
}
int main() {
    SetUnhandledExceptionFilter(topLvFilter);
    int* A = NULL;
    *= 100;
}
cs

예외처리도 불가능한 코드가 발생시 topLvFilter필터를 걸어 해당 함수로 거치게 만든다.

거기에 minidup를 다음과같이 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>
#include <Windows.h>
#include <chrono>
#include <string>
#include <mutex>
#include <DbgHelp.h>
#pragma comment(lib, “dbghelp.lib”)
LONG __stdcall topLvFilter(PEXCEPTION_POINTERS exception) {
    MINIDUMP_EXCEPTION_INFORMATION infrom;
    infrom.ExceptionPointers = exception;
    infrom.ThreadId = GetCurrentThreadId();
    infrom.ClientPointers = false;
    std::wstring DumpFileName = L“dmpfile.dmp”;
    HANDLE hDumpFile = ::CreateFile(DumpFileName.c_str(),
        GENERIC_WRITE,
        FILE_SHARE_WRITE,
        nullptr,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL, nullptr);
    MiniDumpWriteDump(GetCurrentProcess(),
        GetCurrentProcessId(),
        hDumpFile,
        MiniDumpNormal,
        &infrom,
        nullptr,
        nullptr);
    ::TerminateProcess(GetCurrentProcess(), 0);
    return 0;
}
int main() {
    SetUnhandledExceptionFilter(topLvFilter);
    int* A = NULL;
    *= 100;
}
cs

 

해당 프로그램을 실행시키게 되면 문제가 난 시점의 dump파일이 만들어지게된다.

 

해당 덤프파일은 디버깅은 windbg나 vs로 하면된다.

이 방법은 클라이언트에 dmp가 남기때문에 서버를 하나 구축해서 해당 파일을 업로드해야한다.