AIGC标识 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-debuginfoappstream-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 文件分析和二进制工具集

关于 makedumpfilemakedumpfile包含在 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 获取推荐值
posted @ 2026-08-13 18:35  LoftyAmbition  阅读(16)  评论(0)    收藏  举报