GDB调试学习
GDB是命令行下调试C++程序的好工具,在Linux上跑程序就不可避免地与它打交道。
GDB调试的一般流程
程序编译时带上-g选项
1 | g++ test.cpp -o test -g |
启动gdb对可执行程序进行调试
启动gdb来调试程序有两种思路:
- 利用gdb启动程序
- 利用gdb调试运行中的程序
方式一:利用gdb启动程序
1 | gdb ./file_name |
或者先运行gdb命令,进入gdb调试程序内,然后利用file命令启动程序
1 | file ./file_name |
方式二:利用gdb调试运行中的程序
1 | gdb -p 进程号 |
或者先运行gdb命令,进入gdb调试程序内,然后利用attach命令依附到进程
1 | attach 进程号 |
设置断点等单步调试
start 程序运行至主函数main入口处暂停(相当于在main函数打了一个断点)
run 程序开始运行直到遇到一个断点,如果没有断点则一直运行至结束
break/b + 文件路:行数 在文件的第x行打断点
next/n 执行单行代码
- n 10 # 执行10行代码
[enter] 回车键执行上次运行的命令
continue/c 运行到下一个断点
list /l 显示当前附近的代码
- list file:line 查看指定文件行数附近的代码
print/p 变量 打印变量的值
backtrace/bt 查看当前的调用栈以及当前的文件和行
finish 执行完当前的函数
until 执行完当前的循环
好用的进阶知识点
gdb启动程序时传参数
很多时候,我们的程序启动需要带上命令行参数,此时可利用–args选项带上参数
1 | gdb --args ./program arg1 arg2 |
打印相关
(1)格式控制
print可以指定显示的格式,支持的变量显示格式有:
- x 按十六进制格式显示变量。
- d 按十进制格式显示变量。
- u 按十六进制格式显示无符号整型。
- o 按八进制格式显示变量。
- t 按二进制格式显示变量。
- a 按十六进制格式显示变量。
- c 按字符格式显示变量。
- f 按浮点数格式显示变量。
比如:用16进制显示(var)值: (gdb) print /x var
(2)地址/解引用
打印变量的地址 (print &var)
打印地址的数据值 (print *address)
打印数组 print *a@10 显示10个元素,无论a是double或者是int的都会正确地显示10个元素。
(3)GDB打印要点
- 在对地址做强制转换时,括号全都加:(类型)(地址)
- 后续想要对转换后的变量操作时,再加一层大括号 ,以视为整体操作:((类型)(地址))-> 成员
宏相关
- info macro+宏名 查看宏的定义
- macro expand+宏 可以展开宏的表达情况
其他
- watchpoint 监听一个变量,改变时通知
info line *<地址>获取某个地址对应的源代码行号
调试崩溃的程序
程序崩溃时会由操作系统生成core dump文件,里面包含了程序崩溃时的内存内容、寄存器状态和其他相关信息,借此我们可以调试分析程序崩溃的原因。
core文件设置
(1)默认情况下,core文件的大小可能被设置为0,这意味着系统不会为崩溃的进程生成core文件。
1 | ulimit -a # 查看core文件的大小设置 |
以上针对的是当前shell会话,如果要永久开启core文件的生成,需要编辑/etc/security/limits.conf文件(针对使用PAM的Linux系统)。在该文件中,为需要生成core文件的用户添加或修改一行配置,如* soft core unlimited,这里的*代表所有用户,也可以指定具体用户名。
(2)设置core文件的路径和名称
1 | 将core文件保存到/corefile目录下,并以core-命令名-进程号-时间戳的格式命名。 |
ore文件的生成路径和名称可以通过修改/proc/sys/kernel/core_pattern文件来设置。这个文件包含了一个字符串,用于指定core文件的命名格式和存储位置。
直接修改
/proc/sys/kernel/core_pattern文件的效果是临时的,重启后会失效。为了永久更改,可以在系统启动时通过sysctl配置或使用其他系统管理工具进行设置。
core加载调试
使用命令 gdb [exec file] [core file] 加载core 文件进行调试。
使用bt(backtrace)命令查看当前的调用栈。这将列出从程序入口到崩溃点的所有函数调用序列。
1 | (gdb) bt |
使用frame命令加上栈帧编号来切换到该栈帧。
1 | (gdb) frame 1 |
在切换到特定栈帧后,你可以使用info locals命令来查看该栈帧中的所有局部变量。此外,info args命令可以查看当前函数的参数。
1 | (gdb) info locals |
多线程调试
gdb还可以调试多线程的程序
查看可切换调试的线程:info threads
切换调试的线程:thread 线程id
只运行当前线程:set scheduler-locking on
指定某线程执行某gdb命令:thread apply 线程id gdb_cmd
运行全部的线程:set scheduler-locking off
全部的线程执行某gdb命令:thread apply all gdb_cmd
调试死锁的程序
有些时候我们的程序会死锁,这时候该怎么调试程序呢?答案是保存现场,然后慢慢进行调试。
gdb attach pid # 依附到hang的进程上
set logging on # 将gdb的会话输出保存
thread apply all bt # 打印所有线程的堆栈
generate-core-file # 输出core文件 或者
gcore 进程号保存程序产生的日志