2010-11-25 6 views
4

私は最近、私たちの製品に不具合を修正しました。逆Heisenbug - デバッガが接続されている場合にのみユニットテストが失敗する

私はバグが戻ってこないように単体テストを追加しました。ユニットテストを書くとき、私はいつも私の欠陥修正を取り消し、ユニットテストが失敗するのを確実にします。そうでなければ、それが適切に仕事をしていないことを知っています。

欠陥修正を取り消した後、私は単体テストがまだ合格していないことを発見しました。なぜそれが合格するのかを見るためにユニットテストにデバッガをつけたところ、テストは失敗しました(例外がスローされました)。私はブレークして、コールスタックが私が修正した元の欠陥のものと一致しているのを観察しました。

Visual Studio 2005で "Break on exception"設定を変更していませんでした。これは実際にはテストハーネスを終了させる重要なWin32例外です(優雅な例外ハンドラはありません)。

例外のテキストは次のとおり

Unhandled exception at 0x0040fc59 in _testcase.exe: 0xC0000005: 
Access violation reading location 0xcdcdcdcd. 

注:位置は常に0xcdcdcdcdallocated but unwritten Win32 heap memory)ではありません。場合によっては0x00000000であり、別のアドレスであることもあります。

これは伝統的なHeisenbugの逆のように見えますが、デバッガを使ってそれを観察すると問題が解消されます。私の場合は、デバッガを介してそれを観察すると、問題が表示されます!

私が最初に考えたのは、これはデバッガのタイミングの違いによって明らかになった競合状態でした。しかし、コードにトレースを追加してデバッガとは別に実行したときに、私が出力しているデータは、デバッガで実行しているときと同様の方法でアプリケーションを中止する必要があることを示しています。そうではありません!

これを引き起こす可能性のあることについてのご意見はありますか?


アップデート:私はこの問題の原因にで狭めています。詳細はthis questionを参照してください。見つけたらこの質問を答えに更新します。

+1

デバッガが接続されたときにプログラムがクラッシュしたような楽しいバグがありました。私は最後に、 'sem_wait'の呼び出しでブロックされたスレッドがあることを知りました。デバッガは接続時にスレッドを中断し、 'sem_wait'をエラーEINTRで返します。その後、スレッドは実行を続け、Bad Things Happenedを実行しました。言うまでもなく、私はエラーコードをチェックすることが重要である理由を学びました... –

答えて

0

この問題の原因を特定しました。詳細はthis questionを参照してください。

デバッガでテストハーネスを実行しているとき、デバッグ環境で消費されるメモリは、同じオブジェクトの後続の割り当て/割り当て解除がいつもの異なるメモリのに割り当てられていました。これは、私のテストハーネスがぶら下がっているポインターにアクセスしようとしたときにテストをクラッシュさせたことを意味します(技術的にはこれは未定義の動作ですが、これはテストコードであり、

コマンドラインからテストハーネスを実行すると、同じオブジェクトの後続の割り当て/割り当て解除は、常にと同じメモリブロックを再利用しました。このコインシデンスの振る舞いは、テストケース内で実際にぶら下がっているポインタにアクセスしたときに、ダングリングポインタがまだ有効なオブジェクトを指していたことを意味しました。だからこそ私はクラッシュを見なかった。

+0

これはあまり信頼性がありません。はい、それらはメモリの異なる部分に割り当てられますが、オブジェクトが削除された後もメモリ領域はまだプロセスのアドレス空間にマップされますか?あなたは非常に不安定な地面に建てようとします - テストするにはあまりにも不安定です。 – sharptooth

0

これはあなたを助けてくれるのかどうか分かりませんが、プログラムがVisual Studioデバッガで実行された場合やプログラムが外部で実行され、デバッガが接続されている場合に別の問題が発生しました。

+0

これはおそらくどのように役立つか分かりません。面白いですが、非常に役に立たない:) –

+0

これはコメントでなければなりません。 –

+0

@OJ:私はどちらも、実際には。私の場合は、プログラムの起動時に若干の変更が加えられた可能性があります。 (私はCランタイムが初期化時にデバッガのチェックをしていることを知っていますが、追加の割り当てなどをしているかもしれません) – Hasturkun

1

私はこの数年前に逆に走っていました。この問題は、デバッガがではなく、であるときにのみ発生していました。

このコードでは、以前のメソッドのアクティブ化のスタックフレームが破損していて、デバッガを使用して中間スタックフレームが導入されていました。

おそらく同様の状況があります。

+0

これはもっと伝統的な珍重虫のように聞こえる。私の理論は理解しやすいはずですが、それは私を困惑させるものです。 – LeopardSkinPillBoxHat

3

通常、VC++デバッガは、そのメモリへのポインタを削除すると、ヒープ割り当てメモリを既知の値で埋めます。私はVisual Studioを使用して以来かなりの時間がかかりましたが、0xcdcdcdcdがこのような値になることは妥当と思われます。デバッガで実行しているときに、アプリケーションが正しくクラッシュしている可能性が高いです。リリースモードで実行すると、ランタイムは割り当てられていないメモリを上書きする時間を無駄にしないので、あなたが「ラッキー」になり、そのメモリに格納されたデータが有効な場合があります。

ビルド設定を変更して、割り当て解除されたメモリをリリースモードで既知の値に設定するオプションを有効にすることができます(終了したらもう一度オフにすることを忘れないでください)。私はあなたのアプリケーションがリリースモードでクラッシュすると思います。

この値は必ずしも0xcdcdcdcdというわけではありません。これは私が間違っていることや、ぶら下がっているポインタへのパスが複数あることを意味する可能性があります。

+0

私はどちらの場合もデバッグモードを実行しています。それは私が欠陥を再現可能かどうかを決定するデバッガ*添付*で実行しているかどうかです。デバッガが接続された状態でクラッシュしますが、コマンドラインで手動でテストハーネスを実行するとクラッシュしません(例: 'c:\> testHarness.exe')。 – LeopardSkinPillBoxHat

関連する問題