目录
1.Debug与Release模式
如何证明debug是可以被调试的?
2.Linux调试器-gdb使用
gdb调试的指令
指令一:list(l) 查看源代码
编辑 编辑 命令二:run(r)运行程序
命令三:breakpoint(b)设置断点
命令四:infobreak(info)查看断点 命令五:delete breakpoint(d)删除断点
命令六:启用/禁用断点enable /disable breakpoint
命令七:next(n)逐过程运行
命令八:step(s)逐语句运行
命令九: printf 变量(p 变量)查看变量
命令十: displayundisplay 变量 常显示查看变量(跟踪变量)
命令十一:undisplay 跟踪编号 取消常显示(取消跟踪)
命令十二:until 行号 执行中跳至x行
命令十三:finish 直接运行结束
命令十四:continue(c) 当前位置开始连续而非单步执行程序
命令十五:bt 查看调用堆栈
命令十六:set var 变量 修改变量
1.Debug与Release模式
在学习c++时我们就已经了解到这两种编译模式。
Debug 通常称为调试版本,是通过一系列编译选项的配合,编译的结果通常包含调试信息,而且不做任何优化,以为开发人员提供 强大的应用程序调试能力。
Release:Release通常称为 发布版本,是为用户使用的,一般客户不允许在发布版本上进行调试。所以不保存调试信息,同时,它往往进行了各种优化,以期达到代码最小和速度最优。
总的来看,Debug与release是两种代码编译模式,Debug下代码可以被调试,而Release下会将代码优化。
如何证明debug是可以被调试的?
1.Debug模式下可以被调试主要是因为在编译后会添加Debug信息,从而使得文件可以被调试。
对于Release是没有这些信息的,仔细发现两种方式下编译的可执行程序大小是不一样的。
ll 文件名//查看两种方式下的可执行程序大小
2.我们还可以查看可执行程序里面的内容,很容易看出两者是存在差异的:
readelf -a 文件名 //可以查看二进制文件
readelf -S 文件名 | grep -i debug //通过管道筛选出是否有debug信息
如果一份代码要进行调试,这份代码的发布模式必须是Debug模式的,而当一份代码要进行发布时,测试人员进行测试的是Release版本,测试完成之后就会上线。
2.Linux调试器-gdb使用
当我们学会了编译代码时,就想知道代码书写错误,程序报错如何去调试,对于Linux提供了gdb用来调试:
GDB是GNU symbolic debugger的缩写,是一款常用的程序调试器,主要用于调试C、C++、Go、Objective-C、OpenCL、Ada等编程语言编写的程序。
它可以帮助我们完成以下四个方面的功能:
- 启动你的程序,可以按照你的自定义的要求随心所欲地运行程序。
- 在某个指定的地方或条件下暂停程序。
- 当程序被停住时,可以检查此时你的程序中所发生的事。
- 在程序执行过程中修改程序中的变量或条件,将一个bug产生的影响修正从而测试其他bug。
其次我们还需要知道的是在linux环境下编译默认都是Release模式下,故无法被调试。我们需要借助gdb,那么我们如何调试呢?
gdb调试的指令
首先对于调试工具gdb也为我们提供了多种调试方法
调试命令:
退出: ctrl + d 或 quit 。
我们现在以一个求某个数的累加和的.c程序为例,在下面实验我们去如何调试:
对于gdb默认一般都是安装的,我们也可以查看是否拥有gdb:
gdb --v
查看拥有gdb之后,我们先尝试去gdb调试,先将我们的文件直接gcc编译生成可执行文件,之后我们直接对我们想要调试的可执行程序进行gdb 文件名:
gdb 文件名
之后我们就进入了gdb下命令窗口:
可以看到对于我们想要调试的程序,这里会说对于我这个可执行文件(code.c),是没有debug的信息被发现,当前是无法被调试的,即当前调试指令时无效的。
这里对于Linux下的gcc 编译工具默认是在Release模式下编译,编译生成的文件是无法被调试的,故会报错无调试信息。那么如何去增加debug信息呢?
我们需要在编译生成可执行程序时增加编译选项:
gcc -g 文件 -o 目标文件 //添加-g选项,以debug方式编译文件
之后我们就可以调试该文件:
指令一:list(l) 查看源代码
l按gdb的方式显示代码 l 0 从开头开始打印
再次回车时(gdb会自动记录上一个命令),显示后面的源码,直到全部,此时会提示我们多少行已全部显示
命令二:run(r)运行程序
直接r可以看到程序直接被运行完,结果出现
命令三:breakpoint(b)设置断点
b 行号 //该行处设置断点
设置完毕会返回关于断点的信息。
对于多源文件,gdb也可以打断点
b 文件名:行号
其次我们还可以直接给出函数名来设置断点
b 函数名 //默认打断点在函数的入口处
b 文件名:函数名
命令四:infobreak(info)查看断点
可以看到在第六行设置一个断点,还有其地址,类型等。
命令五:delete breakpoint(d)删除断点
d 断点编号
虽然打断点可以直接用行号和函数名,但是删除断点必须要用他的断点编号,所谓的断点编号就是在我们设置断点后返回的断点信息中的NUM,利用info也可以查看到。
其中对于一个周期的调试下,断点的编号是一直递增的,当我们退出调试后,重新在调试即开启下一个周期,之前的都会被清除。
命令六:启用/禁用断点enable /disable breakpoint
对于一个断点,我们目前不想使用它,但不取消它,用来记录我们调试的区域,可以禁用它让他不起作用,其次我们也可以查看到一个断点的状态(y代表启用,n代表禁止)
现在我么来禁用一个端点
disable 断点编号
再开启它:
enable 断点编号
命令七:next(n)逐过程运行
对于断点设置好之后就可以run了,程序直接跑到断点处执行,类比于vs的F10,之后断点处输入n就可以逐过程(一行一行的运行,遇到空行直接跳过)的运行程序,
可以看到运行到了下一行第7行,再次n,又运行到下一行,直到退出。
命令八:step(s)逐语句运行
所谓逐语句区别于逐过程,逐语句执行更加的严密仔细,一个一个语句运行,遇到函数直接进入函数内部,s之后,继续执行:
可以看到断点处开始执行,遇到函数,进入函数内部。
命令九: printf 变量(p 变量)查看变量
在上述逐语句的过程中,我们此时再循环内,此时就可以查看一些变量的值:
例如这里循环了四次,累加了四次,此时 i=4,sum =6,之后再运行再查看,就可以观察到变量的变化了。
当然我们也可以查看变量的地址:
命令十: displayundisplay 变量 常显示查看变量(跟踪变量)
对于上述p查看变量每次查看我都要输入,怎样可以跟vs的监视窗口的 效果一样的,长时间显示我们变量的变化,利用display我们也可以做到。、
可以看到每一next逐过程执行时都会为我们自动打印我们所要看的变量的值。
命令十一:undisplay 跟踪编号 取消常显示(取消跟踪)
对于我们不长时间查看的变量,我们不能直接undisplay 变量来取消,仔细发现在我们每一次跟踪变量时,每一次显示都可以看到变量前有一个编号,而取消编号才能做取消跟踪。
可以看到取消掉了跟踪,不再显示变量。
命令十二:until 行号 执行中跳至x行
由于我此时在循环里,且循环比较大,一直next循环结束太过离谱,英雌我们可以直接跳至程序某一行。
现在我跳至程序的第7行,打印结果,此时代码是抬到了20行,可以看到如果遇到断点会跳到断点处,若没有断点此时会跳至第7行。
故跳出循环有两种方式,一是设置断点,再run或者until眺出去,二是不设置断点直接跳出去。
命令十三:finish 直接运行结束
可以看到直接到程序结束,打印了结果
命令十四:continue(c) 当前位置开始连续而非单步执行程序
简单说可以理解为从一个断点跳至另一个断点处。
命令十五:bt 查看调用堆栈
在运行过程中,查看函数的调用顺序,这里先调用主函数再调用addtop,因为这里是以栈的方式,故是倒着的顺序。
命令十六:set var 变量 修改变量
我们可以在调试的过程中去修改变量达到我们想要的调试情况:
如这里i我们可以直接修改为10,查看我们改变时运行的效果。