调试 Linux 驱动是内核开发中非常重要、也最容易踩坑的一环。下面从准备、常用手段、典型流程、常见工具几个层面系统地讲一下。
在编译内核或驱动时,确保开启以下选项:
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_KALLSYMS=y
CONFIG_DEBUG_DRIVER=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_MAGIC_SYSRQ=y
如果是学习/调试:
CONFIG_DEBUG_STACKOVERFLOW=y
CONFIG_DEBUG_SPINLOCK=y
CONFIG_PROVE_LOCKING=y
printk 是最基本的手段驱动中最常用的调试方式:
printk(KERN_DEBUG "my_driver: reg=%x\n", reg);
查看日志:
dmesg
dmesg -w # 实时查看
日志级别(从高到低):
KERN_EMERG > ALERT > CRIT > ERR > WARNING > NOTICE > INFO > DEBUG
控制控制台日志级别:
echo 8 > /proc/sys/kernel/printk
不需要重新编译驱动:
# 打开某个文件的调试
echo 'file my_driver.c +p' > /sys/kernel/debug/dynamic_debug/control
# 打开某个函数
echo 'func my_probe +p' > /sys/kernel/debug/dynamic_debug/control
查看输出:
dmesg
/sys 和 /proc 调试接口static ssize_t debug_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "debug info\n");
}
DEVICE_ATTR_RO(debug);
用户态访问:
cat /sys/class/mydev/debug
适合死机、Oops、卡死等严重问题。
内核配置:
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
启动参数(示例):
kgdboc=ttyS0,115200 kgdbwait
主机端:
gdb vmlinux
target remote /dev/ttyS0
dmesg
关键字段:
RIP: xxx
Call Trace:
使用 addr2line:
addr2line -e vmlinux 0xffffffff81234567
或使用 gdb:
gdb vmlinux
list *(func+0x50)
cd /sys/kernel/debug/tracing
echo function > current_tracer
echo my_probe > set_ftrace_filter
echo 1 > tracing_on
# 触发驱动
cat trace
echo function_graph > current_tracer
perf record -g insmod mydrv.ko
perf report
适合:
dmesg 看 Oops / 错误信息probe() 返回值printk 定位大致位置ftrace 看函数调用kgdb 断点调试SysRq 键echo t > /proc/sysrq-trigger # 查看进程栈
echo p > /proc/sysrq-trigger # panic
| 场景 | 推荐工具 |
|---|---|
| 普通逻辑问题 | printk + dynamic debug |
| 函数调用分析 | ftrace |
| 严重崩溃 | KGDB + Oops |
| 性能问题 | perf |
| 并发/锁 | lockdep |
如果你愿意,可以告诉我:
我可以直接给你针对性的调试步骤和示例代码。