]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
libbpf: Use available_filter_functions_addrs with multi-kprobes
authorJackie Liu <liuyun01@kylinos.cn>
Wed, 5 Jul 2023 09:12:09 +0000 (17:12 +0800)
committerAndrii Nakryiko <andrii@kernel.org>
Thu, 6 Jul 2023 23:05:08 +0000 (16:05 -0700)
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
tools/lib/bpf/libbpf.c

index 924bf52f06e4369e5b43931e04ed170863dc6be2..fd4f1875df65335d56070200c33fb6fec0565721 100644 (file)
@@ -10234,6 +10234,12 @@ static const char *tracefs_available_filter_functions(void)
                             : TRACEFS"/available_filter_functions";
 }
 
+static const char *tracefs_available_filter_functions_addrs(void)
+{
+       return use_debugfs() ? DEBUGFS"/available_filter_functions_addrs"
+                            : TRACEFS"/available_filter_functions_addrs";
+}
+
 static void gen_kprobe_legacy_event_name(char *buf, size_t buf_sz,
                                         const char *kfunc_name, size_t offset)
 {
@@ -10650,6 +10656,57 @@ cleanup:
        return err;
 }
 
+static bool has_available_filter_functions_addrs(void)
+{
+       return access(tracefs_available_filter_functions_addrs(), R_OK) != -1;
+}
+
+static int libbpf_available_kprobes_parse(struct kprobe_multi_resolve *res)
+{
+       const char *available_path = tracefs_available_filter_functions_addrs();
+       char sym_name[500];
+       FILE *f;
+       int ret, err = 0;
+       unsigned long long sym_addr;
+
+       f = fopen(available_path, "re");
+       if (!f) {
+               err = -errno;
+               pr_warn("failed to open %s: %d\n", available_path, err);
+               return err;
+       }
+
+       while (true) {
+               ret = fscanf(f, "%llx %499s%*[^\n]\n", &sym_addr, sym_name);
+               if (ret == EOF && feof(f))
+                       break;
+
+               if (ret != 2) {
+                       pr_warn("failed to parse available_filter_functions_addrs entry: %d\n",
+                               ret);
+                       err = -EINVAL;
+                       goto cleanup;
+               }
+
+               if (!glob_match(sym_name, res->pattern))
+                       continue;
+
+               err = libbpf_ensure_mem((void **)&res->addrs, &res->cap,
+                                       sizeof(*res->addrs), res->cnt + 1);
+               if (err)
+                       goto cleanup;
+
+               res->addrs[res->cnt++] = (unsigned long)sym_addr;
+       }
+
+       if (res->cnt == 0)
+               err = -ENOENT;
+
+cleanup:
+       fclose(f);
+       return err;
+}
+
 struct bpf_link *
 bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog,
                                      const char *pattern,
@@ -10686,7 +10743,10 @@ bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog,
                return libbpf_err_ptr(-EINVAL);
 
        if (pattern) {
-               err = libbpf_available_kallsyms_parse(&res);
+               if (has_available_filter_functions_addrs())
+                       err = libbpf_available_kprobes_parse(&res);
+               else
+                       err = libbpf_available_kallsyms_parse(&res);
                if (err)
                        goto error;
                addrs = res.addrs;