Programming/C++ & Unreal

C++ 메모리 덤프

장형이 2020. 2. 7. 19:00

개요

졸작으로 C++ 서버를 만들면서 제일 잣같았던 부분은, Try-Catch로 모든 Exception을 캐치 할 수 없다는 것이다.
특히 밥먹듯이 발생하던 "잘못된 메모리 접근"이 골치아팠는데, 그에 대한 해결 방법 중 하나인 Unhandled Exception Dump를 따는 법을 간단하게 적어보겠다.

 

Try-Catch가 안되는 Exception

void CreateException(int* a = nullptr) {
	a[100] = 100;
}

...

try
{
	CreateException(); // 강제로 Memory Access Exception 유발.
}

// Memory Access Exception은 Catch되지 않는다.
catch (...)
{
	cout << "Wow" << endl;
}
cout << "Success" << endl;
// 결과 : 아무 메세지가 출력되지 않음.

위 코드에서 try-catch를 사용하여 이상한 메모리 접근 시도를 감쌌는데도 아무 출력없이 프로그램이 죽어버리는 것을 볼 수 있다.

 

 

Unhandled Exception(관리 안되는 예외) 잡기

LONG WINAPI ExceptionCallBack(EXCEPTION_POINTERS* exceptionInfo) {
	cout << "Exception!" << endl;
	return 0L;
}

int main()
{
	// Exception 콜백을 등록해준다.
	SetUnhandledExceptionFilter(ExceptionCallBack);


	try
	{
		CreateException(); // 강제로 Memory Access Exception 유발.
	}

	// Memory Access Exception은 Catch되지 않는다.
	catch (...)
	{
		cout << "Wow" << endl;
	}
	cout << "Success" << endl;
}

// 결과 : "Exception!"이 출력됨

여러가지 방법이 더 있는 것으로 보이지만, 위 함수를 쓰면 간편하게 Unhandled Exception의 콜백 함수를 등록 할 수가 있다.
여기서 해당 Exception의 원인을 좀 더 트래킹하고 싶다면 아래처럼 하면 된다.

 

Mini Dump를 사용하기

#include <iostream>
#include <Windows.h>
#include <DbgHelp.h>

__declspec(noinline) void CreateException(int* a = nullptr) {
	a[100] = 100;
}

LONG WINAPI ExceptionCallBack(EXCEPTION_POINTERS* exceptionInfo) {

	MINIDUMP_EXCEPTION_INFORMATION info = { 0 };
	info.ThreadId = ::GetCurrentThreadId(); // Threae ID 설정
	info.ExceptionPointers = exceptionInfo; // Exception 정보 설정
	info.ClientPointers = FALSE;

	std::wstring stemp(L"test.dmp");

	HANDLE hFile = CreateFile(stemp.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	// 위에서 받은 내용들을 토대로 덤프 파일을 만든다.
	MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &info, NULL, NULL);

	cout << "Exception!" << endl;

	return 0L;
}

int main()
{
	// Exception 콜백을 등록해준다.
	SetUnhandledExceptionFilter(ExceptionCallBack);


	try
	{
		CreateException(); // 강제로 Memory Access Exception 유발.
	}

	// Memory Access Exception은 Catch되지 않는다.
	catch (...)
	{
		cout << "Wow" << endl;
	}
	cout << "Success" << endl;
}

// 결과 : "Exception!"이 출력됨

미니 덤프를 사용해서 덤프파일을 만든 뒤, 확인해보면 아래와 같은 방법으로 어디서 오류가 났는지 까지 찾아낼수가 있다.

 

 

C# 에도 비슷한 기능으로 Unhandledexception이 있는데 참고하면 좋을 듯 하다.