1. 前文回顾
Linux的Dynamic debug功能简单讲了下如何使用。再来回顾下其概念和用法。
Dynamic Debug 是 Linux 内核的动态调试功能,允许在运行时动态开启/关闭特定的 pr_debug() 和 dev_dbg() 输出,而无需重新编译内核。
核心特点:
- ✅ 运行时控制:无需重启
- ✅ 精细化控制:可按模块、文件、函数、行号过滤
- ✅ 零性能开销:关闭时完全无开销
- ✅ 使用 DebugFS 作为控制接口
1.1 DebugFS 控制接口
主控制文件: /sys/kernel/debug/dynamic_debug/control
# 查看所有可用的 debug 点
$ cat /sys/kernel/debug/dynamic_debug/control
drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c:123 [amdgpu]amdgpu_init =_ "AMDGPU driver loaded\012"
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c:456 [amdgpu]amdgpu_device_init =_ "Device init started\012"
...
# 启用特定模块的所有 debug 输出
$ echo 'module amdgpu +p' > /sys/kernel/debug/dynamic_debug/control
# 启用特定文件的 debug 输出
$ echo 'file amdgpu_ring.c +p' > /sys/kernel/debug/dynamic_debug/control
# 启用特定函数的 debug 输出
$ echo 'func amdgpu_ring_init +p' > /sys/kernel/debug/dynamic_debug/control
# 禁用 debug 输出
$ echo 'module amdgpu -p' > /sys/kernel/debug/dynamic_debug/control
2. 内核实现分析
位置: lib/dynamic_debug.c
2.1. 数据结构
/* 每个 pr_debug() 对应一个 _ddebug 结构 */
struct _ddebug {
const char *modname; /* 模块名 */
const char *function; /* 函数名 */
const char *filename; /* 文件名 */
const char *format; /* 格式字符串 */
unsigned int lineno:18; /* 行号 */
unsigned int flags:8; /* 标志 (是否启用) */
} __attribute__((aligned(8)));
/* 标志定义 */
#define _DPRINTK_FLAGS_PRINT (1<<0) /* 启用打印 */
#define _DPRINTK_FLAGS_INCL_MODNAME (1<<1)
#define _DPRINTK_FLAGS_INCL_FUNCNAME (1<<2)
#define _DPRINTK_FLAGS_INCL_LINENO (1<<3)
2.2. DebugFS 文件操作
/* 读操作:显示所有 debug 点 */
static int ddebug_proc_show(struct seq_file *m, void *p)
{
struct ddebug_iter *iter = m->private;
struct _ddebug *dp = p;
char flagsbuf[10];
/* 格式化输出每个 debug 点的信息 */
seq_printf(m, "%s:%u [%s]%s =%s \"%s\"\n",
trim_prefix(dp->filename),
dp->lineno,
iter->table->mod_name,
dp->function,
ddebug_describe_flags(dp->flags, flagsbuf, sizeof(flagsbuf)),
dp->format);
return 0;
}
/* 写操作:处理控制命令 */
static ssize_t ddebug_proc_write(struct file *file, const char __user *ubuf,
size_t len, loff_t *offp)
{
char *tmpbuf;
int ret;
/* 限制输入大小 */
if (len == 0 || len > PAGE_SIZE)
return -EINVAL;
/* 从用户空间复制命令 */
tmpbuf = memdup_user_nul(ubuf, len);
if (IS_ERR(tmpbuf))
return PTR_ERR(tmpbuf);
/* 解析并应用控制命令 */
ret = ddebug_exec_queries(tmpbuf, NULL);
kfree(tmpbuf);
if (ret < 0)
return ret;
return len;
}
/* file_operations 定义 */
static const struct file_operations ddebug_proc_fops = {
.owner = THIS_MODULE,
.open = ddebug_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release_private,
.write = ddebug_proc_write,
};
2.3. 初始化
static int __init dynamic_debug_init_debugfs(void)
{
struct dentry *dir;
/* 创建 dynamic_debug 目录 */
dir = debugfs_create_dir("dynamic_debug", NULL);
if (!dir)
return -ENOMEM;
/* 创建 control 文件 */
debugfs_create_file("control", 0644, dir, NULL, &ddebug_proc_fops);
return 0;
}
fs_initcall(dynamic_debug_init_debugfs);
2.4 pr_debug() 宏实现
/* include/linux/dynamic_debug.h */
#ifdef CONFIG_DYNAMIC_DEBUG
#define pr_debug(fmt, ...) \
do { \
static struct _ddebug __aligned(8) \
__section("__dyndbg") descriptor = { \
.modname = KBUILD_MODNAME, \
.function = __func__, \
.filename = __FILE__, \
.format = (fmt), \
.lineno = __LINE__, \
.flags = _DPRINTK_FLAGS_DEFAULT, \
}; \
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \
__dynamic_pr_debug(&descriptor, pr_fmt(fmt), \
##__VA_ARGS__); \
} while (0)
#else
#define pr_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__)
#endif
工作原理:
- 每个
pr_debug()编译时生成一个_ddebug结构 - 所有
_ddebug放在特殊段__dyndbg中 - 内核启动时收集所有
_ddebug结构 - 通过 debugfs 控制
flags字段 - 运行时检查
flags决定是否打印
使用示例
驱动代码
/* drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c */
int amdgpu_ring_init(struct amdgpu_device *adev, struct amdgpu_ring *ring,
unsigned int max_dw, struct amdgpu_irq_src *irq_src)
{
/* 这些 pr_debug 默认不输出 */
pr_debug("Initializing ring %s\n", ring->name);
pr_debug("Ring size: %u DWs\n", max_dw);
/* ... 初始化逻辑 ... */
pr_debug("Ring %s initialized successfully\n", ring->name);
return 0;
}
运行时控制
# 启用该文件的所有 debug 输出
$ echo 'file amdgpu_ring.c +p' > /sys/kernel/debug/dynamic_debug/control
# 现在执行操作会看到 debug 信息
$ dmesg | tail
[ 123.456] amdgpu: Initializing ring gfx
[ 123.457] amdgpu: Ring size: 1024 DWs
[ 123.458] amdgpu: Ring gfx initialized successfully
# 禁用
$ echo 'file amdgpu_ring.c -p' > /sys/kernel/debug/dynamic_debug/control
高级用法
# 按行号启用
$ echo 'file amdgpu_ring.c line 123 +p' > control
# 组合条件(文件 + 函数)
$ echo 'file amdgpu_ring.c func amdgpu_ring_init +p' > control
# 格式化标志
$ echo 'module amdgpu +pflmt' > control
# +p: 启用打印
# +f: 包含函数名
# +l: 包含行号
# +m: 包含模块名
# +t: 包含线程 ID
# 使用通配符
$ echo 'file amdgpu_* +p' > control
$ echo 'func *_init +p' > control
性能影响分析
/* 关闭时的性能开销 */
if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) // 仅一次位测试
__dynamic_pr_debug(&descriptor, ...); // 不执行
/*
* unlikely() 提示:分支预测优化
* 位测试:极快的 CPU 操作
* 关闭时:几乎零开销(<1 纳秒)
*/
与其他调试方式对比
| 方式 | 运行时控制 | 性能开销 | 灵活性 | 需要重启 |
|---|---|---|---|---|
| printk | ❌ | 高 | 低 | ✅ |
| Dynamic Debug | ✅ | 极低 | 高 | ❌ |
| Ftrace | ✅ | 中 | 极高 | ❌ |
| 条件编译 (#ifdef) | ❌ | 无 | 低 | ✅ |
学习要点
Dynamic Debug 展示了 DebugFS 的典型应用模式:
-
复杂的写操作:
- 解析用户命令
- 应用配置到内核数据结构
- 错误处理和验证
-
大量数据的读操作:
- 使用 seq_file 遍历内核数据
- 格式化输出便于阅读
-
运行时控制内核行为:
- 无需重启或重新加载模块
- 即时生效

被折叠的 条评论
为什么被折叠?



