1.4 嵌入式Linux调试器GDB的使用
在程序编译通过生成可执行文件之后,就进入了程序的调试环节。调试一直是程序开发中的重中之重,如何使程序员能够迅速找到错误的原因是一款调试器的首要目标。
GDB是GNU开源组织发布的一个Linux下的程序调试工具,它是一种强大的命令行调试工具。
一个出色的调试器需要有以下几项功能:
· 能够运行程序,设置所有能影响程序运行的参数。
· 能够让程序在指定的条件下停止运行。
· 能够在程序停止时检查所有参数的情况。
· 能够根据指定条件改变程序的运行。
1.4.1 GDB使用实例
下面通过一个简单的实例使读者对GDB有一个感性的认识,这里所介绍的指令都是GDB中最为基本也是最为常用的指令,希望读者能够动手操作,掌握GDB的使用方法。程序代码如下:
#include <stdio.h> /*子函数add:将自然数从1~m 相加*/ void 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 (GDB) 7.2-ubuntu Copyright (C) 2010 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later This is free software: you are free to chang and redistribute it. There is NO WARRANTY, to the extent perimitted by law.Type “show copying” and “show warranty” for details. This GDB was configured as “i686-linux-gnu”. For bug reporting instructions, please see: http://www.gnu.org/software/gdb/bugs/. (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中利用行号设置断点是指代码运行到对应行之前暂停。如上例中,代码运行到第6行之前暂停(并没有执行第6行的代码)。
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)即可,如下所示:
(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行赋值的,所以此时n的值已经为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) 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中最为常见的命令,下面几节将会详细讲解其他的一些命令。
1.4.2 设置/删除断点
GDB中有丰富的断点设置、删除命令,可以满足用户各个方面的需求。如表1.8所示列出了GDB中常见的断点设置及删除命令。
表1.8 GCC中常见断点设置与删除指令
小知识
在多线程的程序中,观察点的作用很有限,GDB只能观察一个线程中的表达式的值。如果用户确信表达式只被当前线程所存取,那么使用观察点才有效。GDB无法检测一个非当前线程对表达式值的改变。
1.4.3 数据相关命令
在GDB中也有丰富的数据显示相关命令,它们使用户可以以各种形式显示所要查看的数据,数据相关命令如表1.9所示。
表1.9 GDB中数据相关指令
小技巧
在使用print命令时,可以对变量按指定格式进行输出,其命令格式为:print /变量名+格式。
其中常用格式有以下几种:
x,十六进制;d,十进制;u,无符号数;o,八进制;c,字符格式;f,浮点数。
1.4.4 调试运行环境相关命令
在GDB中控制程序的运行也是非常方便的,用户可以自行设定变量值、调用函数等,其具体命令如表1.10所示。
表1.10 GDB调试运行环境相关命令
1.4.5 堆栈相关命令
GDB中也提供了多种堆栈相关的命令,可以查看堆栈的情况、寄存器的情况等,其具体命令如表1.11所示。
表1.11 GDB中堆栈相关命令