GDB 调试 vmcore 完整教程(Rocky Linux 篇)
一、概述
vmcore 是 Linux 内核崩溃时生成的内存转储文件,记录了崩溃瞬间系统的完整状态,包括 CPU 寄存器、内存数据、进程信息、内核数据结构等。通过分析 vmcore,可以定位内核崩溃的根本原因,如空指针解引用、内存越界、死锁、驱动 Bug 等。
本教程以 Rocky Linux 9 为例,涵盖从环境安装到实战调试的完整流程。
二、环境准备:安装所需工具和包
2.1 准备工作:安装 dnf 插件
dnf config-manager 命令需要 dnf-plugins-core 包支持:
dnf install -y dnf-plugins-core
2.2 启用 debuginfo 仓库
Rocky Linux 9 的调试符号包位于独立的 debug 仓库中,仓库名格式为 <组件>-debuginfo(如 baseos-debuginfo、appstream-debuginfo):
# 查看所有仓库,确认 debug 仓库名称
dnf repolist all | grep -i debuginfo
# 启用 BaseOS 的 debug 仓库
dnf config-manager --set-enabled baseos-debuginfo
或者直接使用 debuginfo-install 工具(会自动处理仓库配置):
# debuginfo-install 属于 yum-utils(dnf-utils 为其兼容别名)
dnf install -y yum-utils
# 正确用法:指定 kernel-core 或 kernel-debuginfo
dnf debuginfo-install kernel-core-$(uname -r)
# 或
debuginfo-install kernel-debuginfo-$(uname -r)
2.3 安装核心调试工具
# 一键安装所有必要包
dnf install -y crash gdb kernel-debuginfo-$(uname -r) \
kernel-devel kernel-headers kexec-tools \
elfutils binutils dnf-plugins-core yum-utils
各包的作用说明:
| 包名 | 作用 |
|---|---|
| crash | 专为 vmcore 分析设计的工具,提供比 gdb 更丰富的内核专用命令 |
| gdb | GNU 调试器,也可直接调试 vmcore |
| kernel-debuginfo | 关键包,提供内核调试符号,没有它无法解析函数名和变量 |
| kernel-devel / kernel-headers | 内核头文件,用于符号解析 |
| kexec-tools | 配置 kdump 服务,用于自动捕获 vmcore。内含 makedumpfile |
| elfutils / binutils | ELF 文件分析和二进制工具集 |
关于 makedumpfile:
makedumpfile包含在kexec-tools中。安装kexec-tools后即可使用:dnf install kexec-tools which makedumpfile # /sbin/makedumpfile
2.4 验证安装
# 检查工具是否可用
crash --version
gdb --version
makedumpfile -v
# 检查调试符号文件是否存在
ls -la /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
# 如果 vmlinux 是 .gz 压缩格式,需要解压
ls -la /usr/lib/debug/lib/modules/$(uname -r)/vmlinux.gz
gunzip /usr/lib/debug/lib/modules/$(uname -r)/vmlinux.gz
2.5 如果找不到匹配内核版本的 debuginfo 包
# 查看当前内核版本
uname -r
# 列出可用版本
dnf list available kernel-debuginfo
# 手动下载安装
wget https://dl.rockylinux.org/vault/rocky/9.4/debug/x86_64/os/Packages/k/kernel-debuginfo-$(uname -r).rpm
dnf install ./kernel-debuginfo-$(uname -r).rpm
三、配置 kdump 自动捕获 vmcore
3.1 安装并启动 kdump
systemctl enable kdump
systemctl start kdump
3.2 配置 crashkernel 内存预留
注意:在 Rocky Linux 9 中,
crashkernel=auto已被新机制替代(虽仍可用但已 deprecated)。推荐使用kdumpctl get-default-crashkernel获取系统推荐的值。
# 查看推荐的 crashkernel 值
# RL9 返回示例:1G-2G:192M,2G-64G:256M,64G-:512M
kdumpctl get-default-crashkernel
# 使用推荐值设置(替换为实际返回值)
grubby --args="crashkernel=1G-2G:192M,2G-64G:256M,64G-:512M" --update-kernel=ALL
# 重启使配置生效
reboot
# 验证 kdump 状态
systemctl status kdump
kdumpctl status
3.3 kdump 配置文件
# 核心配置文件
/etc/kdump.conf
# 常用配置项示例
cat /etc/kdump.conf
path /var/crash # vmcore 保存路径
core_collector makedumpfile -l --message-level 7 -d 31 # 压缩级别(官方默认 level 7)
default reboot # 崩溃后默认动作
# 重启 kdump 使配置生效
systemctl restart kdump
3.4 vmcore 默认存放路径
# kdump 生成的 vmcore 默认存放在
/var/crash/
# 示例路径
/var/crash/127.0.0.1-2025-01-01-10:00:00/vmcore
四、启动调试
4.1 调试前检查:确认版本匹配
这是最常见的坑——vmlinux 必须与 vmcore 来自同一内核版本。
# 查看 vmcore 的内核版本
strings /var/crash/.../vmcore | grep "Linux version" | head -1
# 查看当前系统的内核版本
uname -r
# 确认 vmcore 文件格式
file /var/crash/.../vmcore
# 正常输出:vmcore: ELF 64-bit LSB core file, x86-64, version 1 (SYSV)
4.2 使用 crash 工具(推荐)
crash /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/.../vmcore
4.3 使用 gdb 调试
gdb /usr/lib/debug/lib/modules/$(uname -r)/vmlinux /var/crash/.../vmcore
五、常用调试命令详解
5.1 查看崩溃现场
# 查看当前 CPU 的堆栈回溯(最常用,第一步)
bt
# 查看所有 CPU 的堆栈
bt -a
# 带函数参数的详细回溯
bt -f
# 读取指定地址的内存内容
rd <address> # 读取内存(kernel virtual / user virtual / physical)
# 寄存器信息在 bt 输出中自动显示([exception RIP: ...] 下方)
# 如需查看所有寄存器,可使用 gdb 模块
gdb info registers
5.2 查看系统基本信息
# 查看所有进程
ps
ps -p <pid> # 查看指定进程
# 查看系统信息
sys # 系统基本信息
mach # 机器架构信息
cpu # CPU 详细信息
# 查看内核日志(dmesg 内容)
log
log | tail -50 # 只看最后 50 行
5.3 内存与变量查看
# 查看全局变量
p <variable_name>
p (struct task_struct *)current
# 查看指定地址的内存
x/10gx <address> # 以 16 进制显示 10 个 8 字节
x/20wx <address> # 以 16 进制显示 20 个 4 字节
5.4 模块与驱动分析
# 查看已加载的内核模块
mod
# 查看模块的符号信息
mod -s <module_name>
# 查看模块的代码段和数据段地址
mod -S <module_name>
5.5 锁与同步分析
# 查看锁状态
lock # 所有锁信息
# 查看等待队列
waitq # 等待队列信息
# 查看阻塞任务
foreach UN bt
5.6 内存管理分析
# 查看内存使用概况
kmem -i
# 查看 slab 分配器信息
kmem -s <slab_name>
# 查看虚拟内存
vm
# 查看页表
pte <address>
# 查看交换空间
swap
5.7 文件系统和网络
# 查看打开的文件
files
# 查看挂载信息
mount
# 查看网络连接
net
net -s <socket> # 指定 socket 详情
5.8 进程与运行队列
# 查看运行队列中的进程
runq
# 查看指定 CPU 的运行队列
runq -c <cpu_id>
# 对所有进程执行命令(非常实用)
foreach bt # 查看所有进程的堆栈
foreach files # 查看所有进程打开的文件
foreach -p <pid> bt # 查看指定进程的所有线程堆栈
5.9 结构体查看
# 方式 1:crash 原生命令
struct task_struct <address>
whatis struct task_struct
# 方式 2:通过 gdb 模块执行
gdb ptype struct task_struct
# 查看结构体成员的值
p ((struct task_struct *)0xffff...)->comm
p ((struct task_struct *)0xffff...)->pid
5.10 反汇编
# 反汇编指定函数(crash 命令为 dis,不是 disasm)
dis <function_name>
# 反汇编指定地址
dis <address>
# 反汇编指定数量的指令
dis <function_name> 20
5.11 上下文切换
# 查看当前上下文
set
# 切换到指定进程的上下文
set <pid>
set -p <pid>
5.12 帮助命令
# 查看所有可用命令
help
# 查看某个命令的详细用法
help bt
help ps
help kmem
六、实战:快速定位崩溃原因(三板斧)
当拿到一个 vmcore 时,按以下步骤快速定位问题:
第一板斧:看堆栈
crash vmlinux vmcore
bt
通过堆栈回溯,可以看到崩溃时 CPU 正在执行什么函数、调用链是什么。这是最重要的第一步。
第二板斧:看日志
log | tail -100
内核崩溃前通常会打印关键日志,如 Oops 信息、异常类型(NULL pointer dereference、page fault 等)、异常指令地址等。
第三板斧:看所有 CPU 状态
bt -a
多核系统中,可能只有一个 CPU 触发了崩溃,但其他 CPU 的状态也能提供线索。
分析示例
假设看到如下堆栈:
crash> bt
PID: 12345 TASK: ffff88003e2c0000 CPU: 2 COMMAND: "kworker/2:1"
#0 [ffff88003e2c3d50] machine_kexec at ffffffff8105a1be
#1 [ffff88003e2c3db0] crash_kexec at ffffffff8105b1c2
#2 [ffff88003e2c3e80] oops_end at ffffffff8103a0b8
#3 [ffff88003e2c3ea0] no_context at ffffffff8103a1b3
#4 [ffff88003e2c3ef0] __bad_area_nosemaphore at ffffffff8103a2e9
#5 [ffff88003e2c3f38] bad_area_nosemaphore at ffffffff8103a3ae
#6 [ffff88003e2c3f48] __do_page_fault at ffffffff8105c1df
#7 [ffff88003e2c3f80] do_page_fault at ffffffff8105c2de
#8 [ffff88003e2c3fb0] page_fault at ffffffff8153e2e5
[exception RIP: my_driver_ioctl+0x25]
RIP: ffffffffa03b1025 RSP: ffff88003e2c4068 RFLAGS: 00010246
RAX: 0000000000000000 RBX: ffff88003d8f6000 RCX: 0000000000000000
RDX: 0000000000000000 RSI: 000000000000c040 RDI: 0000000000000000
可以判断:
- 崩溃发生在 my_driver_ioctl 驱动的 ioctl 函数中
- 异常类型是 page fault(缺页异常)
- RAX = 0,说明可能是空指针解引用
- 结合
log查看 Oops 信息确认
七、高级技巧
7.1 使用 gdb 脚本批量调试
# 创建 gdb 脚本
cat > debug.gdb << 'EOF'
set pagination off
bt
info registers
thread apply all bt
quit
EOF
# 运行脚本
gdb -x debug.gdb vmlinux vmcore
7.2 gdb 中查看多线程
# 查看所有线程
info threads
# 切换线程
thread <thread_id>
# 查看所有线程的堆栈
thread apply all bt
7.3 搜索特定值
# 在内存中搜索特定值
search <value>
# 查看特定类型的对象,应使用 struct 或 p 命令
struct task_struct <address>
p ((struct task_struct *)<address>)->comm
7.4 查看定时器和中断
# 查看内核定时器
timer
# 查看中断信息
irq
7.5 压缩大内存 vmcore
对于大内存系统生成的 vmcore,直接加载可能因内存不足而失败:
# 使用 makedumpfile 压缩(makedumpfile 已随 kexec-tools 安装)
makedumpfile -d 31 -l vmcore vmcore.small
# 然后用压缩后的文件调试
crash vmlinux vmcore.small
7.6 检查符号加载状态
# 在 crash 中检查符号是否加载成功
sym -l
# 如果符号未加载,使用 -d 1 开启调试输出重新启动
crash -d 1 vmlinux vmcore
八、常见问题排查
8.1 "vmlinux: No such file or directory"
原因:未安装 kernel-debuginfo 包或版本不匹配。
解决:
dnf install kernel-debuginfo-$(uname -r) kernel-debuginfo-common-$(uname -m)-$(uname -r)
8.2 "crash: cannot resolve symbol"
原因:vmlinux 与 vmcore 的内核版本不一致。
解决:确保使用与 vmcore 相同内核版本的 vmlinux 文件。
# 检查 vmcore 版本
strings vmcore | grep "Linux version" | head -1
# 检查 vmlinux 版本(更可靠的方式)
file /usr/lib/debug/lib/modules/$(uname -r)/vmlinux
strings /usr/lib/debug/lib/modules/$(uname -r)/vmlinux | grep "Linux version"
8.3 堆栈显示不全
原因:堆栈被破坏或优化级别过高。
解决:尝试 bt -f 查看带参数的堆栈,或使用 bt -a 查看所有 CPU。
8.4 kdump 无法捕获 vmcore
解决:
# 检查 crashkernel 是否已预留
cat /proc/cmdline | grep crashkernel
# 检查 kdump 服务状态
systemctl status kdump
kdumpctl status
# 手动测试 kdump
echo c > /proc/sysrq-trigger
8.5 vmcore 文件格式异常
# 确认是 ELF core file
file vmcore
# 如果显示 "data" 而非 "ELF core file",说明文件损坏或不是标准 vmcore
九、命令速查表
| 用途 | 命令 | 说明 |
|---|---|---|
| 启动调试 | crash vmlinux vmcore |
最常用 |
| 查看堆栈 | bt / bt -a / bt -f |
单CPU/所有CPU/带参数 |
| 读取内存 | rd <address> |
读取内存内容 |
| 查看日志 | log |
内核 dmesg 日志 |
| 查看进程 | ps / ps -p <pid> |
进程列表/指定进程 |
| 查看模块 | mod / mod -s <name> |
模块列表/符号信息 |
| 查看变量 | p <variable> |
打印变量值 |
| 查看内存 | x/10gx <addr> |
16进制查看内存 |
| 查看结构体 | struct <type> <addr> / whatis |
结构体定义/类型 |
| 反汇编 | dis <func> |
反汇编函数 |
| 查看锁 | lock / foreach UN bt |
锁状态/阻塞任务 |
| 查看内存 | kmem -i / kmem -s |
内存使用/slab信息 |
| 查看运行队列 | runq |
就绪队列 |
| 遍历进程 | foreach bt |
所有进程堆栈 |
| 切换上下文 | set <pid> |
切换进程上下文 |
| 查看符号 | sym -l |
符号加载状态 |
| 压缩 vmcore | makedumpfile -d 31 -l |
压缩大内存 vmcore |
| 帮助 | help <cmd> |
命令帮助 |
十、总结
| 步骤 | 操作 | 关键命令 |
|---|---|---|
| 1 | 安装工具 | dnf install crash gdb kernel-debuginfo-$(uname -r) kexec-tools |
| 2 | 配置 kdump | kdumpctl get-default-crashkernel -> grubby --args="crashkernel=..." --update-kernel=ALL |
| 3 | 确认版本匹配 | `strings vmcore |
| 4 | 启动调试 | crash vmlinux vmcore |
| 5 | 看堆栈 | bt |
| 6 | 看日志 | `log |
| 7 | 查变量 | p <variable> |
| 8 | 查内存 | x/10gx <address> |
| 9 | 查结构体 | struct task_struct <address> |
核心要点:
- crash 是分析 vmcore 的首选工具,命令比 gdb 更丰富、更专一
- kernel-debuginfo 必须安装且版本必须与内核完全匹配,同时需安装
kernel-debuginfo-common-$(uname -m) - bt 是最常用的命令,先看堆栈再逐步深入
- 结合 bt + log + 寄存器 三板斧,大多数问题可以快速定位
- makedumpfile 不是独立包,它包含在
kexec-tools中,安装kexec-tools后即可使用 - Rocky Linux 9 的 debug 仓库名格式为
baseos-debuginfo,启用前需先安装dnf-plugins-core - Rocky Linux 9 中
crashkernel=auto已 deprecated,推荐使用kdumpctl get-default-crashkernel获取推荐值

浙公网安备 33010602011771号