64

ヒープを破損しているマルチスレッドC++ アプリケーションで作業しています。この破損を特定するための通常のツールは適用できないようです。ソース コードの古いビルド (18 か月前) は最新のリリースと同じ動作を示します。欠点としては、ソース デルタを使用して、バグがいつ導入されたかを特定できません。リポジトリには多くのコード変更があります。

クラッシュ動作のプロンプトは、このシステムでスループットを生成することです。つまり、内部表現に変更されたデータのソケット転送です。アプリに定期的に例外を発生させる一連のテスト データがあります (さまざまな場所、さまざまな原因 - ヒープ割り当ての失敗など: ヒープの破損)。

この動作は、CPU パワーまたはメモリ帯域幅に関連しているようです。マシンのそれぞれが多ければ多いほど、クラッシュしやすくなります。ハイパースレッディング コアまたはデュアルコア コアを無効にすると、破損の割合が減少します (ただし、完全にはなくなりません)。これは、タイミング関連の問題を示唆しています。

問題は次のとおりです:
軽量のデバッグ環境 (たとえばVisual Studio 98 / AKA MSVC6) で実行すると、ヒープの破損はかなり簡単に再現できますalloc;。洗練されたデバッグ環境 (Rational Purify、VS2008/MSVC9または Microsoft Application Verifier でさえ) システムはメモリ速度の制約を受け、クラッシュしません (メモリの制約: CPU が を超えていない50%、ディスク ライトが点灯していない、プログラムが可能な限り高速に動作している、ボックスが1.3G2G の RAM を消費している) . したがって、問題を再現できる (ただし原因は特定できない) か、原因または再現できない問題を特定できるかの選択肢があります。

次の場所に関する私の現在の最良の推測は次のとおりです。

  1. 非常に汚れたボックスを取得します (現在の開発ボックスを置き換えるには: 2Gb RAM にE6550 Core2 Duo); これにより、強力なデバッグ環境での実行時に誤動作を引き起こすクラッシュを再現できるようになります。また
  2. 演算子newandを書き換えて、 anddeleteを使用し、使用が終了したらすぐにメモリを読み取り専用としてマークします。実行して、解放されたメモリに書き込んでいる悪者を OS に捕まえさせます。はい、これは絶望のしるしです。一体誰が書き直しているのですか?! これにより、Purify などと同じくらい遅くなるのだろうか。VirtualAllocVirtualProtectMSVC6newdelete

いいえ、Purify インスツルメンテーションを組み込んで出荷することはできません。

同僚がちょうど通り過ぎて、「スタック オーバーフローですか? 今、スタック オーバーフローが発生していますか?!?」と尋ねました。

そして今、質問:ヒープ破損者を見つけるにはどうすればよいですか?


更新: バランスが取れてnew[]おりdelete[]、問題の解決に向けて長い道のりを歩んでいるようです. アプリがクラッシュするまでに 15 分ではなく、約 2 時間かかるようになりました。まだありません。さらに提案はありますか?ヒープの破損が続く。

更新: Visual Studio 2008 でのリリース ビルドは劇的に優れているようです。現在の疑惑は、にSTL付属する実装にかかっていVS98ます。


  1. 問題を再現します。Dr Watsonさらなる分析に役立つ可能性のあるダンプが生成されます。

私はそれをメモしておきますが、ワトソン博士がつまずくのは事後であって、ヒープが踏みつけられたときではないことを懸念しています.

別の試みWinDebugは、非常に強力であると同時に軽量でもあるデバッグツールとして使用することです。

現時点では、これもうまくいきました。何かがうまくいかない限り、あまり助けにはなりません。行為中の荒らしを捕まえたい。

おそらく、これらのツールを使用すると、少なくとも問題を特定のコンポーネントに絞り込むことができます。

私はあまり希望を持っていませんが、絶望的な時代が求められています...

また、プロジェクトのすべてのコンポーネントが正しいランタイム ライブラリ設定 ( C/C++ tabVS 6.0 プロジェクト設定のコード生成カテゴリ) を持っていることを確認しますか?

いいえ、そうではありません。明日、ワークスペース (58 個のプロジェクト) を調べて、それらがすべてコンパイルされ、適切なフラグでリンクされていることを確認するのに数時間を費やします。


更新: これには 30 秒かかりました。ダイアログですべてのプロジェクトを選択しSettings、適切な設定を持たないプロジェクトが見つかるまで選択を解除します (それらはすべて適切な設定を持っていました)。

4

15 に答える 15

29

私の最初の選択肢は、 pageheap.exeなどの専用のヒープ ツールです。

new と delete を書き直すと便利かもしれませんが、それでは下位レベルのコードによってコミットされた割り当てをキャッチできません。これが必要な場合は、low-level alloc APIMicrosoft Detours を使用して s を迂回することをお勧めします。

また、次のようなサニティ チェック: ランタイム ライブラリの一致を確認する (リリースとデバッグ、マルチスレッドとシングルスレッド、dll と静的ライブラリ)、不適切な削除を探す (たとえば、delete [] があったはずの場所を削除する)使用されている)、割り当てを混在させたり一致させたりしていないことを確認してください。

また、スレッドを選択的にオフにして、問題がいつ解消されるかを確認してください。

最初の例外発生時のコール スタックなどはどのようになっていますか?

于 2008-08-04T07:51:02.997 に答える
10

私は仕事で同じ問題を抱えています(私たちもVC6時々使用します)。そして、それに対する簡単な解決策はありません。私はいくつかのヒントしかありません:

  • 本番マシンで自動クラッシュ ダンプを試してください (「プロセス ダンパー」を参照)。私の経験によると、ワトソン博士はダンピングに最適ではありません。
  • コードからすべてのcatch(...)を削除します。多くの場合、重大なメモリ例外が隠されています。
  • Advanced Windows Debuggingを確認してください- あなたのような問題に対するすばらしいヒントがたくさんあります。これを心からお勧めします。
  • STLトライSTLPortアンド チェック ビルドを使用する場合。無効なイテレータは地獄です。

幸運を。あなたのような問題は、解決するのに数か月かかります。これに備えて...

于 2008-08-06T12:41:52.737 に答える
8

元のアプリケーションを次のコマンドで実行しますADplus -crash -pn appnename.exe 。メモリの問題がポップアップすると、大きなダンプが表示されます。

ダンプを分析して、破損したメモリの場所を特定できます。運が良ければ、上書きメモリは一意の文字列であり、どこから来たのかを把握できます。運が悪ければ、ヒープを掘り下げてwin32、元のメモリの特性を把握する必要があります。(ヒープ-xが役立つ場合があります)

何がめちゃくちゃになったかがわかったら、特別なヒープ設定を使用してappverifierの使用を絞り込むことができます。DLLつまり、監視する対象、または監視する割り当てサイズを指定できます。

うまくいけば、これは犯人を捕まえるのに十分な監視をスピードアップするでしょう。

私の経験では、フルヒープベリファイアモードは必要ありませんでしたが、クラッシュダンプの分析とソースの参照に多くの時間を費やしました。

PS:DebugDiagを 使用してダンプを分析できます。DLL破損したヒープの所有を指摘し、その他の有用な詳細を提供できます。

于 2008-09-16T07:33:25.580 に答える
8

独自の malloc 関数と free 関数を作成することで、かなり幸運に恵まれました。本番環境では、標準の malloc と free を呼び出すだけですが、デバッグでは、必要なことを何でも実行できます。また、これらの関数を使用するために new 演算子と delete 演算子をオーバーライドするだけの単純な基本クラスもあります。作成したクラスは、そのクラスから単純に継承できます。コードが大量にある場合、malloc と free の呼び出しを新しい malloc と free に置き換えるのは大変な作業になるかもしれませんが (realloc を忘れないでください!)、長い目で見れば非常に役に立ちます。

Steve Maguire の本Writing Solid Code (強くお勧めします) には、これらのルーチンで実行できるデバッグの例があります。

  • 割り当てを追跡してリークを見つける
  • 必要以上のメモリを割り当て、メモリの最初と最後にマーカーを配置します。フリールーチン中に、これらのマーカーがまだそこにあることを確認できます。
  • 割り当て時 (初期化されていないメモリの使用状況を見つけるため) および解放時 (解放されたメモリの使用状況を見つけるため) にマーカーを付けてメモリを memset します。

もう 1 つの良い考えは、、、またはなどを使用しないことです。常に、、およびを使用します。バッファの末尾を削除しないようにするために、これらの独自のバージョンも作成しましたが、これらにも多くの問題がありました。strcpystrcatsprintfstrncpystrncatsnprintf

于 2008-08-22T17:11:22.950 に答える
4

この問題には、ランタイム分析と静的分析の両方で対処する必要があります。

静的解析の場合は、PREfast ( cl.exe /analyze) を使用してコンパイルすることを検討してください。不一致deletedelete[]、バッファ オーバーラン、およびその他の多くの問題を検出します。L4ただし、特にプロジェクトがまだ修正されていない場合は、数キロバイトの L6 警告を通過する準備をしてください。

PREfast は Visual Studio Team System で利用でき、どうやらWindows SDK の一部として利用できます。

于 2008-10-12T21:55:18.483 に答える
3

これはメモリ不足の状態ですか?NULLもしそうなら、 std :: bad_allocをスローするのではなく、newが戻ってきている可能性があります。古いVC++コンパイラはこれを適切に実装していませんでした。で構築されたアプリをクラッシュさせるレガシーメモリ割り当ての失敗に関する記事があります。 STLVC6

于 2008-09-02T06:03:55.913 に答える
2

メモリ破損の明らかなランダム性は、スレッド同期の問題に非常によく似ています。バグは、マシンの速度に応じて再現されます。オブジェクト(メモリの塊)がスレッド間で共有され、同期(クリティカルセクション、ミューテックス、セマフォ、その他)プリミティブがクラスごと(オブジェクトごと、クラスごと)に基づいていない場合、状況が発生する可能性がありますここで、クラス(メモリのチャンク)は、使用中に削除/解放されるか、削除/解放後に使用されます。

そのためのテストとして、各クラスとメソッドに同期プリミティブを追加できます。これにより、多くのオブジェクトが相互に待機する必要があるため、コードの速度が低下しますが、ヒープの破損がなくなると、ヒープの破損の問題がコードの最適化の問題になります。

于 2008-08-25T19:55:09.893 に答える
1

古いビルドを試してみましたが、リポジトリの履歴をさかのぼって、バグがいつ導入されたかを正確に確認できない理由はありますか?

それ以外の場合は、問題を追跡するのに役立つ何らかの単純なログを追加することをお勧めしますが、具体的に何をログに記録したいかはわかりません。

この問題を正確に引き起こす可能性があるものを見つけることができれば、Google と取得している例外のドキュメントを介して、コードで何を探すべきかについてさらに洞察が得られるかもしれません。

于 2008-08-04T07:48:51.440 に答える
1

私の最初のアクションは次のとおりです。

  1. 「リリース」バージョンでバイナリをビルドしますが、デバッグ情報ファイルを作成します (プロジェクト設定でこの可能性を見つけることができます)。
  2. 問題を再現するマシンで、ワトソン博士をデフォルトのデバッガ (DrWtsn32 -I) として使用します。
  3. 問題を再現します。ワトソン博士は、さらなる分析に役立つ可能性のあるダンプを生成します。

もう 1 つの試みは、非常に強力であると同時に軽量でもあるデバッグ ツールとして WinDebug を使用することです。

おそらく、これらのツールを使用すると、少なくとも問題を特定のコンポーネントに絞り込むことができます。

また、プロジェクトのすべてのコンポーネントのランタイム ライブラリ設定が正しいことを確認していますか (C/C++ タブ、VS 6.0 プロジェクト設定の [コード生成] カテゴリ)。

于 2008-08-04T08:26:55.967 に答える
1

したがって、あなたが持っている限られた情報から、これは 1 つまたは複数の組み合わせになる可能性があります。

  • ヒープの不適切な使用、つまり、二重解放、解放後の読み取り、解放後の書き込み、同じヒープ上の複数のスレッドからの割り当てと解放で HEAP_NO_SERIALIZE フラグを設定する
  • メモリ不足
  • 不正なコード (バッファ オーバーフロー、バッファ アンダーフローなど)
  • 「タイミング」の問題

それが最初の 2 つではあるが最後ではない場合は、いずれかの pageheap.exe でキャッチされているはずです。

これは、コードが共有メモリにアクセスする方法が原因である可能性が高いことを意味します。残念ながら、それを追跡するのはかなり苦痛です。共有メモリへの非同期アクセスは、多くの場合、奇妙な「タイミング」の問題として現れます。共有メモリへのアクセスをフラグと同期するために取得/解放セマンティクスを使用しない、ロックを適切に使用しないなどのこと。

少なくとも、以前に提案されたように、何らかの方法で割り当てを追跡できると役立ちます。少なくとも、ヒープが破損するまでに実際に何が起こったのかを確認し、そこから診断を試みることができます。

また、割り当てを複数のヒープに簡単にリダイレクトできる場合は、それを試して、問題が解決するか、より再現可能なバグのある動作になるかを確認することをお勧めします。

VS2008 でテストしていたとき、[メモリの節約] を [はい] に設定して HeapVerifier を実行しましたか? これにより、ヒープ アロケーターのパフォーマンスへの影響が軽減される可能性があります。(さらに、Debug->Start with Application Verifier で実行する必要がありますが、既にご存知かもしれません。)

また、Windbg を使用したデバッグや、!heap コマンドのさまざまな使用法を試すこともできます。

MSN

于 2008-08-22T16:51:06.710 に答える
0

同様の問題を解決するために必要なわずかな時間。問題が解決しない場合は、これを行うことをお勧めします: new/delete および malloc/calloc/realloc/free へのすべての呼び出しを監視します。すべての呼び出しを登録するための関数をエクスポートする単一の DLL を作成します。この関数は、コード ソース、割り当てられた領域へのポインター、およびこの情報をテーブルに保存する呼び出しの種類を識別するためのパラメーターを受け取ります。割り当てられた/解放されたすべてのペアが削除されます。最後または必要になった後に、残りのデータのレポートを作成するために他の関数を呼び出します。これにより、間違った呼び出し (new/free または malloc/delete) または欠落を特定できます。コードでバッファが上書きされた場合、保存された情報は間違っている可能性がありますが、各テストは特定された障害の解決策を検出/発見/含める可能性があります。エラーの特定に役立つ多くの実行。幸運を。

于 2008-12-19T11:52:50.977 に答える
0

カスタム malloc/free の Graeme の提案は良い考えです。活用するためのハンドルを与えるために、破損に関するいくつかのパターンを特徴付けることができるかどうかを確認してください。

たとえば、常に同じサイズ (たとえば 64 バイト) のブロックにある場合は、malloc/free ペアを変更して、独自のページに常に 64 バイトのチャンクを割り当てます。64 バイトのチャンクを解放したら、そのページにメモリ保護ビットを設定して、読み取りと書き込みを防止します (VirtualQuery を使用)。次に、このメモリにアクセスしようとすると、ヒープが破損するのではなく、例外が生成されます。

これは、未処理の 64 バイト チャンクの数が中程度であるか、ボックスに焼き付けるメモリがたくさんあることを前提としています。

于 2008-09-02T04:23:34.610 に答える
0

いくつかの提案。あなたは W4 で大量の警告について言及しています - 時間をかけてコードを修正し、警告レベル 4 できれいにコンパイルすることをお勧めします - これは微妙な見つけにくいバグを防ぐのに大いに役立ちます。

2 つ目 - /analyze スイッチの場合 - 確かに大量の警告が生成されます。自分のプロジェクトでこのスイッチを使用するために、#pragma warning を使用して /analyze によって生成される追加の警告をすべてオフにする新しいヘッダー ファイルを作成しました。次に、ファイルのさらに下で、重要な警告のみをオンにします。次に、/FI コンパイラ スイッチを使用して、このヘッダー ファイルが最初にすべてのコンパイル ユニットに含まれるように強制します。これにより、出力を制御しながら /analyze スイッチを使用できるようになります。

于 2009-10-03T16:48:57.280 に答える
0

これは競合状態だと思いますか?複数のスレッドが 1 つのヒープを共有していますか? HeapCreate を使用して各スレッドにプライベート ヒープを与えることができますか。そうすれば、HEAP_NO_SERIALIZE を使用して高速に実行できます。それ以外の場合、システム ライブラリのマルチスレッド バージョンを使用している場合は、ヒープをスレッド セーフにする必要があります。

于 2009-07-30T13:48:40.267 に答える
0

新規/削除を書き直すことを選択した場合、私はこれを行い、簡単なソースコードを次の場所に用意しています:

http://gandolf.homelinux.org/~smhanov/blog/?id=10

これにより、メモリ リークが検出され、メモリ ブロックの前後にガード データが挿入されてヒープの破損が検出されます。#include "debug.h" をすべての CPP ファイルの先頭に置き、DEBUG と DEBUG_MEM を定義するだけで統合できます。

于 2008-09-17T13:40:35.547 に答える