引题:
有一待调试的C语言源程序,请问在Linux系统下应该如何进行调试操作,写出相关的命令(假定源文件名为 test.c)
解答:
gcc -g test.c -o test
编译出可执行文件 test,然后
gdb test
然后用 l
看源码,用b设置断点,用r开始运行程序,用p打印变量,用n单步执行下一条语句,用s单步进入函数,用help查看所有命令,用help
xxx查看xxx命令的帮助。
------------------------------------------------------------------------
gcc中的-g选项:
-g Produce debugging
information in the operating system's native for‐
mat (stabs, COFF, XCOFF, or DWARF 2). GDB can
work with this
debugging information.
On most systems that use stabs format, -g enables use of
extra
debugging information that only GDB can use; this extra
information
makes debugging work better in GDB but will probably make
other
debuggers crash or refuse to read the program.
------------------------------------------------------------------------
调 试是每个程序员都会面临的问题. 如何提高程序员的调试效率, 更好更快地定位程序中的问题从而加快程序开发的进度,
是大家共同面对的问题. 可能Windows用户顺口就会说出:用VC呗 , 它提供了设置断点, 单步跟踪等的图形界面,
使调试起来直观易用. 但Linux用户可能要生闷气了 : 难道我们Linux程序员就只能使用原始的调试方法,
在代码中加入printf信息吗?难道Linux下就没有好的C语言调试工具吗?
当然不是了. GNU早就组织开发了一套C语言编译器(Gcc)和调试工具(Gdb).
Gdb虽然没有图形化的友好界面, 但是它强大的功能也足以与微软的VC工具相媲美, 给Linux程序员带来了福音.
下面通过一个简单的例子, 演示一下Gdb的使用流程:
示例文件 demo.c 的源代码如下:
#include
int sum(int, int);
int main()
{
int
result;
int a = 1, b
= 2;
result =
sum(a, b);
printf("%d +
%d = %d\n", a, b, result);
return
0;
}
int
sum(int a, int b)
{
return a +
b;
}
编译源文件, 生成可执行文件
$ gcc -g -Wall -o demo demo.c
虽然这段程序没有错误, 但调试完全正确的程序可以更加了解Gdb的使用流程. 接下来就启动Gdb进行调试.
注意:
Gdb进行调试的是可执行文件, 而不是”.c”源文件, 因此, 需要先通过Gcc编译生成可执行文件才能用Gdb进行调试.
一定要加上选项”-g”, 这样编译出的可执行代码中才包含调试信息,
否则Gdb无法载入该可执行文件.不能使用 -O2选项对可执行文件进行优化,
因为优化之后可执行文件里的符号表信息将被删除, 这样Gdb就无法找到使可执行文件与源文件之间的关联了, 也就不能调试了.
(1) 启动Gdb
$ gdb demo可以看出, 在Gdb的启动画面中指出了Gdb的版本号, 使用的库文件等头信息,
接下来就进入了由”(gdb)”开头的命令行界面了.
(2) 查看源文件
在Gdb中键入”l”(list的缩写)可以查看所载入的文件, 如下所示:
(gdb) l
1 #include
2
3 int sum(int, int);
4
5 int
6 main()
7 {
8 int result;
9 int a = 1, b = 2;
10 result = sum(a, b);
(gdb) l
11 printf("%d + %d = %d\n", a, b, result);
12 return 0;
13 }
14
15 int
16 sum(int a, int b)
17 {
18 return a + b;
19 }
(gdb) l
Line number 20 out of range; demo.c has 19 lines.
可以看出, Gdb列出的源代码中明确地给出了对应的行号,
这样就可以大大地方便代码的定位.
(3) 设置断点
设置断点是调试程序中一个非常重要的手段, 它可以使程序到一定位置暂停运行. 因此,可以在该位置方便地查看变量的值, 堆栈情况等,
从而找出代码的症结所在.
在Gdb中设置断点非常简单, 只需在”b”后加入对应的行号即可(这是最常用的方式).
如下所示:
(gdb) b 9Breakpoint 2 at 0x4004f4: file
demo.c, line 9.
注意: 该断点的作用是当程序运行到第 9 行时暂停(第 8 行执行完毕, 第 9
行未执行)
注意:用cleaer 9 可以取消断点。
(4) 查看断点信息
(gdb) info
bNum Type Disp Enb
Address What
2 breakpoint keep y 0x00000000004004f4 in
main at demo.c:9
(5) 运行代码
接下来就可运行代码了, Gdb默认从首行开始运行代码, 可键入”r”(run的缩写)即可.
若想从程序中指定的行开始运行, 可在r后面加上行号.
(gdb) r
Starting program: /home/wangsheng/tmp/demo/gdb/demo
Breakpoint 2, main () at demo.c:9
9 int a = 1, b = 2;
可以看到程序运行到断点处就停止了.
(6) 查看变量值
键入p(print的缩写)+变量名即可查看该变量在此时的值(gdb) p a
$1 = 1
(gdb) p b
$2 = 2
(gdb) p result
$3 = 32767
在你调试程式时,当程式被停住时,你能使用 print
命令(简写命令为 p ),或是同义命令 inspect 来查看当前程式的运行数据。 print 命令的格式是:
/
是表达式,是你所调试的程式的语言的表达式( GDB 能调试多种编程语言), 是输出的格式,比如,如果要把表达式按 16
进制的格式输出,那么就是 /x 。
p/d就表示十进制输出
注意: 这里之所以result是一个莫名其妙的值, 是因为声明result是没有初始化, 其值是不固定的。
(7) 单步执行
单步运行可以使用n(next的缩写)或者s(step的缩写), 它们之间的区别在于: 若有函数调用的时候,
s会进入该函数而n不会. 因此, s就类似于VC等工具中的”step in”, n就类似于VC等工具中的”step
over”.
如果使用n命令显示如下:
(gdb) n
10 result = sum(a, b);
下面使用 s 命令,跟踪进入 sum 函数:
(gdb) s
sum (a=1, b=2) at demo.c:18
18 return a + b;
可以看出执行 s 命令时进入了sum函数内部, 如果用 n 命令则跳过函数的调用部分
(8) 恢复程序运行
在查看变量值以及堆栈之后, 就可以使用命令c(continue)恢复程序的正常运行了. 这时,
它会把剩余还未执行的程序执行完, 并显示剩余程序的执行结果.(gdb) c
Continuing.
1 + 2 = 3
Program exited normally.
可以看出, 程序在运行完后退出, 之后程序处于”停止状态”.
说 明: 在Gdb中, 程序的运行状态有”运行”,”暂停”和”停止”3种.
其中”暂停”状态是程序遇到了断点或者观察点, 程序暂时停止运行, 而此时函数的地址, 函数参数,
函数内的局部变量都会被压入”栈(Stack)中. 故在这种状态下可以查看函数的变量值等各种属性. 但在函数处于”停止”状态之后,
“栈”就会自动撤销, 它也就无法查看各种信息了
9. 退出 gdb
要退出 gdb
时,只用发 quit 或命令简称 q 就行了。
关于Gdb的更多命令, 你可以在启用Gdb后, 输入help命令查看.
(gdb) help
List of
classes of commands:
aliases --
Aliases of other commands
breakpoints
-- Making program stop at certain points
data --
Examining data
files --
Specifying and examining files
internals --
Maintenance commands
obscure --
Obscure features
running --
Running the program
stack --
Examining the stack
status --
Status inquiries
support --
Support facilities
tracepoints
-- Tracing of program execution without stopping the program
user-defined
-- User-defined commands
Type "help"
followed by a class name for a list of commands in that
class.
Type "help"
followed by command name for full documentation.
Command name
abbreviations are allowed if unambiguous.
(gdb)
help 命令只是例出 gdb
的命令种类,如果要看种类中的命令,能使用 "help 种类名",如: help breakpoints
,查看设置断点的所有命令。 gdb 中,输入命令时,能不用打全命令,只用打命令的前几个字符就能了(需求命令的前几个字符应唯一标志该命令),在 Linux
下,你能敲击两次 TAB 键来补齐命令的全称,如果有重复的,那么 gdb 会所有类似的都列出来。