/ MTおよび/ MDビルドがクラッシュしますが、デバッガーが接続されていない場合のみ:デバッグ方法[複製]
-
03-07-2019 - |
質問
この質問にはすでに回答があります:
Visual Studio 2005を使用してコンパイルおよびリンクされた小さなシングルスレッドC ++アプリケーションがあり、ブースト(crc、program_options、tokenizer)、STLの一部、およびその他のシステムヘッダーが使用されています。
(主な目的は、.csvを読み込み、.datの形式を「説明」するカスタムバイナリ.datとペアの.h宣言構造を生成することです。)
リリース時のみ、デバッガーの外部で実行すると、ツールがクラッシュします(NULLのアクセス違反)。例えば。 F5を押してもツールはクラッシュしませんが、Ctrl-F5はクラッシュします。デバッガを再接続すると、次のスタックが取得されます。
ntdll.dll!_RtlAllocateHeap@12() + 0x26916 bytes
csv2bin.exe!malloc(unsigned int size=0x00000014) Line 163 + 0x63 bytes C
csv2bin.exe!operator new(unsigned int size=0x00000014) Line 59 + 0x8 bytes C++
>csv2bin.exe!Record::addField(const char * string=0x0034aac8) Line 62 + 0x7 bytes C++
csv2bin.exe!main(int argc=0x00000007, char * * argv=0x00343998) Line 253 C++
csv2bin.exe!__tmainCRTStartup() Line 327 + 0x12 bytes C
クラッシュする行は、やや無害に見える割り当てです:
pField = new NumberField(this, static_cast<NumberFieldInfo*>(pFieldInfo));
...まだコンストラクタに到達したとは思わない。コンストラクタにジャンプする前にメモリを割り当てているだけだ。また、クラッシュするまでにこのコードを何十回も実行しました。通常は一貫性のある(ただし、疑わしい場所ではありません)場所です。
/ MTdまたは/ MDd(デバッグランタイム)でコンパイルすると問題はなくなり、/ MTまたは/ MDを使用すると問題が再発します。
NULLはスタックからロードされ、メモリビューで確認できます。 _RtlAllocateHeap @ 12 + 0x26916バイトは、誤ったジャンプが行われたように、巨大なオフセットのように見えます。
デバッグビルドで _HAS_ITERATOR_DEBUGGING
を試しましたが、疑わしいものは何も表示されませんでした。
Record :: addFieldの最初と最後にHeapValidateをドロップすると、クラッシュするまでOKヒープが表示されます。
これは以前は機能していました-現在と前回ツールをコンパイルしたとき(おそらく数年前、おそらく古いVSの下で)で何が変わったのかは完全にはわかりません。ブーストの古いバージョンを試しました(1.36対1.38)。
コードの手動調査に戻る前、またはこれをPC-Lintに供給してその出力を確認する前に、これを効果的にデバッグする方法に関する提案はありますか?
[コメントで情報をリクエストした場合、詳細情報で質問を更新させていただきます。]
解決
デバッガを接続した状態で実行する場合としない場合の違いは、OSデバッグヒープです(デバッガをアタッチしたときにコードの実行速度が遅いのはなぜですか)。環境変数_NO_DEBUG_HEAPを使用して、デバッグヒープをオフにできます。これは、コンピューターのプロパティ、またはVisual Studioのプロジェクト設定で指定できます。
デバッグヒープをオフにすると、デバッガが接続されていても同じクラッシュが発生するはずです。
とはいえ、メモリの破損はデバッグが困難な場合があることに注意してください。破損の実際の原因(バッファオーバーランなど)は、症状(クラッシュ)が発生する場所から非常に遠いことが多いためです。
他のヒント
アプリケーション検証ツール環境で_NO_DEBUG_HEAP = 1になったらこれを解決するのに非常に便利でした。受け入れられた答えはこちらをご覧ください:メモリが最後に解放された場所を見つけますか
おそらく、 pageheap に言及する価値もあります。これは、Application Verifierを見ているときに見つけたものです。似たような地面を覆っているように見えます。
(FYI、1文字のバッファオーバーフローでした:
m_pEnumName = (char*)malloc(strlen(data) /* missing +1 here */);
strcpy(m_pEnumName, data);
... strcpy
を直接使用しないという、とんでもなく良い引数です。
通常、newまたはmalloc内でのクラッシュは、malloc実装の(内部)構造が破損していることのヒントです。これは、ほとんどの場合、以前の割り当てを過ぎて書き込むことによって行われます(バッファオーバーフロー)。次に、newまたはmallocを次に呼び出すと、内部構造に無効なデータが含まれるようになるため、アプリがクラッシュします。
以前に割り当てられたスペースを上書きできるかどうかを確認します。
アプリケーションが移植可能な場合は、Linux上でビルドして、 Valgrind 。