見つけにくいのはなぜですか?どのように追跡しましたか?
閉めるのに十分ではないが、また見る
https://stackoverflow.com/questions/175854/what-is-the-funniest-bug-youve-ever-experienced
監視カメラで実行されているjpegパーサーは、会社のCEOが部屋に入るたびにクラッシュしました。
100%の再現可能なエラー。
子供じゃない
これが理由です:
JPEG圧縮についてあまり知らないあなたのために-画像は一種の小さなブロックのマトリックスに分解され、マジックなどを使用してエンコードされます。
CEOが部屋に入ったとき、パーサーは窒息しました。彼は常に正方形のパターンのシャツを着ていたため、コントラストとブロック境界アルゴリズムの特殊なケースを引き起こしました。
本当にクラシック。
これには、Z-8000アセンブラーの知識が必要です。これについては、後ほど説明します。
私は組み込みシステム(Z-8000アセンブラー)で作業していました。会社の別の部門が同じプラットフォーム上に別のシステムを構築しており、関数のライブラリを作成していたので、それもプロジェクトで使用していました。バグは、1つの関数を呼び出すたびにプログラムがクラッシュすることでした。すべての入力を確認しました。彼らは大丈夫だった。それはライブラリのバグでなければなりませんでしたが、ライブラリは全国の数千のPOSサイトで使用されていた(そして正常に動作していた)ことを除いて。
現在、Z-8000 CPUには16個の16ビットレジスタ、R0、R1、R2 ... R15があり、RR0、RR2、RR4..RR14などの名前の8つの32ビットレジスタとしてアドレス指定することもできます。ゼロから、多数の古いライブラリをリファクタリングします。とてもきれいで、厳格なプログラミング標準に従っていました。各関数の開始時に、関数で使用されるすべてのレジスターがスタックにプッシュされ、その値が保持されました。すべてがきちんとしてきちんとしていて、完璧でした。
それにもかかわらず、ライブラリのアセンブラーリストを調べたところ、その関数について奇妙なことに気付きました---関数の開始時に、プッシュRR0 /プッシュRR2があり、最後にPOP RR2/POP R0がありました。さて、もしあなたがそれに従わなかった場合、開始時にスタックに4つの値をプッシュしましたが、最後に3つの値のみを削除しました。それは災害のレシピです。リターンアドレスが必要なスタックの最上部に不明な値があります。関数はおそらく動作しませんでした。
ただし、機能していたことを思い出させてください。数千台のマシンで1日に数千回呼び出されていました。おそらく動作しませんでした。
しばらくデバッグした後(1980年代半ばのツールを使用した組み込みシステムのアセンブラでは簡単ではありませんでした)、不正な値がランダムアドレスに送信していたため、戻り時に常にクラッシュしました。動作しているアプリをデバッグして、失敗しなかった理由を明らかにしなければなりませんでした。
ライブラリはレジスタの値を保存するのに非常に優れていたことを思い出してください。したがって、レジスタに値を入力すると、そこにとどまります。 R1には0000が含まれていました。その関数が呼び出されると、常に0000が含まれます。したがって、バグはスタックに0000を残しました。したがって、関数が返されたとき、アドレス0000にジャンプします。これはたまたまRETであり、次の値(正しい戻りアドレス)をスタックからポップして、そこにジャンプします。データはバグを完全に覆い隠しました。
もちろん、私のアプリでは、R1の値が異なっていたため、クラッシュしました。..
これはLinuxで発生していましたが、ほぼすべてのOSで発生する可能性がありました。現在、ほとんどの人はおそらくBSDソケットAPIに精通しているでしょう。私たちは年々喜んで使用していますが、機能しています。
私たちは、多くのソケットを開く超並列アプリケーションに取り組んでいました。その動作をテストするために、データ転送のために数百、時には千を超える接続を開くテストチームがありました。最大のチャネル番号では、アプリケーションは奇妙な動作を示し始めます。時々クラッシュしました。それ以外の場合、単に真ではありえないエラーが発生しました(たとえば、accept()が後続の呼び出しで同じファイル記述子を返すため、もちろん混乱が生じます)。
ログファイルで何かが間違っていることがわかりましたが、特定するのは非常に困難でした。 Rational Purifyを使用したテストでは、何も問題はありませんでした。しかし、何かが間違っていました。私たちはこれに何日も取り組みましたが、ますますイライラしました。既にネゴシエートされたテストがアプリに大混乱を引き起こすので、それはショーブロッカーでした。
エラーは高負荷の状況でのみ発生したため、ソケットで行ったすべてのことを再確認しました。 Purifyは、メモリを集中的に使用する状況では実現不可能であるため、Purifyで高負荷のケースをテストしたことはありませんでした。
最後に(そして幸運にも)ソケットの状態の変化を待つselect()で大量のソケットが問題になる可能性があることを思い出しました(読み取り可能/書き込み可能/エラー)。確かに、アプリケーションが記述子1025でソケットに到達した瞬間に大混乱を引き起こし始めました。問題は、select()がビットフィールドパラメーターで機能することです。ビットフィールドは、マクロFD_SET()およびそのパラメーターの有効性をチェックしない友人によって埋められます。
したがって、1024以上の記述子を取得するたびに(各OSには独自の制限があり、Linux Vanillaカーネルには1024があり、実際の値はFD_SETSIZEとして定義されています)、FD_SETマクロは喜んでそのビットフィールドを上書きし、メモリ内の次の構造にゴミを書き込みます。
すべてのselect()呼び出しをpollane()に置き換えました。これは、不可解なselect()呼び出しに代わる適切に設計された代替手段です。幸運なことに、すべてのソケット処理が1つのフレームワーククラスにあり、15分間の作業で問題を解決できました。 select()呼び出しがコード全体に散らばっていると、さらにひどくなります。
学んだ教訓:
aPI関数が25年前であり、誰もがそれを使用している場合でも、まだ知らない暗いコーナーがある場合があります
aPIマクロの未チェックのメモリ書き込みはEVILです
purifyのようなデバッグツールは、すべての状況、特に大量のメモリが使用されている場合に役立ちません
可能であれば、常にアプリケーションのフレームワークを用意してください。これを使用すると、移植性が向上するだけでなく、APIバグの場合にも役立ちます
多くのアプリケーションは、ソケットの制限を考慮せずにselect()を使用します。したがって、多くのソケットを使用するだけで、多くの一般的なソフトウェアにバグを引き起こす可能性があると確信しています。ありがたいことに、ほとんどのアプリケーションには1024以上のソケットはありません。
安全なAPIを使用する代わりに、OS開発者は開発者を非難することを好みます。 Linuxのselect()manページには
「ディスクリプタ値が0未満またはFD_SETSIZE以上である場合、これらのマクロの動作は未定義であり、通常は少なくともシステムでサポートされるディスクリプタの最大数と同じです。」
それは誤解を招くです。 Linuxは1024を超えるソケットを開くことができます。また、動作は完全に明確に定義されています。予期しない値を使用すると、実行中のアプリケーションが台無しになります。開発者は、マクロを不正な値に対して回復力を持たせる代わりに、他の構造を単純に上書きします。 FD_SETは、LinuxヘッダーのインラインAssembly(!)として実装され、単一のアセンブラー書き込み命令に評価されます。どこでも起こっているわずかな境界チェックではありません。
独自のアプリケーションをテストするために、main()の直後にプログラムでFD_SETSIZEファイルまたはソケットを開き、アプリケーションを実行することで使用される記述子の数を人為的に増やすことができます。
トルステン79
私のものはハードウェアの問題でした...
当時、私は大きな21インチCRTモニターを備えたDEC VaxStationを使用していました。新しい建物の研究室に移動し、部屋の反対側の角に2つのVaxStationを設置しました。 (ええ、80年代でしたが)他のモニターはそうではありませんでした。
さて、モニターを交換してください。他のモニター(現在VaxStationに接続されている)はちらつきましたが、以前のモニター(部屋を横切って移動した)はちらつきませんでした。
CRTベースのモニターは磁場の影響を受けやすいことを思い出しました。実際、それらは60 Hzの交番磁界に非常に敏感でした。私はすぐに、自分の作業エリアで何かが60 Hzの交番磁界を生成しているのではないかと疑っていました。
最初は、自分の作業エリアに何か疑いがありました。残念ながら、他のすべての機器の電源を切ってプラグを抜いても、モニターはちらつきます。その時点で、私は建物の中に何かを疑い始めました。
この理論をテストするために、VaxStationとその85 lbモニターをポータブルシステムに変換しました。システム全体をロールアラウンドカートに置き、100フィートのオレンジ色の建設延長コードに接続しました。問題の機器を見つけるために、このセットアップを携帯型電界強度計として使用する計画でした。
モニターを回転させると、まったく混乱しました。モニターは部屋のちょうど半分でちらつきましたが、反対側ではちらつきませんでした。部屋は四角い形をしていて、反対側の角にドアがあり、モニターはドアを結ぶ横線の片側で点滅しましたが、反対側では点滅しませんでした。部屋は四方すべてが廊下に囲まれていました。モニターを廊下に押し出したところ、ちらつきが止まりました。実際、フリッカーは部屋の三角形の半分にのみ発生し、他のどこにも発生しないことがわかりました。
かなりの混乱の期間の後、私はその部屋には双方向の天井照明システムがあり、各ドアに照明スイッチがあることを思い出しました。その瞬間、私は何が間違っていたかに気づきました。
問題のある部屋の半分にモニターを移動し、天井照明を消しました。ちらつきは止まりました。ライトをオンにすると、フリッカーが再開しました。いずれかの照明スイッチから照明をオンまたはオフにし、部屋の半分内でフリッカーをオンまたはオフにしました。
この問題は、誰かが天井灯を配線したときに角を切ったことが原因でした。照明回路の双方向スイッチを配線するとき、SPDTスイッチの接点間に1対のワイヤを配線し、一方のスイッチのコモンからライトを通り、もう一方のスイッチのコモンまで1本のワイヤを配線します。
通常、これらのワイヤは束ねられています。彼らは1つのスイッチボックスからグループとして出発し、天井天井の据え付け品まで走り、もう1つのボックスに向かいます。重要な考え方は、すべての通電ワイヤがまとめられているということです。
建物が配線されたとき、スイッチとライトの間の単一のワイヤは天井を経由していましたが、スイッチ間を移動するワイヤは壁を経由していました。
すべてのワイヤが互いに近接して平行に走った場合、1本のワイヤの電流によって生成された磁場は、近くのワイヤの等しく反対の電流によって生成された磁界によって相殺されます。残念ながら、照明の実際の配線方法は、部屋の半分が基本的に大きなシングルターントランス一次内部にあることを意味していました。ライトが点灯しているとき、電流はループ状に流れ、貧弱なモニターは基本的に大きな電磁石の中に座っていました。
ストーリーの教訓:AC電源配線のホットラインとニュートラルラインは、正当な理由で隣り合っています。
今、私がしなければならなかったのは、新しい建物の一部を再配線しなければならない理由を経営陣に説明することだけでした...
いくつかのコードに出くわし、それを勉強した後、「これが機能するはずがないという結論に達しました」というバグ。以前は常に動作していましたが、突然動作しなくなります。
仕事での構築を支援した製品の1つは、顧客サイトで数か月間実行され、受信した各イベントを収集してSQL Serverデータベースに幸福に記録することでした。約6か月間非常に良好に動作し、約3500万件のレコードを収集しました。
それからある日、顧客から、データベースが約2週間更新されなかった理由が尋ねられました。さらなる調査の結果、挿入を行っていたデータベース接続がODBC呼び出しから戻ることに失敗しました。記録を行うスレッドは他のスレッドから分離され、すべてを許可しましたしかし、録音スレッドはほぼ2週間正常に機能し続けます!
私たちは、このマシン以外のマシンで問題を再現するために数週間続けて試みました。問題を再現することはできませんでした。残念ながら、他の製品のいくつかはほぼ同じ方法で失敗し始めましたが、いずれもデータベーススレッドが他の機能から分離されていないため、アプリケーション全体がハングし、毎回手動で再起動する必要がありましたクラッシュしました。
調査の週は数か月になりましたが、同じ症状がまだありました。データベースを使用したすべてのアプリケーションで完全なODBCデッドロック。一部の製品がデッドロックを検出し、情報を収集し、結果を電子メールで送信し、それからそれ自体を再起動するまで、どこで何が間違っていたのか。
ある日サーバーで作業中に、クラッシュしたアプリケーションからデバッグ情報を収集し、サーバーBSoDで何が起こっているかを把握しようとしました。サーバーがオンラインに戻ったときに、WinDbgでミニダンプを開いて、問題のドライバーが何であるかを調べました。ファイル名を取得し、実際のファイルにトレースバックしました。ファイル内のバージョン情報を調べたところ、コンピューターにインストールされているMcAfeeアンチウイルススイートの一部であることがわかりました。
アンチウイルスを無効にし、それ以来、単一の問題は発生していません!!
私はこのグーグルエリアの時間に発生する可能性のある非常に一般的で厄介なバグを指摘したいだけです:
コードの貼り付けと悪名高いマイナス
そのとき通常のASCII文字ハイフンマイナス( '-'の代わりに、 minus )。
プラス、マイナス(U + 2212)、ハイフンマイナス(U + 002D)
現在、マイナスはハイフンマイナスよりも長くレンダリングされているはずですが、特定のエディター(またはDOSシェルウィンドウ)では、使用される文字セットに応じて、実際には通常の「-」ハイフンマイナス記号としてレンダリングされます。
そして...実際の原因が見つかるまで、このコードがコンパイルされない理由を把握しようとして何時間も費やし、各行を1行ずつ削除することができます!
そこに最も難しいバグではないかもしれませんが、十分にイライラします;)
(私の元の投稿で反転を見つけてくれてありがとう ShreevatsaR -コメントを参照)
1つ目は、リリースされた製品にバグがあったことですが、問題をデバッグしようとしても発生しませんでした。これは最初は「リリース対デバッグ」だと思っていましたが、リリースモードでコードをコンパイルしても、問題を再現できませんでした。他の開発者が問題を再現できるかどうかを見に行きました。いや。プログラム出力の多くの調査(混合アセンブリコード/ Cコードリストの作成)の後、リリースされた製品のアセンブリコードをステップ実行しました(うん!)、問題のある行を見つけました。しかし、ラインは私にはちょうどよさそうでした!次に、アセンブリ命令が何をしたかを調べなければなりませんでした。そして、リリースされた実行可能ファイルに間違ったアセンブリ命令が十分にあったことを確認しました。次に、ビルド環境が生成した実行可能ファイルを確認しました。これには、正しいAssembly命令が含まれていました。ビルドマシンが何らかの理由で破損し、このアプリケーションのたった1つの命令に対して不適切なアセンブリコードを生成したことが判明しました。他のすべて(当社製品の以前のバージョンを含む)は、他の開発者のマシンと同一のコードを生成しました。ソフトウェアマネージャーに調査結果を示した後、ビルドマシンをすばやく再構築しました。
ネットワーク化されたアプリケーションの腸内のどこかに、次の行がありました(簡略化)。
_if (socket = accept() == 0)
return false;
//code using the socket()
_
呼び出しが成功したときに何が起こりましたか? socket
は1に設定されました。1が与えられたときにsend()
は何をしますか? (例:
_send(socket, "mystring", 7);
_
これはstdout
...に出力されます。これは、4時間後に、すべてのprintf()
sを取り出して、アプリがデータを送信する代わりにターミナルウィンドウに出力していたことに気付いた後です。通信網。
80年代のData GeneralミニコンピューターでのFORTRANでは、コンパイラーが定数1(1)を0(ゼロ)として処理する場合がありました。これは、古いコードの一部が値1の定数を、変数をFORTRANパラメーターとして宣言した関数に渡していたために発生しました。コードの欠陥のため、パラメーター変数に割り当てを行い、コンパイラーは定数1に使用したメモリー位置のデータを大喜びで変更しました。
後で多くの無関係な関数には、リテラル値1と比較するコードがあり、テストは失敗します。デバッガーでそのコードを最も長く見つめたことを覚えています。変数の値を出力しますが、1になりますが、テスト 'if(foo .EQ。1)'は失敗します。デバッガーに1の値が何であると考えているのかを出力するように依頼するまで、長い時間がかかりました。その後、コードをさかのぼって定数1が0になったときを見つけるために、多くの髪を引っ張っていました。
それほど難しいことではありませんでしたが、明らかになったときは大笑いしました。
オンラインショップの24時間365日の注文処理システムを保守しているときに、顧客から注文が「切り捨てられた」という苦情がありました。彼は、注文した注文には実際にはNのポジションが含まれていたが、システムは警告なしではるかに少ないポジションを受け入れたと主張した。
システム内の注文フローを追跡した後、次の事実が明らかになりました。データベースに注文アイテムを保存するストアドプロシージャがありました。次のような(product-id, quantity, price)
トリプルのリストをエンコードした文字列として注文アイテムのリストを受け入れました。
「<12345、3、19.99> <56452、1、8.99> <26586、2、12.99>」
現在、ストアドプロシージャの作成者はあまりにも頭が良く、通常の解析やループのようなものに頼ることができません。そこで彼は、"<"
を"insert into ... values ("
で、">"
を");"
で置き換えることにより、文字列を直接SQLマルチ挿入ステートメントに変換しました。結果の文字列をvarchar(8000)変数に格納しなかった場合に限り、これはすべてうまくいきました。
起こったのは、彼の"insert ...; insert ...;"
が8000番目の文字で切り捨てられ、その特定の順序では、カットがinsert
sの間で「ラッキー」であったため、切り捨てられたSQLは構文的に正しいままでした。
その後、spの作成者が私のボスであることがわかりました。
私はあなたが戦い、長いボス戦に勝った後にのみ発生したコンソールゲームにバグがあり、その後5回に1回程度しか発生しませんでした。まったく。
それは私が今まで遭遇した中で最もシャイなバグでした。ボスバトルの修正、自動化、インストルメント化、デバッグはバグを隠します(そしてもちろん、バグが隠されたことを確認するために10〜20回実行する必要があります)。
最終的に、コードを2〜3日間繰り返し読み取ることで問題(キャッシュ/ DMA /割り込みの競合)を見つけました。
これは、C++とデジタル時計はかなりきれいだと思ったときに戻ってきました...
難しいメモリリークを解決できるという評判を得ました。別のチームには、追跡できないリークがありました。彼らは私に調査するように頼みました。
この場合、それらはCOMオブジェクトでした。システムの中核には、ほとんど同じように見える多くのねじれた小さなCOMオブジェクトを提供するコンポーネントがありました。それぞれがAddRef()
とRelease()
を同じ回数実行することに責任を持つ多くの異なるクライアントに渡されました。
各AddRef
を呼び出したユーザーと、それらがRelease
dを持っているかどうかを自動的に計算する方法はありませんでした。
デバッガーで数日間過ごし、小さな紙に16進アドレスを書き留めました。私のオフィスは彼らで覆われていました。最後に犯人を見つけました。私に助けを求めたチームはとても感謝していました。
翌日、GCされた言語に切り替えました。*
(*実際には真実ではありませんが、物語の良い結末になるでしょう。)
最近、取引アプリケーションに追加したいくつかの新しい機能をテストしているときに、特定の種類の取引の結果を表示するコードが適切に機能しないことに気づきました。ソース管理システムを見た後、このバグが少なくとも1年は存在していたことが明らかであり、トレーダーが誰もそれを発見したことがないことに驚いた。
しばらく困惑し、同僚と確認した後、バグを修正し、新しい機能のテストを続けました。約3分後、電話が鳴りました。ラインの反対側には、自分のトレードの1つが正しく表示されていないと不満を言っている激怒トレーダーがいました。
さらに調査すると、トレーダーは3分前にコードで気付いたのとまったく同じバグに見舞われていることに気付きました。このバグは1年間横たわっており、開発者が来てそれを見つけて実際に攻撃できるようになるのを待っていました。
これは、 Schroedinbug として知られるタイプのバグの良い例です。私たちのほとんどはこれらの奇妙な実体について聞いたことがありますが、実際に野生のものに遭遇したときは不気味な感じです。
Sun MicrosystemsのBryan Cantrillは、dtraceと呼ばれる開発を支援したツールを使用して追跡したバグについて、優れたGoogle Tech Talkを行いました。
Tech Talk はおもしろくてこっけいで、有益で、非常に印象的です(そしてlong、約78分)。
バグについてはネタバレはしませんが、53:00頃に犯人を明らかにし始めます。
クライアントのペットのうさぎがイーサネットケーブルの途中でかじったとき。はい。それは悪かったです。
頭に浮かぶ2つの最も難しいバグは、どちらも同じ種類のソフトウェアにあり、1つだけがWebベースのバージョンにあり、もう1つはWindowsのバージョンにありました。
この製品は、フロアプランビューアー/エディターです。 Webベースのバージョンには、データをSVGとしてロードするフラッシュフロントエンドがあります。現在、これは正常に機能し、ブラウザがハングする場合があります。いくつかの図面上でのみ、図面上でマウスを少し動かしたときのみ。問題を1.5 MBのSVGデータを含む単一の描画レイヤーに絞り込みました。データのサブセクション、サブセクションのみを使用した場合、ハングは発生しませんでした。最終的に、問題はおそらく、ファイル内にいくつかの異なるセクションがあり、それらが組み合わさってバグを引き起こしたということだと思いました。案の定、レイヤーのセクションをランダムに削除し、バグをテストした後、描画ステートメントの問題のある組み合わせを見つけました。 SVGジェネレーターで回避策を作成しましたが、actionscriptの行を変更せずにバグを修正しました。
Delphiで書かれたWindows側の同じ製品では、同様の問題がありました。ここでは、製品がautocad DXFファイルを取得し、それらを内部描画形式にインポートし、カスタム描画エンジンでレンダリングします。このインポートルーチンは特に効率的ではありません(多くの部分文字列のコピーを使用します)が、仕事は完了します。この場合にのみそうではありませんでした。通常、5メガバイトのファイルは20秒でインポートされますが、1つのファイルではメモリフットプリントがギガバイト以上に膨れ上がったため、20分かかりました。最初は典型的なメモリリークのように見えましたが、メモリリークツールはそれをきれいに報告し、手動のコード検査でも何も見つかりませんでした。この問題は、Delphi 5のメモリアロケーターのバグであることが判明しました。この特定のファイルが適切に再作成されていたいくつかの条件では、深刻なメモリフラグメンテーションが発生しやすくなります。システムは大きな文字列を割り当てようとし続け、割り当てられた最上位のメモリブロックを除いてそれらを配置する場所を見つけません。新しいメモリ割り当てライブラリを統合すると、インポートコードの行を変更せずにバグが修正されました。
振り返ってみると、最も困難なバグは、問題が発生した部分とは異なるシステムの部分の変更が修正に関係するバグのようです。
プラットフォームにバグがあり、デバイスデバッガが非常に悪い。デバイス上でcrashを取得しますprintfを追加した場合になります。その後、printfの場所とは異なる場所でクラッシュします。 printfを移動した場合、クラッシュは移動または消滅します。実際、単純なステートメントの順序を変更してそのコードを変更した場合、変更したコードとは関係のない場所でクラッシュが発生します。
プラットフォームのリロケーターのバグリロケーターはZIセクションをゼロで初期化するのではなく、再配置テーブルを使用して値を初期化していました。したがって、バイナリで再配置テーブルが変更されるたびに、バグが移動します。そのため、単純にprintfを追加すると、バグのために再配置テーブルが変更されます。
これは、私がコンピューター店で働いていたときに起こりました。
ある顧客はある日店に来て、彼の真新しいコンピューターが夕方と夜にうまく働いたと言ったが、それは正午または深夜に全く働きません。問題は、その時点でマウスポインターが動かないことでした。
私たちが最初にしたことは、マウスを新しいものに変更することでしたが、問題は修正されませんでした。もちろん、両方のマウスは店頭で問題なく動作しました。
何度か試してみたところ、その特定のブランドとモデルのマウスに問題があることがわかりました。顧客のワークステーションは非常に大きな窓の近くにあり、正午にはマウスは直射日光にさらされていました。そのプラスチックは非常に薄いため、その状況下では半透明になり、日光が作業用のオプトメカニカルホイールを妨げました。
私のチームは、CGIベースのマルチスレッドC++ Webアプリを継承しました。メインプラットフォームはWindowsでした。遠く離れたセカンダリプラットフォームは、Posixスレッドを備えたSolarisでした。何らかの理由で、Solarisの安定性は災害でした。弊社の営業スタッフがWindowsバージョンのプッシュに成功している間、1年以上(大抵はオフ)にわたって問題を検討するさまざまな人々がいました。
症状は哀れな安定性でした:広範囲のシステムがほとんど韻や理由もなくクラッシュします。このアプリは、Corbaと自社開発のプロトコルの両方を使用していました。ある開発者は、必死の手段としてCorbaサブシステム全体を削除するまで行った:運はありません。
最後に、元のシニア開発者がアイデアについて声を上げて疑問に思いました。私たちはそれを調べて、最終的に問題を発見しました:Solarisには、実行可能ファイルのスタックサイズを調整するためのコンパイル時(または実行時?)パラメーターがありました。誤って設定された:小さすぎる。そのため、アプリはスタックを使い果たし、合計赤ニシンのスタックトレースを印刷していました。
それは本当の悪夢でした。
学んだ教訓:
上記のAdam Lissのメッセージは、私たち2人が取り組んだプロジェクトについて語ったもので、私が対処しなければならなかった楽しいバグを思い出しました。実際、それはバグではありませんでしたが、すぐにそれを説明します。
Adamのメッセージがまだ表示されていない場合のアプリのエグゼクティブサマリー:営業部隊の自動化ソフトウェア...ラップトップで...ダイヤルした日の終わり... Motherデータベースと同期します。
あるユーザーは、ダイヤルしようとするたびにアプリケーションがクラッシュすると不満を言いました。カスタマーサポートの担当者は、通常は電話での診断のトリックをすべて実行しましたが、何も見つかりませんでした。そのため、彼らは究極に寛容でなければなりませんでした:ユーザーFedExが私たちのオフィスにラップトップを持っています。 (これは、各ラップトップのローカルデータベースがユーザーにカスタマイズされているため、非常に大きな問題でした。そのため、新しいラップトップを準備し、ユーザーが元の作業中に使用できるようにユーザーに出荷しなければならず、最後に最初のオリジナルのラップトップでデータを同期させてください)。
そのため、ラップトップが届くと、問題を解決するために私に与えられました。現在、同期には電話回線を内蔵モデムに接続し、アプリの「通信」ページに移動し、ドロップダウンリストから電話番号を選択します(最後に使用した番号が事前に選択されています)。 DDLの番号はカスタマイズの一部であり、基本的には、オフィスの番号、「+ 1」で始まるオフィスの番号、「9 ,」で始まるオフィスの番号でした。ホテルなどからの電話.
それで、「COMM」アイコンをクリックして、リターンを押しました。ダイヤルインし、モデムに接続し、すぐにクラッシュしました。もう数回疲れました。 100%の再現性。
そこで、ラップトップと電話回線の間にデータスコープをフックし、回線を通過するデータを調べました。それはかなり奇妙に見えた...最も奇妙な部分は、私がそれを読むことができるということでした!
ユーザーは、明らかに自分のラップトップを使用してローカルのBBSシステムにダイヤルインしたかったため、会社の代わりにBBSの電話番号を使用するようにアプリの構成を変更しました。私たちのアプリは、独自のバイナリプロトコルを期待していました-ASCIIテキストの長いストリームではありません。バッファがオーバーフローしました-KaBoom!
電話番号を変更した直後にダイヤルインの問題が始まったという事実は、それが問題の原因であるという手がかりを平均的なユーザーに与えるかもしれませんが、この男はそれについて言及しませんでした。
電話番号を修正し、サポートチームに送り返しました。その男を「Bonehead user of the week」に選出するメモがありました。 (*)
(*)OkOkOk ...その男の子供が実際に何が起こったのか、父親が毎晩ダイヤルするのを見て、それがあなたがBBSにもダイヤルする方法だと考え、彼が一人で家にいたときに電話番号を変更した可能性が高いラップトップで。クラッシュしたとき、彼はラップトップに触れたことを認めたくありませんでした。だから彼はそれを片付けて、誰にも言わなかった。
それは卒業証書の論文でした。 FORTRANを使用して、ヘリウムに対する高強度レーザーの影響をシミュレートするプログラムを作成していましたatom=。
1回のテスト実行は次のように機能しました。
これらは合計で一定でなければなりませんが、そうではありませんでした。彼らはあらゆる種類の奇妙なことをしました。
2週間デバッグした後、ロギングを徹底的に試し、定数を含むシミュレーションのすべてのステップですべての変数を記録しました。
そのようにして、配列の終わりを上書きして書いていることがわかりました定数を変更しました!
友人は、そのような間違いで文字通り2を変更したことがあると言いました。
私は高校で古典的なバグについて聞いた。あなたがその前の椅子に座った場合にのみログインできる端末。 (立っている場合、パスワードは拒否されます。)
ほとんどの人にとってかなり確実に再現されました。椅子に座り、ログインし、ログアウトすることができます...しかし、立ち上がると、毎回拒否されます。
最終的には、キーボードの隣接するいくつかのキー、E/RおよびC/V IIRCを交換して、座ったときにタッチ入力して入ったが、立ったときに狩りをしなければならなかったことが判明したn peck、だからあなたは間違ったラベルを見て失敗した。
私の最初のマルチスレッドプログラムのデッドロック!
スレッドプールで発生したため、見つけるのは非常に困難でした。プール内のスレッドがデッドロックする場合がありますが、他のスレッドは引き続き動作します。プールのサイズは必要以上に大きかったため、最初の症状であるアプリケーションが完全にハングしたことに気付くまでに1、2週間かかりました。
基本的に、スレッドに関係するもの。
かつて、ある会社で役職を務めたことがありました。その会社では、厄介な問題をデバッグするためのスレッド処理を十分に快適に行える唯一の人物であるという疑わしい区別がありました。ホラー。スレッド化されたコードを書くことを許可される前に、何らかの認証を取得する必要があります。
私は何時間も何日もかけて、文字通りほんの数文字で修正できるものをデバッグしました。
いくつかのさまざまな例:
ffmpegには、ストリーム内のクロップ値が実際に完全に有効であるときに、「brainfart cropping」(インストリームクロップ値が16以上の場合を参照)に関する警告を生成するという厄介な習慣があります。 「h->」という3つの文字を追加して修正しました。
x264には、非常にまれなケース(100万フレームに1つ)で特定のオプションを使用すると、完全に間違った色のランダムブロックが生成されるというバグがありました。コードの2つの場所に文字「O」を追加して、バグを修正しました。以前のコミットで#defineの名前のスペルを間違えていました。
私の最初の「本当の」仕事は、クライアントサーバーセールスフォースオートメーションソフトウェアを書いた会社でした。お客様は(15ポンドの)ラップトップでクライアントアプリを実行し、1日の終わりに、Unixサーバーにダイヤルアップして、Motherデータベースと同期しました。一連の苦情の後、認証の最中に天文学的な数の呼び出しが落ちていることがわかりました。
数週間のデバッグの後、プロセスIDに偶数が含まれ、すぐに9が続くサーバー上のgettyプロセスが着信呼び出しに応答した場合、認証alwaysが失敗することがわかりました。 PIDの8文字の文字列表現に依存する自作スキーム。バグにより、問題のあるPIDがgettyをクラッシュさせ、新しいPIDで再生成されました。通常、2回目または3回目の呼び出しで許容可能なPIDが検出され、自動リダイヤルにより顧客が介入する必要がなくなったため、月末に電話料金が請求されるまで重大な問題とは見なされませんでした。
「修正」(ahem)は、PIDを10進数ではなく8進数でその値を表す文字列に変換し、9を含めることが不可能で、根本的な問題に対処する必要がないようにすることでした。
当社のネットワークインターフェイスであるDMA対応ATMカードは、受信パケットで破損したデータを配信することが非常にまれです。 AAL5 CRCは、パケットが回線から届くときに正しいものとしてチェックアウトしていましたが、メモリへのDMAdデータは正しくありませんでした。 TCPチェックサムは一般的にそれをキャッチしますが、ATMのheadただしい時代には、AAL5でネイティブアプリケーションを直接実行し、TCP/IPを完全に省くことに熱狂していました。ベンダーのワークステーションの一部のモデル(名前のないもの)で発生し、他のモデルでは発生しません。
ドライバーソフトウェアでCRCを計算することにより、パフォーマンスが大幅に低下する代わりに、破損したパケットを検出できました。デバッグを試みている間に、しばらくパケットを保存して、後でそれを調べに戻ると、データ破損が魔法のように自然に回復することに気付きました。パケットの内容は問題なく、ドライバーがCRCを2回計算した場合、チェックアウトは問題ありません。
出荷中のCPUのデータキャッシュにバグが見つかりました。このプロセッサのキャッシュはDMAと一貫性がなく、ソフトウェアは適切なタイミングで明示的にフラッシュする必要がありました。バグは、キャッシュがそうするように言われたときに時々キャッシュが実際にその内容をフラッシュしなかったということでした。
特定のインスタンスを思い出すことはできませんが、最も難しいカテゴリは、システムが数時間または数日間稼働した後にのみ現れ、それらがダウンしたときにクラッシュの原因の痕跡をほとんどまたはまったく残さないバグです。特に悪いのは、原因を推論し、それを修正するために適切な修正を適用したとしても、自信を得るためにさらに数時間または数日待たなければならないことです。本当に釘付けにしました。
私が今までに修正しなければならなかった最も難しいバグは、私が自分で育てたものでした-私は大規模な電話会社のテスターとして契約し、別の会社の製品をテストしました。数年後、私は他の会社と契約を結びました。彼らが最初にくれたのは、自分で育てたバグでした。
6809アセンブラーとBCPLで記述された組み込みオペレーティングシステムのカーネルの競合状態でした。デバッグ環境は、シリアルデバイスに書き込む特別なprintfで構成されていました。この設定では、空想IDE stuff。
修正するのにかなり時間がかかったが、最終的にナットを外したとき、それは大きな満足感をもたらした。
Purdueに戻ったCS435では、最終プロジェクト用にレイトレーサーを作成する必要がありました。私が作ったものはすべて、強いオレンジの色合いがありましたが、シーン内のすべてのオブジェクトを見ることができました。私は最終的にあきらめてそのまま提出し、教授にバグを見つけるために私のコードを調べてもらい、彼がそれを見つけることができなかったとき、私は夏の大部分を地獄が間違っているかを見つけるために掘りました。
色の計算関数の一部として、コードの奥深くに埋もれて、ついにintを分割して、float値を期待するOpenGL関数に渡すことに気付きました。色成分の1つは、ほとんどのシーンで十分に低かったため、0に切り下げられ、オレンジの色合いが生じました。 1つの場所(分割前)でフロートにキャストすると、バグが修正されました。
入力と期待されるタイプを常に確認してください。
それはアクセス違反のクラッシュでした。
クラッシュダンプから、コールスタックのパラメーターが破損していることがわかりました。
理由 このコードだった :
n = strlen(p->s) - 1;
if (p->s[n] == '\n')
p->s[n] = '\0';
文字列の長さが0で、上記のスタックのパラメーターがアドレス0x0Axxxxxxxにある場合
==>スタック破損
幸いなことに、このコードは実際のクラッシュの場所に十分近いため、(lyい)ソースコードを参照することで問題を見つけることができました。
私は大規模なコミュニティカレッジで働いており、昨年BlackboardからMoodleに切り替えました。 Moodleは「コース」と「グループ」の命名法を使用します。たとえば、コースはMicroeconomics ECO-150で、グループはセクションと呼ばれるものです(例として、OL1、OL2、01、14、W09)。
とにかく私たちは原始的です。 LDAPもありません。すべてがテキストファイル、Excelスプレッドシート、およびGd Microsoft Accessデータベースです。私の仕事は、上記のすべてを入力として使用し、Moodleにアップロードしてコース、コースおよびユーザーのグループを作成し、ユーザーをコースおよびグループに入れることができるよりも多くのテキストファイルを生成するWebアプリケーションを作成することです。全体のセットアップは明確にビザンチンであり、約17の個別の手順を順番に実行する必要があります。しかし、これは機能し、以前は学期の最も忙しい時間に数日かかっていたプロセスを置き換えます。
しかし、1つの問題がありました。時々、「クレイジーグループ」と名付けたものを手に入れました。したがって、それぞれ20人の学生の4つのグループでコースを作成する代わりに、それぞれ1人の学生の80のグループでコースを作成します。最悪の部分は、作成されたグループを削除するためにcpanel(私はアクセスできません)に入ることをプログラムで行う方法がありません。これは、ボタンを5回クリックするだけの手動プロセスです。そのため、Crazy Groupsのコースを作成するたびに、コースを削除する必要がありましたが、これは望ましいですが、教師がすでにコースにコンテンツを入れ始めている場合はオプションではないか、同じパターンに従って繰り返し1時間を費やす必要がありました:グループの選択、グループの表示、グループの編集、グループの削除、グループを削除してもよろしいですか?神酒はい!
また、各コースを手動で開いて(数百のコースで)見た場合、または苦情が発生するまで、狂ったグループが発生したかどうかを知る方法はありませんでした。 Crazy Groups seemedランダムに発生し、GoogleとMoodleフォーラムは役に立ちませんでした。他の誰もがLDAPまたはREALデータベースと呼ばれるものを使用しているため、問題に遭遇したことはありません。
最後に、クレイジーなグループをどれだけ調査し、削除する時間が私がこれまで認めたくなかったかを知りませんでした。それは私のコードではなく、Moodleのバグでした!これは私に少しの喜びを与えませんでした。グループを作成する方法は、誰かをグループに登録しようとするだけで、グループがまだ存在しない場合は、Moodleが作成します。そして、これはOL1またはW12またはSugarCandyMountainという名前のグループでもうまく機能しましたが、名前に番号を付けてグループを作成しようとした場合、クレイジーなグループが発生するのは01または14です。 Moodleは数字を文字列として適切に比較しません。コース内に01という名前のグループがいくつあっても、そのグループはまだ存在しないと常に考えているため、作成されます。これが、それぞれ1人ずつ80のグループになります。
私の発見を誇りに思って、私はMoodleフォーラムに行き、自由に問題を再現するための手順を備えた私の調査結果を投稿しました。それは約1年前であり、私の知る限り、問題はまだMoodle内に存在します。私たちのプリミティブ以外は誰もテキストファイル登録を使用しないため、修正する動機はありません。私の解決策は、すべてのグループ名に数字以外の文字が少なくとも1つ含まれていることを確認することです。クレイジーなグループは少なくとも私たちにとっては永遠に消え去りましたが、学期分のコースをアップロードしたばかりで、失礼な目覚めを目前にしたモンゴルのコミュニティカレッジで働いている人には気がつきます。少なくとも今回は、Googleが彼を助けるかもしれません。なぜなら、私は彼にこのメッセージをサイバースペースの潮流のボトルに書いたからです。
インスピレーションのフラッシュのおかげで、これは追跡するのにあまり長くかかりませんでしたが、それでも少し奇妙でした。 IT部門の他の人だけが使用する小さなアプリケーション。ドメイン内のすべてのデスクトップPCに順番に接続しています。多くはオフになり、接続はタイムアウトするまでAGESを要するため、スレッドプールで実行されます。 ADをスキャンし、数千の作業項目をスレッドプールにキューイングするだけです。すべて正常に動作しました。数年後、私はこのアプリケーションを実際に使用している別のスタッフと話していましたが、彼はそれがPCを使用できなくなったと言いました。 Webページを開いたり、ネットワークドライブを参照しようとして実行されている間は、数分かかるか、まったく起こりません。
問題はXPの半オープンtcp制限であることが判明しました。元のPCはデュアルプロセッサであったため、.NETは50(または100、不明)のスレッドをプールに割り当てます。問題ありません。現在、デュアルプロセッサデュアルコアがあり、半分開いている接続よりも多くのスレッドがスレッドプールにあるため、アプリケーションの実行中に他のネットワークアクティビティが不可能になります。
現在は修正されており、マシンに接続する前にpingを実行してタイムアウトを大幅に短縮し、少数の固定スレッドを使用して実際の作業を行います。
かつて複数のネットワーク監視カメラから画像をポーリングし、画像に対してあらゆる種類の魔法をかけたリアルタイムマルチスレッド(シャダー)システムを設計しました。
バグは単にシステムをクラッシュさせるだけであり、いくつかの重要なセクションはもちろん虐待されました。失敗を直接トリガーする方法はわかりませんでしたが、発生するまで待たなければなりませんでした。これは、3〜4日に1回程度でした(奇数:30 fpsで15000000に1回)。
できる限りのものを準備し、コードを汚染している出力メッセージをデバッグし、カメラ上のリモートデバッグツールを追跡し、リストが続きます。その後、2、3日待つだけで、失敗したミューテックスなどを特定するためのすべての情報を取得したいと考えました。追跡するまでに4週間かかりました。4週間です。もう1回実行すると、顧客の期限を破ってしまいます。
インターネットが流行する直前、私たちはモデムベースのホームバンキングアプリケーション(北米で最初のアプリケーション)に取り組んでいました。
リリースの3日前、私たちは(ほぼ)スケジュール通りで、残りの時間を使ってシステムを徹底的にテストすることを計画していました。テスト計画があり、リストの次はモデム通信でした。
ちょうどその頃、私たちのクライアントは急ぎの機能アップグレードを急いでやって来ました。もちろん、私はこれに完全に反対でしたが、却下されました。バカなものを追加して真夜中のオイルを3日間燃やし、リリース日までに機能させました。期限を設け、2000枚以上のフロッピーディスクを顧客に届けました。
リリースの翌日、テストスケジュールに戻り、モデム通信モジュールのテストを再開しました。驚いたことに、モデムがランダムに接続できなくなることがわかりました。ちょうどその頃、怒っている顧客がアプリケーションを使用できなくなったため、電話が鳴り始めました。
歯をたくさんかじって髪を引っ張った後、シリアルポートの初期化に問題を突き止めました。下級プログラマーが、制御レジスターの1つへの書き込みをコメントアウトしました。レジスターは初期化されずに残り、ユーザーの構成や事前に実行したアプリケーションに応じて、無効な値が含まれる可能性が約10%ありました。
それについて尋ねられたとき、プログラマーはそれが自分のマシンで動作するようになったと主張しました。
したがって、それらの2000以上のフロッピーを再書き込みし、すべての顧客を追跡してそれらを思い出す必要がありました。特にすでに燃え尽きているチームでは、楽しいことではありません。
私たちはその1つに大ヒットしました。私たちのクライアントは、それが私たちのバグであったため、リコールのコストを吸収する必要があると主張しました。次のリリースのスケジュールが1か月遅れました。そして、クライアントとの関係が損なわれました。
最近、私は土壇場での機能追加で柔軟性がはるかに低くなり、チームとのコミュニケーションを改善しようとしています。
私が取り組んでいたゲームでは、特定のスプライトはリリースモードでは表示されなくなりましたが、デバッグモードでは正常に動作し、特定のエディションでのみ動作しました。別のプログラマーがこのバグを2日間見つけようとし、その後休暇に出ました。リリースの約5時間前にバグを見つけようとするのは私の肩にかかった。
デバッグビルドが機能したため、リリースビルドでデバッグする必要がありました。 Visual Studioは、リリースビルドで一部のデバッグをサポートしていますが、デバッガーが正しいことを示すすべての情報に依存することはできません(特に、使用していた積極的な最適化設定では)。そのため、半分のコードリストと半分のアセンブラリストを実行する必要がありました。適切にフォーマットされたデバッガービューではなく、16進ダンプでオブジェクトを直接見ることもありました。
すべての正しい描画呼び出しが行われていることを確認した後、Spriteのマテリアルカラーが間違っていることがわかりました。完全な不透明のオレンジであるはずでしたが、代わりに黒に設定され、完全に透明になりました。色は、EditionManagerクラスのconst配列にあるパレットから取得されました。最初は正しいオレンジ色として設定されていましたが、実際の色がスプライトの描画コードから取得されると、再び透明な黒になりました。 EditionManagerコンストラクターでトリガーされたメモリブレークポイントを設定しました。別の配列への書き込みにより、パレット配列の値が変更されました。
結局のところ、他のプログラマーはシステムの基本的な列挙型を変更しました。
enum {
EDITION_A = 0,
EDITION_B,
//EDITION_DEMO,
EDITION_MAX,
EDITION_DEMO,
};
彼はEDITION_DEMO
の直後にEDITION_MAX
を配置し、書き込まれた配列はEDITION_DEMO
でインデックス付けされたため、パレットにオーバーフローし、そこで間違った値を設定しました。ただし、エディション番号は変更できないため(バイナリ送信で使用されていたため)、列挙を元に戻すことはできませんでした。そのため、列挙型にEDITION_REAL_MAX
エントリを作成し、それを配列サイズとして使用することになりました。
デバッグでの実行は問題ありませんが、リリースで実行するとすぐにタイミングが若干異なるため、問題が発生するマルチスレッドアプリケーション。 Console.WriteLine呼び出しを製品の基本的なデバッグアウトピットに追加しても、それが機能し、問題が表示されないようにタイミングが十分に変更されました。変更を必要とする数行のコードを見つけて修正するための1週間のツール。
ずっと前に、Cと(文字ベースの)フォームライブラリを使用してオブジェクト指向言語を書きました。各フォームはオブジェクトであり、フォームにはサブフォームを含めることができます。これを使用して記述された複雑な請求書発行アプリケーションは約20分間正常に動作し、ランダムな文字化け文字が時々画面に表示されます。アプリを使用してさらに数分後、マシンが再起動したり、ハングしたり、または何か大きな問題が発生したりします。
これは、メッセージ処理エンジンでの誤った方向の委任に起因する不適切な割り当て解除であることが判明しました。誤ってルーティングされたメッセージは、スーパークラスを使い果たしたときに包含ツリーに委任されていました。また、親オブジェクトに同じ名前のメソッドがあり、ほとんどの場合動作するように見えることがありました。残りの時間は、間違ったコンテキストで小さなバッファー(8バイト程度)の割り当てを解除します。誤って割り当て解除されたポインターは、実際には別の操作の中間カウンターによって使用されるデッドメモリーであったため、その値は時間の経過後にゼロに収束する傾向がありました。
はい、不正なポインタはゼロページに向かう途中で画面のメモリマップ領域を通過し、最終的に割り込みベクトルを上書きしてPCを強制終了します。
これは、最新のデバッグツールの前の方法だったので、何が起こっているのかを理解するには数週間かかりました...
私の1人ではありませんが、以前の勤務地の同僚がJavaScriptポップアウトエディターコントロールのデバッグに3日間を費やしました(これはかなり前のことで、フレームワークの喜びがありませんでした)。巨大なコアファイルの1つ。
私たちは「世界で最も高価なセミコロン」と呼んでいますが、歴史を通じてずっと悪化していると確信しています!
OracleのOracleDecimalクラスのToStringメソッド(同じ機能のネイティブバージョンを呼び出します)とGC.KeepAlive呼び出しの欠落が原因のガベージコレクターとの競合。呼び出しが終了する前に上書きされることがあります。
詳細なバグ報告を書いたが、返事はなかった。私はこれがまだあることを知っているからだ。テスト用のハーネスもありましたが、このテストハーネスには、番号1の新しいOracleDecimal表現を作成するだけで、それらに対してToStringを呼び出し、結果を「1」と比較しました。クレイジーなちらつき(巨大な数字、負の数字、さらには英数字のジャンク文字列)で1000万回ごとに失敗します。
P/Invoke呼び出しには注意してください!インスタンスメソッドがthis
参照の使用を終了している限り、.NETガベージコレクターがそのインスタンスのインスタンスメソッドの呼び出しが保留中の間にインスタンスを収集することは正当です。
Reflector は、このようなものの絶対的な命の恩人です。
大きな顧客のサイトでボックスがクラッシュし、WebXセッションを介して、ボックスに接続されているIT担当者のコンピューターに接続する必要がありました。約1時間、スタックトレース、レジスタダンプ、統計、カウンター、および関連があると思われるメモリのセクションを取得しました。
その後、彼らのIT担当者からセッションの記録がメールで送られ、仕事に取り掛かりました。
数時間後、パケットメタデータとそれに続くパケットデータを含む構造体の配列までさかのぼります。パケットのメタデータの1つが破損しており、数バイトのパケットデータによって上書きされたように見えました。 Bugzillaには同様の記録はありませんでした。
コードを掘り下げて、明らかなことをすべてチェックしました。パケットデータをバッファーにコピーするコードは、境界を超えないように細心の注意を払っていました。バッファーはインターフェイスのMTUサイズであり、コピールーチンはデータがMTUサイズを超えないことを確認しました。私のメモリダンプにより、クラッシュが発生したときにfoo-> barが実際に4だったことを検証できました。何も追加されません。問題の原因となるはずの問題は何もありませんでした。次のヘッダーには、16バイトのパケットデータのように見えるものがありました。
数日後、私は考えられるすべてのものをチェックし始めました。
データバッファの長さが実際に正しいことに気付きました。つまり、次のヘッダーがMTU-16で始まっていても、データの開始からデータの終わりまでのバイト数はMTUでした。
これらの構造体がmallocされたとき、各要素へのポインターが配列に配置され、その配列をダンプしました。これらのポインター間の距離の測定を開始しました。 6888 ... 6888 ... 6888 ... 6872 ... 6904 ... 6880 ... 6880 ...
待って、何?
両方の構造の内部ポインターとオフセットを調べ始めました。すべてが追加されました。それはちょうど私の1つの悪い構造のように見えました-部分的に破壊されていたものは-メモリー内の16バイトが早すぎました。
割り当てルーチンは、これらの男をチャンクとしてmallocしてから、ループに切り分けました。
for (i = 0; i < NUM_ELEMS; i++) {
array[i] = &head[i*sizeof(foo)];
}
(アライメントの手当など)。
配列がいっぱいになったとき、破損したポインターの値は、0x8a112 9 acではなく、0x8a112 8 acとして読み込まれている必要があります。
私は割り当て中に1ビットのメモリエラーの犠牲になったという結論に達しました(私は知っています、私は知っています!私もそれを信じていませんでしたが、このハードウェアで以前にそれらを見ていた-NULL値0x00800000として読み取られました)。いずれにせよ、私は上司や同僚に、他に合理的な説明はないこと、そして私の説明が見ているものを正確に説明していることを納得させました。
それで、箱はRMAされました。
これは少しトピックから外れています(だから私はコミュニティにしました)。
しかし The Bug Ellen Ullmanはこのまさにトピックに関する素晴らしい架空の本です。
最初に会社に入社したとき、私は多くのCPRを行って製品を学びました。
HC11アセンブリで記述されたこの組み込み製品には、8時間ごとに発生する機能がありました。カウンタをチェックしているコードの間に値が発生したことを減少させた割り込みを見つけます。いくつかのCLI/STIをコードの周りで平手打ちしましたが、問題ありませんでした。イベントを8時間ごとではなく、1秒に2回ハッキングすることで追跡しました。
これから学んだ教訓は、まれに失敗するコードをデバッグするとき、最初に割り込みで使用される変数をチェックする必要があることでした。
私は現在大学に通っていますが、私が遭遇した最も難しいバグは、そこのプログラミングクラスからのものでした。前の2学期では、独自のコードをすべて記述しました。しかし、3学期では、教授とTAが半分のコードを書き、残りの半分を書きます。これは、コードの読み方を学ぶのに役立ちました。
その学期の最初の割り当ては、DNA遺伝子の分裂をシミュレートするプログラムを書くことでした。基本的に、より大きなサブストリングを見つけて結果を処理する必要がありました。どうやら、教授とTAは両方ともその週忙しかったので、完全な実装をまだ完了させることなく、コードの半分を提供してくれました。彼らは解決策として行動するために残りの半分を書く時間がありませんでした。半分はコンパイルされますが、完全なソリューションがコーディングされていないと、テストする方法がありませんでした。 教授コードを変更しないように言われた。クラスの全員がまったく同じバグを抱えていましたが、私たちは皆、同じ間違いを犯しているだけだと考えていました。
プログラムはギガバイトのメモリをゴブリングし、その後実行されてクラッシュしました。私たち(学生)は全員、コードの半分にあいまいなメモリリークが必要であると想定していました。クラスの全員が2週間コードを精査し、何度も何度もデバッガーで実行していました。入力ファイルは5.7 MBの文字列で、その中に数百の部分文字列を見つけて保存していました。教授/ TAのコードはこれを使用しました。
myString = myString.substr(0,pos);
問題が発生しましたか?文字列変数を独自のサブストリングに割り当てると、メモリは再割り当てされません。これは、誰も知らなかった(教授やTAでさえも)情報のほんの一部です。したがって、myStringには、数バイトの実際のデータを保持するためだけに5.7 MBのメモリが割り当てられていました。これは何百回も繰り返されました。したがって、大量のメモリ使用量。この問題に2週間を費やしました。最初の1週間は、自分のコードでメモリリークをチェックしました。フラストレーションの中で、ようやく教授/ TAの半分にリークがあるはずだと結論付けたため、2週間目はコードをチェックしました。しかし、それでも、これは技術的にリークではなかったため、見つけるのに時間がかかりました。最終的にすべての割り当てが解放され、入力データがわずか数十キロバイトのときにプログラムが正常に機能しました。私がそれを見つけた唯一の理由は、私がサイコクレイジーを送り、すべての最後の変数を分析することを決めたからです。一時的なものでも。また、割り当てられた量ではなく、文字列が実際に持っていた文字数を確認するのに多くの時間を費やしていました。文字列クラスがこれを処理していると思いました。これが解決策でした。1行の変更で、数週間のイライラが解消され、教師のコードを見つけて修正するための課題でAが得られました。
myString.substr(0,pos).swap(myString);
スワップ方法は、再割り当てを強制します。
特定のユーザーが特定のインベントリ機能にアクセスすると、レガシーデータベースベースのアプリケーション(ソースの一部のみが使用可能)がクラッシュしました。他のすべてのユーザーにとっては完璧に機能しました。ユーザープロファイルは正しいですか?いや。別のユーザーとして(管理者としても)ログインすると、同じユーザーに同じ問題が発生しました。
コンピューターの問題?いや。同じユーザー、別のPC(彼女のログインまたは他のログイン)がまだクラッシュしています。
問題:プログラムにログインすると、「X」をクリックしてウィンドウを閉じるか、キーを押すと閉じることができる著作権スプラッシュ画面が表示されました。このユーザーにログインするときは、他のユーザーが常にキーを押す「X」をクリックしました。これにより、インベントリルックアップにアクセスしたときにのみ発生するメモリリークが発生しました。
修正:Xをクリックしないでください。
面白そうに思えるかもしれませんが、学習中にif文が常にtrueに評価される理由を解明しようとして午後全体を費やしました==の代わりに=を使用しました:d
Javaサーバーアプリケーション。ただし、2つのスレッドによる単純なデッドロックではありません。8つのスレッドが関与するデッドロックを追跡しました。スレッド1はスレッド3を待機するスレッド2を待機します最後に、スレッド8はスレッド1を待ちます。
何が起こっているのかを理解するのに丸一日かかり、それを修正するのにわずか15分かかりました。デッドロックが見つかるまで、Eclipseを使用して約40個のスレッドを監視します。
Pythonでは、次のようなことを行うスレッドがありました。
_while True:
with some_mutex:
...
clock.tick(60)
_
clock.tick(60)
は、1秒間に60回を超えないようにスレッドを中断します。問題は、ほとんどの場合、プログラムが黒い画面を表示することでした。しばらく実行させると、ようやくゲーム画面が表示されました。
スレッドが一時停止を行っていたためですmutexを維持しながら。したがって、他のスレッドがミューテックスを獲得することはほとんどありません。ここでは明白に思えるかもしれませんが、私はそれを理解するのに2日かかりました。解決策は、単にインデントレベルを削除することです。
_while True:
with some_mutex:
...
clock.tick(60)
_
CLRをクラッシュさせるバグが.NETアプリにあったことがあります。はい、CLRはゼロ以外の結果で終了し、デバッグ情報はありません。
問題がどこにあるか(起動時にエラーが発生する)を見つけようとして、コンソールトレースメッセージをコードに追加し、最終的に問題の原因となっている数行を見つけました。私は問題を切り分けようとしましたが、そのたびに切り分けられたケースが機能しました!
最終的に、コードを次のように変更しました。
int value = obj.CalculateSomething();
に
int value;
value = obj.CalculateSomething();
理由を聞かないでください、しかしこれはうまくいきました。
アプリケーションがインストールされているディレクトリパスに少なくとも1つのスペースが含まれておらず、データディクショナリXPOのチェックが100%正しく行われていない場合、Oracleデータベースと通信するDevExpress XPOが激しくクラッシュします(プログラムがサイレントに終了するなど)データベース。
説明されている問題 ここ 。
私はあなたにこれを伝えることができます:私はこれでした> <問題を回避する方法を見つけたとき、泣きそうになりました。問題の実際の本当の原因が何であるかはまだわかりませんが、私たちの製品は将来のバージョンでOracleをサポートしないので、実際には....を提供していません。
最も困難なバグは、プログラマーがログ「General Error!」に出力するときでなければなりません。コードを調べた後、「General Error!」というテキストが至る所に散らばっていました。その1つを釘付けにしてみてください。
少なくとも__LINE__または__FUNCTION__を出力するマクロを作成すると、デバッグ出力に追加するのに少し役立ちます。
現在の年に1を追加し、日と月を同じにして、現在の日付に1年を加えた有効期限を設定するコードがありました。データベースが2009年2月29日の受け入れを拒否したため、これは2008年2月29日に大きな失敗に終わりました。
それが「タフ」であることの資格があるかどうかはわかりませんが、もちろん、すぐに書き直された奇妙なコードでした!
これが最も難しいかどうかはわかりませんが、数年前に特定のクラスを保存/ロードするためにXMLEncoder
を使用するJavaプログラムがありました。何らかの理由でクラスエラーを簡単にバイナリ検索したところ、ある関数呼び出しの後、別の呼び出しの前にエラーが発生していることがわかりました。これは不可能だったはずです。休憩を取った(そして去っていった)問題に気づいた。クラスとクラスへの参照の両方が同じオブジェクトを参照するのではなく、XMLEncoder
がクラスのデフォルトで構築されたインスタンスを作成していたことが判明した。したがって、特定のクラスの同じインスタンスのメンバーの両方で2つの関数呼び出しを考えたが、一方は実際にはデフォルトで構築されたコピー上にあった。
私はknewが両方とも同じクラスへの参照であったため、見つけるのは困難でした。
一度カスタム同期プログラムにバグがありました。ファイル/フォルダーの日付/時刻スタンプを使用して、フラッシュキーからWindowsのネットワーク共有にデータを同期するために変更されたものを比較し、追加の整合性とビジネスロジックを組み込みました。
ある日、オペレーターがログを確認した後、同期が永遠にかかっていると報告しました。何らかの理由で、ソフトウェアはスティック(またはサーバー)のすべてのファイルが本来より3時間古いと判断し、8つのすべてのギグを更新しましたデータの!私はUTCを使用していましたが、これはどのようにできますか?
実際、この特定のオペレーターはタイムゾーンを東部ではなく太平洋時間に設定して問題を引き起こしましたが、すべてのコードがUTCを使用していたため、問題はないはずです。ローカルシステムでテストすると機能しました...
この時点で、すべてのオペレーターにラップトップが同期する前に東部時間に設定されていることを確認し、調査する時間が増えるまでバグはキューにとどまりました。
その後、10月がやってきて、BOOM!サマータイム!なんてこった!?今、誰もが同期が永遠にかかっていると不平を言っていました!修正する必要があり、高速でした!
ローカルハードドライブからではなくスティックから実行するようにテストケースを変更することで追跡しましたが、確かに、失敗しました...メモリスティックのことです-ちょっと待ってください、FAT32でフォーマットされていますか...あはは! FAT32は、ファイルのタイムスタンプを記録するときにローカルタイムを使用します!
http://msdn.Microsoft.com/en-us/library/ms724290(VS.85).aspx
そのため、FAT32メディアに書き込むときにプログラムでUTCに設定するように、ソフトウェアが書き直されました...
数年前、私は数日かけて、AIX上のテキストベースのデバッガーであるdbxの小さなバグを追跡して修正しようとしました。正確なバグを覚えていません。難しかったのは、インストールしたdbxを使用して、作業中のdbxの開発バージョンをデバッグしていたことです。私がいた場所を追跡することは非常に困難でした。 2回以上、その日に出発する準備をし、dbxを2回(devバージョンとインストール済みバージョン)終了して、dbx内でstillを実行していることを確認しました。 。
-
bmb
Turbo Pascalで書かれたGUIアプリでの厄介なクラッシュ。 3日前に、デバッガでシングルステップすることで、単純で明らかに正しいコード上で、マシンコードレベルで、32ビット(または一部のそのような不一致)
今、私はそれが賢明です。ただし、最新のコンパイラーでは、この種の問題はもう許されません。
Rhino(JavaのJavascriptインタープリター)の小さなバグが原因で、1つのスクリプトが失敗していました。インタープリターがどのように機能するかについてほとんど知らなかったので大変でしたが、別のプロジェクトのために、できるだけ早くバグを修正するためにそこに飛び込む必要がありました。
最初に、Javascriptのどの呼び出しが失敗したかを突き止めたため、問題を再現できました。実行中のインタープリターをデバッグモードでステップ実行しましたが、最初はかなり失われましたが、動作の仕組みを少しずつ学びました。 (ドキュメントを読むことは少し助けになりました。)私は関連があると思われる箇所にprintlns/loggingを追加しました。
実行中の実行のログファイルをブレーク実行と比較して、それらが最初に分岐し始めた時点を確認しました。再実行して多数のブレークポイントを追加することで、失敗に至る一連のイベントへの道を見つけました。どこかにコード行があり、わずかに異なって書かれていれば、問題を解決しました! (nextNode()はIndexOutOfBoundsの代わりにnullを返す必要があるなど、非常に単純なものでした。)
2週間後、他の特定の状況で修正によりスクリプトが破損することに気付き、すべてのケースでうまく機能するように行を変更しました。
私はなじみのない環境にいました。そのため、そのうちの1つが機能するまで、または少なくともある程度の進歩/理解を助けるまで、さまざまなことを試しました。それdidしばらくかかりますが、最終的にそこに到着できてうれしいです!
もう一度やり直す場合は、プロジェクトのIRCチャネル(メーリングリストだけでなく))を探して、いくつかの丁寧な質問をして、ポインタを探します。
DOSプロンプトでRMIサーバーが実行されていました。誰かがウィンドウを「選択」しました-プロセスを一時停止しました
修正は非常に簡単でした... Enterを押してください。
とてもつらい日でした...
プラットフォームにバグがあり、デバイスデバッガに非常に悪い影響がありました。コードにprintfを追加すると、デバイスでクラッシュが発生します。その後、printfの場所とは異なる場所でクラッシュします。 printfを移動すると、クラッシュは移動するか消えます。実際、いくつかの単純なステートメントの順序を変更してコードを変更すると、変更したコードとは関係のない場所でクラッシュが発生します。
これは古典的な Heisenbug のように見えます。それを認識するとすぐに、初期化されていない変数またはスタック境界のトラッシングをすぐに探します。
主な難点がそれを理解していなかったハイゼンバグは、私のバグではありませんでした。
問題はAPIインターフェースでした。実際の関数を呼び出すと(セットアップとは対照的に)、保護違反でクラッシュする可能性が非常に高くなりました。関数をシングルステップ実行すると(可能な範囲で、割り込みが発生し、そのポイントを超えてトレースできませんでした。これは、割り込みを使用してシステムと通信したときに戻っていました)、正しい出力を生成し、クラッシュしませんでした。
間違って何をしていたのか無駄に長い間検索した後、私はついにRTLルーチンを掘り下げて、間違って何をしていたのかを理解しようとしました。私が間違っていたのは、ルーチンが機能していると信じることでした。爆撃されたすべてのルーチンは、プロテクトモードポインタータイプでリアルモードポインターを操作していました。リアルモードセグメントの値が保護モードで有効でない限り、これはブームになりました。
ただし、デバッガーによるプログラムの操作に関する何かが、シングルステップ中に正しい動作を引き起こしたので、私はその理由をわからないようにしました。
2つの言葉で:メモリリーク。
使用したテキスト編集コントロールでのヒープメモリ違反。それを探して何ヶ月も(...)探した後、別のプログラマーと協力して、問題をピアデバッグするソリューションを見つけました。このインスタンスは、チームおよびアジャイル全般で働くことの価値を確信させました。詳細については my blog をご覧ください
私は次のコードで誰かのバグを修正します:
private void foo(Bar bar) {
bar = new Bar();
bar.setXXX(yyy);
}
彼はbar
がfoo
外で変更されることを期待していました!
進行中の進行状況バーを更新する長い処理ルーチンを実行するデルファイコードがありました。コードは16ビットDelphi 1で正常に実行されましたが、デルファイ2にアップグレードすると、2分かかっていたプロセスが突然約1時間かかりました。
ルーチンを数週間引き離した後、問題を引き起こしたプログレスバーを更新した行であることが判明しました。 dbaseテーブルでtable.recordcountを呼び出すdelphiは、テーブルのコピーを取得してレコードをカウントし、その量を返します。解決策は、処理を開始する前にレコードをカウントし、変数に量を保存することでした。
見つけるのに何年もかかりましたが、とても簡単であることがわかりました。
サービスからロードされたDLLで発生するクラッシュ。システムをシャットダウンするとトリガーされます。
バグを修正するのは簡単でしたが、見つけるのに約1週間、そして多くのフラストレーションがかかりました。
私が思い出すことができるものがいくつかありますが、それらのほとんどは私が原因です:)。これらのほとんどの1つは、多くの頭を引っかく必要がありました。
私はJavaプロジェクト(リッチクライアント)の一部であり、Javaコードはバニラビルドまたは新しいマシンで問題なく動作していましたが、プレゼンテーションラップトップにインストールすると、突然動作を停止し、スタックダンプをスローし始めました。さらなる調査により、コードはcygwinと競合するカスタムdllに依存していることが示されました。これで話は終わりではありません。他の5つの町にインストールして、マシンの1つで再びクラッシュしたものを推測することになっています。今回は犯人がjvmであり、私たちが与えたコードはSun microsystems jdkを使用してビルドされたもので、マシンにはibmのjvmがありました。
私が思い出すことができる別のインスタンスは、カスタムイベントハンドラコードと関係しています。コードはユニットテストと検証が行われ、最後にprint()ステートメントを削除しました。デバッグすると、コードは完全に実行され、私たちの責任に追加されました。私は禅瞑想(机の上の仮眠)に頼らなければならず、一時的なアナモリーがあるかもしれません!委任していたイベントは、条件が設定される前でも関数をトリガーしていました。印刷ステートメントとデバッグモードは、条件が設定されるのに十分な時間を与え、適切に機能しました。安のため息といくつかのリファクタリングが問題を解決しました。
ある晴れた日、Clonableインターフェースを実装するためにいくつかのドメインオブジェクトが必要であると判断しました。数週間後、アプリケーションが異常に動作し始めたことを確認しました。何だと思う?これらの浅いコピーをコレクションクラスに追加し、remove()メソッドが実際にコンテンツを適切にクリアしていませんでした(同じオブジェクトを指す参照が重複しているため)。これにより、いくつかの深刻なモデルのレビューと眉の浮き上がりが発生しました。
私がこれまでに持っていた最も難しいバグは、私が原因ではありませんでしたが、コードがクラッシュしました!これはDOS上のTurboPascalでした。 TurboPascalコンパイラコンパイラのマイナーアップグレードが行われ、突然バイナリがクラッシュし始めました。新しいバージョンでは、メモリはセグメントの境界でのみ割り当てられていました。もちろん、なぜ私のプログラムはそのようなことをチェックしなかったのですか?プログラマはどのようにしてそのようなことを知るのでしょうか?古いcompuserve特別利益団体の誰かがこの手がかりと回避策を投稿しました:
セグメントは4ワード長だったので、修正は常にmod(4)を実行して、割り当てるメモリのサイズを計算することでした。
説明されていないSQL Serverのタイムアウトと断続的なブロック
明らかに理由もなくユーザーがタイムアウトするという問題がありました。 SQL Serverをしばらく監視したところ、たまに多くのブロッキングが発生することがわかりました。そのため、この原因を見つけて修正する必要があります。
ブロッキングが発生している場合は、ストアドプロシージャコールのチェーンのどこかに排他ロックがあったはずです。正しい?
呼び出されたストアドプロシージャの完全なリスト、および後続のストアドプロシージャ、関数、ビューのすべてを確認しました。時々、この階層は深く、さらには再帰的です。
UPDATEまたはINSERTステートメントを探していました…。何もありませんでした(ストアドプロシージャのスコープしか持たないため、カウントされない一時テーブルを除きます)。
さらなる調査で、ロックの原因は次のとおりであることがわかりました。
A. SELECT INTOを使用して一時テーブルを作成すると、SQL Severはシステムオブジェクトをロックします。 getUserPrivilegesプロシージャには次のものがあります。
--get all permissions for the specified user
select permissionLocationId,
permissionId,
siteNodeHierarchyPermissionId,
contactDescr as contactName,
l.locationId, description, siteNodeId, roleId
into #tmpPLoc
from vw_PermissionLocationUsers vplu
inner join vw_ContactAllTypes vcat on vplu.contactId = vcat.contactId
inner join Location l on vplu.locationId = l.locationId
where isSelected = 1 and
contactStatusId = 1 and
vplu.contactId = @contactId
GetUserPrivilegesプロシージャは、すべてのページリクエストで呼び出されます(ベースページにあります)。期待どおりにキャッシュされませんでした。似ていませんが、上記のSQLはFROM句またはJOIN句で23個のテーブルを参照しています。これらの表には「with(nolock)」ヒントが含まれていないため、必要以上に時間がかかっています。含まれる行の数を把握するためにWHERE句を削除すると、159,710行が返され、実行に3〜5秒かかります(サーバー上に誰もいない時間後に)。
したがって、このストアドプロシージャはロックのために一度に1つしか実行できず、ページごとに1回呼び出され、選択および一時テーブルの作成中にシステムテーブルのロックを保持する場合、アプリケーション全体のパフォーマンスにどのように影響するかを確認できます。
これに対する修正方法は次のとおりです。1.セッションレベルのキャッシュを使用して、セッションごとに1回だけ呼び出されるようにします。 2. SELECT INTOを標準のTransact-SQL DDLステートメントを使用してテーブルを作成するコードに置き換えてから、INSERT INTOを使用してテーブルに入力します。 3.この呼び出しに関係するすべてに「with(nolock)」を付けます。
B.ストアドプロシージャgetUserPrivilegesに十分な問題がなかった場合は、追加してみましょう。おそらく、呼び出しごとに再コンパイルされます。そのため、SQL Serverは各呼び出しでCOMPILEロックを取得します。
再コンパイルされる理由は、一時テーブルが作成され、そこから多くの行が削除されるためです(@locationIdまたは@permissionLocationIdが渡される場合)。これにより、ストアドプロシージャが後続のSELECTで再コンパイルされます(はい、ストアドプロシージャの実行中)。他のプロシージャでは、SELECTステートメントが一時テーブルを参照するDECLARE CURSORステートメントに気付きました。再コンパイルも。
再コンパイルの詳細については、以下を参照してください。 http://support.Microsoft.com/kb/243586/en-us
これに対する修正方法は次のとおりです。 2.テーブルの作成中に、WHERE句で@locationIdまたは@permissionLocationIdフィルタリングを適用します。 3.一時テーブルをテーブル変数に置き換えます-再コンパイルが少なくなります。
期待どおりに機能しない場合は、何が間違っているのかを把握することなく、何かをじっと見つめることができます。
PHPをアンインストールしたことがあります。手動で。一度の動きで修正された多くのバグ...