<!-- The FreeBSD Documentation Project The FreeBSD Japanese Documentation Project Original revision: 1.30 $FreeBSD: doc/ja_JP.eucJP/books/handbook/kerneldebug/chapter.sgml,v 1.11 2001/03/11 18:47:12 hrs Exp $ --> <chapter id="kerneldebug"> <title>カーネルデバッグ</title> <para><emphasis>原作 &a.paul; and &a.joerg;</emphasis></para> <para><emphasis>訳: &a.jp.yoshiaki;. 1997 年 3 月 18 日.</emphasis></para> <sect1> <title><command>gdb</command> によるカーネルのクラッシュダンプのデバッグ</title> <para>ここではクラッシュダンプ (crash dump : 訳注 この文脈では kernel 自身 の異常によって停止した場合に出力されるイメージを指します) によるカー ネルデバッグの方法を示します.</para> <para>ここではダンプするための十分なスワップ (swap) の容量があるものとします. もし複数のスワップパーティションを持ち, 最初のパーティションがダンプ を保持するのに十分な大きさを持たない場合は 別のダンプデバイスを使うよ うに (<literal>config kernel</literal> 行で) カーネルのコンフィグをおこなうか, &man.dumpon.8; コマンドを使って別のデバイスを示すことができます. &man.dumpon.8; を使うもっともよい方法は変数 <literal>dumpdev</literal> を <filename>/etc/rc.conf</filename> で設定することです. 一般的には <filename>/etc/fstab</filename> で設定されているスワップデバイスが 使われるでしょう. スワップに使えないデバイスへのダンプ, 例えばテープへのダンプは現在サポートさ れていません. カーネルのコンフィグは <command>config <option>-g</option></command> によって行ってください. <link linkend="kernelconfig"> FreeBSD カーネルのコンフィグレーション </link> には FreeBSD のカーネルの設定の詳細がありますので 参照してください.</para> <para>&man.dumpon.8; コマンドを使ってどこへダンプするか カーネルに伝えてください (&man.swapon.8; によってそのパーティションが スワップとして設定された 後でなければならないことに注意してください). これは普通は <filename>/etc/rc.conf</filename> や <filename>/etc/rc</filename> で設定されます. あるいは 別の方法としてカーネルコンフィグレーションファイルの <literal>config</literal> 行の <literal>dump</literal> 節 で ダンプデバイスをハードコードすることができます. この方法はあまりよくは ありません. カーネルがブート時に crash する場合のクラッシュダンプを取り たい時だけ使うべきです.</para> <note> <para>以下では <command>gdb</command>という用語は <command>gdb</command> を<quote>カーネルデバッグモード</quote>で動かしていることを意味します. <command>gdb</command> を <option>-k</option>オプションをつけて起動することで, このモードになります. カーネルデバッグモードでは, プロンプトが <prompt>(kgdb)</prompt> に変わります.</para> </note> <tip> <para>FreeBSD 3 およびそれ以前のシステムを使っているなら, 巨大なデバッグカーネルをそのままインストールするのではなく strip されたデバッグ用カーネルをつくるべきでしょう.</para> <screen>&prompt.root; <userinput>cp kernel kernel.debug</userinput> &prompt.root; <userinput>strip -g kernel</userinput></screen> <para>この手順は必須ではありませんが, ぜひ行なうことをおすすめします (FreeBSD 4 およびそれ以降では, カーネルの <command>make</command> の段階で自動的にこれが行なわれます). 自動的に, あるいは上のコマンドを手動で実行してカーネルが strip されたら, 普通に <command>make install</command> と実行し, カーネルをインストールして構いません.</para> <para>FreeBSD の古いリリース (3.1 を含まない以前のもの) は, 標準で a.out カーネルを使っていることに注意してください. これはシンボルテーブルが常に物理メモリ上に存在することを要求するため, strip されていないデバッグカーネルに含まれる大きなシンボルテーブルは非常に無駄になります. ELF カーネルを使った FreeBSD の最近のリリースでは, そのような問題がなくなりました.</para> </tip> <para>カーネルを作った時にそのコピーを <filename>kernel.debug</filename> という名前で作りましょう. また, オリジナルに対して <command>strip -g</command>を実行します. オリジナルを普通にインストールします. また strip していないカーネル も同様にインストールすることができますが, シンボルテーブルの参照時間 がいくつかのプログラムでは劇的に増加するでしょう. また, カーネル全体 はブート時に読み込まれ スワップアウトされないため数メガバイトの物理メ モリが無駄になります.</para> <para>例えばブートプロンプトで 新しいカーネルの名前をタイプすることによって, 新しいカーネルをテストした場合で, 再びシステムを動かすのに別のカーネ ルで立ち上げることが必要な場合はブートプロンプトで <option>-s</option>フラグ を使いシングルユーザの状態にしてください. そして以下のような操作をおこな います.</para> <screen>&prompt.root; <userinput>fsck -p</userinput> &prompt.root; <userinput>mount -a -t ufs</userinput> # /var/crash 用のファイルシステムを書き込み可能にする &prompt.root; <userinput>savecore -N /kernel.panicked /var/crash</userinput> &prompt.root; <userinput>exit</userinput> # ...マルチユーザモードへ移行</screen> <para>ここに示した &man.savecore.8; は (現在動いているものとは別の) カーネルのシンボル名の抽出をおこなうために使っています. 抽出はデフォルトで は現在動いているカーネルに対しておこなわれ, クラッシュダンプとカーネルシンボ ルのくい違いのためにまったく何もしません (訳注:そのためにオプション で実際にダンプをおこしたカーネルを指定します).</para> <para>クラッシュダンプの起きた後に <filename>/sys/compile/WHATEVER</filename>へ行き <command>gdb</command>を動かします. <command>gdb</command> より次のようにします.</para> <screen><userinput>symbol-file kernel.debug</userinput> <userinput>exec-file /var/crash/kernel.0</userinput> <userinput>core-file /var/crash/vmcore.0</userinput></screen> <para>こうすると, クラッシュダンプを使ってカーネルソースを他のプログラムと同様に デバッグすることができます.</para> <para>次に <command>gdb</command> での手順のセッションのログを示します. 長い行は読 みやすくするために改行しました. また, 参照のために行番号を入れてあり ます. ただし, これは実際の pcvtコンソールドライバの開発中の実際のエ ラーのトレースです.</para> <screen> 1:Script started on Fri Dec 30 23:15:22 1994 2:&prompt.root; <userinput>cd /sys/compile/URIAH</userinput> 3:&prompt.root; <userinput>gdb -k kernel /var/crash/vmcore.1 </userinput> 4:Reading symbol data from /usr/src/sys/compile/URIAH/kernel ...done. 5:IdlePTD 1f3000 6:panic: because you said to! 7:current pcb at 1e3f70 8:Reading in symbols for ../../i386/i386/machdep.c...done. 9:<prompt>(kgdb)</prompt> <userinput>where</userinput> 10:#0 boot (arghowto=256) (../../i386/i386/machdep.c line 767) 11:#1 0xf0115159 in panic () 12:#2 0xf01955bd in diediedie () (../../i386/i386/machdep.c line 698) 13:#3 0xf010185e in db_fncall () 14:#4 0xf0101586 in db_command (-266509132, -266509516, -267381073) 15:#5 0xf0101711 in db_command_loop () 16:#6 0xf01040a0 in db_trap () 17:#7 0xf0192976 in kdb_trap (12, 0, -272630436, -266743723) 18:#8 0xf019d2eb in trap_fatal (...) 19:#9 0xf019ce60 in trap_pfault (...) 20:#10 0xf019cb2f in trap (...) 21:#11 0xf01932a1 in exception:calltrap () 22:#12 0xf0191503 in cnopen (...) 23:#13 0xf0132c34 in spec_open () 24:#14 0xf012d014 in vn_open () 25:#15 0xf012a183 in open () 26:#16 0xf019d4eb in syscall (...) 27:<prompt>(kgdb)</prompt> <userinput>up 10</userinput> 28:Reading in symbols for ../../i386/i386/trap.c...done. 29:#10 0xf019cb2f in trap (frame={tf_es = -260440048, tf_ds = 16, tf_\ 30:edi = 3072, tf_esi = -266445372, tf_ebp = -272630356, tf_isp = -27\ 31:2630396, tf_ebx = -266427884, tf_edx = 12, tf_ecx = -266427884, tf\ 32:_eax = 64772224, tf_trapno = 12, tf_err = -272695296, tf_eip = -26\ 33:6672343, tf_cs = -266469368, tf_eflags = 66066, tf_esp = 3072, tf_\ 34:ss = -266427884}) (../../i386/i386/trap.c line 283) 35:283 (void) trap_pfault(&frame, FALSE); 36:<prompt>(kgdb)</prompt> <userinput>frame frame->tf_ebp frame->tf_eip</userinput> 37:Reading in symbols for ../../i386/isa/pcvt/pcvt_drv.c...done. 38:#0 0xf01ae729 in pcopen (dev=3072, flag=3, mode=8192, p=(struct p\ 39:roc *) 0xf07c0c00) (../../i386/isa/pcvt/pcvt_drv.c line 403) 40:403 return ((*linesw[tp->t_line].l_open)(dev, tp)); 41:<prompt>(kgdb)</prompt> <userinput>list</userinput> 42:398 43:399 tp->t_state |= TS_CARR_ON; 44:400 tp->t_cflag |= CLOCAL; /* cannot be a modem (:-) */ 45:401 46:402 #if PCVT_NETBSD || (PCVT_FREEBSD >= 200) 47:403 return ((*linesw[tp->t_line].l_open)(dev, tp)); 48:404 #else 49:405 return ((*linesw[tp->t_line].l_open)(dev, tp, flag)); 50:406 #endif /* PCVT_NETBSD || (PCVT_FREEBSD >= 200) */ 51:407 } 52:<prompt>(kgdb)</prompt> <userinput>print tp</userinput> 53:Reading in symbols for ../../i386/i386/cons.c...done. 54:$1 = (struct tty *) 0x1bae 55:<prompt>(kgdb)</prompt> <userinput>print tp->t_line</userinput> 56:$2 = 1767990816 57:<prompt>(kgdb)</prompt> <userinput>up</userinput> 58:#1 0xf0191503 in cnopen (dev=0x00000000, flag=3, mode=8192, p=(st\ 59:ruct proc *) 0xf07c0c00) (../../i386/i386/cons.c line 126) 60: return ((*cdevsw[major(dev)].d_open)(dev, flag, mode, p)); 61:<prompt>(kgdb)</prompt> <userinput>up</userinput> 62:#2 0xf0132c34 in spec_open () 63:<prompt>(kgdb)</prompt> <userinput>up</userinput> 64:#3 0xf012d014 in vn_open () 65:<prompt>(kgdb)</prompt> <userinput>up</userinput> 66:#4 0xf012a183 in open () 67:<prompt>(kgdb)</prompt> <userinput>up</userinput> 68:#5 0xf019d4eb in syscall (frame={tf_es = 39, tf_ds = 39, tf_edi =\ 69: 2158592, tf_esi = 0, tf_ebp = -272638436, tf_isp = -272629788, tf\ 70:_ebx = 7086, tf_edx = 1, tf_ecx = 0, tf_eax = 5, tf_trapno = 582, \ 71:tf_err = 582, tf_eip = 75749, tf_cs = 31, tf_eflags = 582, tf_esp \ 72:= -272638456, tf_ss = 39}) (../../i386/i386/trap.c line 673) 73:673 error = (*callp->sy_call)(p, args, rval); 74:<prompt>(kgdb)</prompt> <userinput>up</userinput> 75:Initial frame selected; you cannot go up. 76:<prompt>(kgdb)</prompt> <userinput>quit</userinput> 77:&prompt.root; <userinput>exit</userinput> 78:exit 79: 80:Script done on Fri Dec 30 23:18:04 1994</screen> <para>上の出力についてのコメントをします.</para> <variablelist> <varlistentry><term>line 6:</term> <listitem> <para>これは DDB (後述) からのダンプです. このため <quote>because you said to!</quote> という panicコメントがつき, ページフォルトのト ラップによって DDBに入ったことが原因の, やや長いスタックトレー スがあります.</para> </listitem> </varlistentry> <varlistentry><term>line 20:</term> <listitem> <para>スタックトレースでのこれは <function>trap()</function>関数の位置で す.</para> </listitem> </varlistentry> <varlistentry><term>line 36:</term> <listitem> <para>新しいスタックフレームの使用を指定しています. これは現 在は必要ありません. trapの場合ではスタックフレームは正 しい場所を指していると考えられます. (私は新しいコアダンプ を持っていません. 私のカーネルは長い間 panicを起こしていま せん.) ソースコードの 403 行を見ると, <quote>tp</quote> ポインタのアク セスが失敗しているか配列のアクセスが範囲外である可能性が高 いことがわかります.</para> </listitem> </varlistentry> <varlistentry><term>line 52:</term> <listitem> <para>怪しいポインタですが, アクセスは正常におこなえました.</para> </listitem> </varlistentry> <varlistentry><term>line 56:</term> <listitem> <para>ところが, 明らかにポインタはゴミを指しています. これで エラーを見つけました! (ここのコードの部分からはよくわかり ませんが, <literal>tp->t_line</literal>はコンソールデバイスの規定 する行を参照しているので, もっと小さな整数でなければなりませ ん. )</para> </listitem> </varlistentry> </variablelist> </sect1> <sect1> <title>DDD によるクラッシュダンプのデバッグ</title> <para>カーネルのクラッシュダンプは <command>ddd</command> のようなグラフィカルなデバッガで調べることもできます. 通常はコマンドラインで <option>-k</option> オプションをつけて <command>ddd</command> を起動します. たとえば:</para> <screen>&prompt.root; <userinput>ddd -k /var/crash/kernel.0 /var/crash/vmcore.0</userinput></screen> <para>クラッシュダンプを <command>ddd</command> のグラフィカルなインターフェースを使って 見ることができます.</para> </sect1> <sect1> <title>突然ダンプした場合の解析</title> <para>カーネルが予想もしない時にコアダンプして <command>config -g</command> を行ってコンパイルされていなかった場合にはどうしたら よいでしょう. すべてが失われるわけではありません. パニックを起こさないでください.</para> <para>もちろん, クラッシュダンプを使えるようにする必要があります. 使い方は前述の部分を見てください.</para> <para>カーネルのコンパイルディレクトリ (<filename>/usr/src/sys/<replaceable>arch</replaceable>/conf</filename>) で, 設定ファイルを編集します. 以下の行のコメントを外します (行が存在しなければ追加します):</para> <programlisting>makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols</programlisting> <para>カーネルを再構築しましょう. Makefileのタイムスタンプの変更により, 例えば <!-- kuriyama - should be filename --> <emphasis remap=tt> trap.o </emphasis>などのいくつかの他のオブジェクトファイルも作り直さ れます. 少しの幸運があれば, <option>-g</option>オプションが追加されても作ら れるコードは変更されず, いくらかのデバッグシンボル以外には 問題を 起こしたコードとそっくりな新しいカーネルを手に入れることが できます. 少なくとも &man.size.1; コマンドで古い方と新しい方のサイズを比較すべ きです. これが食い違っていれば, 多分あきらめなければならないでしょう.</para> <para>ダンプを使って前述のように動かして調べます. デバッグシンボルは 必ずしも十分ではありません. 上の例ではスタックトレースでいくつかの関 数の行番号や引数リストが表示されないかもしれません. もしより多くのデ バッグシンボルが必要であれば,十分になるまで 適切なオブジェクトファイ ルを消して (makeして) <command>gdb <option>-k</option></command>セッションを繰り返してください.</para> <para>これは必ずしもうまく動くと保証はできません. しかしほとんどの場合でう まくいくでしょう.</para> </sect1> <sect1> <title>DDBを使ったオンラインカーネルデバッグ</title> <para><command>gdb <option>-k</option></command> は非常に高レベルのユーザインタフェースを提 供するオフラインデバッガですが, いくつかのことはできません. (できないことの中で) 極めて重要なことはカーネルコードへのブレークポイ ントの設定とシングルステップ実行です.</para> <para>カーネルの低レベルデバッグが必要であれば, DDBと呼ばれる on-lineデバッ ガが使えます. ブレークポイントの設定, シングルステップのカーネルの実 行, 変数の検査と変更などができます. ただし,これはカーネルのソースファ イルにアクセスすることはできません. <command>gdb</command>のようにすべてのデ バッグ情報にはアクセスできず, globalと staticのシンボルにアクセス することができるだけです.</para> <para>カーネルに DDB を含めるためにはコンフィグファイルに次のようなオプショ ンを加えて,</para> <programlisting>options DDB</programlisting> <para>再構築をおこないます. ( FreeBSDのカーネルの設定の詳細については<link linkend="kernelconfig"> FreeBSD カーネルのコンフィグレーション</link>を参照してくださ い.</para> <note> <para>もしブートブロックが古いバージョンですと, デバッガのシンボルが完 全にはロードされないかもしれませんので注意してください. DDB シンボル がロードされるようにブートブロックを 最新の物にアップデートしてくださ い)</para> </note> <para>DDB カーネルの実行において, DDBに入るいくつかの方法があります. 最初 の, 最も早い方法はブートプロンプトが出ている時に <option>-d</option>のブート フラグをタイプすることです. カーネルはデバッグモードで起動し, デバ イスのプローブ以前に DDBに入ります. したがって, デバイスのプローブ/初期 設定ファンクションのデバッグができます.</para> <para>2つ目のシナリオはキーボードのホットキーで, 通常は Ctrl-Alt-ESCです. syscons ではホットキーは再設定することができ, 配付されているいくつかの キーマッピングでは別のキーに 再設定されていますので確認しておいてください. シリアルラインの BREAKを使って シリアルコンソールから DDBへ入ることを可 能にするオプションもあります (カーネルコンフィグレーションファイルの <literal>options BREAK_TO_DEBUGGER</literal>). これは 多くのつまらないシリ アルアダプタが, 例えばケーブルを引き抜いた時に BREAK状態を意味もなく 作り出してしまうのでデフォルトでは無効になっています.</para> <para>3つ目は, DDB を使うようになっているカーネルがパニック状態になると DDB へ入るというものです. このため, 無人運転するマシンのカーネルにDDBを 入れるのは賢明ではありません.</para> <para>DDB のコマンドはおおまかには <command>gdb</command> のいくつかのコマンドと似て います. おそらく最初にブレークポイントを 設定する必要があるでしょう.</para> <screen><userinput>b function-name</userinput> <userinput>b address</userinput></screen> <para>数値はデフォルトでは16進数で, シンボル名とはまったく異なります. 16進数で <literal>a-f</literal> の文字で始まる場合は, 先頭に <literal>0x</literal> をつける必要があります(それ以外の数字の場合はどちらでもか まいません). <literal>function-name + 0x103</literal>のような単純な式を使うこ とができます.</para> <para>割り込みされたカーネルから処理を続行するためには, </para> <screen><userinput>c</userinput></screen> <para>とタイプするだけです. スタックのトレースには</para> <screen><userinput>trace</userinput></screen> <para>とします.</para> <note> <para>DDB にホットキーで入った場合は, カーネルはその (ホットキーの) 割り込み の処理を行っていますのでスタックトレースは あまり役にたたないことに注 意してください.</para> </note> <para>ブレークポイントを削除したい場合は,</para> <screen><userinput>del</userinput> <userinput>del address-expression</userinput></screen> <para>とします. 最初の形式はブレークポイントにヒットしたすぐ後で使うことが でき, 現在のブレークポイントを削除します. 2番目の形式では任意のブレー クポイントを削除することができますが, 次の形式で得られるような正確な アドレスを与えることが必要です.</para> <screen><userinput>show b</userinput></screen> <para>カーネルをシングルステップ実行させるには</para> <screen><userinput>s</userinput></screen> <para>としてみてください. これは関数呼出し先までステップ実行 (step into function) するでしょう. 次のステートメントが終了するまでのDDBトレースは</para> <screen><userinput>n</userinput></screen> <para>によっておこなうことができます.</para> <note> <para>これは <command>gdb</command> の <command>next</command> 命令とは異ります. <command>gdb</command>の <command>finish</command>命令と似ています.</para> </note> <para>メモリ上のデータを調べるには (例として) 次のようにします. <screen><userinput>x/wx 0xf0133fe0,40</userinput> <userinput>x/hd db_symtab_space</userinput> <userinput>x/bc termbuf,10</userinput> <userinput>x/s stringbuf</userinput></screen> word/halfword/byte 単位でアクセスをおこない, hex (16進) /dec (10進) / char (文字) /string (文字列) で表示します. カンマの後ろの数字はオブジェク トカウントです. 次の 0x10個の要素を表示するには, 単純に</para> <screen><userinput>x ,10</userinput></screen> <para>とします. 同様に次のように使うことができます. <screen><userinput>x/ia foofunc,10</userinput></screen> <function>foofunc</function> の最初の 0x10個の命令語をディスアセンブルし, <function>foofunc</function> の先頭からのオフセットとともに表示します.</para> <para>メモリの内容を変更するには writeコマンドを使います.</para> <screen><userinput>w/b termbuf 0xa 0xb 0</userinput> <userinput>w/w 0xf0010030 0 0</userinput></screen> <para>コマンドモディファイアの (<literal>b</literal>/<literal>h</literal>/<literal>w</literal>) はデータを 書くサイズを定義し, これに続く最初の式は書き込むアドレス, 残りがこれ に続く連続するメモリアドレスに書き込まれるデータになります. </para> <para>現在のレジスタ群の内容を知りたい場合は</para> <screen><userinput>show reg</userinput></screen> <para>とします. また, 単一のレジスタの値を表示するには, 例えば <screen><userinput>p $eax</userinput></screen> とします. また値の変更は</para> <screen><userinput>set $eax new-value</userinput></screen> <para>とします.</para> <para>DDBからカーネルの関数を呼び出す必要がある場合は, 単に</para> <screen><userinput>call func(arg1, arg2, ...)</userinput></screen> <para>とします. return 値が出力されます.</para> <para>動いているプロセスの &man.ps.1; スタイルの概要は</para> <screen><userinput>ps</userinput></screen> <para>です.</para> <para>カーネルの失敗の原因の調査が終わったらリブートすべきです. それまでの 不具合によりカーネルのすべての部分が期待するような 動作をしているわけ ではないということを忘れないでください. 以下のうちいずれかの方法でシ ステムのシャットダウンおよびリブートを行ってください.</para> <screen><userinput>panic</userinput></screen> <para>カーネルをコアダンプしてリブートしますので, 後で <command>gdb</command>によってコアの高 レベル解析をすることができます. このコマンドは通常, 一度 <command>continue</command>命令を使った後に 使うことになるでしょう.</para> <screen><userinput>call boot(0)</userinput></screen> <para> は動いているシステムを `clean' に shut downするよい方法です. すべて のディスクを <function>sync()</function>して最後にリブートします. ディスクとカー ネルのファイルシステムインタフェースが破損していない限り, ほぼ完全 に `clean'にシャットダウンするよい方法でしょう.</para> <!-- kuriyama - ldquo? --> <screen><userinput>call cpu_reset()</userinput></screen> <para>は大惨事を防ぐための最後の手段で 「赤い大きなボタン」 を押すのとほとんど 同じです.(訳注: リセットボタンを押すのとほぼ同じであるという意味です)</para> <para>短いコマンドの要約は</para> <screen><userinput>help</userinput></screen> <para>をタイプします. ただし, デバッグセッションのために &man.ddb.4; の マニュアルページのプリントアウトを用意しておくことを 強くお奨めします. カーネルのシングルステップ中にオンラインマニュアルを 読むことは難しい ということを覚えておいてください.</para> </sect1> <sect1> <title>リモート GDB を使ったオンラインカーネルデバッグ</title> <para>この機能は FreeBSD 2.2 からサポートされました. これは本当にすばらし い機能です.</para> <para>GDB はすでにかなり以前より <emphasis>リモートデバッグ</emphasis> をサポートしてい ます. これはシリアル回線を使い非常に単純なプロトコルで行ないます. もちろん, この方法では今までに示した方法とは違い, 2台のマシンが必 要になります. 1台はデバッグ環境のためのホストで, すべてのソースとす べてのシンボルを含んだバイナリのコピーを持っています. もう 1台は ターゲットマシンで, 同一のカーネルのコピー (ただしデバッグ情報は 取り除いてあるもの) を単に実行するためのものです.</para> <para>この場合, カーネルのコンフィグレーションは <command>config -g</command> で行な い, <option>DDB</option> を含めなくてはなりません. そうして通常通りコンパイルし ます. こうして作ったバイナリファイルはデバッグ情報のために非常に大き くなります. このカーネルをターゲットマシンにコピーして <command>strip -x</command> でデバッグシンボルを取り除きます. そして <option>-d</option> ブートオプションを使いブートします. sio デバイスにフラグ 0x80 が設定されているターゲットマシンの シリアル回線を, デバッグホストのいずれかのシリアル回線に つないでください. それからデバッグ(訳注:ホスト)マシン上で, ターゲットとなって いるカーネルのコンパイルディレクトリで <command>gdb</command> を起動します:</para> <screen>&prompt.user; <userinput>gdb -k kernel</userinput> 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.16 (i386-unknown-freebsd), Copyright 1996 Free Software Foundation, Inc... <prompt>(kgdb)</prompt> </screen> <para>リモートデバッグセッションの初期化 (1番目のシリアルポートを使用する ことの設定) を以下のように行ないます.</para> <screen><prompt>(kgdb)</prompt> <userinput>target remote /dev/cuaa0</userinput></screen> <para>次にターゲットマシン (デバイスのプローブ直前で DDB に入っています) で次のように入力します:</para> <screen>Debugger("Boot flags requested debugger") Stopped at Debugger+0x35: movb $0, edata+0x51bc <prompt>db></prompt> <userinput>gdb</userinput></screen> <para>DDB は次のような出力を返すでしょう. </para> <screen>Next trap will enter GDB remote protocol mode</screen> <para><command>gdb</command>と入力するたびに リモート GDB とローカル DDB が交互に切り替わ ります. トラップをすぐに起こすために単に ``s'' (step) と入力して下 さい. そうするとホストの GDB はターゲットのカーネルの制御を行なうよ うになります.</para> <screen>Remote debugging using /dev/cuaa0 Debugger (msg=0xf01b0383 "Boot flags requested debugger") at ../../i386/i386/db_interface.c:257 <prompt>(kgdb)</prompt></screen> <para>このセッションではソースコードへのフルアクセスや Emacs の window 上 の gud-mode (これは別の Emacs window に自動的にソースコードを表示し ます) で動かすなど, 通常の GDB セッションでできることのほとんどのこ とができます.</para> </sect1> <sect1> <title>GDB を使ったローダブルモジュールのデバッグ</title> <para>モジュール内部で発生する panic のデバッグや, 動的モジュールを使っているマシンを GDB でリモートデバッグしている場合, モジュールのシンボル情報を得る方法を GDB に伝える必要があります.</para> <para>まず, モジュールをデバッグ情報を含めて構築する必要があります.</para> <screen>&prompt.root; <userinput>cd /sys/modules/linux</userinput> &prompt.root; <userinput>make clean; make COPTS=-g</userinput></screen> <para>リモート GDB を使っている場合は, ターゲットマシンで <command>kldstat</command> を実行することで モジュールがどこにロードされたか調べることが可能です.</para> <screen>&prompt.root; <userinput>kldstat</userinput> Id Refs Address Size Name 1 4 0xc0100000 1c1678 kernel 2 1 0xc0a9e000 6000 linprocfs.ko 3 1 0xc0ad7000 2000 warp_saver.ko 4 1 0xc0adc000 11000 linux.ko </screen> <para>クラッシュダンプをデバッグしている場合, <literal>linker_files->tqh_first</literal> から始まる <literal>linker_files</literal> リストを調べ, 探している <literal>filename</literal> が見つかるまで <literal>link.tqe_next</literal> ポインタをたどる必要があります. エントリ中の <literal>address</literal> メンバが, モジュールのロードアドレスです.</para> <para>次に, モジュール内の text セクションのオフセットを調べます.</para> <screen>&prompt.root; <userinput>objdump --section-headers /sys/modules/linux/linux.ko | grep text</userinput> 3 .rel.text 000016e0 000038e0 000038e0 000038e0 2**2 10 .text 00007f34 000062d0 000062d0 000062d0 2**2</screen> <para>必要なのは <literal>.text</literal> セクションで, 上の例では 10 にあたります. その 4 番目の 16 進数フィールド (全部で 6 フィールドあります) が, ファイル中の text セクションのオフセットになります. そして, このオフセットをモジュールのロードアドレスに加算すると モジュールのコードの再配置アドレスを求めることができます. この例では 0xc0adc000 + 0x62d0 = 0xc0ae22d0 です. GDB コマンド <command>add-symbol-file</command> を使い, 得られたモジュールの情報をデバッガに伝えるには, 次のようにします.</para> <screen><prompt>(kgdb)</prompt> <userinput>add-symbol-file /sys/modules/linux/linux.ko 0xc0ae22d0</userinput> add symbol table from file "/sys/modules/linux/linux.ko" at text_addr = 0xc0ae22d0? (y or n) <userinput>y</userinput> Reading symbols from /sys/modules/linux/linux.ko...done. <prompt>(kgdb)</prompt></screen> <para>これで, モジュール内のすべてのシンボルにアクセスできるようになります.</para> </sect1> <sect1> <title>コンソールドライバのデバッグ</title> <para>DDBを動かすためにはコンソールドライバが必要ですから, コンソールドラ イバ自身に不具合のある場合は複雑になります. シリアルコンソールを利 用する方法 (ブートブロックを変更するか <prompt>Boot:</prompt>プロンプトで <option>-h</option>と入力する) を思い出してください. そして標準ター ミナルを最初のシリアルポートに設定します. DDBは, もちろんシリアルコ ンソールを含むいずれの コンソールドライバの設定でも動作します.</para> </sect1> </chapter> <!-- Local Variables: mode: sgml sgml-declaration: "../chapter.decl" sgml-indent-data: t sgml-omittag: nil sgml-always-quote-attributes: t sgml-parent-document: ("../book.sgml" "part" "chapter") End: -->