调试是所有程序员都会面临的问题. 如何提高程序员的调试效率, 更好更快地定位程序中的问题从而加快程序开发的进度, 是大家共同面对的问题. 就如读者熟知的Windwos下的一些调试工具, 如VC自带的设置断点, 单步跟踪等, 都受到了广大用户的赞赏. 那么, 在Liunx下有什么好的调试工具呢?
Gdb是一款GNU开发组织并发布的Linux下的程序调试工具. 虽然它没有图形化的友好界面, 但是它强大的功能也足以与微软的VC工具相媲美.
下面举一个例子, 演示一下Gdb的使用流程:
测试文件 test.c 的代码如下:
#include <stdio.h>
int sum(int m);
int main()
{
int i, n=0;
sum(50);
for(i=1; i<=50; i++)
{
n += i;
}
printf("The sum of 1-50 is %d \n", n);
}
int sum(int m)
{
int i, n=0;
for(i=1; i<=m; i++)
n += i;
printf("The sum of 1-m is %d\n", n);
}
编译源代码文件, 生成可执行文件.
注意:一定要加上选项"-g", 这样编译出的可执行代码中才包含调试信息, 否则Gdb无法载入该可执行文件.
$ gcc -g test.c -o test
虽然这段程序没有错误, 但调试完全正确的程序可以更加了解Gdb的使用流程. 接下来就启动Gdb进行调试. 注意, Gdb进行调试的是可执行文件, 而不是".c"源文件, 因此, 需要先通过Gcc编译生成可执行文件才能用Gdb进行调试.
启动Gdb
$gdb test
GNU gdb (GDB) 7.0-debian
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/wangsheng/tmp/demo/gdb/test...done.
可以看出, 在Gdb的启动画面中指出了Gdb的版本号, 使用的库文件等头信息, 接下来就进入了由"(gdb)"开头的命令行界面了.
(1) 查看源文件
在Gdb中键入"l"(list的缩写)可以查看所载入的文件, 如下所示:
(gdb) l
1 #include <stdio.h>
2 int sum(int m);
3 int main()
4 {
5 int i, n=0;
6 sum(50);
7 for(i=1; i<=50; i++)
8 {
9 n += i;
10 }
(gdb) l
11 printf("The sum of 1-50 is %d \n", n);
12 }
13
14 int sum(int m)
15 {
16 int i, n=0;
17 for(i=1; i<=m; i++)
18 n += i;
19 printf("The sum of 1-m is %d\n", n);
20 }
(gdb) l
Line number 21 out of range; test.c has 20 lines.
可以看出, Gdb列出的源代码中明确地给出了对应的行号, 这样就可以大大地方便代码的定位.
(2) 设置断点
设置断点是调试程序中一个非常重要的手段, 它可以使程序到一定位置暂停运行. 因此,可以在该位置方便地查看变量的值, 堆栈情况等, 从而找出代码的症结所在.
在Gdb中设置断点非常简单, 只需在"b"后加入对应的行号即可(这时最常用的方式). 如下所示:
(gdb) b 6
Breakpoint 1 at 0x4004fb: file test.c, line 6.
注意: 该断点的作用是当程序运行到第6行时暂停(第5行执行完毕, 第6行未执行)
(3) 查看断点情况
在设置完断点之后, 用户可以键入"info b" 来查看设置断点情况, 在Gdb中可以设置多个断点.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x00000000004004fb in main at test.c:6
(4) 运行代码
接下来就可运行代码了, Gdb默认从首行开始运行代码, 可键入"r"(run的缩写)即可. 若想从程序中指定的行开始运行, 可在r后面加上行号.
(gdb) r
Starting program: /home/wangsheng/tmp/demo/gdb/test
Breakpoint 1, main () at test.c:6
6 sum(50);
可以看到程序运行到断点处就停止了.
(5) 查看变量值
在程序停止运行之后, 程序员所要做的工作是查看断点处的相关变量值. 在Gdb中只需键入"p"(print的缩写) +变量名称 即可.
(gdb) p n
$1 = 0
(gdb) p i
$2 = 0
(6) 单步运行
单步运行可以使用n(next的缩写)或者s(step的缩写), 它们之间的区别在于: 若有函数调用的时候, s会进入该函数而n不会. 因此, s就类似于VC等工具中的"step in", n就类似于VC等工具中的"step over".
下面是使用n命令的输出
(gdb) n
The sum of 1-m is 1275
7 for(i=1; i<=50; i++)
下面是使用s命令的输出
(gdb) s
sum (m=50) at test.c:16
16 int i, n=0;
可以看出执行s命令时进入了sum函数内部, 而n命令则跳过函数的调用部分
(7) 恢复程序运行
在查看变量值以及堆栈之后, 就可以使用命令c(continue)恢复程序的正常运行了. 这时, 它会把剩余还未执行的程序执行完, 并显示剩余程序的执行结果.
(gdb) c
Continuing.
The sum of 1-m is 1275
The sum of 1-50 is 1275
Program exited with co
de 031.
可以看出, 程序在运行完后退出, 之后程序处于"停止状态".
说明: 在Gdb中, 程序的运行状态有"运行","暂停"和"停止"3种. 其中"暂停"状态是程序遇到了断点或者观察点, 程序暂时停止运行, 而此时函数的地址, 函数参数, 函数内的局部变量都会被压入"栈(Stack)中. 故在这种状态下可以查看函数的变量值等各种属性. 但在函数处于"停止"状态之后, "栈"就会自动撤销, 它也就无法查看各种信息了.