GDB 调试与 Core Dump(段错误)排查指南(Linux/C/C++)
目录(大纲)
- [1. GDB 是什么?能解决什么问题?](#1. GDB 是什么?能解决什么问题?)
- [2. 编译准备:一定要带调试信息](#2. 编译准备:一定要带调试信息)
- [3. GDB 常用命令速查(高频)](#3. GDB 常用命令速查(高频))
- [4. 实战:用 GDB 定位段错误(SIGSEGV)](#4. 实战:用 GDB 定位段错误(SIGSEGV))
- [5. Core Dump:程序崩了也能"回放现场"](#5. Core Dump:程序崩了也能“回放现场”)
- [6. 段错误(Segmentation fault)常见原因总结](#6. 段错误(Segmentation fault)常见原因总结)
- [7. 高效排查套路(建议你照着做)](#7. 高效排查套路(建议你照着做))
- [8. 进阶小技巧(可选但很实用)](#8. 进阶小技巧(可选但很实用))
- [9. 总结](#9. 总结)
- 参考资料(可扩展阅读)
1. GDB 是什么?能解决什么问题?
GDB(GNU Debugger)是 Linux 平台最常用的程序调试器之一,主要用来:
- 断点调试:在指定代码行/函数处暂停,观察程序状态。
- 单步执行:一行一行执行,定位逻辑错误。
- 查看/修改变量:实时查看局部变量、参数、全局变量,必要时可临时修改变量值验证思路。
- 调用栈回溯 :程序崩溃后查看调用链(
bt/where),快速定位问题入口。 - Core Dump 事后分析:程序崩溃生成 core 文件后,用 GDB 还原"事故现场"。
适用场景:段错误(SIGSEGV)、非法访问、野指针、栈溢出、数组越界、use-after-free 等。
2. 编译准备:一定要带调试信息
调试/分析崩溃时,可执行文件与崩溃现场必须匹配(同版本、同编译产物)。建议:
bash
# 推荐:关闭过度优化,保留调试信息
gcc -g -O0 main.c -o app
# 或者 C++
g++ -g -O0 main.cpp -o app
-g:生成调试符号(行号、变量名、函数名)-O0/-Og:降低优化程度,避免变量被优化掉导致<value optimized out>
3. GDB 常用命令速查(高频)
3.1 启动与加载
bash
# 方式1:直接调试可执行文件
gdb ./app
# 方式2:带参数启动(也可在 gdb 内 set args)
gdb --args ./app arg1 arg2
# 方式3:调试 core 文件(事后分析)
gdb ./app /path/to/core
# 或
gdb -c /path/to/core ./app
3.2 断点/运行控制
| 命令 | 缩写 | 作用 |
|---|---|---|
break <位置> |
b |
设置断点(行号/函数名/文件:行号) |
info breakpoints |
i b |
查看断点列表 |
delete <编号> |
d |
删除断点 |
run |
r |
启动程序(命中断点会停住) |
continue |
c |
继续运行到下一个断点/结束 |
next |
n |
单步执行(不进入函数) |
step |
s |
单步执行(进入函数) |
finish |
跑完当前函数并返回到上一层 | |
until |
u |
运行到指定行/跳出循环场景常用 |
quit |
q |
退出 gdb |
常见断点位置写法:
gdb
(gdb) b main
(gdb) b 20
(gdb) b main.c:20
(gdb) b foo if x > 10
3.3 查看代码/栈/变量
| 命令 | 缩写 | 作用 |
|---|---|---|
list |
l |
显示源代码(可 l 1 从第1行) |
where / backtrace |
bt |
查看调用栈 |
bt full |
调用栈 + 每帧局部变量(core 分析很有用) | |
frame <n> |
f |
切换到第 n 层栈帧 |
up / down |
在栈帧之间上下移动 | |
print <表达式> |
p |
打印变量/表达式值 |
display <表达式> |
每次停住自动打印 | |
info locals |
打印当前栈帧所有局部变量 | |
info args |
打印当前函数参数 | |
info registers |
查看寄存器 |
3.4 内存查看(定位越界/野指针很关键)
x(examine)用于按格式查看内存:
gdb
# x/<数量><格式><单位> <地址>
(gdb) x/16xb ptr # 16字节,按16进制byte显示
(gdb) x/8xw ptr # 8个word(4字节),按16进制显示
(gdb) x/s ptr # 按字符串显示
(gdb) x/i $pc # 反汇编当前指令
4. 实战:用 GDB 定位段错误(SIGSEGV)
4.1 构造一个会崩溃的例子
main.c:
c
#include <stdio.h>
#include <stdlib.h>
int main() {
int *p = NULL;
*p = 123; // 对 NULL 解引用,触发段错误
return 0;
}
编译运行:
bash
gcc -g -O0 main.c -o app
./app
4.2 用 GDB 运行并定位
bash
gdb ./app
在 gdb 中:
gdb
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
(gdb) bt
#0 main () at main.c:7
(gdb) list
(gdb) info locals
(gdb) p p
$1 = (int *) 0x0
这类问题的定位套路:
- 先
bt看栈:崩溃点在哪个函数、哪一行。 - 再
frame/list看上下文:看崩溃行附近逻辑。 - 最后
p/info locals/info args:确认关键指针、数组下标、长度参数是否异常。
5. Core Dump:程序崩了也能"回放现场"
当程序崩溃时,系统可把当时的内存映射、寄存器、栈等信息写入 core 文件,用于事后分析。
5.1 开启 core 文件生成(Linux)
bash
# 查看当前限制(0 表示不生成)
ulimit -c
# 临时开启(当前 shell 生效)
ulimit -c unlimited
core 文件路径与命名常由内核参数控制:
bash
cat /proc/sys/kernel/core_pattern
注意:线上环境经常被 systemd 接管 core,core 可能不落在当前目录。需要结合发行版策略确认落盘位置。
5.2 使用 GDB 分析 core
假设生成了 core 文件:
bash
gdb ./app core
高频排查命令:
gdb
(gdb) bt
(gdb) bt full
(gdb) info threads
(gdb) thread apply all bt
(gdb) frame 0
(gdb) info locals
(gdb) info args
(gdb) p some_ptr
(gdb) x/32xb some_ptr
如果看到类似提示:
no debugging symbols found:二进制没有-g调试符号warning: core file may not match specified executable file:core 与可执行文件版本不匹配
6. 段错误(Segmentation fault)常见原因总结
段错误本质:访问了不允许访问的内存区域(地址不存在或受保护)。典型原因:
- 空指针/野指针解引用 :
char *p = NULL; *p = 'x'; - 已释放内存再次使用(use-after-free)
- 数组越界/指针越界:写穿栈/堆,导致随机崩溃
- 返回局部变量地址:函数返回后栈帧销毁,地址失效
- 栈溢出:递归太深、局部数组过大
- 字符串处理长度错误 :如
sprintf等导致溢出(建议snprintf)
7. 高效排查套路(建议你照着做)
7.1 现场调试(可复现崩溃)
- 先断点 :
b main或b 可疑函数 - 单步逼近 :
n/s - 观察关键数据 :
p、info locals、info args
7.2 事后分析(只有 core)
gdb ./app corebt full一步到位获取:崩溃行 + 参数 + 局部变量x/查看指针指向内存是否合理- 多线程程序:
info threads+thread apply all bt
7.3 变量被优化掉怎么办?
如果出现:<value optimized out>
- 编译时用
-O0或-Og - 确保带
-g
8. 进阶小技巧(可选但很实用)
gdb
# 让长数组/长字符串完整打印(默认可能截断)
(gdb) set print elements 0
# 打印结构体更友好
(gdb) set print pretty on
# 查看动态库与映射
(gdb) info sharedlibrary
(gdb) info proc mappings
9. 总结
- GDB = 运行时调试 + 崩溃现场还原 的核心工具。
- 带
-g、尽量-O0/-Og,能显著提升定位效率。 - 段错误优先用:
bt→list→info locals/args→p/x的套路。 - Core Dump 是线上排查的关键:可执行文件必须与 core 匹配。