ftrace实现(二) ----function tracer
因为ftrace中涉及到大量的初始化,故调用脚本postseq列出被自动调用的函数。
脚本:http://www.cnblogs.com/openix/archive/2013/02/03/2890827.html version_two
为了便于以后查阅,记录下……结合Makefile和config文件,可以进一步分析出有哪些函数被自动调用了。对于如何实现“进一步分析”的自动化实现,以小白的身份显然还做不到。
__setup "ftrace_notrace=", set_ftrace_notrace ftrace.c 2306 __setup "ftrace_filter=", set_ftrace_filter ftrace.c 2313 __setup "ftrace=", set_ftrace trace.c 140 __setup "ftrace_dump_on_oops", set_ftrace_dump_on_oops trace.c 147 __setup "trace_buf_size=", set_buf_size trace.c 298 __setup "trace_event=", setup_trace_event trace_events.c 1155 __setup "stacktrace", enable_stacktrace trace_stack.c 330 early_initcall tracer_alloc_buffers trace.c 4478 early_initcall init_trace_printk trace_printk.c 251 early_initcall trace_workqueue_early_init trace_workqueue.c 295 fs_initcall ftrace_init_debugfs ftrace.c 2953 fs_initcall rb_init_debugfs ring_buffer.c 3837 fs_initcall init_annotated_branch_stats trace_branch.c 335 fs_initcall all_annotated_branch_stats trace_branch.c 391 fs_initcall tracer_init_debugfs trace.c 4479 fs_initcall event_trace_init trace_events.c 1239 fs_initcall init_trace_printk_function_export trace_printk.c 244 fs_initcall stat_workqueue_init trace_workqueue.c 251 module_init ring_buffer_benchmark_init ring_buffer_benchmark.c 414 device_initcall init_blk_tracer blktrace.c 1404 device_initcall ftrace_mod_cmd_init ftrace.c 1892 device_initcall ftrace_nodyn_init ftrace.c 2770 device_initcall init_kmem_tracer kmemtrace.c 511 device_initcall init_branch_tracer trace_branch.c 198 device_initcall init_function_trace trace_functions.c 402 device_initcall init_graph_trace trace_functions_graph.c 1076 device_initcall init_bts_trace trace_hw_branches.c 311 device_initcall init_irqsoff_tracer trace_irqsoff.c 482 device_initcall init_mmio_trace trace_mmiotrace.c 304 device_initcall init_events trace_output.c 1233 device_initcall init_sched_switch_trace trace_sched_switch.c 297 device_initcall init_wakeup_tracer trace_sched_wakeup.c 415 device_initcall stack_trace_init trace_stack.c 350 device_initcall init_stack_trace trace_sysprof.c 271 late_initcall clear_boot_tracer trace.c 4480 late_initcall event_trace_self_tests_init trace_events.c 1462
分析下function tracer的注册流程,这里涉及到对指令的修改,就像ftrace_init中将call mcount指令修改为空指令
function_trace“对象":
static struct tracer function_trace __read_mostly = { .name = "function", .init = function_trace_init, .reset = function_trace_reset, .start = function_trace_start, .wait_pipe = poll_wait_pipe, .flags = &func_flags, .set_flag = func_set_flag, +-- 3 lines: #ifdef CONFIG_FTRACE_SELFTEST------------ };
init_function_trace |-->init_func_cmd_traceon(); |-->register_ftrace_command(&ftrace_traceoff_cmd); |-->register_ftrace_command(&ftrace_traceon_cmd); |-->return register_tracer(&function_trace);
int register_tracer(struct tracer *type) |-->struct tracer *t = NULL; |-->int ret = 0; |-->for (t = trace_types; t; t = t->next) { |-->扫描trace_types链表,检查是否是重复注册 |-->} | |-->if (!type->set_flag) type->set_flag = &dummy_set_flag; |-->if (!type->flags) type->flags = &dummy_tracer_flags; |-->else if (!type->flags->opts) type->flags->opts = dummy_tracer_opt; |-->if (!type->wait_pipe) type->wait_pipe = default_wait_pipe; |-->type->next = trace_types; trace_types = type; |-->if (ret || !default_bootup_tracer) ... //注意set_ftrace参数解析函数,此处假定"ftrace=function" |-->if (strncmp(default_bootup_tracer, type->name, ...) ... |-->tracing_set_tracer(type->name); |-->default_bootup_tracer = NULL;
static int tracing_set_tracer(const char *buf) |-->struct trace_array *tr = &global_trace; |-->struct tracer *t; //根据buf和trace_types链表找到相应的tracer; |-->current_trace = t; |-->if (t->init) tracer_init(t, tr); int tracer_init(struct tracer *t, struct trace_array *tr) |-->tracing_reset_online_cpus(tr); |-->return t->init(tr); | //function_trace.init(&global_trace); | //function_trace_init(&global_trace);
struct trace_array *func_trace = NULL; unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | \ TRACE_ITER_PRINTK | \ TRACE_ITER_ANNOTATE | \ TRACE_ITER_CONTEXT_INFO | \ TRACE_ITER_SLEEP_TIME | \ TRACE_ITER_GRAPH_TIME;
static int function_trace_init(struct trace_array *tr) |-->func_trace = tr; |-->tr->cpu = get_cpu(); |-->put_cpu(); |-->tracing_start_cmdline_record(); |-->tracing_sched_register(); |-->register_trace_sched_wakeup(probe_sched_wakeup); //宏拼接 register_trace_## 对于这几个register_trace_sched并未分析 |-->register_trace_sched_wakeup_new(probe_sched_wakeup); |-->register_trace_sched_switch(probe_sched_switch); |-->tracing_start_function_trace();
static void tracing_start_function_trace(void) |-->if (trace_flags & TRACE_ITER_PREEMPTONLY) |--> trace_ops.func = function_trace_call_preempt_only; |-->else |--> trace_ops.func = function_trace_all; |-->if (func_flags.val & TRACE_FUNC_OPT_STACK) |--> register_ftrace_function(&trace_stack_ops); |-->else |--> register_ftrace_function(&trace_ops);
//Register a function to be called by all functions in the kernel int register_ftrace_function(struct ftrace_ops *ops) |-->int ret = __register_ftrace_function(ops); |-->ops->next = ftrace_list; |-->ftrace_list = ops; |-->if (ftrace_enabled) { |--> ftrace_func_t func; |--> if (ops->next == &ftrace_list_end) func = ops->func; |--> else func = ftrace_list_func; |--> if (ftrace_pid_trace) { set_ftrace_pid_function(func); func = ftrace_pid_func; } |--> ftrace_trace_function = func; // function_trace_all; |-->} |-->ftrace_startup(0); |-->ftrace_startup_enable(FTRACE_ENABLE_CALLS); |-->if(saved_ftrace_func != ftrace_trace_function) { |--> saved_ftrace_func = ftrace_trace_function; |--> command |= FTRACE_UPDATE_TRACE_FUNC; |--> //FTRACE_UPDATE_TRACE_FUNC | FTRACE_ENABLE_CALLS; |--> ftrace_run_update_code(command); |-->}
void ftrace_run_update_code(int command) |-->int ret = ftrace_arch_code_modify_prepare(); //例如修改text段属性,使其可写 |-->stop_machine( __ftrace_modify_code, &command, NULL); //注意smp架构,此处对于stop_machine对了简化分析 | //command = FTRACE_UPDATE_TRACE_FUNC | FTRACE_ENABLE_CALLS; |-->ret = ftrace_arch_code_modify_post_process();
int __ftrace_modify_code(void *data) |-->int *command = data; |-->if (*command & FTRACE_ENABLE_CALLS) |--> ftrace_replace_code(1); // go |-->else if (*command & FTRACE_DISABLE_CALLS) |--> ftrace_replace_code(0); | |-->if (*command & FTRACE_UPDATE_TRACE_FUNC) |--> ftrace_update_ftrace_func(ftrace_trace_function); //go | |-->if (*comand & FTRACE_START_FUNC_RET) |--> ftrace_enable_ftrace_graph_caller(); |-->else if (*command & FTRACE_STOP_FUNC_RET) |--> ftrace_disable_ftrace_graph_caller();
void ftrace_replace_code(int enable) |-->struct dyn_ftrace *rec; |-->strcut ftrace_page *pg; |-->int failed; |-->do_for_each_ftrace_rec(pg, rec) { |--> unfreeze_record(rec); |--> failed = __ftrace_replace_code(rec, enable); |-->} while_for_each_ftrace_rec();
int __ftrace_replace_code(struct dyn_ftrace *rec, int enable) |-->unsigned long ftrace_addr, flag = 0UL; |-->ftrace_addr = (unsigned long)FTRACE_ADDR; | //#define FTRACE_ADDR ((unsigned long)ftrace_caller) | |-->if (enable && !(rec->flags & FTRACE_FL_NOTRACE)) { |--> if (!ftrace_filtered || (rec->flags & FTRACE_FL_FILTER)) |--> flag = FTRACE_FL_ENABLED; |--> //也可以看出FTRACE_FL_FILTER是把设置为filter的存储下来 |-->} |-->if ((rec->flags & FTRACE_FL_ENABLED) == flag) return 0; |-->if (flag) { |--> rec->flags |= FTRACE_FL_ENABLED; |--> return ftrace_make_call(rec, ftrace_addr); |--> //1. 编译链接时为 call mcount |--> //2. ftrace_init中将其改为nop |--> //3. 此处再将其改为 call ftrace_addr:ftrace_caller |--> //问题来了: |--> // 1)ftrace_init中,将ftrace_stub处的指令内容改为0x00000000(以x86为例) |--> // 2)对于0x00000000 x86是如何处理的???? |--> // 3)对于ARM架构ftrace_dyn_arch_init是如何实现的?其调用的ftrace_mcount_set并未找到定义
int ftrace_update_ftrace_func(ftrace_func_t func) // 参数function_trace_all |-->unsigned long ip = (unsigned long) (&ftrace_call);//注意,并不是ftrace_caller,刚开始老是搞混 |-->unsinged char old[MCOUNT_INSN_SIZE], *new; |-->int ret; |-->memcpy(old, &ftrace_call, MCOUNT_INSN_SIZE); 将ftrace_call处指令内容拷贝到old数组中 |-->new = ftrace_call_replace(ip, (unsigned long)func); | //构建一条指令,使得相应ip地址处的call指令变为call func; |-->ret = ftrace_modify_code(ip, old, new); | //按照我们自上而下的分析,即将ftrace_call的调用指令改为:call function_trace_all; //但是仍然没有解决将ftrace_stub处指令修改为0x00000000的问题
#if defined(CONFIG_FUNCTION_TRACER) && defined(CONFIG_DYNAMIC_FTRACER) ENTRY(mcount) retq END(mcount) ENTRY(ftrace_caller) cmpl $0, function_trace_stop jne ftrace_stub MCOUNT_SAVE_FRAME movq 0x38(%rsp), %rdi movq 8(%rbp), %rsi subq $MCOUNT_INSN_SIZE, %rdi GLOBAL(ftrace_call) call ftrace_stub ;call function_trace_all MCOUNT_RESTORE_FRAME #ifdef CONFIG_FUNCTION_GRAPH_TRACER GLOBAL(ftrace_graph_call) jmp ftrace_stub #endif GLOBAL(ftrace_stub) retq ;0x00000000 ????????? END(ftrace_caller) #endif
#if !defined(_TRACE_TIMER_H) || defined(TRACE_HEADER_MULTI_READ) ENTRY(mcount) cmpl $0, function_trace_stop jne ftrace_stub cmpq $ftrace_stub, ftrace_trace_function jnz trace #ifdef CONFIG_FUNCTION_GRAPH_TRACER cmpq $ftrace_stub, ftrace_graph_return jnz ftrace_graph_caller cmpq $ftrace_graph_entry_stub, ftrace_graph_entry jnz ftrace_graph_caller #endif GLOBAL(ftrace_stub) retq trace: MCOUNT_SAVE_FRAME movq 0x38(%rsp), %rdi movq 8(%rbp), %rsi subq $MCOUNT_INSN_SIZE, %rdi call *ftrace_trace_function MCOUNT_RESTORE_FRAME jmp ftrace_stub END(mcount) #endif
.macro MCOUNT_SAVE_FRAME /* taken from glibc */ subq $0x38, %rsp movq %rax, (%rsp) movq %rcx, 8(%rsp) movq %rdx, 16(%rsp) movq %rsi, 24(%rsp) movq %rdi, 32(%rsp) movq %r8, 40(%rsp) movq %r9, 48(%rsp) .endm .macro MCOUNT_RESTORE_FRAME movq 48(%rsp), %r9 movq 40(%rsp), %r8 movq 32(%rsp), %rdi movq 24(%rsp), %rsi movq 16(%rsp), %rdx movq 8(%rsp), %rcx movq (%rsp), %rax addq $0x38, %rsp .endm
问题:
1)为什么ftrace_stub出的指令内容被修该为0x00000000呢?ARM架构中对ftrace_stub是如何处理的?
希望这是我自己错了
2)MCOUNT_(SAVE\|RESTORE)_FRAEM和ABI有关。
3)对于function tracer,现在需要看下function_trace_all的实现
4)通过设置filter,可以过滤掉/出某些函数
5)function_graph 和 function tracer貌似类似
6)irq off tracer应该可以单独作为一类,但是原理应该和function tracer差不多
7)event tracer实现原理貌似和function tracer不同,这个比较复杂。
2014-12-21 23:08
实在不能在ftrace上的实现上投入太多时间,还有很多bug要解,还有看不完的data sheet……
浙公网安备 33010602011771号