Oracle® Developer Studio 12.5: dbxtool チュートリアル

印刷ビューの終了

更新: 2016 年 6 月
 
 

ブレークポイントとステップ動作の使用法

ブレークポイントにより、バグの出現箇所の前でプログラムを停止したり、何が間違っているかを検出するためにコードをステップスルーしたりできます。

まだ行なっていない場合は、「出力」ウィンドウの連結を解除します。

以前はこのプログラムをコマンド行から実行しました。dbxtool でプログラムを実行して、バグを再現します。

  1. ツールバーの「再起動」ボタン image:「再起動」ボタン をクリックするか、「デバッガ・コンソール」ウィンドウで run と入力します。

  2. 「デバッガ・コンソール」ウィンドウで Return キーを押します。

    警告ボックスに、SEGV に関する情報が表示されます。

    image:SEGV を表示する「シグナルがキャッチされました」警告ボックス
  3. 警告ボックスで、「破棄して一時停止」をクリックします。

    「エディタ」ウィンドウで、Interp::find() 内の strcmp() の呼び出しが再度強調表示されます。

  4. ツールバーの「呼び出し元を現在に設定」ボタン image:「呼び出し元を現在に設定」ボタン をクリックして、以前に Interp::dispatch() に表示された不明なコードに移動します。

  5. 次のセクションでは、find() の呼び出し箇所の少し前にブレークポイントを設定し、コードをステップスルーして不具合の理由を特定できるようにします。

ブレークポイントを設定する

ブレークポイントは、行ブレークポイントや関数ブレークポイントなど、いくつかの方法で設定できます。次のリストで、ブレークポイントを作成する複数の方法について説明します。


注 -  行番号が表示されていない場合は、左マージン内を右クリックし、「行番号を表示」オプションを選択して、エディタの行番号を有効にします。
  • 行ブレークポイントの設定

    127 行目の横の左マージン内をクリックして、行ブレークポイントを切り替えます。

    image:127 行目に行ブレークポイントが表示された「エディタ」ウィンドウ
  • 関数ブレークポイントの設定

    関数ブレークポイントを設定します。

    1. 「エディタ」ウィンドウで、「Interp::dispatch」を選択します。

    2. 「デバッグ」->「新規ブレークポイント」を選択するか、右クリックして「新規ブレークポイント」を選択します。

      「新規ブレークポイント」ダイアログボックスが表示されます。

      image:「新規ブレークポイント」ダイアログボックス

      選択した関数の名前が「関数」フィールドに表示されています。

    3. 「OK」をクリックします。

  • コマンド行でのブレークポイントの設定

    関数ブレークポイントを設定するもっとも簡単な方法は、dbx コマンド行を使用することです。「デバッガ・コンソール」ウィンドウに stop in コマンドを入力します。

    (dbx) stop in dispatch                 
    (4) stop in Interp::dispatch(char*) 
    (dbx) 

    Interp::dispatch と入力する必要がなかったことに注目してください。関数名を指定するだけで十分です。

「ブレークポイント」ウィンドウとエディタは、通常次のように表示されます。

image:乱雑な「エディタ」ウィンドウ

エディタ内が乱雑になるのを避けるため、「ブレークポイント」ウィンドウを使用します。

  1. 「ブレークポイント」タブをクリックします (または前に最小化している場合は最大化します)。

  2. 行ブレークポイントおよび関数ブレークポイントの 1 つを選択して、右クリックして「削除」を選択します。

ブレークポイントの詳細は、Oracle Developer Studio 12.5: dbx コマンドによるデバッグ の 第 6 章, ブレークポイントとトレースの設定を参照してください。

関数ブレークポイントの利点

エディタで切り替えて、行ブレークポイントを設定することは直感的である場合があります。ただし、多くの dbx ユーザーは、次の理由で関数ブレークポイントの方を好みます。

  • 「デバッガ・コンソール」ウィンドウに si dispatch と入力すると、エディタでファイルを開き、ブレークポイントを配置する行までスクロールする必要がなくなります。

  • エディタ内のテキストを選択すれば関数ブレークポイントを作成できるようになるため、ファイルを開かなくても、関数の呼び出し側で関数にブレークポイントを設定できます。


    ヒント  -  si は、stop in の別名です。ほとんどの dbx ユーザーは多数の別名を定義し、その定義内容を dbx の構成ファイル ~/.dbxrc に置きます。次に、一般的な例を示します。
    alias si stop in
    alias sa stop at
    alias s step
    alias n next
    alias r run

    .dbxrc ファイルと dbxenv 変数のカスタマイズの詳細は、Oracle Developer Studio 12.5: dbx コマンドによるデバッグ の dbxenv 変数の設定を参照してください。

  • 関数ブレークポイントの名前は、「ブレークポイント」ウィンドウに表示されています。行ブレークポイントの名前は説明的なものではありませんが、「ブレークポイント」ウィンドウの行ブレークポイントを右クリックし、「ソースへ移動」を選択するか、そのブレークポイントをダブルクリックすると、127 行目の内容を特定できます。

  • 関数ブレークポイントの方が、より持続します。dbxtool はブレークポイントを永続させるため、コードを編集したりソースコード制御のマージを行なったりすると、行ブレークポイントが簡単にずれてしまうことがあります。関数名の方が編集に耐えられます。

ウォッチポイントとステップ動作の使用法

これで Interp::dispatch() にブレークポイントが 1 つできたので、「デバッガ・コンソール」ウィンドウで「再起動」 image:「再起動」ボタン を再度クリックして Return キーを押すと、実行可能コードを含む dispatch() 関数の最初の行でプログラムが停止します。

image:122 行目で実行が停止した「エディタ」ウィンドウ

    find() に渡される argv[0] の問題が特定されたので、argv に対してウォッチポイントを使用します。

  1. 「エディタ」ウィンドウで argv のインスタンスを選択します。

  2. 右クリックして「新規ウォッチポイント」を選択します。選択したテキストを含む「新規ウォッチ」ダイアログボックスが表示されます。

    image:「新規ウォッチポイント 」ダイアログボックス
  3. 「OK」をクリックします。

  4. 「ウォッチ」ウィンドウを開くには、「ウィンドウ」->「ウォッチ」(Alt+Shift+2) を選択します。

  5. 「ウォッチ」ウィンドウで、argv を展開します。

    image:argv が展開された「ウォッチポイント 」ウィンドウ

    argv は初期化されておらず、argv はローカル変数であるため、前の呼び出しでスタックに残されたランダムな値を「継承」している可能性があることに注意してください。これは問題の原因でしょうか。


    注 -  ウォッチ変数を確認する際には、「変数」ウィンドウ、「ウォッチ」ウィンドウのどちらを使ってもかまいません。
  6. 緑色の PC の矢印が int argc = 0; を指すまで、「ステップ・オーバー」(F8) image:「ステップオーバー」ボタン を 2 回クリックします。

  7. argcargv のインデックスになるため、argc のウォッチポイントも作成します。argc も現時点では初期化されておらず、意図しない値が含まれている可能性があることに注意してください。

    argc のウォッチポイントは、argv のウォッチポイントのあとで作成したため、「ウォッチ」ウィンドウでは 2 番目に表示されます。

  8. ウォッチポイント名をアルファベット順に並べるには、「名前」列見出しをクリックして列をソートします。次の図のソートの三角印を確認してください。

    image:ウォッチポイントがソートされた「ウォッチポイント」ウィンドウ
  9. 「ステップ・オーバー」(F8) image:「ステップオーバー」ボタン をクリックします。

    argc は、初期化された値である 0 を示し、値がちょうど変更されたことを示す太字で表示されます。

    image:argc 値が太字で表示された「ウォッチポイント 」ウィンドウ

    アプリケーションが strtok() を呼び出します。

  10. 「ステップ・オーバー」をクリックして、関数をステップオーバーし、たとえば、バルーン式を使用して、token が NULL であることを監視します。

    strtok() 関数は、たとえば文字列をいずれかの DELIMITERS で区切られたトークンに分割するのに役立ちます。詳細は、strtok(3) のマニュアルページを参照してください。

  11. 「ステップ・オーバー」を再度クリックして、トークンを argv に割り当てます。次に、ループ内で strtok() の呼び出しが行われます。

    ステップオーバーすると、ループには入らずに (これ以上トークンがないため)、代わりに NULL が割り当てられます。

  12. サンプルプログラムがどこでクラッシュしたかを特定するため、その割り当てもステップオーバーすると呼び出しのしきい値に達します。

  13. プログラムがこの時点でクラッシュすることをダブルチェックするため、find() の呼び出しをステップオーバーします。

    「シグナルがキャッチされました」警告ボックスが再度表示されます。

    image:「シグナル捕獲 」警告ボックス
  14. 以前のように「破棄して一時停止 」をクリックします。

    Interp::dispatch() で停止したあとの find() の最初の呼び出しが、本当に不具合のある場所です。

    最初に find() を呼び出した場所にすばやく戻ることができます。

    1. 「呼び出し元を現在に設定」 image:「呼び出し元を現在に設定」ボタン をクリックします。

    2. find() の呼び出しサイトで行ブレークポイントを切り替えます。

    3. 「ブレークポイント」ウィンドウを開いて、Interp::dispatch() 関数ブレークポイントを無効にします。

      dbxtool の表示は次の図のようになります。

      image:1 つを無効にし、2 つのブレークポイントを示す「エディタ 」ウィンドウ
    4. 下矢印は、2 つブレークポイントが 141 行に設定され、それらの 1 つが無効であることを示しています。

  15. 「デバッガ・コンソール」ウィンドウで「再起動」 image:「実行」ボタン をクリックし、Return キーを押します。

    プログラムが find() の呼び出しの前に戻ります。「再起動」ボタンは再起動を実行します。デバッグ中は、初期の起動より再起動の方が頻繁に行われます。


    ヒント  -  たとえば、プログラムを再構築する場合、バグを検出して修正したあとで、dbxtool を終了して、再起動する必要はありません。「再起動」ボタンをクリックすると、dbx はプログラム (またはその構成要素) が再コンパイルされていることを検出し、再ロードします。 したがって、デバッグ中の問題に対してすぐに使用できるように、dbxtool をデスクトップ上に、通常は最小化して保持することを検討してください。
  16. バグはどこにあるでしょうか。ふたたびウォッチポイントをみてみましょう。

    image:「ウォッチポイント」ウィンドウ

    行が空であり、トークンを持っていなかったため、strtok() の最初の呼び出しが NULL を返したため、argv[0] が NULL であることに注意してください。

    必要に応じて、このチュートリアルの残りに進む前にこのバグを修正してください。

    デバッガでプログラムを実行する場合は、ブレークポイントスクリプトを使用してコードにパッチを適用するの説明に従って、デバッガでコードにパッチを適用できます。

コード例の開発者は、この条件に対しておそらくテストし、Interp::dispatch() の残りを省略しているはずです。

ディスカッション

この例は、誤動作するプログラムを不具合が発生する前の時点で停止し、コードの意図と実際のコードの動作を比較しながらコードをステップスルーする、もっとも一般的なデバッグパターンを示しています。

次のセクションでは、この例で使用したステップ動作とウォッチポイントの一部を回避するためにブレークポイントを使用する高度な技術について説明します。