呼び出し履歴を取得する

環境

  • Windows7 SP1
  • VisualStudio2012 SP1
  • SCons-2.3.0

やってみた

$ make run-x86
make run TARGET_ARCH=x86
make[1]: ディレクトリ `/home/github/shive/blogpost/20130918-stacktrace' に入ります
scons -Qj4 TARGET_ARCH=x86 build
cl /Fo.obj\x86\main.obj /c .obj\x86\main.cpp /TP /nologo /Zi /MD /EHsc /GR- /Od /Fdmain.pdb /D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS /D_MBCS /D_WINDOWS /DWIN32 /DNDEBUG /DVC_EXTERN /DWIN32_LEAN_AND_MEAN /DNOMINMAX /DSTRICT
main.cpp
link /nologo /DEBUG /INCREMENTAL:NO /SUBSYSTEM:CONSOLE /OUT:.obj\x86\main.exe imagehlp.lib .obj\x86\main.obj
scons -Qj4 TARGET_ARCH=x86 run
".obj\x86\main.exe"
---- BEGIN BACKTRACE ----
1 : 0x011c1638 : main : mycode::foo : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(112) :         backtrace();
2 : 0x011c1648 : main : mycode::bar : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(116) :         foo();
3 : 0x011c1658 : main : mycode::baz : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(120) :         bar();
4 : 0x011c1668 : main : mycode::hoge : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(124) :         baz();
5 : 0x011c16a8 : main : main : c:\home\github\shive\blogpost\20130918-stacktrace\.obj\x86\main.cpp(131) :         mycode::hoge();
6 : 0x011c3bde : main : __tmainCRTStartup : f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c(536) : ?
7 : 0x75d3336a : kernel32 : BaseThreadInitThunk : ?(0) : ?
---- END BACKTRACE ----

make[1]: ディレクトリ `/home/github/shive/blogpost/20130918-stacktrace' から出ます

重要なとこ

  • RtlCaptureStackBackTrace : 関数呼び出しのリターンアドレス一覧を取得
  • SymGetModuleInfo : モジュール名の取得
  • SymGetSymFromAddr : 関数アドレスからシンボル名の取得
  • SymGetLineFromAddr : 関数アドレスからファイル名と行番号の取得

あとがき

  • 最適化を入れた場合当然関数呼び出しが一部無くなるが、無くなったなりの履歴でちゃんと動く
  • x64でもちゃんと動いた
  • 当然だがデバッグシンボルありでビルドしないとちゃんと情報が取れない
  • 呼び出した行番号ではなく戻る行番号を取得して表示しているので微妙にズレる
  • この機能を使えば呼び出し元がどこそこだったらこう動く!とか気持ち悪いこともできるなぁ

参考