本节书摘来自异步社区《嵌入式 Linux C 语言应用程序设计(修订版)》一书中的第2章,第2.4节,作者 华清远见嵌入式培训中心,孙琼,更多章节内容可以访问云栖社区“异步社区”公众号查看。
2.4 嵌入式Linux调试器GDB的使用
在程序编译通过生成可执行文件之后,就进入了程序的调试环节。调试一直来是程序开发中的重中之重,如何使程序员能够迅速找到错误的原因是一款调试器的目标。
GDB是GNU开源组织发布的一个强大的Linux下的程序调试工具,它是一种强大的命令行调试工具。
一个出色的调试器需要有以下几项功能。
- 能够运行程序,设置所有能影响程序运行的参数。
- 能够让程序让指定的条件下停止。
- 能够在程序停止时检查所有参数的情况。
- 能够根据指定条件改变程序的运行。
2.4.1 GDB使用实例
下面通过一个简单的实例使读者对GDB有一个感性的认识,这里所介绍的指令都是GDB中最为基本也是最为常用的指令,希望读者能够动手操作,掌握GDB的使用方法。 首先,有以下程序段。
#include <stdio.h>
/*子函数add:将自然数从1~m相加*/
int add(int m)
{
int i,n=0;
for(i=1; i<=m;i++)
n += i;
printf("The sum of 1-%d in add is %d\n", m,n);
}
int main()
{
int i,n=0;
add(50);
for(i=1; i<=50; i++)
n += i;
printf("The sum of 1-50 is %d \n", n );
}
注意将此程序用GCC进行编译时要加上“-g”选项。
1.进入GDB
进入GDB只需输入GDB和要调试的可执行文件即可,如下所示:
[root@localhost gdb]# gdb test
GNU gdb Red Hat Linux (6.3.0.0-1.21rh)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
(gdb)
可以看出,在GDB的启动画面中指出了GDB的版本号、使用的库文件等信息,接下来就进入了由“(gdb)”开头的命令行界面了。
2.查看文件
在GDB中键入‘l’(list)就可以查看所载入的文件,如下所示:
(gdb) l
4 {
5 int i,n=0;
6 for(i=1; i<=m;i++)
7 n += i;
8 printf("The sum of 1-%d in add is %d\n", m,n);
9 }
10
11 int main()
12 {
13 int i,n=0;
(gdb) l
14 add(50);
15 for(i=1; i<=50; i++)
16 {
17 n += i;
18 }
19 printf("The sum of 1-50 is %d \n", n );
20
21 }
22
可以看出,GDB列出的源代码中明确地给出了对应的行号,这样可以大大地方便代码的定位。
注意 在一般情况下,源代码中的行号与用户书写程序中的行号是一致的,但有时由于用户的某些编译选项会导致行号不一致的情况,因此,一定要查看在GDB中的行号。
3.设置断点
设置断点可以使程序到一定位置暂停它的运行,程序员在该位置处可以方便地查看变量的值、堆栈情况等,从而找出代码的症结所在。
在GDB中设置断点非常简单,只需在“b”后加入对应的行号即可(这是最常用的方式,另外还有其他方式设置断点),其命令如下所示:
(gdb) b 6
Breakpoint 1 at 0x804846d: file test.c, line 6.
要注意的是,在GDB中利用行号设置断点是指代码运行到对应行之前暂停,如上例中,代码运行到第5行之前暂停(并没有运行第5行)。
4.查看断点处情况
在设置完断点之后,用户可以键入“info b”来查看设置断点情况,在GDB中可以设置多个断点。
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804846d in main at test.c:6
5.运行代码
接下来就可运行代码了,GDB默认从首行开始运行代码,可键入“r”(run)即可,在“r”后面加上行号即可从程序中指定行开始运行。
(gdb) r
Starting program: /home/yul/book/test
Breakpoint 1, add (m=50) at test.c:6
6 for(i=1; i<=m;i++)
可以看到,程序运行到断点处就停止了。
6.查看变量值
在程序停止运行之后,程序员需要查看断点处的相关变量值。在GDB中只需键入“p+变量值”即可,如下所示:
(gdb) p n
$1 = 0
(gdb) p i
$2 = 134518440
在此处,为什么变量“i”的值为如此奇怪的一个数字呢?原因就在于程序是在断点设置的对应行之前停止的,那么在此时,并没有把“i”的数值赋为0,而只是一个随机的数字。但变量“n”是在第5行赋值的,故在此时已经为0。
小技巧 GDB在显示变量值时都会在对应值之前加上“$N”标记,它是当前变量值的引用标记,所以以后若想再次引用此变量就可以直接写作“$N”,而无需写冗长的变量名。
7.观察变量
在某一循环处,程序员往往希望能够观察一个变量的变化情况,这时就可以键入命令“watch”来观察变量的变化情况,如下所示:
(gdb) watch n
Hardware watchpoint 2: n
可以看到,GDB在“n”设置了观察点。
注意 在此处必须键入完整的命令“watch”,因为在GDB中有不少以‘w’开头的命令,如“where”、“while”等。
8.单步运行
单步运行是指一次只运行一条语句,这样可以方便程序员来查看程序运行的结果,在此处只需键入“n”(next)即可。
(gdb) n
7 n += i;
(gdb) n
Hardware watchpoint 2: n
Old value = 15
New value = 21
可以看到,随着程序的单步运行,当“n”的值发生变化时,GDB就会自动显示出n的变化情况。
9.程序继续运行
命令“c”(continue)可以使GDB继续运行以下的程序,程序在再次遇到断点时停止,如下所示:
(gdb) c
Continuing.
The sum of 1-50 is 1275
Program exited with code 031.
10.退出GDB
退出GDB只需使用指令“q”(quit)即可,如下所示:
(gdb) q
[root@localhost gcc]
到此为止,使用GDB的整体过程已经结束了。以上所讲述的命令是GDB中最为常见的命令,下面几节将会详细讲解GDB的命令。
2.4.2 设置/删除断点
GDB中有丰富的断点设置、删除命令,可以满足用户各个方面的需求。表2.8列出了GDB中常见的断点设置及删除命令。
小知识 在多线程的程序中,观察点的作用很有限,gdb只能观察在一个线程中的表达式的值。如果用户确信表达式只被当前线程所存取,那么使用观察点才有效。gdb不能注意一个非当前线程对表达式值的改变。
2.4.3 数据相关命令
在GDB中也有丰富的数据显示相关命令,他们可以使用户可以以各种形式显示所要查看的数据,数据相关命令如表2.9所示。
小技巧 在使用print命令时,可以对变量按指定格式进行输出,其命令格式为:print /变量名+格式其中格式有以下几种方式。
X:十六进制;d:十进制;u:无符号数;o:八进制;
T:二进制;a:十六进制打印;c:字符格式;f:浮点数。
2.4.4 调试运行环境相关命令
在GDB中控制程序的运行也是非常方便地,用户可以自行设定变量值、调用函数等,其具体命令如表2.10所示。
2.4.5 堆栈相关命令
gdb中也提供了多种堆栈相关的命令,可以查看堆栈的情况、寄存器的情况等,其具体命令如表2.11所示。