Dynamic Debug的原理分析:debugfs的具体应用

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

工作原理:

  1. 每个 pr_debug() 编译时生成一个 _ddebug 结构
  2. 所有 _ddebug 放在特殊段 __dyndbg
  3. 内核启动时收集所有 _ddebug 结构
  4. 通过 debugfs 控制 flags 字段
  5. 运行时检查 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 的典型应用模式:

  1. 复杂的写操作

    • 解析用户命令
    • 应用配置到内核数据结构
    • 错误处理和验证
  2. 大量数据的读操作

    • 使用 seq_file 遍历内核数据
    • 格式化输出便于阅读
  3. 运行时控制内核行为

    • 无需重启或重新加载模块
    • 即时生效
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DeeplyMind

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值