Linux下我们编译好的代码是无法直接进行调试的,gcc/g++的默认工作模式是release模式,gdb是一个调试器,本篇将介绍一下它的使用。
1.快速认识gdb
要使⽤gdb调试,必须在源代码⽣成⼆进制程序的时候(编译时), 加上 -g 选项,让生成的可执行程序添加调试信息,也可就是让代码处于debug模式。
- readelf -S 可执行程序 | grep -i debug:查看可执行程序是有debug信息
我们的Makefile可以完善一下。
//Makefile文件
SRC=$(wildcard *.c)
OBJ=$(SRC:.c=.o)
BIN=mycode
$(BIN):$(OBJ)
gcc -o $@ $^
%.o:%.c
gcc -c $< -g #加上-g选项
.PHONY:clean
clean:
rm -f $(OBJ) $(BIN)
- gdb 可执行文件:进入gdb,gdb后面跟的是可执行程序,不是源文件!
- quit:退出gdb
2.解决gdb难用问题
我们直接使用cgdb,用命令安装
Ubuntu环境:sudo apt-get install cgdb
安装好之后我们再打开cgdb。
cgdb有分屏,是上面是代码屏,下面是调试信息屏,它的界面就清楚很多。
- 按ESC可以进入代码屏,但是可能会卡死!
- 如果不小心导致页面卡死了,按i光标就能回退到下面调试信息处。
3.gdb/cgdb相关指令
cgdb的指令和gdb的指令是一样的,下面我就用cgdb演示,演示的代码如下:
#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("begin...\n");
int n = Sum(start, end);
printf("running done, result is: [%d-%d]=%d\n", start, end, n);
return 0;
}
3.1 常见使用
- l:list,查看源代码,按回车就会往后显示,每次显示10行
- r:run,从头运行,会在遇到的第一个断点处停下来,没有断点就直接运行到结束
在gdb中会记录最新的那条指令,只要按回车就会执行最新的那条语句
3.1.1 断点
- b n:break,在第n行打断点
- b 函数名:在指定的函数入口处打断点
- b 文件名:n:在指定文件的第n行打断点
- b 文件名:函数名:在指定文件的指定函数入口处打断点
- info b:查看所有断点
- d 断点编号:delete,删除断点
- disable 断点编号:禁用断点,此断点仍存在
- enable 断点编号:启用断点
- c:continue,运行到下一个断点处
gdb不退出,断点编号依次递增
3.1.2 语句
- n:next,逐过程
- s:step,逐语句
-
finish:执⾏到当前函数返回然后停⽌
- bt:backtrace,查看当前执⾏栈的各级函数调⽤及参数
- until n:执行到指定行
3.1.3 变量
- p 变量名:查看指定的变量名,不能常显
- p 表达式:可以查看一个表达式的值
- display 变量名:跟踪显⽰指定变量的值,常显示
- undisplay 变量名编号:取消跟踪显⽰指定变量
- info locals:查看一个函数里所有的变量
3.2 常见技巧
3.2.1 watch
- watch 变量或表达式:监视一个变量或表达式,如果这个变量或表达式在运行期间发生了变化,gdb会暂停运行并通知使用者,不会常显,有变化才会显示
比如说我们监视这个result变量,watch result之后会有一个watchpoint的断点,用指令info b就能查看这个断点类型和监视的对象
当程序往后执行,检测到这个变量的变化后就会显示出来,并且这个watch可以对这个变量进行新旧的对比
如果不想要这个断点,就用 d 断点编号 进行删除
常见应用场景:如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如果变化了,就会通知你。
3.2.2 set var
- set var 变量=新的值:我们在调试期间修改这个变量的值变成新的值,就不用退出gdb修改源代码来确定问题
比如这个求和函数,result的初始值应该是0,这里故意设置成1,设置成错的。
int Sum(int s, int e)
{
//int result = 0;
int result = 1; //设置错误
for(int i = s; i <= e; i++)
{
result += i;
}
return result;
}
在我们调试的时候发现这个地方写错了,可直接用set var result=0进行值的暂时修改
但是这个操作不会修改源代码,发现问题知道错在哪后,还是要对源代码修改。
3.2.3 条件断点
- b n if 条件:当条件成立的时候在第n行设置断点
比如在求和函数循环体内,当i为20的时候打个断点。
- condition 断点编号 条件:给已存在的断点添加条件,断点已存在不用加if
注意两者的语法有区别,不要写错了!
这个断点可以搭配指令c一起用,我们刚刚打了一个条件断点,当i==20时触发断点,按c就会直接跳到这个地方
本次分享就到这里了,我们下篇再见~