- ベストアンサー
アプリケーション終了時例外エラー(アクセス違反)の調査方法について
大変困っています。 アプリケーションが終了するときに「アクセス違反」がワトソン博士によって取得されています。 当方アプリケーションなどに弱く、解決策の想像が付きません。どなたかご教授お願いいたします。 <解決策例> ・どういったスキルを持った人にどの様な調査を進めさせれば良いのか。。。 ・以前同様な事があり原因は○○だった ・恐らく○○だろう など、お願いいたします。 <ユーザ報告> 処理終了し、画面が消えたところでワトソン博士のメッセージが表示された <ログ抜粋> 例外番号c0000005(アクセス違反) ファンクション:RtlDestroyHeap ~略~ フォールト → 77f6d672 8908 mov [eax],ecx ds:09000001=00000000 <備考> 開発環境:MSVC6.0 動作環境:Windows NT4.0 SP6a 発生頻度:2回/年 使用頻度:2~3回/(平日) 以上、よろしくお願いします。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
発生頻度が年2回というのはなかなか厳しい条件ですね。 さて・・・ 「私であれば、次の手順で調査を行います。」という書き始めで延々とデバッグ方法を書いていたのですが、書き終わってからちょっとGoogleで検索したら、ひょっとするとちょうど質問者さんのトラブルと同じかもしれない現象がMicrosoftのKBにありました。 場所はここです: http://support.microsoft.com/kb/168006/ja 要点をかいつまんで書くと、MSVCRT/MFCのDLLのバージョン不整合でエラーが発生することがある、という内容です。KB自体は特定のアプリケーションについて書かれていますが、記述されている現象と原因の関係から考えるに、他のアプリケーションでも同様の現象が発生すると思われます。 さてさて。 せっかく書いた文章を捨てるのがもったいないので(貧乏性)、邪魔かもしれませんが下に続けることにします。もし上のKBの内容がそれらしいようであれば、読み飛ばしてください。 ======== 私であれば、次の手順で調査を行います。 1. MAPファイル、CODファイル作成 「ワトソン博士のログを取得した際に実行していたEXEファイル」をビルドした際、一緒にMAPファイルやCODファイル(リスティングファイル)を作成していれば、そのファイルを用意しておきます。 もし作成していない場合は、「ワトソン博士のログを取得したEXEファイル」と、バイナリレベルで全く同じEXEファイル(バイト単位で比較すると、ファイルに埋め込まれたタイムスタンプ・チェックサム以外は一致する)が作成可能かどうか調べます。(ビルドに必要なソースファイルやビルドオプションに変更を加えていなければ作成可能です。) 作成可能であれば、コンパイルオプションに「リスティングファイルタイプ:マシン語コードとソースを含む」、リンクオプションに「MAPファイル作成」を追加してEXEを再作成してください。これで、「ワトソン博士のログを取得した際に実行していたEXEファイル」に対応するMAPファイルとCODファイルが得られます。 2. エラー発生行を特定 ワトソン博士のログがどれだけ取れているかにもよりますが、スタックダンプが含まれていればたいていエラー発生行を特定できます。 まず「フォールト->」が含まれる逆アセンブルリストを探します。次に、その下にある「スタックバックトレース」を探します。 スタックバックトレースを上から下に順にたどっていくと、そのうち「ReturnAd」(リターンアドレス)がアプリケーションのアドレス範囲(VC++6の標準オプション設定を変更していなければ0x00400000~)に入るところが出てきます。見つかったら、そのアドレスの直前にあるcall命令が例外を発生させたAPIを直接呼び出している場所です。 さて、仮にリターンアドレスが0x00401234だったとします。そうしたら、次はMAPファイルを見てこのアドレスがどの関数に属しているか探します。ちょうど0x00401234というアドレスは見つからないでしょうけれども、これに近いアドレスは見つかるはずです。そのアドレスに対応する関数名もMAPファイルにあります。 次はその関数名をCODファイルから探します。見つかったら、MAPファイルにあるアドレスがCODファイルにあるマシン語コードの先頭アドレスになるので、そこからリターンアドレス0x00401234に対応するはずの場所まで順番にアドレスを辿っていきます。関数の先頭アドレスが0x00401200であれば、0x34バイト先を探すわけです。 そうすると、その探した場所にある命令の直前の命令がcall命令になっているはずです。CODファイルには、その場所のC++ソースでの行番号とソース文もコメントとして入っているはずなので、あとは対応するソースをよーく見てエラーの見当をつけてください。 アセンブラの知識があれば、そこでcallを使った(他の関数を呼び出した)ときの引数の内容もある程度分かります。(ポインタ渡しだと、そのポインタの先の内容までは分かりませんが。) 3. 置き換え用EXEファイルと対応するMAPファイル作成 これ以降は将来への備えです。 コンパイルオプションでデバッグ情報を「プログラムデータベースを使用」、リスティングファイルタイプを「マシン語コードとソースを含む」、リンクオプションで「MAPファイルを作成する」、デバッグ情報「他の種類」を追加してビルドし、出来たEXEファイルを本番用として使用します。同時に作成されるMAPファイル、CODファイル、PDBファイルは保管しておきます。 MAPファイル、CODファイルの使い方は上記2.のとおりです。PDBファイルは、もし完全なクラッシュダンプが取得できればWinDbgを使って事後ソースレベルデバッグが可能になりデバッグ作業が非常に楽になるので、念のため取っておきます。 4. ワトソン博士のオプション変更 drwtsn32.exeを起動し、「クラッシュダンプファイルの作成」をチェックします。(デフォルトは、チェックが入っています。) クラッシュダンプファイルとEXEとPDBがあればWinDbgで事後ソースレベルデバッグができます。(いわゆるポストモーテムデバッグです。UNIX系でコアダンプしたコアをデバッガで読み込んでデバッグするのと同じ種類のものです。) 普段何とも思わずに行っていることでも、文章にすると長いですね・・・ えーと、「どういうスキルを持った人に調査させればいいか」については、上記の内容を読んで『なるほど!』と言える人でしょうか。
その他の回答 (6)
- rinkun
- ベストアンサー率44% (706/1571)
問題点の調査方法ではないですし抜本的な解決策でもないですけど、アクセス違反が発生するのが処理中ではなく終了時(終了処理によるもの)なら、終了時のメモリ解放処理を省いてしまうという解決策もあります。 実のところC/C++でもメモリ解放処理は結構複雑で、データ構造によってはエラーを起こさずに解放するのが難しいこともあります。どうせプロセスが終了すれば全解放されるのでプログラム上での解放処理を省いてしまうのが簡単な場合も多いです。
No.4に対する「回答者へのお礼」のコードを見ると、 何処かで char *p = new char[256]; p += 10; ... delete [] p; // 加算したポインタで開放 などとしているということはないでしょうね? これだとポインタの演算が入るような場合にのみアクセス違反が発生しますよ。
- buihyaku
- ベストアンサー率29% (97/326)
あと、この場合、一番可能性の高いのがどこかの処理でメモリを破壊している、という可能性です。 たまたま壊した箇所にアクセスに行かない限り落ちないので落ちたり落ちなかったり、壊した箇所がデータであればフラグを書き換えたりして誤動作の原因になったりと始末に終えません。 ほかにもいろいろあるとおもいますがとりあえず思いつく原因はこんなところです。 ー あらかじめ確保したバッファのサイズを越えて書き込みを行っている箇所はないか ー 基本クラスのインスタンスに追加のメンバ変数をもつ派生クラスのポインタでアクセスしたりしていないか ー 未初期化ポインタで処理されている箇所はないか あたりまえの回答でもうしわけありませんが、 VCがわかる人に、メモリを壊している箇所がないか調査してもらう、ということになるのでは?
- buihyaku
- ベストアンサー率29% (97/326)
あるいは未初期化ポインタを解放しようとしているとか。 クラス内のデストラクタでfreeまたはdeleteしているところがあるとしたら、そのポインタ変数がちゃんと明示的に初期化されているか確認してください。
お礼
ありがとうございます。 上記回答に追加質問させていただきました。 >ルール違反? すみませんが、よろしくお願いいたします。
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
あるいは、二重の解放とか
お礼
早急な回答ありがとうございます。 みなさんの間では常識なのですね~。 実は、怪しい箇所をみつけ、そこにメモリ内容を越えるデータを入れたのですが、同一現象が再現しなかったのです。。 何か参考になるurlなどありましたら教えてください。
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
メモリの解放し忘れじゃないですかね
お礼
早速ありがとうございます。 当たり前の回答とおっしゃりますが、非常に助かります。こちらはその当たり前も。。。 実は、あまりわからないなりにも解析していたところ、作成していた方のレベルが。。。だった為か、以下のような記述を発見しました。。 FuncA(CString strSrc){ char szA[50]; memcpy(szA, strSrc, strSrc.GetLength()); } (もちろん呼び側でもlength未精査)。。 また、少しわからないのが、原因不明だったためか、 InitInstanceでダイアログをmodalする時(DialogBase)にtry文で例外を取得しているのですが、効果がはなかったみたいです。(以下参照) 解釈としては、「上記のような箇所がある為InitInstanceのメモリ開放時に落ちている」でよろしいですかね? InitInstance() { CADialog ADlg; try { nRet = ADlg.DoModal(); } catch(...){ } } すみません。。たびたび。