天天看点

mcount 和 ftrace_caller1. mcount2. ftrace_caller

1. mcount

1.1 没有开启CONFIG_DYNAMIC_FTRACE

所有没有标记为no_trace的函数都会加上一条指令:call mcount。

所以这时mcount是和类似ftrace_caller的函数。也就是所有这些函数都会被trace。

1.2 开启CONFIG_DYNAMIC_FTRACE

内核编译每一个源文件后,生成一个.o,都会用recordmcount.pl脚本把有call mcount指令的函数

的地址添加到mcount_loc段中;

注意:并不是所有有call mcount指令的函数都被添加,只有一些常规段中的函数。

所以有些函数没有no_trace标记,有call_mcount指令,ftrace不想trace这些函数(不安全),

那么这个脚本会把所有这些函数替换成nop指令。并且这部分函数不会记录到mcount_loc段。

那么这时mcount只是ret指令,因为这些函数需要开trace时,会把nop替换成ftrace_caller。

这个函数其实和1.1的mcount函数功能一样。

2. ftrace_caller

2.1 ftrace_caller简介

ftrace_caller的作用就是在函数需要被trace时加入到函数的开始地址,也就是增加一个调用链;

下面先讨论这个调用链可以如何被修改。

2.2 修改调用链的机制

函数跳转到ftrace_caller后,可以通过修改ftrace_call这条指令来修改;当然这只是针对开启dynamic的情况。

对于静态调用mcount的函数,需要修改不同的函数。

所以记住:第一可以修改某个函数的nop为call ftrace_caller;第二还可以修改其中的ftrace_call来调用具体的函数,即调用链的变化。

但修改秩序应该是:先修改增加的这个调用链,再修改那些函数需要关闭调用链,那些函数需要开启调用链。

2.3 注册ftrace_ops到ftrace

函数入口为:register_ftrace_function;这个函数根据要注册的ftrace_ops先准备好,最后修改ftrace_call和修改函数的nop或ftrace_caller;

这里先关心最后两个步骤:ftrace_startup_enable(int command)根据情况设置好command后直接调用了ftrace_run_update_code(int command)。

2.4 ftrace_run_update_code(int command)

2.4.1 ftrace_arch_code_modify_prepare

因为修改调用链,开启或关闭函数调用链,都需要对代码段进行修改,所以这个函数先设置代码段为可读写;

2.4.2 arch_ftrace_update_code

修改调用链,开启或关闭函数调用,当然这里要保证并发安全,所以用了stop_machine,使其他cpu都停止运行;

虽然这最安全,但开销最大,所以可以采用其他的方法,可以看看x86架构的实现;

2.4.3 ftrace_arch_code_modify_post_process

修改完成,最后把代码段的属性设置成只读。

2.5 arch_ftrace_update_code

这个函数首先保证安全,如用stop_machine机制,然后直接调用了ftrace_modify_all_code(int command);

command命令主要有三种:

FTRACE_UPDATE_CALLS 表示开启函数的调用链,至于那些函数需要开启,只有通过全局变量来传递;

FTRACE_DISABLE_CALLS 表示关闭函数的调用链,至于那些函数需要关闭,只有通过全局变量来传递;

FTRACE_UPDATE_TRACE_FUNC 表示修改这个调用链;

一般是修改成ftrace_ops_list_func,这个函数只会调用包含这个ip的 ftrace_ops 的 func,有多个ftrace_ops存在就是这种情况;

另一种是修改成一个ftrace_ops的func,但只有一个ftrace_ops注册就是这种情况;

但这种情况要注意,开启或关闭函数的调用链之前,调用链必须先到ftrace_ops_list_func,不然这个ftrace_ops的func会捕捉到错误的ip。

2.5.1 调用链直接到一个ftrace_ops的func

这种情况时ftrace_caller会传入第三个参数function_trace_op表示这个func所属的ftrace_ops,这样函数内可以判断ip是否在ftrace_ops的范围内;另外还注意这种前后设置相互依赖是如何解决的。

开启或关闭函数的调用链 与 调用链改变的依赖;

调用链改变到func时 与 传入参数function_trace_op改变的依赖;