Linux的调试器--gbd/cgbd
1.引入
#include <stdio.h>
int Sum(int s, int e)
{int result = 0;for(int i = s; i <= e; i++){result += i;}return result;
}
int main()
{int start = 1;int end = 100;printf("I will begin\n");int n = Sum(start, end);printf("running done, result is: [%d-%d]=%d\n", start, end, n);return 0;
}
程序的发布⽅式有两种, debug 模式和 release 模式, Linux gcc/g++ 出来的⼆进制程
序,默认是 release 模式。这一点在我们window下比较直观。

要使⽤gdb调试,必须在源代码⽣成⼆进制程序的时候, 加上 -g 选项,如果没有添加,程序⽆法被
编译。
在创建好两种格式后我们可以看到他们的内存是不一样的。

2.gbd/cgbd的使用
一般情况下,我们更建议使用cgbd,因为cgbd会显示源代码

开始: gdb binFile退出: ctrl + d 或 quit 调试命令
命令 | 作用 | 样例 |
---|---|---|
list/l | 显示源代码,从上次位置开始,每次列出 10 行 | list/l 10 |
list/l 函数名 | 列出指定函数的源代码 | list/l main |
list/l 文件名:行号 | 列出指定文件的源代码 | list/l mycmd.c:1 |
r/run | 从程序开始连续执行 | run |
n/next | 单步执行,不进入函数内部 | next |
s/step | 单步执行,进入函数内部 | step |
break/b [文件名:] 行号 | 在指定行号设置断点 | break 10 break test.c:10 |
break/b 函数名 | 在函数开头设置断点 | break main |
info break/b | 查看当前所有断点的信息 | info break |
finish | 执行到当前函数返回,然后停止 | finish |
print/p 表达式 | 打印表达式的值 | print start+end |
p 变量 | 打印指定变量的值 | p x |
set var 变量 = 值 | 修改变量的值 | set var i=10 |
continue/c | 从当前位置开始连续执行程序 | continue |
delete/d breakpoints | 删除所有断点 | delete breakpoints |
delete/d breakpoints n | 删除序号为 n 的断点 | delete breakpoints 1/d 1 |
disable breakpoints | 禁用所有断点 | disable breakpoints |
enable breakpoints | 启用所有断点 | enable breakpoints |
info/i breakpoints | 查看当前设置的断点列表 | info breakpoints |
display 变量名 | 跟踪显示指定变量的值(每次停止时) | display x |
undisplay 编号 | 取消对指定编号的变量的跟踪显示 | undisplay 1 |
until x 行号 | 执行到指定行号 | until 20 |
backtrace/bt | 查看当前执行栈的各级函数调用及参数 | backtrace |
info/i locals | 查看当前栈帧的局部变量值 | info locals |
quit | 退出 GDB 调试器 | quit |
对于一些常用的指令我们解释一下:
(1)
r/run | 从程序开始连续执行 | run |
细节1:gdb启动调试的时候,只是开启了gdb,被调试程序,并没有运行起来。
细节2:r/run,表示的是在gdb的场景中,启动我们自己的mycmd程序
细节3:在没有断点的情况下,r/run,就是 让我们的程序直接运行结束
细节4:断点的本质功能:让我们的程序,在运行到指定的行,进行暂停!
(2)
n/next | 单步执行,不进入函数内部 | next |
s/step | 单步执行,进入函数内部 | step |
这两条指令就相当于vs里的F10和F11。
(3)
continue/c | 从当前位置开始连续执行程序 | continue |
运行到结束或者下一个节点处
(4)
disable breakpoints | 禁用所有断点 | disable breakpoints |
enable breakpoints | 启用所有断点 | enable breakpoints |
为什么不删除?因为要保留调试痕迹
3. 调试技巧
3.1watch
执⾏时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运⾏期间的值发⽣变化,GDB 会暂停程序的执⾏,并通知使⽤者。
如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如果变
化了,就会通知你。
watch的查看和删除类似于断点,info查看,d删除。

3.2set var确定问题原因
在我们的测试案例中,
更改⼀下标志位,假设我们想得到 +-result
#include <stdio.h>
int flag = 0; // 故意错误
//int flag = -1;
//int flag = 1;
int Sum(int s, int e)
{
int result = 0;
for(int i = s; i <= e; i++)
{
result += i;
}
return result*flag;
}
int main()
{
int start = 1;
int end = 100;
printf("I will begin\n");
int n = Sum(start, end);
printf("running done, result is: [%d-%d]=%d\n", start, end, n);
return 0;
}
运行出来结果为0,但在调试的时候用set var flag=1改变flag的话就是5050。
3.3条件断点
使用方法:
b 行号 条件
eg. b 9 if i == 30 # 9是⾏号,表⽰新增断点的位置。
在原有的断点基础上:
condition 编号 条件
条件断点添加常⻅两种⽅式:1. 新增 2. 给已有断点追加
• 注意两者的语法有区别,不要写错了。
• 新增: b ⾏号/⽂件名:⾏号/函数名 if i == 30(条件)
• 给已有断点追加:condition 2 i==30, 其中2是已有断点编号,没有if