kernel/debug 内核调试机制与原理详解
源码路径 :rk3588/kernel-6.1/kernel/debug/内核版本 :Linux 6.1(RK3588 平台)平台 :RK3588(4×Cortex-A76 + 4×Cortex-A55 big.LITTLE,ARM64)
该目录实现 Linux 内核 KGDB(Kernel GDB) 调试子系统的核心代码。KGDB 允许通过串口或网络,使用主机端 GDB 远程调试运行中的内核;可选的 KDB 前端提供内核内置命令行调试器。二者共享 debug_core.c 提供的异常处理、断点管理、SMP 协调等基础设施。
说明 :架构相关代码(断点指令、寄存器读写、异常向量)位于 arch/arm64/kernel/kgdb.c 等目录;串口 I/O 模块位于 drivers/tty/serial/kgdboc.c 等;本目录是调试框架的通用核心 。
目录
一、源码目录结构 1.1 编译依赖(Makefile) 1 2 obj-$(CONFIG_KGDB) += debug_core.o gdbstub.o obj-$(CONFIG_KGDB_KDB) += kdb/
配置项
说明
CONFIG_KGDB
总开关,依赖 HAVE_ARCH_KGDB 和 DEBUG_KERNEL
CONFIG_KGDB_SERIAL_CONSOLE
串口共享 kgdb(kgdboc)
CONFIG_KGDB_KDB
启用 KDB 内置命令行前端
CONFIG_KDB_KEYBOARD
PS/2 键盘作为 KDB 输入
CONFIG_KGDB_HONOUR_BLOCKLIST
禁止在不安全符号上设断点
1.2 编译单元
编译单元
源文件
规模
功能
调试核心
debug_core.c
~1,239 行
异常入口、SMP roundup、断点、I/O 注册
GDB Stub
gdbstub.c
~1,156 行
GDB 远程串行协议实现
KDB 主循环
kdb/kdb_main.c
~2,937 行
命令解析、注册、主 REPL 循环
KDB I/O
kdb/kdb_io.c
~882 行
控制台输出、命令行输入
KDB 断点
kdb/kdb_bp.c
~591 行
KDB 断点/观察点管理
KDB 支持
kdb/kdb_support.c
~556 行
内存读写、符号解析、参数解析
KDB 栈回溯
kdb/kdb_bt.c
~221 行
bt/btp/bta/btc 命令
KDB 键盘
kdb/kdb_keyboard.c
~262 行
PS/2 键盘输入(可选)
KDB/KGDB 桥接
kdb/kdb_debugger.c
~176 行
kdb_stub、模式切换
内部头文件
debug_core.h
~87 行
核心与前端私有接口
KDB 私有头
kdb/kdb_private.h
~245 行
KDB 内部类型与 API
初始命令
kdb/kdb_cmds
~32 行
启动时执行的 defcmd 宏
1.3 相关代码分布(本目录外)
位置
功能
arch/arm64/kernel/kgdb.c
ARM64 断点异常、寄存器、单步
include/linux/kgdb.h
公共 API、arch_kgdb_ops
drivers/tty/serial/kgdboc.c
串口 I/O 驱动(kgdboc=)
drivers/misc/kgdbts.c
KGDB 内部测试套件
kernel/trace/trace_kdb.c
ftrace 与 KDB 集成
二、整体架构 2.1 分层模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌─────────────────────────────────────────────────────────────┐ │ 主机端调试器 │ │ GDB (target remote) / kdb 命令行 │ └──────────────────────────┬──────────────────────────────────┘ │ 串口 / 网络 ┌──────────────────────────▼──────────────────────────────────┐ │ gdbstub.c / kdb/kdb_main.c │ │ GDB Remote Protocol │ KDB 命令 REPL │ └──────────────────────────┬──────────────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────────────┐ │ debug_core.c (核心) │ │ kgdb_handle_exception │ kgdb_cpu_enter │ 断点 │ SMP roundup │ └──────────────────────────┬──────────────────────────────────┘ │ ┌──────────────────────────▼──────────────────────────────────┐ │ arch/arm64/kernel/kgdb.c │ │ 断点指令 │ pt_regs 读写 │ 单步 │ kgdb_brk_fn 异常处理 │ └─────────────────────────────────────────────────────────────┘
2.2 双前端设计
模式
默认
入口
特点
KDB
dbg_kdb_mode = 1
kdb_stub()
内核内置 shell,无需主机 GDB
GDB Stub
切换后
gdb_serial_stub()
标准 GDB 远程协议,功能完整
两者可在运行时通过 kgdb 命令或 GDB 3 转义包互相切换。
2.3 关键全局状态 1 2 3 4 5 6 struct debuggerinfo_struct kgdb_info [NR_CPUS ]; atomic_t kgdb_active; int kgdb_connected; struct kgdb_io *dbg_io_ops ; int dbg_kdb_mode = 1 ;
三、核心数据结构 3.1 struct kgdb_state — 异常上下文 1 2 3 4 5 6 7 8 9 10 11 12 struct kgdb_state { int ex_vector; int signo; int err_code; int cpu; int pass_exception; unsigned long threadid; long kgdb_usethreadid; struct pt_regs *linux_regs ; atomic_t *send_ready; };
3.2 struct debuggerinfo_struct — 每 CPU 状态 1 2 3 4 5 6 7 8 9 struct debuggerinfo_struct { void *debuggerinfo; struct task_struct *task ; int exception_state; int ret_state; int irq_depth; int enter_kgdb; bool rounding_up; };
CPU 状态标志:
标志
含义
DCPU_WANT_MASTER
等待成为 master 调试 CPU
DCPU_NEXT_MASTER
切换 master CPU
DCPU_IS_SLAVE
从 CPU 已进入等待
DCPU_WANT_BT
从 CPU 需打印栈回溯
3.3 struct kgdb_bkpt — 软件断点 1 2 3 4 5 6 struct kgdb_bkpt { unsigned long bpt_addr; char saved_instr[BREAK_INSTR_SIZE]; int state; };
最多 KGDB_MAX_BREAKPOINTS 个(通常 1000),由 GDB 或 KDB 管理。
3.4 struct kgdb_io — I/O 驱动接口 1 2 3 4 5 6 7 8 9 10 struct kgdb_io { const char *name; int (*read_char)(void ); void (*write_char)(u8); void (*flush)(void ); int (*init)(void ); void (*pre_exception)(void ); void (*post_exception)(void ); int is_early; };
通过 kgdb_register_io_module() 注册,典型实现为 kgdboc(串口)。
四、异常进入与 SMP 协调 4.1 主入口 1 2 3 4 5 6 7 int kgdb_handle_exception (int evector, int signo, int ecode, struct pt_regs *regs) { }
ARM64 上由 kgdb_brk_fn() 在断点异常时调用:
1 2 3 4 5 6 static int kgdb_brk_fn (struct pt_regs *regs, unsigned long esr) { kgdb_handle_exception(1 , SIGTRAP, 0 , regs); return 0 ; }
4.2 kgdb_cpu_enter 流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 kgdb_cpu_enter() ├── 获取 master 锁 (dbg_master_lock) ├── kgdb_roundup_cpus() // SMP: 通知其他 CPU 停止 ├── 等待所有 CPU 进入 kgdb 等待态 ├── dbg_deactivate_sw_breakpoints() // 临时移除 SW 断点 ├── 关闭 ftrace (tracing_off) └── 主循环: if (dbg_kdb_mode) kdb_stub(ks) // KDB 命令行 else gdb_serial_stub(ks) // GDB 协议 → 根据返回值切换模式或退出 ├── dbg_activate_sw_breakpoints() // 恢复断点 ├── 释放 slave 锁,等待从 CPU 退出 └── 恢复 tracing,返回正常执行
4.3 SMP Roundup 多核系统中,一个 CPU 触发调试后需冻结其他 CPU ,避免并发修改内核状态:
Master CPU 获取 dbg_master_lock,设置 kgdb_active
通过 IPI/NMI 调用 kgdb_nmicallback() 让从 CPU 进入 spin 等待
从 CPU 设置 DCPU_IS_SLAVE,在 cpu_loop 中等待
调试结束后释放锁,从 CPU 恢复
可通过内核参数 nokgdbroundup 禁用 roundup(调试用,有风险)。
4.4 递归保护
exception_level — 防止调试器内部再次触发异常时死锁
kgdb_info[cpu].enter_kgdb — 每 CPU 进入计数
kgdb_reenter_check() — 检测双重异常
五、GDB Stub 远程协议 5.1 协议格式 GDB 远程串行协议使用 $<data>#<checksum> 帧格式:
1 2 3 static void get_packet (char *buffer) ; static void put_packet (char *buffer) ;
5.2 主要命令处理
命令
功能
?
查询停止原因(信号号)
g / G
读/写全部寄存器
p / P
读/写单个寄存器
m / M
读/写内存
c
继续执行
s
单步执行
Z / z
设置/删除断点
H
设置当前线程
T
查询线程是否 alive
q
各类查询(supported、offsets、Xfer 等)
D / k
分离 / 杀死
R
重启
3
转义回 KDB 模式
5.3 线程与多任务 GDB stub 支持按线程调试:
Hg<tid> — 设置当前线程
Hc<tid> — 设置继续执行的线程
T<tid> — 检查线程状态
配合 /proc 或内核 task 列表遍历所有线程
5.4 架构相关处理 kgdb_arch_handle_exception()(ARM64)处理 c/s 包中的地址参数,设置 PC 并启用/禁用硬件单步。
六、KDB 内置调试器 6.1 命令注册机制 1 2 3 4 5 6 7 8 9 10 11 static struct kdb_cmd kdb_cmds [] = { { .name = "md" , .func = kdb_md, ... }, { .name = "mm" , .func = kdb_mm, ... }, { .name = "go" , .func = kdb_go, ... }, { .name = "bt" , .func = kdb_bt, ... }, { .name = "ps" , .func = kdb_ps, ... }, { .name = "bp" , .func = kdb_bp, ... }, { .name = "kgdb" , .func = kdb_kgdb, ... }, };
命令通过 kdb_register() 加入 kdb_cmds_head 链表;kdb_parse() 解析用户输入并 dispatch。
6.2 常用 KDB 命令
命令
功能
md / mdr / mdp / mds
显示内存(raw/phys/proc/symbol)
mm
修改内存
rd / rm
读/写 MSR(架构相关)
go
继续执行(可选地址)
bt
当前栈回溯
btp <pid>
指定进程栈回溯
bta
所有进程栈回溯
btc
每 CPU 一个进程栈回溯
ps
进程列表
pid
切换当前进程上下文
bp / bc / ba
断点 set/clear/list
ss
单步
cpu
切换 CPU
dmesg
显示内核 log 缓冲
summary
系统摘要
lsmod
已加载模块
reboot
重启
kill
发送信号给进程
set / env
环境变量(RADIX、PROMPT 等)
help / ?
帮助
kgdb
切换到 GDB stub 模式
defcmd / endefcmd
定义命令宏
sr
触发 SysRq
6.3 权限控制 kdb_cmd_enabled(模块参数 cmd_enable)按位控制命令权限:
位
权限
0x0002
任意内存读、符号查找
0x0004
任意内存写
0x0008
寄存器读
0x0010
寄存器写
0x0020
被动检查(bt、ps、lsmod)
0x0040
流程控制(断点、单步)
0x0080
信号进程
0x0100
重启
6.4 启动宏(kdb_cmds) kdb/kdb_cmds 在 KDB 初始化时执行,预定义诊断宏:
1 2 3 defcmd dumpcommon ... # summary + cpu + ps + dmesg + bt defcmd dumpall ... # dumpcommon + bta defcmd dumpcpu ... # dumpcommon + btc
七、断点管理 7.1 软件断点(KGDB 核心) 1 2 3 4 5 6 7 kgdb_arch_set_breakpoint(bpt) → 保存原始指令到 saved_instr → 写入架构断点指令(ARM64: BRK) kgdb_arch_remove_breakpoint(bpt) → 恢复原始指令
进入调试器时 deactivate 所有 SW 断点(避免调试代码自身触发),退出时 activate 恢复。
7.2 KDB 断点(kdb_bp.c) KDB 独立维护最多 KDB_MAXBPT(16)个断点:
类型
说明
BP_HARDWARE_BREAKPOINT
指令断点
BP_WRITE_WATCHPOINT
数据写观察点
BP_ACCESS_WATCHPOINT
数据读写观察点
支持延迟断点(bp_delay)和单步恢复机制(SSBPT 标志)。
7.3 Blocklist 保护 启用 CONFIG_KGDB_HONOUR_BLOCKLIST 时,禁止在 kprobe blocklist 中的符号(如异常处理路径上的函数)设置断点,防止递归陷阱。
八、I/O 与触发方式 8.1 串口 I/O(kgdboc) 1 2 3 4 kgdboc=ttyFIQ0,115200 kgdboc=ttyS2,115200
I/O 驱动通过 kgdb_register_io_module() 注册到 dbg_io_ops。
8.2 触发进入调试器
方式
说明
SysRq-g
echo g > /proc/sysrq-trigger 或 Magic SysRq 键 + g
kgdbwait
内核启动参数,启动后等待 GDB 连接
断点/Oops
内核 panic/oops 时自动进入(若已注册)
KDB 键盘
PS/2 键(需 CONFIG_KDB_KEYBOARD)
1 2 3 4 5 6 static void sysrq_handle_dbg (int key) { kgdb_breakpoint(); } register_sysrq_key('g' , &sysrq_dbg_op);
8.3 内核参数
参数
功能
kgdbwait
启动时等待 GDB 连接
kgdboc=<tty>,<baud>
指定 kgdb 串口
nokgdbroundup
禁用 SMP CPU roundup
kdb.cmd_enable=<hex>
KDB 命令权限掩码
kgdbreboot=<n>
panic 时行为
九、KDB 与 GDB 模式切换 9.1 KDB → GDB 在 KDB 提示符输入:
kdb_stub() 返回 DBG_PASS_EVENT,主循环切换 dbg_kdb_mode = 0,下次迭代进入 gdb_serial_stub()。
9.2 GDB → KDB GDB 发送转义包 '3',或 KDB I/O 检测到非 GDB 格式输入时切换。
9.3 主循环切换逻辑 1 2 3 4 5 6 7 8 9 10 11 12 13 14 while (1 ) { if (dbg_kdb_mode) { error = kdb_stub(ks); } else { error = gdb_serial_stub(ks); } if (error == DBG_PASS_EVENT) dbg_kdb_mode = !dbg_kdb_mode; else if (error == DBG_SWITCH_CPU_EVENT) goto cpu_loop; else break ; }
十、完整调试会话时序 以 SysRq-g 触发 → GDB 连接 为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 [用户 / 按键] echo g > /proc/sysrq-trigger → sysrq_handle_dbg() → kgdb_breakpoint() → ARM64 BRK 异常 → kgdb_brk_fn(regs) → kgdb_handle_exception(SIGTRAP, regs) [debug_core — SMP 协调] kgdb_cpu_enter() → master CPU 获取 dbg_master_lock → kgdb_roundup_cpus() — IPI 冻结其他 7 核 → dbg_deactivate_sw_breakpoints() → tracing_off() [KDB 模式 — 默认] kdb_stub(ks) → kdb_common_init_state() → 显示 kdb> 提示符,等待输入 [用户输入 kgdb 切换] kdb> kgdb → 返回 DBG_PASS_EVENT → dbg_kdb_mode = 0 [GDB Stub 模式] gdb_serial_stub(ks) → put_packet("S05") // 通知 GDB: SIGTRAP → get_packet() 循环处理 m/M/g/G/c/s/Z/z ... [主机 GDB] (gdb) target remote /dev/ttyUSB0 (gdb) break some_kernel_func (gdb) continue [继续执行后再次触发] → 再次进入 kgdb_handle_exception → GDB 收到停止通知,可 inspect 寄存器/内存 [退出调试] GDB 发送 'D' (detach) 或 'c' (continue) → kgdb_cpu_enter 主循环 break → dbg_activate_sw_breakpoints() → 释放 slave 锁,从 CPU 恢复 → 系统正常运行
十一、RK3588 平台配置与使用 11.1 内核配置 1 2 3 4 5 6 7 8 9 zcat /proc/config.gz | grep -E 'KGDB|KDB|DEBUG'
ARM64 RK3588 在 arch/arm64/Kconfig 中选择 HAVE_ARCH_KGDB,具体实现在 arch/arm64/kernel/kgdb.c。
11.2 设备树 / 命令行示例 1 2 3 console=ttyFIQ0,115200 earlycon=uart8250,mmio32,0xfeb50000 \ kgdboc=ttyFIQ0,115200 kgdbwait
ttyFIQ0:Rockchip 平台常用 FIQ 调试串口
kgdbwait:内核初始化完成后暂停,等待 GDB 连接
11.3 主机端 GDB 连接 1 2 3 4 5 6 7 8 aarch64-linux-gnu-gdb vmlinux (gdb) set serial baud 115200 (gdb) target remote /dev/ttyUSB0 (gdb) bt (gdb) info registers (gdb) x/10i $pc (gdb) continue
11.4 无 GDB 时使用 KDB 1 2 3 4 5 6 7 8 9 echo g > /proc/sysrq-triggerkdb> bt kdb> ps kdb> md 0xffffffc008000000 kdb> dmesg 100 kdb> go
11.5 Android / 嵌入式注意事项
要点
说明
串口访问
需物理 UART 或 USB 转串口,Android 量产机通常无暴露接口
性能影响
kgdbwait 会阻塞启动,仅用于 bring-up 调试
SMP
RK3588 8 核,roundup 需等待所有 CPU,超时约 1 秒
与 ftrace 交互
进入 KGDB 时自动 tracing_off(),退出后恢复
Lockdown
启用 Kernel Lockdown 时,GDB 写内存会被拒绝,自动回退 KDB
11.6 与 ftrace/KDB 集成 启用 CONFIG_KGDB_KDB 且 ftrace 支持时,kernel/trace/trace_kdb.c 允许在 KDB 中使用 ftrace 相关命令,便于运行时追踪与调试结合。
十二、总结 kernel/debug 是 Linux 内核源码级调试的基础设施:
debug_core.c — 统一异常入口、SMP 多核协调、断点管理、I/O 抽象
gdbstub.c — 实现 GDB 远程串行协议,支持主机 GDB 全功能调试
kdb/ — 可选内置命令行调试器,适合无 GDB 环境或 early boot 诊断
双模式切换 — KDB 与 GDB stub 共享核心,可按场景灵活切换
架构分离 — ARM64 相关代码在 arch 层,本目录保持架构无关
在 RK3588 平台 bring-up、驱动调试、内核 panic 分析场景中,KGDB/KDB 是与串口日志、ftrace、crash dump 互补的重要工具。生产环境通常不启用;开发和深度调试内核时,配合 kgdboc + kgdbwait + 主机 GDB 是标准工作流。
附录:源文件完整清单
文件
行数
分类
kdb/kdb_main.c
2,937
KDB 主循环与命令
debug_core.c
1,239
KGDB 核心
gdbstub.c
1,156
GDB 远程协议
kdb/kdb_io.c
882
KDB I/O
kdb/kdb_bp.c
591
KDB 断点
kdb/kdb_support.c
556
KDB 辅助函数
kdb/kdb_keyboard.c
262
键盘输入
kdb/kdb_bt.c
221
栈回溯
kdb/kdb_debugger.c
176
KDB/KGDB 桥接
debug_core.h
87
内部头文件
kdb/kdb_private.h
245
KDB 私有头文件
kdb/kdb_cmds
32
启动宏定义
正在加载留言…