trace系列0 - ftrace初始化

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


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的通用性吧

  1. ftrace_allocate_pages:为分配尽量连续的物理地址,因此可能会分为多段,每个地址段由一个struct ftrace_page进行管理,多个ftrace_page组成一个链表,每个struct dyn_ftrace代表一个entry,用于描述插入点
    在这里插入图片描述

  2. 之后将通过一个while循环,遍历每一个struct dyn_ftrace,初始化它的ip,此处的ip就是插入指令的地址,也就是bl <_mcount>的地址

  3. 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值