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……

 

posted on 2014-12-21 23:08  阿加  阅读(1642)  评论(0)    收藏  举报

导航