在VS里面调试很方便对吧?(F5直接调试,F10逐过程调试--不进函数,F11逐语句调试--进函数,F9创建断点),那在Linux中怎么调试呢?
我们需要用到一个工具:gdb
我们知道VS中程序的版本大致有两种模式:debug(开发模式,可被调试)和release(发布模式,不可被调试),为什么会形成这样的差异呢?其实在编译器形成可执行程序的时候,会给可执行程序添加调试信息,gcc编译的时候默认生成的是release版本,这样实现gcc/g++使用debug模式编译(makefile):
processbar-debug:Main.c Processbar.c
gcc -o $@ $^ -g
.PHONY:clean
clean:
rm -f processbar-debug
debug和release版本的不同可以看出:
debug确实比release版本大了很多(11936-8792=3144) debug版本是新增调试信息,debug版本的肯定比release版本的占用空间大(确实如此则证明新增数据)
那为什么要存在这样的差异呢?
其实是因为使用角色的差异,用户是用户,程序员是程序员,用户是程序的使用者,他不需要知道如何调试,只要会用就好了,多放调试信息还浪费空间,但是程序员是开发者,必须要知道调试信息以供调试,否则开发受阻,bug很难de出来(在公司,产品经理==用户,有的时候产品经理就很抽象,假用户威给程序组没事找事,我要diss这类产品经理)
可以这样来查看debug版本下的调试信息:
readelf -S processbar-debug
.rodata:只读区
.text:代码区
.data:数据区
.bss:未初始化全局数据区
debug信息:
readelf -S processbar-debug | grep -i debug
下面简单介绍gdb的使用
使用前先安装下:
yum install -y gdb
所以想要使用就直接gdb+可执行程序名称
gdb processbar-debug
常用命令
quit/q
在进入调试器后想退出可直接quit或者q:
通过分屏可以看出gdb在使用时算一个超绝进程:
list/l
list可用于查询源文件内容,但是是不支持这样直接查询源文件的:
list Processbar.c
从第0行开始查:
list 0
简写效果是一样的:
l 0
一查默认是查十行,在gdb中是会记住上次执行过的命令的,所以只需要按回车就可以查询整个文件了:
也可以这样查:
l Processbar.c:0
还可以查询某个具体函数:
l Processbar.c:ProcBar
在查询某行的时候并不是从它开始,而是显示上下文:
l 15
run/r
直接r相当于VS中的F5,直接执行不调试
break/b
直接执行需要配合断点一起使用,break/b就是打断点
b x //x是行号
断点只能一个一个打
可以指明给某个文件:
b Processbar.c:20
还可以直接给某个函数打断点:
b main
这样打出来的断点是位于函数的第一条语句处的
与在VS中的打断点作比较:
打过断点的地方可以看出有红点,那gdb中怎么查看打过的断点呢?
可以使用这条命令(info break):
info b
i b
Enb:断点的使能:有/没有
delete/d
在打断点的时候通过文件名+行号打,但是删除的时候不可以通过这种方式删除
通过加编号的方式删除断点:
d x //x为号码
断点编号也是线性增长的
disable/enable
在VS中可以实现禁用断点的功能:
可以通过disable来禁用断点:
disable x //x为断点编号
断点有但无用:
1.被禁用
2.为空行
可以通过enable命令来开启禁用的断点:
enable x //x为断点编号
next/n
逐过程:F10
即遇到函数不进入函数调试
step/s
逐语句 :F11
遇到函数进入函数,进行逐语句调试
print/p
在VS中调试是想要看到某些变量的变化过程
在gdb中可以这样来查看变量的变化过程:
p x //x为变量名
地址也可以查哦:
p &i //i为变量名
但是这样查看又太麻烦了,太原始了哇!!!
display/undisplay
display可以常显示变量
undisplay可以把当前常显示的内容去掉
undisplay x //x为变量编号
finish
finish是运行结束所在函数就停止
until
在调试时可能出现的情况还有比如任意跳转到函数的某一行处(中间代码都运行了)
until x //x为行号
info
info b是查看断点的情况,info locals则可用于查看当前栈帧局部变量的值
info locals
i locals
set var
set var可用于修改变量的值
set var i=100 //将i的值变为100
区域化执行
调试工具的核心作用是帮助你找问题,具体的解决需要人为,如果已经定位到问题所在行,那就不需要进行调试了,调试是为了帮助我们避免进行干瞪眼这种费时费力的调试方法诞生的,如果一份代码几万行,从头盯到尾是很费时费力的,我们希望它做的是区域化执行
因为有断点的存在,我们就可以通过断点实现区域化执行,从一个断点运行到下一个断点处,按范围去打断点就可以实现按范围查找,二分打可快速缩小范围