NetBSDのkgdbをQEMUでお手軽に試す


NetBSD ドキュメンテーション: GDB を使い NetBSD カーネルをデバッグする HOWTO をQEMUでサクッと試すためのHowtoです。

前提

  • 先に QEMUのgdbserver機能でNetBSD kernelをデバッグする を終えて下さい。こちらの方が難易度(ハマり度)が低いですし、こちらに載っているビルド方法などの手順は本記事でも必要になります。
  • Linux (Ubuntu 17.10), macOS (High Sierra) で検証済みです。Windowsや他OSでの動作報告お待ちしております!

注意事項

NetBSD-currentはkgdbが壊れていて動作しません。リリース版のソースか、2017/08/15 08:35:56 より前のソースを使って下さい。(currentではこのコミット以外にも複数の問題があるようです)

実用面では、QEMUでkgdbを使う意味はありません[1]。QEMU gdbserverに勝るメリットが無いためです[2]。このHowtoの目的は、kgdbの正常な挙動を理解することです。その知識は物理マシンでkgdbを使う際に役立つと思います(実際自分がそうだった)。逆に既に物理マシンで問題なくkgdbを使えている方には不要なHowtoでしょう。

(注釈 [1]: ただしkgdb自体のデバッグをするためにQEMU上でkgdbを使うのは有意です。QEMU gdbserver+kgdbでKGDB自体(kgdb_stub.c など)のデバッグが可能になります。[2]: qemuの方がデバッグできる範囲が広い(kgdb_stub.c、kgdb_connect() の前、etc)、qemuの方が通信が速い、後述するような「中断できない」などの制限が無い)

kgdbを有効にしたカーネルのビルド

GENERICカーネルコンフィグ を編集して、

  • 最適化無効
  • DDB無効
  • KGDB有効、KGDB_DEVRATE=115200

にしてビルドします。

diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index 4170eaa92a3..e751a603b39 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -104,13 +104,13 @@ options   DIAGNOSTIC      # inexpensive kernel consistency checks
 # Because gcc omits the frame pointer for any -O level, the line below
 # is needed to make backtraces in DDB work.
 #
-makeoptions    COPTS="-O2 -fno-omit-frame-pointer"
-options        DDB             # in-kernel debugger
+makeoptions    COPTS="-O0 -fno-omit-frame-pointer"
+#options       DDB             # in-kernel debugger
 #options       DDB_COMMANDONENTER="bt" # execute command when ddb is entered
 #options       DDB_ONPANIC=1   # see also sysctl(7): `ddb.onpanic'
 options        DDB_HISTORY_SIZE=512    # enable history editing in DDB
-#options       KGDB            # remote debugger
-#options       KGDB_DEVNAME="\"com\"",KGDB_DEVADDR=0x3f8,KGDB_DEVRATE=9600
+options        KGDB            # remote debugger
+options        KGDB_DEVNAME="\"com\"",KGDB_DEVADDR=0x3f8,KGDB_DEVRATE=115200
 makeoptions    DEBUG="-g"      # compile full symbol table for CTF
 #options       SYSCALL_STATS   # per syscall counts
 #options       SYSCALL_TIMES   # per syscall times

0x3f8 はx86での I/Oアドレスcom0を表します

ビルド後、カーネルを仮想マシンにインストール(scp -P10022 ${OBJDIR}/sys/arch/amd64/compile/GENERIC/netbsd root@localhost:/netbsd)後 reboot して、正常に起動できるか確認後 poweroff します。

com0を使う修行

一旦読み飛ばして頂いて大丈夫です。ハマったら読んでみて下さい。

qemu-system-x86_64 -rtc base=utc -m 256 -hda netbsd.qcow2 -net nic -net user,hostfwd=tcp::10022-:22 -serial telnet::4444,server で仮想マシンを起動します。QEMUのオプションについてはカーネルデバッグで使うQEMUオプションチートシートを参考にして下さい。別ターミナルで telnet localhost 4444 でQEMUに接続すると、QEMUウィンドウが表示されてNetBSDがブートします。

ブート中に流れるdmesgに com0: kgdb が表示されていることを確認します。

/var/run/dmesg.boot:

...
[   1.0536313] iic0 at piixpm0: I2C bus
[   1.0536313] vga0 at pci0 dev 2 function 0: vendor 1234 product 1111 (rev. 0x02)
[   1.0536313] wsdisplay0 at vga0 kbdmux 1: console (80x25, vt100 emulation), using wskbd0
[   1.0536313] wsmux1: connecting to wsdisplay0
[   1.0536313] drm at vga0 not configured
...
[   1.0536313] lpt0 at isa0 port 0x378-0x37b irq 7
[   1.0536313] com0 at isa0 port 0x3f8-0x3ff irq 4: ns16550a, working fifo
[   1.0536313] com0: kgdb
[   1.0536313] attimer0 at isa0 port 0x40-0x43
...

reboot 後、QEMUウィンドウのboot promptで consdev com0 と入力します。

左のターミナル (com0) がコンソールになりました。コンソールをQEMUウィンドウに戻すには consdev pc と入力すれば良いです。このままcom0で boot と打つとcom0にdmesgが流れて。login: が表示されます。

dmesgには wsdisplay0 at vga0 kbdmux 1: console が無くなり、com0: console が現れます。

...
[   1.0301096] iic0 at piixpm0: I2C bus
[   1.0301096] vga0 at pci0 dev 2 function 0: vendor 1234 product 1111 (rev. 0x02)
[   1.0301096] wsdisplay0 at vga0 kbdmux 1
[   1.0301096] wsmux1: connecting to wsdisplay0
[   1.0301096] wskbd0: connecting to wsdisplay0
[   1.0301096] drm at vga0 not configured
[   1.0301096] wm0 at pci0 dev 3 function 0: Intel i82540EM 1000BASE-T Ethernet (rev. 0x03)
...
[   1.0301096] lpt0 at isa0 port 0x378-0x37b irq 7
[   1.0301096] com0 at isa0 port 0x3f8-0x3ff irq 4: ns16550a, working fifo
[   1.0301096] com0: console
[   1.0301096] com0: kgdb
[   1.0301096] attimer0 at isa0 port 0x40-0x43
...

kgdbに接続する

仮想マシンを qemu-system-x86_64 -rtc base=utc -m 256 -hda netbsd.qcow2 -net nic -net user,hostfwd=tcp::10022-:22 -serial telnet::4444,server,nowait で起動します。QEMUのオプションについてはカーネルデバッグで使うQEMUオプションチートシートを参考にして下さい。
QEMUウィンドウでスペースを押して5を選択した後、boot -d で起動すると kgdb waiting... が表示されたます。
この状態でホストのクロスgdbから target remote :4444 を実行すると、kgdbに接続されてデバッグが開始されます。

NetBSDのkgdbはクセが強く、色々気をつけるべきことがあります。

  1. gdbを一度切断すると、二度と接続できなくなります。もう再接続するには仮想マシンを再起動する必要があります。
  2. continue した後、Ctrl-Cによる中断ができません。うっかり押してしまうと再接続できなくなり、仮想マシンの再起動が必要になります。公式HOWTO を見ると、昔はCtr-Cで中断できたようです。Regressionしてるっぽいです。(調査中です)
  3. 上の性質から、continue する前に break を入れることが必須です。

gdbを直接使うは非常に使いづらいので、テキストエディタやIDEから使いましょう。CLionから使うHowtoを近日中に公開予定です。

ライセンス


この 作品 は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。

関連するソフトウェアの公式ドキュメントのメンテナの方へ:この記事の内容を公式ドキュメントに記載して頂ける場合、この記事にコメントして頂くか、twitter @wata_ash にご連絡下さい。