linux内核kgdb调试

从2.6.26开始,Linux 主干内核开始内置了代码级调试器 kgdb。通过 kgdb,可以在内核代码中设置断点,单步调试和观察变量。为了使用 kgdb,需要有两个系统。一个作为上位机,一个作为下位机(目标机)。两台机器通过串口线连接。需要调试的内核运行在下位机上。串口线用于kgdb连接远程目标板。

内核配置
使用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传递给内核,启动相应的功能。
(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

你可能感兴趣的:(linux)