Linux kernel debug  trace: tracing机制debugging方式 在使用各种trace方式debug时必须要有对应的trace记录即trace数据源谁在产生trace。kernel中有多种方式进行trace记录打点如Tracepiontskernel预埋事件, Kprobe动态插入函数, Ftrace函数调用, perf events软硬件事件等但在使用这些方式打点时需要先确保tracefs文件系统已挂载。#挂载tracefs文件系统 sudo mount -t tracefs tracefs /sys/kernel/tracingtrace_printktrace_printk是一种非常轻量级的实用工具把trace数据以无锁方式写入tracing ring buffer中因而可以在任何场景下使用在kernel中正常ISR高频路径等对实时性影响极小。ring-buffer写入读取流程trace_printk 为了更轻量甚至可能只记录format string指针只把raw arguments存进ring buffer中等输出时再进行后处理raw arguments的格式转换因此这种方式使trace_printk可以在hot path中调用注意trace_printk 仅能用于debug阶段被调用时kernel会报warning影响性能占用内存正式版本可用traces event代替Tracepointtracepoint是kernel中预定义的高性能事件打点用于记录关键运行事件调度发生时中断进入/退出block I/O内存alloc/free, 网络收发等是tracing的首选机制。当tracepoint功能打开时被打点的函数每次被调用时该函数内的trace函数都会被执行。tracepoint 是轻量级钩子可传递任意数量的参数其原型放置在头文件一般由唯一的事件标志符结构化参数及格式定义组成tracepoint优势由官方定义ABI相对稳定开销低静态key优化参数结构化非纯字符串可以被perf, ftrace, eBPF统一使用是linux tracing体系中最核心最推荐的使用机制。tracepoint解析方法参考关于使用事件和追踪点分析行为的笔记——Linux 内核文档#define TP_PROTO(args...) args #define TP_ARGS(args...) args #define __field(type, item) type item; #define TP_STRUCT__entry(args...) args #define TP_fast_assign(args...) args #define TP_printk(fmt, args...) fmt \n, args /* 调用TRACE_EVENT 对 reg_read生成Tracepoints实现 */ /* reg_read: 即为该事件的唯一标识符对该trace的具体传参类型num 可自定义 */ /* 如下传参会以结构体的形式传入TP_STRUCT_entry */ TRACE_EVENT(reg_read, TP_PROTO(const struct device *dev, const char *reg, u32 offs, u32 val), TP_ARGS(dev, reg, offs, val), TP_STRUCT__entry( __string(dev, dev_name(dev)) __field(const char *, reg) __field(u32, offs) __field(u32, val) ), TP_fast_assign( __assign_str(dev, dev_name(dev)) __entry-reg reg; __entry-offs offs; __entry-val val; ), TP_printk([%s] read %s:[%#x] %#x, __get_str(dev), __entry-reg, __entry-offs, __entry-val) ); #define TRACE_EVENT(name, proto, args, struct, assign, print) \ DECLARE_TRACE(name, PARAMS(proto), PARAMS(args)) #define DECLARE_TRACE(name, proto, args) \ __DECLARE_TRACE(name, PARAMS(proto), PARAMS(args), \ cpu_online(raw_smp_processor_id()), \ PARAMS(void *__data, proto), \ PARAMS(__data, args)) #define __DECLARE_TRACE(name, proto, args, cond, data_proto, data_args) \ extern int __traceiter_##name(data_proto); \ DECLARE_STATIC_CALL(tp_func_##name, __traceiter_##name); \ extern struct tracepoint __tracepoint_##name; \ /* 构建trace_xx, 如下示例中的trace_reg_read*/ static inline void __nocfi trace_##name(proto) \ { \ if (static_key_false(__tracepoint_##name.key)) \ /* Trace 实现 */ __DO_TRACE(name, \ TP_PROTO(data_proto), \ TP_ARGS(data_args), \ TP_CONDITION(cond), 0); \ if (IS_ENABLED(CONFIG_LOCKDEP) (cond)) { \ rcu_read_lock_sched_notrace(); \ rcu_dereference_sched(__tracepoint_##name.funcs);\ rcu_read_unlock_sched_notrace(); \ } \ } \ __DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args), \ PARAMS(cond), PARAMS(data_proto), PARAMS(data_args)) \ /* trace_##name 的register/unregister/enabled 方法*/ static inline int \ register_trace_##name(void (*probe)(data_proto), void *data) \ { \ return tracepoint_probe_register(__tracepoint_##name, \ (void *)probe, data); \ } \ static inline int \ register_trace_prio_##name(void (*probe)(data_proto), void *data,\ int prio) \ { \ return tracepoint_probe_register_prio(__tracepoint_##name, \ (void *)probe, data, prio); \ } \ static inline int \ unregister_trace_##name(void (*probe)(data_proto), void *data) \ { \ return tracepoint_probe_unregister(__tracepoint_##name,\ (void *)probe, data); \ } \ static inline void \ check_trace_callback_type_##name(void (*cb)(data_proto)) \ { \ } \ static inline bool \ trace_##name##_enabled(void) \ { \ return static_key_false(__tracepoint_##name.key); \ } /* trace 使用示例 */ static intline u32 xx_reg_read(struct device *dev) { u32 reg; reg reg_read(dev, A_REG_OFFSET); /* 调用上述声明的reg_read trace函数在该位置进行打点操作: 触发一个tracepoint */ trace_reg_read(dev, A_REG_NAME, A_REG_OFFSET, reg); return reg; }KprobeKprobe运行机制可参考Linux kernel debug trace: Kprobe-CSDN博客Tracefs ring-bufferTracefs ring-buffer是一个高性能无锁无阻塞按CPU分离的循环缓冲区用来存储所有trace数据。CPU分离每个CPU一个ring-buffer, 这样做不需要跨CPU竞争实现安全无锁写且具有极高性能无竞争无阻塞循环缓冲区写满后会覆盖最旧的数据Tracefs ring-buffer memory map提供了一种高效的数据流传输方法不需要内存复制也无需系统调用ring-buffer通过mmap把内核里的buffer直接映射到用户空间两个虚拟地址指向同一物理页避免copy参考使用追踪器进行调试——Linux 内核文档