内核配置
使用KGDB之前,需要在内核中将KGDB配置下:
Kernel hacking --->
[*] Kernel debugging
[*] Compile the kernel with debug info
[*] KGDB: kernel debugger --->
<*> KGDB: use kgdb over the serial console
编译出内核之后,我们给上位机使用的是ELF格式的内核vmlinux,可以通过如下命令查看内核是否提供调试信息支持。
$../arm-eabi-4.4.3/bin/arm-eabi-readelf -S ./vmlinux | grep debug
[17] .debug_line PROGBITS 00000000 4d4a3b 229041 00 0 0 1
[18] .debug_info PROGBITS 00000000 6fda7c 1f8538f 00 0 0 1
[19] .debug_abbrev PROGBITS 00000000 2682e0b 10eead 00 0 0 1
[20] .debug_aranges PROGBITS 00000000 2791cb8 009b50 00 0 0 8
[21] .debug_pubnames PROGBITS 00000000 279b808 031751 00 0 0 1
[22] .debug_str PROGBITS 00000000 27ccf59 11fc28 01 MS 0 0 1
[23] .debug_frame PROGBITS 00000000 28ecb84 07e470 00 0 0 4
[24] .debug_loc PROGBITS 00000000 296aff4 26e872 00 0 0 1
[25] .debug_ranges PROGBITS 00000000 2bd9868 0c8368 00 0 0 8
同时要对对i.MX内核UART驱动代码修改:
static struct uart_ops mxc_ops = {
...
#ifdef CONFIG_CONSOLE_POLL
.poll_put_char = mxcuart_poll_put_char,
.poll_get_char = mxcuart_poll_get_char,
#endif
}
主要实现struct uart_ops 中的poll_get_char,pool_put_char这两个成员。实现KGDB所需的poll功能。
如何使用KGDB调试内核
使用之前解释两个三个参数
kgdboc:[KGDB,HW] kgdb over consoles.
Requires a tty driver that supports console polling,or a supported polling keyboard driver (non-usb).
Serial only format: [,baud]
keyboard only format: kbd
keyboard and serial format: kbd,[,baud]
kgdbwait: [KGDB] Stop kernel execution and enter the kernel debugger at the earliest opportunity.
kgdbcon: share console serial port with KGDB
上面的参数通过bootargs传递给内核,启动相应的功能。
内核初始化时进入gdb调试模式
驱动程序在内核启动的时候,要做很多初始化动作,这个时候可以利用GDB参与调试。
kgdboc=ttymxc0,115200 kgdbwait kgdbcon
启动内核,退出minicom或者kermit,因为gdb需要用到串口。
上位机执行如下:
./arm-eabi-4.4.3/bin/arm-eabi-gdb ./vmlinux
出现如下界面:
GNU gdb (GDB) 7.1-android-gg2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-linux-gnu --target=arm-elf-linux".
For bug reporting instructions, please see:
<
Reading symbols from /work/projects/lanke/kernel_imx/vmlinux...done.
(gdb) set remotebaud 9600
(gdb) target remote /dev/ttyS0
Remote debugging using /dev/ttyS0
kgdb_breakpoint (new_dbg_io_ops=) at kernel/debug/debug_core.c:966
966 arch_kgdb_breakpoint();
(gdb)
上面进入kgdb后配置gdb参数的命令,可以通过脚本实现。
在内核顶层目录创建.gdbinit文件,再配置参数:
1 define kgdb
2 set remotebaud 115200
3 target remote /dev/ttyS0
4 end
有了这个文件后,每次进入gdb,直接输入kgdb即可初始化进入调试模式
(gdb) kgdb
kgdb_breakpoint (new_dbg_io_ops=) at kernel/debug/debug_core.c:966
966 arch_kgdb_breakpoint();
(gdb)
下面就可以调试了。
Notes:由于内核log和kgdb使用同一个串口,所以log这个串口要关闭(关闭kermit或者minicom)
(2)内核启动后进入gdb调试模式
通过bootargs传递给内核的参数:
console=ttymxc0,115200 kgdboc=ttymxc0,115200 kgdbcon
启动内核后,在串口终端中,输入如下命令:
bash-3.2# echo g > /proc/sysrq-trigger
SysRq : DEBUG
Entering KGDB
退出串口终端,执行./arm-eabi-4.4.3/bin/arm-eabi-gdb ./vmlinux。
然后和上面操作一样,进入内核调试模式。
NOTES:之所以分两个部分,因为两个部分可以调试不能的功能,第一个可以调试驱动初始化的部分,第二个是可以调试整个系统起来后,应用和驱动交互功能的调试,这两点很重要。
有益的断点
到目前为止,已经可以通过上面的方法建立上位机和目标板kgdb的调试连接。在内核启动时进入调试模式,设置好断点之后,输入命令continue,内核继续往下跑,要是不出例外,整个引导过程就将完成。在这个阶段可以设置一些断点,来建立一些列的调试会话。有时候一些有益的断点能帮我们更好的了解调试我们的程序。下面就是用的比较多的两个,举例下。在以后的开发过程中药善于总结积累调试技巧和方法。
(gdb) b panic
Breakpoint 1 at 0x8033e900: file kernel/panic.c, line 88.
(gdb) b sys_sync
Breakpoint 2 at 0x800cd450: file fs/sync.c, line 100.
利用gdb命令breakpoint的简化版,设置了两个断点。一个是panic(),另一个是系统调用后要调用的sys_sync()。这两个断点的好处是,我们可以检查kernel panic时系统的状态,另一可以在用户空间通过sync命令,halt住内核,进而进入调试会话。由于串口被占用,我们可以通过adb shell进入终端。
在内核引导过程中,启动kgdb之前,我们还是没法调试之前的代码的。这是必须借助于一些仿真器进行。当然这个时候内核也不是全然不提供调试方法的,这个时候可以使用汇编级打印函数实现。
DDD简介
在目标板那一端口,配置环境是一样的,区别是下面
ddd --debugger ./arm-eabi-4.4.3/bin/arm-eabi-gdb ./vmlinux
gdbtui简介
./arm-eabi-4.4.3/bin/arm-eabi-gdbtui ./vmlinux