【Linux】调试器-gdb使用 结合 vs2022学会代码调试

     📚 博主的专栏

🐧 Linux   |   🖥️ C++   |   📊 数据结构  💡C++ 算法 | 🌐 C 语言

上篇文章:Makefile的应用,以及第一个Linux程序进度条

下篇文章:操作系统内存管理:地址空间、页表、写时拷贝与进程调度原理                

目录

1. 背景

2.程序是debug模式才能能被调试,release不可被调试,why?

如何证明?

指令readelf:读取elf可执行程序

2.开始调试

下载gdb指令

gdb调试命令:

回车:gdb会默认记录你最近一次执行的命令,直接回车执行最近一次执行的命令

1.list / l + 文件名  +  :+  行号/函数名   :显示binFile源代码,接着上次的位置往下列,每次列10行

2.r   :运行程序

3.break / b   :打断点

4. info / i + b   :查看我们所打的断点

5.delete / d + 断点Num (编号)   :删除指定断点

6.disable + 断点号    :Num禁用(关闭)断点

7.enable + 断点号Num    :使能断点,打开断点

现在我们删除所有的断点,进入实际调试:

8.  step / s  :逐语句(会进入函数) | next / n    :逐过程

9.display + 变量名/&变量名     :常显示,每次执行next、step,会自动打印出display 过的变量值

10.undisplay + 编号Num   :取消一个常显示:

现在我们开始学习实用的调试方法

11.continue / c    :从当前断点移动到下一个断点

12.finish   :快速运行完当前函数,并停下

13. info / i + i、local     : i(查看进程信息)、local查看当前函数当中所含的临时变量的值等价于vs查自动变量​编辑

14.breaktrace(或bt)    :查看各级函数调用及参数


1. 背景

1.程序的发布方式有两种,debug模式和release模式
2.Linux gcc/g++出来的二进制程序,默认是release模式
3.要使用gdb调试,必须在源代码生成二进制程序的时候,加上 -g 选项

2.程序是debug模式才能能被调试,release不可被调试,why?

因为在debug模式下、变异形成可执行程序时,会给可执行程序添加调试信息

如何证明?

首先:

gcc/g++默认编译,采用release模式

gcc/g++,让他debug模式编译,  -g

我们可以利用在debug模式下、变异形成可执行程序时,会给可执行程序添加调试信息

对比debug、release模式下生成的可执行文件的 体积大小(debug>release)来证明:

在调试之前、我们需要先创建一个程序(myprocess.c),并使它能够自动化构建(Makefile):

1.Makefile:(此时所编译形成的可执行文件是默认的release模式下的)

myprocess:myprocess.c
    gcc -o $@ $^
.PHONY: clean
clean:
    rm -f myprocess

2.Makefile:(此时所编译形成的可执行文件是默认的rdebug模式下的)

myprocess-debug:myprocess.c
    gcc -o $@ $^ -g
.PHONY: clean
clean:
    rm -f myprocess

3.我在myprocess.c中写了一个这样的求和代码:

int AddToTarget(int start,int end)
{
	int i = start;
	int sum = 0;
	for(; i <= end; i++)
	{
		sum += i;
	}
	return sum;
}	

int main()
{
	printf("run begin...\n");
	
	int result = 0;
	result = AddToTarget(1,100);
	printf("result: %d\n",result);

	printf("run end...\n");

	return 0;
}

在我编译运行后:

怎么证明是调试信息呢?

在命令行输入:通过管道忽略大小写查看包含debug的信息

指令readelf:读取elf可执行程序

我们在linux下变异形成的可执行程序遵循ELF格式

readelf -S myprocess-debug | grep -i debug

往下阅读可发现:

在命令行输入:通过管道忽略大小写查看包含debug的信息查看release模式的:

readelf -S myprocess | grep -i debug

不会输出任何信息

2.开始调试

首先,也许你的系统下还没有下载gdb

下载gdb指令

apt-get install gdb

调试:

gdb myprocess-debug

gdb调试命令:

回车:gdb会默认记录你最近一次执行的命令,直接回车执行最近一次执行的命令

ctrl + d退出
quit / q退出
list/l 行号显示binFile源代码,接着上次的位置往下列,每次列10行
list/l 函数名列出某个函数的源代码
r/ run运行程序
n / next单条执行
s / step单步运行,进入函数调用
break / b 行号在某一行设置断点
break/b 函数名在某个函数开头设置断点
info / i break / b查看断点信息
delete / d 断点的Num删除指定断点
finish执行到当前函数返回,然后挺下来等待命令
print / p +变量/&变量打印表达式的值,通过表达式可以修改变量的值或者调用函数
set var修改变量的值
run / r从开始连续而非单步执行程序
delete / d + Num删除序号为Num的断点
delete / d + b删除所有断点
disable + Num禁用断点
enable + Num启用断点
info / i + break / b:查看当前设置了哪些断点
display + 变量名 / &变量名跟踪查看一个变量,每次停下来都显示它的值
undisplay + 变量名 / &变量名取消对先前设置的那些变量的跟踪
breaktrace / bt 查看各级函数调用及参数
info / i + locals查看当前栈帧局部变量的值

查看进程:

ps ajx | grep gdb

1.list / l + 文件名  +  :+  行号/函数名   :显示binFile源代码,接着上次的位置往下列,每次列10行

list:

list / l  + 行号

list / l + 文件名 + :+ 行号 / 函数名

:行号

:函数名

都是显示上下文

2.r   :运行程序

3.break / b   :打断点

b myprocess.c:main

从程序的入口打断点

b 20

给程序的第20行打断点

4. info / i + b   :查看我们所打的断点

5.delete / d + 断点Num (编号)   :删除指定断点

6.disable + 断点号    :Num禁用(关闭)断点

在vs2022中

在Linux中:

使能 - no

7.enable + 断点号Num    :使能断点,打开断点

vs:

Linux

现在我们删除所有的断点,进入实际调试:

将断点设置在,第一次打印时

在vs中:有逐语句、逐过程

在Linux中:

8.  step / s  :逐语句(会进入函数) | next / n    :逐过程

next / n :逐过程是不会进入函数内部:

step / s 逐语句:

9.print 查看变量情况(监视、自动窗口)打印表达式的值,通过表达式可以修改变量的值或者调用函数

vs:

监视

自动窗口

Linux

print / p   变量名

$数字 -- 查询次数 , = 后面是查询的变量的值

print / p   &变量名 --- 取地址

9.display + 变量名/&变量名     :常显示,每次执行next、step,会自动打印出display 过的变量值

我们注意到每个显示的前面也有编号:

10.undisplay + 编号Num   :取消一个常显示:

以上都是最基础的gdb调试指令

现在我们开始学习实用的调试方法

需要用到调试器的地方一般是去解决中大型项目的问题

调试工具只是帮我们来找问题的

当代码出问题了,我们应该以什么样子的思路来找问题???

以二分查找的办法查找问题

我们可以将我们的代码想象成一个有序地数组,此时想要查找出问题的部分

我们可以先放开代码的前半部分,注释掉后半部分,查找完成后、如果出现问题再分成两半来查找,前半部分没有问题,再放后半部分,这样一步步缩小范围的来查找问题所在。

我们再分析问题

在调试时,我们利用断点来使代码二分。区域性处理问题

像这样:我想直接从第一个断点在run begin停止 运行到下一个断点 run end停止,中间直接跳过,就可以像这样打断点,一个范围一个范围的解决问题。

断点的本质:帮我们快速缩小问题所在的范围,方便我们分析出问题

在vs

在Linux中

11.continue / c    :从当前断点移动到下一个断点

再根据是否出问题选择next / step(step可进入函数)去查找问题。

12.finish   :快速运行完当前函数,并停下

假如我现在确认问题就出在函数体内部:

在vs中:

我想要从当前的循环中先出来:

我们可以直接拖动当前的箭头拖动到指定位置

此时再继续执行,就相当于跳过了从上一次循环完,直接跳出

直接打印出上次循环完,i = ?某个值时,的result。而不是最终的结果5050。

在Linux中,我现在也想要直接跳出循环直接看result的值:

从当前,将所有循环执行后跳出到指定的第12行

直接得到结果:

13. info / i + i、local     : i(查看进程信息)、local查看当前函数当中所含的临时变量的值等价于vs查自动变量

14.set var  +   需要修改的变量以及赋值 :改变量的值

有些类似于:刚才在finish中所举的vs的例子

set var 通常用来

在你的代码中,可能有一个判断条件,但是他总是不执行,所以根据判断条件将某变量设置成某值

再去找为什么这个判断条件没有设置成功的原因

14.breaktrace(或bt)    :查看各级函数调用及参数

在vs中调用堆栈:

刚开始点击:栈顶为main()

开始调试:进入函数:

此时栈顶就成了:AddToTarget(int start,int end)

调用栈能看到我们函数的调用链

在Linux中

结语:

       随着这篇关于题目解析的博客接近尾声,我衷心希望我所分享的内容能为你带来一些启发和帮助。学习和理解的过程往往充满挑战,但正是这些挑战让我们不断成长和进步。我在准备这篇文章时,也深刻体会到了学习与分享的乐趣。

       在此,我要特别感谢每一位阅读到这里的你。是你的关注和支持,给予了我持续写作和分享的动力。我深知,无论我在某个领域有多少见解,都离不开大家的鼓励与指正。因此,如果你在阅读过程中有任何疑问、建议或是发现了文章中的不足之处,都欢迎你慷慨赐教。         你的每一条反馈都是我前进路上的宝贵财富。同时,我也非常期待能够得到你的点赞、收藏,关注这将是对我莫大的支持和鼓励。当然,我更期待的是能够持续为你带来有价值的内容,让我们在知识的道路上共同前行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值