嵌入式Linux C语言开发工具—调试器gdb详解

时间:2021-11-12 20:17:23

    程序的调试工作在整个程序的开发过程中占据了相当大的比例。使用gcc调试C程序时,只能依靠gcc发出的警告或错误信息来进行,所以调试的效率非常低。

     为此,GNU开发了GDB调试器(GNU Debugger)。GDB的调试功能非常强大,甚至可以和Visual C++、Visual Basic、Jbuilder等开发工具的调试器相媲美。但GDB的缺点是没有图形调试界面。尽管如此,对于从事嵌入式Linux应用开发的人员还是有必要知道GDB的使用方法的。

例如:

test.c:

 

test.c文件是一个通过递归调用来计算5的阶乘的程序。通过运行命令“gcc –g test.c -o test”对test.c进行编译,其中参数g的作用是把调试信息加入生成的test可执行文件中,否则GDB就无法对test进行调试。

接下来可以使用命令“gdb test”启动GDB对test进行调试了。如下所示:

[root@localhost home]# gdb test

GNU gdb Everest Linux (6.4-1)

Copyright 2005 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 "i686-pc-linux-gnu"...Using host libthread_db

library "/lib/libthread_db.so.1".

可以看到,GDB首先显示了版本信息和库信息。随后GDB停留在符号“(gdb)”处等待用户输入调试命令。GDB提供了大量的命令来实现各种调试功能,下面仅对一些常用的命令进行介绍。

(1)查看源文件

在调试程序时,gcc会给出产生警告或错误的代码行数。但在普通的文本环境中是无法

直接获得语句行数的。在GDB中通过命令l(list的缩写)可以查看所有的代码行数。如下所示:

(gdb) l

2       int cal(int n)

3       {

4          if(n == 1)

5            return 1;

6          else

7            return n * cal(n - 1);

8       }

9

10      int main()

11      {

(gdb) l

12         int n = 5;

13         n = cal(n);

14         printf("%/d",n);

15         return 0;

16      }

(gdb) l

Line number 17 out of range; test.c has 16 lines.

可以看到,GDB以10行为单位进行显示。再运行一次命令l就会显示下10行代码。这样设计方便了源代码的阅读。

(2)设置断点

断点是调试程序的重要方法,通过断点可以知道程序每一步的执行状况(比如当前变量

的值、函数是否调用、堆栈使用情况等)。在GDB中通过命令b(breakpoint的缩写)进行断点设置。如下所示:

(gdb) b 7

Breakpoint 1 at 0x8048389: file test.c, line 7.

可以看到,命令b在程序的第7行处设置了第一个断点,并显示了该断点在内存中的物理地址。

(3)查看断点情况

由于使用命令b可以设置多个断点,所以用户需要能够随时查看各个断点的情况。在GDB中通过命令“info b”查看所有的断点情况。如下所示:

(gdb) info b

Num Type         Disp Enb  Address     What

1   breakpoint   keep y    0x08048389  in cal at test.c:7

可以看到,GDB在程序的第7行处设置了第一个断点,并显示了断点的位置信息。

(4)运行程序

在GDB中通过命令r(run的缩写)运行程序。GDB默认从代码的首行开始运行(也可

以通过“r 行数”的方式让程序从指定行数开始运行)。如果程序中有断点,则程序会在断点行数的前一行暂停运行,结果如下所示:

(gdb) r

Starting program: /home/test

 

Breakpoint 1, cal (n=5) at test.c:7

7         return n * cal(n - 1);

可以看到,程序在运行到第7行时就暂停了,没有继续执行第8行的代码。

(5)查看变量值

程序暂停运行后就可以查看当前的状态了。在GDB中通过命令“p 变量名”(print的缩写)查看当前变量n的值。如下所示:

(gdb) p n

$1 = 5

GDB通过“$N”(“$1”、“$2”)来显示变量的值。这样在下次查看变量值时,就可以用“$N”代替变量名了。可以看到,当前变量n的值为5。

(6)继续运行程序

查看完当前程序的情况后,就可以让程序继续往下运行了。在GDB中通过命令c让程序

继续往下运行。在test.c中,由于函数cal是递归调用运行,所以程序会再次在断点处暂停。如下所示:

(gdb) c

Continuing.

 

Breakpoint 1, cal (n=4) at test.c:7

7         return n * cal(n - 1);

程序暂停后可以再次查看当前变量n的值。如下所示:

(gdb) p n

$2 = 4

(7)单步运行

在程序逻辑比较复杂的时候往往需要程序能一步一步的往下运行,但如果每行都设置一

个断点的话又会很麻烦。在GDB中可以通过命令s(step的缩写)和n(next的缩写)让程序一步一步的往下运行。其中s可以在发生函数调用时进入函数内部运行,而n不会进入函数内部运行。在test.c中。由于函数cal是递归调用运行,所以只能选择s才能看到变量n的值。如下所示:

(gdb) s

cal (n=3) at test.c:4

4       if(n == 1)

(gdb) s

 

Breakpoint 1, cal (n=3) at test.c:7

7       return n * cal(n - 1);

(gdb) s

cal (n=2) at test.c:4

4       if(n == 1)

(gdb) s

 

Breakpoint 1, cal (n=2) at test.c:7

7        return n * cal(n - 1);

(gdb) s

cal (n=1) at test.c:4

4        if(n == 1)

(gdb) s

5        return 1;

由于在使用s前函数cal已经调用了两次,所以运行s后当前变量n的值为3。可以看到,函数cal进行3次调用后返回了值1。

此外,GDB还具有很多的功能,比如程序环境设置、使用shell命令等。由于后续章节会介绍图形调试工具,所以这里就不再对GDB进行深入介绍了。有兴趣的读者可以参考相关资料。