Last active
July 20, 2018 08:24
-
-
Save hexagit/fbe0d86b9209c154caa0a1b062347aed to your computer and use it in GitHub Desktop.
アクセス違反でゲームエンジンが落ちないようにしよう!全部
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Windows.h> | |
#include <iostream> | |
#include <sstream> | |
#pragma comment(lib,"dbghelp.lib") | |
#include <DbgHelp.h> | |
// スコープの開始と終了を出力する | |
struct PrintScope | |
{ | |
// コンストラクタ | |
PrintScope(const char* name) | |
: _Name(name) | |
{ | |
std::cout << _Indent << "Start " << _Name << std::endl; | |
_Indent.push_back(' '); | |
} | |
// デストラクタ | |
~PrintScope() | |
{ | |
_Indent.pop_back(); | |
std::cout << _Indent << "End " << _Name << std::endl; | |
} | |
const char* _Name; | |
static std::string _Indent; | |
}; | |
std::string PrintScope::_Indent; | |
// 構造化例外時のログ出力とか | |
struct ExceptionUtils | |
{ | |
// インスタンス作成 | |
static ExceptionUtils* Get() | |
{ | |
static ExceptionUtils instance; | |
return &instance; | |
} | |
// コンストラクタ | |
ExceptionUtils() | |
{ | |
_ProcessHandle = GetCurrentProcess(); | |
// シンボルハンドラの初期化 | |
SymInitialize(_ProcessHandle, NULL, TRUE); | |
// StackWalkのためのコンテキスト取得 | |
RtlCaptureContext(&_Context); | |
// シンボル情報格納用バッファの初期化 | |
_pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, 10000); | |
_pSym->SizeOfStruct = 10000; | |
_pSym->MaxNameLength = 10000 - sizeof(IMAGEHLP_SYMBOL); | |
} | |
// デストラクタ | |
~ExceptionUtils() | |
{ | |
// 後処理 | |
SymCleanup(_ProcessHandle); | |
GlobalFree(_pSym); | |
} | |
// 例外コードを出力 | |
void PrintExceptionCode(unsigned int code) | |
{ | |
switch( code ) { | |
case EXCEPTION_ACCESS_VIOLATION: | |
std::cout << "[Exception ] アクセス違反" << std::endl; | |
break; | |
case EXCEPTION_INT_DIVIDE_BY_ZERO: | |
std::cout << "[Exception ] ゼロ除算" << std::endl; | |
break; | |
case EXCEPTION_STACK_OVERFLOW: | |
std::cout << "[Exception ] スタックオーバーフロー" << std::endl; | |
break; | |
default: | |
std::cout << "[Exception ] その他" << std::endl; | |
break; | |
} | |
} | |
// コールスタックを出力 | |
void PrintCallStack(PCONTEXT contexRecord) | |
{ | |
// スタックフレームの初期化 | |
STACKFRAME stackFlame; | |
ZeroMemory(&stackFlame, sizeof(stackFlame)); | |
#if defined(_AMD64_) | |
stackFlame.AddrPC.Offset = contexRecord->Rip; | |
stackFlame.AddrStack.Offset = contexRecord->Rsp; | |
stackFlame.AddrFrame.Offset = contexRecord->Rbp; | |
#elif defined(_X86_) | |
stackFlame.AddrPC.Offset = contexRecord->Eip; | |
stackFlame.AddrStack.Offset = contexRecord->Esp; | |
stackFlame.AddrFrame.Offset = contexRecord->Ebp; | |
#endif | |
stackFlame.AddrPC.Mode = AddrModeFlat; | |
stackFlame.AddrStack.Mode = AddrModeFlat; | |
stackFlame.AddrFrame.Mode = AddrModeFlat; | |
bool doOnce = false; | |
HANDLE threadHandle = GetCurrentThread(); | |
// スタックフレームを順に取得 | |
while( NextStackWalk(stackFlame, threadHandle) ) { | |
// (長くて見づらいので)一回目だけ、ファイル名と行数を出力 | |
if( !doOnce && (doOnce = true) ) PrintFileNameAndLine(stackFlame); | |
// 関数名を出力 | |
PrintFunctionName(stackFlame); | |
} | |
} | |
// スタックフレームの取得 | |
bool NextStackWalk(STACKFRAME& stackFlame, HANDLE threadHandle) | |
{ | |
// アプリがx64かx86とかで、指定する物が変わる | |
DWORD imageFileMachine = IMAGE_FILE_MACHINE_UNKNOWN; | |
#if defined(_AMD64_) | |
imageFileMachine = IMAGE_FILE_MACHINE_AMD64; | |
#elif defined(_X86_) | |
imageFileMachine = IMAGE_FILE_MACHINE_I386; | |
#endif | |
// スタックフレームの取得 | |
BOOL result = StackWalk( | |
imageFileMachine, | |
_ProcessHandle, | |
threadHandle, | |
&stackFlame, | |
&_Context, | |
NULL, | |
SymFunctionTableAccess, | |
SymGetModuleBase, | |
NULL); | |
// 成功したか? | |
return result && stackFlame.AddrFrame.Offset != 0; | |
} | |
// スタックフレームからファイル名と行数を出力 | |
void PrintFileNameAndLine(STACKFRAME& stackFlame) | |
{ | |
DWORD Disp; | |
IMAGEHLP_LINE line; | |
line.SizeOfStruct = sizeof(IMAGEHLP_LINE); | |
if( SymGetLineFromAddr(_ProcessHandle, stackFlame.AddrPC.Offset, &Disp, &line) ) { | |
std::cout << "[File Name ] " << line.FileName << "(" << line.LineNumber << ")" << std::endl; | |
} | |
} | |
// 次のスタックフレームから関数名を出力 | |
void PrintFunctionName(STACKFRAME& stackFlame) | |
{ | |
// アプリがx64かx86とかで、形が変わる | |
#if !defined(_IMAGEHLP_SOURCE_) && defined(_IMAGEHLP64) | |
DWORD64 Disp; | |
#else | |
DWORD Disp; | |
#endif | |
if( SymGetSymFromAddr(_ProcessHandle, stackFlame.AddrPC.Offset, &Disp, _pSym) ) { | |
std::cout << "[Call Stack] " << _pSym->Name << std::endl; | |
} | |
} | |
PIMAGEHLP_SYMBOL _pSym; | |
CONTEXT _Context; | |
HANDLE _ProcessHandle; | |
}; | |
// 例外を生成する | |
struct ExceptionFactory | |
{ | |
// アクセス違反を発生させる | |
static void AccessViolation() | |
{ | |
PrintScope scope("AccessViolation"); | |
*((int*)0) = 0; | |
} | |
// ゼロ除算を発生させる | |
static void DivideByZero() | |
{ | |
PrintScope scope("DivideByZero"); | |
int zero = 0; | |
// inf | |
std::cout << 1.0f / zero << std::endl; | |
// nan | |
std::cout << 0.0f / zero << std::endl; | |
// exception | |
std::cout << 1 / zero << std::endl; | |
} | |
// スタックオーバーフローを発生させる | |
static void StackOverflow() | |
{ | |
PrintScope scope("StackOverflow"); | |
struct RecursiveCall | |
{ | |
static void Function() | |
{ | |
Function(); | |
} | |
}; | |
RecursiveCall::Function(); | |
} | |
}; | |
// とくに意味のない適当な関数 | |
void Exec() | |
{ | |
PrintScope scope("Exec"); | |
// 例外がいっぱい | |
ExceptionFactory::AccessViolation(); | |
//ExceptionFactory::DivideByZero(); | |
//ExceptionFactory::StackOverflow(); | |
} | |
// 構造化例外時に呼ばれる | |
void SE_TransFunc(unsigned int code, struct _EXCEPTION_POINTERS *ep) | |
{ | |
PrintScope scope("SE_TransFunc"); | |
// 適当にログ出力 | |
std::cout << "*------ " << std::endl; | |
ExceptionUtils::Get()->PrintExceptionCode(code); | |
std::cout << std::endl; | |
ExceptionUtils::Get()->PrintCallStack(ep->ContextRecord); | |
std::cout << "*------ " << std::endl; | |
} | |
// メイン | |
int main(int argc, char *argv[]) | |
{ | |
// インスタンス作成と初期化 | |
ExceptionUtils::Get(); | |
// 構造化例外時に呼び出す関数を登録 | |
_set_se_translator(SE_TransFunc); | |
{ | |
PrintScope scope("Main"); | |
try { | |
PrintScope scope("Try"); | |
Exec(); | |
} | |
catch( ... ) { | |
PrintScope scope("Catch"); | |
} | |
} | |
// デバッグ実行で、終了しないように無限ループ | |
for(;;) Sleep(1); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment