Oracle Solaris Studio 12.2: dbx コマンドによるデバッグ

第 1 章 dbx の概要

dbx は、対話型でソースレベルの、コマンド行ベースのデバッグツールです。dbx を使用して、プログラムを制御下に置いた状態で実行し、停止したプログラムの状態を調べることができます。このツールにより、プログラムの動的な実行を完璧に制御できるほか、パフォーマンスデータとメモリーの使用状況の収集、メモリーアクセスの監視、およびメモリーリークの検出も行えます。

dbx は、C、C++、または Fortran で記述されたアプリケーションのデバッグに使用できます。また、多少の制限はありますが (「Java コードのデバッグにおける dbx の制限事項」を参照)、Java コードおよび C JNI (Java Native Interface) コードまたは C++ JNI コードの混在するアプリケーションをデバッグすることも可能です。

dbxtool により、dbx にグラフィカルユーザーインターフェースが提供されます。

この章では、dbx によるアプリケーションのデバッグの基礎について説明します。この章の内容は次のとおりです。

デバッグを目的としてコードをコンパイルする

dbx でソースレベルのデバッグを行えるようにプログラムを作成するには、-g オプションを付けてプログラムをコンパイルする必要があります。このオプションは、C、C++、Fortran 95、および Java の各コンパイラで利用できます。詳細については、「デバッグのためのプログラムのコンパイル」を参照してください。

dbx または dbxtool を起動してプログラムを読み込む

dbxを起動するには、シェルプロンプトで dbx を入力します。


$ dbx

dbxtool を起動するには、シェルプロンプトに dbxtool コマンドを入力します。


$ dbxtool

dbx を起動してデバッグ対象プログラムを読み込むには、次を入力します。


$ dbx program_name

dbxtool を起動してデバッグ対象プログラムを読み込むには、次を入力します。


$ dbxtool program_name

dbx を起動して、Java コードおよび C JNI コードまたは C++ JNI コードが混在する プログラムを読み込むには、次のように入力します。


$ dbx program_name{.class | .jar}

dbx コマンドを使用すると、dbx を起動し、プロセス ID で指定した実行中プロセスに接続できます。


$ dbx - process_id

dbxtool コマンドを使用すると、dbx が起動し、プロセス ID で指定した実行中プロセスに接続できます。


$ dbxtool - process_id

プロセスの ID がわからない場合、dbx コマンドに pgrep コマンドを含めることで、ID を調べてプロセスに接続します。次に例を示します。


$ dbx - `pgrep Freeway`
Reading -
Reading ld.so.1
Reading libXm.so.4
Reading libgen.so.1
Reading libXt.so.4
Reading libX11.so.4
Reading libce.so.0
Reading libsocket.so.1
Reading libm.so.1
Reading libw.so.1
Reading libc.so.1
Reading libSM.so.6
Reading libICE.so.6
Reading libXext.so.0
Reading libnsl.so.1
Reading libdl.so.1
Reading libmp.so.2
Reading libc_psr.so.1
Attached to process 1855
stopped in _libc_poll at 0xfef9437c
0xfef9437c: _libc_poll+0x0004:   ta     0x8
Current function is main
   48   XtAppMainLoop(app_context);
(dbx)

dbx コマンドと起動オプションの詳細については、dbx コマンド」および dbx(1) マニュアルページを参照するか、dbx -h と入力してください。

すでに dbx を実行している場合、debug コマンドにより、デバッグ対象プログラムを読み込むか、デバッグしているプログラムを別のプログラムに切り替えることができます。


(dbx) debug program_name

Java コードおよび C JNI コードまたは C++ JNI コードを含むプログラムを読み込むかそれに切り替える場合は、次を入力します。


(dbx> debug program_name{.class | .jar}

すでに dbx を実行している場合、debug コマンドにより、dbx を実行中プロセスに接続することもできます。


(dbx) debug program_name process_id

Java コードと C JNI (Java Native Interface) コードまたは C++ JNI コードの混在する実行中プロセスに dbx を接続するには、次のように入力します。


(dbx) debug program_name{.class | .jar} process_id

debug コマンドの詳細については、debug コマンド」を参照してください。

プログラムを dbx で実行する

dbx に最後に読み込んだプログラムを実行するには、run コマンドを使用します。引数を付けないで run コマンドを最初に入力すると、引数なしでプログラムが実行されます。引数を引き渡したりプログラムの入出力先を切り替えたりするには、次の構文を使用します。


run [ arguments ] [ < input_file ] [ > output_file ]

次に例を示します。


(dbx) run -h -p < input > output
Running: a.out
(process id 1234)
execution completed, exit code is 0
(dbx)

Java コードを含むアプリケーションを実行する場合は、実行引数は、JVM ソフトウェアに渡されるのではなく、Java アプリケーションに渡されます。main クラス名を引数として含めないでください。

引数を付けないで run コマンドを繰り返し使用した場合、プログラムは前回の run コマンドの引数や入力先を使用します。rerun コマンドを使用すれば、オプションをリセットできます。run コマンドの詳細については、run コマンド」を参照してください。rerun コマンドの詳細については、rerun コマンド」を参照してください。

アプリケーションは、最後まで実行され、正常に終了するかもしれません。ブレークポイントが設定されている場合には、ブレークポイントでアプリケーションが停止するはずです。アプリケーションにバグが存在する場合は、メモリーフォルトまたはセグメント例外のため停止することがあります。

dbx を使用してプログラムをデバッグする

プログラムをデバッグする理由としては、次が考えられます。

コアファイルをチェックする

プログラムがどこで クラッシュするかをつきとめるには、プログラムがクラッシュしたときのメモリーイメージであるコアファイルを調べるとよいでしょう。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 は、このような関数にはステップインできません。このような場合、stepnext は同じような動作を示します。

次は、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 章実行時検査を参照してください。

dbx を終了する

dbx セッションは、dbx を起動してから終了するまで継続されます。dbx セッション中に、任意の数のプログラムを連続してデバッグできます。

dbx セッションを終了するには、quitdbx プロンプトに入力します。


(dbx) quit

起動時に process_id オプションを使用してデバッガを動作中のプロセスに接続した場合、デバッグセッションを終了しても、そのプロセスは終了しないで動作を続けます。 すなわち、dbx はセッションを終了する前に自動的に detach コマンドを実行します。

dbx の終了の詳細については、「デバッグセッションを終了する」を参照してください。

dbx オンラインヘルプにアクセスする

dbx には、help コマンドでアクセスできるヘルプファイルが含まれています。


(dbx) help