KGDB调试汇总
通过KGDB,可以在内核代码中设置断点,单步调试和观察变量;为了使用KGDB,需要有两个系统,一个称为开发机,一个称为目标机,被调试的内核运行于目标机上,同时开发机与目标机不要求有相关的体系结构,两者通过串口连接。
目的:在某嵌入式设备中使用kgdb对内核的调试
gdb+gdbserver用来调试用户程序
gdb+kgdb用来调试内核
如何共用串口
1)在PC机安装Cgwin,通过读写/dev/ttyS0即可与某嵌入式设备共用同一个串口,此时需要将secureCRT的连接断开;
2)在PC机上安装虚拟机,如virtual box上将串口设置成host device,即可与主机共享串口,连接的时候亦需要将secureCRT的连接断开。
在一端输入cat /dev/ttyS0,在另一端echo “字符串”>/dev/ttyS0,如何cat中显示此字符串,表明串口通信成功。
内核选项
-->打开-g选项
-->打开kgdb选项
在cgwin中编译gdb
$ tar -zxvf gdb-7.6.tar.gz
$./configure--target=mips-linux--prefix=/home/lym/bin
$make
$make install
编译成功后如下:
在目标机上动态启动KGDB
1)通过输入echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc注册通过ttyS0进行数据传输;
2)输入echo g > /proc/sysrq-trigger动态启动KGDB。
在开发机上远程连接目标机
1)输入set remotebaud 115200设置对端波特率为115200;
2)输入target remote /dev/ttyS0通过串口连接到对端。
通过设置set debug remote 1,可以查看发送和接收的报文。
问题1
Bogus trace status replay from target:E22
解释:由于kgdb不支持qTStatus命令而返回E22错误信息,修改kgdb代码,此时返回空包即可,修改如下:(/kernel-2.6.32-longterm/kernel/kgdb.c)
问题2
Remote 'g' packet reply is too long
解释:修改gdb接收报文处理方式,如下:(gdb-7.6/gdb/remote.c)
附录一
GDB与远程目标之间通过串行协议来进行通信,其为基于ASCII,以gdb发送命令,目标端返回执行结果的方式来进行,命令和回复在传输过程中封装在一个packet中,每个packet以$开始,接着是实际数据,数据以#结束,后面再接用于校验的数据。
GDB通过set remotelogfile [filepath]指定log路径,此文件需要再开始记录前手工创建。
KGDB实现的基本命令:
‘g’
格式:$g#67
描述:gdb向目标端发送这个命令来获取目标机当前寄存器的值
回复:+ $123456789abcdef0...#xx
'+'用来应答'g'这个命令,表明目标端正确地收到这个命令,然后就是目标端的回复包,gdb规定用8十六进制个数字来表示一个寄存器的值,所以第一个寄存器的值为12345678,第二个为9abcdef0,依此类推,而具体每个寄存器的含义和寄存器个数又体系结构决定,定义在gdb的代码中. 当然这里8个数字是对32位系统来说的,为什么是8位?限于我们这个协议是基于ASCII的,一个十六进制数只能标记4位,那32位自然是8个十六进制数了。
‘G’
格式:$GXXXXXXXXXXX...#xx
描述:和g相反,这个命令用来设置目标机当前寄存器的值
回复:+ $OK#9a (OK表示成功)
‘m’
格式:$m6a1bbb,2#b9
描述:读取一段内存的值,这里是读取以6a1bbb位起始地址的两个字节
回复:+ $f488#0a 目标端把值返回.
‘M’
格式:$Mccc5cc,2:a340#01
描述:设置一段内存的值,这里是把以ccc5cc位开始地址的两个字节设成a340
回复:+ $OK#9a
‘s’
格式:$sADDR#xx
描述:用户进行单步调试时用到,ADDR指明了程序将从那个地址恢复运行,如果忽略ADDR,程序就从断点处继续运行
回复:+ 目标端会马上返回数据正确或错误接收,但不会马上返回信息,具体信息要到下一次断点被触发时才会返回
‘c’
格式:$cADDR#xx
描述:让程序恢复正常运行
回复:和‘s’一样
‘Z’
格式:$ZTADDR,LENGTH#xx
描述:'Z'命令用来设置断点或watch点,用过gdb的同志应该不会陌生了
'T'字段定义了这个命令的对象,0:软件断点,1:硬件断点,2:写watch点,3:读watch点,4:访问watch点.
'ADDR'就是我们所关心的内存地址,'LENGTH',对于软件中断它指明被断点指令覆盖的内存长度,kgdb对于软件断点忽略掉它,因为触发 kgdb的指令与体系结构相关,已经定义在kgdb这边,就如x86的int3在内存里面的二进制指令为"0xcc";对于硬件断点和watch 点,'LENGTH'指明gdb关注的内存长度.
‘z’
格式:$zTADDR,LENGTH#xx
描述:各项与'Z'相同.用来取消断点。
回复类型:
错误回复
格式:+ $E01#a6
描述::如果目标端在执行gdb的命令时出错时返回错误回复,比如访问内存时出错.E后面根两位的错误码,错误码在gdb里面没有定义
空回复
格式:+ $#00
描述:当目标端不认识gdb发来的命令时,返回空回复表示自己不支持这个命令
‘S’
格式:$SAA#b8
作用:AA表明触发这次通信的那个异常相关的信号,这个信号就是posix标准中的信号
‘T’
格式:$TAAN..:R..;N..:R..#xx
作用:AA同样是信号号, N..:R.. 这表明一个寄存器和它的值,N标记寄存器号,R是它对应的值,其中,如果N不是一个16进制数而是"thread",那么后面R的值就指明当前的进程号. 如果是其它的字符串gdb会省略
附录二
gdb常用指令
r |
run |
运行.程序还没有运行前使用 |
c |
cuntinue |
运行到下一断点处 |
q |
quit |
退出 |
tab |
tab |
命令补全功能 |
h |
help |
帮助 |
s |
step |
跟入函数 |
n |
next |
执行下一行的代码。如果是函数调用,也当作一行代码,执行到此函数返回 |
b |
breakpoint |
设置断点,用法:(1)b 函数名 (2)b 文件名:行号 |
info b |
info breakpoints |
查看断点数 |
del |
delete |
删除断点:delete 断点号 |
l |
list |
列出代码行。一次列10 行。 也可在 list 后面跟上 文件名:行号 |
watch |
watch |
观察一个变量的值。每次中断时都会显示这个变量的值 |
what |
what |
显示变量的类型 |
p |
|
打印一个变量的值。print 也可改变一个值,通过指令 p b = 100,变量b 的值就变成100了 |
start |
start |
从主函数 main 开始运行调试 |
fin |
finish |
运行直至当前函数返回 |
KGDB调试内核:
用gdb调试内核类似于调试应用程序进程,kgdb支持gdb的执行控制命令、栈跟踪和线程分析等,但kgdb不支持watchpoint,kgdb通过gdb宏来执行watchpoint。
- 停止内核执行
在gdb终端中按下ctrl+c,gdb将发送停止消息给kgdb stub,kgdb stub控制内核的运行,并与gdb通信;
- 内核继续运行
在gdb终端中发命令“continue”告诉kgdb stub让内核继续运行,直到遇到一个断点;
- 设置断点
设置断点命令如”(gdb)b module.c:2168”
- 进入函数代码
使用命令”(gdb)step”进入一个函数,使用命令”(gdb)next”执行下一行
- 显示堆栈信息
使用命令”(gdb)bt”
- 线程分析
gdb具有分析应用程序线程的特征,允许查看其中的线程,通过与kgdb可以查看内核线程,通过backtrace,info regi等命令可以显示线程上下文信息
- Watchpoint
执行断点 当代码在断点地址执行时,触发执行断点。由于硬件断点有限,建议通过gdb break命令使用软件断点,除非可避免修改代码。
写断点 当系统对在断点地址的内存位置进行写操作时,触发一个写断点。写断点可以放置可变长度的数据。写断点的长度指示为观察的数据类型长度,1表示为字节数据,2表示为2字节数据,3表示为4字节数据。
访问断点 当系统读或写断点地址的内存时,触发一个访问断点。访问断点也有可变长度数据类型。
因为gdb stub目前不使用gdb用于硬件断点的协议,因此,它通过gdb宏访问硬件断点。硬件断点的gdb宏说明如下:
1)hwebrk – 放置一个执行断点。
用法:hwebrk breakpointno address
2)hwwbrk – 放置一个写断点。
用法:hwwbrk breakpointno length address
3)hwabrk – 放置一个访问断点。
用法:hwabrk breakpointno length address
4)hwrmbrk – 删除一个断点
用法:hwrmbrk breakpointno
5)exinfo – 告诉是否有一个软件或硬件断点发生。如果硬件断点发生,打印硬件断点的序号。
这些命令要求的参数说明如下:
breakpointno – 0~3
length - 1~3
address - 16进制内存位置(没有0x),如:c015e9bc
注1:
telnet远程设置:
Config模式下:
line vty 0 4
no login
privil level 15
注2:
修改串口波特率:
stty ispeed 115200 ospeed 115200 -F /dev/ttyS0
注3:
有时候gdb会收到非法报文,提示Ignoring packet error,continue…
修改printk级别,不再向串口输出打印信息
echo 0 > /proc/sysrq-trigger