《告别Bug!GDB/CGDB调试实战指南》
坚持用 清晰易懂的图解 + 代码语言,让每个知识点变得简单!
🚀呆头个人主页详情
🌱 呆头个人Gitee代码仓库
📌 呆头详细专栏系列
座右铭: “不患无位,患所以立。”
《\"告别Bug!GDB/CGDB调试实战指南\"》
- 前言
- 目录
- 一、debug和release
- 二、环境准备
- 三、gdb/cgdb的使用
前言
🚀 欢迎来到《Linux系统实战》!
这里是命令行到内核的跃迁基地,也是你从\"rm -rf恐惧症\"到\"权限管理大师\"的修炼场。
🔍 专栏特色:
- 图解+实战:用最直观的方式拆解Linux核心机制
- 从应用到底层:覆盖Shell脚本、系统调优、内核模块开发
- 真实场景:每篇附服务器运维/开发中的实际问题解决方案
💡 学习建议:
1️⃣ 先动手尝试(搞崩了也没关系)
2️⃣ 对照文章分析原理
3️⃣ 用文末【实战任务】巩固技能
📌 Linux经典名言:
“Linux不是背出来的,是在一次次Permission denied
中练出来的!”
(正文开始👇)
目录
一、debug和release
程序的发布方式有两种, debug 模式和 release 模式。
- 在linux中gcc/g++ 出来的⼆进制程序,编译器默认生成的可执行程序(二进制程序)是release发布版本,即追求性能的版本
- 如果想要生成debug版本的可执行程序,就需要我们在使用gcc/g++工具将源文件进行编译链接生成二进制程序的时候进行手动指定选项-g添加debug信息,使我们的可执行程序可以被执行追踪
- 通常debug版本由于添加了debug调试信息,通常都是比默认生成的release版本的所占用的空间大
- gcc 要进行编译的文件 -o 可执行文件名 -g
我们可以发现动态链接的debug版本占用空间略大于release,静态链接也是如此
readelf -S 可执行文件名
可执行文件形成的时候不是无序的,而是有独特的格式,可执行程序有自己的二进制格式(elf格式),我们可以使用readelf -S 可执行文件名,来进行查看可执行文件的二进制格式
同时我们还可以借助管道,使用grep对二进制格式中的信息进行搜索,其中-i选项是忽略大小写进行查找,由于test是release是发行版本,没有添加debug信息,所以无法查找到debug,test_debug是debug版本,添加了debug信息,所以可以查找到debug
二、环境准备
- 编写要进行调试的代码,创建一个test.c的文件用于编写如下代码,这里编写了一个求x减到1的总和函数用于进行我们的调试
#include int sum(int s, int e) { int result = 0; for(int i = e; i >= s; i--) { result += i; } return result;}int main() { int start = 1, end = 100; printf(\"I will begin!\\n\"); int n = sum(start, end); printf(\"running done, result is: [%d~%d]=%d\\n\", start, end, n); return 0;}
- 创建自动化构建工具的文件makefile,并写入指令,这里特别注意由于我们想要生成的是可调试的debug版本的可执行程序,那么要在我们使用gcc命令后面加入-g选项,底行模式wq保存退出之后,在命令行运行make指令生成debug版本的二进制程序test
test:test.cgcc test.c -o test -gclean:rm -f test
- 检查,当你的程序使用readelf -S 文件名 | grep debug查找到如下debug信息代表你生成dubug版本的二进制程序成功,该程序可以使用gdb进行调试
三、gdb/cgdb的使用
🛸GDB 与 CGDB 对比
list
查看代码next
、print
)F2
切换窗口)break
或 list
跳转🛸GDB 调试命令速查表
list
/ l
list
/ l 10
list
/ l 函数名
list main
list
/ l 文件名:行号
list mycmd.c:1
run
/ r
run
next
/ n
next
step
/ s
step
break
/ b [文件名:]行号
break 10
/ break test.c:10
break
/ b 函数名
break main
info break
/ info b
info break
finish
finish
print
/ p 表达式
print start+end
print
/ p 变量
p x
set var 变量=值
set var i=10
continue
/ c
continue
delete breakpoints
/ d breakpoints
delete breakpoints
delete breakpoints n
/ d breakpoints n
delete breakpoints 1
disable breakpoints
disable breakpoints
enable breakpoints
enable breakpoints
info breakpoints
/ i breakpoints
info breakpoints
display 变量名
display x
undisplay 编号
undisplay 1
until 行号
until 20
backtrace
/ bt
backtrace
info locals
/ i locals
info locals
quit
quit
以下是整理成 CSDN Markdown 表格格式 的 CGDB 调试命令速查表,包含 基础 GDB 命令 和 CGDB 专属快捷键,方便快速查阅:
🛸CGDB 调试命令速查表
break
/ b
b main
(在 main
函数断点)run
/ r
run
next
/ n
next
step
/ s
step
print
/ p
p x
continue
/ c
continue
quit
/ q
quit
Esc
Esc
后可用方向键浏览代码i
i
后输入 GDB 命令空格
空格
/
+ 文本/sum
(搜索 sum
函数):
+ 行号:20
(跳转到第 20 行)F2
F6
空格
info break
/ i b
info break
delete
/ d
d 1
(删除断点编号 1)display
display x
(每次暂停时打印 x
)🛸启动gdb
sudo yum install -y gdb #gdb下载sudo yum install -y epel-release # 启用EPEL仓库sudo yum install -y cgdb # 安装CGDB
gdb 可执行文件名,观察到如下场景即为我们的gdb调试器启动成功,那么接下来我们将在(gdb)命令行中输入调试命令进行我们程序的调试。
q,退出gdb
🛸查看行号对应x行代码
- l n,n代表你所要查看行号,即可显示该行号对应代码,如果输入的行号为1,那么会从第1行开始显示共计10行代码,如果该行号为一个中间的行号,那么会居中显示该行号,并且显示该行号上5行,下5行,共计10行代码
- gdb会默认记住命令,如果你输入命令后,下一次输入默认继续执行上一次的命令,并且如果你查看的是行号会继续从上一次查看的最后一行的位置继续向下查看
🛸运行程序
r ,可以运行程序,如果没有设置断点,那么默认执行到程序的最后,如果设置了断点,那么默认运行到第一个断点的位置
🛸查看函数代码
l 函数名
🛸断点
- b n,n代表行号,在某一行设置断点
- b 函数名,在函数的开头设置断点
- b 源文件: n,在源文件行号为n的位置处设置断点
🍑查看断点
如果你想查询断点信息 info b
🍑删除断点
d n,这个n代表断点的序号,例如下图中的Nnm那一行代表的序号
d breakpoints,可以删除所有断点
🍑禁用,启用断点
- disable n,这个n代表断点的序号,禁用断点
- enable n,这个n代表断点的序号,启动断点
🛸调试
- 这里的调试过程一定是r先运行起来才能进行逐过程调试或逐语句调试
- n,逐过程调试,类似于vs中的F10,不会进入函数
- s,逐语句调试,类似于vs中的F11,会进入函数
🍑逐过程 、 语句调试
🛸打印值
p 变量,调试运行起来,可以进行打印变量对应的值(这里的作用仅仅是打印,无其它作用)
🛸跟踪、取消变量
display 变量名,跟踪查看一个变量,每次停下来都显示它的值,类似于vs中的监视窗口监视变量的作用
undisplay,可以取消对所有变量的追踪(这里不可以单独取消对某个变量的追踪,要取消只能统一取消对全部变量的追踪)
🛸设置局部变量值
set var 变量名=你要给变量设置的值,比如循环前50次没有问题,你想快速跳转至第51次有没有问题,可以使用修改循环变量的值达到快速跳转进行调试
🛸跳到下一个断点
c,跳转到下一个断点
🛸跳到指定行
until n,n代表行号,跳转到指向行n
🛸函数调用堆栈
bt,查看函数调用堆栈(即各级函数调用及其参数)