(部分摘抄于http://www.cnblogs.com/hankers/archive/2012/12/07/2806836.html)
一.gdb常用命令:
命令 | 描述 |
backtrace(或bt) | 查看各级函数调用及参数 |
finish | 连续运行到当前函数返回为止,然后停下来等待命令 |
frame(或f) 帧编号 | 选择栈帧 |
info(或i) locals | 查看当前栈帧局部变量的值 |
list(或l) | 列出源代码,接着上次的位置往下列,每次列10行 |
list 行号 | 列出从第几行开始的源代码 |
list 函数名 | 列出某个函数的源代码 |
next(或n) | 执行下一行语句 |
print(或p) | 打印表达式的值,通过表达式可以修改变量的值或者调用函数 |
quit(或q) | 退出gdb调试环境 |
set var | 修改变量的值 |
start | 开始执行程序,停在main函数第一行语句前面等待命令 |
step(或s) | 执行下一行语句,如果有函数调用则进入到函数中 |
1.cpp文件的源码如下:
#include <stdio.h>
int add_range(int low, int high)
{
int i, sum;
for (i = low; i <= high; i++)
sum = sum + i;
return sum;
}
int main(void)
{
int result[100];
result[0] = add_range(1, 10);
result[1] = add_range(1, 100);
printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
return 0;
}
在编译时要加上-g选项,生成的可执行文件才能用gdb进行源码级调试:
-g选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb能找到源文件。gdb提供一个类似Shell的命令行环境,上面的(gdb)就是提示符,在这个提示符下输入help可以查看命令的类别:
list 1列出第一行开始的10行代码 , 显示后,回车则是执行上一次输入的命令。
输入quit退出gdb
源代码改名或移到别处再用gdb调试,这样将列不出源代码(调试时需要用到源文件)。
start命令开始执行程序
(gdb) start
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Temporary breakpoint 3 at 0x80484aa: file 1.cpp, line 15.
Starting program: /share/test/a.out
Temporary breakpoint 3, main () at 1.cpp:15
15 result[0] = add_range(1, 10);
gdb停在main函数中变量定义之后的第一条语句处等待我们发命令,gdb列出的这条语句是即将执行的下一条语句。我们可以用next命令控制这些语句一条一条地执行:
(gdb) next16 result[1] = add_range(1, 100);
陆续按下回车键,直到程序运行结束。
(gdb)
17 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]);
(gdb)
result[0]=1275219
result[1]=1280269
18 return 0;
(gdb)
19 }
(gdb)
0x00154cc6 in __libc_start_main () from /lib/libc.so.6
(gdb)
Single stepping until exit from function __libc_start_main,
which has no line number information.
Program exited normally.
用n命令依次执行两行赋值语句和一行打印语句,在执行打印语句时结果立刻打出来了,然后停在return语句之前等待我们发命令。虽然我们完全控制了程序的执行,但仍然看不出哪里错了,因为错误不在main函数中而在add_range函数中,现在用start命令重新来过,这次用step命令(简写为s)钻进add_range函数中去跟踪执行:
(gdb) start
Temporary breakpoint 4 at 0x80484aa: file 1.cpp, line 15.
Starting program: /share/test/a.out
Temporary breakpoint 4, main () at 1.cpp:15
15 result[0] = add_range(1, 10);
(gdb) step
add_range (low=1, high=10) at 1.cpp:7
7 for (i = low; i <= high; i++)
这次停在了add_range函数中变量定义之后的第一条语句处。在函数中有几种查看状态的办法,backtrace命令(简写为bt)可以查看函数调用的栈帧:
(gdb) backtrace
#0 add_range (low=1, high=10) at 1.cpp:7
#1 0x080484be in main () at 1.cpp:15
(gdb)
可见当前的add_range函数是被main函数调用的,main传进来的参数是low=1, high=10。main函数的栈帧编号为1,add_range的栈帧编号为0。现在可以用info命令(简写为i)查看add_range函数局部变量的值:
(gdb) info locals
i = 1148052
sum = 1275164
(gdb)
如果想查看main函数当前局部变量的值也可以做到,先用frame命令(简写为f)选择1号栈帧然后再查看局部变量:
(gdb) frame 1
#1 0x080484be in main () at 1.cpp:15
15 result[0] = add_range(1, 10);
(gdb) info locals
result = {4, 1147604, 1147700, 1318352, 1256356, -1073744508, 1331888, 13, 22683471, 1185218, 1378450, 134513341, -1207959784,
-1208025086, 1210029, 134513280, -1207960704, 1273796, 1317832, 4, -1073744308, 1187172, 79484788, 1, 1273796, -1073744208,
-1207962224, -1073744252, 1187674, -1073744268, 79484788, -1073744280, 1276500, 0, -1208020624, 1, 4, 1, -1207962664, 1318352,
1256356, -1073744364, 1353520, 14, 129100401, -1207959828, -163754450, 0, 6, 1276152, 0, 0, 1, 2198, -1207959784, -1207960648,
134513312, 1355600, 134513120, 1, 1273796, -1073744064, 1276592, -1073744108, 1187674, -1073744124, 134513120, -1073744136,
1276500, 0, -1207959784, 1, 0, 1, 1276152, 79542637, 1, -1073744192, 1206213, -1207962224, -1208020624, 1, 1, 0, -1073744064,
134518756, -1073744200, 134513516, 1331888, 134518756, -1073744152, 134513961, 80389456, 134513312, 0, 2908148, 134513936,
134513600, 134513947, 2908148}
注意到result数组中有很多元素具有杂乱无章的值,我们知道未经初始化的局部变量具有不确定的值。到目前为止一切正常。用s或n往下走几步,然后用print命令(简写为p)打印出变量sum的值:
(gdb) p sum
$1 = 1275170
(gdb) print sum
$2 = 1275170
(gdb)
第一次循环i是1,第二次循环i是2,加起来是3,没错。这里的$1表示gdb保存着这些中间结果,$后面的编号会自动增长,在命令中可以用$1、$2、$3等编号代替相应的值。由于我们本来就知道第一次调用的结果是正确的,再往下跟也没意义了,可以用finish命令让程序一直运行到从当前函数返回为止。
用finish命令让程序一直运行到从当前函数返回为止:
(gdb) finish
Run till exit from #0 add_range (low=1, high=10) at 1.cpp:7
0x080484be in main () at 1.cpp:15
15 result[0] = add_range(1, 10);
Value returned is $3 = 1275219
返回值是1275219,当前正准备执行赋值操作,用s命令赋值,然后查看result数组:
Value returned is $3 = 1275219(gdb)
(gdb) step
16 result[1] = add_range(1, 100);
(gdb) print result
$4 = {1275219, 1147604, 1147700, 1318352, 1256356, -1073744508, 1331888, 13, 22683471,
可以在gdb中马上把sum的初值改为0继续运行,看看这一处改了之后还有没有别的Bug:
(gdb) step
add_range (low=1, high=100) at 1.cpp:7
7 for (i = low; i <= high; i++)
(gdb) backtrace
#0 add_range (low=1, high=100) at 1.cpp:7
#1 0x080484d6 in main () at 1.cpp:16
(gdb) info locals
i = 11
sum = 1275219
(gdb) set var sum = 0
(gdb) finish
Run till exit from #0 add_range (low=1, high=100) at 1.cpp:7
0x080484d6 in main () at 1.cpp:16
16 result[1] = add_range(1, 100);
Value returned is $5 = 5050
(gdb)
这样结果就对了。修改变量的值除了用set命令之外也可以用print命令,因为print命令后面跟的是表达式,而我们知道赋值和函数调用也都是表达式,所以也可以用print命令修改变量的值或者调用函数: