听闻 ftrace 也是基于 trampoline 实现的。而且,最近打算使用 kprobe 开发个工具,所以就将快速翻看了一下 ftrace 和 kprobe 的底层源代码。
P.S. 不介绍 ftrace 的使用,使用文档请看 ftrace - Function Tracer。
ftrace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
register_ftrace_function() // ${KERNEL}/kernel/trace/ftrace.c
|-->register_ftrace_function_nolock()
|-->ftrace_startup()
|-->__register_ftrace_function()
| |-->ftrace_update_trampoline()
| |-->arch_ftrace_update_trampoline() // ${KERNEL}/arch/x86/kernel/ftrace.c
| |-->ftrace_call_replace();
| |-->text_poke_bp();
|-->ftrace_startup_enable()
|-->ftrace_run_update_code()
|-->arch_ftrace_update_code()
|-->ftrace_modify_all_code()
|-->ftrace_modify_all_code()
|-->ftrace_call_replace()
|-->text_poke_bp()
|
当看到 text_poke_bp()
函数时,可以推断出 ftrace 的底层实现就是基于 poke
的一种 trampoline
实现。
kprobe
1
2
3
4
5
6
7
8
|
register_kprobe() // ${KERNEL}/kernel/kprobes.c
|-->prepare_kprobe()
| |-->arch_prepare_kprobe_ftrace() // ${KERNEL}/arch/x86/kprobes/ftrace.c
|-->arm_kprobe()
|-->arm_kprobe_ftrace()
|-->__arm_kprobe_ftrace()
|-->ftrace_set_filter_ip() // ${KERNEL}/kernel/trace/ftrace.c
|-->register_ftrace_function()
|
这代码片段展示的是基于 ftrace 实现的 kprobe。
不过,eBPF kprobe 使用的并不是基于 ftrace 的 kprobe,而是基于 int3
调试指令实现的 kprobe。
使用 tracefs /sys/kernel/debug
中的 kprobe 时,内核函数调用栈如下:
1
2
3
4
5
6
7
8
|
trace_kprobe_run_command()
|-->create_or_delete_trace_kprobe()
|-->trace_kprobe_create()
|-->__trace_kprobe_create()
|-->register_trace_kprobe()
|-->__register_trace_kprobe()
|-->register_kretprobe()
|-->register_kprobe()
|
快速翻看了下,在这执行过程中并没有设置使用 ftrace 的 flag KPROBE_FLAG_FTRACE
。
所以可以推断,平常使用的 kprobe 并不是基于 ftrace 实现的。
内核给 kprobe 提供 flag KPROBE_FLAG_FTRACE
,不就白费了?
非也,毕竟可以在内核模块里使用 register_kprobe()
时指定该 flag。
总结
快速翻看某个特性的源代码,是快速了解该特性底层实现的有效方法。
不过就是需要先理解该特性依赖的底层技术。比如,kprobe 所使用的 ftrace 或者 int3
调试指令。