基于A33的GDB+KGDB内核调试环境搭建
平台
- 芯片:全志A33
- 系统:Tina3.5
- 内核:linux3.4
- 通信方式:串口
修改内核配置文件
在内核配置文件.config中,打开如下选项:
配置项 | 开关 | 备注 |
---|---|---|
CONFIG_KGDB | y | 加入KGDB支持 |
CONFIG_KGDB_SERIAL_CONSOLE | y | 使用KGDB通过串口与主机通信 |
CONFIG_DEBUG_INFO | y | 打开这个,使得内核包含基本信息 |
CONFIG_DEBUG_KERNEL | y | 打开这个,包含驱动调试信息 |
添加启动参数
在启动内核的时候,会读取内核启动参数。在启动参数一栏添加如下:
console=ttyS0,115200 kgdboc=ttyS0,115200
上述参数表明:终端使用串口ttyS0,通信波特率为115200;kgdb调试通信也使用串口ttyS0,通信波特率为115200。当内核启动时,会读取并解析这些参数,并触发相应的行为。
验证串口驱动是否支持kgdb调试
可以打开驱动程序,看下是uart_ops结构的对象下poll_get_char和poll_put_char函数指针是否有赋值。如果没有赋值,需要重新修改驱动,使其支持poll功能,添加poll功能后的驱动函数类似如下:
验证kgdb是否支持远程调试
使用gdb调试内核时出现:
Truncated register 19 in remote 'g' packet
这貌似一个gdb的bug,需要更改内核中kgdb的源码,更改如下:
------------------------- arch/arm/include/asm/kgdb.h -------------------------
index 48066ce..006793e 100644
@@ -75,7 +75,7 @@ extern int kgdb_fault_expected;
#define KGDB_MAX_NO_CPUS 1
#define BUFMAX 400
-#define NUMREGBYTES (DBG_MAX_REG_NUM << 2)
+#define NUMREGBYTES (GDB_MAX_REGS << 2)
#define NUMCRITREGBYTES (32 << 2)
#define _R0 0
更改之后,再次验证一下,是可以成功启动gdb调试的。
使用agent-proxy复用串口
当只有一个串口用于调试时,我又想通过串口观察打印信息,又想通过串口调试内核,那么可以通过agent-proxy来复用串口。其中agent-proxy程序是运行在pc机器(开发机)上。
agent-proxy的下载地址是:agent-proxy下载
我下载的版本是1.96的,如图所示:
agent-proxy使用方法
其实比较简单,先下载下来,然后解压到自己的工作目录下,然后使用 make 编译形成二进制文件:
cd agent-proxy-1.96
make #make之后,会生成agent-ptoxy可执行文件,后面会使用到
使用例子(在人家的README中,也有类似介绍):
## 首先在你的终端中开启agent-proxy,其中5550表示终端打印显示的端口号;
## 5551表示gdb调试使用的远程端口号;ttyUSB0表示复用的串口。
./agent-proxy 5550^5551 0 /dev/ttyUSB0,115200 &
## 连接终端打印端口
telnet localhost 5550
## Boot the remote system with the kernel arg: kgdboc=ttyS0,115200
## 当要调试的内核处于等待gdb连接状态时,这时可以通过gdb连接到目标端口
gdb ./vmlinux
target remote localhost:5551
按照上面操作后,可以一边断点调试内核,一边观察内核输出的打印信息:
当内核全速运行时:
pc端(开发机)的gdb调试步骤
首先进入到vmlinux(vmlinux是在编译内核过程中产生的未压缩的内核,它是elf可执行文件,用于内核debug,不可用于直接加载)文件目录下,进行如下操作:
当直接使用串口调试时:
arm-openwrt-linux-gdb ./vmlinux
set serial baud 115200
target remote /dev/ttyUSB0
当使用agent-proxy代理时:
arm-openwrt-linux-gdb ./vmlinux
target remote localhost:5551 # 5551是通过agent-proxy复用出来的端口
内核启动参数的选择
进入内核后,打开kgdb调试
如果想要调试加载的驱动时,可以进入到内核后再调试,这内核启动参数可以是:
console=ttyS0,115200 kgdbwait kgdboc=ttyS0,115200
然后编译内核,之后烧录到flash中,进行开机启动,可以进入到目标机内核文件系统中,在文件系统中进行如下操作可以打开kgdb:
echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
当出现:
kgdb: Registered I/O driver kgdboc.
kgdb: Waiting for connection from remote gdb...
上述信息时,表明目标机(开发板)正在等待gdb连接。
这里面其实不用再次执行echo g > /proc/sysrq-trigger ,依然可以进入等待gdb的连接中。因为内核代码中的kgdboc中有检测到ttyS0配置参数时,会直接进入gdb连接中。涉及到的相关代码如下:
->param_set_kgdbts_var()
->configure_kgdbts()
->kgdb_register_io_module()
->kgdb_initial_breakpoint()
->kgdb_breakpoint()
进行驱动(.ko)调试
先把.ko文件放到目标机的文件系统目录下,然后进行加载:
insmod xxx.ko
加载成功后,会生成一些信息。之后查看加载模块的加载地址信息:
cat /proc/modules
上图的地址:0xbf106000就是gt9xxnew_ts模块加载的地址,这个地址在调试gt9xxnew_ts模块时会使用到。
这个时候,在开发机上进入远程gdb连接状态,使用上面获取的模块加载地址,进行调试加载的模块:
add-symbol-file xxx.ko text_address(ko)
上面指令执行后,gdb会读取模块的符号,这个时候就可以像调试应用层序一样调试驱动程序了:
进行到上面步骤后,就可以调试驱动文件了,但是美中不足的是,该方法无法调试模块的初始化函数,对于上面模块而言,断点是无法进入goodix_ts_init函数内部的,因为加载过程中已经执行过上面的初始化函数了,所以是无法再次进入初始化函数的。
如果想要调试模块的初始化函数怎么办,我目前采用的方法是将驱动模块直接编译进入内核影像中,在启动内核的时候进行调试,这样就可以调试指定模块的初始化函数了。
进入内核初,打开gdb调试
如果想要调试内核启动过程中的代码,就需要调整代码了:
------------------------- drivers/tty/serial/kgdboc.c -------------------------
index 2b42a01..7d5ac3d 100644
@@ -321,7 +321,7 @@ static int __init kgdboc_early_init(char *opt)
early_param("ekgdboc", kgdboc_early_init);
#endif /* CONFIG_KGDB_SERIAL_CONSOLE */
-module_init(init_kgdboc);
+core_initcall_sync(init_kgdboc);
module_exit(cleanup_kgdboc);
module_param_call(kgdboc, param_set_kgdboc_var, param_get_string, &kps, 0644);
MODULE_PARM_DESC(kgdboc, "<serial_device>[,baud]");
----------------------- drivers/tty/serial/sunxi-uart.c -----------------------
index 78dd9c2..48dc093 100644
@@ -2101,7 +2101,7 @@ static void __exit sunxi_uart_exit(void)
uart_unregister_driver(&sw_uart_driver);
}
-module_init(sunxi_uart_init);
+core_initcall(sunxi_uart_init);
module_exit(sunxi_uart_exit);
MODULE_AUTHOR("Aaron<leafy.myeh@allwinnertech.com>");
上述更改是为了提高终端串口模块和kgdboc模块的初始化优先级。这种情况下,可在内核初期进入到kgdb连接等待中,用于调试内核初始化程序。
将上述代码重新编译烧录到emmc中,上电启动:
可以看到在启动内核初,kgdb已经在等待远程连接了,这个时候就可以像上面那种方法继续调试了。
这个个更改有一个缺点,就是串口终端无法登录了,不过不影响kgdb调试。
总结
- 此种是以串口方法进行搭建kgdb调试环境的,如果通过网络的话,需要使用到其他的模块