この例外を投げたのは誰だー スタックトレースで遊ぼう

まぁ、不慮の例外といいますか、突然飛んでくる例外というのはいろいろあるわけで。
そりゃ、例外なんて名前がついているから、例外的に突然飛んでくるのは当たり前だろうとこともありますね。
では、この例外をだれが投げたかを C++ で追跡することはできるのでしょうか。

    _ □□    _      ___、、、
  //_   [][]//   ,,-―''':::::::::::::::ヽヾヽ':::::/、  誰  投   こ
//  \\  //  /::::::::::::::::::::::::::::::i l | l i:::::::ミ  だ   げ   の
 ̄      ̄   ̄/ /:::::::::,,,-‐,/i/`''' ̄ ̄ ̄ `i::;|  あ  た   例
―`―--^--、__   /:::::::::=ソ   / ヽ、 /   ,,|/   っ  の   外
/f ),fヽ,-、     ノ  | 三 i <ニ`-, ノ /、-ニニ' 」') !! は    
  i'/ /^~i f-iノ   |三 彡 t ̄ 。` ソ ハ_゙'、 ̄。,フ | )         を
,,,     l'ノ j    ノ::i⌒ヽ;;|   ̄ ̄ / _ヽ、 ̄  ゙i )
  ` '' -  /    ノ::| ヽミ   `_,(_  i\_  `i ヽ、 ∧ ∧ ∧ ∧
     ///  |:::| ( ミ   / __ニ'__`i |  Y  Y Y Y Y
   ,-"        ,|:::ヽ  ミ   /-───―-`l  |  //     |
   |  //    l::::::::l\    ||||||||||||||||||||||/  |     // |
  /     ____.|:::::::|    、  `ー-―――┴ /    __,,..-'|
 /゙ー、,-―'''XXXX `''l::,/|    ー- 、__ ̄_,,-"、_,-''XXXXX |
/XX/ XXXXXXXXXX| |         _,  /ノXXXXXXXXXX|

この記事は、kernelvm advent calendar 用の記事でつ。

int f4(int a)
{
	throw my_exception("あらいを作りました");
}

int f3(int a)
{
	try
	{
		return f4(3) + 3;
	}
	catch(const my_exception & e)
	{
		//この例外を読んだのはだれたー
		//ここでだれが throw したか知りたい
	}
}

int f2(int a)
{
	return f3(2) + 2;
}

int f1(int a)
{
	return f2(1) + 1;
}

int main()
{
	return 0;
}

f4関数 で飛ばされる例外を f3関数 の catchで取得します。
この時、f4が例外を飛ばしたということを f3関数のcatchの中だけで知ることができるでしょうか。


結論から言うと、無理です。

     *      *
  *     +  無理です
     n ∧_∧ n
 + (ヨ(* ´∀`)E)
      Y     Y    *

orz
なんか、処理系とか特殊な条件とかが整うと、とれなくもないような気もしますが、、、
catch区の中だけでは、どこのだれがこの例外を飛ばしたか、追跡することができません。


これをするには、my_exception の時に stacktrace を保存しておく、ぐらいしかないです。

class my_exception 
{
public:
	my_exception(const char * inMessage)
	{
		//ここでスタックトレースを保存する.
		//例えばこんな感じ
		this->Stacktrace.recordTrace();
	}
};

スタックトレース

しかし、一つ問題があります。
exception のたびに毎回重い処理を走らせるとパフォーマンス上の問題が発生する場合があります。
ふつー exceptionってそんなに発生しないからいいような気もしますが、どっかのコーヒー言語かぶれの人とかが実装した処理系とかだとなんでも何でも exceptionをガシガシなげてくるみたいな実装もあるから油断は禁物です。

//Unix系でスタックトレースを取得する.
//関数にラップしているのは、この後の windowsとの互換性のためだよ
int backtrace(void** buffer , int n) const
{
	return ::backtrace(buffer , n);
}

StackWalk APIってゆーのがあるんだけど、ちょっと癖があっていまいち使いづらい。
ふつーに呼ぶとスレッドコンテキスト取得してとかいろいろあって非常に面倒だし、オーバーヘッドが怖いです。
#自分自信のスレッドコンテキストが取れないので、一度別スレッド作ってとかやって死ぬほど遅くなった記憶がある。。。


で、代わりになるのが RtlCaptureStackBackTrace API です。
http://msdn.microsoft.com/en-us/library/ff552119.aspx
#このRtlCaptureStackBackTrace APIが早いかどうか議論するところでしょうけど。

//windowsで簡単にスタックトレース
int backtrace(void** buffer , int n) const
{
	if (n >= 63)
	{
		n = 62;
	}
    return (int) RtlCaptureStackBackTrace(0,n,buffer,NULL);
}

RtlCaptureStackBackTrace は、windows XP 以降から使えるらしいAPIです。
Unix系だと backtrace みたく簡単にスタックトレースをかけることが可能です。
windows 2000ではないこともないらしいが、バグがあったりしてうまく動作しないことがあるらしいですが。


で、ここで問題になるのが、 windows2000を未サポートでもいいのかって問題。
10年以上も前のOSだから、そろそろサポートしなくてもいいんだろうけど、ビジネス的な問題で切れない場合もあるだろう。
そーゆーときは、スタックトレースは動かなくてもいいから、 windows 2000でメインの機能は動いてほしい、、、ぐらいで手を打つしかないのかも。
なんで、こんな風にダイナミックリンクで呼び出すようにする。

#include <windows.h>
#include <stdlib.h>


//dll読み込みヘルパー
class LoadLibraryHelper
{
private:
	//DLL インスタンス.
	HMODULE DllInstance;

public:
	LoadLibraryHelper()
	{
		this->DllInstance = NULL;
	}
	virtual ~LoadLibraryHelper()
	{
		if (this->DllInstance != NULL)
		{
			::FreeLibrary(this->DllInstance);
			this->DllInstance = NULL;
		}
	}
	bool Load( const char* inDLLName )//std読んでいない化石環境とかのために const char* で作る.
	{
		if (this->DllInstance != NULL)
		{
			return false;
		}
		this->DllInstance = ::LoadLibraryA(inDLLName);
		return this->DllInstance != NULL;
	}
	FARPROC GetProcAddress(const char* inProcName)
	{
		if (!this->DllInstance)
		{
			return NULL;
		}
		return ::GetProcAddress(this->DllInstance,inProcName);
	}
};

int backtrace(void** buffer , int n) 
{
	LoadLibraryHelper kernel32;
	if ( !kernel32.Load("kernel32.dll") )
	{
		return 0;
	}

	//スタックトレースAPI
	typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG FramesToSkip,ULONG FramesToCapture,PVOID *BackTrace,__out_opt  PULONG BackTraceHash);
	RtlCaptureStackBackTraceDef RtlCaptureStackBackTraceProc;

	RtlCaptureStackBackTraceProc = (RtlCaptureStackBackTraceDef)kernel32.GetProcAddress("RtlCaptureStackBackTrace");
	if (!RtlCaptureStackBackTraceProc)
	{
		return 0;
	}
	
	if (n >= 63)
	{
		n = 62;
	}
    return (int) RtlCaptureStackBackTraceProc(0,n,buffer,NULL);
}


//実行テスト用
int f2(int a)
{
	void* stack[50];
	int n = backtrace(stack , 50);
	
	for(int i = 0; i < n ; i ++)
	{
		printf("%p\r\n",stack[i]);
	}
	return a + 2;
}

int f1(int a)
{
	return f2(1) + 1;
}

int main()
{
	f1(1);
	return 0;
}


あー、めちゃコードが長くなったね。
ともあれ、これで、バックトレースはできた。
こいつを動かすと、スタックトレースを取得してくれる。

000000013FA84517
000000013FA84581
000000013FA84626
000000013FA8465F
000000013FA82D4C
000000013FA82B9E
0000000076CEF56D
0000000076E23021
続行するには何かキーを押してください . . .

うーん、 16進数だけだと味気ないね。
一応、この16進数からコードの位置をデバックシンボルをもとに、デバッガーを使うなり、手計算するなりすれば、元のコードの位置を求めることはできるんだけど、なんか、めんどい。


つーわけで、プログラムで自動的に復元ささせるようにしてみよう。
ちゃんとデバックシンボルが埋め込まれたバイナリなら、関数名はもとより、コードのファイル名と行数まで取得できるよ。

アドレスから関数名を求める

windows系だと、dbghelp.dll にある関数でごにょごにょする。
Unix系だと、dladdr abi::__cxa_demangle bfd とかでごにょごにょするみたい。

あるアドレス address が割り当てられて、それをもとにコードの行数までを復元するコードはこんな感じになる。
Windows系、Unix系ともに初期化が必要なので、めんどいね。
あと、すべてが終わったら、一応、解放処理もしないとだめだ。
なんで、 singleton な感じで実装してみた。
#最近のOSは賢いから、プロセス終わったらたいていのリソースは解放してくれるけどさー

//Windows系
//本当は dbghelp.dllからダイナミックロード(+ 32/64bit対応)をしているんだけど、長くなるので適当にはしょる.

#include <windows.h>
#include <stdlib.h>
#include <dbghelp.h>

class BackTrace
{
	//シンボルエンジンの準備ができているか
	bool IsSymbolEngineReady;
	//プロセスハンドル
	HANDLE	Process;

	//初期化
	BackTrace()
	{
		this->IsSymbolEngineReady = FALSE;

		//プロセスを記録.
		this->Process = ::GetCurrentProcess();

		//シンボルエンジンの初期化.
		//行番号付きのデータをロードしてねとお願いする.
		SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
        if (this->SymInitialize(this->Process, NULL, TRUE))
        {
			//シンボルエンジン準備完了
			this->IsSymbolEngineReady = true;
        }

		return ;
	}
public:
	//解放
	virtual ~BackTrace()
	{
		if (this->IsSymbolEngineReady)
		{
			this->SymCleanup(this->Process);
			this->IsSymbolEngineReady = false;
		}
	}

	//シンボルの解決
	void addressToSymbolString(void* address ,char * outBuffer , int len) const
	{
		if ( ! this->IsSymbolEngineReady )
		{
			//シンボルエンジンが準備できていない.
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#else
			_snprintf(outBuffer , len , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#endif
			return ;
		}

		//モジュール名
		IMAGEHLP_MODULE imageModule = { sizeof(IMAGEHLP_MODULE) };
		BOOL r = SymGetModuleInfo(this->Process ,(DWORD) address , &imageModule);
		if (!r)
		{
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#else
			_snprintf(outBuffer , len , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#endif
			return ;
		}

		//シンボル情報格納バッファ.
		IMAGEHLP_SYMBOL * imageSymbol;
		char buffer[MAX_PATH + sizeof(IMAGEHLP_SYMBOL) ] = {0};
		imageSymbol = (IMAGEHLP_SYMBOL*)buffer;
		imageSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
		imageSymbol->MaxNameLength = MAX_PATH;

		//関数名の取得...
		DWORD disp = 0;
		r = SymGetSymFromAddr(this->Process , (DWORD)address , &disp , imageSymbol );
		if (!r)
		{//関数名がわかりません.
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ %s @ ??? @ ???:???" ,address,imageModule.ModuleName );
			#else
			_snprintf(outBuffer , len , "0x%p @ %s @ ??? @ ???:???" ,address,imageModule.ModuleName );
			#endif
			return ;
		}

		//行番号の取得
		IMAGEHLP_LINE line ={sizeof(IMAGEHLP_LINE)};
		r = SymGetLineFromAddr(this->Process ,(DWORD) address , &disp , &line);
		if (!r)
		{//行番号が分かりません
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ %s @ %s @ %s+%d" ,address,
				imageModule.ModuleName , imageSymbol->Name, imageSymbol->Name,(int) ((char*)address - (char*)line.Address) );
			#else
			_snprintf(outBuffer , len , "0x%p @ %s @ %s @ %s+%d" ,address,imageModule.ModuleName , 
				imageModule.ModuleName , imageSymbol->Name, imageSymbol->Name,(int) ((char*)address - (char*)line.Address) );
			#endif
			return ;
		}

		//行番号がわかりました.
		#if __STDC_WANT_SECURE_LIB__
		_snprintf_s(outBuffer , len ,_TRUNCATE, "0x%p @ %s @ %s @ %s:%d" ,address,imageModule.ModuleName , imageSymbol->Name , line.FileName , line.LineNumber);
		#else
		_snprintf(outBuffer , len , "0x%p @ %s @ %s @ %s:%d" ,address,imageModule.ModuleName , imageSymbol->Name , line.FileName , line.LineNumber);
		#endif
	}

	//singletonの取得
	static const BackTrace* Get()
	{
		static BackTrace s;
		return &s;
	}
};

はい、ソース長いですね。
windowsはこんな感じで dbghelp.dllを使ってごにょごにょすればいろいろできます。


次は、Unix系を見てみましょう。

//Unix系


#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <cxxabi.h>		//デマングル
#include <bfd.h>		//行番号まで取り出す
						//入っていないなら yum install binutils-devel とかしてね!
						// #include <bfd.h> 自体をコメントアウトすると機能全体をOFFにできるよ!!

class BackTrace
{
	//シンボルエンジンの準備ができているか
	bool		IsSymbolEngineReady ;
	#ifdef __BFD_H_SEEN__
		bfd*		Abfd;
		asymbol**	Symbols;
		int			NSymbols;
		asection*	Section;
	#endif

	//初期化
	BackTrace()
	{
		#ifdef __BFD_H_SEEN__
			this->IsSymbolEngineReady = false;
			this->Abfd = NULL;
			this->Symbols = NULL;
			this->NSymbols = 0;

			//see http://0xcc.net/blog/archives/000073.html
			this->Abfd = bfd_openr("/proc/self/exe", NULL);
			if (!this->Abfd)
			{
				return ;
			}
			bfd_check_format(this->Abfd, bfd_object);

			int size = bfd_get_symtab_upper_bound(this->Abfd);
			if (size <= 0)
			{
				return ;
			}
			this->Symbols = (asymbol**) malloc(size);
			if (!this->Symbols)
			{
				return ;
			}
			this->NSymbols = bfd_canonicalize_symtab(this->Abfd, this->Symbols);
			if (!this->NSymbols)
			{
				return ;
			}
			this->Section = bfd_get_section_by_name(this->Abfd, ".debug_info");
			if (!this->Section)
			{
				return ;
			}

			//シンボルエンジンの初期化完了
			this->IsSymbolEngineReady = true;
		#else
			//シンボルエンジンは利用できない!!
			this->IsSymbolEngineReady = false;
		#endif
	}
public:
	//解放
	virtual ~BackTrace()
	{
		#ifdef __BFD_H_SEEN__
			if (this->Symbols)
			{
				free(this->Symbols);
				this->Symbols = NULL;
			}
			if (this->Abfd)
			{
				bfd_close (this->Abfd);
				this->Abfd = NULL;
			}
		#endif
	}

	//シンボルの解決
	void addressToSymbolString(void* address ,char * outBuffer , int len) const
	{
		//backtrace_symbols で一発、、、なんてことをすると demangle されない場合があるそうな。
		//だから、dladdr でバラしていく。
		//see http://d.hatena.ne.jp/syuu1228/20100215/1266262848
		Dl_info info;
		if (! dladdr(address, &info) ) 
		{
			snprintf(outBuffer ,len , "0x%p @ ??? @ ??? @ ???:???" ,address );
			return ;
		}
		if (!info.dli_sname)
		{
			snprintf(outBuffer ,len , "0x%p @ %s @ ??? @ ???:???" ,address , info.dli_fname );
			return ;
		}
		if (!info.dli_saddr)
		{
			snprintf(outBuffer ,len , "0x%p @ %s @ %s @ ???:???" ,address , info.dli_fname , info.dli_sname );
			return ;
		}

		//デマングルして関数名を読める形式に
		int status = 0;
		char * demangled = abi::__cxa_demangle(info.dli_sname,0,0,&status);

		//シンボルエンジンは使えるの?
		if (!this->IsSymbolEngineReady)
		{
			snprintf(outBuffer ,len , "0x%p @ %s @ %s @ %s+0x%p" ,
					address , info.dli_fname , (demangled ? demangled : info.dli_sname),
											   (demangled ? demangled : info.dli_sname), 
											   (unsigned int) ((char *)address - (char *)info.dli_saddr) );
			free(demangled);
			return ;
		}
		#ifdef __BFD_H_SEEN__
			//ファイル名と行数を求める.
			const char* filename = NULL;
			const char* functionname = NULL;
			unsigned int line = 0;

			int found = bfd_find_nearest_line(this->Abfd, this->Section, this->Symbols,
												(long)address,
												&filename,
												&functionname,
												&line);
			if (found && filename != NULL && functionname != NULL) 
			{
				snprintf(outBuffer ,len , "0x%p @ %s @ %s @ %s+0x%p" ,
						address , info.dli_fname , (demangled ? demangled : info.dli_sname),
												    (demangled ? demangled : info.dli_sname),
											        (unsigned int) ((char *)address - (char *)info.dli_saddr) );
				free(demangled);
				return ;
			}

			snprintf(outBuffer ,len , "0x%p @ %s @ %s @ %s:%d" ,
					address , info.dli_fname , (demangled ? demangled : info.dli_sname), filename , line );
			free(demangled);
		#endif

		return ;
	}

	//singletonの取得
	static const BackTrace* Get()
	{
		static BackTrace s;
		return &s;
	}
};


はい、こちらもソースが長いですね。
本当は Unix系も動的に読み込むようにしたほうがいいんだろうけど、、、だれか直して!!


さて、このコードを stacktrace した結果に対して実行すると、以下のようになる。

0x000000013FC9450B @ stacktrace @ f3 @ c:\users\rti\documents\my dropbox\stacktr
ace\stacktrace\stacktrace.cpp:636

アドレスだけだったのが、モジュール名、関数名、ソースファイル名、行数が表示されている。

すべてを一つに

さてさて、断片的に書いたソースをすべてつくってけてすべてを一つにまとめるとこうなる。
windows 32bit / 64bit対応、 Unix対応とかが絡んでくるので結構巨大なソースになってしまった。

あと一応、 STL とかを組み込まないような過去の遺産プロジェクトでも導入できるように、べたC++で書いているw。
メモリ管理がめんどいネ。。。

#ifdef _MSC_VER
#pragma once
#include <windows.h>
#include <stdlib.h>
#include <dbghelp.h>
#elif __GNUC__
#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <cxxabi.h>		//デマングル
#include <bfd.h>		//行番号まで取り出す
						//入っていないなら yum install binutils-devel とかしてね!
						// #include <bfd.h> 自体をコメントアウトすると機能全体をOFFにできるよ!!
#endif

class BackTrace
{
#ifdef _MSC_VER
	//dll読み込みヘルパー
	class LoadLibraryHelper
	{
	private:
		//DLL インスタンス.
		HMODULE DllInstance;

	public:
		LoadLibraryHelper()
		{
			this->DllInstance = NULL;
		}
		virtual ~LoadLibraryHelper()
		{
			if (this->DllInstance != NULL)
			{
				::FreeLibrary(this->DllInstance);
				this->DllInstance = NULL;
			}
		}
		bool Load( const char* inDLLName )//std読んでいない化石環境とかのために const char* で作る.
		{
			if (this->DllInstance != NULL)
			{
				return false;
			}
			this->DllInstance = ::LoadLibraryA(inDLLName);
			return this->DllInstance != NULL;
		}
		FARPROC GetProcAddress(const char* inProcName)
		{
			if (!this->DllInstance)
			{
				return NULL;
			}
			return ::GetProcAddress(this->DllInstance,inProcName);
		}
	};

	//スタックトレースAPI
	typedef USHORT (WINAPI *RtlCaptureStackBackTraceDef) (ULONG FramesToSkip,ULONG FramesToCapture,PVOID *BackTrace,__out_opt  PULONG BackTraceHash);
	RtlCaptureStackBackTraceDef RtlCaptureStackBackTraceProc;

	//シンボルエンジンの準備ができているか
	bool IsSymbolEngineReady;
	//プロセスハンドル
	HANDLE	Process;

	#if (_WIN64 || __x86_64__)
	//アドレスからモジュールを求める
	typedef BOOL (WINAPI *SymGetModuleInfo64Def) (HANDLE hProcess, DWORD64 dwAddr,  PIMAGEHLP_MODULE64 ModuleInfo );
	SymGetModuleInfo64Def SymGetModuleInfo64Proc;
	//アドレスからシンボルを求める
	typedef BOOL (WINAPI *SymGetSymFromAddr64Def) (HANDLE hProcess,DWORD64 Address,PDWORD64 Displacement,PIMAGEHLP_SYMBOL64 Symbol);
	SymGetSymFromAddr64Def SymGetSymFromAddr64Proc;
	//アドレスからファイルと行番号を求める
	typedef BOOL (WINAPI *SymGetLineFromAddr64Def) (HANDLE hProcess,  DWORD64 Address,  PDWORD64 Displacement,  PIMAGEHLP_LINE64 Line);
	SymGetLineFromAddr64Def SymGetLineFromAddr64Proc;
	#else
	//アドレスからモジュールを求める
	typedef BOOL (WINAPI *SymGetModuleInfoDef) (HANDLE hProcess, DWORD dwAddr,  PIMAGEHLP_MODULE ModuleInfo );
	SymGetModuleInfoDef SymGetModuleInfoProc;
	//アドレスからシンボルを求める
	typedef BOOL (WINAPI *SymGetSymFromAddrDef) (HANDLE hProcess,DWORD Address,PDWORD Displacement,PIMAGEHLP_SYMBOL Symbol);
	SymGetSymFromAddrDef SymGetSymFromAddrProc;
	//アドレスからファイルと行番号を求める
	typedef BOOL (WINAPI *SymGetLineFromAddrDef) (HANDLE hProcess,  DWORD dwAddr,  PDWORD pdwDisplacement,  PIMAGEHLP_LINE Line);
	SymGetLineFromAddrDef SymGetLineFromAddrProc;
	#endif


	//シンボルエンジンのオプション
	typedef BOOL (WINAPI *SymSetOptionsDef) ( DWORD SymOptions );
	SymSetOptionsDef SymSetOptionsProc;

	//シンボルエンジンの初期化
	typedef BOOL (WINAPI *SymInitializeDef) (HANDLE hProcess,      PSTR UserSearchPath,   BOOL fInvadeProcess  );
	SymInitializeDef SymInitializeProc;

	//シンボルエンジンの終了
	typedef BOOL (WINAPI *SymCleanupDef) (HANDLE hProcess);
	SymCleanupDef SymCleanupProc;
  
	LoadLibraryHelper Kernel32Librsry;
	LoadLibraryHelper DbgHelpLibrsry;

	BackTrace()
	{
		this->IsSymbolEngineReady = FALSE;
		this->RtlCaptureStackBackTraceProc = NULL;
		this->Process = NULL;
		this->SymSetOptionsProc = NULL;
		this->SymInitializeProc = NULL;
		this->SymCleanupProc = NULL;
		#if (_WIN64 || __x86_64__)
			this->SymGetModuleInfo64Proc = NULL;
			this->SymGetSymFromAddr64Proc = NULL;
			this->SymGetLineFromAddr64Proc = NULL;
		#else
			this->SymGetModuleInfoProc = NULL;
			this->SymGetSymFromAddrProc = NULL;
			this->SymGetLineFromAddrProc = NULL;
		#endif
		if ( ! this->Kernel32Librsry.Load("kernel32.dll") )
		{
			return ;
		}
		if ( ! this->DbgHelpLibrsry.Load("dbghelp.dll") )
		{
			return ;
		}
		this->RtlCaptureStackBackTraceProc = (RtlCaptureStackBackTraceDef)this->Kernel32Librsry.GetProcAddress("RtlCaptureStackBackTrace");
		#if (_WIN64 || __x86_64__)
			this->SymGetModuleInfo64Proc = (SymGetModuleInfo64Def)this->DbgHelpLibrsry.GetProcAddress("SymGetModuleInfo64");
			this->SymGetSymFromAddr64Proc = (SymGetSymFromAddr64Def)this->DbgHelpLibrsry.GetProcAddress("SymGetSymFromAddr64");
			this->SymGetLineFromAddr64Proc = (SymGetLineFromAddr64Def)this->DbgHelpLibrsry.GetProcAddress("SymGetLineFromAddr64");
		#else
			this->SymGetModuleInfoProc = (SymGetModuleInfoDef)this->DbgHelpLibrsry.GetProcAddress("SymGetModuleInfo");
			this->SymGetSymFromAddrProc = (SymGetSymFromAddrDef)this->DbgHelpLibrsry.GetProcAddress("SymGetSymFromAddr");
			this->SymGetLineFromAddrProc = (SymGetLineFromAddrDef)this->DbgHelpLibrsry.GetProcAddress("SymGetLineFromAddr");
		#endif
		this->SymSetOptionsProc = (SymSetOptionsDef)this->DbgHelpLibrsry.GetProcAddress("SymSetOptions");
		this->SymInitializeProc = (SymInitializeDef)this->DbgHelpLibrsry.GetProcAddress("SymInitialize");
		this->SymCleanupProc = (SymCleanupDef)this->DbgHelpLibrsry.GetProcAddress("SymCleanup");

		if (   !this->RtlCaptureStackBackTraceProc 
			&& !this->SymSetOptionsProc
			&& !this->SymInitializeProc
			&& !this->SymCleanupProc
			#if (_WIN64 || __x86_64__)
			&& !this->SymGetModuleInfo64Proc
			&& !this->SymGetSymFromAddr64Proc
			&& !this->SymGetLineFromAddr64Proc
			#else
			&& !this->SymGetModuleInfoProc
			&& !this->SymGetSymFromAddrProc
			&& !this->SymGetLineFromAddrProc
			#endif
			)
		{
			return ;
		}

		//プロセスを記録.
		this->Process = ::GetCurrentProcess();


		//シンボルエンジンの初期化.
		//行番号付きのデータをロードしてねとお願いする.
		this->SymSetOptionsProc(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
        if (this->SymInitializeProc(this->Process, NULL, TRUE))
        {
			//シンボルエンジン準備完了
			this->IsSymbolEngineReady = true;
        }

		return ;
	}
public:
	virtual ~BackTrace()
	{
		if (this->IsSymbolEngineReady)
		{
			this->SymCleanupProc(this->Process);
			this->IsSymbolEngineReady = false;
		}
	}
#elif __GNUC__
	//シンボルエンジンの準備ができているか
	bool		IsSymbolEngineReady ;
	#ifdef __BFD_H_SEEN__
		bfd*		Abfd;
		asymbol**	Symbols;
		int			NSymbols;
		asection*	Section;
	#endif

	BackTrace()
	{
		#ifdef __BFD_H_SEEN__
			this->IsSymbolEngineReady = false;
			this->Abfd = NULL;
			this->Symbols = NULL;
			this->NSymbols = 0;

			//see http://0xcc.net/blog/archives/000073.html
			this->Abfd = bfd_openr("/proc/self/exe", NULL);
			if (!this->Abfd)
			{
				return ;
			}
			bfd_check_format(this->Abfd, bfd_object);

			int size = bfd_get_symtab_upper_bound(this->Abfd);
			if (size <= 0)
			{
				return ;
			}
			this->Symbols = (asymbol**) malloc(size);
			if (!this->Symbols)
			{
				return ;
			}
			this->NSymbols = bfd_canonicalize_symtab(this->Abfd, this->Symbols);
			if (!this->NSymbols)
			{
				return ;
			}
			this->Section = bfd_get_section_by_name(this->Abfd, ".debug_info");
			if (!this->Section)
			{
				return ;
			}

			//シンボルエンジンの初期化完了
			this->IsSymbolEngineReady = true;
		#else
			//シンボルエンジンは利用できない!!
			this->IsSymbolEngineReady = false;
		#endif
	}

public:
	virtual ~BackTrace()
	{
		#ifdef __BFD_H_SEEN__
			if (this->Symbols)
			{
				free(this->Symbols);
				this->Symbols = NULL;
			}
			if (this->Abfd)
			{
				bfd_close (this->Abfd);
				this->Abfd = NULL;
			}
		#endif
	}
#endif

public:

#ifdef _MSC_VER
	//スタックトレースを取得する.
	int backtrace(void** buffer , int n) const
	{
		if ( ! this->RtlCaptureStackBackTraceProc )
		{
			//ロードされていない。
			return 0;
		}

		if (n >= 63)
		{
			n = 62;
		}
        return (int) this->RtlCaptureStackBackTraceProc(0,n,buffer,NULL);
	}

	#if (_WIN64 || __x86_64__) //for64bit
	//シンボルの解決
	void addressToSymbolString(void* address ,char * outBuffer , int len) const
	{
		if ( ! this->IsSymbolEngineReady )
		{
			//シンボルエンジンが準備できていない.
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#else
			_snprintf(outBuffer , len , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#endif
			return ;
		}

		//モジュール名
		IMAGEHLP_MODULE64 imageModule = { sizeof(IMAGEHLP_MODULE64) };
		BOOL r = this->SymGetModuleInfo64Proc(this->Process ,(DWORD64) address , &imageModule);
		if (!r)
		{
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#else
			_snprintf(outBuffer , len , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#endif
			return ;
		}

		//シンボル情報格納バッファ.
		IMAGEHLP_SYMBOL64 * imageSymbol;
		char buffer[MAX_PATH + sizeof(IMAGEHLP_SYMBOL64) ] = {0};
		imageSymbol = (IMAGEHLP_SYMBOL64*)buffer;
		imageSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
		imageSymbol->MaxNameLength = MAX_PATH;

		//関数名の取得...
		DWORD64 disp = 0;
		r = this->SymGetSymFromAddr64Proc(this->Process , (DWORD64)address , &disp , imageSymbol );
		if (!r)
		{//関数名がわかりません.
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ %s @ ??? @ ???:???" ,address,imageModule.ModuleName );
			#else
			_snprintf(outBuffer , len , "0x%p @ %s @ ??? @ ???:???" ,address,imageModule.ModuleName );
			#endif
			return ;
		}

		//行番号の取得
		IMAGEHLP_LINE64 line ={sizeof(IMAGEHLP_LINE64)};
		r = this->SymGetLineFromAddr64Proc(this->Process ,(DWORD64) address , &disp , &line);
		if (!r)
		{//行番号が分かりません
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ %s @ %s @ %s+%d" ,address,
				imageModule.ModuleName , imageSymbol->Name, imageSymbol->Name,(int) ((char*)address - (char*)line.Address) );
			#else
			_snprintf(outBuffer , len , "0x%p @ %s @ %s @ %s+%d" ,address,imageModule.ModuleName , 
				imageModule.ModuleName , imageSymbol->Name, imageSymbol->Name,(int) ((char*)address - (char*)line.Address) );
			#endif
			return ;
		}

		//行番号がわかりました.
		#if __STDC_WANT_SECURE_LIB__
		_snprintf_s(outBuffer , len ,_TRUNCATE, "0x%p @ %s @ %s @ %s:%d" ,address,imageModule.ModuleName , imageSymbol->Name , line.FileName , line.LineNumber);
		#else
		_snprintf(outBuffer , len , "0x%p @ %s @ %s @ %s:%d" ,address,imageModule.ModuleName , imageSymbol->Name , line.FileName , line.LineNumber);
		#endif
	}
	#else //for 32bit
	//シンボルの解決
	void addressToSymbolString(void* address ,char * outBuffer , int len) const
	{
		if ( ! this->IsSymbolEngineReady )
		{
			//シンボルエンジンが準備できていない.
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#else
			_snprintf(outBuffer , len , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#endif
			return ;
		}

		//モジュール名
		IMAGEHLP_MODULE imageModule = { sizeof(IMAGEHLP_MODULE) };
		BOOL r = this->SymGetModuleInfoProc(this->Process ,(DWORD) address , &imageModule);
		if (!r)
		{
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#else
			_snprintf(outBuffer , len , "0x%p @ ??? @ ??? @ ???:???" ,address );
			#endif
			return ;
		}

		//シンボル情報格納バッファ.
		IMAGEHLP_SYMBOL * imageSymbol;
		char buffer[MAX_PATH + sizeof(IMAGEHLP_SYMBOL) ] = {0};
		imageSymbol = (IMAGEHLP_SYMBOL*)buffer;
		imageSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
		imageSymbol->MaxNameLength = MAX_PATH;

		//関数名の取得...
		DWORD disp = 0;
		r = this->SymGetSymFromAddrProc(this->Process , (DWORD)address , &disp , imageSymbol );
		if (!r)
		{//関数名がわかりません.
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ %s @ ??? @ ???:???" ,address,imageModule.ModuleName );
			#else
			_snprintf(outBuffer , len , "0x%p @ %s @ ??? @ ???:???" ,address,imageModule.ModuleName );
			#endif
			return ;
		}

		//行番号の取得
		IMAGEHLP_LINE line ={sizeof(IMAGEHLP_LINE)};
		r = this->SymGetLineFromAddrProc(this->Process ,(DWORD) address , &disp , &line);
		if (!r)
		{//行番号が分かりません
			#if __STDC_WANT_SECURE_LIB__
			_snprintf_s(outBuffer ,len , _TRUNCATE , "0x%p @ %s @ %s @ %s+%d" ,address,
				imageModule.ModuleName , imageSymbol->Name, imageSymbol->Name,(int) ((char*)address - (char*)line.Address) );
			#else
			_snprintf(outBuffer , len , "0x%p @ %s @ %s @ %s+%d" ,address,imageModule.ModuleName , 
				imageModule.ModuleName , imageSymbol->Name, imageSymbol->Name,(int) ((char*)address - (char*)line.Address) );
			#endif
			return ;
		}

		//行番号がわかりました.
		#if __STDC_WANT_SECURE_LIB__
		_snprintf_s(outBuffer , len ,_TRUNCATE, "0x%p @ %s @ %s @ %s:%d" ,address,imageModule.ModuleName , imageSymbol->Name , line.FileName , line.LineNumber);
		#else
		_snprintf(outBuffer , len , "0x%p @ %s @ %s @ %s:%d" ,address,imageModule.ModuleName , imageSymbol->Name , line.FileName , line.LineNumber);
		#endif
	}
	#endif //(_WIN64 || __x86_64__)
#elif __GNUC__
	//スタックトレースを取得する.
	int backtrace(void** buffer , int n) const
	{
		return ::backtrace(buffer , n);
	}

	//シンボルの解決
	void addressToSymbolString(void* address ,char * outBuffer , int len) const
	{
		//backtrace_symbols で一発、、、なんてことをすると demangle されない場合があるそうな。
		//だから、dladdr でバラしていく。
		//see http://d.hatena.ne.jp/syuu1228/20100215/1266262848
		Dl_info info;
		if (! dladdr(address, &info) ) 
		{
			snprintf(outBuffer ,len , "0x%p @ ??? @ ??? @ ???:???" ,address );
			return ;
		}
		if (!info.dli_sname)
		{
			snprintf(outBuffer ,len , "0x%p @ %s @ ??? @ ???:???" ,address , info.dli_fname );
			return ;
		}
		if (!info.dli_saddr)
		{
			snprintf(outBuffer ,len , "0x%p @ %s @ %s @ ???:???" ,address , info.dli_fname , info.dli_sname );
			return ;
		}

		//デマングルして関数名を読める形式に
		int status = 0;
		char * demangled = abi::__cxa_demangle(info.dli_sname,0,0,&status);

		//シンボルエンジンは使えるの?
		if (!this->IsSymbolEngineReady)
		{
			snprintf(outBuffer ,len , "0x%p @ %s @ %s @ %s+0x%p" ,
					address , info.dli_fname , (demangled ? demangled : info.dli_sname),
											   (demangled ? demangled : info.dli_sname), 
											   (unsigned int) ((char *)address - (char *)info.dli_saddr) );
			free(demangled);
			return ;
		}
		#ifdef __BFD_H_SEEN__
			//ファイル名と行数を求める.
			const char* filename = NULL;
			const char* functionname = NULL;
			unsigned int line = 0;

			int found = bfd_find_nearest_line(this->Abfd, this->Section, this->Symbols,
												(long)address,
												&filename,
												&functionname,
												&line);
			if (found && filename != NULL && functionname != NULL) 
			{
				snprintf(outBuffer ,len , "0x%p @ %s @ %s @ %s+0x%p" ,
						address , info.dli_fname , (demangled ? demangled : info.dli_sname),
												    (demangled ? demangled : info.dli_sname),
											        (unsigned int) ((char *)address - (char *)info.dli_saddr) );
				free(demangled);
				return ;
			}

			snprintf(outBuffer ,len , "0x%p @ %s @ %s @ %s:%d" ,
					address , info.dli_fname , (demangled ? demangled : info.dli_sname), filename , line );
			free(demangled);
		#endif

		return ;
	}

#endif
	//シンボルをまとめて解決
	void addressToFullSymbolString(void** address ,int size , char * outBuffer , int len) const
	{
		int writesize = 0;

		int i = 0;
		for( i = 0 ; i < size ; i ++)
		{
			void * p = address[i];
			if (p == NULL)
			{
				break;
			}

			this->addressToSymbolString(p,outBuffer + writesize, len - writesize);
			writesize += (int)strlen(outBuffer + writesize);

			if (len - writesize >= 2)
			{
				#if __STDC_WANT_SECURE_LIB__
				strncat_s(outBuffer + writesize , len - writesize , "\r\n",2);
				#else
				strncat(outBuffer + writesize , "\r\n",2);
				#endif
				writesize += 2;
			}

			if (len <= writesize)
			{
				break;
			}
		}
	}

	//バックトレースの取得して画面に表示
	void printBackTrace() const
	{
		void* stackBuffer[50];
		char symbolBuffer[1024];

		int stacksize = this->backtrace(stackBuffer , 50  );
		this->addressToFullSymbolString(stackBuffer , stacksize , symbolBuffer , 1024);

		puts(symbolBuffer);
	}

	//singletonの取得
	static const BackTrace* Get()
	{
		static BackTrace s;
		return &s;
	}
};

class BackTraceAuto
{
	void**	StackBuffer;
	int		StackBufferSize;
public:
	BackTraceAuto()
	{
		this->StackBuffer = NULL;
		this->StackBufferSize = 0;
	}
	virtual ~BackTraceAuto()
	{
		free(this->StackBuffer);
		this->StackBuffer = NULL;
	}

	//バックトレースを記録
	void recordTrace()
	{
		if (this->StackBuffer != NULL)
		{
			return ;
		}

		this->StackBuffer = (void**)malloc(sizeof(void*) * 50);
		if (this->StackBuffer == NULL)
		{
			return ;
		}
		this->StackBufferSize = BackTrace::Get()->backtrace(this->StackBuffer , 50);
	}
	//バックトレースを表示
	void show() const
	{
		if (this->StackBuffer == NULL)
		{
			return ;
		}

		char symbolBuffer[1024];
		BackTrace::Get()->addressToFullSymbolString(this->StackBuffer , this->StackBufferSize , symbolBuffer , 1024);

		puts(symbolBuffer);
	}
};

class my_exception
{
	BackTraceAuto	Stacktrace;
	const char * Message;
public:
	my_exception(const char * message)
	{
		//ここでスタックを保存する
		this->Stacktrace.recordTrace();
		this->Message = message;
	}
	void show() const
	{
		puts(this->Message);
		//スタックのアドレスを関数名などに変換しながら表示する
		this->Stacktrace.show();
	}
};


void f5()
{
	throw my_exception("aaaa");
}

void f4()
{
	f5();
}


void f3()
{
	int aaa = 0;
	try
	{
		f4();
	}
	catch(my_exception const & e)
	{
//		BackTrace::Get()->printBackTrace();
		e.show();
	}
}
void f2()
{
	f3();
}
void f1()
{	
	f2();
}


int main()
{
	f1();
	return 0;
}


さっそく使ってみる。

//windows 64bit
0x000000013FC9150D @ stacktrace @ ?backtrace@BackTrace@@QEBAHPEAPEAXH@Z @ c:\use
rs\rti\documents\my dropbox\stacktrace\stacktrace\stacktrace.cpp:282
0x000000013FC9149A @ stacktrace @ BackTraceAuto::recordTrace @ c:\users\rti\docu
ments\my dropbox\stacktrace\stacktrace\stacktrace.cpp:583
0x000000013FC91561 @ stacktrace @ my_exception::my_exception @ c:\users\rti\docu
ments\my dropbox\stacktrace\stacktrace\stacktrace.cpp:609
0x000000013FC94486 @ stacktrace @ f5 @ c:\users\rti\documents\my dropbox\stacktr
ace\stacktrace\stacktrace.cpp:622
0x000000013FC944CA @ stacktrace @ f4 @ c:\users\rti\documents\my dropbox\stacktr
ace\stacktrace\stacktrace.cpp:628
0x000000013FC9450B @ stacktrace @ f3 @ c:\users\rti\documents\my dropbox\stacktr
ace\stacktrace\stacktrace.cpp:636
0x000000013FC9454A @ stacktrace @ f2 @ c:\users\rti\documents\my dropbox\stacktr
ace\stacktrace\stacktrace.cpp:647
0x000000013FC9457A @ stacktrace @ f1 @ c:\users\rti\documents\my dropbox\stacktr
ace\stacktrace\stacktrace.cpp:651
0x000000013FC945AA @
//linux
//コンパイルオプション g++ -g -O0 stacktrace.cpp -rdynamic -ldl -lbfd -liberty
//なんか、途中から行番号の取得に失敗している、、、なんでだろう。
0x0x4039b7 @ ./a.out @ BackTrace::backtrace(void**, int) const @ /home/rti/stacktrace/stacktrace/stacktrace.cpp:431
0x0x403a13 @ ./a.out @ BackTraceAuto::recordTrace() @ /home/rti/stacktrace/stacktrace/stacktrace.cpp:583
0x0x403a47 @ ./a.out @ my_exception::my_exception(char const*) @ my_exception::my_exception(char const*)+0x0x23
0x0x403055 @ ./a.out @ f5() @ f5()+0x0x25
0x0x40308f @ ./a.out @ f4() @ f4()+0x0x9
0x0x4030a7 @ ./a.out @ f3() @ f3()+0x0x15
0x0x403103 @ ./a.out @ f2() @ f2()+0x0x9
0x0x40310f @ ./a.out @ f1() @ f1()+0x0x9
0x0x40311b @ ./a.out @ main @ main+0x0x9
0x0x362981d994 @ /lib64/libc.so.6 @ __libc_start_main @ /proc/self/exe:0
0x0x402f69 @ ./a.out @ __gxx_personality_v0 @ /proc/self/exe:0


うまくできたー。
やったね。車輪ができたよ。

車輪?

え?車輪。
まずは、コーヒーでも飲めよ。

  _、_     
( ,_ノ` )      
     ζ 
    [ ̄]'E 
 


'さて、何の話だったかな。
そう、車輪だ。
そう、これは再発明なんだよ。


世の中には Boost.Backtrace ってゆーのがあるんだよ。
http://d.hatena.ne.jp/faith_and_brave/20101022/1287731209


これを使うと、こんな感じで同じようなことができるんだ。

#include <boost/backtrace.hpp>
#include <iostream>


void f5()
{
	 throw boost::runtime_error("My Error");
}

void f4()
{
	f5();
}


void f3()
{
	int aaa = 0;
	try
	{
		f4();
	}
	catch(boost::runtime_error const & e)
	{
        std::cerr << e.what() << std::endl;
        std::cerr << boost::trace(e);
	}
}
void f2()
{
	f3();
}
void f1()
{	
	f2();
}


int main()
{
	f1();
	return 0;
}


出力結果

My Error
00309E1F: boost::stack_trace::trace +0x3f
003025AB: boost::backtrace::backtrace +0x8b
00302352: boost::runtime_error::runtime_error +0x62
00302269: f5 +0x69
003029D3: f4 +0x23
00302A54: f3 +0x54
00302D23: f2 +0x23
00302DF3: f1 +0x23
00302EA3: main +0x23
0031092F: __tmainCRTStartup +0x1bf
0031075F: mainCRTStartup +0xf
75D73677: BaseThreadInitThunk +0x12
77009D42: RtlInitializeExceptionChain +0x63
77009D15: RtlInitializeExceptionChain +0x36

そうなんだ。
これだけで、できちゃうんだ。
ここまで読んでくれてすまないと思っている。


だけど、今回作った方がAPIを動的リンクで呼び出しているので下位互換性があるし、ソースの行数レベルでのトレースができるんだよ、それに、STLすら使っていない化石環境でも導入できる。


まぁ、それだけなんだけどネw

おまけ1 API対応表

windows unix 用途
RtlCaptureStackBackTrace backtrace スタックトレースを取得したい
SymGetModuleInfo
SymGetSymFromAddr
dladdr アドレスから
モジュール名と関数名を取得したい
SymGetLineFromAddr bfd_find_nearest_line アドレスから
ソースファイル名と行数を取得したい
SYMOPT_UNDNAMEオプション abi::__cxa_demangle デマングル
dbghelp.h dlfcn.h
cxxabi.h
bfd.h
アドレスから復元するライブラリ

おまけ2 catchでのスタックの状態

	try
	{
//esp 0x0020f400
//ebp 0x0020f4f8
00031E68  mov         dword ptr [ebp-4],0  
		f4();
00031E6F  call        f4 (312DAh)  
	}
00031E74  jmp         __catch$?f3@@YAXXZ$0+24h (31E9Ah)  
	catch(int & e)
	{
//esp 0x0020ebb0 まだ伸びっぱなしぢゃね?
//ebp 0x0034f948
		puts("123");
00031E76  mov         esi,esp  
00031E78  push        offset string "123" (40834h)  
00031E7D  call        dword ptr [__imp__puts (455FCh)]  
00031E83  add         esp,4  
00031E86  cmp         esi,esp  
00031E88  call        @ILT+1215(__RTC_CheckEsp) (314C4h)  
	}
00031E8D  mov         dword ptr [ebp-4],0FFFFFFFFh  
00031E94  mov         eax,offset $LN2 (31EA1h)  
00031E99  ret  


あと、catchの下に変数を配置すると、ちょっと変わる。

	try
	{
//esp 0x0034f844
//ebp 0x0034f948
010E1E68  mov         dword ptr [ebp-4],0  
		f4();
010E1E6F  call        f4 (10E12DAh)  
	}
010E1E74  jmp         __catch$?f3@@YAXXZ$0+24h (10E1E9Ah)  
	catch(int & e)
	{
//esp 0x0034eff4   まだ伸びっぱなしぢゃね?
//ebp 0x0034f948
		puts("123");
010E1E76  mov         esi,esp  
010E1E78  push        offset string "123" (10F0834h)  
010E1E7D  call        dword ptr [__imp__puts (10F55FCh)]  
010E1E83  add         esp,4  
010E1E86  cmp         esi,esp  
010E1E88  call        @ILT+1215(__RTC_CheckEsp) (10E14C4h)  
	}
010E1E8D  mov         dword ptr [ebp-4],0FFFFFFFFh  
010E1E94  mov         eax,offset __tryend$?f3@@YAXXZ$1 (10E1EA1h)  //←これ!
010E1E99  ret                                                      //←これ!


//esp 0x0034f844 戻る
//ebp 0x0034f948
010E1E9A  mov         dword ptr [ebp-4],0FFFFFFFFh  
	int bbb = 0;
010E1EA1  mov         dword ptr [ebp-30h],0  
}


ここら辺の仕様ってどうなってるの?

参考

普通のやつらの下を行け: BFDでデバッグ情報の取得
http://0xcc.net/blog/archives/000073.html

C++でbacktrace_symbols()してみたらmanglingされてて読めない件
http://d.hatena.ne.jp/syuu1228/20100215/1266262848

Getting the backtrace from the catch block
http://stackoverflow.com/questions/4283943/getting-the-backtrace-from-the-catch-block

How to Log Stack Frames with Windows x64
http://stackoverflow.com/questions/590160/how-to-log-stack-frames-with-windows-x64

//↓後で試す.
Windows の NTDLL.DLLの RtlCaptureStackBackTrace の バグ
http://blog.livedoor.jp/blackwingcat/archives/845435.html

Boost.Backtrace?
http://d.hatena.ne.jp/faith_and_brave/20101022/1287731209

obcheck
http://sourceforge.jp/projects/obcheck/


最後まで読んでくれてありがとう。