リリースコンパイルモードでのみ発生するが、デバッグモードでは発生しないバグや異常なプログラム動作の一般的な理由は何ですか?
18 に答える
多くの場合、C ++のデバッグモードでは、すべての変数がnullで初期化されますが、リリースモードでは、明示的に指定されていない限り、同じことは起こりません。
デバッグマクロと初期化されていない変数を確認します
プログラムがスレッド化を使用している場合、最適化によってリリースモードでいくつかの問題が発生する可能性もあります。
また、すべての例外を確認します。たとえば、リリースモードに直接関連していませんが、VC ++でのmemアクセス違反など、いくつかの重要な例外を無視する場合がありますが、少なくともLinux、Solarisなどの他のOSでは同じ問題になる可能性があります。理想的には、プログラムはNULLポインターへのアクセスなどの重大な例外をキャッチしないようにする必要があります。
よくある落とし穴は、ASSERT内で副作用のある式を使用することです。
私は過去に、デバッグビルドでは問題がなかったが、リリースビルドではクラッシュする多くのバグに悩まされてきました。多くの根本的な原因があり(もちろん、このスレッドですでに要約されているものを含む)、私は次のすべてに捕らえられました:
- のメンバー変数またはメンバー関数
#ifdef _DEBUG
。これにより、デバッグビルドでクラスのサイズが異なります。#ifndef NDEBUG
リリースビルドで使用されることもあります - 同様に
#ifdef
、2つのビルドのうちの1つにのみ存在する別のビルドがあります - デバッグバージョンは、システムライブラリのデバッグバージョン、特にヒープとメモリの割り当て関数を使用します
- リリースビルドのインライン関数
- ヘッダーファイルのインクルードの順序。これは問題を引き起こさないはずですが、リセットされていないようなものがある場合、
#pragma pack
これは厄介な問題につながる可能性があります。同様の問題は、プリコンパイル済みヘッダーと強制インクルードを使用しても発生する可能性があります - キャッシュ:リリースビルドでのみ使用されるキャッシュや、異なるキャッシュサイズ制限などのコードがある場合があります
- プロジェクト構成:デバッグ構成とリリース構成のビルド設定が異なる場合があります(これは、IDEを使用している場合に発生する可能性があります)
- デバッグのみのコードの結果として発生する競合状態、タイミングの問題、およびその他の副作用
デバッグ/リリースのバグの根底に到達するために私が何年にもわたって蓄積したいくつかのヒント:
- 可能であれば、デバッグビルドで異常な動作を再現してみてください。さらに良いのは、それをキャプチャするための単体テストを作成することです。
- コンパイラ設定、キャッシュ、デバッグ専用コードの2つの違いについて考えてみてください。それらの違いを一時的に最小限に抑えるようにしてください
- 最適化をオフにしてリリースビルドを作成するか(デバッガーで有用なデータを取得する可能性が高くなります)、または最適化されたデバッグビルドを作成します。デバッグとリリースの間の変更を最小限に抑えることで、バグの原因となっている違いを特定できる可能性が高くなります。
その他の違いは次のとおりです。
- ガベージコレクションされた言語では、コレクターは通常、リリースモードでより積極的になります。
- 多くの場合、メモリのレイアウトは異なる場合があります。
- メモリは別の方法で初期化される場合があります(たとえば、デバッグモードでゼロにするか、リリースでより積極的に再利用することができます)。
- ローカルはリリースで値を登録するようにプロモートされる可能性があり、浮動小数点値で問題が発生する可能性があります。
はい!、条件付きコンパイルがある場合は、タイミングのバグ(最適化されたリリースコードと最適化されていないデバッグコード)、メモリの再利用とデバッグヒープが存在する可能性があります。
CRTライブラリ関数は、デバッグとリリースで動作が異なります(/MDと/MDd)。
たとえば、デバッグバージョンでは、クレームを検証するために、指定された長さに渡すバッファを事前に入力することがよくあります。例にはstrcpy_s
、、StringCchCopy
などがあります。文字列が早く終了した場合でも、szDestの長さはnバイトの方が適切です。
特にC領域にいる場合は、それが可能です。
1つの原因は、DEBUGバージョンが浮遊ポインターをチェックするコードを追加し、何らかの方法でコードがクラッシュしないように保護する(または正しく動作しない)可能性があることです。この場合、コンパイラから受け取る警告やその他のメッセージを注意深く確認する必要があります。
もう1つの原因は、最適化である可能性があります(通常、リリースバージョンではオンで、デバッグではオフです)。コードとデータのレイアウトが最適化されている可能性があります。たとえば、デバッグプログラムが未使用のメモリにアクセスしている間、リリースバージョンは予約済みのメモリにアクセスしようとしている、またはコードを指していることさえあります。
編集:私は他の人がそれについて言及しているのを見ます:もちろん、デバッグモードでコンパイルしない場合、条件付きで除外されるコードセクション全体があるかもしれません。その場合、それが実際にコードをデバッグするものであり、プログラム自体の正確さにとって不可欠なものではないことを願っています。
確かに、たとえば、次のような構造を使用する場合
#if DEBUG
//some code
#endif
これは、デバッグコードとリリースコードが異なる条件付きコンパイルがあり、リリースモードでのみ使用されるコードにバグがある場合に可能です。
それ以外は不可能です。デバッグコードとリリースコードのコンパイル方法や、デバッガーで実行した場合と実行しない場合のコードの実行方法に違いがありますが、これらの違いのいずれかがパフォーマンスの違い以外の原因である場合、問題はずっとありました。
デバッグバージョンでは、エラーが発生していない可能性があります(タイミングまたはメモリ割り当てが異なるため)が、エラーが発生していないことを意味するわけではありません。コードのタイミングを変更してエラーが発生するかどうかを引き起こすデバッグモードに関係のない他の要因もあるかもしれませんが、コードが正しければエラーは発生しないという事実に要約されますどんな状況でも。
したがって、エラーなしで実行できるという理由だけで、デバッグバージョンはOKではありません。リリースモードで実行したときにエラーが発生した場合、それはリリースモードが原因ではなく、最初からエラーが発生したことが原因です。
もっと多くの情報を提供する必要がありますが、そうです、それは可能です。デバッグバージョンの機能によって異なります。リリースバージョンにコンパイルされないログや追加のチェックがあるかもしれません。これらのデバッグ専用コードパスには、状態を変更したり、奇妙な方法で変数に影響を与えたりする意図しない副作用が発生する可能性があります。デバッグビルドは通常、実行速度が遅いため、スレッド化に影響を与え、競合状態を隠す可能性があります。リリースコンパイルからの単純な最適化についても同じですが、リリースコンパイルが最適化として何かを短絡させる可能性があります(最近ではありそうにありませんが)。
詳細がなければ、「OKではない」とは、コンパイルされないか、実行時に何らかのエラーが発生することを意味すると思います。#if DEBUG
ステートメントまたは属性でマークされたメソッドのいずれかを介して、コンパイルバージョンに依存するコードがあるかどうかを確認しConditional
ます。
.NETでは、のような条件付きコンパイルを使用しない場合でも#if DEBUG
、コンパイラはデバッグモードよりもリリースモードでの最適化により自由度が高く、バグのみをリリースする可能性があります。
攻撃的すぎるために有効なコードを壊す可能性のあるコンパイラの最適化があります。
最適化を少なくしてコードをコンパイルしてみてください。
非void関数では、すべての実行パスはreturnステートメントで終了する必要があります。
デバッグモードでは、このようなパスをreturnステートメントで終了するのを忘れた場合、関数は通常、デフォルトで0を返します。
ただし、リリースモードでは、関数がガベージ値を返す場合があり、プログラムの実行方法に影響を与える可能性があります。
それが可能だ。それが発生し、条件付きコンパイルが含まれていない場合は、プログラムが間違っていることをかなり確信でき、偶然のメモリの初期化またはメモリ内のレイアウトのためにのみデバッグモードで動作しています!
レジスタの以前の値を復元しなかったアセンブリ関数を呼び出していたときに、それを経験しました。
「リリース」構成では、VSはコードの速度を最適化する/O2でコンパイルしていました。したがって、前述の関数と共有されたCPUレジスタ(最適化用)にマッピングするだけのローカル変数は、深刻なメモリ破損につながります。
とにかく、コード内のどこかでCPUレジスタを間接的にいじっていないかどうかを確認してください。
昔、c /c++でdllとpdbをビルドしていたときのことを覚えています。
私はこれを覚えています:
- ログデータを追加すると、バグが移動または消失したり、まったく別のエラーが表示されたりすることがあります(したがって、実際にはオプションではありませんでした)。
- これらのエラーの多くは、strcpyとstrcatのchar割り当て、およびchar[]などの配列に関連しています。
- 境界チェッカーを実行し、メモリの割り当て/割り当て解除の問題を修正するだけで、いくつかを取り除くことができました。
- 多くの場合、コードを体系的に調べて、文字の割り当てを修正しました。
- 私の2セントは、メモリの割り当てと管理、およびデバッグモードとリリースモードの制約と違いに関連しているということです。
そして、そのサイクルを続けました。
これらのバグに取り組んでいる間、本番環境を妨げないように、リリースを一時的にdllのデバッグバージョンに交換することがありました。
もう1つの理由は、DB呼び出しである可能性があります。同じスレッドで同じレコードを複数回保存および更新していますか?場合によっては更新のためです。前のcreateコマンドがまだ処理中であり、更新の場合、db呼び出しがレコードを見つけることができなかったため、更新が失敗したか、期待どおりに機能しなかった可能性があります。デバッガーは着陸前にすべての保留中のタスクを完了することを確認するため、これはデバッグでは発生しません。