Skip to content

printk 与动态调试

printk 的工作原理

printk 将消息写入内核环形缓冲区(ring buffer),大小由 CONFIG_LOG_BUF_SHIFT 决定(默认 17 = 128KB)。消息异步输出到控制台,不会阻塞调用者(除非缓冲区满)。

bash
# 查看 ring buffer 大小
dmesg --buffer-size

# 清空 ring buffer
dmesg -C

# 保存到文件
dmesg > /tmp/kernel.log

格式化技巧

c
/* 打印内核地址(自动处理 KASLR 混淆) */
pr_info("function: %pS\n", func_ptr);      /* 符号名 + 偏移 */
pr_info("address: %px\n", ptr);            /* 原始地址(需 root) */
pr_info("address: %p\n", ptr);             /* 混淆后的地址 */

/* 打印内存块(十六进制转储) */
print_hex_dump(KERN_DEBUG, "buf: ", DUMP_PREFIX_OFFSET,
               16, 1, buf, len, true);

/* 打印位图 */
pr_info("cpumask: %*pb\n", nr_cpu_ids, cpu_online_mask);

/* 打印 IPv4 地址 */
pr_info("ip: %pI4\n", &ip_addr);

/* 打印 MAC 地址 */
pr_info("mac: %pM\n", mac_addr);

速率限制(避免日志洪泛)

c
/* 每 5 秒最多打印一次 */
if (printk_ratelimit())
    pr_warn("error occurred\n");

/* 更精确的控制 */
static DEFINE_RATELIMIT_STATE(rs, 5 * HZ, 3);  /* 5秒内最多3条 */
if (__ratelimit(&rs))
    pr_warn("rate limited message\n");

/* 一次性打印(只打印第一次) */
pr_warn_once("this only prints once\n");

动态调试详解

动态调试(CONFIG_DYNAMIC_DEBUG=y)允许在运行时按需开启 pr_debug / dev_dbg,无需重新编译:

bash
# 控制文件
/sys/kernel/debug/dynamic_debug/control

# 格式:<查询条件> <操作>
# 操作:+p 开启打印,-p 关闭,+f 加文件名,+l 加行号,+m 加模块名,+t 加线程ID

# 开启某文件所有 debug
echo 'file drivers/i2c/i2c-core-base.c +p' > /sys/kernel/debug/dynamic_debug/control

# 开启某函数
echo 'func i2c_transfer +p' > /sys/kernel/debug/dynamic_debug/control

# 开启某模块
echo 'module my_driver +p' > /sys/kernel/debug/dynamic_debug/control

# 开启所有(慎用,日志量极大)
echo '+p' > /sys/kernel/debug/dynamic_debug/control

# 关闭
echo 'module my_driver -p' > /sys/kernel/debug/dynamic_debug/control

# 查看当前状态
grep my_driver /sys/kernel/debug/dynamic_debug/control

内核启动参数中开启:

bash
# 在 bootargs 中添加
dyndbg="module my_driver +p"

追踪特定代码路径

c
/* 打印调用栈(调试时定位调用来源) */
dump_stack();

/* 打印当前任务信息 */
pr_info("current task: %s (pid %d)\n", current->comm, current->pid);

/* 打印寄存器(仅在 Oops 时有意义) */
show_regs(regs);

条件断点式调试

c
/* 触发内核 BUG(打印调用栈并继续运行) */
WARN_ON(ptr == NULL);
WARN_ON_ONCE(ret < 0);

/* 触发 BUG(停止当前任务) */
BUG_ON(critical_condition);

/* 断言(仅在 DEBUG 模式下检查) */
BUILD_BUG_ON(sizeof(struct my_struct) != 64);  /* 编译时检查 */

性能影响

方法性能影响适用场景
pr_debug(未开启)零开销生产代码
pr_debug(动态调试开启)调试阶段
pr_info低(异步)正常日志
printk 同步模式崩溃前最后日志
dump_stack()偶发调试
bash
# 开启同步 printk(确保崩溃前日志不丢失)
echo 1 > /sys/kernel/debug/printk/sync
# 或内核参数
# printk.synchronous=1

褚成志的笔记