schrödinbugとは何ですか?


52

このWikiページは次のことを示しています。

schrödinbugは、ソースコードを読んだり、異常な方法でプログラムを使用したりした場合に最初に動作するはずのないことに気づいた後にのみ現れるバグです。Jargonファイルには、「…これは不可能に思えますが、起こります。一部のプログラムは潜在的なシュレディンバグを長年にわたって隠していました。」

話されていることは非常にあいまいです。

誰かがschrödinbugがどのようなものであるかの例を提供できますか(架空/現実の状況のように)?


15
引用は冗談を言って行われることに注意してください。

11
:私はあなたがShrodingerの猫を知っていたならば、あなたがより良いshrodinbugを理解すると思うen.wikipedia.org/wiki/Shrodingers_cat
Eimantas

1
@Eimantas私は実際にはもっと混乱していますが、それは興味深い記事です:)

回答:


82

私の経験では、パターンは次のとおりです。

  • 多くの場合何年もの間、システムが機能する
  • エラーが報告されます
  • 開発者はエラーを調査し、完全に欠陥があると思われるコードを見つけて、「機能しなかった」と宣言します
  • バグが修正され、機能することはなかった(しかし長年は機能した)コードの凡例が大きくなります

ここで論理的にしましょう。働いたことがない可能性があり、コードは... 働いたことがない可能性が。それは場合はやった仕事を、その後の文はfalseです。

したがって、説明したとおりのバグ(欠陥のあるコードが動作を停止するのを観察しているバグ)は、明らかにナンセンスです。

現実には、次の2つのいずれかが発生しました。

1)開発者はコードを完全には理解していません。この場合、コードは通常混乱であり、そのどこかに何らかの外部条件に対する重大だが非自明な感度があります(たとえば、特定のOSバージョンまたは機能がマイナーではあるが重要な方法で動作する方法を制御します)。この外部条件は変更され(たとえば、サーバーのアップグレードまたは無関係と思われる変更によって)、そうすることでコードが破損します。

その後、開発者はコードを見て、履歴コンテキストを理解せず、考えられるすべての依存関係とシナリオをトレースする時間がないため、機能しなかったと宣言して書き直します。

この状況で、ここで理解するべきことは、「それは機能しなかった」という考えは間違いなく間違っているということです。

それは、それを書き換えることが悪いことではないということではありません-多くの場合、そうではありませんが、多くの場合、時間がかかり、コードのセクションを書き換えることは多くの場合、より速く、あなたが物事を修正したことを確認することができます。

2)実際には機能せず、誰も気づいていない。これは、特に大規模システムでは驚くほど一般的です。この例では、誰かが新しいものを開始し、これまで誰もしなかった方法で物事を検討し始めます。または、ビジネスプロセスが変更されて、以前はマイナーなエッジケースがメインプロセスに持ち込まれ、実際には機能しなかった(または一部ではなく一部が機能した)時間)が見つかり、報告されます。

開発者はそれを見て、「それは機能しなかったはずだ」と宣言しますが、ユーザーは「ナンセンス、私たちは何年もそれを使用しています」と言います。開発者は、彼らが「ああ、私たちが行うのです行く、その時点での正確な条件見つかったことを)今までになかった」に変更しました。

ここで開発者は正しい-それは決して機能しなかったかもしれないし、機能しなかった。

ただし、いずれの場合も、次の2つのいずれかが当てはまります。

  • 「機能しなかった」という主張は真実であり、機能したことはありません。
  • それは機能し、「機能することはなかった」というステートメントは偽であり、コードとその依存関係の(通常は合理的な)理解の欠如に至るまでです。

1
私に頻繁に起こる
創世記

2
グレートリアリズムこうした状況への洞察
StuperUser

1
私はそれが通常「WTF」の瞬間の結果だと思います。私は一度それを持っていました。私が書いたいくつかのコードを読み直し、最近気づいたバグがアプリ全体を破壊するはずだったことに気付きました。実際、さらに調べた後、私が書いた別のコンポーネントが非常に良かったので、間違いを補った。
サディータイル

1
@Thaddee-それは以前見たことがありますが、コードモジュールに2つのバグがあり、お互いを呼び出して互いにキャンセルしているので、実際に機能しました。どちらかを見て、彼らは壊れていたが、一緒に彼らは大丈夫だった。
ジョンホプキンス

7
@Jon Hopkins:2つのバグが互いにキャンセルされているケースもありましたが、それは本当に驚くべきことです。バグを見つけ、悪名高い「それは機能しなかったはずだ」という声明を口に出し、とにかくそれが機能した理由を見つけるために深く調べ、少なくともほとんどの場合、最初のバグを修正した別のバグを見つけました。私はその発見と、たった1つのバグがあれば壊滅的な結果をもたらしたという事実に本当に驚きました!
アレクシスDufrenoy

54

誰もが機能するはずのないコードに言及しているため、約8年前に、.netに変換された死にかけているVB3プロジェクトで私が遭遇した例を紹介します。残念ながら、.netバージョンが完成するまでプロジェクトを最新の状態に保つ必要がありました。VB3をリモートで理解したのは私だけでした。

計算ごとに数百回と呼ばれる非常に重要な関数が1つありました。これは、長期年金プランの月利を計算しました。興味深い部分を再現します。

Function CalculateMonthlyInterest([...], IsYearlyInterestMode As Boolean, [...]) As Double
    [about 30 lines of code]
    If IsYearlyInterestMode Then
        [about 30 lines of code]
        If Not IsYearlyInterestMode Then
            [about 30 lines of code (*)]
        End If
    End If
End Function

星印の付いた部分には最も重要なコードがありました。実際の計算を行ったのはそれだけです。明らかに、これはうまくいかないはずですよね?

多くのデバッグIsYearlyInterestModeが必要でしたがTrue、最終的には原因が判明しましたNot IsYearlyInterestMode。それは、誰かが線に沿って整数にキャストし、それを真に設定することになっている関数でそれをインクリメントしたためです(0のFalse場合は1に設定され、VB Trueなので、ロジックを見ることができますそこで)、それをブール値にキャストバックします。そして、私は決して起こり得ない状態でありながら、常に起こる状態を残されました。


7
エピローグ:その機能を修正したことはありません。失敗した呼び出しサイトにパッチを当てて、他のすべてのサイトと同様に2を送信しました。
コンフィギュレー

だからあなたは人々がコードを誤解したときに使用されるということですか?
-Pacerier

1
@Pacerier:コードが非常に混乱しているため、偶然にしか正しく動作しない場合がよくあります。私の例では、開発者は、IsYearlyInterestMode真と非真の両方を評価するつもりはありませんでした。1を含む数行(追加元の開発者ifsが、実際にそれがどのように動作するかを理解していなかった-それはちょうど仕事に起こった、それは良い十分だったので。
コンフィギュレータ

16

実世界の例を知らないが、例の状況でそれを簡素化する:

  • バグはしばらくの間は気づかれません。なぜなら、アプリケーションは、失敗するような条件下でコードを実行しないからです。
  • 誰かが通常の使用以外で何かをする(またはソースを調べる)ことでそれに気づきます。
  • バグが認識されたので、バグが修正されるまで、アプリケーションは通常の状態になるまで失敗します。

これは、バグがアプリケーションの一部の状態を破壊し、以前の通常の状態で障害を引き起こすために発生する可能性があります。


4
1つの説明は、ソフトウェアにランダムな障害があり、誰も精神的にリンクできなかったということです。したがって、これらのエラーは自然な原因(ランダムなハードウェア障害など)であると考えられていました。ソースコードが読めば、人々は以前のすべてのランダムエラーをこの1つの原因に関連付けることができるようになり、そもそも機能しなかったはずだということに気付くでしょう。
-rwong

4
2番目の説明は、ソフトウェアに責任の連鎖パターンで実装されている部分があるということです。1つのハンドラーに重大なバグがあるにもかかわらず、各ハンドラーは堅牢な方法で記述されています。現在、最初のハンドラーは常に失敗しますが、2番目のハンドラー(責任が重複している)が同じタスクを実行しようとするため、操作全体が成功したように見えます。責任領域の変更など、2番目のモジュールに変更がある場合、全体的な障害が発生しますが、実際のバグは別の場所にあります。
-rwong

13

実際の例。コードを表示することはできませんが、ほとんどの人はこれに関係します。

私が働いているユーティリティ関数の大きな内部ライブラリがあります。ある日、特定のことを行う関数を探していますが、Frobnicate()それを使用してみてください。ええと:Frobnicate()常にエラーコードが返されることがわかりました。

実装を掘り下げてみるFrobnicate()と、常に失敗するという基本的な論理エラーがいくつか見つかりました。ソース管理では、関数が記述されてから変更されていないことがわかります。つまり、関数が意図したとおりに動作したことはありません。誰もこれに気付いていないのはなぜですか?残りのソース登録を検索すると、既存のすべての呼び出し元Frobnicate()が戻り値を無視していることがわかります(したがって、独自の微妙なバグが含まれています)。これらの関数を変更して戻り値をチェックする必要がある場合、それらも失敗し始めます。

これは、ジョン・ホプキンスが答えで言及した条件#2の一般的なケースであり、大規模な内部ライブラリでは憂鬱なほど一般的です。


...これは、外部ライブラリが使用可能な場合はいつでも内部ライブラリを書くことを避ける正当な理由になります。よりテストされ、そのような厄介なサプライズははるかに少なくなります(オープンソースのライブラリは、とにかく修正できるので望ましいです)。
ジャン・ヒューデック

ええ、でもプログラマーがライブラリーのせいではない戻りコードを無視するなら。(ところで、最後にretcodeをチェックしたのはprintf()いつですか?)
JensG 14

これがまさにチェック例外が発明された理由です。
ケビンクルムウィーデ

10

これは、いくつかのシステムコードで見た本物のSchrödinbugです。ルートデーモンはカーネルモジュールと通信する必要があります。そのため、カーネルコードはいくつかのファイル記述子を作成します。

int pipeFDs[1];

次に、名前付きパイプに接続されるパイプを介して通信をセットアップします。

int pipeResult = pipe(pipeFDs);

これは機能しませんpipe()2つのファイル記述子を配列に書き込みますが、1つだけのスペースがあります。しかし、約7年間機能しました。配列は、メモリ内でファイル記述子として採用される未使用の領域の前にありました。

その後、ある日、コードを新しいアーキテクチャに移植しなければなりませんでした。動作しなくなり、動作するはずのないバグが発見されました。


5

Schrödinbugの帰結はHeisenbugです -これは調査または修正、あるいはその両方を試みると消える(または時々現れる)バグを記述しています。

ハイゼンバグは、デバッガーがロードされたときに実行および非表示になる神話上の賢い小さなブライターですが、視聴を停止すると木工から出てきます。

実際には、これらは通常、次のいずれかによって引き起こされるようです。

  • 最適化の影響。コンパイルされたコード-DDEBUGはリリースビルドとは異なるレベルに最適化されます。
  • シミュレーションされた「完全な」ダミーロードとは微妙に異なる実際の通信バスまたは割り込みによる微妙なタイミングの違い

どちらも、リリース機器でリリースコードをテストすることの重要性と、エミュレータを使用したユニット/モジュール/システムテストを強調しています。


これを投稿する前に、S.Loteの回答とdelnanのコメントに気付かなかったのはなぜですか?
アンドリュー

私はほとんど経験していませんが、これをいくつか見つけました。私はAndroid NDK環境で働いていました。デバッガーがブレークポイントを検出すると、C ++スレッドではなくJavaスレッドのみを停止し、要素がC ++で初期化されたため、一部の呼び出しが可能になりました。デバッガなしで放置すると、JavaコードはC ++よりも速くなり、まだ初期化されていない値を使用しようとします。
MLProgrammer-CiM

数か月前にDjangoデータベースAPIを使用して、Heisenbugを発見しました。いつDEBUG = True、生のSQLクエリの「パラメータ」引数の名前が変更されます。私たちは、原因、それはベータサイトにプッシュする時間だったときに完全に破ったクエリの長さに明確にするため、Aのキーワード引数としてそれを使用していたDEBUG = False
Izkata

2

私はいくつかのシェーディンバグを見てきましたが、いつも同じ理由で:

会社の方針では、誰もがプログラムを使用することになっています。
誰もそれを実際に使用しませんでした(主にトレーニングがなかったためです)。
しかし、彼らはこれを経営者に伝えることができませんでした。そのため、誰もが「このプログラムを2年間使用しており、今日までこのバグに遭遇したことはありません」と言わなければなりませんでした。
少数のユーザー(それを書いた開発者を含む)を除いて、プログラムは実際には機能しませんでした。

あるケースでは、プログラムは十分なテストを受けていましたが、実際のデータベースではテストされていませんでした(機密性が高すぎると見なされたため、偽のバージョンが使用されました)。


1

私自身の歴史の例がありますが、これは約25年前のことです。私はTurbo Pascalで初歩的なグラフィックプログラミングをしている子供でした。TPには、画面の領域をポインターベースのメモリブロックにコピーし、それを他の場所にblitできる関数を含むBGIというライブラリがありました。白黒画面でのxor-blittingと組み合わせると、単純なアニメーションを実行するために使用できます。

さらに一歩踏み込んでスプライトを作りたかった。大きなブロックとコントロールを描画して色を付けるプログラムを作成しましたが、それらをピクセルとして再現し、簡単な描画プログラムを作成してスプライトを作成し、それをメモリにコピーしました。問題が1つだけありました。これらのブリットスプライトを使用するには、他のプログラムが読み取れるようにファイルに保存する必要があります。しかし、TPには、ポインターベースのメモリ割り当てをシリアル化する方法がありませんでした。マニュアルには、ファイルに書き込めないと書かれていました。

私は、ファイルへの書き込みに成功したコードを思い付きました。そして、ゲームを作成する途中で、バックグラウンドで描画プログラムからスプライトをブリットするテストプログラムの作成を開始しました。そして、それは美しく働きました。しかし翌日、それは機能しなくなりました。それは文字化けした混乱以外何も見せませんでした。二度と機能しなかった。私は新しいスプライトを作成しましたが、完全に機能しました。機能しないまで、そして再び文字化けしました。

長い時間がかかりましたが、最終的には何が起こっているのかがわかりました。私が思ったように、描画プログラムはコピーしたピクセルデータをファイルに保存しませんでした-ポインター自体を保存していました。次のプログラムがファイルを読み取ると、最後のプログラムがそこに書き込んだものがまだ含まれているメモリの同じブロックへのポインタで終わりました(これはMS-DOS上にあり、メモリ管理は存在しませんでした)。しかし、それは機能しました...再起動するか、同じメモリ領域を再利用したものを実行するまで、ビデオメモリブロックにまったく無関係なデータを大量に送信していたため、文字化けしました。

動作することはないはずで、動作するように見えることさえなかったはずです(そして実際のOSでは動作しません)、それでも動作し、一度壊れると、壊れたままになります。


0

これは、人々がデバッガを使用するときに常に発生します。

デバッグ環境は、実際の(デバッガーなしの)実稼働環境とは異なります。

デバッガーで実行すると、デバッガーのスタックフレームがバグをマスクするため、スタックオーバーフローなどをマスクする場合があります。


デバッガで実行されるコードとコンパイルされたときのコードの違いについて言及しているとは思わない。
ジョンホプキンス

26
それはschrödinbugではなく、heisenbug です

@delnan:それは端にあります、IMO。知らない自由度があるので、それは不確定なものだと思います。私は実際に(など、つまり、競合状態、オプティマイザの設定、ネットワークの帯域幅の制限)別のを妨げる一つのことを測定するもののために予備の特異なバグに好き
S.Lott

@ S.Lott:あなたが説明する状況は、スタックフレームなどをいじることによって物事を変化させる観察を伴います。(私が今まで見た最悪の例は、デバッガーがシングルステップモードで無効なセグメントレジスタ値のロードを平和的かつ「正しく」実行することでした。結果は、保護モードでリアルモードポインターをロードしても。コピーされただけであり、逆参照されていないため、完全に動作しました。)
ローレンペクテル

0

私は真のシュロディンバグを見たことがなく、それらが存在できるとは思わない-それを見つけることは物事を壊さないだろう。

むしろ、何年も前から潜んでいたバグを暴露する何かが変更されました。変更されたものはすべて変更されたままであるため、バグは表示され続け、同時に誰かがバグを見つけます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.