前言
gcc/g++编译器默认的是编译为release发行版本的,只有加上-g选项才能将文件编译为debug版本的。所以我们先来看一下语法再看指令。上节说gcc的时候我们也提到了-g选项的事。
我们来单独比较一下两个版本的大小:
结果是:发行版大小会比调试版本的大小小一些。因为调试版本的文件内部会有一些我们看不到的调试符号表等调试信息。另外一方面,发行版本的会有一些优化,所以相对小一点。
如何查看调试信息:
bash
readelf -S file.out |grep -i debug
语法
bash
gcc main.c -o main -g #必须加上-g选项编译出来的main文件才可以调试,因为调试需要一个调试信息符号表
gdb main #输入命令后就开始调试了,后续就是输入调试指令来调试程序
gdb的一个特点是:默认执行上次的命令 ,这点在list命令处有明显突出效果。
基础
退出调试
【quit】【q】:退出调试
查看代码
【list number】【l number】:list或小写L,用于查看代码,从number开始往下十行,如果不输入number,那么默认从未读的第一行开始,比如:刚开始调试时,我输入了一个l,那么他会向你展示文件的从第1行开始的10行代码,然后紧接着输入l,那么它将从第11行开始往下10行。
【list function】【l function】:用于查看function函数的附近十行代码:
开始执行
【run】【r】:输入后,开始执行代码。直到断点或结束或者遇到段错误
断点
开始调试
【start】:开始运行程序,到主函数第一行截至,也就是默认在主函数第一行添加一个临时断点,但此时你去查看断点,你又找不到。
分析过程:使用start命令后,显示在文件第十行(file gdbtest.c,line 10)添加了一个临时(Temporary)断点(breakpoint),断点地址为(at):0x1187,然后开始运行,运行到第十行时,触发断点,也就是for(int i=1;i
我们通过list查看当前位置(应该是第10行)附近的十行内容:我们看到,第8行为int main,第9行为花括号{,所以第10行才是真正的main函数内的第一条语句所在行。这也验证了我们先前所说的。
设置断点
【break number】【b number】:在第number行设置断点
当然,这里也可以以函数为标记进行打断点的操作
删除断点
【delete bid】【d bid】:删除断点编号=bid的断点
查看断点
【info b】【i b】:查看断点信息
设置的断点可以通过info b或者i b来查看,对于查看结果,我们逐列说明:
第一列Num:断点编号,本次调试的第几个断点,删除后,下次再新增断点仍然在此基础上+1。
第二列Type:就是类型--代表这是一个断点(breakpoint)
第三列Disp:
第四列Enb:enable,断点是否有效。当前断点y表示yes,当使用disable 2时(使2号断点无效),断点的Enb将变为n(表示not)。反之,enable可以使已经标记无效的断点有效
第五列Address:断点所在的地址
第六列What:断点信息:包括断点所在的文件,所在的函数,所在的行这些信息。
【in后紧跟函数;at后紧跟文件;: 后面紧跟行数】
跳过断点
【continue】【c】:继续,跳过本次断点调试,从当前语句继续向下执行
单步执行
【step】【s】:单步执行--会进入函数,就是vs中的逐步进行
【next】【n】:单步执行-会跳过函数,也就是vs中的逐过程执行
打印变量
【print variable】【p variable】:查看variable的值
我们先查看i的值,然后通过n进行下一步,然后检测输出的是否就是我们通过print得到的值,目前看来,没问题。让我们继续往下执行,我们看到,前面dollar符$后面的数字也在进行变化,这个数字的含义是,本次调试查看的第几次变量。
条件断点
【b 11 if i=5】在11行添加断点,如果i=5,断点生效。
需要注意的问题:用完之后记得删除断点,不然会一直卡到这个地方循环执行,跳不过这个断点
跟踪变量
【display variable】跟踪变量
【undisplay variable】取消跟踪变量
函数
进入函数
【s】:(step),下一步,可以进入函数。所以可以用该命令在执行到函数的时候进入函数。
结束函数
【finish】:直达函数结尾的大括号,达到结束函数的目的
其它
1.查看变量类型:【ptype】
根据dollar符$后面的数字我们可以看出p i为本次调试历史第一次查看变量,所以在此之前使用ptype,会提示历史变量为空
由此我们可以得出一个结论:ptype是查看当前历史变量的最后一个变量的类型。也可以使用ptype i来查看。
2.列出当前程序正存活者的栈帧:【bt / backtrace】
main函数的函数栈帧就是:0x7fffffffe448,栈帧编号为 0
3.frame / f:根据栈帧编号,切换栈帧。
栈帧:随着函数调用而在stack上开辟的一片内存空间,用于存放函数调用时产生的局部变量和临时值。
快速访问:
这里因为代码比较短,函数就这么几个,没有好演示的。直到这个命令的作用就行了。
4.set args 字符串1 字符串2 ...:设置main函数命令行参数,在start、run之前设置。
使用run 字串1 字串2 ...也能设置main函数的命令行参数
对比VS
基础对比
图一--从上往下:开始执行(run),添加断点(break)
图二--从左往右:继续(continue),终止(quit),逐语句(step),逐过程(next),跳出(finish)
对标VS(visual studio)的调试方法:鼠标添加断点(=》b),点击开始执行(=》r),逐句执行(=》s),逐过程执行(=》n),添加变量监视(=》p),退出调试(=》q)。
bash
#第一步:开始调试filec程序
gdb filec
#第二步:列出源码,方便查看
(gdb)list 1 #简写l
(gdb)l
(gdb)l
#对于该步,我们也可以单开一个终端查看.c代码文件
#第三步:在行号为lineNumber的地方添加断点
(gdb)b lineNumber
#不再使用断点的方法为:disable 断点编号:使断点不生效 / delete(d) 断点编号:删除断点
#第四步:单步执行
(gdb)run #简写r
(gdb)next #简写n
(gdb)step #简写s
#这两个的使用取决于你想不想进入非主函数内部查看非主函数内部的代码有没有问题
#第五步:查看变量
(gdb)print i #简写p
#i是一个变量名,你想查看谁的就填谁的变量名
#第六步:继续执行
(gdb)continue #简写c
#在断点处查看完变量了,后面明显是对的,没必要一步一步太麻烦,就使用c继续执行到下个断点,在循环体内常用。
#第七步:退出调试
(gdb)quit #简写q
ctrl+F5:
下面这个相当于执行执行ctrl+F5,无需自己加断点,就直接开始调试
bash
#直接开始调试
(gdb)start
#根据start的用法我们可以知道,start是为了方便我们不设置断点,直接开始执行调试的
监视窗口:
下面这种流程对标了添加监视窗口的操作,每次执行后,都会显示这个变量的值,一直跟踪该变量。
bash
#添加监视窗口
(gdb)display i #跟踪变量i,i是一个变量名
(gdb)undisplay i #取消跟踪变量
(gdb)ptype i #查看变量i的数据类型
gdb常见错误说明
(gdb后输入的第一条命令执行后)error:没有符号表被读取。请使用 "file" 命令。
原因:没有符号表 说明 没有生成符号表 说明 没有调试信息 说明 编译出来的文件不是调试版本 说明 编译时没有加 -g 选项
解决方案1:(gdb)quit先退出,再使用 gcc main.c -o main.debug -g重新编译一个debug版本的程序,即可。
解决方案2:根据报错提示:(gdb)file a.out无需退出即可完成读取符号表。
(输入info b语句执行后)error:No breakpointers or watchpointers.
原因:没有断点或观测点 说明 没有添加断点就妄想去观测断点信息
解决方案:使用b n在第n行添加断点,再使用info b进行断点信息查看
(输入ptype 语句,执行后)error:The history is empty.
原因:历史纪录为空导致错误 说明 ptype查看的变量不指定时需要根据历史查看变量记录来操作 说明 现在还没有查看过任何变量
解决方案:使用p i进行查看变量,再使用ptype就可以查看i的数据类型。
(执行过程中出现段错误)检查本次执行行附近涉及的代码逻辑问题,修改代码。
感谢大家!!!