173

Visual Studio 2008で(ネイティブ)マルチスレッドC ++アプリケーションをデバッグしています。一見ランダムな場合に、「Windowsがブレークポイントをトリガーしました...」というエラーが表示されます。これは、ヒープ。これらのエラーは、すぐにアプリケーションをクラッシュさせるとは限りませんが、すぐにクラッシュする可能性があります。

これらのエラーの大きな問題は、破損が実際に発生した後にのみポップアップすることです。これにより、特にマルチスレッドアプリケーションでは、エラーの追跡とデバッグが非常に困難になります。

  • どのようなことがこれらのエラーを引き起こす可能性がありますか?

  • それらをデバッグするにはどうすればよいですか?

ヒント、ツール、方法、啓発...大歓迎です。

4

15 に答える 15

130

Windows用のデバッグツールと組み合わせたアプリケーションベリファイアは素晴らしいセットアップです。両方をWindowsDriverKitまたは軽量のWindowsSDKの一部として入手できます。(ヒープ破損の問題に関する以前の質問を調査したときに、Application Verifierについて知りました。)過去にもBoundsCheckerとInsure ++(他の回答で言及)を使用しましたが、ApplicationVerifierの機能の量には驚いていました。

電気柵(別名「efence」)、dmallocvalgrindなどはすべて言及する価値がありますが、これらのほとんどはWindowsよりも*nixで実行する方がはるかに簡単です。Valgrindは途方もなく柔軟性があります。私はそれを使用して多くのヒープの問題がある大規模なサーバーソフトウェアをデバッグしました。

他のすべてが失敗した場合、独自のグローバル演算子new/deleteおよびmalloc/calloc / reallocオーバーロードを提供できます-これを行う方法は、コンパイラーとプラットフォームによって少し異なります-これは少しの投資になります-しかし、それは長期的には報われるかもしれません。望ましい機能リストは、dmallocとelectricfence、および驚くほど優れた本「WritingSolidCode」から見覚えがあるはずです

  • 歩哨値:最大配置要件を尊重して、各割り当ての前後にもう少しスペースを確保します。魔法数で埋める(バッファオーバーフローとアンダーフロー、および時折「ワイルド」ポインタをキャッチするのに役立ちます)
  • alloc fill:新しい割り当てを0以外の魔法の値で埋めます-Visual C ++は、デバッグビルドですでにこれを実行します(初期化されていない変数の使用をキャッチするのに役立ちます)
  • フリーフィル:解放されたメモリを0以外の魔法の値で埋めます。ほとんどの場合、逆参照された場合にセグメンテーションフォールトをトリガーするように設計されています(ダングリングポインターをキャッチするのに役立ちます)
  • 遅延解放:解放されたメモリをしばらくヒープに戻さないでください。解放されたままにしておきますが、使用できません(より多くのダングリングポインタをキャッチし、近接するダブルフリーをキャッチします)
  • 追跡:割り当てが行われた場所を記録できると便利な場合があります

ローカルの自作システム(埋め込みターゲットの場合)では、実行時のオーバーヘッドがはるかに高いため、追跡を他のほとんどのものから分離していることに注意してください。


これらの割り当て関数/演算子をオーバーロードするその他の理由に興味がある場合は、「グローバル演算子を新規にオーバーロードして削除する理由はありますか?」に対する私の回答をご覧ください。; 恥知らずな自己宣伝はさておき、ヒープ破損エラーの追跡に役立つ他の手法や、他の適用可能なツールをリストします。


MSが使用するalloc/free / fence値を検索するときに、ここで自分の答えを見つけ続けるので、Microsoftのdbgheap塗りつぶし値をカバーする別の答えがあります

于 2009-06-18T04:46:28.500 に答える
36

アプリケーションでページヒープを有効にすることで、多くのヒープ破損の問題を検出できます。これを行うには、 Windows用のデバッグツールの一部として提供されているgflags.exeを使用する必要があります

Gflags.exeを実行し、実行可能ファイルの[画像ファイル]オプションで、[ページヒープを有効にする]オプションをオンにします。

次に、exeを再起動し、デバッガーに接続します。ページヒープを有効にすると、ヒープの破損が発生するたびにアプリケーションがデバッガーに侵入します。

于 2009-06-18T04:24:34.610 に答える
14

物事を本当に遅くし、多くのランタイムチェックを実行するにはmain()、Microsoft Visual StudioC++のまたは同等のものの上部に以下を追加してみてください

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );
于 2012-01-13T23:37:16.190 に答える
13

非常に関連性の高い記事は、ApplicationVerifierとDebugdiagを使用したヒープ破損のデバッグです

于 2009-08-06T09:17:27.790 に答える
8

どのようなことがこれらのエラーを引き起こす可能性がありますか?

バッファの終了後に書き込む、またはヒープに解放された後にバッファに書き込むなど、メモリを使用していたずらなことを行う。

それらをデバッグするにはどうすればよいですか?

実行可能ファイルに自動境界チェックを追加する機器を使用します。つまり、Unixではvalgrind、WindowsではBoundsChecker(WikipediaではPurifyとInsure ++も推奨)などのツールを使用します。

これらはアプリケーションの速度を低下させるため、ソフトリアルタイムアプリケーションの場合は使用できない可能性があることに注意してください。

別の可能なデバッグ支援/ツールは、MicroQuillのHeapAgentである可能性があります。

于 2009-06-18T00:09:33.063 に答える
8

解放されたメモリへのアクセスの検出から得た簡単なヒントの1つは、次のとおりです。

メモリブロックにアクセスするすべてのステートメントをチェックせずにエラーをすばやく特定したい場合は、ブロックを解放した後にメモリポインタを無効な値に設定できます。

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif
于 2009-06-18T08:54:18.327 に答える
5

私が便利で毎回機能する最高のツールは、コードレビューです(優れたコードレビュー担当者による)。

コードレビュー以外に、最初にページヒープを試してみます。ページヒープの設定には数秒かかりますが、運が良ければ問題を特定できる可能性があります。

ページヒープがうまくいかない場合は、MicrosoftからWindows用のデバッグツールをダウンロードして、WinDbgの使用方法を学びます。申し訳ありませんが、より具体的なヘルプを提供することはできませんが、マルチスレッドヒープの破損をデバッグすることは、科学よりも芸術です。グーグルは「WinDbgヒープの破損」を意味し、このテーマに関する多くの記事を見つける必要があります。

于 2009-06-18T04:20:42.877 に答える
4

また、動的または静的Cランタイムライブラリに対してリンクしているかどうかを確認することもできます。DLLファイルが静的Cランタイムライブラリに対してリンクしている場合、DLLファイルには個別のヒープがあります。

したがって、あるDLLでオブジェクトを作成し、それを別のDLLで解放しようとすると、上記と同じメッセージが表示されます。この問題は、別のスタックオーバーフローの質問である別のDLLに割り当てられたメモリの解放で参照されています。

于 2011-02-04T01:20:28.437 に答える
3

どのタイプの割り当て機能を使用していますか?最近、ヒープ*スタイルの割り当て関数を使用して同様のエラーが発生しました。

HEAP_NO_SERIALIZEオプションを使用して誤ってヒープを作成していたことが判明しました。これにより、基本的にヒープ関数がスレッドセーフなしで実行されます。適切に使用するとパフォーマンスが向上しますが、マルチスレッドプログラムでHeapAllocを使用している場合は使用しないでください[1]。あなたの投稿がマルチスレッドアプリを持っていると述べているので、私はこれについてのみ言及します。どこかでHEAP_NO_SERIALIZEを使用している場合は、それを削除すると、問題が解決する可能性があります。

[1]これが合法である特定の状況がありますが、ヒープ*への呼び出しをシリアル化する必要があり、通常、マルチスレッドプログラムには当てはまりません。

于 2009-06-18T00:25:27.133 に答える
3

これらのエラーがランダムに発生する場合は、データの競合が発生している可能性が高くなります。確認してください:異なるスレッドからの共有メモリポインタを変更しますか?Intelスレッドチェッカーは、マルチスレッドプログラムでこのような問題を検出するのに役立つ場合があります。

于 2009-06-18T17:39:51.080 に答える
1

ツールを探すことに加えて、犯人の可能性を探すことを検討してください。マルチスレッド環境で実行するように設計およびテストされていない可能性のある、使用しているコンポーネント(おそらくユーザーが作成していないコンポーネント)はありますか?または、単にあなたが知らないものがそのような環境で実行されました。

それが私に最後に起こったとき、それは何年もの間バッチジョブから首尾よく使用されてきたネイティブパッケージでした。しかし、この会社で.NET Webサービス(マルチスレッド)から使用されたのは初めてでした。それはそれでした-彼らはコードがスレッドセーフであることについて嘘をついていました。

于 2009-06-18T00:29:20.340 に答える
1

_CrtSetDbgFlagのVCCRTヒープチェックマクロを使用できます:_CRTDBG_CHECK_ALWAYS_DFまたは_CRTDBG_CHECK_EVERY_16_DF .._ CRTDBG_CHECK_EVERY_1024_DF

于 2011-11-07T11:28:36.313 に答える
0

私の経験を追加したいと思います。過去数日間で、アプリケーションでこのエラーのインスタンスを解決しました。私の特定のケースでは、コードのエラーは次のとおりです。

  • 反復中にSTLコレクションから要素を削除する(Visual Studioにはこれらをキャッチするためのデバッグフラグがあると思います。コードレビュー中にキャッチしました)
  • これはもっと複雑です、私はそれをステップに分けます:
    • ネイティブC++スレッドから、マネージコードにコールバックします
    • 管理対象の土地でControl.Invoke、コールバックが属するネイティブオブジェクトをラップする管理対象オブジェクトを呼び出して破棄します。
    • オブジェクトはネイティブスレッド内でまだ生きているので(終了するまでコールバック呼び出しでブロックされたままになりますControl.Invoke)。を使用していることを明確にする必要があるためboost::thread、スレッド関数としてメンバー関数を使用します。
    • 解決策Control.BeginInvoke代わりに(私のGUIはWinformsで作成されています)を使用して、オブジェクトが破棄される前にネイティブスレッドを終了できるようにします(コールバックの目的は、スレッドが終了してオブジェクトが破棄される可能性があることを正確に通知することです)。
于 2012-05-23T16:40:39.163 に答える
0

私も同様の問題を抱えていました-そしてそれはかなりランダムに現れました。ビルドファイルで何かが破損している可能性がありますが、最初にプロジェクトをクリーンアップしてから再構築することで修正することになりました。

したがって、与えられた他の応答に加えて:

どのようなことがこれらのエラーを引き起こす可能性がありますか? ビルドファイルで何かが破損しています。

それらをデバッグするにはどうすればよいですか? プロジェクトのクリーニングと再構築。修正された場合、これが問題である可能性があります。

于 2016-10-30T22:18:33.973 に答える
0

私もこの問題に直面しました。私の場合、xサイズのメモリを割り当て、x+nサイズのデータ​​を追加しました。したがって、解放すると、ヒープオーバーフローが発生しました。割り当てられたメモリが十分であることを確認し、メモリに追加されたバイト数を確認してください。

于 2020-08-12T06:45:38.703 に答える