程序需使用 -g 编译,否则无法查看源码和变量
如:gcc -g -O0 xxx.c -o xxx
进入gdb调试
常用的有两种进入gdb的方式
1、直接通过gdb启动程序:一般需要在给程序初始化打断点的时候使用
gdb [可执行程序名]
gdb --args [可执行程序名] // 有参则使用这个
2、在程序运行中途打断点,先过滤出需要打断点的进程id,通过 attach [进程id] 触发
ps aux | grep [运行的进程名]
gdb [可执行程序名]
(gdb) attach
Pasted image 20260211104512.png
常用命令
0)运行程序
如果是通过 gdb [可执行程序名] 方式启动的程序,可以在此时设置一些断点等操作,但无法通过 c 命令运行,必须先执行run
r:启动程序
Pasted image 20260211142935.png
1)打断点:常见有两种方式
通过函数名,或者文件名+行号
打断点到某个函数 --> 普通函数,或者成员函数
b main
b MyClass::test_fn
info b // 查看断点
d 1 // 删除断点
delete // 删除全部
disable 1 // 禁用断点
2)执行程序
只需要记住四个命令:c,n,s,u
c:继续执行程序,直到遇到断点b
n:单步执行,一行一行的执行代码
s:进入函数到函数内部
u:执行到当前函数后续代码位置,一般都用来跳出循环。当循环条件很长可以通过u直接跳出,避免浪费时间以及不小心n过头
3)查看和修改变量值
通过p可以查看,或者修改某个变量的值。(修改变量推荐用 set var,虽然效果一样)
p i
p i = 100
4)查看指针 + 一层层解引用(重点!!)
当某个类或者结构体特别复杂,比如有很多指针无法简单通过p命令查看内部的值,可以通过内存地址一层层解引用,下面用实例演示:
Pasted image 20260211115337.png
1、通过b打断点
2、通过c继续执行
3、程序停止并触发断点
4、通过 Ctrl + X+ A 可视化调试 (后面会讲)
5、通过p打印 aInviteMsg 类可以看到有特别多类嵌套在内部,嵌套的类内部还有很多层
总结:有些类会嵌套特别多层,此时只通过简单的p无法看到具体内容
p *(int *) 0x7fffffffdabc
或者
p *(MyClass *) 0x7fffffffdabc
使用对应的类型强制类型转换。(具体的原理以后有时间再出一个类内部的内存分布的文章)
当打印的还是如下图一样有内存地址,还是可以通过上面的方法继续强转并接引用来打印
Pasted image 20260211115610.png
5) 查看和切换堆栈
使用 bt 和 f 命令:
由于函数的调用是一层一层的,会将上一层函数压入堆栈,因此可以使用bt命令通过打印出堆栈的函数,来看当前的函数是由谁调用的。打印后可以通过f加前面序列号的方式来切换到上层指定的函数
bt:打印堆栈信息
f:切换堆栈,可以通过后面介绍的代码可视化查看代码进行调试
6)自动监控变量变化
当指定的变量发生值变动时,会自动触发断点。如i原本 == 1,当变量值发生变化时触发断点
watch i
代码可视化gdb窗口
此时程序已经暂停,进入到了调试窗口。大多是时候都不会直接这个时候断点,而是先调用出代码可视化的界面,方便后续操作,下面介绍两种方式,可以灵活调整。
原理:GDB需要能找到源码路径后,才能可视化看到代码。由于程序的编译路径已经固定,我们无法做调整,如果路径不同,需要用 substitute-path 映射
Pasted image 20260211104638.png
查看代码的编译路径,通过info source命令
Pasted image 20260211111530.png
1、通过下面的命令来修改gdb搜索代码的路径。
set substitute-path [编译路径] [运行环境的源代码路径]
[编译路径]:通过info source命令查的
[运行环境的源代码路径]:当前正在运行环境的代码,可以跟编译路径不同
调整完成后,可以通过快捷键 ctrl + A + X 展示代码,如下图所示:
Pasted image 20260211142321.png
2)软连接代码路径
当编译和运行的不是同一台机器的时候,会遇到每次都要执行set substitute-path的问题,下面分享一个小技巧可以不需要每次都执行set substitute-path,而是可以直接进入gdb就能通过Ctrl + X +A可视化代码
1、通过前面介绍的 info source 查看编译路径;假如为 /home/myCode/project
2、当前机器的代码存放在 /home/myTestCode/project
3、先创建跟编译环境一样的空文件夹 /home/myCode
4、通过软连接将 myTestCode 连接到 myCode
5、此时就可以通过 /home/myCode/project 路径找到原来 /home/myTestCode/project 的代码
cd /home
ln -sfn -T myTestCode myCode
Pasted image 20260211160340.png