4.一个简单的例子
上面仅是GDB常用指令的简单介绍。本节将结合一个简单的例子,向大家演示这些常用指令的具体应用。这是一个冒泡排序算法的程序,这个例子的目的仅仅是演示,并不是实际调试。将下面的源程序存为bubble.c文件,并编译好。
#include <stdio.h> #define MAX_RECORD_NUMBER 10 int record[MAX_RECORD_NUMBER] = {12,76,48,62,94,17,37,52,69,32}; swap(int * x , int * y ) { int temp; temp = *x; *x = *y; *y = temp; } int main() { int i,j; for( i = 0 ; i < MAX_RECORD_NUMBER - 1; i++ ) { for( j = MAX_RECORD_NUMBER - 1; j > i; j--) if( record[j] < record[j-1] ) swap(&record[j],&record[j-1]); } for( i = 0; i < MAX_RECORD_NUMBER -1; i++) printf("%d ",record[i]); printf("/n"); return 1; } |
记得在编译时用-g开关。如:gcc -g -o bubble bubble.c。你能在当前子目录下得到一个编译好的文件bubble。我们下面将以这个程序为例子向大家演示上面的指令在实际中的应用。
首先启动GDB,可以在启动的同时载入文件bubble,如:gdb bubble。也可以分两步进行,先启动GDB,执行gdb,进入GDB后,再执行file bubble。
这时可以用list指令列出源程序,list的使用比较简单,但其实在GDB中最不方便的就是看源程序,主要原因是因为GDB仅是一个文本方式的调试器,无法让你用鼠标和光标键来翻阅源程序,在这方面ddd等窗口程序有巨大的优势。
我们先来查看一下当前源程序的信息,如下:
(gdb) info source Current source file is bubble.c Compilation directory is /root/sample/ Located in /root/sample/bubble.c Contains 32 lines. Source language is c. Compiled with stabs debugging format. |
我们可以知道程序名,目录,文件大小,语言等信息。
下面我们来设置断点,我们想在函数swap出设置一个断点:
(gdb) br swap
Breakpoint 1 at 0x80483d6: file bubble.c, line 11.
br是break的简写。上面的一行是GDB告诉我们这个断点的信息,我们可以知道这个断点的断点号是1,地址是0x80483d6,它在文件bubble.c的11行。
我们再在一个行号上设一个断点,
(gdb) br 23
Breakpoint 2 at 0x804844a: file bubble.c, line 23.
我们已经设了两个断点,许多时候你会想查看一下断点的信息和状态,因此你会用到你最常使用的info指令,info br。
(gdb) info br
Num Type Disp Enb Address What
1 breakpoint keep y 0x080483d6 in swap at bubble.c:11
2 breakpoint keep y 0x0804844a in main at bubble.c:23
我用这条指令的大多数原因是想查看一下某个断点的断点号,就是第一列的数值。有时也会看一下断点的状态是enable还是disable。以上的两个断点都是y,也就是都处于enable状态。type列显示breakpoint,是因为info br指令同时也会显示watch的信息,因此用type来识别是断点breakpoint还是检查点watch。
如果你知道断点号,想删除断点很简单,例如想删除断点2,执行del 2就行了。
在程序中,断点2本来设在循环中,那样程序会频烦断下,这也许不是我们希望的。也许我们仅想在某个条件下让它断下,如想当j=5时。
(gdb) br 23 if j==5 Breakpoint 3 at 0x804844a: file bubble.c, line 23. (gdb) info br Num Type Disp Enb Address What 1 breakpoint keep y 0x080483d6 in swap at bubble.c:11 3 breakpoint keep y 0x0804844a in main at bubble.c:23 stop only if j == 5 |
注意现在的断点信息,虽然断点2被删除了,但新设的断点号没有使用2号,而是使用了3号。新设的断点是个条件断点,这从"stop only if j == 5"可以清楚的看出。
现在执行程序,输入run指令。
(gdb) run Starting program: /root/sample/bubble Breakpoint 1, swap (x=0x80495a4, y=0x80495a0) at bubble.c:11 11 temp = *x; |
程序已经在断点1停了下来。当断点停下时,我们经常需要查看变量值。如查看x值。
(gdb) p x
$1 = (int *) 0x80495a4
GDB告诉我们x是一个指向整数的指针,指针值是0x80495a4。如果想查看指针指向的值。执行:
(gdb) p *x
$2 = 32
(gdb) p *y
$3 = 69
单步执行
(gdb) n
12 *x = *y;
查看变量temp值
(gdb) p temp
$4 = 32
(gdb) n
13 *y = temp;
(gdb) p *x
$5 = 69
现在删除断点1
(gdb) del 1
继续执行
(gdb) cont
Continuing.
Breakpoint 3, main () at bubble.c:23
23 swap(&record[j],&record[j-1]);
程序在断点3停下,记得断点3是个条件断点。要验证很简单,查看一下变量j的值是不是5。
(gdb) p j
$6 = 5
我们可以查看一下全局变量record的值,
(gdb) p record
$7 = {12, 76, 48, 62, 94, 17, 32, 37, 52, 69}
也可以查看一下变量record的地址,
(gdb) p &record
$8 = (int (*)[10]) 0x8049580
知道地址时,也可以x指令查看内存值。
(gdb) x/4uw 0x8049580
0x8049580 : 12 76 48 62
上面的指令查看4个4字节数,以整数方式显示。可以看到这与reocrd值是相附的。
(gdb) x/4bb record
0x8049580 : 12 0 0 0
显示4个单字节数,以字节当时显示。上面的4个字节值正好是record数组第一个整数值,因为整数是4字节,而且intel机器的数值是低字节在前。
改变变量值也很简单,如果想将reocrd数组第一个值该为1,
(gdb) set record[0]=1
看一下值是否改变了。
(gdb) p record
$10 = {1, 76, 48, 62, 94, 17, 32, 37, 52, 69}
第一个值以改成了1。
以上简单地介绍了一些常用的GDB指令,由于篇幅所限,我们无法涉及GDB所有指令及GDB其它许多功能,读者应当自己在实践中不断地学习。Linux系统中会有详细的GDB的资料,你可以用info gdb来查阅这些资料。