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