4.6.4. 使用 Kdb/Kgdb 调试内核
4.6.4.1. Kdb/Kgdb 概述
Kdb 是 Linux 内核
2001年:Kdb 的
初步 版本 由 Kurt Garloff 开发。Kdb 是 为 Linux 内核 提供 的 一种 简单 的 内核 调试 工具。它 的 设计 目标 是 提供 一种 不 依赖于 外部 系统 的 调试 方法,能够 直接 在 控制 台上 与 内核 交互。 2000年代
初期:Kdb 成为 Linux 内核 开发者 调试 的 一个 重要 工具,它 可以 在 系统 崩溃 或 内核 问题 发生 时 帮助 开发者 诊断 和 修复 问题。Kdb 运行 在 内核 模式 下,因此 即使 操作系统 崩溃 或 停止 响应,开发者 仍能 通过 串口 或 控制台 进行 调试。 2000年代
中期:随着 内核 功能 的 不断 增加,Kdb 开始 集成 到 更 多 的 内核 版本 中。它 可以 在 内核 启动 时 启用,提供 更 强大 的 调试 支持。 目前:Kdb 依然
是 内核 开发 中 常用 的 工具,特别 是 在 低级 故障 排查 和 崩溃 后 分析 中。它 通常 用于 更 简单、即时 的 内核 调试,但 由于 功能 上 比 GDB 更为 简单,开发者 往往 还 需要 配合 其他 工具 来 进行 更 复杂 的 调试。
Kgdb 旨在
2001年:KGDB 的
初步 工作 由 Jason Wessel 提出 和 实现。KGDB 是 基于 GDB 实现 的,它 通过 串行 端口、以太网 或 其他 通信接口 与 目标 内核 进行 通信,使得 开发者 可以 像 调试 用户 空间 程序 一样 调试 内核。 2002年:KGDB 在 Linux 2.6 内核
中 开始 引起 广泛 关注。它 使得 开发者 能够 在 用户 空间 使用 GDB 调试 内核 空间 代码,极大 提高 了 调试 的 灵活性。与 KDB 相比,KGDB 支持 更 复杂 的 调试 操作,如 设置 断点、单步 执行 等,类似 于 调试 用户 空间 程序 的 方式。 2000年代
后期:KGDB 支持 的 功能 逐渐 增多,包括 支持 多种 硬件平台(如 x86、ARM 等)和 更 高效 的 远程 调试 功能。 2010年代:KGDB 与
其他 调试 工具(如 QEMU 等)结合 使用,支持 通过 虚拟化 平台 进行 内核 调试。它 的 调试 方式 更加 灵活,可以 使用 远程 调试 和 硬件 仿真 工具 进行 更 复杂 的 调试。 目前:KGDB 继续
得到 内核 开发者 和 调试 人员 的 使用,尤其 是 在 内核 开发 过程 中,它 与 GDB 和 KDB 配合 使用,提供 了 强大 的 调试 能力。虽然 KGDB 配置 较为 复杂,但 它 仍然 是 Linux 内核 开发者 进行 内核 级别 调试 的 首选 工具 之一。
4.6.4.2. Kdb/kgdb 功能介绍
Kdb 功能介绍
Kdb 是 Linux 内核
Kdb 的
内核
模式 : KDB 直接调试 运行 在 内核 模式 下,不 需要 外部 调试 工具 或 环境。它 嵌入 在 内核 中,因此 在 内核 崩溃 时,开发者 仍然 可以 通过 控制台、串口 等 与 内核 交互 并 进行 调试。 触发式
调试 : KDB 可以在 内核 发生 特定 事件 时 自动 启动,例如 系统 崩溃、异常 或 错误 检测 时。开发者 可以 设置 断点,查看 特定条件 下 的 内核 状态。 实时
调试 : KDB 不依赖 外部 硬件 或 调试器,开发者 可以 实时 与 内核 交互,检查 内核 变量、堆栈、内存 等。 支持
多种 : KDB 支持接口 通过 控制台、串口、甚至 直接 从 调试 命令行 访问 和 调试 内核,这 在 没有 完整 图形界面 的 环境 下 非常 有用,尤其 是 嵌入式 开发。 调试
内核 : KDB 可以数据结构 直接 访问 和 查看 内核 数据结构,如 进程 调度、内存 管理、文件系统 等,有助于 发现 底层 问题。 独立
于 : 与 GDB 等外部 工具 外部 工具 不同,KDB 不 需要 额外 配置,它 是 内核 的 一部分,适合 用于 嵌入式 系统 或 无法 连接 外部 调试 工具 的 场合。
Kdb 的
内核
崩溃 时 的 调试:当 内核 发生 崩溃 时,KDB 可以 自动 触发 并 启动 调试 会话,开发者 可以 查看 崩溃 现场,检查 内存、寄存器、调用 栈 等。 低级
调试:KDB 能 直接 访问 内核 数据结构,适合 用于 低级 调试,特别 是 在 无法 使用 外部 调试器 时。 实时
系统 调试:在 实时操作系统 中,KDB 能 即时 响应 系统 状态 并 进行 调试,帮助 快速 定位问题。
Kgdb 功能介绍
Kgdb 是
Kgdb 的
通过 GDB 调试
内核 :Kgdb 允许
开发者 在 用户 空间 使用 GDB 调试 内核 代码。这 意味着 你 可以 使用 GDB 提供 的 强大 调试 功能(如 断点、单步 执行、堆栈 跟踪 等)来 调试 内核 代码。
与 GDB 的
集成 :Kgdb 与 GDB 集成,通过
串口、网络 或 其他 通信 方式 将 调试 会话 与 内核 调试 目标 连接起来。用户 可以 使用 GDB 的 命令 在 内核 上 设置 断点、单步 执行、查看 变量 等。
远程
调试 :Kgdb 允许
远程 调试 内核。内核 通过 串口、网络 等 通道 与 GDB 主机 进行 通信,这 对于 远程 调试 硬件 设备 上 的 内核 非常 有用。
支持
多种 :调试 功能 使用 GDB 时,开发者
可以 设置 断点、单步 执行、检查 内存、查看 寄存器 内容、调试 内核模块 等。GDB 提供 了 非常 强大 的 调试 功能,可以 轻松 进行 复杂 的 内核 调试。
调试
内核 :崩溃 Kgdb 也
可以 用于 内核 崩溃 的 调试。开发者 可以 在 崩溃 时 通过 GDB 进行 远程 调试,检查 崩溃 现场、查看 调用 栈、打印 内存 内容 等。
与 KDB 配合
使用 :Kgdb 和 KDB 并
不是 互相 排斥 的,实际上,它们 可以 一起 使用。KDB 允许 开发者 在 不 依赖 外部 工具 的 情况 下 进行 内核 调试,而 Kgdb 允许 通过 GDB 进行 更 详细 和 复杂 的 调试。
Kgdb 的
远程
调试 :通过串口、以太网 等 通信 方式,Kgdb 可以 用于 远程 调试 嵌入式 设备 或 其他 目标 机器 上 的 内核。这 对于 开发 和 维护 嵌入式 系统 或 其他 特殊 硬件平台 非常 有 帮助。 复杂
内核 :当调试 内核 代码 需要 进行 复杂 的 调试 时,开发者 可以 利用 GDB 的 强大 功能,如 多线程 调试、内存 分析 等,来 定位 和 解决问题。 内核模块
调试 :开发者可以 使用 GDB 调试 内核模块,分析 模块 加载、执行 过程 中 的 问题。
Kdb 与 Kgdb 对比
| 特性/功能 | KDB | KGDB |
|---|---|---|
| 调试 |
内核 |
通过 GDB 外部 |
| 使用 |
适用 |
适用 |
| 调试 |
提供 |
提供 GDB 的 |
| 调试 |
功能 |
功能强大,适合 |
| 调试 |
无需 |
依赖 GDB 和 |
| 调试 |
提供 |
由于 |
| 适用性 | 适用 |
适用 |
| 远程 |
不 |
支持 |
| 与 KDB 配合 |
可 |
可 |
4.6.4.3. Kdb/Kgdb 具体使用方法
开启 kdb/kgdb
X5 内核
执行 ./bd.sh boot menuconfig,将 kgdb 相关
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_KGDB_KDB=y
CONFIG_KDB_DEFAULT_ENABLE=0x1
修改kernel/arch/arm64/configs/hobot_x5_soc_defconfig 确保 kgdb 相关
板端启动 kdb 调试
板端
Uboot 阶段启动 kdb
在 UBoot 内
Hobot# setenv bootargs kgdboc=ttyS0,115200 kgdbwait
Hobot# run bootcmd
等待
Starting kernel ...
[ 0.000000] Linux version 6.1.83-DR-PL5.1_V1.0.14 (sxq@DESKTOP-6VORLA0) (aarch64-none-linux-gnu-gcc (Arm GNU Toolchain 11.3.Rel1) 11.3.1 20220712, GNU ld (Arm GNU Toolchain 11.3.Rel1) 2.38.20220708) #1 SMP PREEMPT Wed Dec 4 20:10:42 CST 2024
[ 0.000000] Kernel command line: console=ttyS0,115200n8 root=/dev/mmcblk0p9 ro rootwait hobotboot.slot_suffix=_a hobotboot.reason=COLD_BOOT hobotboot.medium=MMC hobotboot.mode=normal hobotboot.ab_switch_reason=normal hobotboot.pmic_type=single-pmic kgdboc=ttyS0,115200 kgdbwait
[ 0.111978] audit: type=2000 audit(0.090:1): state=initialized audit_enabled=0 res=1
[ 0.144479] (NULL device *): no horizon,gpio-banks in node /soc/disp_apb/disp_iomuxc@3e0a0054
[ 0.182704] SCSI subsystem initialized
[ 0.262379] Initialise system trusted keyrings
[ 0.294398] Key type asymmetric registered
[ 0.294409] Asymmetric key parser 'x509' registered
[ 0.393114] KGDB: Waiting for connection from remote gdb...
Entering kdb (current=0xffff0000058a0000, pid 1) on processor 7 due to Keyboard Entry
[7]kdb>
内核启动后启动 kdb
在
# 先卸载 watchdog 驱动(如果存在)
rmmod hobot_watchdog
echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
echo g > /proc/sysrq-trigger
日志
root@buildroot:~# echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
root@buildroot:~# echo g > /proc/sysrq-trigger
Entering kdb (current=0xffff0000cca42b80, pid 1003) on processor 4 due to Keyboard Entry
[4]kdb>
注意:
在 X5 BSP 的 debug 版本
Kdb 调试命令介绍
在 Kdb 命令help 命令
[7]kdb> help
Command Usage Description
----------------------------------------------------------
md <vaddr> Display Memory Contents, also mdWcN, e.g. md8c1
mdr <vaddr> <bytes> Display Raw Memory
mdp <paddr> <bytes> Display Physical Memory
mds <vaddr> Display Memory Symbolically
mm <vaddr> <contents> Modify Memory Contents
go [<vaddr>] Continue Execution
rd Display Registers
rm <reg> <contents> Modify Registers
ef <vaddr> Display exception frame
bt [<vaddr>] Stack traceback
btp <pid> Display stack for process <pid>
bta [<state_chars>|A] Backtrace all processes whose state matches
btc Backtrace current process on each cpu
btt <vaddr> Backtrace process given its struct task address
env Show environment variables
set Set environment variables
help Display Help Message
? Display Help Message
cpu <cpunum> Switch to new cpu
kgdb Enter kgdb mode
ps [<state_chars>|A] Display active task list
pid <pidnum> Switch to another task
reboot Reboot the machine immediately
lsmod List loaded kernel modules
sr <key> Magic SysRq key
dmesg [lines] Display syslog buffer
defcmd name "usage" "help" Define a set of commands, down to endefcmd
kill <-signal> <pid> Send a signal to a process
summary Summarize the system
per_cpu <sym> [<bytes>] [<cpu>]
Display per_cpu variables
grephelp Display help on | grep
bp [<vaddr>] Set/Display breakpoints
bl [<vaddr>] Display breakpoints
bc <bpnum> Clear Breakpoint
be <bpnum> Enable Breakpoint
bd <bpnum> Disable Breakpoint
ss Single Step
dumpcommon Common kdb debugging
dumpall First line debugging
dumpcpu Same as dumpall but only tasks on cpus
ftdump [skip#entries] [cpu]
Dump ftrace log; -skip dumps last #entries
下面
内存
相关 命令
| 命令 | 参数 | 描述 |
|---|---|---|
md |
<vaddr> |
显示<vaddr> 为WcN 参数md8c1)。 |
mdr |
<vaddr> <bytes> |
以<vaddr> 地址<bytes> 字节 |
mdp |
<paddr> <bytes> |
显示<paddr> 地址 |
mds |
<vaddr> |
符号化<vaddr> 开始 |
mm |
<vaddr> <contents> |
修改<vaddr> 处<contents>。 |
执行
控制 命令
| 命令 | 参数 | 描述 |
|---|---|---|
go |
[<vaddr>] |
继续<vaddr>,则 |
bt |
[<vaddr>] |
显示<vaddr> 作为 |
bta |
[<state_chars>\|A] |
回溯<state_chars> 过滤,A 表示 |
btc |
回溯 |
|
btt |
<vaddr> |
从<vaddr> 开始 |
ss |
进行 |
|
kill |
<-signal> <pid> |
向<signal> 是<pid> 是 |
pid |
<pidnum> |
切换pidnum)进行 |
sr |
<key> |
触发 |
寄存器
相关 命令
| 命令 | 参数 | 描述 |
|---|---|---|
rd |
显示 |
|
rm |
<reg> <contents> |
修改<reg> 为<contents> 为 |
环境变量
命令
| 命令 | 参数 | 描述 |
|---|---|---|
env |
显示 |
|
set |
设置 |
进程
相关 命令
| 命令 | 参数 | 描述 |
|---|---|---|
ps |
[<state_chars>\|A] |
显示<state_chars> 过滤,A 表示 |
lsmod |
列出 |
|
pid |
<pidnum> |
切换pidnum)进行 |
btp |
<pid> |
显示pid) 的 |
内核
调试 命令
| 命令 | 参数 | 描述 |
|---|---|---|
dmesg |
[lines] |
显示lines。 |
dumpcommon |
执行 |
|
dumpall |
执行 |
|
dumpcpu |
仅 |
自定义
命令 与 帮助 命令
| 命令 | 参数 | 描述 |
|---|---|---|
defcmd |
name "usage" "help" |
定义 |
grephelp |
显示\|)进行 |
其他
命令
| 命令 | 参数 | 描述 |
|---|---|---|
kgdb |
进入KGDB模式,进行 |
|
summary |
显示 |
|
per_cpu |
<sym> [<bytes>] [<cpu>] |
显示<sym>)的<bytes> 和CPU <cpu>。 |
bl |
显示 |
|
bp |
<vaddr> |
设置 |
bc |
<bpnum> |
清除 |
be |
<bpnum> |
启用 |
bd |
<bpnum> |
禁用 |
下面
# 显示栈回溯
[7]kdb> bt
Stack traceback for pid 999
0xffff0000c60f0000 999 1 1 7 R 0xffff0000c60f09f0 *bash
CPU: 7 PID: 999 Comm: bash Tainted: P C O 6.1.83-DR-PL5.1_V1.0.14 #11
Hardware name: D-Robotics X5 EVB LP4 1_B board (DT)
Call trace:
dump_backtrace+0xd8/0x130
show_stack+0x18/0x30
dump_stack_lvl+0x68/0x84
dump_stack+0x18/0x34
kdb_dump_stack_on_cpu+0x88/0x90
kdb_show_stack+0x90/0xa0
kdb_bt1+0xc4/0x140
kdb_bt+0x328/0x37c
kdb_parse+0x2c4/0x63c
kdb_main_loop+0x434/0x7b4
kdb_stub+0x270/0x444
kgdb_cpu_enter+0x168/0x66c
kgdb_handle_exception+0xcc/0x120
kgdb_compiled_brk_fn+0x28/0x40
call_break_hook+0x68/0x7c
brk_handler+0x1c/0x60
# 显示指定虚拟内存内容,如下是显示当前的内核完整版本字符串
[0]kdb> md linux_banner
0xffff800008baae48 65762078756e694c 2e36206e6f697372 Linux version 6.
0xffff800008baae58 2d52442d33382e31 31565f312e354c50 1.83-DR-PL5.1_V1
0xffff800008baae68 78732820302e312e 4f544b5345444071 .1.0 (sxq@DESKTO
0xffff800008baae78 414c524f56362d50 6372616128202930 P-6VORLA0) (aarc
0xffff800008baae88 656e6f6e2d343668 672d78756e696c2d h64-none-linux-g
0xffff800008baae98 28206363672d756e 20554e47206d7241 nu-gcc (Arm GNU
0xffff800008baaea8 696168636c6f6f54 522e332e3131206e Toolchain 11.3.R
0xffff800008baaeb8 2e31312029316c65 3232303220312e33 el1) 11.3.1 2022
# 显示当前CPU的寄存器内容
[7]kdb> rd
x0: ffff800009268000 x1: 0000000000000001 x2: ffff800009268558
x3: 0000000000000000 x4: ffff0000ff746b60 x5: ffff0000ff746b60
x6: 0000000000000000 x7: ffff800009184748 x8: 00000000ffffefff
x9: ffff80000912c748 x10: ffff800009184748 x11: 00000000000002fa
x12: 00000000000008ee x13: ffff80000912c748 x14: 0000000000000000
x15: fffffffffffed7d8 x16: 0000000000000000 x17: 0000000000000000
x18: 0000000000000018 x19: 0000000000000067 x20: ffff80000912c000
x21: ffff80000911a000 x22: 0000000000000006 x23: 0000000000000000
x24: 0000000000000000 x25: ffff800008af8f40 x26: 0000000000000000
x27: 0000000000000000 x28: ffff0000c60f0000 x29: ffff800019563c80
x30: ffff800008116184 sp: ffff800019563c80 pc: ffff8000081160ec
pstate: 60400009 v0: ?? v1: ?? v2: ?? v3: ?? v4: ?? v5: ?? v6: ?? v7: ??
v8: ?? v9: ?? v10: ?? v11: ?? v12: ?? v13: ?? v14: ?? v15: ?? v16: ??
v17: ?? v18: ?? v19: ?? v20: ?? v21: ?? v22: ?? v23: ?? v24: ?? v25: ??
v26: ?? v27: ?? v28: ?? v29: ?? v30: ?? v31: ?? fpsr: 00000000
fpcr: 00000000
Kgdb 远程连接单板调试
板端kgdb 命令,等待
kdb> kgdb
Entering please attach debugger or use $D#44+ or $3#33
在opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gdb,加载.bashrc 中
alias arm_gdb='/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gdb'
然后,通过sudo):
arm_gdb out/build/kernel/vmlinux
(gdb) set serial baud 115200
(gdb) target remote /dev/ttyUSB0
连接
关于 Kdb/Kgdb 的
4.6.4.4. 常见问题
Kdb 常见问题
无法
连接 : 如果 Kdb 是调试 终端 通过 串口 或 其他 终端设备 与 外部 交互,设备 配置 不 正确(例如 波特率 设置 错误)可能 导致 无法 连接。 调试信息
不全 : Kdb 提供的 调试信息 相对 较 少,可能 无法 提供 足够 的 上下文 信息,尤其 是 复杂 崩溃 时。此时 可能 需要 结合 其他 调试 工具,如 kgdb 或 crash 工具。 Kdb 无法
响应 : 当输入 系统 崩溃 或 卡死 时,Kdb 可能 无法 正常 响应 输入。可能 是 由于 控制台 或 串口 设备 配置 问题,或 内核 在 崩溃 前 未能 正确 初始化 调试 端口。
Kgdb 常见问题
GDB 无法
连接 : 可能到 内核 是因为 内核 没有 正确 配置 以 启用 Kgdb,或者 外部设备(如 串口 或 网络)连接 不 稳定。确保 内核 启用 了 CONFIG_KGDB,并且 通信 端口 和 GDB 配置 正确。 内核
调试信息 : 调试不 完整 符号 可能 未 包含 在 内核 映像 中,导致 调试信息 不足。需要 确保 内核 配置 了 调试 符号(如 CONFIG_DEBUG_INFO)。 GDB 与
内核 : GDB 版本版本 不 匹配 可能 与 内核 的 调试 接口 不 兼容,导致 调试 时 出现 崩溃 或 连接 问题。需要 使用 与 内核 版本 匹配 的 GDB。 调试器
与 : Kgdb 通过内核 之间 的 通信 延迟 串口 或 网络 进行 远程 调试 时,可能 会 遇到 较大 的 延迟,尤其 是 在 高 负载 系统 中。调试 过程 中 可能 会 出现 响应 缓慢 或 超时 问题。