再看内核的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函数