Remote Kernel Debugging via Qemu2008年03月11日 11時13分18秒

Qemu を使うと、シリアル や null modem などの設備や知識も無く簡単に通称リモートカーネルデバッグと言われるものを出来る。

カーネルに ddb を組み込むと、動作中にデバッガに落として、gdb と同等の事が出来る。しかし、ローカルで行うと、普段使っている gdb の様にはいかない。普通の gdb はカーネルのサポートを通して動く。その為、ソースファイルの表示などの動作は簡単だ。カーネルをデバッグしている時に、カーネルが gdb の為にソースファイルを急に読みにいったら、それこそ実行パスと順序が全然変わってしまう。

それを二台に分けて作業する事で、実行順序などを破壊せずに、普段の gdb の要領でデバッグを行える様にするのがリモートデバッグだ。対象のカーネル、つまりターゲット機は各ステップの実行を行う。そして、ホスト機は gdb を動かす末端になる。こちらでは、カーネルは普段通りに動いているのでソースファイルの表示や、C 言語での行単位での実行を制御できる。

前置きが長くなったが、実際に行うのは簡単だ。


% qemu -s -hda FreeBSD.img

qemu を -s オプションを付けて起動する。man qemu では一切触れられていないオプションだが、qemu -help などでこのオプションが見つけられる。デバッグ用のオプションだ。これで、外から gdb の接続を受けられる様になる。

% cd /usr/obj/usr/src/sys/GENERIC
% gdb kernel.debug 
GNU gdb 6.1.1 [FreeBSD]
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or 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.
This GDB was configured as "i386-marcel-freebsd"...
Ready to go.  Enter 'tr' to connect to the remote target
with /dev/cuad0, 'tr /dev/cuad1' to connect to a different port
or 'trf portno' to connect to the remote target with the firewire
interface.  portno defaults to 5556.

Type 'getsyms' after connection to load kld symbols.

If you're debugging a local system, you can use 'kldsyms' instead
to load the kld symbols.  That's a less obnoxious interface.
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
During symbol reading, unsupported tag: 'DW_TAG_const_type'.
cpu_idle_default () at /usr/src/sys/i386/i386/machdep.c:1159
1159    }
warning: Unable to find dynamic linker breakpoint function.
GDB will be unable to debug shared library initializers
and track explicitly loaded dynamic code.
warning: shared library handler failed to enable breakpoint
(gdb) where
#0  cpu_idle_default () at /usr/src/sys/i386/i386/machdep.c:1159
During symbol reading, Incomplete CFI data; unspecified registers at 0xc0a4b023.
#1  0xc0a4d068 in cpu_idle () at /usr/src/sys/i386/i386/machdep.c:1181
#2  0xc077c734 in sched_idletd (dummy=0x0) at /usr/src/sys/kern/sched_ule.c:2597
#3  0xc073db79 in fork_exit (callout=0xc077c470 <sched_idletd>, arg=0x0, frame=0xcbd3bd38)
    at /usr/src/sys/kern/kern_fork.c:781
#4  0xc0a41d70 in fork_trampoline () at /usr/src/sys/i386/i386/exception.s:205
(gdb) continue
Continuing.

その後、gdb kernel.debug で起動して、target remote localhost:1234 で Qemu に接続する。ここで、1234 は qemu がデバッガを受け付けているポート番号になる。後は、普通に gdb を使うように、break などを仕掛けて、continue でカーネルの実行を再開する。