Skip to content

调试工具总览

调试工具选择矩阵

问题类型首选工具备选工具
驱动崩溃 / Oopsdmesg + addr2lineKGDB
内存越界 / UAFKASANKFENCE
内存泄漏kmemleakvalgrind(用户空间)
性能瓶颈perfftrace
函数调用追踪ftraceeBPF
死锁检测lockdepdmesg
中断延迟ftrace irqsoffcyclictest
网络问题tcpdumpWireshark

内核日志系统

printk 日志级别

c
#include <linux/printk.h>

pr_emerg("系统不可用\n");      /* KERN_EMERG   0 */
pr_alert("需要立即处理\n");    /* KERN_ALERT   1 */
pr_crit("严重错误\n");         /* KERN_CRIT    2 */
pr_err("错误\n");              /* KERN_ERR     3 */
pr_warn("警告\n");             /* KERN_WARNING 4 */
pr_notice("重要通知\n");       /* KERN_NOTICE  5 */
pr_info("信息\n");             /* KERN_INFO    6 */
pr_debug("调试信息\n");        /* KERN_DEBUG   7,需 DEBUG 宏或动态调试 */

/* 带设备前缀(驱动中推荐) */
dev_err(dev, "初始化失败: %d\n", ret);
dev_warn(dev, "超时,重试中\n");
dev_info(dev, "设备就绪\n");
dev_dbg(dev, "寄存器值: 0x%08x\n", val);

控制日志级别

bash
# 查看当前日志级别
cat /proc/sys/kernel/printk
# 输出:4  4  1  7
# 含义:当前控制台级别 默认消息级别 最低控制台级别 默认控制台级别

# 显示所有级别(包括 DEBUG)
echo 8 > /proc/sys/kernel/printk

# 实时查看内核日志
dmesg -w
dmesg -T   # 显示人类可读时间戳
dmesg -l err,warn   # 只显示错误和警告

动态调试

bash
# 启用特定文件的 pr_debug
echo 'file my_driver.c +p' > /sys/kernel/debug/dynamic_debug/control

# 启用特定函数
echo 'func my_probe +p' > /sys/kernel/debug/dynamic_debug/control

# 启用整个模块
echo 'module my_driver +p' > /sys/kernel/debug/dynamic_debug/control

# 查看当前动态调试配置
cat /sys/kernel/debug/dynamic_debug/control | grep my_driver

Oops 分析

内核 Oops 是最常见的驱动崩溃形式,典型输出:

BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
PGD 0 P4D 0
Oops: 0002 [#1] SMP PTI
CPU: 2 PID: 1234 Comm: my_app Tainted: G           OE     5.15.0
RIP: 0010:my_driver_write+0x45/0x120 [my_driver]
RSP: 0018:ffffc90001234a80 EFLAGS: 00010246
...
Call Trace:
 vfs_write+0xb5/0x1f0
 ksys_write+0x5f/0xe0
 do_syscall_64+0x5b/0x1d0

分析步骤

bash
# 1. 找到崩溃地址
# RIP: 0010:my_driver_write+0x45/0x120

# 2. 反汇编定位源码行
objdump -d my_driver.ko | grep -A 20 "my_driver_write"

# 3. 使用 addr2line(需要调试符号)
addr2line -e my_driver.ko 0x45

# 4. 使用 gdb
gdb vmlinux
(gdb) list *(my_driver_write+0x45)

下一步

褚成志的笔记