个人主页 : zxctscl 如有转载请先通知
在前面的博客【Linux】编译器-gcc/g++使用已经分享了关于编译器的使用,而编译器的使用离不开调试,这次就来分享一下Linux调试器-gdb使用。
要调试就得先有代码,先用C语言写一段简单的代码myprocess.c,再写好Makefile: myprocess.c代码:
1 #include<stdio.h>
2
3 int AddToTarget(int start,int end)
4 {
5 int i=start;
6 int sum=0;
7 for(i=start;i<=end;i++)
8 {
9 sum+=i;
10 }
11 return sum;
12
13 }
14 int main()
15 {
16 printf("run begin...\n");
17
18 int result=0;
19 result=AddToTarget(1,100);
20 printf("result:%d\n",result);
21
22 printf("run end...\n");
23 }
Makefile:
1 myprocess:myprocess.c
2 gcc -o $@ $^
3 .PHONY:clean
4 clean:
5 rm -f myprocess
make一下没有问题:
在vs发布软件有两种模式一种是debug,一种是release。 测试用的是debug,可以被跳绳,而开发出来的release版本,是不可以调试的。
在debug版本中,编译器形成可执行程序的时候,会给可执行程序添加调试信息。
gcc/g++默认编译,采用的是release模式
如果想让gcc/g++采用debug模式,就得再后面加上-g选项
怎么来判断是不是debug模式呢? 在debug模式下新加了调试信息,体积就会比release模式下的大,那么就确定debug模式会新增加东西。 来测试一下:在Makefile里面新加-g选项:
1 myprocess-debug:myprocess.c
2 gcc -o $@ $^ -g
3 .PHONY:clean
4 clean:
5 rm -f myprocess
然后make一下: 发现两个程序都可以跑:
对比发现debug的体积比较大:
为了给用户更好的体验,发布release模式,减少了不必要的调试,也减小下载时带宽的浪费。 而程序员在写代码的时候需要调试,所以就有了release模式和debug模式。
读一下debug模式下的信息:
readelf -S myprocess-debug
发现下面有debug信息
忽略大小写信息,在debug下的调试信息:
readelf -S myprocess-debug | grep -i debug
用同样的方式来查看release版本下的调试信息:readelf -S myprocess | grep -i debug
发现没有debug信息
默认系统中会安装gdb,使用方法就是gdb后面直接加上调试的可执行程序名:
gdb myprocess-debug
就会默认进入到调试模式
想要退出就直接输入quit或者q
quit/q
gdb本身就是一个进程,把程序启动起来
list来显示程序代码
list
list后面直接更程序名是查不了代码的:
为了方便list可以直接简写为l
。
如果想要从程序第一行开始查就用命令:
l 0
还可以使用l加程序名再加0的方式
l myprocess.c:0
不可以直接查文件,但是加上行号就可以查。 还可以查main函数:
l myprocess.c:main
想要查某一块代码可以加上行号,也可以加上对应的函数名就可以了。 发现gdb查代码只能默认查10行
如果想要全部打出来怎么办? gdb默认会记录用户最近的一条命令,直接按回车 就可以拿到全部的代码
如果查看第15行:
发现它并不是从15行开始,而15行差不多是在显示的代码中间的位置。 同样查看AddToTarget也是:
l查指定的行或者函数时候,会显示它的上下文。
run
就是把程序运行起来,简写为r
。
这个run的功能就类似于VS里面的F5,直接运行不调试。
gdb中用b来打断点 可以直接用b加程序名加函数 比如在main函数处打一个断点:
b myprocess.c:main
比较一下发现,代码断点并不是打在15行位置,而值17行,因为17行是main函数的入口位置:
在第20行打断点,直接这样:
b myprocess.c:20
因为每个断点都有自己的编号
想要连续打断点,怎么办呢? 如果加上“,”发现不能用。
试一下空格:发现也不行
所以断点不允许连续去打,而必须一个一个去打:
而这里的断点编号是3和4,因为1和2在前面已经用了。
所以断点的本质是一个线性增长的计数器。 代码运行到断点位置就会停下来:
info(或i) breakpoints
:参看当前设置了哪些断点
info(i) locals
:查看当前栈帧局部变量的值
在vs下打断点就会有红点
那么在gdb下怎么知道哪些地方打了断点呢? 这里就要用到一个命令info
info b
或者写成:
i b
就能显示出来在哪些地方打了断点:
这里的Enb表示使能,代表一定有断点,但不一定打开。而这里的y表示断点打了能用。
info查当前变量的值:
在vs里面要取消断点直接点一下或者按F9。 在gdb下用的是d加文件名加行号,发现不能用
删断点就要用这里的Num:
删除一号断点:
d 1
再删除2号断点
此时已经没有断点了。
在vs中在断点位置右击可以禁用断点:
在调试的时候就会跳过禁用的断点:
也就是把这个断点使能了。
而在gdb中也想这样做,那么就用下面这个命令:
disable Num
试一下myprocess.c里面的3号断点:
disable 3
发现3号断点的Enb就变为n了:
重新运行一下代码:发现停在了19行,因为17行的断点使能已经关闭,而18行是空行,就直接到了19行:
空行是不做调试的,打了断点也没用。
在vs里面要想启动断点,直接右击就会出现
而在gdb中重新启动,想要用到命令:
enable Num
重新启动3号断点:
enable 3
发现这里的3号断点的Enb就变为y了
在vs里面的F10就是逐过程,就是在调试时候单步往下走时,如果碰见当前行的代码是函数,就直接把这个函数执行完,把这个函数当成一条语句直接执行完。 在gdb中想要实现逐过程就得用到命令:
next/n
打了一个断点在17行:
然后用来实现逐过程调试:发现并没有进入到AddToTarget函数里面:
在在vs里面的F11就是逐语句,在调试的时候,遇到函数就要进到这个函数里面,把这个函数里面的代码一行一行运行完。
在gdb中想要进入到函数的内部就用命令:
step/s
打了一个断点在17行:
然后用来实现逐语句调试:发现进入到AddToTarget函数里面:
发现这个代码就在7和9行之间反复执行:
print/p查看变量内容及地址 在vs里面常用的监视窗口:
而在gdb中用到的命令就是:
print/p
来试一下: 这时就能看到i对应的值了:
查里面的地址就加上取地址符就行:
每次都先输入p才能查看监视的内容,太麻烦,就用display来进行常显示,每次都自动变化:
display
它也可以来查看地址:
发现每一次常显示前面都会有一个编号:
如果不想常显示,就用到undisplay去掉常显示
undisplay+编号
去掉5号和4号监视:
一般在vs里面可以直接从一个断点处运行到下一个断点处,就是执行中间那一部分。 而在gdb中想要一个部分一个部分的调试,从而方便找出代码的问题,就用到命令:
continue/c
先打一些断点;
此时运行的时候就发现在17行就停下来了:
想要直接从一个断点运行到下一个断点处就直接:c
finish运行结束所在函数,就停下来 如果函数里面有问题,在不进入函数里面,就想知道函数有没有问题,就用到命令:
finish
finish就是把指定的函数跑完 来测试一下:
until:跳转到指定行,中间的代码都是运行了的。 进入到函数体里面就退不出来
如果想要跳转到某一行,就用到命令:
until
试一下跳转到12行:
总结一下gdb使用就是:
set var:修改变量的值 把指定变量直接修改为目标值,然后检测等于这个目标值时会不会执行对应代码。 举个例子:把i值改为100
bt就相当于调用堆栈
有问题请指出,大家一起进步!!!