注: 2.4.0-test6以上のカーネルをもったUMLを対象にしてます。
UMLは、ホストPC(UMLを走らせるLinux)からみれば、単なるプロセスです。 よって、普通のアプリをデバッグするのと同じ感覚で gdb での カーネルのデバッグが可能です。 もし、若干異なる点があるとすれば、それは、カーネルスレッドが gdb が ptrace するより前に ptrace されていることです。 この点に関しては、解決策が用意されています。
まず、ソースからUMLのLinuxカーネルをビルドしましょう。 ここ Compiling the kernel and modules 参照。 この二つのオプション CONFIG_DEBUGSYM CONFIG_PT_PROXY を有効にすることをお忘れなく。 前者のオプションにより -g 付き (訳注:gdb でのデバッグを可能にするオプションだっけ?) でのカーネルのコンパイルが指定され、後者のオプションにより ptrace proxy が 可能になり、これにより、gdb が UML に接続できます。
まず、UML 立ち上げの際のコマンドラインに、'debug' を指定して、 UMLをgdb のコントロール下において立ち上げてください。 (訳注:なお、普通にダウンロードしたRPMでは無理です。 上の記載を参考にデバッグオンで コンパイルしなおしてください)。 UML内部のカーネルの先頭にある、start_kernel シンボルのところで 停止するはずです。あとは、'next' / 'step' / 'cont'あたり で適当に。
実行例が ここに。 ブレークポイントをスケジューラと割り込みハンドラに仕掛けています。
gdb の中で UML を走らせなくても、あとで gdb を UMLに接続することも 可能です。UML立ち上げの瞬間に、このような
tracing thread pid = 20093
出力があるはずなので、そこに向かってSIGUSR1 を送ってください。
kill -USR1 20093
これで接続できるはずです。
mconsoleありでUMLをコンパイルしていれば、そこからgdb をスタートできます。
(mconsole) config gdb=xterm
これで、xterm を走らせ、そのなかで gdb が動いています。
gdb は、動的にロードされるコードのデバッグも可能ですが、これにより、 カーネルモジュールのデバッグも可能になります。
gdb によるデバッグを可能にした形で、 UMLを立ち上げ、その中で insmod / modprobe でモジュールをロードします。 gdb でこのコマンドを実行すると、
(gdb) p module_list
ロードされたタイミングが現在に近いものから順に、ロードされている
モジュールが表示されます。
このモジュールのリストの取得はこれでいいはずですが、
場合によってはリンクをたどって探しにいく必要がある場合もあります。
(ここ、ちょっと訳に自信なし、カーネルモジュールのUMLでのデバッグはやったことないので)。リンクをたどって探したモジュール管理構造体を、gdb の場合なら、無理やり module.size_of_struct ( 2.4.10のカーネルなら 96 (0x60)) です)に加えることができます。
printf "%#x\n", (int)module_list module_list->size_of_struct
カーネルのバージョンにより、モジュールの管理用のリスト構造のアドレスは
異なっているので、(2.4.0以前なら module.size_of_struct + 4),
(gdb) add-symbol-file /path/to/module/on/host that_address
こんな風にして、シンボルテーブルを読み込む必要性があるのかも。
どうも、シンボルテーブルの構造が怪しくて、なんかうまくデバッグできないと おもったら、こんな方法でチェックしてください。
init = 0x588066b0 <init_hostfs>, cleanup = 0x588066c0 <exit_hostfs>
オフセットなしでシンボルネームを参照します。
もし、名前が正しいのに、オフセットが狂ってる場合は、
正しいオフセットが表示されると思います。
それをもとに、アドレスを修正します。
新しいバージョンのモジュールをロードする場合は、 gdb に対し、古いバージョンのモジュールに対するシンボルテーブルを 破棄するようにさせます。これに関しては、Jeff Dike が思いついた 唯一の方法はすべてのシンボルテーブルをはきさせることです。
(gdb) symbol-file
そして、カーネルのバイナリから、新しいシンボルテーブルをロードします。
(gdb) symbol-file /path/to/kernel
そして、いままで説明した手段をもう一度行い、正しいシンボルテーブル情報を
構築します。この際、いままでのブレークポイントは無効になるので、
必要なら再設定してください。
gdb 以外のデバッガも使用可能です。 2001/4/17 以降のCVSのソースツリーには含まれています。(これは、Alan Cox に送られ、 ac ツリーに含まれています)。
これは、gdb を直接使うのではなく、gdb を内部にもったデバッガ(正確に言えば、デバッガのユーザインターフェース)を使う場合に有効です。たとえば、emacs の gdb モードや、ddd を使うときなどに有効です。また、gdb 以外のデバッガでUML内部のLinuxカーネルをデバッグする際も有効です。ここで、strace を使って UML 内部のカーネルをデバッグする様子をお見せします。
まず、UMLに対し、デバッガのpidを渡してください。 'gdb-pid=<pid>'とか。'debug' スイッチもお忘れなく。
gdb のばあい、このあと、「att 1」でUMLに接続してください。
それ以外の場合も、同じようなコマンドがあると思うので、それを使ってください。
strace の場合はこんな風。
sh -c 'echo pid=$$; echo -n hit return; read x; exec strace -p 1 -o
strace.out'
strace ./linux
これは、UML内部のリナックスのカーネルをトレースする(これが望みですよね)
のではなく、UMLのメインスレッド
(訳注:UML全体を統括するコントローラのような存在)
をトレースします。これは、仮想マシンがどのようにして作られているかを
知りたい場合は有用ですが、カーネルのデバッグには無意味です。