プログラムをデバッグする理由としては、次が考えられます。
クラッシュする場所と理由をつきとめるため、クラッシュの原因をつきとめる方法としては、次があります。
プログラムを dbx で 実行します。dbx はクラッシュの発生場所をレポートします。
コアファイルを調べ、スタックトレースをチェックします (「コアファイルをチェックする」および 「呼び出しスタックを確認する」を参照)。
次の方法で、プログラムが不正な実行結果を出力する原因を判定します。
ブレークポイントを設定して実行を停止することにより、プログラムの状態をチェックして変数の値を調べます (「ブレークポイントを設定する」および 「変数を調べる」参照)。
ソースコードを 1 行ずつステップ実行することによって、プログラムの状態がどのように変わっていくかを監視します (「プログラムをステップ実行する」参照)。
メモリーリークやメモリー管理問題を見つける方法としては、次があります。実行時検査を行えば、メモリーアクセスエラーやメモリーリークエラーといった実行時エラーを確認できるとともに、メモリー使用状況を監視できます (「メモリーアクセス問題とメモリーリークを検出する」を参照)。
プログラムがどこで クラッシュするかをつきとめるには、プログラムがクラッシュしたときのメモリーイメージであるコアファイルを調べるとよいでしょう。where コマンドを使用すれば (「where コマンド」を参照)、コアをダンプしたときのプログラムの実行場所がわかります。
ネイティブコードのときと異なり、コアファイルから Java アプリケーションの状態情報を入手することはできません。
$ 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 では、さまざまな種類のブレークポイントを設定できます (「Ctrl+C によってプロセスを停止する」を参照)。
もっとも単純なブレークポイントは、停止ブレークポイントです。停止ブレークポイントを使用すれば、関数や手続きの中で停止させることができます。たとえば、main 関数が呼び出されたときに停止させる方法は次のとおりです。
(dbx) stop in main (2) stop in main |
stop in コマンドの詳細については、「関数に stop ブレークポイントを設定する」および 「stop コマンド」を参照してください。
また、特定のソースコード行で停止するようにブレークポイントを設定することもできます。たとえば、ソースファイル t.c の 13 行目で停止させる方法は次のとおりです。
(dbx) stop at t.c:13 (3) stop at “t.c”:13 |
stop at コマンドの詳細については、「ソースコードの特定の行に stop ブレークポイントを設定する」および 「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 入力レジスタの内容を示しています。内容に意味があるレジスタは $i0 から $i1 までだけです (「プログラムをステップ実行する」の例の printf に引き渡された引数は 2 つだけであるため)。
-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 などの dbx コマンド、変数を調べるには print コマンドを使用します。
Java コードおよび C JNI コードまたは C++ JNI コードが混在するアプリケーションには、実行時検査を使用できません。
実行時検査の詳細については、第 9 章実行時検査を参照してください。