この章では、ブレークポイントとトレースの設定方法とウォッチポイントの使用方法について説明します。
この章は、次の各節から構成されています。
stop, when, trace コマンドを総称してイベント管理コマンドと呼びます。イベント管理は、デバッグ中のプログラムで特定のイベントが発生したときに特定のアクションを行う dbx の一般的な機能です。
ブレークポイントで動作するコマンドには次の 3 種類があります。
stop ブレークポイント |
stop コマンドによって作成されたブレークポイントに到達すると、プログラムは停止します。停止したプログラムはほかの dbx コマンドを実行するまで再開されません。 |
when ブレークポイント |
プログラムは停止し、dbx が 1 つまたは複数の dbx コマンドを実行した後、プログラムは再開します。プログラムは、コマンドの中に stop が含まれていない限り処理を続けます。 |
trace ブレークポイント |
プログラムは停止し、イベント固有のトレース情報行を出力した後、プログラムは再開します。 |
dbx の stop at コマンドを使用して、行番号にブレークポイントを設定できます。
(dbx) stop at filename: n
ここで、n はソースコードの行番号、filename はそのコードが含まれているソースファイルの名前 (省略可能) です。たとえば、次のようにします。
(dbx) stop at main.cc:3
stop または when コマンドに指定された行が、ソースコードの実行可能行ではない場合、dbx は次の有効な実行可能行にブレークポイントを設定します。
when タイプのブレークポイントは、ほかの dbx コマンドを副作用コマンドとして利用できます。たとえば、when で list コマンドを副作用コマンドとして用いれば、独自のトレースを実現することができます。
(dbx) when at 123 { list $lineno;}
when は cont コマンドを暗黙指定して動作します。上の例の場合、現在行のコードを出力した後、プログラムの実行が続けられます。
実行時リンカーとの間でプログラムインタフェースを使用するコード、すなわち dlopen()、dlclose()、およびそれらに関連する関数を呼び出すコードをデバッグするときは、dbx の全機能を利用できます。実行時リンカーは、プログラムの実行中に共有ライブラリをリンクしたり、そのリンクを解除したりします。dlopen() と dlclose() のサポートにより、動的にリンクされている共有ライブラリの関数もプログラムの起動時にリンクされたライブラリの関数と同様に、ステップ実行したり、ブレークポイントを設定したりできます。
例外が 3 つあります。
dlopen ( ) によって読み込まれるライブラリには、そのライブラリが dlopen ( ) によって読み込まれる前にブレークポイントを設定することはできません。
dlopen ( ) によって読み込まれたフィルタライブラリには、その中の最初の関数が呼び出されるまでブレークポイントを設定することはできません。
ライブラリが dlopen() によって読み込まれるとき、_init() という名前の初期化ルーチンが呼び出されます。このルーチンは、ライブラリ内のほかのルーチンを呼び出すことがあります。この初期化が終わるまで、dbx は読み込まれたライブラリにブレークポイントを置くことができません。dlopen() によって読み込まれたライブラリの _init()内で dbx を停止させることはできません。
異なるクラスのメンバー関数の呼び出し、特定のクラスのすべてのメンバー関数の呼び出し、または多重定義されたトップレベル関数の呼び出しに関連する問題が発生する可能性があります。このような場合に対処するために、inmember、inclass、infunction または inobject のキーワードのうちの 1 つを stop, when,または trace コマンドとともに使用することにより、1 回のコマンドで C++ コードに複数のブレークポイントを挿入できます。
特定のメンバー関数のオブジェクト固有のもの (同じメンバー関数名でクラスの異なるもの) それぞれにブレークポイントを設定するには、stop inmember を使用します。
when ブレークポイントを設定するには、when コマンドをキーワード inmember とともに使用します。
たとえば、関数 draw が複数の異なるクラスに定義されている場合は、それぞれの関数ごとにブレークポイントを設定します。
(dbx) stop inmember draw
特定のクラスのすべてのメンバー関数にブレークポイントを設定するには、stop inclass コマンドを使用します。
when タイプのブレークポイントを設定する場合は、when コマンドをキーワード inclass とともに使用します。
クラス wedge のすべてのメンバー関数にブレークポイントを設定するには、次のように入力します。
(dbx) stop inclass wedge
ブレークポイントは、該当するクラスで定義されているクラスメンバー関数にだけ挿入されます。基底クラスから継承された関数には挿入されません。
stop inclass やほかのブレークポイントの選択によって挿入される多数のブレークポイントがあるため、必ず dbxenv step_events を on に設定して、step と next の速度を上げる必要があります。
多重定義された名前を持つ非メンバー関数 (同じ名前を持ち、引数の型または数の異なるもの) に複数のブレークポイントを設定するには、stop infunction コマンドを使用します。
when タイプのブレークポイントを設定する場合は、when コマンドをキーワード infunction とともに使用します。
たとえば、C++ プログラムで sort() という名前の関数が 2 種類定義されていて、一方が int 型の引数、もう一方が float 型の引数をとる場合に、両方の関数にブレークポイントを置くためには、次のように入力します。
(dbx) when infunction sort {cmd;}
In Object ブレークポイントを設定して、特定オブジェクトに適用される演算を確認します。In Object ブレークポイントは、オブジェクトから呼び出されると、そのオブジェクトクラスのすべての非静的メンバー関数によるプログラムの実行を中断します。
オブジェクトにブレークポイントを設定するには、stop inobject コマンドを使用します。
(dbx) stop inobject foo
トレースにより、実行しようとしている行や呼び出そうとしている関数についての情報が表示されます。
コマンド行に trace コマンドを入力し、トレースを設定します。Failed Cross Reference Format は、設定できるトレースの各タイプのコマンド構文を示しています。トレースにより表示される情報は、そのトレースに対応づけられたイベントのタイプによって異なります。
表 5-1 trace コマンドの構文
コマンド |
出力される内容 |
trace step |
プログラムのすべての行をそれが実行されるときに表示する。 |
trace next -in function |
関数 function に制御が移っている間、各行で表示する。 |
trace at line_number |
line_number で指定された行を次に実行する場合に、その行の行番号と内容。 |
trace in function |
関数 function を呼び出した関数の名前、行番号、渡されたパラメータ、戻り値。 |
trace inmember member_function |
すべてのクラスの member_function を呼び出した関数の名前、その行番号、渡されたパラメータ、その戻り値。 |
trace inclass class |
クラス class 内のすべてのメンバー関数を呼び出した関数の名前、その行番号、渡されたパラメータ、その戻り値。 |
trace infunction function |
C++ プログラム内の非メンバー関数 function を呼び出した関数の名前、その行番号、渡されたパラメータ、その戻り値。 |
trace change variable [-in function] |
指定された関数 function 内の変数 variable の値が変更されたとき、その新しい値とその変数が変更された行。 |
多くのプログラムは高速に実行されるため、表示がよく見えません。環境変数 dbxenv trace_speed を使用して、トレーススコープ内の各行の実行間隔を制御することができます。デフォルトの実行間隔は 0.5 秒です。
トレース時の各行の実行間隔を設定するには、次のように入力します。
dbxenv trace_speed number
dbx セッション中にブレークポイントやトレースポイントを複数設定することがよくあります。dbx には、それらのポイントを表示したりクリアしたりするためのコマンドが用意されています。
すべての有効なブレークポイントのリストを表示するには、status コマンドを使用します。ブレークポイントは ID 番号付きで表示され、この番号はほかのコマンドで使用できます。
C++ の多重ブレークポイントのところでも説明したように、dbx はキーワード inmember、inclass、infunction で設定された多重ブレークポイントを、1 つのステータス ID 番号を使用してまとめて報告します。
status コマンドを使用してブレークポイントをリスト表示した場合、dbx は、各ブレークポイントの作成時に割り当てられた ID 番号を表示します。 delete コマンドを使用することで、ID 番号によってブレークポイントを削除したり、キーワード all により、プログラム内のあらゆる場所に現在設定されているブレークポイントをすべて削除することができます。
ブレークポイントを ID 番号 ID_number によって削除するには、次のように入力します。
(dbx) delete 3 5
dbx に現在読み込まれているプログラムに設定されているすべてのブレークポイントを削除するには、次のように入力します。
(dbx) delete all
ウォッチポイントは、変数または式の値が変化したときにイベントを発生させる dbx の一般的な機能です。
アドレスの内容に書き込みが行われたときにプログラムの実行を停止するには、次のように入力します。
(dbx) stop modify & variable
stop modify を使用するときの注意事項
変数 variable にそれまで設定されていた値と同じ値が書き込まれた場合でもイベントは発生します。
dbx が命令をエミュレートすることによって、メモリーに新しい内容が設定された場合でも、変数への書き込みを行なった命令が実行される前にイベントが発生します。
関数に局所的な自動変数など、スタック変数のアドレスを使用することはできません。
指定した変数の値が変化したときにプログラムの実行を停止するには、次のように入力します。
(dbx) stop change variable
stop change を使用するときの注意事項
dbx は、指定された変数の値に変更があった行の後ろの行でプログラムを停止します。
変数が関数の局所変数の場合、その関数に最初に制御が移り、変数のための記憶領域が割り当てられた時点で変更があったと見なします。パラメータについても、同じことが言えます。
dbx は、自動的なステップ実行を行い、各ステップで値を検査することによって stop change を実現しています。ステップ実行では、ライブラリ呼び出しは飛ばされます。したがって、制御の流れが次のようになっているとき、
user_routine calls library_routine, which calls user_routine2, which changes variable
dbx は入れ子になった user_routine2 をトレースしません。トレース時にライブラリ呼び出しと user_routine2 の呼び出しがスキップされるため、入れ子になった呼び出しをトレースしません。この結果、変数の値は user_routine2 ルーチンの中ではなく、ライブラリ呼び出しから戻った後に変更されたように示されます。
ブロック局所変数、すなわち、{} 内で定義された変数の変化に対するブレークポイントは設定できません。このような変数にブレークポイント (またはトレース) を設定しようとすると、その操作ができないことを示すエラーメッセージが出されます。
条件文が真になったときにプログラムの実行を停止するには、次のように入力します。
(dbx) stop cond condition
ウォッチポイントは、modify コマンドを使用すると簡単に設定できます。このコマンドは、プログラムを自動的にステップ実行する代わりに、はるかに高速のページ保護構造を使用します。処理速度は、デバッグ中のプログラムのシステム呼び出し率だけでなく、監視中の変数の存在するページが何回修正されるかによって異なります。
dbx では、イベント管理コマンドのほとんどで (省略可能な) イベントフィルタの修飾文も利用することができます。最も単純なフィルタは次のいずれかの時点で、dbx に対してある特定の条件をテストするように指示します。
プログラムがブレークポイントまたはトレースポイントに到達した後
ウォッチ条件が発生した後
このフィルタ条件が真 (非 0) と評価されると、イベントコマンドが適用されます。条件が偽 (0) と評価されると、dbx はイベントが発生しなかったかのようにプログラムの実行を続けます。
条件つきフィルタを含む行または関数にブレークポイントを設定するためには、-if condition 指示子文を stop コマンドまたは trace コマンドの終わりに追加します。
condition には、任意の有効な式を指定できます。コマンドの入力時に有効だった言語で書かれた、ブール値または整数値を返す関数呼び出しも有効な式に含まれます。
in や at など位置にもとづくブレークポイントでは、スコープはブレークポイント位置のスコープになります。それ以外の場合、イベントではなくエントリ発生時のスコープになります。スコープを正確に指定する構文を使用しなければならないことがあります。
stop in foo -if a>5 stop cond a>5
前者は foo にブレークポイントが設定され、条件を検査します。後者は自動的に条件を検査します。
最初のうちは、条件つきイベントコマンド (watch タイプのコマンド) の設定とフィルタの使用とを混乱してしまうかもしれません。watch タイプのコマンドは、各行の実行前の前提条件の検査に用いられます (watch のスコープ内で)。しかし、このような条件つきブレークポイントに対してもフィルタを利用することができます。
(dbx) stop modify &speed -if speed==fast_enough
このコマンドは、変数 speed を監視するように dbx に指令します。speed に書き込みが行われると、-if フィルタが有効になります。dbx は speed の新しい値が fast_enough と等しいかどうかチェックします。等しくない場合、プログラムは実行を継続し、stop を「無視」します。
dbx 構文では、フィルタはブレークの「事後」、構文の最後で [-if condition] 文の形式で指定されます。
stop in function [-if condition]
デバッグ中のプログラムの実行時間に関するオーバーヘッドの量はイベントの種類によって異なります。最も単純なブレークポイントのように、実際はオーバーヘッドが何もないイベントもあります。1 つのブレークポイントしかないイベントも、オーバーヘッドは最小です。
実際のブレークポイントがときには何百にもなることのある多重ブレークポイント (inclass など) は、コマンド発行時にのみオーバーヘッドがあります。これは、dbx が永続的ブレークポイントを使用するためです。永続的ブレークポイントは、プロセスに常に保持され、停止するたびに取り除かれたり、cont のたびに置かれたりすることはありません。
step および next の場合、デフォルトでは、プロセスが再開される前にすべてのブレークポイントが取り除かれ、ステップが完了するとそれらは再び挿入されます。したがって、多くのブレークポイントを使用したり、多くのクラスで多重ブレークポイントを使用したりしているとき、step および next の速度は大幅に低下します。dbxenv 変数 step_events を使用して、step や next のたびにブレークポイントを取り除いたり、挿入し直したりするかどうかを制御することができます。
自動ステップ実行を利用するイベントは最も低速です。これは、各ソース行をステップ実行する単純な trace step コマンドの場合と同様にはっきりしています。一方、stop change expression や trace cond variable (ウォッチポイントと呼ばれるイベントのクラス) のようなイベントは、自動的にステップ実行するだけでなく、各ステップで式 expression や変数 variable を評価する必要があります。
これらのイベントは非常に低速ですが、イベントと修飾語 -in を使用した関数とを結び付けることで、効率が上がることがよくあります。たとえば、次のようにします。
trace next -in mumble stop change clobbered_variable -in lookup
trace -in main を使用しないでください。これは、main によって呼び出された関数の中でも、トレースが有効になるためです。関数 lookup() が変数の値を頻繁に変更すると思われる場合には、この方法を使用してください。