fentry 示例使用 fentry 和 fexit 类型的 BPF 程序进行跟踪。它会将 fentry 和 fexit 跟踪器附加到 do_unlinkat() 函数上,此函数会在文件被删除时被调用,并且会将函数的返回值、进程标识符(PID)以及文件名记录到跟踪管道(trace pipe)中。
与 kprobes 相比,增强了性能和可用性
能够像 c 语言一样直接解引用指针
fexit 不仅可以访问入参,还可以访问返回值;而 kretprobe 只能访问返回值
NOTE:
内核版本 5.5 开始支持 fentry 和 fexit
运行 1 2 3 $ ./fentry libbpf: loading object 'fentry_bpf' from buffer ...
1 2 3 $ cat /sys/kernel/debug/tracing/trace_pipe <...>-45337 [004] ...11 107881.547637: bpf_trace_printk: fentry: pid = 45337, filename = test_file <...>-45337 [004] ...11 107881.547704: bpf_trace_printk: fexit: pid = 45337, filename = test_file, ret = 0
1 2 3 $ cd ~ $ touch test_file $ rm -rf test_file
The BPF side SEC(“fentry/do_unlinkat”) 1 2 3 4 5 6 7 8 9 SEC("fentry/do_unlinkat" ) int BPF_PROG (do_unlinkat, int dfd, struct filename *name) { pid_t pid; pid = bpf_get_current_pid_tgid() >> 32 ; bpf_printk("fentry: pid = %d, filename = %s\n" , pid, name->name); return 0 ; }
BPF_PROG(do_unlinkat, int dfd, struct filename *name) BPF 程序的函数签名为 do_unlinkat,输入参数为 dfd 和 name。
args 函数的入参 int dfd, struct filename *name 是如何确定的?
do_unlinkat 是内核函数,查询Linux 源码 可获知该函数具有 dfd 与 name 两个输入参数、返回值为一个 int 类型的值。
SEC(“fexit/do_unlinkat”) 1 2 3 4 5 6 7 8 9 SEC("fexit/do_unlinkat" ) int BPF_PROG (do_unlinkat_exit, int dfd, struct filename *name, long ret) { pid_t pid; pid = bpf_get_current_pid_tgid() >> 32 ; bpf_printk("fexit: pid = %d, filename = %s, ret = %ld\n" , pid, name->name, ret); return 0 ; }
The user-space side open_and_load 1 2 3 4 5 6 skel = fentry_bpf__open_and_load(); if (!skel) { fprintf (stderr , "Failed to open BPF skeleton\n" ); return 1 ; }
attach 1 2 3 4 5 6 err = fentry_bpf__attach(skel); if (err) { fprintf (stderr , "Failed to attach BPF skeleton\n" ); goto cleanup; }
while 1 2 3 4 while (!stop) { fprintf (stderr , "." ); sleep(1 ); }
cleanup 1 2 cleanup: fentry_bpf__destroy(skel);