kgdbを使ったLinux Kernelのデバッグ方法をまとめてみました。
この説明は以下の内容を元にして、具体的な手順を説明したものです。
kgdb, kdb の使い方と、カーネルデバッガーの内部
https://sites.google.com/site/kandamotohiro/linux/kgdb
まずはカーネルの設定を確認。以下の通りに設定をします。
ビルドして起動できることを確認します。
ホストとターゲットは2本のUARTで接続します。
1本はカーネルログの出力確認、breakコマンドの入力、シェルの操作を行うために使用します(以降の説明ではUART0)。
もう1本はkgdbの通信用に使用します(以降の説明ではUART1)。
UARTが1本しかないケースは、SSHで代替することもできます。
ただし、この場合、任意のタイミングでbreakをかけられないことと、カーネルログの出力が確認できないため、あまりおすすめはしないです。
カーネルの引数にkgdbwait, kgdbocを追加します。
kgdbwaitは、カーネル起動時にkgdbの接続を待ちます。
kgdbocはUART経由でgdbに接続することを指定します。kgdbocには使用するttyとボーレートを設定します。
以下はUART1に対して、115.2kbpsのボーレートを設定しています。
1kgdbwait kgdboc=ttyS1,115200
またカーネルログの出力先は以下のとおりUART0に対して設定します。
1console=ttyS0,115200
カーネルを起動して、以下の表示が出るとkgdbの接続待ちの状態になります。
ホストからターゲットのカーネルに対して、gdbでアタッチします。
ビルドしたvmlinuxをgdbで読み込みます。
ターゲットがロードしたカーネルがzImageの場合でも、シンボル情報が必要になるためvmlinuxをロードします。
1$ arm-linux-gnueabihf-gdb vmlinux
次にkgdbocでUART1に対してアタッチします。
以下はホストの/dev/ttyUSB2とターゲットのUART1が接続されている例です。
接続に成功すると以下のようにbreakpointで停止します。
ほとんどの操作はgdbと同様に行うことができます。
以下によく使う操作を記載します。
ブレークポイントを設定&実行を再開する
手順はgdbと同様です。
以下はuio_readにブレークを設定する例です。
1(gdb) b uio_read 2Breakpoint 1 at 0x803d910c: file drivers/uio/uio.c, line 812. 3(gdb) c 4Continuing. 5[New Thread 1172] 6[New Thread 1135] 7[New Thread 1143] 8[New Thread 1146] 9[New Thread 1152] 10[New Thread 1153] 11[New Thread 1167] 12[Switching to Thread 1172] 13 14Thread 42 hit Breakpoint 1, uio_read (filep=0x839f9240, buf=0x7ef96c20 "", count=4, ppos=0x83bdff80) at drivers/uio/uio.c:812 15812 { 16(gdb)
任意のタイミングでブレークをかける(つまり実行を停止して、gdbで操作できる状態にする)には、ホストからUART0に対してSysRq + gを入力します。
ホストからUART0への接続にscreenを使っている場合には、Ctrl-a -> b -> gでSysRq + gの入力できます。
SysRq +g を受け付けるとUART0には以下のように表示されます。
1# sysrq: SysRq : DEBUG
gdbは以下のようにブレークがかかります。