编译出内核之后,我们给上位机使用的是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传递给内核,启动相应的功能。
(1) .内核初始化时进入gdb调试模式
驱动程序在内核启动的时候,要做很多初始化动作,这个时候可以利用GDB参与调试。
console=ttymxc0,115200 kgdboc=ttymxc0,115200 kgdbwait
启动内核,退出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
966arch_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
966arch_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
//基本步骤如下:
1.通过bootargs传递给内核的参数:
console=ttyS0,115200 kgdboc=ttyS0,115200 kgdbwait
或者
console=ttyS0,115200 kgdboc=ttyS0,115200 kgdbcon
启动内核后,在串口终端中,输入如下命令:
bash-3.2# echo g > /proc/sysrq-trigger
SysRq : DEBUG
Entering KGDB
2.退出串口终端,
内核根目录执行./arm-eabi-4.4.3/bin/arm-eabi-gdb ./vmlinux
gdb>set remotebaude 115200
gdb>target remote /dev/ttyS0
调试信息如下:
[ 4285.692745] SysRq : DEBUG
(gdb) target remote /dev/ttyUSB0
Remote debugging using /dev/ttyUSB0
kgdb_breakpoint () at kernel/debug/debug_core.c:998
998 arch_kgdb_breakpoint();
(gdb) c
Continuing.
[ 54.680409] SysRq : DEBUG
Program received signal SIGTRAP, Trace/breakpoint trap.
kgdb_breakpoint () at kernel/debug/debug_core.c:998
998 arch_kgdb_breakpoint();
(gdb) bt
#0 kgdb_breakpoint () at kernel/debug/debug_core.c:998
#1 0xc02600b0 in __handle_sysrq (key=103, check_mask=) at drivers/tty/sysrq.c:525
#2 0xc0260194 in write_sysrq_trigger (file=, buf=, count=2, ppos=) at drivers/tty/sysrq.c:873
#3 0xc010f350 in proc_reg_write (file=0xee590500, buf=0x40c579fc "g\n\023@\234\274\023@0^\305@WD:-?} # ", count=2, ppos=0xee185f80)
at fs/proc/inode.c:216
#4 0xc00ced10 in vfs_write (file=0xee590500, buf=0x40c579fc "g\n\023@\234\274\023@0^\305@WD:-?} # ", count=2, pos=0xee185f80)
at fs/read_write.c:435
#5 0xc00cef50 in sys_write (fd=, buf=, count=2) at fs/read_write.c:487
#6 0xc000d900 in ?? ()
#7 0xc000d900 in ?? ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) c
Continuing.
[ 65.958605] [AXP]-----------in change_flag-----------
[ 79.783913] codec:start 1
...............................
[203407.942242]
Unable to handle kernel paging request at virtual address 4016873c
[203407.967632] pgd = ee530000
[203407.977616] [4016873c] *pgd=7e54b831
[203407.992619] Internal error: Oops: 17 [#1] PREEMPT SMP ARM
[New Thread 17797]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 17797]
vector_swi () at arch/arm/kernel/entry-common.S:379
warning: Source file is more recent than executable.
379 ldreq r10, [lr, #-4] @ get SWI instruction
(gdb) bt
#0 vector_swi () at arch/arm/kernel/entry-common.S:379
#1 0x40168740 in ?? ()
Cannot access memory at address 0x4078c8ec
#2 0x40168740 in ?? ()
Cannot access memory at address 0x4078c8ec
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) info threads
[New Thread -2]
[New Thread -3]
..................................
..................................
10 Thread 8 (kworker/1:0) kthread (_create=0xc0064d1c) at kernel/kthread.c:100
9 Thread 7 (migration/1) kthread (_create=0x13) at kernel/kthread.c:100
8 Thread 6 (migration/0) kthread (_create=0x13) at kernel/kthread.c:100
7 Thread 3 (ksoftirqd/0) kthread (_create=0xc005635c) at kernel/kthread.c:100
6 Thread 2 (kthreadd) 0x00000000 in ?? ()
5 Thread 1 (init) 0x0000f5f0 in ?? ()
4 Thread -3 (shadowCPU1) vector_swi () at arch/arm/kernel/entry-common.S:379
---Type to continue, or q to quit---
3 Thread -2 (shadowCPU0) 0x56f3a0c8 in ?? ()
* 2 Thread 17797 (mediaserver) vector_swi () at arch/arm/kernel/entry-common.S:379
(gdb) bt
#0 vector_swi () at arch/arm/kernel/entry-common.S:379
#1 0x40168740 in ?? ()
Cannot access memory at address 0x4078c8ec
#2 0x40168740 in ?? ()
Cannot access memory at address 0x4078c8ec
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
(gdb) info registers
r0
0x570e1e60
1460543072
r1 0x0 0
r2 0x1 1
r3 0x432b3414 1126904852
r4 0x43931e60 1133715040
r5 0x56aaa870 1454024816
r6 0x0 0
r7 0x4e 78
r8 0x68000010 1744830480
r9 0x4078c8ec 1081657580
r10 0x58c8c750 1489553232
r11 0x4078c8ec 1081657580
r12 0x41a5bf4c 1101381452
sp 0xdb863fb0 0xdb863fb0
lr 0x40168740 1075218240
pc 0xc000d9e8 0xc000d9e8
cpsr 0x68000093 1744830611
(gdb) x/15i vector_swi
0xc000d9c0 : sub sp, sp, #72 ; 0x48
0xc000d9c4 : stm sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12}
0xc000d9c8 : add r8, sp, #60 ; 0x3c
0xc000d9cc : stmdb r8, {sp, lr}^
0xc000d9d0 : mrs r8, SPSR
0xc000d9d4 : str lr, [sp, #60] ; 0x3c
0xc000d9d8 : str r8, [sp, #64] ; 0x40
0xc000d9dc : str r0, [sp, #68] ; 0x44
0xc000d9e0 : tst r8, #32
0xc000d9e4 : movne r10, #0
=> 0xc000d9e8 : ldreq r10, [lr, #-4]
0xc000d9ec : ldr r12, [pc, #172] ; 0xc000daa0 <__cr_alignment>
0xc000d9f0 : ldr r12, [r12]
0xc000d9f4 : mcr 15, 0, r12, cr1, cr0, {0}
0xc000d9f8 : cpsie i