Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libbpf: Use available_filter_functions_addrs with multi-kprobes

Now that kernel provides a new available_filter_functions_addrs file
which can help us avoid the need to cross-validate
available_filter_functions and kallsyms, we can improve efficiency of
multi-attach kprobes. For example, on my device, the sample program [1]
of start time:

$ sudo ./funccount "tcp_*"

before after
1.2s 1.0s

[1]: https://github.com/JackieLiu1/ketones/tree/master/src/funccount

Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20230705091209.3803873-2-liu.yun@linux.dev

authored by

Jackie Liu and committed by
Andrii Nakryiko
56baeeba 8a3fe76f

+61 -1
+61 -1
tools/lib/bpf/libbpf.c
··· 10234 10234 : TRACEFS"/available_filter_functions"; 10235 10235 } 10236 10236 10237 + static const char *tracefs_available_filter_functions_addrs(void) 10238 + { 10239 + return use_debugfs() ? DEBUGFS"/available_filter_functions_addrs" 10240 + : TRACEFS"/available_filter_functions_addrs"; 10241 + } 10242 + 10237 10243 static void gen_kprobe_legacy_event_name(char *buf, size_t buf_sz, 10238 10244 const char *kfunc_name, size_t offset) 10239 10245 { ··· 10656 10650 return err; 10657 10651 } 10658 10652 10653 + static bool has_available_filter_functions_addrs(void) 10654 + { 10655 + return access(tracefs_available_filter_functions_addrs(), R_OK) != -1; 10656 + } 10657 + 10658 + static int libbpf_available_kprobes_parse(struct kprobe_multi_resolve *res) 10659 + { 10660 + const char *available_path = tracefs_available_filter_functions_addrs(); 10661 + char sym_name[500]; 10662 + FILE *f; 10663 + int ret, err = 0; 10664 + unsigned long long sym_addr; 10665 + 10666 + f = fopen(available_path, "re"); 10667 + if (!f) { 10668 + err = -errno; 10669 + pr_warn("failed to open %s: %d\n", available_path, err); 10670 + return err; 10671 + } 10672 + 10673 + while (true) { 10674 + ret = fscanf(f, "%llx %499s%*[^\n]\n", &sym_addr, sym_name); 10675 + if (ret == EOF && feof(f)) 10676 + break; 10677 + 10678 + if (ret != 2) { 10679 + pr_warn("failed to parse available_filter_functions_addrs entry: %d\n", 10680 + ret); 10681 + err = -EINVAL; 10682 + goto cleanup; 10683 + } 10684 + 10685 + if (!glob_match(sym_name, res->pattern)) 10686 + continue; 10687 + 10688 + err = libbpf_ensure_mem((void **)&res->addrs, &res->cap, 10689 + sizeof(*res->addrs), res->cnt + 1); 10690 + if (err) 10691 + goto cleanup; 10692 + 10693 + res->addrs[res->cnt++] = (unsigned long)sym_addr; 10694 + } 10695 + 10696 + if (res->cnt == 0) 10697 + err = -ENOENT; 10698 + 10699 + cleanup: 10700 + fclose(f); 10701 + return err; 10702 + } 10703 + 10659 10704 struct bpf_link * 10660 10705 bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog, 10661 10706 const char *pattern, ··· 10743 10686 return libbpf_err_ptr(-EINVAL); 10744 10687 10745 10688 if (pattern) { 10746 - err = libbpf_available_kallsyms_parse(&res); 10689 + if (has_available_filter_functions_addrs()) 10690 + err = libbpf_available_kprobes_parse(&res); 10691 + else 10692 + err = libbpf_available_kallsyms_parse(&res); 10747 10693 if (err) 10748 10694 goto error; 10749 10695 addrs = res.addrs;