Linux基础开发工具——gdb/cgdb(7)
文章目录
- 前言
- 一、生成可调试文件
- 二、调试打开与关闭
- 启动调试
- l 查看代码
- 退出调试
- 运行与断点
- 单行与单步
- 三、查看变量
- bt 查看调用堆栈
- p 临时查看变量
- display 常显示变量
- 四、快速跳转
- until 指定行
- finish 函数
- c 断点
- 五、其他指令
- disable 断点使能
- set var 设置条件
- ptype 查看变量类型
- 六、cgdb
- watch 监视变量
- 条件断点
- 总结
前言
Linux学习继续,话说这个操作系统的名字看起来就很酷!
vim 可以编写代码,gcc/g++ 可以编译代码,此时只最后一件神器,就能进行完整的开发工作,那就是通过 gdb 调试代码,毕竟谁都不敢保证自己的代码没有问题,所以就有调试器这种东西帮助我们定位问题,进而解决问题
一、生成可调试文件
在正式开始进入gdb调试环节前,我们先来检查一下是否下载好了gdb
# 用来检查
$ gdb --version
# 用来下载
$ sudo yum install -y gdb
可是当我们用gdb进入编译好的可执行文件后,会发现什么指令都用不了,除了q退出gdb程序或者r运行程序
原因很简单:gcc/g++ 默认生成的程序为 release 发行版,也就是说不含调试信息,所以我们首先要解决这个问题
release 与 debug
程序分为 release 与 debug 两个版本,其中前者是给测试工程师找毛病的,而后者则是我们开发使用的版本,debug 内置很多调试信息,因此它能很好的进行调试
而 gcc/g++ 默认不会生成 debug 版的可执行程序,我们可以通过指令来搜索默认生成的程序中是否含有调试信息
想要解决问题也很简单:在编译时,指定编译器生成 debug 版的程序就行了,又因为我们之前已经学习了Makefile,于是这样改就可以了
接下来就可以愉快的进入 gdb 进行调试了
二、调试打开与关闭
启动调试
我们调试的对象是已经生成的可执行程序,并非最开始的源文件
这很好理解,因为在VS中也是先编译、再调试
通过 gdb + 可执行文件名字 指令可以开始调试
l 查看代码
只要进入了 gdb ,我们可以通过 l 指令随时随地查看我们的代码,且查看代码时不会干扰其他调试命令
l 命令一般是配合数字进行查看,每次只可查看十行,如 l 1 就表示从代码第一行开始查看其前后十行,按回车后可接着往下展示,直到代码展示完毕
(gdb) l 1 //从代码第一行开始查看其前后十行
(gdb) l //默认查看代码最中间的十行内容
退出调试
gdb 退出不像 vim 那样麻烦,指令 q 就表示退出 gdb 调试
(gdb) q //退出 gdb 调试
运行与断点
调试最重要的目的是帮助我们快速定位到问题,然后分析解决,此时断点就显得很重要了,如果没有断点,那只能一步步的调试,效率很低,下面就来看看如何让程序在 gdb 中跑起来及断点相关操作
r 运行程序
gdb 中能直接快速运行程序,假设没有断点,那么程序会直接运行出结果
(gdb) r //运行程序
其实此时可以直接把这个看作VS中的黑框框,r 就相当于 F5 ,在没有断点的情况下,程序会直接出结果的,而最终的结果值也会紧跟着输出
b 断点操作
断点在 gdb 中意为 breakpoint ,其中首字母 b 就表示断点的意思,因为是纯命令行操作,所以刚开始调试麻烦点是必然的
(gdb) b 行号 //在指定行号打断点
(gdb) b 函数名 //在指定函数处打断点
注意: 纯命令打的断点不如图形化界面直观,但我们也可以通过指令查看断点信息
查看断点信息
(gdb) info b //查看所有断点信息
编号含义
查看断点信息时,会发现有一栏 num ,这表示每个断点的编号,因为我们不能直接对断点进行区分,于是就需要引入编号这个概念,这个概念在 gdb 很多地方都有体现
注意: 除非 gdb 关闭,否则它的编号是一直累计的,比如我们把断点1、2都删了,然后再新打一个断点,断点编号就为3
取消断点
有时候想取消断点,就可以通过 d 断点编号,取消指定断点
(gdb) d 断点编号 //由此可见断点编号的重要性
有了断点之后,我们就可以配合 r 指令,运行至断点处
注意: 不同于VS中的 F5,r 指令要么运行至最近一个断点处,要么将程序运行完,也就是说,r 是无法实现两个断点间移动的,再次按 r 会提示是否重新运行程序
单行与单步
调试这个东西总得一步一步来,不然问题就不好找到了
n 单行调试
单行调试即逐过程调试,对应着VS中的 F10,即遇到函数不会进入,指令为 n
(gdb) n //单行调试,不会进入函数内部
单行:一行一行的来,每次运行完一行内容即可
s 单步调试
单步调试对应着VS中的 F11 ,不同于单行调试,单步调试能进入函数内部,指令为 s
(gdb) s //单步运行,会进入函数内部
单步:即一步一步的来,如果遇到函数,就会进入函数内部,确保程序的每一步都被执行
三、查看变量
调试过程中还有一个很重要的工作:查看变量信息,如VS中的监视窗口,假设没有监视功能,那么我们可能连变量的变化情况都无法捕捉到,庆幸的是 gdb 支持监视功能
bt 查看调用堆栈
程序运行时,会先为 main 函数建立栈帧,然后运行程序,如果遇到函数,就会为函数建立栈帧,执行函数,因此程序的运行本质上就是栈帧的创建与销毁
我们可以通过指令 bt 查看当前程序的堆栈调用情况
p 临时查看变量
指令 p 变量 可以查看指定变量的信息
(gdb) p 变量 //查看变量的信息
注意: 指令 p 只能做到临时监视,当执行下一条指令后,原来监视的变量就看不到了;可以看出,p 监视出的值也是有编号的,每调用一次指令,编号就会累加一次
display 常显示变量
gdb 当然也支持一直监视变量,使用指令 display 即可
(gdb) display 变量 //常显示变量信息,不会随着指令的执行而消失
注意: 如果我们忘记了程序中有哪些变量,可以随时随地通过 l 指令查看,像这种查看式的指令,是不会影响其他指令运行的;不难发现,常显示的变量也有属于自己的编号,这个编号运行机制跟断点的一样,只要 gdb 不退出,它是会一直累加的
编号存在的主要意义就是方便我们进行监视变量删除:
(gdb) undisplay 变量编号 //取消监视指定变量
四、快速跳转
gdb 提供了一些快速跳转的指令,赋予了我们在不打断点的情况下进行跳转的权力(注:先要打断点将程序运行起来),这是VS做不到的
until 指定行
程序运行后,我们可以直接通过 until 行号 的方式跳转至指定行,这个指令通常用来跳过循环
(gdb) until 行号 //跳转至指定行
finish 函数
这个指令主要是针对函数的,直接 finish 就可以在不打断点的情况下,跑完当前函数
(gdb) finish //在不打断点的情况下跑完当前函数
c 断点
这个指令就是针对断点的了,前面说过 r 无法实现两个断点间的跳转,因此有一个专门的命令 c 进行断点跳转(注:依然需要先通过 r 指令把程序跑起来)
(gdb) c //进行断点间的跳转
五、其他指令
disable 断点使能
使能 的意思就是开关,比如电灯的开与关,我们的断点也能设置开关状态,在不取消断点的情况下让断点失效
(gdb) disable 断点编号 //关闭断点
能关闭当然也能打开
(gdb) enable 断点编号 //打开断点
set var 设置条件
这可厉害了,可以在调试的时候直接将变量改为想要的值
ptype 查看变量类型
六、cgdb
你不得不说,gdb的调试真的麻烦,所以我们需要可以安装这个更棒的调试工具——cgdb!!!
# 下载命令
$ Ubuntu: sudo apt-get install -y cgdb
$ Centos: sudo yum install -y cgdb
一样的用 cgdb + 可执行文件名字 进入调试
这样是不是就好很多了,嘻嘻,谁用谁知道!!!
watch 监视变量
执⾏时监视⼀个表达式(如变量)的值,如果监视的表达式在程序运⾏期间的值发⽣变化,GDB 会暂停程序的执⾏,并通知使⽤者
如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如果变化了,就会通知你
条件断点
- 条件断点添加常⻅两种⽅式:1. 新增 2. 给已有断点追加
- 注意两者的语法有区别,不要写错了
- 新增: b ⾏号/⽂件名:⾏号/函数名 if i == 30(条件)
- 给已有断点追加:condition 2 i==30, 其中2是已有断点编号,没有if
总结
gdb 是一款功能丰富的调试器,它赋予了我们在纯命令行环境下调试代码的能力,虽然它的使用门槛高,但用熟后就会很顺手,配合我们之前学习过的 vim、gcc ,能做到像VS那样的开发环境,让我们的 Linux 使用场景更加丰富