【五、深入浅出GDB调试器】如何修复程序bug或优化代码:gdb调试器的来龙去脉与debug全方位实战详解(二)

简介: 【五、深入浅出GDB调试器】如何修复程序bug或优化代码:gdb调试器的来龙去脉与debug全方位实战详解

三、GDB实战讲解

1. GDB命令详解

在下面所有的命令标题中,括号内为命令全写,括号外为命令缩写,使用效果一样,例如运行命令 r(run),下面两种用法效果一致

(gdb)r
(gdb)run

下面的例子都是用前面编译好的文件 gdb_test.c 及可执行文件 g3。

(1)r(run)运行与start运行程序

run 运行程序,如果有断点则停在断点处,如果没有断点会一直执行到程序结束。start 会执行到main函数的起始位置,相当于在main()加一个断点,然后使用 run 执行。如果在程序调试或者执行中使用 runstart 都代表从头开始重新执行程序。

rstart 命令后面加参数可以把参数传入并执行(前面已经介绍过了)

(gdb)r para

传入参数para并执行。

start 会执行到 mian 处。

(2)q(quit)退出调试

退出 gdb 调试,回到 shell。

(3)help

查看帮助手册,按 q 退出帮助手册。

(4)l(lsit)查看代码

① 一次显示10行

② 指定一个行号n,查看 n-5 到 n+4 行(共10行)

③ 查看第 n1 到 n2 行代码 list n1,n2

④ 查看其他文件代码,用于包含多个源文件的情况,比如可执行文件 test 由 test1.c 和 test2.c 编译而成,可以通过指定文件名来查看 test1.c 或 test2.c 的源代码。

查看 test1.c 的代码1到10行

(gdb)list test1.c:1,10

(5)set 传入参数

① set 可以传入参数或者修改变量的值

② 变量名与gdb命令名冲突

比如你在源代码中有一个变量名叫 width ,如果你要用 set 设置这个变量的值会产生冲突,因为 set width 是gdb的命令,这时可以通过 set var 告诉gdb该变量是用户变量。建议自己写代码时要避免和系统函数、编译调试等命令重名的函数或变量,以避免不必要的麻烦。

(gdb)set var width=10

③ 设置命令

比如说我们在打印结构体的时候,使用 p 命令默认就是普通的打印,可能不是很美观,我们可以通过命令使打印出来的结构体更符合我们观看的习惯

(gdb)set print pretty

(6)n(next)执行下一条语句,不进入函数内部

单步执行代码,一条语句一条语句的执行,如果遇到函数不会进入函数内部,可以理解为VS的 F10 调试键。也可以在后面加数字表示执行多少行

(gdb)n num

(7)s(step)执行下一条语句,且进入函数内部

用法基本与 next 相同,区别在于 step 在遇到函数的时候会进入函数内部(像 printf 等这种库函数不会进入),可以理解为VS的 F11 调试键。

可以看到,当执行到我们自己的函数 print_array() 的时候,按 step 会进入这个函数的内部,停在这个函数内部语句的第一行。同样,step 也可以在后面加数字表示一次执行多少行。

(8)u(until)

① 跳出循环体

在遇到循环体时,如果在循环体尾部(最后一行代码)按 until 调试键,会直接执行完整个循环体,并停在循环体外。

② 跳转至某一行

(gdb)until num

直接跳至第 num 行执行并停在这一行。

③ 在其他时候,功能和 next 一样,都是单步执行。

(9)b(break)设置断点以及打断点的六种方式

断点(BreakPoint),可以让程序执行到断点处并停在这里,加断点应该是调试的时候最常用的一种方法,就像VS中的 F9 键。加断点的方式有很多种,下面将逐一介绍:

b function (直接加函数名)在某个函数 function 处添加断点

在函数 print_array() 处加断点并执行,会停在该函数内部的第一行

b num (直接加行号)在第num行添加断点

这里有一点要注意,因为程序已经启动了,如果我们要想执行到断点处,应该使用命令 c ,如果使用 runstart 会重新运行程序。

b file.c:num 在 file.c 文件的第 num 行加断点,如果不加文件名 file.c 则默认是在含main函数的那个文件第 num 行加断点。

b file.c:function 在 file.c 文件中名为 function 的函数处加断点。

b ±num 通过偏移地址设置断点,+ 表示从当前程序运行行开始,往下数 num 行并设置断点;- 表示当前程序运行行开始,往上数 num 行并设置断点。

举例,当前程序在第34行,通过 b +12可以把程序打在 34+12=16 行处。

b (上面五种方式指定断点位置) if expression 当满足表达式 expression 的时候打断点,也就是说只有当这个表达式为真的时候,这个断点才会生效。

使用举例:

(gdb)b 12 if i==2   当i==2的时候在第12行加断点
(gdb)b func if i>3  当i>3的时候在函数func处加断点

(10)tbreak

命令的格式与用法与 break 相同,但是设置的断点只生效一次,该断点使用一次后自动去除。

(11)rbreak

该命令用于给函数加断点, rbreak regex 给所有满足表达式 REGEX 的函数加断点,设置的断点和 break 设置的断点一样。这个命令在C++调试的时候,用于给所有重载函数加断点非常方便。也可以加文件名来限制为哪个文件中的所有满足表达式的函数加断点 rbreak file.c:regex

(12)disable 与 enable

用于禁用和激活断点(普通断点、捕捉点、观察点、display的变量),通过断点号来指定要禁用或激活的断点(通过 info 查看断点号),可以通过 help 手册查看用法,被 disable 禁用的断点将会暂时失效,使用 enable 激活后会再度恢复正常使用。

enable 可以激活多个断点,并且可以指点被激活的断点起作用的次数。

举个小例子

可以看到,Enb 那一栏从 yes 变成了 no。

(13)watch

设置观察点,如果在执行过程中变量发生变化,就把他打印出来,并停止运行。

这里要注意,如果你用指针(或地址)来设置观察点,一定要解引用,* 指针才是对指针所指向的变量进行观察如果不解引用,那就是对指针变量本身(地址)进行观察。另外,如果你观察一个临时变量或表达式,当它的生命周期结束的时候,对应的观察点也就失效了。

观察点有软件观察点和硬件观察点,这里不再详细介绍。

(14)rwatch

只要程序中出现读取目标变量或表达式的值的操作,程序就会停止运行。(读)

(15)awatch

只要程序中出现读取目标变量或表达式的值或者改变值的操作,程序就会停止运行。(读写)

(16)catch

(gdb)catch enevt  监控某一事件 event 的发生,当事件发生时,程序停止

这个 event 可以是下面的情况:

① C++中 throw 抛出的异常或 catch 捕捉到的异常;

② load 命令或 unload 命令,在动态库加载或卸载时程序停止执行;

③ fork、vfork、exec 系统调用时,程序停止运行;

举个例子测试一下,先准备一个C++源文件,并编译生成带调试信息的可执行文件 test。

进入调试,设置捕捉点,捕捉 string 类型的异常

(17)c(continue)执行到下一个断点处

继续执行程序,一直执行到下一个断点处。

(18)info 查看

info breakpoints 查看所有断点的信息

  • Num:断点编号
  • Type:断点类型
  • Enb:激活状态,y表示已激活,n表示未激活

info breakpoints num 查询 num 号断点的信息

info variables 查询当前全局变量信息

info watchpoints 查看观察点信息

⑤ 查看寄存器

⑥ 查看当前函数内部临时变量的值

⑦ 查看当前函数参数的值

⑧ 更多用法,请查看帮助手册

(19)del(delete)删除

如果我们使用 quit 退出调试,然后再次启动 gdb 的话,之前设置的所有类型的断点(包括观察点、捕捉点)都会消失。通过 delete 可以在当前调试中删除断点。在使用 delete 删除断点的时候,要先用 info 命令查看断点信息,在显示信息的第一列会有断点的编号,然后再根据编号删除断点即可。(删除观察点、捕捉点方法与删除断点一致)

如果直接使用 delete 命令,不加断点号的话,会删除当前所有断点。

(20)clear

删除断点,后面加行号或函数名,(delete是按照断点号删除)

(gdb)clear func   删除函数 func 处的断点
(gdb)clear num  删除第 num 行的断点

(21)ignore

忽视断点

(gdb)ignore num count   忽视编号为 num 的断点 count 次

(22)p (print)

① 打印变量的值

(gdb)p val  打印变量 val 的值
(gdb)p &val   打印变量 val 的地址

array 类型为 char ,地址每次+1增长1个字节。

② 指定打印变量值的进制,比如 /x 表示按16进制打印

进制表如下:

命令 进制
/t 二进制
/d 十进制有符号
/u 十进制无符号数
/x 十六进制
/o 八进制
/f 浮点型
/c 字符型

其实和我们在C语言中的语法是一样的。

③ 打印表达式结果

④ 修改变量的值

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
相关文章
|
4月前
|
NoSQL Linux 程序员
Linux:gdb调试器的解析+使用(超详细版)
Linux:gdb调试器的解析+使用(超详细版)
229 1
|
17天前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
|
14天前
|
NoSQL Linux 编译器
内核实验(一):使用QEMU+GDB断点调试Linux内核代码
如何配置环境并使用QEMU虚拟机结合GDB进行Linux内核代码的断点调试,包括安装QEMU、交叉编译工具链,编译内核以及通过GDB远程连接进行调试的详细步骤。
28 0
内核实验(一):使用QEMU+GDB断点调试Linux内核代码
|
15天前
|
NoSQL
技术分享:如何使用GDB调试不带调试信息的可执行程序
【8月更文挑战第27天】在软件开发和调试过程中,我们有时会遇到需要调试没有调试信息的可执行程序的情况。这可能是由于程序在编译时没有加入调试信息,或者调试信息被剥离了。然而,即使面对这样的挑战,GDB(GNU Debugger)仍然提供了一些方法和技术来帮助我们进行调试。以下将详细介绍如何使用GDB调试不带调试信息的可执行程序。
21 0
|
3月前
|
NoSQL Linux C语言
GDB:强大的GNU调试器
GDB:强大的GNU调试器
|
3月前
|
NoSQL 编译器 Linux
【Linux】--- Linux编译器-gcc/g++、调试器-gdb、项目自动化构建工具-make/Makefile 使用
【Linux】--- Linux编译器-gcc/g++、调试器-gdb、项目自动化构建工具-make/Makefile 使用
36 0
|
3月前
|
NoSQL Linux C++
Linux C/C++ gdb调试正在运行的程序
Linux C/C++ gdb调试正在运行的程序
|
4月前
|
NoSQL Java Unix
Linux:调试器 - gdb
Linux:调试器 - gdb
52 1
|
4月前
|
NoSQL Linux 程序员
Linux调试器--gdb的介绍以及使用
Linux调试器--gdb的介绍以及使用
|
4月前
|
NoSQL Linux C语言
【Linux】Linux调试器-gdb使用
【Linux】Linux调试器-gdb使用
30 0

相关实验场景

更多