目录
- 1.debug和release
- 拓展
- 2.如何使用gdb调试
- 3.指令集
我们平常调试C/C++代码大多实在Windows平台下的VS中,在LInux中,我们通常使用
gdb
来调试代码,虽然我们很少在LInux上对代码进行调试,gdb在实际的使用中用的较少,但我们必须要懂它,以面对各种环境。本文目的:能够进行基本的掌握单进程程序的调试,即使用VS 2019、VS 2022的基本调试功能,都在Linux上使用gdb实现。
本文会在Linux环境下使用到vim编辑器、make/Makefile、gcc/g++,如果对这三个工具不熟悉,可点击下面的链接,结合本文一起学习。
vim编辑器
make/Makefile
gcc/g++
1.debug和release
在学习Linux下使用gdb调试之前,我们需要掌握以下三个有关debug和release模式的铺垫知识
-
程序的发布方式有两种,debug模式和release模式
debug模式:该模式是程序员编写程序和调试代码的模式,该模式下生成的可执行程序含有调试信息体积要大于release模式生成的可执行程序。
release模式:没有调试信息,对程序做出优化,减小了程序的体积,更适合用户使用。该模式下生成的可执行程序是公司最终要推向市场的,由公司内测试人员测试是否完善,测试通过后推向市场。
- 注意: debug模式下可以调试,release模式下无法调试。
下图是VS 2019下,两种模式的选择图,需要那种模式选择那种模式即可
那在Linux下如何进行两个模式的转换呢? -
Linux下,gcc/g++出来的二进制可执行程序,默认是release模式
- 即 在Linux下,使用常规的指令生成可执行程序为release模式,无法进行调试
//常规生成 gcc -o 可执行文件名 源代码文件 g++ -o 可执行文件名 源代码文件
-
要使用gdb调试,必须在使用gcc/g++生成二进制程序时,加上
-g
选项gcc -o 可执行文件名 源代码文件 -g g++ -o 可执行文件名 源代码文件 -g
下面我们在如下test.c文件下编辑测试代码,进行测试
注意: 第7行代码,在for循环内定义变量i
,这种写法是在c99中开始支持的,我使用的gcc/g++编辑器不支持这种写法,需要在执行命令后添加-std=c99
,
Makefile文件如下:
若不会使用Makefile文件,可直接使用如下两条指令测试
gcc -o MyTest-debug test.c -g -std=c99 //生成debug模式的可执行文件
gcc -o MyTest-release test.c -std=c99 //生成release模式的可执行文件
首先,测试release模式下,是否可以调试
如上图显示,红色框内,表示没有找到调试符号,无法进行调试。
其次,测试debug模式下,是否可以调试
没有显示错误信息,可以正常调试
最后: 观察两个可执行文件的体积,明显debug要大于release
拓展
我们可以使用readlef
指令查看两个可执行文件下的信息,如下:
我们可以通过该指令,查看两个文件下的debug信息,如下(grep 指令为查找字符串指令):
MyTest-debug文件下找到了debug信息,MyTest-release什么都没有。
2.如何使用gdb调试
调试器的核心工作:定位问题
接下来简绍在Linux下如何使用gdb进行调试,该段落为分步骤讲解,将gdb下的命令总结放在了下一段落。
如果大家对gdb并不属性,按步骤操作即可
-
进入调试
使用
gdb file
指令进入调试模式,如下图
-
显示要调试的代码
l/list 行号
:显示文件源代码,从输入的行号起,向下列出十行。一般我们在开始调试时,从l 1
开始查看,接着输入l
或Enter
键,即可获得上次查找位置往下的代码
注意: 输入gdb的指令后,Enter
键就会记录。即,输入指令后,接着输Enter键会重复上面的动作。下面的指令也遵循这个规则。- 在刚进入gdb模式后,单独使用
l
进行查看,显示的代码为随机的
-
设置断点
-
break/b 行号
:在某一行设置断点如下图,在19行设置断点
-
break/b 函数名
:在某个函数开头设置断点如下图,在addTestTop函数处设置断点
5是addTestTop函数内第一行代码的行号。
-
break/b 文件名:函数名
:多文件时,可使用该方法设置断点 -
info break/b
:查看断点信息当程序运行后,会在断点处停下,在次查看断点,会显示在该断点处停下的次数。
注意: 在gdb中断点的序号是增长的,每次使用后会不断增长,直到关闭在开启后,重新从1开始
-
-
运行程序
-
r/run:运行程序
- 有断点的情况,运行到断点处停止
- 无断点的情况,直接运行完
- 有断点的情况,运行到断点处停止
-
注意: 一次运行是在全部main函数内全部代码运行完后结束,中途遇到断点停下后,进行其他查看指令不会影响此次调试
-
-
有断点,运行后,逐语句执行
-
s/step
:遇到函数,进入函数内部
注意: 进入其他函数后,会产生调用链,即一个函数调用另一个函数,接着再去调用其他函数。
可通过
bt
指令查看 -
-
有断点,运行后,逐过程运行
n/next
即遇到函数不进入函数,直接执行下一条指令
- 还记得上面说的按
Enter
键,执行上次执行的指令吗?如果需要调试的的内容过多,按一次s
或n
后接着按Enter
键即可
-
删除断点
-
delete/d breakpoints:删除所有断点
-
delete/d n:删除序号为n的断点
-
-
打开和关闭断点
当我们不想使用一个断点,也不想删除该断点时使用
-
disable breakpoint 断点序号:关闭断点
-
enable breakpoint 断点序号:打开断点
-
-
查看变量
-
p 变量名
:这个指令是需要查看的时候输入,不会影响调试,如下我在调试进入addTestTop函数后,执行了几段语句后,查看sum变量的结果,此时sum为6. -
这样查看的效率太低,我们在VS中使用调试时,是执行一次相对应的变量随之变化,可以直接查看,该方法需要用户自己调用,不方便,我们可以使用以下的方法
-
-
跟踪查看变量
-
display 变量名
:执行后,该变量在每执行一个语句后,都会显示出来 -
可以显示内置类型、结构体等自定义类型、stl
-
-
取消对断点的跟踪
-
undisplay 序号
:不想跟踪某变量时使用
-
-
跳转至指定行
until 行号
:跳转至X行,在我们上面的代码中有一个循环,我们可以使用该指令跳转出循环
-
将整个函数跑完
finash
:进入一个函数后执行,只执行该函数,执行完后停下
-
从一个断点,到下一个断点
-
continue/c
:直接运行到下一个断点处 -
将
util、finish、continue
结合后,可实现基本的调试功能
-
-
退出gdb
q/quit
:如果正在调试,会提示是否需要退出
3.指令集
gdb binFile
退出: ctrl + d
或 quit/q
调试命令:
- list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
- list/l 函数名:列出某个函数的源代码。
- r或run:运行程序。
- n 或 next:单条执行。
- s或step:进入函数调用
- break(b) 行号:在某一行设置断点
- break 函数名:在某个函数开头设置断点
- info break :查看断点信息。
- finish:执行到当前函数返回,然后挺下来等待命令
- print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数
- p 变量:打印变量值。
- set var:修改变量的值
- continue(或c):从当前位置开始连续而非单步执行程序
- run(或r):从开始连续而非单步执行程序
- delete breakpoints:删除所有断点
- delete breakpoints n:删除序号为n的断点
- disable breakpoints:禁用断点
- enable breakpoints:启用断点
- info(或i) breakpoints:参看当前设置了哪些断点
- display 变量名:跟踪查看一个变量,每次停下来都显示它的值
- undisplay:取消对先前设置的那些变量的跟踪
- until X行号:跳至X行
- breaktrace(或bt):查看各级函数调用及参数
- info(i) locals:查看当前栈帧局部变量的值
- quit:退出gdb