Linux 驱动调试完整篇:从printk/dev_dbg、动态调试到 debugfs/ftrace 排障

模块 probe 偶发失败、现场只能复现一次、打开 printk 满屏刷却找不到关键路径------驱动调试的痛点通常不是「不会写驱动」,而是 观测面选错、日志级别不对、或在错误上下文里睡眠/打印 。板子上真正管用的是一套分层手段:日常用 dev_* + 动态调试(dyndbg),结构用 debugfs 暴露,时序用 ftrace/tracepoint,硬核再用 kgdb。

本文把驱动侧调试工具链收成一篇可验证闭环:日志与动态调试 → debugfs → ftrace/事件 → 常见坑与 Checklist。源 chapter(091--096)为提纲;正文按主线 lib/dynamic_debug.cfs/debugfs/kernel/trace/ 真实路径重写。


源码锚点

路径 作用
include/linux/printk.h printk、日志级别、pr_*
include/linux/dev_printk.h dev_err/dev_warn/dev_info/dev_dbg
include/linux/dynamic_debug.h DEFINE_DYNAMIC_DEBUG_METADATAdynamic_dev_dbg
lib/dynamic_debug.c dyndbg 查询解析、/sys/kernel/debug/dynamic_debug/control
include/linux/debugfs.h / fs/debugfs/ debugfs_create_dir/debugfs_create_file
include/linux/tracepoint.h / include/trace/events/ tracepoint 声明与事件头
kernel/trace/ ftrace、function tracer、event tracing
Documentation/admin-guide/dynamic-debug-howto.rst dyndbg 语法与模块参数

设备侧日志(优先于裸 printk):

c 复制代码
/* include/linux/dev_printk.h ------ 带设备名,便于 dmesg/grep */
dev_err(dev, "probe failed: %d\n", err);
dev_warn(dev, "fallback to polling\n");
dev_info(dev, "irq=%d\n", irq);
dev_dbg(dev, "xfer len=%zu\n", len);   /* 默认常被编译为可控 dbg */

动态调试控制面:

c 复制代码
/* 用户态:打开某模块所有 pr_debug/dev_dbg */
/* echo 'module mydrv +p' > /sys/kernel/debug/dynamic_debug/control */

/* 或按文件/行/函数: */
/* echo 'file mydrv.c:120 +p' > .../control */
/* echo 'func my_probe +p' > .../control */

/* 模块加载时:insmod mydrv.ko dyndbg=+p */

debugfs 导出寄存器/状态:

c 复制代码
/* include/linux/debugfs.h */
priv->dbg_dir = debugfs_create_dir("mydrv", NULL);
debugfs_create_u32("errors", 0444, priv->dbg_dir, &priv->err_cnt);
debugfs_create_file("regs", 0444, priv->dbg_dir, priv, &regs_fops);
/* 卸载:debugfs_remove_recursive(priv->dbg_dir); */

调用链

日志从驱动打到控制台/缓冲区

#mermaid-svg-RVnG27yfO5Fk93NM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-RVnG27yfO5Fk93NM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-RVnG27yfO5Fk93NM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-RVnG27yfO5Fk93NM .error-icon{fill:#552222;}#mermaid-svg-RVnG27yfO5Fk93NM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-RVnG27yfO5Fk93NM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-RVnG27yfO5Fk93NM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-RVnG27yfO5Fk93NM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-RVnG27yfO5Fk93NM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-RVnG27yfO5Fk93NM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-RVnG27yfO5Fk93NM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-RVnG27yfO5Fk93NM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-RVnG27yfO5Fk93NM .marker.cross{stroke:#333333;}#mermaid-svg-RVnG27yfO5Fk93NM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-RVnG27yfO5Fk93NM p{margin:0;}#mermaid-svg-RVnG27yfO5Fk93NM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-RVnG27yfO5Fk93NM .cluster-label text{fill:#333;}#mermaid-svg-RVnG27yfO5Fk93NM .cluster-label span{color:#333;}#mermaid-svg-RVnG27yfO5Fk93NM .cluster-label span p{background-color:transparent;}#mermaid-svg-RVnG27yfO5Fk93NM .label text,#mermaid-svg-RVnG27yfO5Fk93NM span{fill:#333;color:#333;}#mermaid-svg-RVnG27yfO5Fk93NM .node rect,#mermaid-svg-RVnG27yfO5Fk93NM .node circle,#mermaid-svg-RVnG27yfO5Fk93NM .node ellipse,#mermaid-svg-RVnG27yfO5Fk93NM .node polygon,#mermaid-svg-RVnG27yfO5Fk93NM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-RVnG27yfO5Fk93NM .rough-node .label text,#mermaid-svg-RVnG27yfO5Fk93NM .node .label text,#mermaid-svg-RVnG27yfO5Fk93NM .image-shape .label,#mermaid-svg-RVnG27yfO5Fk93NM .icon-shape .label{text-anchor:middle;}#mermaid-svg-RVnG27yfO5Fk93NM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-RVnG27yfO5Fk93NM .rough-node .label,#mermaid-svg-RVnG27yfO5Fk93NM .node .label,#mermaid-svg-RVnG27yfO5Fk93NM .image-shape .label,#mermaid-svg-RVnG27yfO5Fk93NM .icon-shape .label{text-align:center;}#mermaid-svg-RVnG27yfO5Fk93NM .node.clickable{cursor:pointer;}#mermaid-svg-RVnG27yfO5Fk93NM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-RVnG27yfO5Fk93NM .arrowheadPath{fill:#333333;}#mermaid-svg-RVnG27yfO5Fk93NM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-RVnG27yfO5Fk93NM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-RVnG27yfO5Fk93NM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RVnG27yfO5Fk93NM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-RVnG27yfO5Fk93NM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RVnG27yfO5Fk93NM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-RVnG27yfO5Fk93NM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-RVnG27yfO5Fk93NM .cluster text{fill:#333;}#mermaid-svg-RVnG27yfO5Fk93NM .cluster span{color:#333;}#mermaid-svg-RVnG27yfO5Fk93NM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-RVnG27yfO5Fk93NM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-RVnG27yfO5Fk93NM rect.text{fill:none;stroke-width:0;}#mermaid-svg-RVnG27yfO5Fk93NM .icon-shape,#mermaid-svg-RVnG27yfO5Fk93NM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-RVnG27yfO5Fk93NM .icon-shape p,#mermaid-svg-RVnG27yfO5Fk93NM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-RVnG27yfO5Fk93NM .icon-shape .label rect,#mermaid-svg-RVnG27yfO5Fk93NM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-RVnG27yfO5Fk93NM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-RVnG27yfO5Fk93NM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-RVnG27yfO5Fk93NM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} err/warn/info 常开
dbg 且未启用
dbg 且 +p
驱动: dev_err / dev_dbg / pr_debug
级别与 dyndbg
vprintk_emit
编译期/运行时丢弃
log_buf 环形缓冲
console / netconsole
用户态: dmesg / journalctl -k
echo module X +p > dynamic_debug/control

调试手段分层(选对层再加码)

#mermaid-svg-XtyN4tFR9StcRp02{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XtyN4tFR9StcRp02 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XtyN4tFR9StcRp02 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XtyN4tFR9StcRp02 .error-icon{fill:#552222;}#mermaid-svg-XtyN4tFR9StcRp02 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XtyN4tFR9StcRp02 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XtyN4tFR9StcRp02 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XtyN4tFR9StcRp02 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XtyN4tFR9StcRp02 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XtyN4tFR9StcRp02 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XtyN4tFR9StcRp02 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XtyN4tFR9StcRp02 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XtyN4tFR9StcRp02 .marker.cross{stroke:#333333;}#mermaid-svg-XtyN4tFR9StcRp02 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XtyN4tFR9StcRp02 p{margin:0;}#mermaid-svg-XtyN4tFR9StcRp02 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XtyN4tFR9StcRp02 .cluster-label text{fill:#333;}#mermaid-svg-XtyN4tFR9StcRp02 .cluster-label span{color:#333;}#mermaid-svg-XtyN4tFR9StcRp02 .cluster-label span p{background-color:transparent;}#mermaid-svg-XtyN4tFR9StcRp02 .label text,#mermaid-svg-XtyN4tFR9StcRp02 span{fill:#333;color:#333;}#mermaid-svg-XtyN4tFR9StcRp02 .node rect,#mermaid-svg-XtyN4tFR9StcRp02 .node circle,#mermaid-svg-XtyN4tFR9StcRp02 .node ellipse,#mermaid-svg-XtyN4tFR9StcRp02 .node polygon,#mermaid-svg-XtyN4tFR9StcRp02 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XtyN4tFR9StcRp02 .rough-node .label text,#mermaid-svg-XtyN4tFR9StcRp02 .node .label text,#mermaid-svg-XtyN4tFR9StcRp02 .image-shape .label,#mermaid-svg-XtyN4tFR9StcRp02 .icon-shape .label{text-anchor:middle;}#mermaid-svg-XtyN4tFR9StcRp02 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XtyN4tFR9StcRp02 .rough-node .label,#mermaid-svg-XtyN4tFR9StcRp02 .node .label,#mermaid-svg-XtyN4tFR9StcRp02 .image-shape .label,#mermaid-svg-XtyN4tFR9StcRp02 .icon-shape .label{text-align:center;}#mermaid-svg-XtyN4tFR9StcRp02 .node.clickable{cursor:pointer;}#mermaid-svg-XtyN4tFR9StcRp02 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XtyN4tFR9StcRp02 .arrowheadPath{fill:#333333;}#mermaid-svg-XtyN4tFR9StcRp02 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XtyN4tFR9StcRp02 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XtyN4tFR9StcRp02 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XtyN4tFR9StcRp02 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XtyN4tFR9StcRp02 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XtyN4tFR9StcRp02 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XtyN4tFR9StcRp02 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XtyN4tFR9StcRp02 .cluster text{fill:#333;}#mermaid-svg-XtyN4tFR9StcRp02 .cluster span{color:#333;}#mermaid-svg-XtyN4tFR9StcRp02 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XtyN4tFR9StcRp02 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XtyN4tFR9StcRp02 rect.text{fill:none;stroke-width:0;}#mermaid-svg-XtyN4tFR9StcRp02 .icon-shape,#mermaid-svg-XtyN4tFR9StcRp02 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XtyN4tFR9StcRp02 .icon-shape p,#mermaid-svg-XtyN4tFR9StcRp02 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XtyN4tFR9StcRp02 .icon-shape .label rect,#mermaid-svg-XtyN4tFR9StcRp02 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XtyN4tFR9StcRp02 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XtyN4tFR9StcRp02 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XtyN4tFR9StcRp02 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 死锁/oops 现场
竞态与延迟
日常排障
L1 日志: dev_* + dyndbg
L2 状态面: debugfs / sysfs
L3 时序: ftrace / tracepoint / perf
L4 交互: kgdb / kdb / crash dump


重点知识

1. 先分清「该不该打印」与「在哪一层看」

现象 优先手段 避免
probe 失败、资源拿不到 dev_err + 返回值路径 无设备上下文的裸 printk
偶发数据路径问题 dev_dbg + dyndbg 按函数打开 生产环境常开 +p 刷屏
寄存器/计数器要现场看 debugfs 只读节点 在硬中断里做复杂格式化
丢中断、延迟尖峰 ftrace / irqsoff / event 只靠猜 sleep 位置
oops / 死锁 完整 oops 文本、锁依赖、kgdb 重启前不保存日志

原则:错误路径永远要有可检索日志;热路径默认静默,用 dyndbg 按需打开。

2. dev_dbg 与动态调试(CONFIG_DYNAMIC_DEBUG)

开启 CONFIG_DYNAMIC_DEBUG 后,dev_dbg/pr_debug 会挂到 __dyndbg 段,由 lib/dynamic_debug.c 按查询串启停。常用 flag:

  • +p / -p:打印开关
  • +f/+l/+m:附带函数名、行号、模块名(排障期有用,热路径慎开)

模块参数惯例:

bash 复制代码
# 启动参数(内置驱动)
mydrv.dyndbg=+p

# 或全局
dyndbg="file drivers/misc/mydrv.c +p"

未开 DYNAMIC_DEBUG 时,许多配置下 dev_dbg 会变成空操作或依赖 DEBUG 宏------板级 defconfig 先确认,否则你以为开了调试其实一条都没有。

3. debugfs:给驱动一个「只读仪表盘」

适合导出:错误计数、最近一次错误码、关键寄存器快照、ring 指针、固件版本。注意:

  • 权限默认收紧;量产镜像常不挂 debugfs,调试镜像再 mount -t debugfs none /sys/kernel/debug
  • file_operations 里用 simple_read_from_buffer 即可;避免在 show 里拿会死锁的锁
  • 卸载必须 debugfs_remove_recursive,否则模块卸载后访问必 oops

4. ftrace:看调用与延迟,不只看字符串

bash 复制代码
# 函数追踪(过滤模块符号)
echo function > /sys/kernel/tracing/current_tracer
echo 'mydrv_*' > /sys/kernel/tracing/set_ftrace_filter
echo 1 > /sys/kernel/tracing/tracing_on
# 复现后
cat /sys/kernel/tracing/trace

# 事件:调度 / 中断
echo 1 > /sys/kernel/tracing/events/irq/enable
echo 1 > /sys/kernel/tracing/events/sched/sched_switch/enable

驱动若定义 tracepoint(TRACE_EVENT),比狂打 printk 轻一个数量级,且可用 perf/trace-cmd 离线分析。

5. 配置与编译开关

配置 用途
CONFIG_DYNAMIC_DEBUG 运行时开关 dbg
CONFIG_DEBUG_FS debugfs
CONFIG_FTRACE / FUNCTION_TRACER 函数追踪
CONFIG_KALLSYMS oops 符号化
CONFIG_DEBUG_KERNEL 及 lockdep/kasan(调试内核) 锁与内存类问题

模块 Makefile:调试包可加 ccflags-y += -DDEBUG,但长期更推荐 dyndbg,避免两套二进制行为不一致。

6. 常见坑与对策

现象 对策
硬中断里 printk 过多 丢中断、调度延迟炸 硬中断只记标志;重活放 thread/wq;dbg 用 rate limit
忘记关 dyndbg 磁盘/串口被日志打满 复现后立刻 -p;自动化脚本成对开关
debugfs 节点在 remove 后仍被读 use-after-free oops remove_recursive 与模块引用计数对齐
只看 dmesg 截断 关键 oops 丢失 dmesg -T / 增大 log_buf_len / 持久日志
用户态权限 打不开 dynamic_debug/control 需要 debugfs + root;确认已 mount
发行版裁掉 debugfs 节点不存在 调试镜像单独开 DEBUG_FS

7. 一条可重复的排障顺序

  1. 固定复现命令与版本(uname -r、模块 vermagic、DT/兼容串)
  2. 清环缓:dmesg -C(慎用生产),再跑复现,抓完整 dmesg -T
  3. 错误路径是否有 dev_err?没有则先补返回值日志再谈玄学
  4. 需要细节:对嫌疑函数 +p,或开对应 tracepoint
  5. 怀疑时序:ftrace/irqsoff;怀疑内存:KASAN/kmemleak(调试内核)
  6. 根因写入回归用例;默认关闭调试开关后再验证一次性能

Checklist

  • defconfig 已确认 DYNAMIC_DEBUG / DEBUG_FS / 必要 ftrace 选项
  • 错误路径一律 dev_err(或等价),带返回值与设备名
  • 热路径只用 dev_dbg,并用 dyndbg 按函数/文件开关,而非常开
  • 模块支持 dyndbg= 参数或文档写明 control 查询串
  • debugfs 目录在 probe 创建、removedebugfs_remove_recursive
  • 硬中断/spinlock 路径无阻塞、无过量打印
  • 能用一条命令复现,并保存完整 dmesg + /proc/version + 模块信息
  • 关闭所有 +p 后功能与性能仍正常(无调试依赖)

合并自:驱动开发/chapters/091--096-驱动调试(2026-09-06)

相关推荐
计算机毕设定制辅导-无忧学长2 小时前
《基于Spring Boot传承之光非遗陶瓷烧造产品交易平台的设计与实现》
java·vue.js·spring boot·后端·毕业设计
Zane19942 小时前
内存都要回收,为什么JVM偏要把堆分成新生代和老年代
java·后端
2603_965148112 小时前
宠物经济崛起:宠物食品用品API选品指南
大数据·服务器·人工智能·python·生活·宠物
Terra.K2 小时前
后端开发阶段性总结
java·开发语言·后端
modelmd2 小时前
Linux SIGTERM信号-实验
linux
云泽8082 小时前
Git 版本控制系统(上):架构原理与操作入门
linux·git·架构
计算机学姐2 小时前
基于SpringBoot的旅游系统的设计与实现
java·vue.js·spring boot·后端·spring·java-ee·旅游
卷心菜的学习路2 小时前
Spring Boot 多数据源落地:AbstractRoutingDataSource + 注解切面(附源码)
java·spring boot·后端
秋饼2 小时前
Spring AI AgentCore SDK 企业级实战:@AgentCoreInvocation 一注解替代 3000 行 Runtime 契约
java·ai·技术分享·后端开发