久しぶりにWin32API関連の覚書です。
Cで呼び出し履歴(コールスタック)の取得サンプルを漁ると、こんなのをよく見かけました。
※これ以降のコードは超概略記述なので、このままコピペしても動作しません。
IMAGEHLP_LINE64 line = { sizeof(IMAGEHLP_LINE64) }; SymFromAddr(hProcess, address, &disp, symbol); SymGetLineFromAddr64(hProcess, symbol->Address, &dispLine, &line64); printf("filename=%s, line=%d\n", line64.FileName, line64.LineNumber);
最初、コピペで実行してみたのですが、どうも呼び出し元の行の位置がずれているのです。例えば、
void GetCallStack() { IMAGEHLP_LINE64 line = { sizeof(IMAGEHLP_LINE64) }; SymFromAddr(hProcess, address, &disp, symbol); SymGetLineFromAddr64(hProcess, symbol->Address, &dispLine, &line64); printf("filename=%s, line=%d\n", line64.FileName, line64.LineNumber); } int main() { Statement1; Statement2; Statement3; GetCallStack(); }
と組んでみたときに、呼び出し元は15行目なので「line=15」と出力してほしいのですが、「line=11」と出力されます。
結論は、SymFromAddr()で取得したDisplacement(変位)をSymGetLineFromAddr64()のアドレスに加えないといけないのです。
SymFromAddr(hProcess, address, &disp, symbol); SymGetLineFromAddr64(hProcess, symbol->Address + disp, &dispLine, &line64);
あと、Release版のときは、コンパイルオプションに「/Od」(最適化を無効にする)を指定しないとDebug版と同様な綺麗な呼び出し履歴が取得できないので注意です。