目录
kernel版本:5.10
平台:arm64
aarch64-linux-gnu-gcc: (Linaro GCC 5.4-2017.01) 5.4.1 20161213
1.前言
本文主要介绍tracefs的初始化,为后续trace学习做好铺垫。我们主要解决如下几个问题:
trace fs目录如何建立起来的?
early trace如何开启的?
我们先来梳理一下,启动过程中有哪些与ftrace有关,此处我们把start_kernel启动流程中与ftrace初始化相关的部分截取出来如下:
start_kernel
|--ftrace_init
|--early_trace_init
|--trace_init
|--arch_call_rest_init
|--core_initcall(tracefs_init);
|--core_initcall(ftrace_mod_cmd_init)
|--core_initcall_sync(trace_boot_init)
|--core_initcall(init_branch_tracer)
|--fs_initcall(tracer_init_tracefs)
|--fs_initcall(bpf_event_init)
|--fs_initcall(init_dynamic_event)
|--device_initcall(init_blk_tracer)
|--late_initcall(test_ringbuffer)
|--late_initcall_sync(clear_boot_tracer)
下面我们逐个看下上面每个初始化函数
- ftrace_init
编译阶段,给gcc加入了-pg -mrecord-mcoun参数,则会在所有可trace的函数中插入 bl <_mcount> 指令,ftrace_init就是遍历__mcount_loc section段,将所有的插入指令替换为nop,这样可以减少性能损耗 - early_trace_init
early_trace_init最重要的就是为trace_printk等分配了buffer,同时注册了nop tracer,function tracer,注册tracer就是通过将tracer链入全局trace_types链表完成注册 - trace_init
主要是对trace event的初始化,它会批量化注册trace_event,将每个trace_event_call(本质上也是一个tracepoint)挂接到全局ftrace_events链表,为每一个trace_event_call创建trace_event_file,对应每个子系统下的一个trace event;
(1)注册filter相关的ftrace_func_command到全局ftrace_commands,这些注册的ftrace_func_command将会在写入/sys/kernel/debug/tracing/set_ftrace_filter文件时被调用,各子系统的filter文件节点是在event_trace_init->early_event_add_tracer时被创建;
(2)注册trigger相关的event_command到全局trigger_commands,这些注册的event_command将会在写/sys/kernel/debug/tracing/xxx/trigger文件时被调用, 各子系统的trigger文件节点是在
event_trace_init->early_event_add_tracer时被创建 - tracefs_init
在/sys/kernel下创建tracing挂载目录, 注册trace fs文件系统 - 初始化阶段将通过fs_initcall(tracer_init_tracefs);来调用tracer_init_tracefs,通过tracer_init_tracefs搭建起tracefs的目录结构
- tracer_init_tracefs
创建/sys/kernel/debug/traing顶级目录,创建events目录下顶层目录;通过遍历所有的trace_event_call(通过TRACE_EVENT定义),如果所属的trace_subsystem_dir没有创建则进行创建,这个就是events目录下的各个trace event子系统目录,在此目录下还会创建enable和filter文件节点。顶层目录下创建一系列tracer公用文件,包括:available_tracers,current_tracer, tracing_cpumask, trace_options,trace, trace_pipe, buffer_size_kb,buffer_total_size_kb, free_buffer, trace_marker, tracing_on, snapshot, error_log, 每一个文件都单独定义了相应的file_operations。ftrace_init_tracefs_toplevel:顶层目录下创建其它ftrace相关文件
2. ftrace_init
ftrace_init
\--ftrace_process_locs(NULL, __start_mcount_loc, __stop_mcount_loc)
| //按插入点的地址进行排序
|--sort(start, count, sizeof(*start),ftrace_cmp_ips, NULL);
| //为插入点的指令地址分配空间,每个插入点都会有一个struct ftrace_page表示
|--start_pg = ftrace_allocate_pages(count)
| //遍历所有struct ftrace_page下的struct dyn_ftrace,初始化它的ip
|--pg = start_pg;
| p = start;
| while (p < end) {
| rec = &pg->records[pg->index++];
| rec->ip = addr;
\--ftrace_update_code(mod, start_pg)
为了实现function trace,我们知道在编译阶段,给gcc加入了-pg -mrecord-mcoun参数,则会在所有可trace的函数中插入 bl <_mcount> 指令,之后会通过scripts/recordmcount.c将所有插入此条指令的位置记录到__mcount_loc section,链接时将会把所有.o文件的__mcount_loc section链接到一起,它的起始地址为__start_mcount_loc,结束地址为__stop_mcount_loc。ftrace_init就是遍历__mcount_loc section段,将所有的插入指令替换为nop,这样可以减少性能损耗,至于为何不在gcc编译阶段就做此替换,可能有它的历史原因,估计是考虑到gcc的通用性吧
-
ftrace_allocate_pages:为分配尽量连续的物理地址,因此可能会分为多段,每个地址段由一个struct ftrace_page进行管理,多个ftrace_page组成一个链表,每个struct dyn_ftrace代表一个entry,用于描述插入点

-
之后将通过一个while循环,遍历每一个struct dyn_ftrace,初始化它的ip,此处的ip就是插入指令的地址,也就是bl <_mcount>的地址
-
ftrace_update_code:由于编译阶段在每个可trace函数里插入了bl <_mcount> 指令,为了减少性能损耗,此处将这些插入的指令替换为nop
3. early_trace_init
early_trace_init最重要的就是为trace_printk等分配了buffer,同时注册了nop tracer,function tracer,register_tracer就是通过将tracer链入全局trace_types链表完成注册
void __init early_trace_init(void)
{
if (tracepoint_printk) {
tracepoint_print_iter =
kmalloc(sizeof(*tracepoint_print_iter), GFP_KERNEL);
if (MEM_FAIL(!tracepoint_print_iter,
"Failed to allocate trace iterator\n"))
tracepoint_printk = 0;
else
static_key_enable(&tracepoint_printk_key.key);
}
tracer_alloc_buffers(); //分配ring buffer
}
如果cmdline中有参数tp_printk,tracepoint_printk可用于控制是否将trace信息写入到printk
echo 0 > /proc/sys/kernel/tracepoint_printk禁用trace信息写入到printk
echo 1 > /proc/sys/kernel/tracepoint_printk使能trace信息写入到printk
tracer_alloc_buffers
|--alloc_cpumask_var(&tracing_buffer_mask, GFP_KERNEL)
|--alloc_cpumask_var(&global_trace.tracing_cpumask, GFP_KERNEL)
|--if (&__stop___trace_bprintk_fmt != &__start___trace_bprintk_fmt)
| trace_printk_init_buffers()
|--ring_buf_size = trace_buf_size;
|--cpumask_copy(tracing_buffer_mask, cpu_possible_mask)
| cpumask_copy(global_trace.tracing_cpumask, cpu_all_mask)
|--temp_buffer = ring_buffer_alloc(PAGE_SIZE, RB_FL_OVERWRITE)
|--trace_create_savedcmd()
|--tracing_set_clock(&global_trace, trace_boot_clock)
|--global_trace

本文详细解析了tracefs在Linux 5.10 kernel中的初始化过程,涉及ftrace、early_trace_init、trace_event_init、tracer_init_tracefs等关键步骤,包括目录创建、事件注册、功能tracer设置和文件系统注册。
最低0.47元/天 解锁文章
1465

被折叠的 条评论
为什么被折叠?



