web-dev-qa-db-ja.com

プログラムはリリースビルドとしてのみクラッシュします-デバッグ方法は?

ここに「シュレーディンガーの猫」タイプの問題があります-私のプログラム(実際には私のプログラムのテストスイートですが、プログラムですが)はクラッシュしますが、リリースモードでビルドされ、コマンドラインから起動されたときのみです。穴居人のデバッグ(つまり、いたるところにある厄介なprintf()メッセージ)によって、コードがクラッシュするテスト方法を決定しましたが、残念ながら、実際のクラッシュはデストラクタで発生するようです。きれいに実行される他のデストラクタ。

Visual Studio内でこのプログラムを実行しようとしても、クラッシュしません。 WinDbg.exeから起動する場合も同様です。クラッシュは、コマンドラインから起動したときにのみ発生します。これはWindows Vistaで発生しますが、残念ながら、今のところテストするためにXPマシンにアクセスできません。

Windowsにスタックトレースを出力させるか、somethingでプログラムが正常に終了したかのように単純に終了する以外に、本当にいいことです。ここでもっと意味のある情報を取得し、うまくいけばこのバグを修正する方法について誰にもアドバイスがありますか?

編集:問題は実際に範囲外の配列、 これについてはこの記事で詳しく説明します によって引き起こされました。この問題を見つけるのにあなたの助けをありがとう!

87
Nik Reiman

私が見たり聞いたりした100%のケースでは、CまたはC++プログラムはデバッガーで正常に実行されますが、外部で実行されると失敗します。 (デバッガーはスタックにより多くを置くため、重要なものを上書きする可能性は低くなります。)

120
James Curran

一般に変数の初期化が原因である前に、このような問題に遭遇したとき。デバッグモードでは、変数とポインターは自動的にゼロに初期化されますが、リリースモードでは初期化されません。したがって、このようなコードがある場合

int* p;
....
if (p == 0) { // do stuff }

デバッグモードではifのコードは実行されませんが、リリースモードpでは未定義の値が含まれており、0になることはほとんどないため、コードが実行されてクラッシュが頻繁に発生します。

初期化されていない変数のコードを確認します。これは配列の内容にも適用できます。

49
David Dibben

これまでのところ、リリースアプリケーションをデバッグするための利用可能な技術について真剣な概要を提供しようとしていません。

  1. リリースビルドとデバッグビルドは、多くの理由で異なる動作をします。ここに優れた概要があります。 これらの違いはそれぞれ、デバッグビルドに存在しないリリースビルド。

  2. デバッガの存在は、リリースビルドとデバッグビルドの両方で、プログラムの動作も変更する可能性がありますこの回答をご覧ください。 要するに、少なくともVisual Studioデバッガーは、プログラムにアタッチされたときに自動的にデバッグヒープを使用します。環境変数_NO_DEBUG_HEAPを使用して、デバッグヒープをオフにできます。これは、コンピューターのプロパティまたはVisual Studioのプロジェクト設定で指定できます。これにより、デバッガをアタッチするとクラッシュが再現可能になる場合があります。

    ヒープ破損のデバッグの詳細はこちら

  3. 前の解決策が機能しない場合、未処理の例外をキャッチして、クラッシュが発生したインスタンスに事後デバッガをアタッチする必要があります。あなたは使用することができますこのためのWinDbg、 利用可能な事後分析デバッガーとMSDNでのインストールに関する詳細

  4. 例外処理コードを改善できます。これが本番アプリケーションの場合、次のことを行う必要があります。

    a。 std::set_terminateを使用してカスタム終了ハンドラをインストールします

    この問題をローカルでデバッグする場合は、終了ハンドラー内で無限ループを実行し、std::terminateが呼び出されたことを通知するテキストをコンソールに出力できます。次に、デバッガーを接続し、呼び出しスタックを確認します。 または、この回答で説明されているようにスタックトレースを出力します。

    本番アプリケーションでは、理想的にはエラー報告を自宅に送り返してください ここで説明するように問題を分析できる小さなメモリダンプと一緒に

    b。 Microsoftの構造化例外処理メカニズムを使用すると、ハードウェアとソフトウェアの両方の例外をキャッチできます。 MSDNを参照 。 SEHを使用してコードの一部を保護し、a)と同じアプローチを使用して問題をデバッグできます。 SEHは、実稼働アプリからエラーレポートを送信するときに使用できる、発生した例外に関する詳細情報を提供します。

20
Sebastian

注目すべきこと:

配列オーバーラン-Visual Studioデバッガーはパディングを挿入し、クラッシュを停止する場合があります。

競合状態-複数のスレッドが関与している場合、競合状態の多くは、アプリケーションが直接実行されたときにのみ表示されます。

リンク-正しいライブラリを取り込むリリースビルドです。

試すべきこと:

ミニダンプ-本当に使いやすい(msdnで調べるだけ)ことで、各スレッドの完全なクラッシュダンプが得られます。出力をVisual Studioにロードするだけで、クラッシュ時にデバッグしているかのようになります。

15
morechilli

WinDbgを事後検証デバッガとして設定できます。これにより、デバッガーが起動し、クラッシュが発生したときにプロセスにアタッチされます。事後デバッグ用にWinDbgをインストールするには、/ Iオプションを使用します(capitalizedであることに注意してください):

windbg /I

詳細 こちら

原因に関しては、他の回答が示唆するように、おそらく単一化された変数です。

12
Franci Penov

数時間のデバッグの後、実際にバッファオーバーフローによって引き起こされた問題の原因が1バイトの違いを引き起こしていることがわかりました。

char *end = static_cast<char*>(attr->data) + attr->dataSize;

これはfencepostエラー(off-by-oneエラー)であり、次の方法で修正されました。

char *end = static_cast<char*>(attr->data) + attr->dataSize - 1;

奇妙なことに、コードのさまざまな部分の周りに_CrtCheckMemory()をいくつか呼び出して、常に1を返しました。「return false;」を置くことで問題の原因を見つけることができました。テストケースを呼び出してから、最終的に試行錯誤を通じて障害の場所を特定します。

コメントありがとうございます。今日windbg.exeについて多くのことを学びました! :)

9
Nik Reiman

Exeをリリース1としてビルドした場合でも、トレースをスタックできるようにするPDB(プログラムデータベース)ファイルを生成でき、限られた量の変数検査を実行できます。ビルド設定には、PDBファイルを作成するオプションがあります。これをオンにして再リンクします。次に、IDEから実行してみて、クラッシュするかどうかを確認します。もしそうなら、素晴らしいことです。次の2つのいずれかを実行できます。

  1. EXEを実行し、クラッシュする前にプロセスにアタッチ(Visual Studioの[ツール]メニュー)を実行します。
  2. クラッシュ後、デバッガーを起動するオプションを選択します。

PDBファイルを指すように求められたら、それらを見つけて見つけます。 PDBがEXEまたはDLLと同じ出力フォルダーに配置された場合、おそらく自動的に選択されます。

PDBは、スタックトレース、変数などを確認できる十分なシンボル情報を持つソースへのリンクを提供します。通常どおり値を検査できますが、最適化パスは物事のみを意味するため、誤った読み取り値を取得できることに注意してくださいレジスタに表示されるか、予期しない順序で発生します。

注意:ここではWindows/Visual Studio環境を想定しています。

7
Greg Whitfield

IDEは通常、初期化されていない変数の内容をゼロ、null、または他のそのような「賢明な」値に設定するため、このようなクラッシュはほとんど常に発生しますが、ネイティブで実行するとランダムシステムが拾うごみ。

したがって、エラーはほぼ確実に、適切に初期化される前にポインターを使用しているようなものを使用しており、IDE危険-または値はエラーチェックによって処理されます-しかし、リリースモードでは、何か厄介なことが行われます。

3
Cruachan

分析できるクラッシュダンプを取得するには:

  1. コードのpdbファイルを生成します。
  2. Exeとdllを同じアドレスにロードするためにリベースします。
  3. Dr。Watson などの事後分析デバッガーを有効にします
  4. crash Finder などのツールを使用して、クラッシュエラーアドレスを確認します。

windowsのデバッグツール のツールもチェックアウトする必要があります。アプリケーションを監視して、セカンドチャンス例外の前にあったすべてのファーストチャンス例外を確認できます。

それが役に立てば幸い...

3
Yuval Peled

アプリがあなたのものと同様に振る舞うときに問題が発生しました。 sprintfでの厄介なバッファオーバーランであることが判明しました。当然、デバッガをアタッチして実行すると機能しました。私がやったことは、未処理の例外フィルター( SetUnhandledExceptionFilter )をインストールすることで、無限にブロックしました(タイムアウト値がINFINITEの偽のハンドルでWaitForSingleObjectを使用)。

だから、あなたはの線に沿って何かをすることができます:

 long __stdcall MyFilter(EXCEPTION_POINTERS *)
 {
 HANDLE hEvt = :: CreateEventW(0,1,0,0); 
 if(hEvt)
 {
 if(WAIT_FAILED == :: WaitForSingleObject(hEvt、INFINITE))
 {
 // log failure 
} 
} 
 
} 
 // wmain/WinMainのどこか:
 SetUnhandledExceptionFilter(MyFilter); 

バグが現れた後、デバッガーを接続しました(GUIプログラムが応答しなくなりました)。

その後、ダンプを取り、後で作業することができます。

.dump/ma path_to_dump_file

または、すぐにデバッグします。最も簡単な方法は、ランタイム例外処理機構によってプロセッサコンテキストが保存された場所を追跡することです。

s-d esp 範囲 1003f

コマンドは、検索の長さを指定すると、CONTEXTレコードのスタックアドレス空間を検索します。私は通常、次のようなものを使用します 「l?10000」。通常、手つかずの例外フィルターフレームの近くにあるレコードとして、異常に大きな数値を使用しないでください。 1003fは、プロセッサの状態をキャプチャするために使用されるフラグの組み合わせです(CONTEXT_FULLに対応すると考えられます)。検索は次のようになります。

0:000> s-d esp l1000 1003f
0012c160 0001003f 00000000 00000000 00000000?...............

結果が返されたら、cxrコマンドでアドレスを使用します。

.cxr 0012c160

これにより、正確にクラッシュ時にこの新しいCONTEXTに移動します(アプリがクラッシュしたときに正確にスタックトレースを取得します)。さらに、以下を使用します。

.exr -1

どの例外が発生したかを正確に調べるため。

それが役に立てば幸い。

2
deemok

重要な操作を「アサート」マクロ内にラップしているために、これが発生することがあります。ご存知かもしれませんが、「アサート」はデバッグモードでのみ式を評価します。

Vista SP1には、実際にシステムに組み込まれた本当に素晴らしいクラッシュダンプジェネレーターがあります。残念ながら、デフォルトでは有効になっていません!

この記事を参照してください: http://msdn.Microsoft.com/en-us/library/bb787181(VS.85).aspx

このアプローチの利点は、影響を受けるシステムに追加のソフトウェアをインストールする必要がないことです。握って、裂いて、ベイビー!

1
nick

私の経験として、それはほとんどメモリ破損の問題です。

例えば ​​:

char a[8];
memset(&a[0], 0, 16);

: /*use array a doing some thing */

コードを実行すると、デバッグモードで正常になる可能性が非常に高くなります。

しかし、リリースでは、それはクラッシュする可能性があります。

私にとって、記憶が限界を超えている場所を探し回るのは面倒です。

Visual Leak Detector (windows)または valgrind (linux)のようないくつかのツールを使用するのが賢明です。

1
Gaiger Chen

私は多くの正しい答えを見てきました。しかし、私を助けたものはありません。私の場合、SSE命令非整列メモリの誤った使用法がありました。数学ライブラリ(使用している場合)を見て、SIMDサポートを無効にし、クラッシュを再コンパイルして再現してみてください。

例:

プロジェクトには mathf が含まれ、STLベクトルのクラスを使用します:std :: vector <mathfu :: vec2>。 STLのデフォルトのアロケーターは必要な16バイトのアライメントを保証しないため、mathfu :: vec2アイテムの構築時に、このような使用法はおそらくクラッシュを引き起こします。 。この場合、アイデアを証明するために、#define MATHFU_COMPILE_WITHOUT_SIMD_SUPPORT 1mathf の各インクルードの前に、リリース構成で再コンパイルし、再度確認します。

DebugおよびRelWithDebInfo構成は私のプロジェクトではうまく機能しましたが、 リリース1。この動作の背後にある理由は、おそらくデバッガが割り当て/割り当て解除要求を処理し、メモリへのアクセスをチェックおよび検証するためにメモリのブックキーピングを行うためです。

私はVisual Studio 2015および2017環境で状況を経験しました。

1
Vlad Serhiienko

診断情報の取得に関する問題について、WinDbg.exeの代替としてadplus.vbsを使用しようとしましたか?実行中のプロセスにアタッチするには、次を使用します

adplus.vbs -crash -p <process_id>

または、クラッシュがすぐに発生した場合にアプリケーションを起動するには:

adplus.vbs -crash -sc your_app.exe

Adplus.vbsの詳細については、次を参照してください。 http://support.Microsoft.com/kb/28635

1
DocMax

デバッガーが添付されたNtdll.dll

コマンドライン/デスクトップから起動するのではなく、IDEまたはWinDbgからプログラムを起動することのわずかな違いは、デバッガーが接続された状態で起動する場合(つまり、IDEまたはWinDbg)ntdll .dllは、メモリの割り当て/解放に関して少し検証を実行する別のヒープ実装を使用します。

ntdll.dllの予期しないユーザーブレークポイント で関連情報を読むことができます。問題の特定に役立つツールの1つに PageHeap.exe があります。

クラッシュ分析

あなたが経験している「クラッシュ」とは何かを書きませんでした。プログラムがクラッシュし、Microsoftにエラー情報を送信するように求められたら、技術情報をクリックし、少なくとも例外コードを確認し、ある程度の労力で事後分析を実行できます(- ハイゼンバグ:一部のコンピューターでWinApiプログラムがクラッシュする) 手順について)

1
Suma

このようなエラーをデバッグする優れた方法は、デバッグビルドの最適化を有効にすることです。

1
Mgill404

グローバルフラグを有効にしてソフトウェアを実行できます(Windows用デバッグツールを参照)。非常に頻繁に問題を解決するのに役立ちます。

0
Marcin Gil

_ CrtCheckMemory()を使用して、割り当てられたメモリの状態を確認してください。すべてがうまくいけば、_ CrtCheckMemory[〜#〜] true [〜#〜]を返し、そうでなければ[〜#〜] false [〜#〜]

0
Vhaerun

リリースビルドのデバッグは、コードの行が実行されるように見える順序を変更する最適化のために苦痛になる場合があります。本当に混乱する可能性があります!

問題を少なくとも絞り込む1つの方法は、MessageBox()を使用して、プログラムのどの部分にコードが到達したかを示す簡単なステートメントを表示することです( "Starting Foo()"、 "Starting Foo2()");疑わしいコードの領域の関数の先頭にそれらを配置し始めます(クラッシュしたときに何をしていましたか?)。どの関数がわかるかは、メッセージボックスをコードのブロック、または関数内の個々の行に変更して、数行に絞り込みます。次に、変数の値の出力を開始して、クラッシュした時点での変数の状態を確認できます。

0
JTeagle

誰かが有益だと思うかもしれないというケースがあります。デバッグではなく、Qt Creatorのリリースでのみクラッシュしました。私は.iniファイルを使用していました(他のドライブにコピーできるアプリと、レジストリが破損した場合に設定が失われるアプリを好むため)。これは、アプリのディレクトリツリーに設定を保存するアプリに適用されます。デバッグビルドとリリースビルドが異なるディレクトリの下にある場合は、それらの間で異なる設定を持つこともできます。他のチェックインされていないプリファレンスをチェックインしました。それが私のクラッシュの原因であることが判明しました。良いこと、私はそれを見つけました。

言いたくありませんが、MS Visual Studio Community Editionでのみクラッシュを診断しました。 VSをインストールした後、Qt Creatorでアプリをクラッシュさせ、それをVisual Studio'sデバッガーで開くことを選択しました。私のQtアプリにはシンボル情報がありませんでしたが、Qtライブラリにはいくつかの情報がありました。それは問題のあるラインに私を導いた。どのメソッドが呼び出されているかを確認できたからです。 (それでも、Qtは便利で強力なクロスプラットフォームLGPLフレームワークだと思います。)

0
CodeLurker

GCCを使用して、似たようなことが一度起こりました。開発プロセス中ではなく、最終リリースの作成時にのみ有効にされた、あまりにも積極的な最適化であることが判明しました。

実は、私の特定の最適化が行われなかったという事実に私のコードが依存していることに気付かなかったため、実はgccではなく私のせいでした。

それを追跡するのに多くの時間がかかりました、そして、私はニュースグループで尋ねて、誰かが私にそれについて考えさせたので、私はそれにたどり着きました。それで、これがあなたにも起こっている場合に備えて、私は恩返しをさせてください。

0
Remo.D

例外が発生したときにプログラムでミニダンプを生成し、デバッガ(たとえば、WinDbg)で開きます。主な機能:MiniDumpWriteDump、SetUnhandledExceptionFilter

0
mikhailitsky

私はこれを見つけました この記事 あなたのシナリオに役立ちます。 ISTRコンパイラオプションは少し古くなっていました。 Visual Studioプロジェクトオプションを調べて、リリースビルドなどのpdbファイルを生成する方法を確認します。

0
fizzer

デバッガーの内部ではなく、デバッガーの外部で発生するのは疑わしいです。通常、デバッガで実行してもアプリケーションの動作は変わりません。コンソールとIDEの環境の違いを確認します。また、明らかに、最適化なしでデバッグ情報を使用してリリースをコンパイルし、それが動作に影響するかどうかを確認します。最後に、他の人がここで提案した事後分析デバッグツールをチェックしてください。通常は、それらからいくつかの手がかりを得ることができます。

0
Nick