用 kGDB 調試 Linux 內核

簡介

這個文檔記錄了用kGDB調試Linux內核的全過程,都是在前人工做基礎上的一些總結。如下操做都是基於特定板子來進行,可是大部分都能應用於其餘平臺。html

要使用KGDB來調試內核,首先須要修改config配置文件,打開相應的配置,配置內核啓動參數,甚至修改串口驅動添加poll支持,而後才能經過串口遠程調試內核。linux

配置內核

基本配置

在內核配置文件:.config中,須要打開以下選項git

CONFIG_KGDB 加入KGDB支持
CONFIG_KGDB_SERIAL_CONSOLE 使KGDB經過串口與主機通訊(打開這個選項,默認會打開CONFIG_CONSOLE_POLL和CONFIG_MAGIC_SYSRQ)
CONFIG_KGDB_KDB 加入KDB支持
CONFIG_DEBUG_KERNEL 包含驅動調試信息
CONFIG_DEBUG_INFO 使內核包含基本調試信息
CONFIG_DEBUG_RODATA=n 關閉這個,能在只讀區域設置斷點

可選選項

CONFIG_PANIC_TIMEOUT=5
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1
CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=1
CONFIG_S3C2410_WATCHDOG_ATBOOT=0
CONFIG_FRAME_POINTER -- 使KDB可以打印更多的棧信息
CONFIG_KALLSYMS -- 加入符號信息
CONFIG_KDB_KEYBOARD -- 若是是經過目標版的鍵盤與KDB通訊,須要把這個打開,且鍵盤不能是USB接口
CONFIG_KGDB_TESTS

啓動參數

打開相應的選項後,須要配置kernel啓動參數,使KGDB和內核可以找到正確的通訊接口。若是是使用串口,則須要配置以下選項:web

console=ttySAC3,115200 kgdboc=ttySAC3,115200

若是須要調試內核的啓動過程,則須要在kgdboc後面加入kgdbwait。多線程

在其餘板子上,若使用以太網口來和KGDB進行通訊,則要把kgdboc換成kgdboe(kgdb 
over ethernet))。函數

配置完後,就能夠正常編譯,而後把內核下載到目標板上面。spa

串口驅動修改

若是在內核啓動的過程當中出現以下錯誤提示:線程

kgdb: Unregistered I/O driver, debugger disabled.

則須要根據這一部分,修改串口驅動程序,若能正常進入kgdb,則忽略該節,直接進入下一節使用KGDB。debug

在drivers/tty/serial/kgdboc.c中的configure_kgdboc函數,會經過tty_find_polling_driver(cptr, 
&tty_line)來找尋內核啓動參數中指定的串口驅動。而後經過kgdboc_get_char()和kgdboc_put_char()來和主機串口正常通訊。調試

能夠看到在config配置文件的CONFIG_CONSOLE_POLL就是使能串口與kgdboc的接口。若是 tty_find_polling_driver沒有找到對應的串口通訊接口,則會調用kernel/debug/debug_core.c中的 kgdb_unregister_io_module進行錯誤處理。

有的板子的串口驅動並無加入對kgdboc通訊的支持,例如Samsung的串口驅動須要在drivers/tty/serial/samsung.c中手動添加。 
添加與kgdboc通訊的接口,只需添加一個發送函數和接收函數,而後在驅動操做結構體中加入對應的函數就能夠了。具體的PATCH以下:

drivers/tty/serial/samsung.c | 22 ++++++++++++++++++++++
1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index ff6a4f8..5ceb7d7 100755
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -893,7 +893,29 @@ static struct console s3c24xx_serial_console;
#define S3C24XX_SERIAL_CONSOLE NULL
#endif

+#ifdef CONFIG_CONSOLE_POLL
+static void s3c24xx_serial_poll_put_char(struct uart_port *port, unsigned char c)
+{
+    while (!(rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE))
+       ;
+
+     wr_regl(port, S3C2410_UTXH, c);
+}
+
+static int s3c24xx_serial_poll_get_char(struct uart_port *port)
+{
+    while (!(rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_RXDR))
+       ;
+
+    return rd_regl(port, S3C2410_URXH);
+}
+#endif
+
static struct uart_ops s3c24xx_serial_ops = {
+#ifdef CONFIG_CONSOLE_POLL
+    .poll_get_char = s3c24xx_serial_poll_get_char,
+    .poll_put_char = s3c24xx_serial_poll_put_char,
+#endif
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
.get_mctrl = s3c24xx_serial_get_mctrl,
--
1.7.5.4

加入這個patch,從新編譯內核,以後就能正常進入kgdb

gdb遠程調試

若是在內核啓動參數中加入了kgdbwait,則內核會在完成基本的初始化以後,停留在kgdb的調試陷阱中,等待主機的gdb的遠程鏈接。

因爲大部分的板子只有一個調試串口,因此你須要把以前與串口通訊的minicom退出來,而後在內核源碼的目錄下,執行如下命令:

$ arm-linux-gnueabi-gcc vmlinux
(gdb) target remote /dev/ttyUSB0
(gdb) set detach-on-fork on
(gdb) b panic()
(gdb) c

固然,你也能夠agent-proxy來複用一個串口,經過虛擬出兩個TCP端口。這時候,gdb就須要用target 
remote命令鏈接kgdb,例如:

(gdb) target remote localhost:5551

agent-proxy能夠經過這裏獲取:git://git.kernel.org/pub/scm/utils/kernel/kgdb/agent-proxy.git,具體用法,請看該repo下的README。

在用gdb來調試內核的時候,因爲內核在初始化的時候,會建立不少子線程。而默認gdb會接管全部的線程,若是你從一個線程切換到另一個線程,gdb會立刻把原先的線程暫停。可是這樣很容易致使kernel死掉,因此須要設置一下gdb。 
通常用gdb進行多線程調試,須要注意兩個參數:follow-fork-mode和detach-on-fork。

  • detach-on-fork參數,指示GDB在fork以後是否斷開(detach)某個進程的調試,或者都交由GDB控制:

    set detach-on-fork [on|off]

    • on: 斷開調試follow-fork-mode指定的進程。
    • off: gdb將控制父進程和子進程。
  • follow-fork-mode指定的進程將被調試,另外一個進程置於暫停(suspended)狀態。follow-fork-mode的用法爲:

    set follow-fork-mode [parent|child]

    • parent: fork以後繼續調試父進程,子進程不受影響。
    • child: fork以後調試子進程,父進程不受影響。

REFERENCE

相關文章
相關標籤/搜索