kgdbを使ったカーネルのデバッグ

kgdbを使ったLinux Kernelのデバッグ方法をまとめてみました。
この説明は以下の内容を元にして、具体的な手順を説明したものです。

kgdb, kdb の使い方と、カーネルデバッガーの内部
https://sites.google.com/site/kandamotohiro/linux/kgdb

カーネルの設定の確認

まずはカーネルの設定を確認。以下の通りに設定をします。

1# CONFIG_DEBUG_RODATA is not set
2CONFIG_FRAME_POINTER=y
3CONFIG_KGDB=y
4CONFIG_KGDB_SERIAL_CONSOLE=y
5CONFIG_DEBUG_INFO=y
6CONFIG_MAGIC_SYSRQ=y

ビルドして起動できることを確認します。

ホストとターゲットの構成

ホストとターゲットは2本のUARTで接続します。
1本はカーネルログの出力確認、breakコマンドの入力、シェルの操作を行うために使用します(以降の説明ではUART0)。
もう1本はkgdbの通信用に使用します(以降の説明ではUART1)。

image

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の接続待ちの状態になります。

1KGDB: Registered I/O driver kgdboc
2KGDB: Waiting for connection from remote gdb...

ホストからターゲットに接続する

ホストからターゲットのカーネルに対して、gdbでアタッチします。

ビルドしたvmlinuxをgdbで読み込みます。
ターゲットがロードしたカーネルがzImageの場合でも、シンボル情報が必要になるためvmlinuxをロードします。

1$ arm-linux-gnueabihf-gdb vmlinux

次にkgdbocでUART1に対してアタッチします。
以下はホストの/dev/ttyUSB2とターゲットのUART1が接続されている例です。

12(gdb) set serial baud 115200
3(gdb) target remote /dev/ttyUSB2

接続に成功すると以下のようにbreakpointで停止します。

10x8016fbb8 in arch_kgdb_breakpoint () at kernel/debug/debug_core.c:1070
21070            wmb(); /* Sync point before breakpoint */

kgdbの操作

ほとんどの操作は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は以下のようにブレークがかかります。

1Thread 1 received signal SIGTRAP, Trace/breakpoint trap.
2[Switching to Thread 4294967294]
3kgdb_breakpoint () at kernel/debug/debug_core.c:1071
41071            arch_kgdb_breakpoint();
5(gdb)
Posted at : 2020-05-06 15:45:26 / Category : none

コメント

まだコメントはありません

コメントを投稿


お名前


メールアドレス(返信を通知する場合のみ記入)

認証コード(200526 と記入してください)