プログラムをデバッグする理由としては、次が考えられます。
クラッシュする場所と理由をつきとめるため、クラッシュの原因をつきとめる方法としては、次があります。
プログラムを dbx で 実行します。dbx はクラッシュの発生場所をレポートします。
コアファイルを調べ、スタックトレースを確認します。コアファイルをチェックするおよび 呼び出しスタックを確認するを参照してください。
プログラムが正しくない結果を返している原因を特定するため。方法としては、次のものがあります。
プログラムの状態をチェックし、変数の値を確認できるように、実行を停止するためのブレークポイントを設定します。ブレークポイントの設定および 変数の調査を参照してください。
プログラムの状態がどのように変化するかをモニターするために、コードを 1 ソース行ずつステップ実行します。プログラムをステップ実行するを参照してください。
メモリーリークやメモリー管理問題を見つける方法としては、次があります。実行時検査 (RTC) を使用すると、メモリーアクセスエラーやメモリーリークエラーなどの実行時エラーを検出したり、メモリー使用をモニターしたりできます。メモリーアクセス問題とメモリーリークを検出するを参照してください。
プログラムがクラッシュしている場所を特定するには、クラッシュ時のプログラムのメモリーイメージであるコアファイルの調査が必要になることがあります。where コマンドを使用すると、コアダンプ時にプログラムが実行されていた場所を特定できます。where コマンドを参照してください。
$ dbx program-name core
または
$ dbx - core
次の例では、プログラムがセグメント例外でクラッシュし、コアダンプが作成されています。まず、dbx が起動され、コアファイルがロードされます。次に、where コマンドによってスタックトレースが表示されます。これにより、ファイル foo.c の 9 行目でクラッシュが発生したことが示されます。
% dbx a.out core Reading a.out core file header read successfully Reading ld.so.1 Reading libc.so.1 Reading libdl.so.1 Reading libc_psr.so.1 program terminated by signal SEGV (no mapping at the fault address) Current function is main 9 printf("string ’%s’ is %d characters long\n", msg, strlen(msg)); (dbx) where [1] strlen(0x0, 0x0, 0xff337d24, 0x7efefeff, 0x81010100, 0xff0000), at 0xff2b6dec =>[2] main(argc = 1, argv = 0xffbef39c), line 9 in "foo.c" (dbx)
コアファイルのデバッグの詳細については、既存のコアファイルのデバッグを参照してください。呼び出しスタックの使用の詳細については、呼び出しスタックを確認するを参照してください。
ブレークポイントとは、一時的にプログラムの実行を停止して dbx に制御を渡すようにするプログラム内のある場所のことです。バグが存在するのではないかと思われるプログラム領域にブレークポイントを設定します。プログラムがクラッシュした場合、クラッシュが発生した個所をつきとめ、その部分の直前のコードにブレークポイントを設定します。
プログラムがブレークポイントで停止したら、プログラムの状態や変数の値を調べることができます。dbx では、さまざまな種類のブレークポイントを設定できます。(Control+C によってプロセスを停止するを参照)。
もっとも単純なブレークポイントは、停止ブレークポイントです。停止ブレークポイントを設定すると、関数または手続き内で停止させることができます。 たとえば、main 関数が呼び出されたときに停止させる方法は次のとおりです。
(dbx) stop in main (2) stop in main
stop in コマンドの詳細は、ブレークポイントとトレースの設定および stop コマンドを参照してください。
また、 停止ブレークポイントを設定して、ソースコードの特定の行で停止することもできます。たとえば、ソースファイル t.c の 13 行目で停止させる方法は次のとおりです。
(dbx) stop at t.c:13 (3) stop at “t.c”:13
stop at コマンドの詳細は、ソースコードの行へのブレークポイントの設定および stop コマンドを参照してください。
停止させる行は、file コマンドを使用して現在のファイルを設定したあと、list コマンドを使用して停止させる関数のリストを表示することによって特定できます。次に、stop at コマンドを使用してソース行にブレークポイントを設定します。
(dbx) file t.c (dbx) list main 10 main(int argc, char *argv[]) 11 { 12 char *msg = "hello world\n"; 13 printit(msg); 14 } (dbx) stop at 13 (4) stop at “t.c”:13
ブレークポイントで停止したプログラムの実行を続行するには、cont コマンドを使用します (プログラムを継続するおよび cont コマンドを参照)。
現在のすべてのブレークポイントのリストを表示するには、status コマンドを使用します。
(dbx) status (2) stop in main (3) stop at "t.c":13
ここでプログラムを実行すれば、最初のブレークポイントでプログラムが停止します。
(dbx) run ... stopped in main at line 12 in file "t.c" 12 char *msg = "hello world\n";
ブレークポイントで停止したあと、プログラムの実際の状態を予測される状態と比較しながら、プログラムを 1 ソース行ずつステップ実行することもできます。 それには、step コマンドと next コマンドを使用します。いずれのコマンドもプログラムのソース行を 1 行実行し、その行の実行が終了すると停止します。この 2 つのコマンドは、関数呼び出しが含まれているソース行の取り扱い方が違います。step コマンドは関数にステップインし、next コマンドは関数をステップオーバーします。
step up コマンドは、現在実行している関数が、自身を呼び出した関数に制御を戻すまで実行され続けます。
step to コマンドは、現在のソース行内の指定された関数か、または関数が指定されていない場合は、現在のソース行のアセンブリコードによって特定される最後に呼び出された関数へのステップインを試みます。
一部の関数 (特に、printf などのライブラリ関数) は -g オプションでコンパイルされていない可能性があるため、dbx はこれらの関数にステップインできません。このような場合、step と next は同様の動作になります。
次の例は、step および next コマンドと、ブレークポイントの設定で設定されたブレークポイントの使用を示しています。
(dbx) stop at 13 (3) stop at "t.c":13 (dbx) run Running: a.out stopped in main at line 13 in file "t.c" 13 printit(msg); (dbx) next Hello world stopped in main at line 14 in file "t.c" 14 } (dbx) run Running: a.out stopped in main at line 13 in file "t.c" 13 printit(msg); (dbx) step stopped in printit at line 6 in file "t.c" 6 printf("%s\n", msg); (dbx) step up Hello world printit returns stopped in main at line 13 in file "t.c" 13 printit(msg); (dbx)
プログラムのステップ実行の詳細については、プログラムのステップ実行を参照してください。step および next コマンドの詳細については、step コマンドおよび next コマンドを参照してください。
呼び出しスタックは、呼び出されたが、まだ対応する呼び出し側に戻っていない、現在アクティブなすべてのルーチンを表します。呼び出しスタックには、呼び出された順序で関数とその引数が一覧表示されます。プログラムフローのどこで実行が停止し、この地点までどのように実行が到達したのかが、スタックトレースに示されます。スタックトレースは、プログラムの状態を、もっとも簡潔に記述したものです。
スタックトレースを表示するには、where コマンドを使用します。
(dbx) stop in printf (dbx) run (dbx) where [1] printf(0x10938, 0x20a84, 0x0, 0x0, 0x0, 0x0), at 0xef763418 =>[2] printit(msg = 0x20a84 "hello world\n"), line 6 in "t.c" [3] main(argc = 1, argv = 0xefffe93c), line 13 in "t.c" (dbx)
-g オプションでコンパイルされた関数の場合は、引数名とその型がわかっているため、正確な値が表示されます。デバッグ情報が存在しない関数の場合は、引数の 16 進数が表示されます。これらの数字に意味があるとはかぎりません。たとえば、上のスタックトレースでは、フレーム 1 は $i0 から $i5 の SPARC 入力レジスタの内容を示しています。プログラムをステップ実行するに示されていた例では printf に 2 つの引数しか渡されなかったため、意味があるのは $i0 から $i1 のレジスタの内容だけです。
-g オプションを使ってコンパイルされなかった関数の中でも停止することができます。このような関数内で停止すると、dbx はスタックを下方向に検索して -g オプションでコンパイルされた関数 (この場合は printit()) を含む最初のフレームを見つけ、現在のスコープをそのフレームに設定します。これは、矢印記号 (=>) によって示されます。
呼び出しスタックの詳細は、効率性に関する考慮事項を参照してください。現在のスコープの詳細については、プログラムスコープを参照してください。
スタックトレースにはプログラムの状態を完全に表すための十分な情報が含まれている可能性がありますが、さらに多くの変数の値を確認することが必要になる場合があります。 print コマンドは式を評価し、式の型に基づいて値を印刷します。次は、単純な C 式の例です。
(dbx) print msg msg = 0x20a84 "Hello world" (dbx) print msg[0] msg[0] = ’h’ (dbx) print *msg *msg = ’h’ (dbx) print &msg &msg = 0xefffe8b4
データ変更ブレークポイントを使用して、変数や式の値がいつ変化したかを追跡できます (データ変更ブレークポイント (ウォッチポイント) の設定を参照)。たとえば、変数 count の値が変更されたときに実行を停止するには、次を入力します。
(dbx) stop change count
実行時検査は、メモリーアクセス検査、およびメモリー使用状況とリーク検査の 2 部で構成されます。アクセス検査は、デバッグ対象アプリケーションによるメモリーの誤った使用をチェックします。メモリー使用状況とメモリーリークの検査では、未処理のヒープ領域をすべて追跡したあと、必要に応じて、またはプログラムの終了時に、使用可能なデータ領域をスキャンし、参照されていない領域を識別します。
メモリーアクセス検査、およびメモリー使用状況とメモリーリークの検査は、check コマンドによって使用可能にします。 メモリーアクセス検査のみを有効にするには、次のように入力します。
(dbx) check -access
メモリー使用状況とメモリーリークの検査を有効にするには、次のように入力します。
(dbx) check -memuse
必要な種類の実行時検査を有効にしたら、プログラムを実行します。プログラムは正常に動作しますが、各メモリーアクセスが実行される直前にその妥当性がチェックされるため、動作速度は遅くなります。無効なアクセスを検出すると、dbx はそのエラーの種類と場所を表示します。その場合は、現在のスタックトレースを表示するための where コマンドや、変数を調べるための print コマンドなどの dbx コマンドを使用できます。
実行時検査の使用の詳細は、実行時検査の使用を参照してください。