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

libbpf: Cross-join available_filter_functions and kallsyms for multi-kprobes

When using regular expression matching with "kprobe multi", it scans all
the functions under "/proc/kallsyms" that can be matched. However, not all
of them can be traced by kprobe.multi. If any one of the functions fails
to be traced, it will result in the failure of all functions. The best
approach is to filter out the functions that cannot be traced to ensure
proper tracking of the functions.

Closes: https://lore.kernel.org/oe-kbuild-all/202307030355.TdXOHklM-lkp@intel.com/
Reported-by: kernel test robot <lkp@intel.com>
Suggested-by: Jiri Olsa <jolsa@kernel.org>
Suggested-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Signed-off-by: Jackie Liu <liuyun01@kylinos.cn>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20230705091209.3803873-1-liu.yun@linux.dev

authored by

Jackie Liu and committed by
Andrii Nakryiko
8a3fe76f e76a0143

+97 -13
+97 -13
tools/lib/bpf/libbpf.c
··· 10228 10228 return use_debugfs() ? DEBUGFS"/uprobe_events" : TRACEFS"/uprobe_events"; 10229 10229 } 10230 10230 10231 + static const char *tracefs_available_filter_functions(void) 10232 + { 10233 + return use_debugfs() ? DEBUGFS"/available_filter_functions" 10234 + : TRACEFS"/available_filter_functions"; 10235 + } 10236 + 10231 10237 static void gen_kprobe_legacy_event_name(char *buf, size_t buf_sz, 10232 10238 const char *kfunc_name, size_t offset) 10233 10239 { ··· 10549 10543 size_t cnt; 10550 10544 }; 10551 10545 10552 - static int 10553 - resolve_kprobe_multi_cb(unsigned long long sym_addr, char sym_type, 10554 - const char *sym_name, void *ctx) 10546 + struct avail_kallsyms_data { 10547 + char **syms; 10548 + size_t cnt; 10549 + struct kprobe_multi_resolve *res; 10550 + }; 10551 + 10552 + static int avail_func_cmp(const void *a, const void *b) 10555 10553 { 10556 - struct kprobe_multi_resolve *res = ctx; 10554 + return strcmp(*(const char **)a, *(const char **)b); 10555 + } 10556 + 10557 + static int avail_kallsyms_cb(unsigned long long sym_addr, char sym_type, 10558 + const char *sym_name, void *ctx) 10559 + { 10560 + struct avail_kallsyms_data *data = ctx; 10561 + struct kprobe_multi_resolve *res = data->res; 10557 10562 int err; 10558 10563 10559 - if (!glob_match(sym_name, res->pattern)) 10564 + if (!bsearch(&sym_name, data->syms, data->cnt, sizeof(*data->syms), avail_func_cmp)) 10560 10565 return 0; 10561 10566 10562 - err = libbpf_ensure_mem((void **) &res->addrs, &res->cap, sizeof(unsigned long), 10563 - res->cnt + 1); 10567 + err = libbpf_ensure_mem((void **)&res->addrs, &res->cap, sizeof(*res->addrs), res->cnt + 1); 10564 10568 if (err) 10565 10569 return err; 10566 10570 10567 - res->addrs[res->cnt++] = (unsigned long) sym_addr; 10571 + res->addrs[res->cnt++] = (unsigned long)sym_addr; 10568 10572 return 0; 10573 + } 10574 + 10575 + static int libbpf_available_kallsyms_parse(struct kprobe_multi_resolve *res) 10576 + { 10577 + const char *available_functions_file = tracefs_available_filter_functions(); 10578 + struct avail_kallsyms_data data; 10579 + char sym_name[500]; 10580 + FILE *f; 10581 + int err = 0, ret, i; 10582 + char **syms = NULL; 10583 + size_t cap = 0, cnt = 0; 10584 + 10585 + f = fopen(available_functions_file, "re"); 10586 + if (!f) { 10587 + err = -errno; 10588 + pr_warn("failed to open %s: %d\n", available_functions_file, err); 10589 + return err; 10590 + } 10591 + 10592 + while (true) { 10593 + char *name; 10594 + 10595 + ret = fscanf(f, "%499s%*[^\n]\n", sym_name); 10596 + if (ret == EOF && feof(f)) 10597 + break; 10598 + 10599 + if (ret != 1) { 10600 + pr_warn("failed to parse available_filter_functions entry: %d\n", ret); 10601 + err = -EINVAL; 10602 + goto cleanup; 10603 + } 10604 + 10605 + if (!glob_match(sym_name, res->pattern)) 10606 + continue; 10607 + 10608 + err = libbpf_ensure_mem((void **)&syms, &cap, sizeof(*syms), cnt + 1); 10609 + if (err) 10610 + goto cleanup; 10611 + 10612 + name = strdup(sym_name); 10613 + if (!name) { 10614 + err = -errno; 10615 + goto cleanup; 10616 + } 10617 + 10618 + syms[cnt++] = name; 10619 + } 10620 + 10621 + /* no entries found, bail out */ 10622 + if (cnt == 0) { 10623 + err = -ENOENT; 10624 + goto cleanup; 10625 + } 10626 + 10627 + /* sort available functions */ 10628 + qsort(syms, cnt, sizeof(*syms), avail_func_cmp); 10629 + 10630 + data.syms = syms; 10631 + data.res = res; 10632 + data.cnt = cnt; 10633 + libbpf_kallsyms_parse(avail_kallsyms_cb, &data); 10634 + 10635 + if (res->cnt == 0) 10636 + err = -ENOENT; 10637 + 10638 + cleanup: 10639 + for (i = 0; i < cnt; i++) 10640 + free((char *)syms[i]); 10641 + free(syms); 10642 + 10643 + fclose(f); 10644 + return err; 10569 10645 } 10570 10646 10571 10647 struct bpf_link * ··· 10686 10598 return libbpf_err_ptr(-EINVAL); 10687 10599 10688 10600 if (pattern) { 10689 - err = libbpf_kallsyms_parse(resolve_kprobe_multi_cb, &res); 10601 + err = libbpf_available_kallsyms_parse(&res); 10690 10602 if (err) 10691 10603 goto error; 10692 - if (!res.cnt) { 10693 - err = -ENOENT; 10694 - goto error; 10695 - } 10696 10604 addrs = res.addrs; 10697 10605 cnt = res.cnt; 10698 10606 }