gdb是GNU开源组织发布的一个强大的UNIX下的程序调试工具。
一般来说,gdb主要帮忙你完成下面四个方面的功能:
从上面看来,gdb和一般的调试工具没有什么两样,基本上也是完成这些功能,不过在细节上,你会发现gdb这个调试工具的强大,大家可能比较习惯了图形化的调试工具,但有时候,命令行的调试工具却有着图形化工具所不能完成的功能。让我们现在一一看来。
首先我们要了解到程序有两个版本,Debug版本和Release版本,
调试的对象:Debug版本的可执行文件
gdb Debug版本的可执行文件
命令 |
简写 |
说明 |
list |
l | 显示main函数的所在的文件的源代码 |
|
list:num | 显示指定行的源代码 |
|
ist first,last | 显示从first行到last行之间的源代码 |
break |
b | 设置断点,程序运行到断点处会停下来 |
|
b linenum | 在linenum行添加一个断点 |
info b |
查看断点信息 |
|
delete d |
删除断点 |
|
run |
r | 开始启动程序 |
next |
n | 单条语句执行,如果该语句为函数调用,不会进入函数内部执行 |
step |
s | 执行下一条指令,如果该语句为函数调用,则进入函数内部执行其第一条语句 |
finish |
退出当前函数 |
|
continue |
c | 继续程序的运行,退出当前断点,直到遇到下一个断点 |
set var name=values |
在程序运行中动态改变变量(name)的值为(values) |
|
|
p | 打印内部变量值 |
p valname |
查看变量的值 |
|
p &valname |
查看变量的地址 |
|
p arrname |
查看数组所有的元素值 |
|
p *parr@len |
通过指向数组的指针查看数组中指定长度的元素的值,你可以使用GDB的“@”操作符,“@”的左边是第一个内存的地址的值,“@”的右边则你你想查看内存的长度。) |
|
display |
disp |
跟踪查看某个变量,每次停下来都会打印它的值 |
display valname |
查看指定的变量,每当一条指令执行完时,显示一次追踪的变量的值 |
|
info display |
查看追踪信息 |
|
undisplay dpnum |
取消被追踪的变量,编号为dpnum |
|
backtrace |
bt | 显示函数调用过程(堆栈) |
until |
u | 当厌倦了在一个循环体内单步跟踪时,这个命令可以运行程序直到退出循环体 |
quit |
q | 退出调试,退出GDB环境 |
我们逐一来看一看这些命令如何使用:
这是一个调试示例:
#include
int fun(int n)
{
int sum = 0;
int i = 0;
for(i; i <= n; i++)
{
sum += i;
}
return sum;
}
int main()
{
int i = 0;
long result = 0;
for(i = 1; i <= 100; i++)
{
result += i;
}
printf("result[1-100] = %d\n", result);
printf("result[1-250] = %d\n", fun(250));
return 0;
}
(1)启动gdb调试:
(2)调试第一个命令“l ---> list”从主函数附近开始列出源码:
(3)如果找的是第8行附近的代码“list :8”:
(4)直接回车,表示重复上一条指令:
(5)调试第二个命令“b ---> break”在指定行设置断点:
(6)在函数fun()入口处设置断点(设置在fun函数中的第一行代码处):
(7)查看断点信息“info break或者info b”:
(8)调试第三个命令“r ---> run”运行程序,从主函数开始在第一个断点处停止:
(9)调试第四个命令“n ---> next”单条语句执行:
(10)调试第五个命令“c ---> continue”继续运行程序:
(11)调试第六个命令“p ---> print”打印变量i的值:
(12)调试第七个命令“bt ---> backtrace”查看函数堆栈:
(13)调试第八个命令“finish”退出函数:
(14)c,继续运行:
(15)调试第九个命令“quit”退出调试,退出GDB环境:
①:就上述而言,gdb调试需要的是Debug版本的可执行文件,而不需要Release版本的,
所以我们编译阶段要注意加上调试信息,使用编译器的-g参数可以做到这一点。
如在编译阶段时,加入调试信息(gcc -c main.c -g)
或者一步生成可执行文件时加上调试信息(gcc -o main main.c -g)
如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。
②:gdb中,输入命令时,可以不用打全命令,只用打命令的前几个字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令,在Linux下,你可以敲击两次TAB键来补齐命令的全称,如果有重复的,那么gdb会把其例出来。
③:如果你觉得已定义好的停止点没有用了,你可以使用delete、clear、disable、enable这几个命令来进行维护。
比删除更好的一种方法是disable停止点,disable了的停止点,GDB不会删除,当你还需要时,enable即可,就好像回收站一样。
④:一般来说,GDB会根据变量的类型输出变量的值。但你也可以自定义GDB的输出的格式。例如,你想输出一个整数的十六进制,或是二进制来查看这个整型变量的中的位的情况。要做到这样,你可以使用GDB的数据显示格式:
例如:(打印变量i的值)
(gdb) p i (默认十进制)
$21 = 101
(gdb) p/a i(按十六进制格式显示变量)
$22 = 0x65
(gdb) p/c i(按字符格式显示变量)
$23 = 101 ''e''
(gdb) p/f i(按浮点数格式显示变量)
$24 = 1.41531145e-43
(gdb) p/x i(按十六进制格式显示变量)
$25 = 0x65
(gdb) p/t i(按二进制格式显示变量)
$26 = 1100101
⑤:查看内存,你可以使用examine命令(简写是x)来查看内存地址中的值。
命令:x/3uh 0x54320 表示,从内存地址0x54320读取内容,h表示以双字节为一个单位,3表示三个单位,u表示按十六进制显示。
⑥:gcc编译.cpp文件时:(记得链接上C++库)
gcc -o main filelist(*.cpp) -lstdc++
总结:GDB是一个强大的命令行调试工具。大家知道命令行的强大就是在于,其可以形成执行序列,形成脚本。UNIX下的软件全是命令行的,这给程序开发提代供了极大的便利,命令行软件的优势在于,它们可以非常容易的集成在一起,使用几个简单的已有工具的命令,就可以做出一个非常强大的功能。这是图形化工具所不能及的。