手動デバッグを効果的に行う方法は?[閉まっている]


8

利用できるデバッガがないとしたら、(期待どおりに)機能しないコードをデバッグするための効果的なアプローチは何でしょうか?


20
「最も効果的なデバッグツールは、慎重に考えられ、慎重に配置された印刷ステートメントと相まって」

3
完全に同意する。最も強力なデバッグツールは、まだprintステートメントです。別のアドバイスは、コメントではなく実際のコードをデバッグすることです。
romeroqj

4
@jromero:printステートメントが「最も強力」だとは言えません。最も広く普及しており、使いやすいことは確かです。
whatsisname

1
カーニガンの引用は、私がコメントに反対票を投じることができることを望んでいます 印刷ステートメントのデバッグは、最後の手段です。
メイソンウィーラー、

2
@メイソン:質問はデバッガーが利用できないことを想定しているため(実行をトレースする「実際の」方法はなくなった)、実行をトレースするために他に何を使用しますか?

回答:


7

いくつかのテクニックがあります:

  1. ロギング。ファイルにアクセスできない場合は、シリアルコンソールまたは使用可能な出力デバイスにログオンします。常にロギングを念頭に置いてコードを記述し、「なし」から「過大」までのさまざまなロギングレベルの条件付きコンパイルを許可することをお勧めします。

  2. それを削減します。バグの疑いのあるポイントの周囲のコードの部分を1つずつ除外し、バグがなくなったときに行ったことを分析します。

  3. アサーションまたは契約。コードを最初からアサートで埋めることは良い考えです。デバッグに役立つだけでなく、コードの追加ドキュメントとしても機能します。

  4. 2.に似ています。バグが消えたり動作が変わったりしない限り、入力を変更してコードを再シャッフルします。ポインター関連のバグはその振る舞いが非常に不安定になる傾向があるため、(CまたはC ++でコーディングしている場合は)さまざまな最適化レベルを試してみるとよい場合がよくあります。

  5. 3.の自動化バージョン-インストルメント済みコードを使用します。たとえば、valgrindでバイナリを実行します。

そしてもちろん、実行環境やコードの性質に応じて、さらに多くのツールやトリックがあります。


1
-1:同意できない4)。バグの解決策として提案しているように見えますか?もしそうならそれは悪であり、あなたは問題を抱えています。それが行うすべては、症状を解消することです
ところ...-mattnz '27

@mattnz、説明してくれませんか?私はこのアプローチを対話型デバッガの代替として提案します。これにより、実際の原因ではなく、症状のみが強調表示されます。4)は問題を特定する方法であり、解決策ではありません。実際、カバレッジが優れているため、ほとんどの場合、私のアプローチはデバッグよりもはるかに優れています。それで、あなたが私が提案することを理解していなかった可能性があります。もう一度読んでみてください。
SKロジック

開発者がステップ4で提示されたアクションを使用して、「私はそれを起こすことができましたが、今はできません、修正しました-出荷する」を使用して欠陥を「修正」するのを見てきました。あなたの投稿は、この方法が有効なバグ修正を提供することを示唆しています。
mattnz 2012

@mattnz、いや、それはこのようなものを示唆していません。バグを修正するのではなく、調査する方法を説明しています。デバッガーはバグを修正せず、問題はデバッガーの代替についてでした。
SKロジック

18

問題のあるコードを1つずつ確認しながら、同僚に連絡して問題の詳細を説明します。

多くの場合、説明するという行為は、あなたの同僚またはあなた自身のいずれかに明らかにします。


12

4
@PéterTörök:わからない...テディベアは冷たい死んだボタンの目でじっと見つめる傾向があります...言うことをすべて無視して、価値のない、小さくて取るに足らないものに感じさせます...テディベアでデバッグを行います。難しい。
FrustratedWithFormsDesigner

1
@FrustratedWithFormsDesigner、私が追加したリンクをご覧ください。
ペーテルTörök

2
@ピーター、私は同僚がデバッグのためにつかむ準備ができているテディベアでいっぱいの棚を持っていると、顧客に間違った印象を与えるかもしれないと恐れています。

1
@FrustratedWithFormsDesigner:そして、それは多くの同僚とどう違うのですか
mattnz

3

プログラムの出力を管理するロギングシステムはありますか?少なくとも、印刷するコンソールまたは書き込み可能なファイルはありますか?コンソールまたはログファイルを使用すると、デバッガーなしでデバッグできます。出力がどうあるべきかがわかるようなプログラムを入力し、出力が機能することを確認し、ログにプロセスの詳細がたくさんあることを確認します。次に、間違った出力を与える入力で試してください。うまくいけば、ログで問題の詳細なトレースが得られます。


どんなものでも使用できますが、gdbの種類のデバッガやIDEで見つけたものを使用できます
Anto

@Anto:宿題の行のように聞こえますが、その場合、ファイルまたはコンソールへのロギングは 「gdbまたはIDEで見つけたもの」などのデバッガーを使用していません。gdbおよびその他のデバッガーを使用すると、プログラムを実行しながらコードを1行ずつ実行し、変数の値を検査できます。ログによるデバッグとは、プログラムの実行が完了した後、その実行のトレースを(ファイルまたはコンソールで)使用し、何が起こったかを把握する必要があることを意味します。
FrustratedWithFormsDesigner

したがって、あなたがお勧めしたものは許可されています。いいえ、これは宿題ではありません。私は中学生で、プログラミングやcsはまったくありません。
Anto

2
@Anto:わかりました。この方法の唯一の欠点は、同期の問題があるプログラムをデバッグしようとしている場合です。たとえば、それがIPCの問題である場合、印刷/ログステートメントはプロセスが互いに話す速度に影響を与える可能性があり、ロギングをオンまたはオフにすると、問題が再現されるかどうかに影響を与える可能性があります(この場合、実際には@ ThorbjørnRavn Andersenのアドバイス)。場合によっては、ロギングによってパフォーマンスが大幅に低下する可能性がありますが、通常、非常に大量のデータを処理するときに大規模なシステムで大量のロギングが行われる場合のみですが、注意が必要です。
FrustratedWithFormsDesigner

3

依存します。以前は機能しましたか?かつて機能していたコードが突然機能しなくなった場合は、最新の変更を慎重に調べる必要があります。


2
このアプローチは控えめに言うべきではありません。改訂履歴は、以前に機能していたコードのエラーを特定するための優れた方法です(新しい機能を追加する場合でも)。
edA-qa mort-ora-y 2011

2
「git bisect」はこのタスクをいくらか自動化すると聞いています。私はまだそれを試していません。
クレイトンスタンレー

2

1)バグを100%再現可能にするため、またはできる限り100%に近づけるために必要なことをすべて実行します。

2)printf()またはその他のロギング機能を使用して、問題を追跡します。ただし、これは芸術であり、バグの性質によって異なります。

バグの場所がまったくわからないが、たとえば、ある時点で条件が偽になる(プログラムの状態が壊れた-isBroken()と呼ぶ)ことがわかっている場合は、ドリルダウン/パーティション検索を実行できます問題の場所を把握する。たとえば、主要なメソッドの最初と最後にisBroken()の値を記録します。

void doSomething (void)
{
    printf("START doSomething() : %d\n", isBroken());
    doFoo();
    doBar();
    printf("END doSomething() : %d\n", isBroken());
}

ログに表示される場合

START doSomething() : 0
END doSomething() : 1

何か問題があったことを知っています。したがって、他のすべてのロギングコードを削除して、この新しいバージョンを試してください。

void doSomething (void)
{
    printf("START doSomething() : %d\n", isBroken());
    doFoo();
            printf("AFTER doFoo() : %d\n", isBroken());
    doBar();
    printf("END doSomething() : %d\n", isBroken());
}

ログでこれを見るかもしれません

START doSomething() : 0
AFTER doFoo() : 0
END doSomething() : 1

これで、doBar()がバグをトリガーすることがわかったので、doBar()で上記の手順を繰り返すことができます。理想的には、エラーを1行に絞り込みます。

もちろん、これは根本的な原因ではなく、バグの症状を明らかにするのに役立ちますたとえば、NULLであってはならないNULLポインタを見つけましたが、なぜですか?その後、再度ログに記録できますが、別の「壊れた」状態をチェックしています。

壊れた状態ではなくクラッシュした場合、それはほぼ同じです-ログの最後の行は、どこが壊れているかについてのヒントを提供します。


2

他の答えが失敗した後、常にバイナリ検索デバッグがあります:

  1. 考えられる原因(コードの行、リビジョン、入力など)の特定の部分(できれば半分)を排除します。
  2. 問題を再現してみてください。
  3. 問題が解決しない場合は、手順1に戻ります。
  4. 原因が1つしかない場合(コード行、リビジョン、入力の一部など)は、ばんざーい!プロシージャを終了します。
  5. それ以外の場合:手順1に戻り、残りの半分を削除します。
  6. 手順2に戻ります。

注:この手法は、問題を確実に再現できる場合にのみ機能します。


1.考えられる原因の半分を排除します。問題は消えます。2.その半分を復元し、もう一方を削除します。問題は消えます。3.考えられる原因をいくつか取り除きます。それらの任意の20%を除去すると、問題は消えます。4.パフォーマンス、基礎となるエンジン、サークルでの実行の調査を開始します。5.パニック。
SF。

大きくてフレンドリーな手紙。
Jeroen、2013年

0

「「最も効果的なデバッグツールは、慎重に検討されたものであり、慎重に配置された印刷ステートメントと相まって」-ブライアンカーニハン '今日は本当だったかもしれません!最も効果的な方法は、単体テストを確認することですが、おそらくあなたは何も持っていません。


特定のプロジェクトやコードがないので、ユニットテストはありません。私はデバッグ方法を求めているだけです
Anto

なぜ地球上でこの回答に反対票を投じるのでしょうか?

反対票を投じたのは私ではなかったので、誰かに涙を流しました
Anto

2
単体テストはデバッグに取って代わるものではなく、単にバグを区分して制約するのに役立ちます。これにより、コード化された単体テスト内でバグが発生した場合に、デバッグが簡単になります。IME、トリッキーなバグのほとんどはコンポーネントの相互作用(ユニットテストが難しい)にあり、回帰スタイルのバッシャーテストスイートでより頻繁に発見されます。
クレイトンスタンレー

-1)壊れた単体テストで識別されたコードをどのように修正しますか-デバッグします......単体テストはバグを検出し、デバッガーとデバッグを使用して欠陥を修正します。
mattnz 2012

0

それはバグに依存します。

バグが「コードがAを実行する理由」のようなものである場合は、「コードを実行するA」の場所を取り巻くコードの理解をテストすることは有用です。新しいバグを生成すると予想される新しいコードを導入します(このコードはBを実行する必要があり、これはCを実行する必要があります)。通常、予期しない動作を生成する新しいコードをすばやく見つけます。次に、私の心がコードの動作の更新されたメンタルモデルを構築して、最後の変更が意味をなすようになるまで辛抱強く待ちます。その後、そのメンタルモデルの変更により、通常、コードがAを実行する理由が説明されます。

2番目のケースについては、ここで詳しく説明しています。コードを継承した場合、コードがどのように機能するかについてのしっかりとしたメンタルモデルがないか、バグの特定の場所などについてよく考えていない。この場合、ドリルダウン/除算および分割印刷ステートメントを含む征服メソッドは機能します。ソース管理下にある場合は、最新のコード変更を確認してください。


0

「最も効果的なデバッグツールは、慎重に検討され、慎重に配置された印刷ステートメントと相まって」に拡張されます。

まず、バグが発生した瞬間を絞り込みます。ユーザーが観察できる症状をシステムで観察できるようにします。(たとえば、一部の文字列が意味不明に変化し、スクリプトの内容をポーリングし、変更時にデバッグをトリガーするループを追加します。)もちろん、エラーがクラッシュの場合は、segfault処理を追加します。

次に、問題がマルチスレッド環境にある場合は、スレッドを絞り込みます。各スレッドに識別子を付け、バグが発生したときにそれをダンプします。

スレッドを取得したら、printfsを使用して、指定したスレッドのコードを大量に散布し、スレッドが表示されるポイントを見つけます。

次に、それを作成する実際のアクションが発生する場所にバックトレースします(破壊的なアクションは、多くの場合、破損したデータが問題を引き起こす場所のかなり前になります)。破損した値が書き込まれる場所。

問題の原因となったポイントを特定したら、修正する前に、正しい動作を2度考えます。

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