デバッグについて詳しく知りたいのですが?[閉まっている]


8

プログラムをデバッグするには、主に次の方法を使用します。

  1. printf(または他の言語では同等のもの)を使用して、特定のステートメントの後の変数の値をチェックするか、プログラムが条件付きステートメントまたはループに入ったかどうかをチェックします。

  2. IDEを使用する場合は、ウォッチ/ブレークポイントを使用します。

上記の方法で問題を解決することができます。しかし、ビルドにはデバッグとリリースの2種類があることに気づきました。名前から、デバッグビルドがデバッグに役立つことを理解できます。また、デバッグビルドはシンボルテーブル情報などと呼ばれるものを保存することも読んだ。

デバッグビルドをデバッグに使用するにはどうすればよいですか?他に学ぶべきデバッグ技術はありますか?


違いはプラットフォームによって異なります。Javaの場合、違いはわずかです。Cにとっては大幅differen.tすることができ

2
どの言語/プラットフォームでコーディングしていますか?
DXM

回答:


7

デバッガーを学ぶ

テキストベース、完全なIDE、またはそれらのいくつかのブレンドであるかどうかにかかわらず、デバッガーを理解することは本当に役立ちます。詳細はあまり説明しないので、一般的なケースについて説明します。

1)ブレークポイント

コードの行で停止するだけでなく、多くのデバッガーでは、コードを何度か通過した後、または一部のメモリが値を変更したときに、条件が発生したとき(「x> 5」など)にブレークするように指定できます。これは、コードがどのように悪い状態になるかを理解するのに非常に役立ちます。たとえば、ポインターが逆参照されたときにクラッシュをキャッチするのではなく、ポインターがnullになるのを監視します。

2)コードのステップ実行

関数にステップインし、コードに沿って行ごとに、行をジャンプして(「次のステートメントを設定」)、関数から「上に」移動できます。これは、コードの実行を追跡して、思ったとおりに動作することを確認する非常に強力な方法です:-)

3)式の評価

したがって、変数をウォッチリスト/ウィンドウに配置して、ブレークポイントに到達したときやコードをステップ実行したときに値の変化を確認できますが、通常は複雑な式の評価も行うことができます。たとえば、「x + y / 5」が評価されます。一部のデバッガーでは、関数呼び出しを監視リストに入れることもできます。「time()」、「MyFunction(...)」、「time()」などを実行して、関数にかかった時間のクロックタイミングを取得できます。

4)例外とシグナル処理

したがって、言語が例外やシグナルをサポートしている場合は、通常、これに対応する方法についてデバッガを構成できます。一部のデバッガーでは、例外のキャッチに失敗した後ではなく、例外が発生しようとしている時点でコードに侵入できます。これは、プログラムが別のユーザーアカウントで実行されているため、「ファイルが見つかりません」エラーなどの奇妙な問題を追跡するのに役立ちます。

5)プロセス/コアへのアタッチ

そのため、時々、デバッガーを使用して、問題のある既存のプロセスにジャンプする必要があります。近くにソースコードがあり、デバッグシンボルが損なわれていない場合は、最初からデバッガーで開始したかのように飛び込むことができます。これはコアダンプの場合も同様ですが、通常はそれらのデバッグを続行できません(プロセスはすでに終了しています)。

ビルド構成

デバッグシンボル、最適化、その他のコンパイラフラグなどの機能をオンまたはオフにすることで、さまざまなビルドバリエーションがあります。

1)デバッグ

従来、これは特別な特性のない単純なビルドであり、デバッグが容易で予測可能です。これはプラットフォームによって多少異なりますが、信頼性を確保するために、割り当てやバッファサイズなどの追加の余裕がある場合があります。通常、DEBUGやConditional( "Debug")などのコンパイラシンボルが存在するため、デバッグ固有のコードが取り込まれます。これは、特に信頼性や再現性が高い場合に、機能レベルのシンボルがそのままの状態で出荷されるビルドであることがよくあります。懸念。

2)リリース/最適化ビルド

コンパイラーの最適化を有効にすると、コンパイラーのいくつかの低レベルコード生成機能が有効になり、コードに関する仮定に基づいて、コードを高速化または小型化できます。アルゴリズムの選択が適切でない場合、可能な速度の増加は関係ありませんが、集中的な計算の場合、これにより、共通部分式の除去やループのアンロールなどによって十分な違いが生じる可能性があります。ノッチを下げる必要があります。最適化されたコードのコンパイラーのバグも、以前は問題でした。

3)インストルメント済み/プロファイル済みビルド

コードは、関数が呼び出された回数とその関数で費やされた時間を測定する特定のインストルメンテーションコードで構築されています。このコンパイラー生成コードは、分析のプロセスの最後に書き出されます。専用のソフトウェアツールを使用する方が簡単な場合があります。以下を参照してください。このタイプのビルドは出荷されません。

4)安全/チェック済みビルド

すべての「安全弁」は、プリプロセッサシンボルまたはコンパイラ設定を介して有効になります。たとえば、ASSERTマクロは関数のパラメーターをチェックし、イテレーターは変更されていないコレクションをチェックし、カナリアはスタックに入れられて破損を検出し、ヒープ割り当てはセンチネル値(0xdeadbeefは記憶に残る値)で埋められてヒープの破損を検出します。サイトでしか再現できない永続的な問題があるお客様にとって、これは便利なことです。

5)機能の構築

ソフトウェア製品の要件が異なる複数の顧客がいる場合、一般的には、顧客ごとにビルドを行い、テスト時に異なる部分を実行します。たとえば、ある顧客はオフライン機能を望んでおり、別の顧客はオンラインのみを望んでいます。コードのビルド方法が異なる場合は、両方の方法をテストすることが重要です。

ロギングとトレース

したがって、printf()に役立つステートメントをいくつか書き込んだ後、包括的な構造化トレース情報をデータファイルに書き込んでいきます。その後、この情報をマイニングして、ソフトウェアの実行時の動作/特性を理解できます。コードがクラッシュしない場合、または再現に時間がかかる場合は、たとえば、スレッドのリスト、それらの状態遷移、メモリ割り当て、プールサイズ、空きメモリ、ファイルハンドルの数などの画像を用意すると便利です。実際には、アプリケーションのサイズ、複雑さ、およびパフォーマンスの要件に依存しますが、ゲーム開発者は、フレームレートに影響を与える可能性があるため、ゲームの進行中にCPUまたはメモリの使用に「スパイク」がないことを確認する必要があります。この情報の一部はシステムによって維持され、一部はライブラリによって維持され、残りはコードによって維持されます。

その他のツール

これらのシナリオをカバーするために別のビルドを作成する必要があるとは限りません。プロセス構成(Windowsレジストリトリック)によって実行時にいくつかの側面を選択できるため、標準ライブラリよりも優先度の高い代替ライブラリを利用できます。ローダーパスを使用するか、ソフトウェアICEまたは専用デバッガーを使用して、ランタイム特性(Intel v-Tuneなど)についてソフトウェアをプローブします。これらの一部は高額な費用がかかり、一部は無料です-dtrace、Xcodeツール。


6

デバッグビルドは、実行可能ファイルのデバッグシンボルを使用してビルドされます。基本的にそれが意味することは、ソースコードのすべての行がそれから生成されたマシンコードにリンクされ、変数と関数のすべての名前が保持されるということです。GCCでは、-gソースファイルをコンパイルするときにフラグを使用してこれを行います。よく行われるもう1つのことは、最適化をオフにすることです。これは、コンパイラーがプログラムを高速化するための巧妙なトリックを実行しますが、デバッグを不可能にするためです。GCCでは、これはで行われ-O0ます。

私がこれまでデバッグに使用した中で最も便利なツールはgdbです。これは、あなたが言及したIDEブレークポイントのテキストバージョンです。IDEを使用する場合よりも、gdbを使用するほうがはるかに多くのことができます。実際、一部のIDEは単なるgdbのラッパーですが、一部の機能は失われています。メモリの場所の監視、アセンブリの印刷、スタックフレームの印刷、信号処理の変更など、さまざまなことができます。デバッグに真剣であれば、gdbまたは同等のテキストベースのデバッグプログラムを学びます。

私がよく使うもう1つのことは、valgrindです。メモリリークを検出し、メモリが初期化されているかどうかを追跡します。デバッグビルドで使用すると、興味深いことが発生する行番号を取得できます。


1
本当に?GDBを使用しなければならないたびに、優れたIDEベースのデバッガーを使用して、手元にあるパワーと使いやすさから大幅に後退しています。特に、GDBは非常に一般的なツールですが、統合デバッガーは、より具体的で関連性の高いフィードバックを提供するために設計された言語のルールを知ることができます。
メイソンウィーラー、

1
GDBはCおよびC ++用に設計されています。私はかなり長い間IDEデバッガーを使っていましたが、彼らは私に終わりのない欲求不満を感じました。GDBを使用すると、自分が何をしているのかをはるかに制御できるようになります。
ジャリード

2
@MasonWheeler gdbはスクリプト可能ですが、IDEは通常はできません。お茶を飲んでいる間、コンピューターがすべての仕事をしてくれるので、指先で「力」を必要としません。
SK-logic

3
@MasonWheeler、いや、狂ったように「step-inspect-step-inspect」を打つとき、あなたは何か間違ったことをしています。問題の原因に関する現在の仮説を検証する小さなスクリプトを作成し、それを実行して(機能テストを実行しているのとほぼ同じ方法で)結果を分析し、必要に応じて繰り返すのははるかに簡単です。インタラクティブなデバッグのすべての形式は、たとえコアダンプの事後であっても、ほとんどの場合ほとんど役に立ちません。典型的なデバッグスクリプトでは、いくつかのブレークポイントを設定し、ブレークポイントの値を実行、検査、および適切にフォーマットします。
SK-logic

1
@Masonウィーラー、簡単なスタックトレース(btgdb、ほとんどの場合、0-仮説のための十分な情報よりもを提供します)(そうでない場合、あなたのコードは、これまで、サブ標準の品質閾値を下回る、よく、です) 。私はVMS以来デバッガーを使用しており、デバッガーを巻き戻す時間のようないくつかの極端なエキゾチックな獣を含むすべての可能なフレーバーを試しましたが、これまでのところ、本当に有用なものを見つけることができませんでした。前提条件をインタラクティブにテストしているので、時間を無駄にしています。私は自分の仮定をバッチで(並行して多くなる可能性があります)、迅速かつほとんど労力なしでテストしています。
SK-logic

3

非常に強力なデバッグ手法が1つありますが、他の回答ではまだ触れていません。一部の開発環境では無料で利用できます。または、ほとんど何も追加せずに追加できます。

これは埋め込みREPLであり、ソケットを介していつでも接続できるようにし、専用スレッドで実行し、実行中のコードに対して考えられるすべてのリフレクションを使用し、実行中のコードを変更または完全に置き換え、新しいものを追加し、実行します。機能など

たとえば、Common Lisp、Smalltalk、Python、Rubyなどでコーディングする場合は、そのまま使用できます。軽量のインタープリター(Lua、Guile、JavaScript、Pythonなど)をネイティブアプリケーションに埋め込むことができます。 。JVMまたは.NETベースの環境では、多くの組み込み可能なコンパイラーとインタープリターが利用可能で、非常に強力なリフレクションが無料で利用できます。

このアプローチは、対話型/反復型デバッガー(gdb、ビジュアルスタジオデバッガーなど)よりもはるかに効率的です。最良の結果を得るには、適切なログ機能と適切に配置されたアサーションと組み合わせて使用​​する必要があります。


ただし、ネットワークにアクセスするハッカーとは併用されません。リリースビルドでこれを無効にしてください:)
gbjbaanb

@gbjbaanb、それがSSLの使用目的です。Viawebストーリーを読んでください-彼らはそのようなアプローチから大きな利益を得ており、実行中の本番システムをデバッグする能力を持っています。
SK-logic

2

私が今まで使用した中で最も重要なデバッグツールは、死後のクラッシュダンプ(Linuxではコアダンプ、Windowsではユーザーdmp)です。

それは本当に複雑なトピックなので、ここにリンクがあります:

基本的に(Windowsでは、死後のデバッグで最も経験のあるプラットフォーム)、別のファイル(.pdbファイル)に保存されるシンボルを使用してアプリを構築します。これらを安全に保ち(誰もコードを簡単にリバースエンジニアリングできないため)、クラッシュを待ちます。実行すると(クラッシュをキャプチャしてダンプファイルを生成するためにDrWatsonまたは同様のツールを実行している場合)、. dmpファイルをシンボル(およびオプションでソースコードへのパス)とともにWinDbg(Windowsデバッガ)にロードし、コールスタック、レジスタ、変数、メモリ値などの多くの情報が表示されます。動作するときは美しいです。

デバッグビルドでは、これらすべてが自動的に実行されるように設定されています。リリースをビルドするときは、シンボルを有効にする必要があります。デバッグビルドは、メモリガードのような他のものも追加します(例外をトリガーすると、それらに書き込みを試みます。これにより、バッファオーバーフローまたはメモリ破損が実際に簡単に示されます。または、正しくないものに対してアサートされます)。一般に、デバッグビルドは、開発者がコードを渡して実行する前に実行してテストするためのものです。

これはネイティブコードのデバッグ用です。.NETコードは、このような事後処理用のPiTAですが、sosロードすることで.NET例外用のものを取得できる場合があります。

すべて複雑なものですが、それほど悪くはありません。とがったクリック感のよいGUIを期待しないでください。これは、コマンドラインのパワーの使いやすさです。


2

printf(または他の言語では同等のもの)を使用して、特定のステートメントの後の変数の値をチェックするか、プログラムが条件付きステートメントまたはループに入ったかどうかをチェックします。

また、提供されているあらゆる種類のassertステートメントを使用する必要があります。

単体テストも作成する必要があります。

上記の方法で問題を解決することができます。

完璧です。あなたはあなたが知る必要があるすべてを知っています。

他に学ぶべきデバッグ技術はありますか?

いいえ、学ぶべきではありません。あなたがそれが役立つと思うなら、あなたはもっと学ぶかもしれません。しかし、あなたが持っているもの以上のものは必要ありません。

過去30年以上の間、私はデバッガを数回(おそらく3または4)使用しました。そして、私はそれを使って、死後のクラッシュダンプを読み取り、失敗した関数呼び出しを見つけました。

デバッガの使用は必須のスキルではありません。印刷ステートメントで十分です。


0

ここにテクニックの簡単なリストがあります:

  • 静的呼び出しグラフ
  • 動的呼び出しグラフ
  • ホットスポットプロファイリング
  • スレッドメッセージングの監視
  • デバッグの実行/逆実行
  • ウォッチポイント
  • トレースポイント

AOPスタイルのツールを使用してカスタム動作を実装したり、優れた静的分析ツールを使用して長い道のりを進んだりすることもできます。


0

デバッグツールに関するすべてを学びます。

彼らはしばしば何が起こっているのかをよりよく理解するのに役立つかもしれない本当に強力な機能を隠しています。(特にVisual StudioのC ++デバッガー)

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