Linux下GDB的使用
环境:Ubuntu 18.04.6
简介:
gdb是由软件系统社区提供的调试器,同gcc配套组成了一套完整的开发环境,可移植性很好,支持非常多的体系结构并被移植到各种系统中(包括各种Unix系统与Windos系统里的MinGW和Cygwin)。此外,除了C语言之外,gcc/gdb还支持包括C++、Objecttive——C、Ada和Pascal等各种语言后端的编译和调试。gcc/gdb是Linux和许多类Unix系统中的标准开发环境。Linux内核也是专门针对gcc进行编码的。
1. 调试准备
参数:
项目程序如果是为了调试而进行的编译,那么必须要打开调试选项-g
(该指令在编译阶段起作用)。此外还有一些其它选项,如:
- 在尽量不影响程序行为的情况下关闭编译器的优化选项
-O0
- 使用
-Wall
选项打开所有的warning输出。
而-g
的作用是在可执行程序中加入源代码的信息,比如可执行文件中的第几条机器指定对应源代码的第几行,但并不是将整个源文件嵌入到可执行文件中,因此在调试时必须能保证gdb能找到源文件
。
EG:
-
编写一个测试文件:
#include <stdio.h> #include <string.h> int main(int argc, char* argv[]){ printf("输入了%d个参数\n", argc); for(int i=0; i<argc; i++){ printf("第%d个参数为:%s\n", i, argv[i]); } return 0; }
-
使用gcc进行编译,一个正常编译,一个通过
-g
参数说明编译时加入gdb支持:gcc args.c -o app # 正常编译 gcc -g args.c -o appGdb # 加入gdb支持 # 展示文件大小 ll -rwxrwxr-x 1 beasts777 beasts777 16696 11月 15 03:24 app* -rwxrwxr-x 1 beasts777 beasts777 19336 11月 15 03:25 appGdb*
可以看出:加入调试支持的可执行程序更大一些。
2. 启动和退出gdb
2.1 启动gdb
命令格式:
gdb 程序名
需要注意的是:此时gdb进程启动,但程序并未启动。如果想要启动程序,需要其它指令。
EG:
gdb appGdb
2.2 命令行传参
有些程序启动时需要传参,在gdb中传参,步骤如下:
-
启动gdb:
gdb 程序名
-
在gdb启动后,程序启动前设置参数:
# 语法: set args 参数1 参数2...
-
查看设置的参数:
show args
EG:
当前调试程序为:appGdb
# 输入
gdb appGdb
set args a b c d
show args
# 输出
Argument list to give program being debugged when it is started is "a b c d".
2.3 gdb中启动程序
在gdb中启动程序有两种方法:
run
(等价于r
):如果程序设置了断点,会停在第一个断点的位置。如果没有,那么程序将会直接执行完毕。start
:启动程序,但会阻塞在程序第一行,等待用户的后续操作。
如果想要在start
阻塞后继续运行,或遇到断点后继续运行,应当使用指令:
continue
(等价于c
):让程序在当前阻塞处或断点处继续运行。
注意:在整个gdb调试过程中,启动应用程序的命令只能执行一次
。
2.4 退出gdb
通过命令:
quit
(等价于q
):通过该命令终止gdb进程。
3. 查看代码
简介:
gdb作为一个命令行程序,并不提供GUI界面,对于代码的查看工作通过指令完成,即:
list
(等价于l
):通过该命令可以查看不同文件中的代码,还可以根据文件行号、函数名来查看指定位置的代码。
3.1 当前文件
一个程序往往由多个源文件组成,默认情况下当前文件为main
函数所在的原文件,list
查看到的代码信息也默认是main函数所在的文件的信息,除非进行文件切换。
list:
# 使用list或l均可
# 默认从第一行进行展示,默认展示地行数为10行
(gdb) list
# 可以指定行号,显示该行的上下文,默认10行
(gdb) list 7
# 可以指定函数名,显示该函数的上下文,默认10行
(gdb) list add
如果想查看后续文件,可以:
- 继续输入
l
- 直接按回车键
3.2 切换文件
有时我们可能需要查看除了main
函数所在文件以外的其他文件,这就需要我们切换当前文件位置,切换方式如下:
# 切换到指定的文件,并列出这行号对应的上下文代码, 默认情况下只显示10行内容
(gdb) l 文件名:行号
# 切换到指定的文件,并显示这个函数的上下文内容, 默认显示10行
(gdb) l 文件名:函数名
注意:切换文件时必须指定行号或函数名。
3.3 一次显示的行数
默认一次显示10行。
关键字为listsize
,使用时等价于list
。
查看当前默认行数:
(gdb) show listsize 行数
修改默认行数:
(gdb) set listsize 行数
4. 断点操作
想要令程序阻塞到指定的代码位置,从而获取该位置程序的一些信息,如变量的值,我们就需要断点操作,并在程序阻塞到这里时通过指令对代码进行调试。
设置断点的命令为break
,也可以缩写为b
。
4.1 设置断点
断点分为下面两种:
常规断点:程序运行到这里时就会阻塞。
-
设置断点到当前文件的指定位置:
(gdb) b 行号 (gdb) b 函数 # 程序将会阻塞在该函数的第一行
-
设置断点到其它文件的指定位置:
(gdb) b 文件名:行号 (gdb) b 文件名:函数 # 程序将会阻塞在该函数的第一行
条件断点:只有满足响应条件,程序才会在该位置阻塞。
-
(gdb) b 行号 if 变量名=某个值
4.2 查看断点
指令为:info break
,可以简写为:i break
EG:
-
设置断点:
# info == i # 当前文件第六行设置断点 (gdb) b 6 # 输出 Breakpoint 1 at 0x11c4: file args.c, line 6. # add.c源文件第5行,当i=2时,断点发挥作用 (gdb) b add.c:5 if i=2 # 输出 Breakpoint 2 at 0x1164: file add.c, line 5.
-
查看断点:
Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000000011c4 in main at args.c:6 2 breakpoint keep y 0x0000000000001164 in add at add.c:5 stop only if i=2
断点表中的字符:
- Num:表示断点的编号,后续操作断点都是通过编号操纵。如删除断点、修改断点状态等。
- Enb:当前断点是否可用。
y
表示可用,n
表示不可用。 - What:描述断点被作用在了哪个文件的哪一行或哪个函数上。
4.3 删除断点
指令为:delete 断点编号
。其中delete
可以简写为del
或者d
指令。
同时,可以分别删除多个断点,或删除一定范围内连续的断点。具体操作如下:
-
删除指定编号的断点:
(gdb) d 2 5
-
删除一定范围内连续的断点:
(gdb) d 2-5 # 将会删除2、3、4、5断点
4.4 设置断点状态
可以通过调整断点装填来令断点失效或生效。指令为:
- 失效断点:
disable 断点编号
。其中disable==dis
- 生效断点:
enable 断点编号。其中
enable==ena`
与删除断点类似,设置断点状态也可以批量操作断点。
EG:
# disable==dis
# 令编号为1、2的断点失效
(gdb) dis 1 2
# 令编号为3、4、5的断点失效
(gdb) dis 3-5
# enabel==ena
# 令编号为1、2的断点生效
(gdb) ena 1 2
# 令编号为3、4、5的断点生效
(gdb) ena 3-5
5. 调试命令
当程序在阻塞时,就可以使用调试命令来对程序进行调试。
5.1 继续运行gdb
命令:continue
(可以缩写为c
)
使用场景:当程序阻塞在某处时,可以使用该指令,让程序继续运行。
# continue == c
(gdb) c
5.2 打印信息
应用场景:当程序阻塞在断点时,可以通过指令打印变量的值或变量类型。
打印变量的值:
-
指令:
print
(可缩写为p
) -
输出的变量需要格式化,具体格式化方式和C中输出格式化方式一致:
格式化字符 说明 /x 以十六进制的形式打印出整数。 /d 以有符号、十进制的形式打印出整数。 /u 以无符号、十进制的形式打印出整数。 /o 以八进制的形式打印出整数。 /t 以二进制的形式打印出整数。 /f 以浮点数的形式打印变量或表达式的值。 /c 以字符形式打印变量或表达式的值。 -
语法格式如下:
# print == p (gdb) p 变量名 # 默认是10进制 # 如果变量是一个整形, 默认对应的值是以10进制格式输出, 其他格式请参考上表 (gdb) p/fmt 变量名
-
实例:
# 默认输出 (gdb) p argc $2 = 5 # 格式化输出 (gdb) p/x argc $3 = 0x5
打印变量类型
-
指令:
ptype
-
语法格式:
(gdb) ptype 变量名
-
EG:
(gdb) ptype argc type = int
5.3 自动打印信息
原因:在一些应用场景中,比如for循环,每次都手动输出变量进行追踪是较为复杂的,这时就需要自动打印信息的功能。
5.3.1 变量名自动显示
打印时机:每当程序暂停执行时,gdb就会帮我们把设置自动显示的变量打印出来。
指令:display
语法:
# 在变量有效范围内,打印变量的值(设置一次,以后就会自动显现)
(gdb) display 变量名
# 按照指定格式打印变量(这里的format和print的format表一致)
(gdb) display/fmt 变量名
5.3.2 查看自动显示列表
说明:所有通过display
设置为自动打印的变量,都会被记录到一张表内,可以通过info diaplay
查看。
指令:info diaplay
(其中的info
可以简写为i
)
EG:
(gdb) info display
Auto-display expressions now in effect:
Num Enb Expression
1: y i
2: y array[i]
3: y /x array[i]
展现出的信息含义:
- Num:变量或表达式的编号,GDB编译器为每一个变量或表达式都分配有唯一的编号。后续操作也是通过这个编号。
- Enb:当前变量的撞他。生效表示为
y
,失效表示为n
- Expression:被自动打印的变量或表达式的名字。
5.3.3 取消自动显示
有两种方式:
-
删除自动显示的变量或表达式:
# 通过undisplay # 指定编号删除 (gdb) undisplay num1 num2... # 删除一定范围内连续的变量或表达式 (gdb) undisplay num1-num2 # 通过delete (gdb) delete display num1 num2... (gdb) delete display num1-num2
-
修改自动显示变量的状态,
-
禁用:
# 通过disable禁用 (gdb) disable display num1 num2... (gdb) disable display num1-num2
-
启用:
# 通过enable启用 (gdb) enable display num1 num2... (gdb) enable display num1-num2
-
5.4 单步调试
简介:所谓单步调试,就是程序阻塞到了某个断点后,对程序进行的一些操作。
5.4.1 step
step
可以缩写为s
,命令执行一次,代码向下执行一行。如果这一行是函数调用,那么程序会进入到函数内部。
5.4.2 finish
如果step
单步进入函数内部,通过该命令可以跳出函数体。前提是该函数内没有有效的断点
。
5.4.3 next
next
可以缩写为n
。其功能与step
相似,但next
不会进入到函数的内部,而是直接将函数运行完毕。
5.4.4 until
通过until
可以跳出循环体。这需要满足以下条件:
- 循环体内不能有有效的断点。
- 必须在循环体的开始/结束部分执行该命令。
5.5 设置变量值
语法:set var 变量名=变量值
应用场景:在调试程序时,有时我们需要程序中的某个变量等于某个值,但短时间内又很难达到,这时我们就可以通过该指令修改该变量的值,从而达到目的。例如在for循环中,我们可以通过设置循环因子的值达到跳出循环的目的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现