ftrace 工作原理-龙芯平台
一、函数注入trace调用
1.以kernel_thread和sys_fork为例,源代码如图1-1所示:
图1-1 源码
2.在编译内核时,通过 gcc -pg 选项,编译时,函数开头自动插入ftrace_caller调用。
上述两个函数的汇编代码如图1-2所示。
图1-2 代码生成的汇编码
在图1-1的源码中并没有ftrace_caller的调用,但是内核编译完成后却加入调用。
3.接下来分析ftrace_caller源码如图1-3所示,汇编代码如图1-4所示。
图1-3 ftrace_caller 源码
图1-4 ftrace_caller汇编码
二、ftrace运行状态
1.系统启动后默认状态kernel_thread和sys_fork二进制码如图2-1所示,ftrace_caller汇编码如图2-2所示。
图2-1 系统启动后代码二进制指令码
图2-2 ftrace_caller系统启动后的指令码
对比图2-1和图1-2,发现地址0x8024137c和地址0x802413bc由原来的0x0c085428即跳转ftrace_caller函数的指令在系统启动后变为0(即nop指令)。
对比图2-2和图1-4,发现地址0x802150a0由原来的0x10000027即跳转ftrace_stub函数的指令在系统启动后变为0(即nop指令)。
系统初始化时调用ftrace_init完成上述的改变:ftrace_dyn_arch_init(&addr)函数调用ftrace_modify_code(MCOUNT_ADDR, INSN_NOP)将ftrace_caller开始的 b ftrace
_stub指令修改为nop指令。函数ftrace_process_locs(NULL,__start_mcount_loc,
__stop_mcount_loc)来完成将所有包含jal ftrace_caller的函数该指令修改为nop。Start
_mcount_loc中的内容为所有调用jal ftrace_caller的地址(在编译阶段生成),方便找到地址并修改,该区域中有地址0x0x8024137c(kernel_thread)和地址0x802413bc
(sys_fork)。
2.echo function > current_tracer后系统中的函数和变量的变化
kernel_thread和sys_fork源码的指令码变化如图2-3所示,ftrace_caller源码的指令码变化如图2-4所示。
图2-3 sys_fork & kernel_thread指令码
图2-4 ftrace_caller指令码
对比图2-3和图2-1,发现地址0x8024137c和地址0x802413bc由原来的0x0(即nop指令)变为0x0c08542a即跳转ftrace_caller函数地址+8的地址指令处(即0x802150a8)。
对比图2-4和图2-2,发现地址0x80215104由原来的0(nop)修改为0x0c0b9ff2即跳转ftrace_ops_no_ops函数(地址为0x802e7fc8)。
ftrace_ops_no_ops源码如图2-5所示,函数会扫描ftrace_ops_list链表调用注册的函数。
图2-5 ftrace_ops_no_ops源码中的关键函数
通过反汇编看一下ftrace_ops_list链表中的内容,内核编译完成后的内容如图2-6所示,
通过echo function > current_tracer后的内容如图2-7所示。
图2-6 ftrace_ops_list内容
图2-7 ftrace_ops_list中的内容码
通过上图可以,ftrace_ops_list的内容有原来的0x81024060(即ftrace_list_end)修改了0x81037510(即global_ops)。global_ops的内容由原来的0x80215140(即ftrace_stub),修改为0x80301330(即function_trace_call函数)。
目前加入了调用ftrace_caller的函数最终调用的函数为function_trace_call函数。
echo function > current_tracer 该动作所调用的函数为tracing_set_trace_write(),该函数从全局变量trace_types中取出与“function”字符一样的tracer。所有的tracer是调用register_tracer()函数来完成注册的,如function的注册过程如图2-8所示(init阶段完成的)。
tracing_set_trace_write()函数调用function的中的init函数即function_trace_init来完成其它的初始化,如调用register_ftrace_function(&trace_ops)函数完成trace_ops的注册,将trace_ops加入global_ops链表;如给全局变量ftrace_trace_function赋值等。最终使得调用ftrace_caller可以调用到function_trace_call函数。
3.echo function > current_tracer后系统目前可以调用函数,但是真正的trace还没有开始,需要echo 1 > tracing_on后,系统才会开始。
在函数function_trace_call()则调用trace_function将信息记录在 ring_buffer中。
echo 1 > tracing_on动作产生调用rb_simple_write(),调用tr->current_trace->start即调用function_trace_start()函数,将buffer->record_disabled减1,将cpu_
buffer-> record_disabled减1来使能记录日志到buffer功能。trace_function记录日志的函数中,其中ring_buffer_lock_reserve会判断两个record_disabled是否已满足要求,如果满足则记录日志,否则退出。
4.ring_buffer如何工作?
具体可参考:http://blog.chinaunix.net/uid-20543183-id-1930845.html