一.基本原理
调试器是运行在host pc机的应用程序, 被调试的程序是运行在target上。
插桩(stub):在目标操作系统和调试器内分别加入某些功能模块,二者互通信息来进行调试。
调试器与被调程序的通信: Gdb和调试stub通过GDB串行协议进行通信。指定通信端口(串口 并口 网卡)遵循远程调试协议进行通信。GDB串行协议是一种基于消息的ASCII码协议,包含了诸如读写内存、查询寄存器、运行程序等命令。
被调程序产生异常并及时通知调试器:通过通信模块,告知调试器当前的异常号;调试器根据此向用户显示被调试程序产生了拿一类异常。
目标操作系统需要定义一个设置断点的函数。目标系统添加的这些模块通称为插桩,驻留在ROM中称为ROM monitor。若将kgdb开关打开,就相当于加入了插桩。
运行于目标系统的被调试程序要在入口处调用设置断点的函数产生异常,异常处理程序调用调试端口通信模块,等待主机上的调试器发来信息。双方建立连接后,调试器便等待用户发出调试命令,目标系统等待调试器根据用户命令生成的指令。
所有的符号表都放在本地主机上,在调试之前将符号表导入,调试就可以对着源码进行调试了。
当调试一个远端目标设备时,gdb依靠了一个调试stub来完成其功能。调试stub即是嵌入式系统中一小段代码,它提供了运行gdb的宿主机和所调试的应用程序间的一个媒介。
为了设置断点,gdb使用内存读写命令,来无损害地将原指令用一个TRAP命令或其它类似的操作码代替,使得执行该命令时,可以使得控制权转移到调试stub手中去。在此时,调试stub的任务就是将当前场景传送给gdb(通过远程串行通信协议),然后从gdb处接收命令,该命令告诉了stub下一步该做什么。
当处理器遇到了一个TRAP指令(该指令是由gdb 设置的,做断点用)时,该指令使得处理器的当前场景转向一个名为gdb_exception()的函数。最终,目标调用了gdb_return_from_exception()函数,该函数恢复了处理器的场景并将控制权交给应用程序。
二.GDB常用命令
file 载入命令 先运行gdb 然后file gdbtest
quit q 推出 gdb
run r 运行载入的程序
info 查看程序信息 info source; info stack; info local; info br; info prog; info var; info function
list l 显示源代码段 list function; list linenum; list; list-; list finelname:function; list filename;
break 设置断点 break function; break linenum; break filename:linenum;
next n 在不单步执行进入其他函数的情况下,向前执行一行源代码
step s 单步执行并进入函数
continue c 继续执行正在调试的程序。
finish 跳出执行函数
print p 打印数据 print exp; /F选择输入打印格式 x d u f
set 修改变量值 set variable=value
x 检查内存 x /NFU ADDR N重复数 F输出格式 U每个数据单位大小b h w g x/4ub 0x4000
clear 删除设置的的断点。
display 每次程序停止后显示表达式的值。display exp
bt 显示所有的调用堆栈。该命令可以用来显示函数的调用顺序
kill 终止正被调试的程序
make 在不退出gdb的情况下运行make工具
help 显示指定命令的帮助信息 help name
GDB为每个断点赋上一个数字,断点有四种状态:有效(enable) 禁止(disable) 一次有效(enable once) 有效后删除(enable for deletion)
enable 2
disable 2
delete 2
三.编译 GDB
#copy gdb-5.3.tar.gz /root //将gdb-5.3.tar.gz拷贝到/root目录下,或任意别的目录
#cd /root
#tar zxvf gdb-5.3.tar.gz
3.1 编译GDB Server
#cd gdb-5.3
#vi ./gdb/arm-tdep.c
default: break; // line 2753添加breaks; 因为如果编译器版本较新 可能出错
#./configure --target=arm-linux --prefix=/usr/local/arm-gdb
#make
#make install // 生成/usr/local/arm-gdb/bin
3.2 编译GDB Client
#cd /root/gdb-5.3
#export PATH=$PATH:/usr/local/arm-gdb/bin
#./configure --target=arm-linux --host=arm-linux
#cd ./gdb/gdbserver
#vi config.h
//#define HAVA_SYS_REG_H //注释此句
#make CC=arm-linux-gcc //编译用于目标机的stub程序 生成gdbserver是GDB客户端程序,在板子上运行。
四.实战调试
1.编辑文件
# vi gdbtest.c
1 #include <stdio.h>
2
3 int
4 func(int n){
5 int sum=0, i;
6 for (i=0; i<n; i++){
7 sum += i;
8 }
9 return sum;
10 }
11
12 int
13 main(void)
14 {
15 int i;
16 long result = 0;
17 for (i=0; i<=100; i++){
18 result += i;
19 }
20
21 printf("result[1-100] = %d \n", result);
22 printf("resutl[1-225] = %d \n", func(255));
23
24 return 0;
25 }
# arm-linux-gcc -g gdbtest.c -o gdbtest // 交叉编译
2.下载
假定 host pc ip:192.168.1.22
board ip:192.168.1.123
将文件拷贝到目标板上 有两种方法:
1)Zmodem
minicom 连接到开发板 登陆进入
#cd /root
ctrl+A然后按Z键 再按S键 选择zmodem(选择目录后,在弹出的框内输入文件完整路径名)下载gdbserver gdbtest 到/root
2)ftp
host pc: 开通ftp服务 // 下面简要介绍在pc机上开通ftp服务
#adduser embedded // 新建用户 假定embedded
#passwd embedded // 输入密码 假定embedded
#vi /etc/vsftpd/vsftpd.conf
chroot_list_enable=YES #禁止用户离开自己的目录
# (default follows)
chroot_list_file=/etc/vsftpd.chroot_list #被禁止的用户名单文件
#vi /etc/vsftpd.chroot_list
embedded // add
#/etc/rc.d/initd.d/vsftpd start // 开通ftp服务
把要传递的文件gdbserver gdbtest拷贝到/home/embedded/目录下
client board:
ftp 192.168.1.22 // 下载需要的文件
注意:我们通过nfs可以把pc机的某个目录挂载到板子上,挂载点下经过交叉编译过的可执行文件可以在板子上运行,但不能把挂载点下的文件拷贝到板子的其他目录下。
3.运行调试
client board:
#./gdbserver 192.168.1.22:1234 gdbtest // 目标板上运行gdbtest 监听端口1234
host pc:
#cd /usr/local/arm-gdb/bin/
#copy gdbtest /usr/local/arm-gdb/bin/ // 将前面编译的文件gdbtest拷贝到此目录
#./arm-linux-gdb gdbtest
(gdb)target remote 192.168.1.123:1234 // 连接到开发板 成功后就可以进行调试
(gdb)symbol-file gdbtest
(gdb)list or l
(gdb)break func
(gdb)break 22
(gdb)info br
(gdb)continue or c // 这里不能用 run
(gdb)next or n
(gdb)print or p result
(gdb) finish // 跳出func函数
(gdb) next
(gdb) quit
建立连接后进行gdb远程调试和gdb本地调试方法是一样的。这里给出的代码是正确的,可以加一些错误代码进行调试。
评论