听闻 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 调试指令。