Skip to content

Instantly share code, notes, and snippets.

@hexagit
Last active July 20, 2018 08:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hexagit/fbe0d86b9209c154caa0a1b062347aed to your computer and use it in GitHub Desktop.
Save hexagit/fbe0d86b9209c154caa0a1b062347aed to your computer and use it in GitHub Desktop.
アクセス違反でゲームエンジンが落ちないようにしよう!全部
#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