Visual Studio 2008で(ネイティブ)マルチスレッドC++アプリケーションをデバッグしています。一見ランダムな状況で、「Windowsがブレークポイントをトリガーしました...」というエラーが表示されます。ヒープ。これらのエラーは、すぐにアプリケーションをクラッシュさせるとは限りませんが、すぐにクラッシュする可能性があります。
これらのエラーの大きな問題は、破損が実際に発生した後にのみポップアップすることです。これにより、特にマルチスレッドアプリケーションでは、追跡とデバッグが非常に難しくなります。
これらのエラーを引き起こす可能性があるのはどのようなものですか?
どうすればデバッグできますか?
ヒント、ツール、方法、啓発などを歓迎します。
Application Verifier と組み合わせて Windows用デバッグツール は素晴らしいセットアップです。 Windows Driver Kitまたはより軽いWindows SDK の一部として両方を取得できます。 ( ヒープ破損の問題に関する以前の質問 を調査する際にApplication Verifierについて確認しました。)過去にもBoundsCheckerとInsure ++(他の回答で言及)を使用しましたが、機能の量には驚かされましたApplication Verifierにありました。
Electric Fence(別名 "efence")、 dmalloc 、 valgrind などはすべて言及する価値がありますが、これらのほとんどはWindowsよりも* nixで実行する方がはるかに簡単です。 Valgrindはとてつもなく柔軟です。私はそれを使用して多くのヒープの問題がある大規模なサーバーソフトウェアをデバッグしました。
他のすべてが失敗した場合、独自のグローバル演算子new/deleteおよびmalloc/calloc/reallocのオーバーロードを提供できます-その方法は、コンパイラーとプラットフォームによって少し異なります-これは少しの投資になります-しかし、長期的には報われる可能性があります。望ましい機能のリストは、dmallocとelectricfenceからおなじみに見えるはずです。驚くほど優れた本 Writing Solid Code :
ローカル組み込みシステム(組み込みターゲットの場合)では、実行時のオーバーヘッドがはるかに高いため、トラッキングを他のほとんどのものとは別にしていることに注意してください。
これらの割り当て関数/演算子をオーバーロードする他の理由に興味がある場合は、 「グローバル演算子をオーバーロードして削除して削除する理由」に対する私の答え ;をご覧ください。恥知らずな自己宣伝はさておき、ヒープ破損エラーの追跡に役立つ他のテクニック、および他の適用可能なツールをリストします。
MSが使用するalloc/free/fence値を検索するときに、ここで自分の答えを見つけ続けるため、ここに Microsoft dbgheap fill値をカバーする別の答え があります。
アプリケーションでページヒープを有効にすると、多くのヒープ破損の問題を検出できます。これを行うには、 Debugging Tools For Windows の一部として提供されるgflags.exeを使用する必要があります
Gflags.exeを実行し、実行可能ファイルのイメージファイルオプションで、[ページヒープを有効にする]オプションをオンにします。
Exeを再起動して、デバッガーにアタッチします。ページヒープを有効にすると、ヒープの破損が発生すると、アプリケーションはデバッガーに侵入します。
非常に関連性の高い記事は、Application VerifierとDebugdiagによるヒープ破損のデバッグです。
本当に遅くなり、多くのランタイムチェックを実行するには、main()
またはMicrosoft Visual Studio C++の同等のものの先頭に次を追加してみてください。
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
解放されたメモリへのアクセスを検出する から得た簡単なヒントの1つは、
メモリブロックにアクセスするすべてのステートメントをチェックせずにエラーをすばやく見つけたい場合は、ブロックを解放した後にメモリポインタを無効な値に設定できます。
#ifdef _DEBUG // detect the access to freed memory #undef free #define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666; #endif
これらのエラーを引き起こす可能性があるのはどのようなものですか?
いたずらなことを記憶でやる、例えばバッファの終了後に書き込む、またはバッファがヒープに解放された後にバッファに書き込む。
どうすればデバッグできますか?
自動化された境界チェックを実行可能ファイルに追加する手段を使用します:Unixの場合はvalgrind、Windowsの場合はBoundsChecker(WikipediaはPurifyとInsure ++も推奨)のようなツール。
これらがアプリケーションの速度を低下させることに注意してください。したがって、あなたのアプリケーションがソフトリアルタイムアプリケーションの場合は使用できない可能性があります。
別の可能なデバッグ支援/ツールは、MicroQuillのHeapAgentです。
私が有用であり、毎回機能することがわかった最高のツールは、コードレビューです(優れたコードレビューアーを使用)。
コードレビュー以外では、まず Page Heap を試します。ページヒープのセットアップには数秒かかり、運が良ければ問題を特定できる可能性があります。
ページヒープがうまくいかない場合は、Microsoftから Windows用デバッグツール をダウンロードし、WinDbgの使用方法を学んでください。詳細なヘルプを提供することはできませんでしたが、マルチスレッドヒープ破損のデバッグは科学よりも芸術です。 Googleの「WinDbgヒープ破損」については、この件に関する多くの記事があります。
また、動的または静的Cランタイムライブラリに対してリンクしているかどうかを確認することもできます。 DLLファイルが静的Cランタイムライブラリに対してリンクしている場合、DLLファイルには個別のヒープがあります。
したがって、あるDLLでオブジェクトを作成し、別のDLLでオブジェクトを解放しようとすると、上記と同じメッセージが表示されます。この問題は、別のStack Overflowの質問別のDLLに割り当てられたメモリの解放で参照されています。
これらのエラーがランダムに発生する場合、データ競合が発生する可能性が高くなります。確認してください:異なるスレッドから共有メモリポインターを変更しますか? Intel Thread Checkerは、マルチスレッドプログラムでこのような問題を検出するのに役立ちます。
どのタイプの割り当て関数を使用していますか?最近、Heap *スタイルの割り当て関数を使用して同様のエラーが発生しました。
HEAP_NO_SERIALIZE
オプションで誤ってヒープを作成していたことが判明しました。これにより、スレッドセーフなしでヒープ関数が実行されます。適切に使用すればパフォーマンスは向上しますが、マルチスレッドプログラムでHeapAllocを使用している場合は使用しないでください[1]。あなたの投稿があなたがマルチスレッドアプリを持っていると言っているので、私はこれについてだけ言及します。 HEAP_NO_SERIALIZEをどこでも使用している場合は、それを削除すると問題が解決する可能性があります。
[1]これが合法である特定の状況がありますが、Heap *への呼び出しをシリアル化する必要があり、通常はマルチスレッドプログラムの場合はそうではありません。
ツールを探すことに加えて、考えられる犯人を探すことを検討してください。マルチスレッド環境で実行するように設計およびテストされていない可能性がある、使用しているコンポーネントはありますか?または、あなたがしない単純なものknowはそのような環境で実行されています。
最後にそれが私に起こったとき、それは何年もの間バッチジョブから首尾よく使用されていたネイティブパッケージでした。しかし、この会社で.NET Webサービス(マルチスレッド)から使用されたのは初めてでした。それはそれでした-彼らはコードがスレッドセーフであることについて嘘をついていました。
_ CrtSetDbgFlag :_ CRTDBG_CHECK_ALWAYS_DFには、VC CRTヒープチェックマクロを使用できます。または_ CRTDBG_CHECK_EVERY_16_DF.._ CRTDBG_CHECK_EVERY_1024_DF。
私は同様の問題を抱えていた-それは非常にランダムにポップアップしました。おそらく、ビルドファイルで何かが破損していましたが、最初にプロジェクトをクリーンアップしてから再構築することで修正しました。
与えられた他の応答に加えて:
これらのエラーが発生する可能性があるのはどのようなものですか?ビルドファイルで何かが壊れています。
どのようにデバッグしますか?プロジェクトのクリーニングと再構築。修正された場合、これが問題である可能性があります。
私の経験を追加したいと思います。この数日で、アプリケーションでこのエラーのインスタンスを解決しました。私の場合、コードのエラーは次のとおりです。
Control.Invoke
を呼び出して、コールバックが属するネイティブオブジェクトをラップするマネージドオブジェクトを破棄します。Control.Invoke
が終了するまで、コールバック呼び出しでブロックされたままになります)。 boost::thread
を使用することを明確にする必要があるため、スレッド関数としてメンバー関数を使用します。Control.BeginInvoke
(私のGUIはWinformsで作成されます)を使用して、オブジェクトが破棄される前にネイティブスレッドを終了できるようにします(コールバックの目的は、スレッドが終了し、オブジェクトが破棄できることを正確に通知することです) 。