web-dev-qa-db-ja.com

デバッグモードに存在しないリリースバージョンのバグの一般的な理由

リリースコンパイルモードでのみ現れるが、デバッグモードでは発生しないバグおよびプログラムの異常な動作の一般的な理由は何ですか?

64
Benny

多くの場合、C++のデバッグモードでは、すべての変数はnullで初期化されますが、明示的に指定されない限り、リリースモードでは同じことは起こりません。

デバッグマクロと初期化されていない変数を確認します

プログラムがスレッドを使用している場合、最適化はリリースモードでいくつかの問題を引き起こす可能性があります。

また、リリースモードに直接関連していないなど、すべての例外を確認しますが、VC++のmemアクセス違反などの重要な例外を無視することもありますが、少なくともLinux、Solarisなどの他のOSでも同じことが問題になる可能性があります。理想的には、プログラムはNULLポインターへのアクセスなどの重要な例外をキャッチしないようにする必要があります。

31
Priyank Bolia

一般的な落とし穴は、ASSERT内で副作用のある式を使用することです。

19
Henrik

その他の違いは次のとおりです。

  • ガベージコレクション言語では、コレクタはリリースモードで通常より積極的です。
  • 多くの場合、メモリのレイアウトは異なる場合があります。
  • メモリは異なる方法で初期化される場合があります(たとえば、デバッグモードでゼロにリセットされるか、リリースでより積極的に再利用される可能性があります)。
  • ローカルはリリースで値を登録するように昇格される可能性があり、浮動小数点値で問題が発生する可能性があります。
9
stusmith

デバッグビルドでは問題ないが、リリースビルドではクラッシュする過去のバグに悩まされてきました。多くの根本的な原因(もちろん、このスレッドで既に要約されているものを含む)があり、私は次のすべてに気づきました。

  • #ifdef _DEBUGのメンバー変数またはメンバー関数。これにより、デバッグビルドでクラスが異なるサイズになります。時々#ifndef NDEBUGがリリースビルドで使用される
  • 同様に、異なる#ifdefがあり、これはたまたま2つのビルドのいずれかにのみ存在します
  • デバッグバージョンは、システムライブラリのデバッグバージョン、特にヒープおよびメモリ割り当て関数を使用します
  • リリースビルドのインライン関数
  • ヘッダーファイルを含める順序。これは問題を引き起こさないはずですが、リセットされていない#pragma packのようなものがある場合、これは厄介な問題につながる可能性があります。プリコンパイル済みヘッダーと強制インクルードを使用しても同様の問題が発生する可能性があります
  • キャッシュ:リリースビルドでのみ使用されるキャッシュや、異なるキャッシュサイズ制限などのコードがある場合があります
  • プロジェクト構成:デバッグ構成とリリース構成ではビルド設定が異なる場合があります(これはIDEを使用している場合に発生する可能性があります)
  • デバッグのみのコードの結果として発生する競合状態、タイミングの問題、およびその他の副作用

デバッグ/リリースバグの根底にたどり着くために長年にわたって蓄積してきたいくつかのヒント:

  • 可能であれば、デバッグビルドで異常な動作を再現し、さらに良いことに、単体テストを作成してそれをキャプチャします
  • コンパイラ設定、キャッシュ、デバッグ専用コードの2つの違いを考えてください。これらの違いを一時的に最小限に抑えるようにしてください
  • 最適化をオフにしてリリースビルドを作成する(したがって、デバッガで有用なデータを取得する可能性が高くなります)か、最適化されたデバッグビルドを作成します。デバッグとリリース間の変更を最小限に抑えることで、どの違いがバグの原因であるかを特定できる可能性が高くなります。
7
the_mandrill

はい!条件付きコンパイルがある場合、タイミングのバグ(最適化されたリリースコードと最適化されていないデバッグコード)、メモリの再利用とデバッグヒープが存在する可能性があります。

3
Simeon Pilgrim

特にC領域にいる場合は可能です。

1つの原因として、DEBUGバージョンがコードを追加して浮遊ポインターをチェックし、何らかの理由でコードがクラッシュしないように保護する(または正しく動作しない)可能性があります。この場合、コンパイラーから得られる警告やその他のメッセージを注意深く確認する必要があります。

別の原因は最適化である可能性があります(通常はリリースバージョンではオンで、デバッグではオフです)。コードとデータのレイアウトは最適化されている可能性があり、デバッグプログラムが未使用のメモリにアクセスしている間、リリースバージョンは予約済みのメモリにアクセスしようとしているだけでなく、コードを指しています!

編集:私は他の言及それを見ます:もちろん、デバッグモードでコンパイルしない場合、条件付きで除外されるコードセクション全体を持っているかもしれません。その場合、それは本当にコードをデバッグすることであり、プログラム自体の正確性に不可欠なものではないことを願っています!

3
Remo.D

CRTライブラリ関数は、デバッグとリリース(/ MDと/ MDd)で異なる動作をします。

たとえば、デバッグバージョンでは、主張を確認するために、指定された長さに渡すバッファーを事前に入力することがよくあります。例には、strcpy_sStringCchCopyなど。文字列がより早く終了する場合でも、szDestnバイト長である方が良いでしょう。

3
Alex Budovski

確かに、たとえば、次のような構造を使用する場合

#if DEBUG

//some code

#endif
2
Max Galkin

.NETでは、#if DEBUGのような条件付きコンパイルを使用しなくても、コンパイラはデバッグモードよりもリリースモードでの最適化の方がずっと寛大です。

2

もっと多くの情報を提供する必要がありますが、はい、可能です。デバッグバージョンの機能によって異なります。リリースバージョンにコンパイルされないログまたは追加のチェックがあります。これらのデバッグ専用コードパスには、状態を変更したり、変な方法で変数に影響を与える意図しない副作用が生じる場合があります。デバッグビルドは通常、実行速度が遅いため、スレッド化に影響を及ぼし、競合状態を隠します。リリースコンパイルからの単純な最適化についても同じですが、リリースコンパイルが最適化として何かを短絡させる可能性があります(最近はほとんどありませんが)。

1
blowdart

詳細なしで、「not OK」は、実行時にコンパイルしないか何らかのエラーをスローすることを意味すると想定します。 #if DEBUGステートメントを介して、またはConditional属性でマークされたメソッドを介して、コンパイルバージョンに依存するコードがあるかどうかを確認します。

1
Konamiman

非void関数では、すべての実行パスはreturnステートメントで終了する必要があります。

デバッグモードでは、このようなパスをreturnステートメントで終了することを忘れた場合、関数は通常デフォルトで0を返します。

ただし、リリースモードでは、関数がガベージ値を返すことがあり、プログラムの実行方法に影響する場合があります。

1
loudandclear

有効なコードを壊す可能性がありますというコンパイラーの最適化があります。これは、それらが攻撃的すぎるためです。

最適化をあまり有効にせずにコードをコンパイルしてみてください。

1
Georg Schölly

デバッグコードとリリースコードが異なるように条件付きコンパイルがあり、リリースモードでのみ使用されるコードにバグがある場合、これは可能です。

それ以外は不可能です。デバッグコードとリリースコードのコンパイル方法に違いがあり、デバッガーで実行する場合としない場合のコードの実行方法に違いがありますが、それらの違いのいずれかがパフォーマンスの違い以外を引き起こす場合、問題はずっとそこにありました。

デバッグバージョンでは、エラーが発生していない可能性があります(タイミングまたはメモリの割り当てが異なるため)が、それはエラーがそこにないという意味ではありません。また、コードのタイミングを変更するデバッグモードに関連しない他の要因があり、エラーが発生するかどうかはわかりませんが、コードが正しい場合はエラーが発生しないという事実に帰着しますどんな状況でも。

そのため、エラーを発生させずに実行できるからといって、デバッグバージョンは問題ありません。リリースモードで実行したときにエラーが発生した場合、それはリリースモードではなく、エラーが最初からあったためです。

1
Guffa

C/c ++でdllとpdbを作成していたときのことを覚えています。

私はこれを覚えている:

  • ログデータを追加すると、バグが移動したり消えたりするか、まったく別のエラーが表示されることがあります(したがって、実際にはオプションではありませんでした)。
  • これらのエラーの多くは、strcpyおよびstrcatでのchar割り当てとchar []などの配列によるものです。
  • 境界チェッカーを実行し、メモリの割り当て/割り当て解除の問題を単純に修正することで、一部を排除しました。
  • 多くの場合、体系的にコードを調べ、charの割り当てを修正しました(すべてのファイルを介してなど)。
  • 間違いなく、メモリの割り当てと管理、制約、デバッグモードとリリースモードの違いに関連するものです。

そして、最高を願っていました。

これらのバグに取り組んでいる間、実稼働を遅らせないために、時々dllのデバッグバージョンをクライアントに一時的に配信しました。

0
Sassine Jaoude

レジスタの以前の値を復元しなかったアセンブリ関数を呼び出していたときに、それを経験しました。

「リリース」構成では、VSは/ O2を使用してコンパイルし、コードの速度を最適化しました。したがって、CPUレジスタへのマッピング(最適化のため)だけである一部のローカル変数は、前述の関数と共有され、深刻なメモリ破損につながりました。

とにかく、コードのどこかでCPUレジスタを間接的に混乱させていないかどうかを確認してください。

0
mycelo

それが可能だ。それが発生し、条件付きコンパイルが含まれていない場合、プログラムが間違っていることを確信でき、偶然のメモリ初期化またはメモリ内のレイアウトのためだけにデバッグモードで動作しています!

0
UncleZeiv

別の理由としては、DB呼び出しが考えられます。同じスレッドで同じレコードを複数回保存および更新していますか?前のcreateコマンドがまだ処理中で、更新のためにdb呼び出しがレコードを見つけることができなかったため、更新が失敗したか、期待どおりに機能しなかった可能性があります。デバッガーが着陸前にすべての保留中のタスクを完了することを確認するため、これはデバッグでは発生しません。

0