分类
未分类

trace_event 注册以及使能

上一篇文章我们看到,内核展开一个 trace_point的宏以后,定义了一个trace_event_call 结构,并把这个结构放在”_ftrace_events” 段中。这一篇文章我们将解析 内核是如何加载 这些结构并使能的.
如果是内核vmlinux中的trace将在这个函数中 直接初始化,如果是模块中的调用trace_module_add_events 初始化。 这里只节选一个函数。

 static __init int event_trace_enable(void)
 {
 >-------struct trace_array *tr = top_trace_array();
 >-------struct trace_event_call **iter, *call;
 >-------int ret;

 >-------if (!tr)
 >------->-------return -ENODEV;

 >-------for_each_event(iter, __start_ftrace_events, __stop_ftrace_events) {

 >------->-------call = *iter;
 >------->-------ret = event_init(call);  \\关键函数,对event进行初始化,
 >------->-------if (!ret)
 >------->------->-------list_add(&call->list, &ftrace_events);
 >-------}

 >-------/*
 >------- * We need the top trace array to have a working set of trace
 >------- * points at early init, before the debug files and directories
 >------- * are created. Create the file entries now, and attach them
 >------- * to the actual file dentries later.
 >------- */
 >-------__trace_early_add_events(tr);

 >-------early_enable_events(tr, false);

 >-------trace_printk_start_comm();

 >-------register_event_cmds();

 >-------register_trigger_cmds();

 >-------return 0;
 }

event_init 函数如下,

 static int event_init(struct trace_event_call *call)
 {
 >-------int ret = 0;
 >-------const char *name;

 >-------name = trace_event_name(call);
 >-------if (WARN_ON(!name))
 >------->-------return -EINVAL;

 >-------if (call->class->raw_init) {
 >------->-------ret = call->class->raw_init(call);         \\ trace_event_raw_init函数
 >------->-------if (ret < 0 && ret != -ENOSYS)
 >------->------->-------pr_warn("Could not initialize trace events/%s\n", name);
 >-------}

 >-------return ret;
 }

这个函数只是调用trace_event_raw_init函数,这个函数调用了 register_trace_event 函数。这个register_trace_event 函数只是将trace_event 注册到全局结构中。

注册完成以后 会调用 __add_event_to_tracers 将trace_event 加入到各个tracer中,并在 /sys/kernel/debug/tracing/events/ 对应的目录下创建控制文件。到这一步 注册就完成了。

使能操作,使能时时给enable 文件中输入1
调用链条如下

system_enable_write --> __ftrace_set_clr_event --> __ftrace_set_clr_event_nolock -->  ftrace_event_enable_disable --> __ftrace_event_enable_disable -->trace_event_reg --> tracepoint_probe_register --> tracepoint_probe_register_prio -->tracepoint_add_func

当这一串函数执行玩意后,在trace_出会执行 trace_event_rawevent##call 对应的函数。 到这儿使能完毕。

raw_trace_point 简介
基于 bpf的raw trace_point 逻辑非常简单, 宏展开如下

 #define DECLARE_EVENT_CLASS(call, proto, args, tstruct, assign, print)>-\
 static notrace void>---->------->------->------->------->------->-------\
 __bpf_trace_##call(void *__data, proto)>>------->------->------->-------\
 {>------>------->------->------->------->------->------->------->-------\
 >-------struct bpf_prog *prog = __data;>>------->------->------->-------\
 >-------CONCATENATE(bpf_trace_run, COUNT_ARGS(args))(prog, CAST_TO_U64(args));>-\
 }

 /*
  * This part is compiled out, it is only here as a build time check
  * to make sure that if the tracepoint handling changes, the
  * bpf probe will fail to compile unless it too is updated.
  */
 #undef DEFINE_EVENT
 #define DEFINE_EVENT(template, call, proto, args)>------>------->-------\
 static inline void bpf_test_probe_##call(void)>->------->------->-------\
 {>------>------->------->------->------->------->------->------->-------\
 >-------check_trace_callback_type_##call(__bpf_trace_##template);>------\
 }>------>------->------->------->------->------->------->------->-------\
 static struct bpf_raw_event_map>__used>->------->------->------->-------\
 >-------__attribute__((section("__bpf_raw_tp_map")))>--->------->-------\
 __bpf_trace_tp_map_##call = {>-->------->------->------->------->-------\
 >-------.tp>---->-------= &__tracepoint_##call,>>------->------->-------\
 >-------.bpf_func>------= (void *)__bpf_trace_##template,>------>-------\
 >-------.num_args>------= COUNT_ARGS(args),>---->------->------->-------\
 };


 #undef DEFINE_EVENT_PRINT
 #define DEFINE_EVENT_PRINT(template, name, proto, args, print)>-\
 >-------DEFINE_EVENT(template, name, PARAMS(proto), PARAMS(args))

在4.19内核里面 还不支持 module 添加raw_tracepoint .通过bpf 可以在raw trace_point中添加任何执行,输入参数是trace_point直接输入。 有兴趣的可以看看bcc 是如何用raw_tracepoint 抓 一些数据的

注意事项:
trace_event 在复值并把 数据放到buffer的时候并没有格式化成字符串,只有当调用 读trace_pipe 或则trace文件时才会正真的将变量格式化成字符串输入。因此如果在写trace_point的时候 赋值指针的。去读的时候可能得到的不是当时的值。

发表评论

电子邮件地址不会被公开。 必填项已用*标注