コールスタックを読み取るにはどうすればよいですか?
-
06-07-2019 - |
質問
Windows 2003 サーバー上で COM+ 経由で実行されるネイティブ C++ アプリケーションがあります。最近、イベントビューアから例外、特に C0000005 例外がスローされていることに気づきました。 http://blogs.msdn.com/calvin_hsia/archive/2004/06/30/170344.aspx これは、プロセスがそのアドレス空間内にないメモリに書き込もうとしていること、つまりアクセス違反を意味します。
イベント ビューアのエントリは、コール スタックを提供します。
libfmwk!util_getdatefromlogbydaydirectory(char const *、class utilcdate&) + 0xa26c libffmwk!util_getdatefromlogbydaydirectory(char const *、class utilcdate&) + 0x8af4 libfmwk! x13a1 libfmwk!utilclogcontroller :: getflfinfolevel(void )const + 0x1070 libfmwk!utilclogcontroller :: getflfinfolevel(void)const + 0x186
さて、調べるべきメソッド名が与えられているのは理解しましたが、各行の末尾にアドレスが表示されているような気がします (例:+ 0xa26c) は、そのメソッド内の特定の行または命令を指し示そうとしています。
そこで私の質問は次のとおりです。
- コールスタック内のこのアドレスまたはその他の情報を使用して、コード内のどの行に該当するかを判断する方法を知っている人はいますか?
- コールスタックをより深く理解するために読むことができるリソースはありますか?
- おそらくデバッグ シンボル ファイルやバイナリに添付することで、コール スタックの分析に役立つフリーウェア/オープンソース ツールはありますか?
編集:リクエストに応じて、問題の原因となっていると思われる方法は次のとおりです。
BOOL UTIL_GetDateFromLogByDayDirectory(LPCSTR pszDir, utilCDate& oDate)
{
BOOL bRet = FALSE;
if ((pszDir[0] == '%') &&
::isdigit(pszDir[1]) && ::isdigit(pszDir[2]) &&
::isdigit(pszDir[3]) && ::isdigit(pszDir[4]) &&
::isdigit(pszDir[5]) && ::isdigit(pszDir[6]) &&
::isdigit(pszDir[7]) && ::isdigit(pszDir[8]) &&
!pszDir[9])
{
char acCopy[9];
::memcpy(acCopy, pszDir + 1, 8);
acCopy[8] = '\0';
int iDay = ::atoi(&acCopy[6]);
acCopy[6] = '\0';
int iMonth = ::atoi(&acCopy[4]);
acCopy[4] = '\0';
int iYear = ::atoi(&acCopy[0]);
oDate.Set(iDay, iMonth, iYear);
bRet = TRUE;
}
return (bRet);
}
これは、会社を辞めて久しいメンバーが 10 年以上前に書いたコードなので、これが何をしているのか正確にはわかりませんが、ログ ディレクトリの名前を「Today」から変更するプロセスに関係していることはわかっています。 ' 特定の日付に対応します。%20090329。配列のインデックス付け、memcpy、および演算子のアドレスを見ると、かなり疑わしいように見えます。
私たちが抱えていると思われるもう 1 つの問題は、これが本番システムでのみ発生し、デバッガを接続できるテスト システムや開発システムでは再現できなかったことです。
とても有難い!アンディ
解決
これらのアドレスを実際に関数にマップする必要がある場合-.MAPファイルを操作して、それらのアドレスが実際に指している場所を確認する必要があります。
しかし、あなたの状況では、この問題をデバッガー(たとえば、MSVSデバッガーまたはwindbg)で調査したいと思います。別の方法として(顧客のサイトでクラッシュが発生した場合)、クラッシュダンプを生成してローカルで調べることができます-Windows MiniDumpWriteDump APIまたはSysInternals ProcDumpユーティリティ( http://download.sysinternals.com/Files/procdump.zip )。
必要なすべてのシンボルファイルが生成されて使用可能であることを確認します(また、Windows DLLのエントリポイントも解決されるようにMicrosoftシンボルサーバーパスを設定します)。
これは、必要なWebサイトです。 http://www.dumpanalysis.org -すべての質問をカバーするのに最適なリソースです。 このPDFの http://windbg.info/download/docもご覧ください。 /pdf/WinDbg_A_to_Z_color.pdf
他のヒント
他の人はこれを行間で言っていますが、明示的には言っていません。見てください:
LibFmwk!UTIL_GetDateFromLogByDayDirectory(char const *,class utilCDate &) + 0xa26c
0xa26cのオフセットは巨大で、関数の最後を過ぎています。デバッガーは明らかにLibFmwkの適切なシンボルを持っていないため、代わりにDLLエクスポートに依存しており、見つけることができる最も近いオフセットを基準にしてオフセットを表示しています。
それで、ええ、適切なシンボルを取得すれば、簡単になります。ここでは、UTIL_GetDateFromLogByDayDirectoryに障害はありません。
ポイント 2 と 3 は簡単に答えられます。
3点目。任意のデバッガ。そのために作られています。この特別な例外でブレークするようにデバッガを設定します。コールスタック内で自分自身をクリックして、スタック上のさまざまな呼び出しを見つけることができるはずです (少なくとも Delphi ではこれができるので、Visual Studio でも同様にできるはずです)。可能であれば最適化を行わずにコンパイルします。OllyDBG も、おそらくそのトレース機能と組み合わせて機能する可能性があります。
2点目。x86 アセンブラー、リバースエンジニアリングに関する情報...試す: OpenRCE, NASM ドキュメント, ASMコミュニティ.
1点目。コールスタックから関数がわかります。それが順番に書かれているのか、逆の順序で書かれているのかはわかりません。したがって、最初の行が最後に呼び出された関数であるか、最初に呼び出された関数である可能性があります。デバッガーを使用して呼び出しを追跡します。場合によっては、asm とコードを切り替えることができます (デバッガー、マップ ファイルなどによって異なります)。ソースがない場合は、アセンブラを学び、リバース エンジニアリングについて読んでください。サードパーティコンポーネントで呼び出す関数のドキュメントを読んでください。おそらく前提条件を満たしていない可能性があります。
プログラムについてもう少し詳しく教えていただければ (ソース コードのどの部分があるのか、ライブラリ呼び出しが関係していますか?...)
ここでコードを読んでみましょう:
この関数は、ゼロで終了する文字列へのポインタと日付オブジェクトへの参照を受け入れます。ポインタは有効であると想定されます。
この関数は、文字列が特定の形式 (% の後に 8 桁の数字が続き、その後に \0 が続く) であるかどうかをチェックします。そうでない場合は false を返します。このチェック (ビッグ if) は、有効性チェックを行わずにポインターにアクセスします。長さはチェックされず、ポインタが野生のどこかを指している場合、この空間がアクセスされます。短い文字列が問題を引き起こすかどうかはわかりません。&& の評価方法により、それは行われるべきではありません。
次に、一部のメモリがスタックに割り当てられます。文字列の数値部分がそこにコピーされ (これは問題ありません)、バッファは \0 で終了します。アトワは数値を抽出します。これは、使用される開始位置が異なり、各部分の後に \0 が終了するため、機能します。なんだか難しいけど素敵。コメントがあればすべてが明らかになるでしょう。
これらの数値はオブジェクトに挿入されます。参照によって関数に渡されるため、有効である必要があります。削除されたオブジェクトへの参照を渡すことができるかどうかはわかりませんが、その場合は、これも問題である可能性があります。
とにかく、文字列ポインターのチェックが欠落していることを除けば、この関数は正常であり、問題の原因ではありません。例外をスローする場所のみです。この関数に渡される引数を検索します。それらは常に有効ですか?ログを記録します。
私は Delphi プログラマーなので、大きな間違いを犯さなかったことを願っています。もしそうなら、お気軽にコメントしてください。