再看内核的ftrace架构
在上面这篇文章中,我们知道了如何在函数中tracepoint上注册函数,那么是谁搭建的这个平台呢?内核中ftrace平台
register_trace_##name
tracepoint_probe_register_prio
__DECLARE_TRACE
DEFINE_TRACE
把所有注册tracepoint的函数都抽象出来了做成了宏。
trace_##name 函数是真正的trace函数
trace_sched_switch后面是如何扩展开来的?
trace_sched_switch并不是系统的函数,包括在最后的符号表中也没有trace_sched_switch这个函数存在,其实trace_sched_switch是个宏里,
扩展trace_sched_switch
1) step1: include/trace/events/sched.h函数中, TRACE_EVENT(sched_switch...)2)step2: TARCE_EVENT(line 483)-->DECLARE_TRACE(347)-->__DECLARE_TRACE(181),逐渐就把整个宏给扩展开来了,__DECLARE_TRACE中生成了许多的函数:包括 static inline void trace_sched_switch static inline int register_trace_prio_sched_switch static inline int unregister_trace_prio_sched_switch static inline void check_trace_callback_type_##name(void (*cb)(data_proot) static inline bool trace_sched_switch_enable(void);所以呢,在函数kernel/sched/core.c函数中当有下面的#include <sched.h>函数时,其实在这个文件中新增加了这样三个函数,所以在sched/sched/core.c中函数中直接调用了trace_sched_switch函数是可以的,看下这个函数的内容是啥183 static inline void trace_##name(proto) 184 { 185 if (static_key_false(&__tracepoint_##name.key)) 186 __DO_TRACE(&__tracepoint_##name, 187 TP_PROTO(data_proto), 188 TP_ARGS(data_args), 189 TP_CONDITION(cond),,); 190 if (IS_ENABLED(CONFIG_LOCKDEP) && (cond)) { 191 rcu_read_lock_sched_notrace(); 192 rcu_dereference_sched(__tracepoint_##name.funcs); 193 rcu_read_unlock_sched_notrace(); 194 } 195 }
static key机制就是在这里生效的,这个tracpoing的结构体是在在哪里注册的呢?
所以呢,在哪个地方肯定是生成了static key, __tracepoint_sched_switch了,到这里都是trace point机制,那么这个tracepoint结构体的定义以及初始化是在哪里完成的呢?,
然后在每个include/trace/events/sched.h文件的最末尾都会有:include<trace/define_trace.h>,这个文件中又会对TRACE_EVENT 进一步扩展,首先上来,TRACE_EVENT会被扩展成tracepoint结构体
3) DEFINE_TRACE->DEFINE_TRACE_FN,(include/linux/tracepint.h)
在这个函数中,所有的tracepoint结构被初始化了, struct tracepoint __tracepoint_sched_swtich
/* * We have no guarantee that gcc and the linker won't up-align the tracepoint * structures, so we create an array of pointers that will be used for iteration * on the tracepoints. */ #define DEFINE_TRACE_FN(name, reg, unreg) static const char __tpstrtab_##name[] __attribute__((section("__tracepoints_strings"))) = #name; struct tracepoint __tracepoint_##name __attribute__((section("__tracepoints"))) = { __tpstrtab_##name, STATIC_KEY_INIT_FALSE, reg, unreg, NULL }; static struct tracepoint * const __tracepoint_ptr_##name __used __attribute__((section("__tracepoints_ptrs"))) = &__tracepoint_##name;
看下tracepoint的结构体长啥样子,
29 struct tracepoint { 30 const char *name; /* Tracepoint name */ 31 struct static_key key; 32 void (*regfunc)(void); 33 void (*unregfunc)(void); 34 struct tracepoint_feveunc __rcu *funcs; 35 };
所以在这里算是声明好了这个tracepoint。
综合上面两部分,tracepoint相关的注册函数等都设置好了,包括tracepoint结构体,还有该tracepoint相关的注册函数,但是缺少怎么使能tracetpoint,已经tracepoint输出函数是怎么导入到ftrace缓冲区
4)step4:)在文件trace/ trace_events.h文件中,还没完呢,还有include <trace/trace_events.h>
结果在这个函数中,还有include<linux/trace_events.h>
TRACE_EVENT->DEFINE_EVENT
define_event包含两个宏,一个是DECLARE_EVENT_CLASS,一个是DEFINE_EVENT,
这里是直接把函数给调用
#undef DEFINE_EVENT #define DEFINE_EVENT(template, call, proto, args) static struct trace_event_call __used event_##call = { .class = &event_class_##template, { .tp = &__tracepoint_##call, }, .event.funcs = &trace_event_type_funcs_##template, .print_fmt = print_fmt_##template, .flags = TRACE_EVENT_FL_TRACEPOINT, }; static struct trace_event_call __used __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call
这里出现了一个新的结构体叫trace_event_call
#undef DECLARE_EVENT_CLASS #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print) static notrace enum print_line_t trace_raw_output_##call(struct trace_iterator *iter, int flags, struct trace_event *trace_event) { struct trace_seq *s = &iter->seq; struct trace_seq __maybe_unused *p = &iter->tmp_seq; struct trace_event_raw_##call *field; int ret; field = (typeof(field))iter->ent; ret = trace_raw_output_prep(iter, trace_event); if (ret != TRACE_TYPE_HANDLED) return ret; trace_seq_printf(s, print); return trace_handle_return(s); } static struct trace_event_functions trace_event_type_funcs_##call = { .trace = trace_raw_output_##call, };
这个文件中果然都是和trace的输出相关的,主要生成的函数包括:
static void trace_event_raw_event_sched_switch(void *_data, proto);
enum print_line_t trace_raw_output_sched_switch(struct trace_iterator *iter, int flags);
struct trace_event_data_offset_sched_switch {
pid_t pid;
......
}
struct trace_event_raw_sched_switch {
struct trace_entry ent;
.....
}
都是和输出相关的。
这些函数都是怎么联系到一起的呢?trace_raw_output_sched_switch函数在哪里会使用呢
struct trace_event_functions trace_event_type_funcs_sched_switch 函数
大boss要出现了:
static struct trace_event_call __used event_##call = { .class = &event_class_##template, { .tp = &__tracepoint_##call, }, .event.funcs = &trace_event_type_funcs_##template, .print_fmt = print_fmt_##template, .flags = TRACE_EVENT_FL_TRACEPOINT, }; static struct trace_event_call __used __attribute__((section("_ftrace_events"))) *__event_##call = &event_##call
在__ftrace_events段中这个函数,那么这个段应该是个数组吧,所有的tracepoint事件都会放到一个数组里,将来是怎么索引的呢?
event_trace_enable函数