あなたの最も難しいバグの狩りは何でしたか?どのようにして見つけて殺しましたか?


31

これは「知識を共有する」質問です。あなたの成功や失敗から学ぶことに興味があります。

役立つ情報...

バックグラウンド:

  • コンテキスト:言語、アプリケーション、環境など
  • バグはどのように識別されましたか?
  • バグを特定したのは誰ですか?
  • バグの再現はどれほど複雑でしたか?

狩り。

  • あなたの計画は何でしたか?
  • どのような困難に遭遇しましたか?
  • 問題のコードはどのようにして最終的に見つかりましたか?

殺害。

  • 修正はどの程度複雑でしたか?
  • 修正の範囲をどのように決定しましたか?
  • どのくらいのコードが修正に含まれていましたか?

死後。

  • 根本的には根本的な原因は何ですか?バッファオーバーランなど
  • 30,000フィートからの根本原因は何でしたか?
  • プロセスには最終的にどのくらい時間がかかりましたか?
  • 修正によって悪影響を受けた機能はありましたか?
  • どのような方法、ツール、動機が特に役立ちましたか?...ひどく役に立たない?
  • もう一度やり直せたら?............

これらの例は一般的なものであり、すべての状況に適用できるわけではなく、おそらく役に立たない可能性があります。必要に応じて味付けしてください。

回答:


71

実際には、アプリケーションのサードパーティの画像ビューアサブコンポーネントにありました。

私たちは、アプリケーションのユーザーのうち2〜3人が頻繁に画像ビューアーコンポーネントに例外を投げさせ、恐ろしく死ぬことを発見しました。ただし、他の何十人ものユーザーが、仕事のほとんどの時間に同じタスクにアプリケーションを使用しているにもかかわらず、この問題を見たことはありませんでした。また、特に他のユーザーよりもはるかに頻繁に取得したユーザーが1人いました。

通常の手順を試しました。

(1)コンピューター/構成を除外する問題がなかった別のユーザーとコンピューターを切り替えました。-問題は彼らに続いた。

(2)アプリケーションにログインし、問題を見たことのないユーザーとして働いていた。-それでも問題は続いた。

(3)ユーザーが表示している画像を報告し、テストハーネスを設定して、その画像を何千回もすばやく連続して表示することを繰り返しました。この問題はハーネスには現れませんでした。

(4)開発者がユーザーと一緒に座って、一日中見守っていた。彼らはエラーを見ましたが、それらを引き起こすために彼らが普通でないことをしていることに気づきませんでした。

私たちはこれに何週間も苦労し、他のユーザーにはない「エラーユーザー」の共通点を見つけようとしました。方法はわかりませんが、ステップ(4)の開発者は、ある日Encyclopedia Brownにふさわしい仕事をするためのドライブにユーレカの瞬間がありました。

彼は、すべての「エラーユーザー」が左利きであることを認識し、この事実を確認しました。左利きのユーザーだけがエラーを受け取りました。しかし、左利きになるとどのようにバグが発生するのでしょうか?

私たちは彼に座って、左利きの人たちが彼らが違うやり方をしているかもしれないことに特に注意を払って再び見るのを見て、それが私たちがそれを見つけた方法です。

バグは、新しい画像のロード中にマウスを画像ビューアーのピクセルの右端の列に移動した場合にのみ発生することが判明しました(ベンダーがmouseoverイベントに対して1回の計算を行ったため、オーバーフローエラーが発生しました)。

どうやら、次の画像がロードされるのを待っている間、ユーザーはすべて自然に手(つまりマウス)をキーボードに向かって動かしました。

エラーが最も頻繁に発生した1人のユーザーは、次のページがロードされるのを待っている間にマウスを強迫的に動かしたADDタイプの1人でした。タイミングがちょうどいいので、ロードイベントが発生したときにそれを行いました。ベンダーから修正プログラムを入手するまで、(次のドキュメント)をクリックした後、マウスを離すように指示し、ロードされるまでタッチしないようにしました。

今後、開発チームの伝説では「左利きのバグ」として知られていました


14
それは私が今まで聞いた中で最も悪いことです。
ネイサンテイラー

9
しかし、それはそれを解決した人からヒーローを作りました。
JohnFx

2
うわー、今それはバグの一体です!
ミッチェル販売者

3
素晴らしい発見!いい話。
トゥーンクリティ

11
私たちの左翼は、二流市民のように十分に扱われていないかのように。さて、私たちはソフトウェアバグのかなりのシェア以上に悩まされる必要があります...うん、ありがとう!:p
ダンモールディング

11

これはずっと前(1980年代後半)のものです。

私が働いていた会社は、さまざまなUnixワークステーション(HP、Sun、Silcon Graphicsなど)で実行されるCADパッケージ(FORTRAN)を作成しました。独自のファイル形式を使用してデータを保存し、パッケージの起動時にディスク領域が不足していたため、エンティティヘッダーに複数のフラグを保存するために多くのビットシフトが使用されました。

エンティティのタイプ(ライン、アーク、テキストなど)は、格納時に4096(私が思うに)倍されました。さらに、この値は、削除されたアイテムを示すために否定されました。そのため、型を取得するためのコードがありました。

type = record[1] MOD 4096

1台を除くすべてのマシンで、これは±1(ラインの場合)、±2(アークの場合)などを与え、削除されたかどうかを確認するためにサインを確認できました。

1台のマシン(HPの場合)で、削除されたアイテムの処理が台無しになるという奇妙な問題が発生しました。

これはIDEおよびビジュアルデバッガーの前の時代であったため、トレースステートメントとログを挿入して問題を追跡する必要がありました。

私は最終的に、それが他のすべてのメーカーが実装しているMODため-4096 MOD 4096-1HPが数学的に正しく実装しているために発生したことを発見し-4096 MOD 4096ました-4097

コードベース全体を調べて値の符号を保存し、正の値にしてから実行してMODから結果に符号値を掛けなければなりませんでした。

これには数日かかりました。


3
おそらく長年に渡ってより困難なバグ探しが行われてきましたが、これは20年以上も私の心に残っています!
ChrisF

7

うわー、ここで良い読書!

私が一番苦労したのは、Turbo Pascalが大きかった頃でしたが、当時の初期のC ++ IDEの1つだったかもしれません。唯一の開発者(そしてこのスタートアップの3番目の人物)として、私は単純化された営業担当者に優しいCADプログラムのようなものを書きました。当時は素晴らしいものでしたが、厄介なランダムクラッシュが発生しました。再現することは不可能でしたが、頻繁に発生したため、バグハントを開始しました。

私の最善の戦略は、デバッガでシングルステップすることでした。ユーザーが十分な図面を入力し、特定のモードまたはズーム状態にする必要がある場合にのみバグが発生したため、多くの退屈な設定とクリアのブレークポイントがあり、図面を入力するために1分間正常に実行してから、大量のコードをステップ実行します。特に役立つのは、調整可能な回数をスキップしてからブレークするブレークポイントです。この演習全体を数回繰り返す必要がありました。

最終的には、サブルーチンが呼び出されている場所に絞り込みました。2が与えられましたが、その中からは意味不明な数字が見えました。私はこれを以前に捕まえたかもしれませんが、それが与えられたものを得たと仮定して、このサブルーチンに足を踏み入れていませんでした。最も単純なものは大丈夫だと仮定して盲目にしました!

スタックに16ビットのintを詰め込んでいることが判明しましたが、サブルーチンは32ビットを想定しています。またはそのようなもの。コンパイラは、すべての値を自動的に32ビットに埋めたり、十分な型チェックを行ったりしませんでした。修正するのは簡単で、1行の一部にすぎず、ほとんど何も考える必要はありませんでした。しかし、そこにたどり着くには、3日間の狩りと明白な質問が必要でした。

だから、私は高価なコンサルタントが入ってくるという逸話について個人的な経験をしており、しばらくするとどこかでタップして2000ドルを請求します。幹部は内訳を要求しており、タップ1ドル、タップする場所を知る1999ドルです。私の場合を除いて、それはお金ではなく時間でした。

学んだ教訓:1)最高のコンパイラを使用します。ここで、「最高」とは、コンピュータサイエンスがチェックする方法を知っている限り多くの問題のチェックを含むと定義されます。

それ以来、必要と思われるよりも単純なことを徹底的にチェックすることを知っているので、すべての難しいバグは本当に難しいものでした。

レッスン2は、私がこれまでに修正した最も難しい電子機器のバグにも適用されます。これも些細な修正ですが、いくつかのスマートEEが数か月間困惑していました。しかし、これは電子フォーラムではないため、これ以上は述べません。


他の場所に電子機器のバグとリンクを投稿してください!
tgkprog

6

地獄からのネットワークデータの競合状態

別の開発者が作成した非常に古い(Encore 32/77)ワークステーションで同様のアプリケーションを使用するために、ネットワーククライアント/サーバー(Windows XP / C#)を作成していました。

アプリケーションが本質的に行ったことは、ホスト上の特定のデータを共有/操作して、システムを実行しているホストプロセスをファンシーPCベースのマルチモニタータッチスクリーンUIで制御することでした。

3層構造でこれを行いました。通信プロセスは、ホストとの間でデータの読み取り/書き込みを行い、必要なすべての形式変換(エンディアン、浮動小数点形式など)を行い、データベースとの間で値を読み書きしました。データベースは、通信とタッチスクリーンUI間のデータ仲介として機能しました。タッチスクリーンUIのアプリは、PCに接続されているモニターの数に基づいてタッチスクリーンインターフェイスを生成しました(これは自動的に検出されました)。

ホストとPCの間の値のパケットが与えられた時間枠では、ラウンドトリップあたり最大110ミリ秒の最大遅延で、一度に最大128個の値しかワイヤを介して送信できませんでした(UDPは、コンピューター)。そのため、接続されたタッチスクリーンの可変数に基づいて許可される変数の数は厳密に制御されていました。また、ホスト(リアルタイムコンピューティングに使用される共有メモリバスを備えた非常に複雑なマルチプロセッサアーキテクチャを備えています)は、私の携帯電話の約100分の1の処理能力を備えていたため、可能な限り少ない処理とサーバーこれを保証するために/ clientをアセンブリで記述する必要がありました(ホストは、プログラムの影響を受けない完全なリアルタイムシミュレーションを実行していました)。

問題はそうでした。一部の値は、タッチスクリーンで変更すると、新しく入力した値だけを取得するのではなく、その値と前の値の間でランダムに循環します。これは、特定のページの組み合わせを持ついくつかの特定のページのいくつかの特定の値でのみ、これまでに症状を示していました。最初の顧客受け入れプロセスを実行し始めるまで、問題をほぼ完全に見落としていました。


問題を特定するために、振動値の1つを選択しました。

  • タッチスクリーンアプリを確認しましたが、振動していました
  • 振動してデータベースを確認しました
  • コミュニケーションアプリを確認し、振動しました

次に、wiresharkを開始し、パケットキャプチャの手動デコードを開始しました。結果:

  • 振動していませんが、パケットが正しく見えなかったため、データが多すぎました。

コミュニケーションコードのすべての詳細を100回ステップスルーして、欠陥やエラーを見つけませんでした。

最後に、私は他の開発者にメールを送り始め、私が行方不明になったものがあるかどうかを確認するために彼の終わりがどのように働いたかを詳細に尋ねました。それから私はそれを見つけました。

どうやら、彼がデータを送信したとき、送信前にデータの配列をフラッシュしなかったため、本質的に、彼は古い値を上書きする新しい値で使用された最後のバッファを上書きしていましたが、上書きされていない古い値はまだ送信されています。

したがって、値がデータ配列の位置80にあり、要求された値のリストが80未満に変更されたが、その同じ値が新しいリストに含まれていた場合、両方の値は任意の特定のバッファーのデータバッファーに存在します与えられた時間。

データベースから読み取られる値は、UIが値を要求していたときのタイムスライスに依存していました。


修正は非常に簡単でした。データバッファ(実際にはパケットプロトコルの一部として含まれていた)に着信するアイテムの数を読み込み、そのアイテム数を超えてバッファを読み込まないでください。


学んだ教訓:

  • 現代のコンピューティングパワーを当然のことと考えないでください。コンピューターがイーサネットをサポートしておらず、アレイのフラッシュが高価であると見なされる場合がありました。あなたが本当にどこまで来たかを見たいなら、動的なメモリ割り当ての形を実質的に持たないシステムを想像してください。IE、エグゼクティブプロセスは、すべてのプログラムにすべてのメモリを順番に事前に割り当てる必要があり、プログラムはその境界を超えて成長することはできませんでした。IE、システム全体を再コンパイルせずにプログラムにより多くのメモリを割り当てると、大規模なクラッシュを引き起こす可能性があります。いつか人々は同じ光の中でゴミ収集前の日について話すのだろうか。

  • カスタムプロトコルを使用してネットワーキングを実行する場合(または一般的にバイナリデータ表現を処理する場合)、パイプを介して送信されるすべての値のすべての機能を理解するまで、必ず仕様を読んでください。つまり、目が痛くなるまで読んでください。個々のビットまたはバイトを操作してデータを処理する人は、非常に賢明で効率的な方法を使用します。最も細かい部分がないと、システムが壊れる可能性があります。

修正に要する全体の時間は2〜3日でしたが、そのことにほとんどの時間を費やして他のことに取り組みました。

SideNote:問題のホストコンピューターはデフォルトでイーサネットをサポートしていませんでした。それを駆動するカードはカスタムメイドで改造され、プロトコルスタックは事実上存在しませんでした。私が一緒に働いていた開発者はプログラマの地獄でした。彼はこのプロジェクトのシステムにUDPの完全なバージョンと最小限の偽のイーサネットスタック(プロセッサがフルイーサネットスタックを処理するほど強力ではなかった)を実装しただけではありませんでも彼は一週間もしないうちにそれをやった。彼はまた、そもそもOSを設計およびプログラムした元のプロジェクトチームリーダーの一人でした。彼がコンピューター/プログラミング/アーキテクチャーについて共有しなければならなかったことは、どれだけ長く巻き込まれていても、私がどれだけ新しくても、すべての言葉を聞いていました。


5

背景

  • ミッションクリティカルなWCFアプリケーションでWebサイトを駆動し、バックエンドトランザクション処理を提供します。
  • 大容量アプリケーション(1秒あたり数百の呼び出し)
  • 複数サーバーの複数インスタンス
  • 数百の合格した単体テストと無数のQA攻撃

不具合

  • 本番環境に移行すると、サーバーはランダムな時間正常に動作し、急速に低下し始め、ボックスCPUが100%になります。

見つけた方法

最初はこれが通常のパフォーマンスの問題であると確信していたので、手の込んだログを作成します。使用率についてデータベースの人々と話をするたびに、パフォーマンスをチェックし、サーバーの問題を監視しました。1週間

それから、スレッドの競合の問題があると確信しました。デバッグでシチュエーションを作成しようとするシチュエーション作成ツールを作成しようとするデッドロックをチェックしました。管理者のフラストレーションが高まる中、プロジェクトをゼロから再起動することから、サーバーを1つのスレッドに制限することまで、どのように提案するかを同僚に尋ねました。1.5週間

次に、Tess Ferrandezブログを見て 、ユーザーダンプファイルを作成し、次回サーバーがダンプを取得したときに、windebugでそれを分析しました。すべてのスレッドがdictionary.add関数でスタックしていることがわかりました。

xスレッドエラーを書き込むログを追跡しただけの短い辞書は、同期されていませんでした。


3

ハードウェアデバイスと通信するアプリケーションがありましたが、場合によっては、デバイスを再度接続して2回ソフトリセットするまで物理的にプラグを抜くと正しく動作しないことがありました。

問題は、起動時に実行されているアプリケーションが、まだマウントされていないファイルシステムから読み取ろうとしたときに、時々セグメンテーション違反を起こすことであることが判明しました(たとえば、ユーザーがNFSボリュームから読み取るように構成した場合)。起動時に、アプリケーションはいくつかのioctlをドライバーに送信してデバイスを初期化し、構成設定を読み取り、さらにioctlを送信してデバイスを正しい状態にします。

ドライバーのバグにより、初期化呼び出しが行われたときにデバイスに無効な値が書き込まれていましたが、呼び出しを行ってデバイスを特定の状態にすると、値は有効なデータで上書きされました。

デバイス自体にバッテリーがあり、マザーボードから電源が失われたかどうかを検出し、電源が失われたことを示すフラグを揮発性メモリに書き込み、次に電源を入れたときに特定の状態に入り、特定のフラグをクリアするために命令を送信する必要がありました。

問題は、デバイスを初期化するためにioctlが送信された(そしてデバイスに無効な値を書き込んだ)後、有効なデータが送信される前に電源が切断された場合です。デバイスの電源を再投入すると、フラグが設定されていることがわかり、初期化が不完全であるためにドライバーから送信された無効なデータを読み取ろうとしました。これにより、デバイスは電源オフフラグがクリアされた無効な状態になりますが、デバイスはドライバーによって再初期化されるまでそれ以上の命令を受け取りません。2回目のリセットは、デバイスが格納された無効なデータを読み取ろうとしておらず、正しい構成指示を受け取り、正しい状態にできるようにすることを意味します(ioctlを送信するアプリケーションがセグメンテーション違反ではないと仮定します) )。

最終的に、問題を引き起こしている正確な状況を把握するのに約2週間かかりました。


2

大学のプロジェクトでは、ファイルを共有する分散P2Pノードシステムを作成しました。これは、相互に検出するマルチキャスト、ノードの複数のリング、およびノー​​ドがクライアントに割り当てられるネームサーバーをサポートしていました。

C ++で書かれているので、これにはPOCOを使用しました。これは、素晴らしいIO、ソケット、およびスレッドプログラミングを可能にするからです。


私たちを悩ませ、多くの時間を失わせるバグが2つ発生しました。これは本当に論理的なものです。

ランダムに、コンピューターがリモートIPではなくローカルホストIPを共有していました。

これにより、クライアントは同じPC上のノードに接続するか、自分で接続するノードを作成しました。

これをどのように識別しましたか?ネームサーバーの出力を改善したときに、後でIPを決定するスクリプトが間違っていることをコンピューターを再起動したときに発見しました。ランダムに、eth0デバイスの代わりにloデバイスが最初にリストされました...本当に愚かです。これは、すべての大学のコンピューター間で共有されるため、eth0から要求するようにハードコーディングしました...


そして今、もっと面倒なもの:

ランダムに、パケットフローはランダムに一時停止します。
次のクライアントが接続すると、続行します...

これは本当にランダムに発生し、複数のコンピューターが関与しているため、この問題をデバッグするのが面倒になり、大学のコンピューターではそれらでWiresharkを実行することはできません側。

コードに多くの出力があるので、コマンドの送信がうまくいくと仮定しましたが、
これは本当の問題がどこにあるのか疑問に思っていました...着信ソケット。

これは、より少ないパケットを含むプロトタイプでより簡単なテストとして機能すると仮定してこの問題を引き起こさなかったため、ポーリングステートメントが機能していると仮定するだけでしたが...そうではありませんでした。:-(


学んだ教訓:

  • ネットワークデバイスの順序のような愚かな仮定をしないでください。

  • フレームワークは常にその仕事(実装またはドキュメント)を正しく行うとは限りません。

  • コードで十分な出力を提供します。許可されていない場合は、ファイルに拡張詳細を記録してください。

  • コードがユニットテストされていない場合(難しすぎるため)、動作することを想定しないでください。


1
Wireshark(または同様のツール)を使用せずにネットワークの問題に対処することは、iteslfの中でも勇敢です。
エヴァンプレイス

2

私は今でも最も難しいバグ探しをしています。それは時々そこにあり、時にはバグではありません。それが、私がここにいる理由で、翌日の午前6時10分です。

バックグラウンド:

  • コンテキスト:言語、アプリケーション、環境など
    • PHP OSコマース
  • バグはどのように識別されましたか?
    • ランダムな順序は、ランダムに失敗してリダイレクトの問題の一部として機能します
  • バグを特定したのは誰ですか?
    • クライアント、およびリダイレクトの問題は明らかでした
  • バグの再現はどれほど複雑でしたか?
    • 私は再現できませんでしたが、クライアントはできました。

狩り。

  • あなたの計画は何でしたか?
    • デバッグコードの追加、注文の記入、データの分析、繰り返し
  • どのような困難に遭遇しましたか?
    • 再現可能な問題と恐ろしいコードの欠如
  • 問題のコードはどのようにして最終的に見つかりましたか?
    • 多くの問題のあるコードが見つかりました。

殺害。

  • 修正はどの程度複雑でしたか?
    • 非常に
  • 修正の範囲をどのように決定しましたか?
    • スコープはありませんでした...それはどこにでもありました
  • どのくらいのコードが修正に含まれていましたか?
    • それのすべて?そのままファイルがあったとは思わない

死後。

  • 技術的に根本的な原因は何ですか?バッファオーバーランなど
    • 悪いコーディング慣行
  • 30,000フィートからの根本原因は何でしたか?
    • 私はむしろ言いたくない...
  • プロセスには最終的にどのくらい時間がかかりましたか?
    • いつまでも
  • 修正によって悪影響を受けた機能はありましたか?
    • 特徴?それともバグですか?
  • どのような方法、ツール、動機が特に役立ちましたか?...ひどく役に立たない?
  • もう一度やり直せたら?............
    • ctrl + a Del

理由が「悪いコーディングプラクティス」だった場合、チームのコーディングプラクティスを修正し、おそらくピアレビューを導入するのに適した時期かどうか、上司と話し合うことをお勧めします。

2

最後のセムセターで混乱を招く並行処理を修正しなければなりませんでしたが、私にとって最も際立っているバグは、宿題のためにPDP-11アセンブリで書いていたテキストベースのゲームにありました。これはConwayのGame of Lifeに基づいており、何らかの奇妙な理由で、グリッドの隣の情報の大部分が、そこにあるべきではない情報で常に上書きされていました。ロジックも非常に単純だったため、非常に混乱していました。それを何度も繰り返して、すべてのロジックが正しいことを再発見した後、私は突然問題が何であるかに気づきました。このこと:.

PDP-11では、数字の横にあるこの小さなドットは、8の代わりに10を基数にします。これは、グリッドに制限されるはずのループの境界をなす数字の隣にありました。 8。

このような小さな4ピクセルサイズの追加によって引き起こされたダメージの量が原因で、それは今でも際立っています。結論は何ですか?PDP-11アセンブリでコーディングしないでください。


2

メインフレームプログラムが理由もなく機能しなくなった

これを別の質問に投稿しました。こちらの投稿をご覧ください

メインフレームに新しいバージョンのコンパイラをインストールしたために発生しました。

アップデート06/11/13:(元の回答はOPによって削除されました)

このメインフレームアプリケーションを継承しました。ある日、澄んだ青から動き出しなくなりました。それだけです...パフは止まりました。

私の仕事は、できるだけ速く動作させることでした。ソースコードは2年間変更されていませんでしたが、突然停止しました。コードをコンパイルしようとすると、XX行目でエラーが発生しました。XX行目を見て、XX行目が壊れる原因がわからなかった。このアプリケーションの詳細な仕様を尋ねたところ、何もありませんでした。行XXは犯人ではありませんでした。

コードを印刷し、上から下へとレビューし始めました。私は何が起こっているかのフローチャートを作成し始めました。コードは非常に複雑であるため、それを理解することすらできませんでした。私はそれをフローチャート化しようとしてあきらめました。特にアプリケーションが何をしたのか詳細が分からなかったので、その変更が残りのプロセスにどのように影響するかを知らずに変更を加えることを恐れました。

そこで、ソースコードの先頭から始め、whitespceとラインブレーキを追加して、コードを読みやすくすることにしました。場合によっては、ANDとORを組み合わせた条件があり、どのデータがAND演算されているか、どのデータがOR演算されているかを明確に区別できない場合がありました。そこで、AND条件とOR条件を読みやすくするために括弧を付け始めました。

ゆっくりと掃除しながら、定期的に作業を保存しました。ある時点でコードをコンパイルしようとすると、奇妙なことが起こりました。エラーはジャンプして元のコード行を通過し、さらに下になりました。そこで、AND条件とOR条件を括弧で分けて続けました。私はそれをきれいにし終わったとき、それは働きました。図に行く。

次に、オペレーションショップを訪問して、メインフレームに新しいコンポーネントを最近インストールしたかどうかを尋ねることにしました。はい、コンパイラを最近アップグレードしました。うーん。

古いコンパイラは、式に関係なく式を左から右に評価したことがわかりました。コンパイラの新しいバージョンは、式を左から右に評価しましたが、ANDとORの不明確な組み合わせを解決できないという意味のあいまいなコードです。

私がこれから学んだ教訓...常に、常に、それらを互いに組み合わせて使用​​するとき、分離されたAND条件とOR条件に括弧を使用します。


リンクが指す投稿が削除されました-回答を更新してもよろしいですか?
グナット

1
@gnat-archive.orgで見つけました:)
マイケルライリー-

1

バックグラウンド:

  • コンテキスト:顧客が自分でチェックインできるWebサーバー(C ++)
  • バグ:ページをリクエストすると、単に応答せず、ファーム全体が停止し、ページを処理するのに時間がかかりすぎた(数秒しか許可されない)ため、プロセスが強制終了(および再起動)されました。
  • 一部のユーザーは文句を言いましたが、それは非常に散発的であるためほとんど気づかれていませんでした(ページが提供されない場合、人々は「更新」を押す傾向があります)。しかし、コアダンプに気付きました;)
  • 実際にローカル環境で再現することはできませんでした。バグはテストシステムで数回現れましたが、パフォーマンステスト中に現れませんでした。

狩り。

  • 計画:まあ、メモリダンプとログがあるので、それらを分析したかったのです。ファーム全体に影響を与えており、過去にいくつかのデータベースの問題があったため、データベース(複数のサーバーの単一DB)が疑われました。
  • 難しさ:完全なサーバーダンプは非常に大きいため、非常に頻繁に(スペースが不足しないように)クリアされるため、発生したときにすぐに取得する必要がありました。ダンプはさまざまなスタックを表示し(そのためDBのものはほとんどありません)、ページ自体の準備中に(以前の計算ではなく)失敗し、ログが示す内容を確認しました。ページの準備には時間がかかり、ただし、事前に計算されたデータを備えた基本的なテンプレートエンジンにすぎません(従来のMVC)
  • アクセス方法:いくつかのサンプルといくつかの検討の後、HDD(ページテンプレート)からデータを読み取るのに時間がかかっていることに気付きました。それはファーム全体に関するだったので、私たちは最初にスケジュールされたジョブ(crontabファイル、バッチ)を探したが、タイミングが別の発生から一致することはありません...最終的に、これは常に数日起こったことを私に起こった前に新しいバージョンの活性化ソフトウェアのと私はAhAhを持っていました瞬間...それはソフトウェアの配布が原因でした!数百メガバイト(圧縮)を配信すると、ディスクのパフォーマンスに多少の影響が出ます。もちろん、配信は自動化され、アーカイブはすべてのサーバーに一度にプッシュされます(マルチキャスト)。

殺害。

  • 複雑さの修正:コンパイル済みテンプレートへの切り替え
  • 影響を受けるコード:なし、ビルドプロセスの簡単な変更

死後。

  • 根本原因:運用上の問題または将来の計画の欠如:)
  • タイムスケール:追跡に数か月、修正とテストに数日、QAとパフォーマンスのテストと展開に数週間かかりました-修正を展開するとバグが発生することがわかっていたので、急いでいません...そうでなければ...ちょっと変態です!
  • 副作用:配信されたコードでテンプレートが焼き付けられたため、実行時にテンプレートを切り替えることはできません。ただし、一般的にテンプレートを切り替えると、より多くのデータを入力できるため、この機能はあまり使用しません。ほとんどの場合、「小さな」レイアウト変更に十分です。
  • メソッド、ツール:gdb+監視!時間をかけてディスクを疑い、監視グラフでアクティビティのスパイクの原因を特定しました...
  • 次回:すべてのIOを悪影響として扱う!

1

最も困難なものは、工場が稼働している完全な実稼働環境以外では再現できないため、決して殺されませんでした。

私が殺したクレイジーなもの:

図面は意味不明です!

コードを見ると、何も見えません。プリンターキューからジョブを取り出して調べたところ、問題はありませんでした。(これはdos時代、PCG5にHPGl / 2が埋め込まれたものでした。実際、図面のプロットに非常に適していて、限られたメモリでラスターイメージを作成するという頭痛の種はありません。) 。

コードをロールバックしてください。問題はまだあります。

最後に、簡単なファイルを手動で作成し、それをプリンターに送信します。プリンタ自体ではなく、私のバグではないことが判明しました。メンテナンス会社は、他の何かを修正していて、その最新バージョンにバグがあったときに、それを最新バージョンにフラッシュしていました。重要な機能を削除し、それを以前のバージョンにフラッシュバックしなければならなかったことを理解させることは、バグ自体を見つけるよりも困難でした。

さらに厄介なものですが、それは私の箱にしかなかったので、私は最初の場所に入れませんでした:

Borland Pascal、サポートされていないAPIを処理するDPMIコード。それを実行し、時には動作し、通常は無効なポインタを処理しようとして急上昇しました。ただし、ポインタを踏むことから期待するように、間違った結果を生成することはありませんでした。

デバッグ-コードを1ステップ実行した場合、常に正しく動作します。それ以外の場合は、以前と同様に不安定でした。検査では常に正しい値が示されました。

犯人:2つありました。

1)Borlandのライブラリコードには大きなバグがありました。保護モードでは、リアルモードのポインターがポインター変数に格納されていました。問題は、ほとんどのリアルモードポインターが保護モードで無効なセグメントアドレスを持ち、ポインターをコピーしようとすると、それがレジスタペアにロードされてから保存されることです。

2)デバッガーは、シングルステップモードでのこのような無効なロードについては何も言いません。内部で何をしたのかわかりませんが、ユーザーに提示された内容は完全に正しいように見えました。実際に命令を実行しているのではなく、代わりにシミュレートしているのではないかと思います。


1

これは非常に単純なバグであり、どういうわけか私は悪夢に変わりました。

背景:独自のオペレーティングシステムの作成に取り組んでいました。デバッグは非常に困難です(トレースステートメントがあれば十分ですが、場合によってはそれさえできません)

バグ:ユーザーモードで2つのスレッドを切り替える代わりに、一般保護違反が発生します。

バグ狩り:この問題を修正するために、おそらく1週間か2週間を費やしました。どこにでもトレースステートメントを挿入します。生成されたアセンブリコードの検査(GCCから)。すべての値を印刷しました。

問題:hltバグハントの早い段階で、crt0に命令を配置していました。crt0は基本的に、オペレーティングシステムで使用するユーザープログラムをブートストラップするものです。このhlt命令は、ユーザーモードから実行されたときにGPFを引き起こします。私はそこに置いて、基本的にそれを忘れました。(元々、問題はバッファオーバーフローまたはメモリ割り当てエラーのようなものでした)

修正:hlt命令を削除します:) 削除後、すべてがスムーズに機能しました。

私が学んだこと:問題をデバッグしようとするとき、あなたが試みた修正を忘れないでください。最新の安定したソース管理バージョンに対して定期的に差分を行い、他に何も機能しないときに最近変更した内容を確認します

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