一、顶级架构一句话总结printk日志 → 动态调试 → ftrace跟踪 → kprobes探针 → KGDB调试器内核调试是驱动开发中定位问题、分析性能的核心技能。二、printk调试日志级别#includelinux/printk.h// 日志级别定义#defineKERN_EMERG0// 系统不可用#defineKERN_ALERT1// 必须立即处理#defineKERN_CRIT2// 严重错误#defineKERN_ERR3// 错误#defineKERN_WARNING4// 警告#defineKERN_NOTICE5// 正常但重要#defineKERN_INFO6// 信息#defineKERN_DEBUG7// 调试信息// 使用方式printk(KERN_INFOHello, kernel\n);printk(KERN_ERRError occurred: %d\n,error);// 便捷宏pr_info(Information message\n);pr_err(Error message\n);pr_warn(Warning message\n);pr_debug(Debug message\n);// 需要定义DEBUGdev_info(dev,Device info\n);dev_err(dev,Device error\n);查看日志# 查看内核日志dmesgdmesg|tail-50dmesg-w# 实时查看# 查看日志级别cat/proc/sys/kernel/printk# 设置日志级别echo8 4 1 7/proc/sys/kernel/printk# 清空日志dmesg-c三、动态调试Dynamic Debug启用动态调试# 内核配置CONFIG_DYNAMIC_DEBUGy# 启用所有调试信息echo8/proc/sys/kernel/printk动态调试命令# 查看所有动态调试点cat/sys/kernel/debug/dynamic_debug/control# 启用模块的所有调试echomodule mydriver p/sys/kernel/debug/dynamic_debug/control# 启用文件的所有调试echofile mydriver.c p/sys/kernel/debug/dynamic_debug/control# 启用函数的调试echofunc my_probe p/sys/kernel/debug/dynamic_debug/control# 启用行号范围echofile mydriver.c:100-200 p/sys/kernel/debug/dynamic_debug/control# 禁用调试echomodule mydriver -p/sys/kernel/debug/dynamic_debug/control# 启用所有调试echop/sys/kernel/debug/dynamic_debug/control调试标志标志说明p打印到日志f打印函数名l打印行号m打印模块名t打印线程ID代码中使用// 动态调试宏pr_debug(Debug message: %d\n,value);dev_dbg(dev,Device debug: %d\n,value);// 条件调试pr_debug(Value is %d\n,value);四、ftrace跟踪启用ftrace# 挂载debugfsmount-tdebugfs none /sys/kernel/debug# 进入ftrace目录cd/sys/kernel/debug/tracing函数跟踪# 查看可用跟踪器catavailable_tracers# 启用函数跟踪echofunctioncurrent_tracer# 跟踪特定函数echomy_probeset_ftrace_filter# 跟踪特定模块echo:mod:mydriverset_ftrace_filter# 查看结果cattrace# 清空缓冲区echotrace函数图跟踪# 启用函数图echofunction_graphcurrent_tracer# 设置跟踪函数echomy_probeset_graph_function# 查看结果cattrace事件跟踪# 查看可用事件lsevents/# 启用事件echo1events/sched/sched_switch/enable# 查看结果cattrace跟踪特定进程# 设置跟踪进程PIDecho1234set_ftrace_pid五、kprobes探针kprobes类型┌─────────────────────────────────────────────────────────┐ │ kprobes类型 │ ├─────────────────────────────────────────────────────────┤ │ kprobe - 在任意指令处插入探针 │ │ jprobe - 函数入口探针已废弃 │ │ kretprobe - 函数返回探针 │ └─────────────────────────────────────────────────────────┘使用kprobes# 添加kprobeechop:myprobe my_probe/sys/kernel/debug/kprobes/add_probe# 添加kretprobeechor:myretprobe my_probe/sys/kernel/debug/kprobes/add_probe# 启用探针echo1/sys/kernel/debug/kprobes/enable# 查看结果cat/sys/kernel/debug/kprobes/listcat/sys/kernel/debug/tracing/trace# 禁用探针echo0/sys/kernel/debug/kprobes/enable# 删除探针echo-:myprobe/sys/kernel/debug/kprobes/del_probe内核模块中使用#includelinux/kprobes.hstaticstructkprobekp{.symbol_namemy_probe,};staticinthandler_pre(structkprobe*p,structpt_regs*regs){pr_info(kprobe: %s called\n,p-symbol_name);return0;}staticvoidhandler_post(structkprobe*p,structpt_regs*regs,unsignedlongflags){pr_info(kprobe: %s returned\n,p-symbol_name);}staticint__initmy_init(void){kp.pre_handlerhandler_pre;kp.post_handlerhandler_post;if(register_kprobe(kp)0){pr_err(Failed to register kprobe\n);return-1;}return0;}staticvoid__exitmy_exit(void){unregister_kprobe(kp);}六、KGDB远程调试配置KGDB# 内核配置CONFIG_KGDByCONFIG_KGDB_SERIAL_CONSOLEyCONFIG_KGDB_KDBy# 内核启动参数kgdbocttyS0,115200 kgdbwait进入调试模式# 在目标机上触发echog/proc/sysrq-trigger# 或使用魔术键SysRq gGDB调试# 在开发机上启动GDBgdb vmlinux# 连接目标机target remote /dev/ttyS0# GDB命令breakmy_probecontinuestep next print variable backtraceKDB命令# 在KGDB提示符下kdbbp my_probe# 设置断点kdbgo# 继续kdbbt# 调用栈kdbps# 进程列表kdbrd# 寄存器kdbmdaddr# 内存dump七、内核oops分析oops信息解读[12345.678901] Unable to handle kernel NULL pointer dereference at virtual address 00000000 [12345.678902] pgd c0004000 [12345.678903] [00000000] *pgd00000000 [12345.678904] [12345.678905] PC is at my_probe0x20/0x100 [mydriver] [12345.678906] LR is at my_probe0x18/0x100 [mydriver] [12345.678907] pc : [bf012345] lr : [bf01233d] psr: 60000013 [12345.678908] sp : c0123456 ip : c0123458 fp : c012345a [12345.678909] r10: 00000000 r9 : c012345c r8 : c012345e [12345.678910] r7 : 00000001 r6 : 00000002 r5 : 00000003 r4 : 00000004 [12345.678911] r3 : 00000000 r2 : 00000005 r1 : 00000006 r0 : 00000007 [12345.678912] Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM [12345.678913] Control: 10c5387d Table: 4000404a DAC: 00000015 [12345.678914] Process myprocess (pid: 1234, stack limit 0xc0123456) [12345.678915] Stack: (0xc0123456 to 0xc0123456) ... [12345.678920] Backtrace: [12345.678921] [bf012345] (my_probe [mydriver]) from [bf023456] (driver_probe_device0x56/0x100)分析步骤# 1. 定位出错的函数和偏移# PC is at my_probe0x20/0x100# 2. 使用addr2line定位源码行addr2line-emydriver.ko 0x20# 3. 使用gdb反汇编gdb mydriver.ko(gdb)disassemble my_probe# 4. 分析调用栈# Backtrace中的函数调用链八、内存调试SLAB调试# 内核配置CONFIG_DEBUG_SLAByCONFIG_DEBUG_KMEMLEAKy# 查看内存泄漏cat/sys/kernel/debug/kmemleakKASAN地址消毒器# 内核配置CONFIG_KASANy# 运行时检测内存错误# 自动报告越界访问、use-after-free等lockdep锁依赖检测# 内核配置CONFIG_LOCKDEPy# 查看锁依赖cat/proc/lockdepcat/proc/lockdep_stats九、调试工具对比工具用途开销适用场景printk简单日志低快速定位动态调试可控日志低生产环境ftrace函数跟踪中性能分析kprobes动态探针中深度调试KGDB源码调试高复杂问题KASAN内存检测高内存问题十、终极总结内核调试 定位问题的关键能力printk最简单、最常用的调试手段动态调试生产环境可控的调试输出ftrace函数调用跟踪、性能分析kprobes动态插入探针、深度调试KGDB源码级调试、复杂问题定位掌握多种调试技术才能高效解决内核问题