目录

深入了解Linux-调试程序

深入了解Linux —— 调试程序

前言

我们已经学习了 linux 下许多的工具, vimgccmake/makefile 等;

已经能够在 linux 写代码,并且进行编译运行,让程序在 linux 下跑起来。

但是,如果我们在写代码的时候遇见了错误;但是我们并不知道错误在哪,在 windows 下,我们可以进行调试来查找代码错误的位置进行修改;我们在 linux 就只能查看源代码,直接查找错误,这样很麻烦;

现在就来学习 linux 如何调试程序。

调试程序 —— gdb/cgdb

Debug/Release 模式

在之前学习 C语言 时,听说过 DebugRelease ,只知道 Debug 时用来调试的,程序员写代码的版本;而 Release 是发布版本。

现在我们来看一下这两个模式有什么区别

  • 首先的区别就是 Debug 会生成程序的调试信息,而 Release 不会生成程序的调试信息。

因为 Debug 模式会生成调试信息,所以 Debug 模式的程序就要比 Release 模式的程序大小要大。

我们如何验证呢?

linuxgcc 编译默认生成的是 Release 版本,我们要生成 Debug 模式就要带 -g 选项。

现在有这样一段代码 test.c 文件

#include<stdio.h>
int func(int n)
{
	int ret = 0;
	for (int i = 1; i <= n; i++)
	{
		ret += i;
	}
	return n;
}
int main()
{
	int n = 100;
	int sum = func(n);

	printf("sum = %d\n", sum);
	return 0;
}

我们在 linux 下进行编译

https://i-blog.csdnimg.cn/direct/7df877abd4e349b785f1a7136b17d0da.png#pic_center

这里有这样的提示,那是因为 C98 不支持在 for 循环中定义变量,要使用 C99 ,上面也有提示。

所以我们就要这样来编译

gcc test.c -o test -st=c99

这里写成 makefile 方便操作。

这样生成的是 Release 模式的程序,我们查看它文件属性

https://i-blog.csdnimg.cn/direct/0e07de51fceb4195ab8fddc41a9f33d3.png#pic_center

现在,我们使用 -g 选项生成 Debug 模式的程序

https://i-blog.csdnimg.cn/direct/790d63439d224021b8f80a08b947f609.png#pic_center

可以看到 Debug 模式的要比 Release 模式的程序要大一些。

gdb / cgdb 的使用

这里,只有 Debug 模式的程序才能被调试; Release 模式下不能被调试,因为缺少调试信息。

在使用之前可能需要进行安装

yum -install -y gdb
yum -install -y cgdb

1. 进入调试

gdb 可执行程序

这里无论是 gdb 还是 cgdb ,都是可执行程序,对可执行程序进行调试。

https://i-blog.csdnimg.cn/direct/42b2732192274d2a93bb7a38751ab2ee.png#pic_center

可以看到这样就进入 gdb 调试了,但是 gdb 调试现在看不到我们的源代码。

2. 退出调试

quit

https://i-blog.csdnimg.cn/direct/5d9e9de8617e4ef4bc2c22bf97af3bee.png#pic_center

现在来看一下 cgdb 调试的界面

cgdb test

https://i-blog.csdnimg.cn/direct/0f6e4866a1c742d28437bec5ebfc7d94.png#pic_center

这样的界面看起来要比 gdb 好用一些,所以这里就以 cgdb 为例,来学习调试

cgdb 中,屏幕上半部分可以看到一部分代码;其中 绿色箭头 指向的地方就是当前程序运行的位置。

退出调试仍然是 quit

3. 查看代码

查看代码, l ;后可以什么的不跟,也可以跟行号或者函数名

  • l :查看源代码,从上次位置开始,依次显示 10 行代码
  • l 文件名:行号 :列出指定文件的源代码
  • l 函数名 :列出指定函数的源代码

https://i-blog.csdnimg.cn/direct/6319d2ca6a2143c0a9041077300870c5.png#pic_center

l 文件名:行号

https://i-blog.csdnimg.cn/direct/2056ae5592654622a5bfa82ec0051bdf.png#pic_center

l:函数名

https://i-blog.csdnimg.cn/direct/f41e3f5334304a269f4628f7d9a6ad02.png#pic_center

这里列出的可能有一些差别。

4. 运行代码

我们进入调试,但是代码并没有运行起来;在 windows 下我们之间 F5 就让代码运行起来了;而 cgdbr 命令可以让代码运行起来

r/run ,执行代码:

https://i-blog.csdnimg.cn/direct/f8e4d60d79cf418eb28f93e7c3a80f74.png#pic_center

逐步执行

有了断点,我们在 r 时程序就会停止在断点处,那我们该如何一行一行执行代码呢?

windows 下,我们是按 F10F11 来依次执行代码;

linuxcgdb ,我们使用 n/nexts/step 来依次执行代码

n/next ,相当于 F10 ,一行一行执行代码, 在遇到函数时,不进入函数内部;

s/step ,就相当于 F11 ,一行一行执行代码, 在遇到函数时,进入函数内部;

这里就不演示了。

执行到某处

在我们调试程序时,程序现在停止在一个断点处,我们不想一行一行执行代码,而是想要让程序直接运行到下一个代码;

只需要指向 c/contine 即可

c/continue

https://i-blog.csdnimg.cn/direct/8304df2495b74dc3b2ee7ac3757e1849.png#pic_center

r 重新执行

如果现在程序正在执行,我们想要让程序重新执行,只需要 r 即可;

这是会询问我们是否重新执行, y 即可

https://i-blog.csdnimg.cn/direct/7facc919745645438b06ca0e7452c75e.png#pic_center

finish

执行到当前函数结束,然后停止

https://i-blog.csdnimg.cn/direct/18e318d6bb7b425ab4bf466ebebf3114.png#pic_center

until 执行到某一行

until 行号 程序执行到某一行然后停止。

https://i-blog.csdnimg.cn/direct/9be29d9e51884ad58cdef994e0660a23.png#pic_center

现在执行 until 11 ,让程序执行到 11 行。

https://i-blog.csdnimg.cn/direct/019080c7b31a4de1b68344dbf6365da0.png#pic_center

5. 断点

增加/删除断点

我们指向 run/r 后,发现代码直接就执行结束了;但是在我们调试的时候,我们并不希望代码执行运行结束,我们需要通过断点让代码在指定位置停下来;

windows 下,我们通过快捷键 F9 或者鼠标点击来打断点和去掉断点;

cgdb 中,我们通过命令** b/break **来打断点,通过 delete/d 来取消断点。

b 打断点

  • b 行号 :在指定行打断点
  • b 函数名 :在函数开头打断点

https://i-blog.csdnimg.cn/direct/dbcdfa6ee7dc40a49b60a10af2f1ca86.png#pic_center

可以看到,我们打断点之后并看不到任何断点信息,那如果我们想要看到已经存在的断点,可以使用命令 info b 来查看

info b 查看所有断点信息

https://i-blog.csdnimg.cn/direct/5c045f0e455646849dc2e6e416529645.png#pic_center

d 删除断点

我们打断点可以通过 行号 ,但是删除断点我们就不能使用行号了,而是使用 Num 断点编号。

https://i-blog.csdnimg.cn/direct/3a7736d94440403f856595282c2d0648.png#pic_center

这里还要注意一个点,断点编号时不断递增的,不会随着我们删除断点而减小

什么意思呢,就是现在存在两个断点我们删除了其中一个断点,然后再次创建了一个断点,它的编号就是 3 而不是 2

https://i-blog.csdnimg.cn/direct/628fea60fa1c4df89da1164c23e8ab28.png#pic_center

这样有了断点,我们在执行 r 时就程序就会停止在断点处。

https://i-blog.csdnimg.cn/direct/2d4cc363baea41b4bc6fa1d97fd91985.png#pic_center

这里看一下上半代码部分,可以看到程序停在了 15 行,并且断点位置的行号颜色为红色。

启用/禁用断点

当我们在调试程序时,我们增加的断点并不一定所有的都能用的到,有一些断点我们不想让它在这次调试中起作用,这时就可以禁用这个断点

看到这里可能有疑惑,为什么不直接删除呢?

如果代码非常的多,删除了之后,接下来调试要用到,又要重新去找,非常浪费时间。

启用断点

enable 断点编号

禁用断点

disable 断点编号

这里断点默认是启用状态的。

https://i-blog.csdnimg.cn/direct/ea5073dd2dd34e4bbb8f873b9a5ee1d6.png#pic_center

那现在执行一下看是否真的禁用了呢?

https://i-blog.csdnimg.cn/direct/b94c198a1cd14ee6bf98d51c020dd3e1.png#pic_center

可以看到第一个断点并未触发,而是直接触发第二个断点。

6. 监视

windows 下我们通过监视窗口来查看一个变量的值;

而在 linuxcgdb 中,我们也可以通过指令来查看变量的值。

监视变量

p

p 用来查看一个变量当前的值

https://i-blog.csdnimg.cn/direct/454e3afd3298464db3130bd14a4373b9.png#pic_center

但是这样,我们在此执行代码会发现,执行过后就不在显示了;

这样我们每次查看就要去输入指令 p 变量名 ,这样好麻烦,我们想要每一次执行过后,它都会显示出来变量的值

display 用来跟踪显示变量的值。

display

https://i-blog.csdnimg.cn/direct/516301131c484c3f9bb9b98637b1b91f.png#pic_center

如上图所示,我们每一次执行代码,变量的值都会显示出来。

监视函数栈帧内局部变量

如果我们不是想要查看某一个变量,而是查看当前函数内所有的局部变量?

我们就要用到 info/i locals

https://i-blog.csdnimg.cn/direct/35f78b215b35451697e03f09051639c6.png#pic_center

可以看到 func 中所有的局部变量都显示出来了。

查看当前函数调用栈帧

如果我们想要查看当前的函数调用栈帧,直接使用 bt/backtrace 即可。

https://i-blog.csdnimg.cn/direct/820f8253b12f43439bd702116133a1ca.png#pic_center

cgdb 常用小技巧

1. watch

watch :用来监视一个变量的值是否发生变化,发生变化时会提示。

https://i-blog.csdnimg.cn/direct/e6ad5caf8ad0488c8401b8e926eb67f4.png#pic_center

我们使用 info b 查看断点中也可以看到 watch 监视的变量。

2. set war

set war :在调试过程中,修改变量的值。

https://i-blog.csdnimg.cn/direct/f6385664be3c4341bd6616e02646f09d.png#pic_center

可以看到,我们在调试过程中,使用 set var 就可以修改一个变量的值。

3. 条件断点

添加条件断点

b 行号 if 条件

https://i-blog.csdnimg.cn/direct/8a72c8560576424eb8c89f7f7fb89f7e.png#pic_center

如上图所示,新添加的条件断点(当 i==10 时触发)。

https://i-blog.csdnimg.cn/direct/7b14eb6c2b5a4d82a467fbeb9a2b8c72.png#pic_center

可以看到程序在 i==0 时,断点触发,停止在第九行。

给已存在断点增加条件

当我们需要给已经存在的断点增加条件时,我们需要指令

condition 断点编号 条件

https://i-blog.csdnimg.cn/direct/17b077254c52426cbed70291ba01c935.png#pic_center

到这里本篇内容就结束了,希望对你有所帮助。

制作不易,感谢大佬的支持。

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws