次: , 前: Summary, 上: Top


1 GDBセッションのサンプル

その気になれば、 このマニュアルを使ってGDBのすべてを学習することももちろん可能ですが、 GDBを使い始めるには、 いくつかのコマンドを知っていれば十分です。 本章では、そのようなコマンドについて説明します。

汎用的なマクロ・プロセッサであるgnu m4には、 かつて、 まだ正式なバージョンがリリースされる以前に、 次のような不具合がありました。 引用を表わす文字列をデフォルトとは異なるものに変更すると、 あるマクロ定義の内部に入れ子状態になっている他のマクロ定義を取り出すために使われるコマンドが、 正しく動作しなくなることがある、 という不具合です。 以下の短いm4セッションでは、 0000に展開されるマクロfooを定義しています。 さらに、 m4の組み込みコマンドdefnを使って、 マクロbarに同一の定義を与えています。 ところが、 引用の開始文字列を<QUOTE>に、 引用の終了文字列を<UNQUOTE>にそれぞれ変更すると、 全く同一の手順で新しい同義語bazを定義しようとしても、 うまくいかないのです。

     $ cd gnu/m4
     $ ./m4
     define(foo,0000)
     
     foo
     0000
     define(bar,defn(`foo'))
     
     bar
     0000
     changequote(<QUOTE>,<UNQUOTE>)
     
     define(baz,defn(<QUOTE>foo<UNQUOTE>))
     baz
     C-d
     m4: End of input: 0: fatal error: EOF in string

ここでGDBを使って、 何が起こっているのか調べてみましょう。

     $ gdb m4
     
     
     GDB is free software and you are welcome to distribute copies
      of it under certain conditions; type "show copying" to see
      the conditions.
     There is absolutely no warranty for GDB; type "show warranty"
      for details.
     
     GDB 4.18, Copyright 1999 Free Software Foundation, Inc...
     (gdb)

GDBは、 必要なときに他のシンボルを見つけるのに最低限必要となるシンボル情報しか読み込みません。 その結果、 最初のプロンプトが表示されるまでの時間が極めて短いのです。 ここで、 出力情報がこのマニュアルの紙幅に収まるようにするために、 GDBに対して表示幅を通常よりも狭くするよう指示を出してみましょう。

     (gdb) set width 70

m4の組み込みコマンドであるchangequoteがどのように動作するのかを調べてみる必要があります。 ソースを見ると、 関連するサブルーチンがm4_changequoteであることがわかります。 そこで、 GDBのbreakコマンドでブレイクポイントを設定してみます。

     (gdb) break m4_changequote
     Breakpoint 1 at 0x62f4: file builtin.c, line 879.

runコマンドを使って、 GDBの管理下でm4を走らせます。 m4_changequoteサブルーチンに到達するまでは、 プログラムは通常どおりの動作をします。

     (gdb) run
     Starting program: /work/Editorial/gdb/gnu/m4/m4
     define(foo,0000)
     
     foo
     0000

ブレイクポイントでプログラムを停止させるためにchangequoteを実行すると、 GDBはm4の実行を停止し、 停止した箇所のコンテキスト情報を表示します。

     changequote(<QUOTE>,<UNQUOTE>)
     
     Breakpoint 1, m4_changequote (argc=3, argv=0x33c70)
         at builtin.c:879
     879         if (bad_argc(TOKEN_DATA_TEXT(argv[0]),argc,1,3))

次にnnext)コマンドを実行すると、 現在停止している関数の中で1行だけ処理が実行されます。

     (gdb) n
     882         set_quotes((argc >= 2) ? TOKEN_DATA_TEXT(argv[1])\
      : nil,

set_quotesというのは、 いわくありげなサブルーチンです。 nextコマンドの代わりにsstep)コマンドを使うことで、 このサブルーチンの中に入ることができます。 stepコマンドは、 それがどのサブルーチンの中にあるかということにかかわりなく、 次の1行に移動します。 この場合、 次の1行はset_quotesの中ですから、 そこへ移動することになります。

     (gdb) s
     set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>")
         at input.c:530
     530         if (lquote != def_lquote)

m4がその中で現在停止しているサブルーチン (および、そのサブルーチンへの引数) が表示されています。 これをスタック・フレーム表示と呼びます。 それは、 スタックの状態を要約した情報を表示しています。 backtraceコマンド (あるいは、 btと省略することもできます) を使って、 現在、 スタック全体の中のどこにいるかを知ることもできます。 backtraceコマンドは、 アクティブなサブルーチンのスタック・フレームを表示します。

     (gdb) bt
     #0  set_quotes (lq=0x34c78 "<QUOTE>", rq=0x34c88 "<UNQUOTE>")
         at input.c:530
     #1  0x6344 in m4_changequote (argc=3, argv=0x33c70)
         at builtin.c:882
     #2  0x8174 in expand_macro (sym=0x33320) at macro.c:242
     #3  0x7a88 in expand_token (obs=0x0, t=209696, td=0xf7fffa30)
         at macro.c:71
     #4  0x79dc in expand_input () at macro.c:40
     #5  0x2930 in main (argc=0, argv=0xf7fffb20) at m4.c:195

次に、2、3行先に進んで、 何が起こっているのかを見てみましょう。 最初の2回は、 `s'コマンドを使います。 続く2回は、 xstrdupサブルーチンの中に入ってしまうのを防ぐために、 nコマンドを使います。

     (gdb) s
     0x3b5c  532         if (rquote != def_rquote)
     (gdb) s
     0x3b80  535         lquote = (lq == nil || *lq == '\0') ?  \
     def_lquote : xstrdup(lq);
     (gdb) n
     536         rquote = (rq == nil || *rq == '\0') ? def_rquote\
      : xstrdup(rq);
     (gdb) n
     538         len_lquote = strlen(rquote);

最後に表示された行は、少し妙な感じがします。 2つの変数lquoterquoteを調べて、 本当にそれが、 新たに指定された引用開始文字列、 引用終了文字列であるかどうか確認することができます。 値を調べるにはpprint)コマンドを使用します。

     (gdb) p lquote
     $1 = 0x35d40 "<QUOTE>"
     (gdb) p rquote
     $2 = 0x35d50 "<UNQUOTE>"

lquoterquoteは確かに引用開始文字列、 引用終了文字列のようです。 前後関係を調べるには、 llist)コマンドを使って、 現在停止している行を中心にその前後10行を表示します。

     (gdb) l
     533             xfree(rquote);
     534
     535         lquote = (lq == nil || *lq == '\0') ? def_lquote\
      : xstrdup (lq);
     536         rquote = (rq == nil || *rq == '\0') ? def_rquote\
      : xstrdup (rq);
     537
     538         len_lquote = strlen(rquote);
     539         len_rquote = strlen(lquote);
     540     }
     541
     542     void

len_lquotelen_rquoteに値を設定している行を実行させてから、 それらの値を調べてみましょう。

     (gdb) n
     539         len_rquote = strlen(lquote);
     (gdb) n
     540     }
     (gdb) p len_lquote
     $3 = 9
     (gdb) p len_rquote
     $4 = 7

len_lquotelen_rquoteが、 それぞれlquoterquoteの長さであるとすると、 ここに表示されている値は明らかに誤りです。 pコマンドを使って、 正しい値を設定することができます。 pコマンドによって任意の式の値を表示することができますが、 ここでいう「式」には、 サブルーチンの呼び出しや、 値の割り当ても含まれます。

     (gdb) p len_lquote=strlen(lquote)
     $5 = 7
     (gdb) p len_rquote=strlen(rquote)
     $6 = 9

新しい引用文字列をセットした状態で、 m4の組み込みコマンドdefnを使用しようとすると発生する問題を修正するには、 これだけで十分でしょうか? ccontinue)コマンドを使えば、 m4に処理を継続させて、 実際に問題を発生させていた例を実行することができます。

     (gdb) c
     Continuing.
     
     define(baz,defn(<QUOTE>foo<UNQUOTE>))
     
     baz
     0000

今度はうまくいきました。 新たにセットされた引用文字列は、 デフォルトの引用文字列と同じように機能しました。 問題の原因は、 プログラム内の2箇所のタイプ・ミスで、 長さの設定が正しく行われていないことにあったようです。 EOFを入力して、 m4を終了させましょう。

     C-d
     Program exited normally.

`Program exited normally.'というメッセージは、 GDBが出力したもので、 m4の実行が終了したことを意味しています。 GDBの quitコマンドで、 GDBセッションを終了することができます。

     (gdb) quit