ヒープ破損エラーをデバッグする方法は?


165

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

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

  • これらのエラーの原因は何ですか?

  • どうすればデバッグできますか?

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

回答:


128

Windows用のデバッグツールと組み合わせたApplication Verifierは、すばらしいセットアップです。どちらも、Windows Driver Kitまたは軽量のWindows SDKの一部として入手できます。(ヒープの破損の問題に関する以前の質問を調べたときに、Application Verifier について知りました。)以前にBoundsCheckerとInsure ++(他の回答で述べた)を使用したこともありますが、Application Verifierの機能に驚かされました。

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

他のすべてが失敗した場合、独自のグローバルオペレーターnew / deleteおよびmalloc / calloc / reallocオーバーロードを提供できます-これを行う方法は、コンパイラーとプラットフォームによって少し異なります-これは少しの投資になります-しかし、長期的には見返りが見込めます。望ましい機能のリストは、dmallocとelectricfence、そして驚くほど優れた著書「Writing Solid Code」からおなじみのはずです。

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

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


これらの割り当て関数/演算子をオーバーロードする他の理由に興味がある場合は、「グローバルオペレーターnewおよびdeleteをオーバーロードする理由はありますか?」に対する私の答えを見てください。; 恥知らずな自己宣伝はさておき、ヒープ破損エラーの追跡に役立つその他のテクニックと、その他の適用可能なツールを示します。


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


3
Application Verifierについて注意する価値のある小さなことの1つ:シンボル検索パスでMicrosoftシンボルサーバーシンボルの前にApplication Verifierのシンボルを登録する必要があります。それを使用する場合...少し検索して、なぜ!avrfがなかったのかを理解してください。必要なシンボルを見つける。
レアンダー

Application Verifierは大きな助けとなり、推測と組み合わせることで、問題を解決することができました!たくさんの人に、そして有益なポイントを教えてくれてありがとう。

Application VerifierはWinDbgで使用する必要がありますか、それともVisual Studioデバッガーで動作する必要がありますか?私はそれを使おうとしましたが、VS2012でデバッグしたときにエラーが発生したり、何も表示されなかったりします。
ネイサンリード2014年

@NathanReed:VSでも動作すると思います-msdn.microsoft.com/en-us/library/ms220944(v=vs.90).aspxを参照してください -このリンクはVS2008用であることに注意してください。それ以降のバージョンについて確認してください。メモリは少しあいまいですが、「以前の質問」リンクで問題が発生したときは、Application Verifierを実行してオプションを保存し、プログラムを実行しただけです。クラッシュすると、デバッグにVSを選択しました。AVは以前にクラッシュ/アサートしました。!avrfコマンドは、私の知る限り、WinDbgに固有です。うまくいけば、他の人がより多くの情報を提供できるようになります!
2014年

ありがとう。私は実際に元の問題を解決しましたが、結局のところヒープの破損ではなく、それ以外の何かであることが判明したため、おそらくApp Verifierが何も見つからなかった理由を説明しています。:)
ネイサンリード

35

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

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

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


はい、ただし、この関数呼び出しをコールスタックダンプで取得すると(メモリ破損のクラッシュ後)、wow64!Wow64NotifyDebugger、どうすればよいですか?私のアプリケーションで何が問題になっているのかまだわかりません
Guillaume07

ここでヒープ破損をデバッグするためにgflagsを試してみましたが、非常に便利な小さなツールです。強くお勧めします。解放されたメモリにアクセスしていたことがわかりました。gflagsでインストゥルメントすると、すぐにデバッガに侵入します...便利です!
デイブF

素晴らしいツール!Windowsが破損のアドレスを言っていないので、私が何日も探していたバグを見つけただけで、「何か」が間違っているだけで、それは本当に役に立ちません。
Devolus 2016年

パーティーには少し遅れましたが、ページヒープをオンにすると、デバッグしているアプリケーションのメモリ使用量が大幅に増加していることに気付きました。残念ながら、ヒープ破損の検出がトリガーされる前に(32ビット)アプリケーションのメモリが不足するまでです。その問題に取り組む方法はありますか?
uceumern 2016年

13

実際に速度を落として多くのランタイムチェックを実行するには、main()Microsoft Visual Studio C ++の上部または同等のものに以下を追加してみてください

_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF );


8

これらのエラーの原因は何ですか?

メモリを使用していたずらを行う。たとえば、バッファの最後に書き込んだり、バッファが解放されてヒープに戻った後にバッファに書き込んだりする。

どうすればデバッグできますか?

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

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

別の可能なデバッグ支援/ツールは、MicroQuillのHeapAgentかもしれません。


1
デバッグランタイム(/ MDdまたは/ MTdフラグ)を使用してアプリケーションを再構築することが、最初のステップです。これらはmallocとfreeで追加のチェックを実行し、多くの場合、バグの場所を絞り込むのに効果がありません。
ロシア語を採用

MicroQuillのHeapAgent:それについて書かれたり聞いたりしたことはほとんどありませんが、ヒープの破損については、リストに含まれているはずです。
Samrat Patil

1
BoundsCheckerはスモークテストとしては問題なく動作しますが、そのプログラムを本番環境で実行しようとしている間は、その下でプログラムを実行することすら考えないでください。スローダウンは、使用しているオプション、およびコンパイラーのインスツルメンテーション機能を使用しているかどうかに応じて、60倍から300倍の範囲になります。免責事項:私はMicro Focusの製品を管理している人の1人です。
リック・Papo

8

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

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

#ifdef _DEBUG // detect the access to freed memory
#undef free
#define free(p) _free_dbg(p, _NORMAL_BLOCK); *(int*)&p = 0x666;
#endif

5

私が便利だと思っていつも働いていた最良のツールは、コードレビュー(優れたコードレビュー担当者による)です。

コードレビュー以外に、最初にPage Heapを試します。ページヒープのセットアップには数秒かかり、運が良ければ問題を特定できるかもしれません。

ページヒープでうまくいかない場合は、MicrosoftからDebugging Tools for Windowsをダウンロードして、WinDbgの使い方を学んでください。申し訳ありませんが、具体的なヘルプは提供できませんでしたが、マルチスレッドのヒープ破損のデバッグは、科学というより芸術です。「WinDbgヒープの破損」についてGoogleを検索すると、この件に関する多くの記事が見つかります。


4

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

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


3

どのタイプの割り当て関数を使用していますか?最近、Heap *スタイルの割り当て関数を使用して同様のエラーに遭遇しました。

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

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


はい:アプリケーションのコンパイラ/ビルドオプションを調べ、Cランタイムライブラリの「マルチスレッド」バージョンに対してリンクするようにビルドされていることを確認します。
ChrisW、2009年

HeapAllocスタイルAPIの@ChrisWはこれが異なります。実際には、リンク時ではなく、ヒープ作成時に変更できるパラメーターです。
JaredPar

ああ。OPがそのヒープについて話しているのではなく、CRTのヒープについて話しているのではないかと思いました。
ChrisW 2009年

@ChrisW、質問はかなり曖昧ですが、私は1週間前に詳述した問題にぶつかっただけなので、心に新鮮です。
JaredPar

3

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


1

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

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


1

あなたは、のためにVC CRTヒープ・チェックのマクロを使用することができます_CrtSetDbgFlag_CRTDBG_CHECK_ALWAYS_DFまたは_CRTDBG_CHECK_EVERY_16_DF ... _CRTDBG_CHECK_EVERY_1024_DF


0

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

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

0

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

与えられた他の応答に加えて:

これらのエラーの原因は何ですか? ビルドファイルが壊れています。

どうすればデバッグできますか? プロジェクトのクリーニングと再構築。修正されている場合、これはおそらく問題でした。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.