一.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) | 执行下一行语句,如果有函数调用则进入到函数中 |
二.gdb例子:
#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; }输出结果:
result[0]=55
result[1]=5105
(每执行一次输出结果都不一样。 预期结果:55 5050)
三.编译
gcc -g main.c -o main
注意:-g
选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证gdb
能找到源文件。
四.开始gdb调试
gdb main 进入gdb调试模式 (或者先 gdb进入gdb模式,再 file main)
(gdb) list 1 (或者 l 1 )
(gdb) list 1 1 #include <stdio.h> 2 3 int add_range(int low, int high) 4 { 5 int i, sum; 6 for (i = low; i <= high; i++) 7 sum = sum + i; 8 return sum; 9 } 10
一次只列10行,如果要从第11行开始继续列源代码可以输入
(gdb) list
也可以什么都不输直接敲回车,gdb
提供了一个很方便的功能,在提示符下直接敲回车表示重复上一条命令。
(gdb) (直接回车) 11 int main(void) 12 { 13 int result[100]; 14 result[0] = add_range(1, 10); 15 result[1] = add_range(1, 100); 16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]); 17 return 0; 18
(gdb) start
Breakpoint 1 at 0x80483ad: file main.c, line 14. Starting program: /home/akaedu/main main () at main.c:14 14 result[0] = add_range(1, 10); (gdb)
gdb
停在main
函数中变量定义之后的第一条语句处等待我们发命令,gdb
列出的这条语句是即将执行的下一条语句。我们可以用next
命令(简写为n
)控制这些语句一条一条地执行:
(gdb) n 15 result[1] = add_range(1, 100); (gdb) (直接回车) 16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]); (gdb) (直接回车) result[0]=(gdb) set var sum=0 (gdb) finish Run till exit from #0 add_range (low=1, high=100) at main.c:6 0x080483db in main () at main.c:15 15 result[1] = add_range(1, 100); Value returned is $4 = 5050 (gdb) n 16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]); (gdb) (直接回车) result[0]=55 result[1]=5105 17 return 0;
用n
命令依次执行两行赋值语句和一行打印语句,在执行打印语句时结果立刻打出来了,然后停在return
语句之前等待我们发命令。虽然我们完全控制了程序的执行,但仍然看不出哪里错了,因为错误不在main
函数中而在add_range
函数中,现在用start
命令重新来过,这次用step
命令(简写为s
)钻进add_range
函数中去跟踪执行:
(gdb) start The program being debugged has been started already. Start it from the beginning? (y or n) y Breakpoint 2 at 0x80483ad: file main.c, line 14. Starting program: /home/akaedu/main main () at main.c:14 14 result[0] = add_range(1, 10); (gdb) s add_range (low=1, high=10) at main.c:6 6 for (i = low; i <= high; i++)
这次停在了add_range
函数中变量定义之后的第一条语句处。在函数中有几种查看状态的办法,backtrace
命令(简写为bt
)可以查看函数调用的栈帧:
(gdb) bt #0 add_range (low=1, high=10) at main.c:6 #1 0x080483c1 in main () at main.c:14
可见当前的add_range
函数是被main
函数调用的,main
传进来的参数是low=1,
high=10
。main
函数的栈帧编号为1,add_range
的栈帧编号为0。现在可以用info
命令(简写为i
)查看add_range
函数局部变量的值:
(gdb) i locals i = 0 sum = 0
如果想查看main
函数当前局部变量的值也可以做到,先用frame
命令(简写为f
)选择1号栈帧然后再查看局部变量:
(gdb) f 1 #1 0x080483c1 in main () at main.c:14 14 result[0] = add_range(1, 10); (gdb) i locals result = {0, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480, ... -1208623680}
注意到result
数组中有很多元素具有杂乱无章的值,我们知道未经初始化的局部变量具有不确定的值。到目前为止一切正常。用s
或n
往下走几步,然后用print
命令(简写为p
)打印出变量sum
的值:
(gdb) s 7 sum = sum + i; (gdb) (直接回车) 6 for (i = low; i <= high; i++) (gdb) (直接回车) 7 sum = sum + i; (gdb) (直接回车) 6 for (i = low; i <= high; i++) (gdb) p sum $1 = 3
第一次循环i
是1,第二次循环i
是2,加起来是3,没错。这里的$1
表示gdb
保存着这些中间结果,$后面的编号会自动增长,在命令中可以用$1
、$2
、$3
等编号代替相应的值。由于我们本来就知道第一次调用的结果是正确的,再往下跟也没意义了,可以用finish
命令让程序一直运行到从当前函数返回为止:
(gdb) finish Run till exit from #0 add_range (low=1, high=10) at main.c:6 0x080483c1 in main () at main.c:14 14 result[0] = add_range(1, 10); Value returned is $2 = 55
返回值是55,当前正准备执行赋值操作,用s
命令赋值,然后查看result
数组:
(gdb) s 15 result[1] = add_range(1, 100); (gdb) p result $3 = {55, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480, ... -1208623680}
第一个值55确实赋给了result
数组的第0个元素。下面用s
命令进入第二次add_range
调用,进入之后首先查看参数和局部变量:
(gdb) s add_range (low=1, high=100) at main.c:6 6 for (i = low; i <= high; i++) (gdb) bt #0 add_range (low=1, high=100) at main.c:6 #1 0x080483db in main () at main.c:15 (gdb) i locals i = 11 sum = 55由于局部变量i和sum没初始化,所以具有不确定的值,又由于两次调用是挨着的,i和sum正好取了上次调用时的值,i的初值不是0倒没关系,在for循环中会赋值为0的,但sum如果初值不是0,累加得到的结果就错了。好了,我们已经找到错误原因,可以退出gdb修改源代码了。如果我们不想浪费这次调试机会,可以在gdb中马上把sum的初值改为0继续运行,看看这一处改了之后还有没有别的Bug:
(gdb) set var sum=0 (gdb) finish Run till exit from #0 add_range (low=1, high=100) at main.c:6 0x080483db in main () at main.c:15 15 result[1] = add_range(1, 100); Value returned is $4 = 5050 (gdb) n 16 printf("result[0]=%d\nresult[1]=%d\n", result[0], result[1]); (gdb) (直接回车) result[0]=55 result[1]=5050 17 return 0;
这样结果就对了。修改变量的值除了用set
命令之外也可以用print
命令,因为print
命令后面跟的是表达式,而我们知道赋值和函数调用也都是表达式,所以也可以用print
命令修改变量的值或者调用函数:
(gdb) p result[2]=33 $5 = 33 (gdb) p printf("result[2]=%d\n", result[2]) result[2]=33 $6 = 13