libbpf-tools: add two helpers
authorWenbo Zhang <ethercflow@gmail.com>
Sat, 24 Apr 2021 00:04:03 +0000 (08:04 +0800)
committeryonghong-song <ys114321@gmail.com>
Sun, 25 Apr 2021 17:06:14 +0000 (10:06 -0700)
Signed-off-by: Wenbo Zhang <ethercflow@gmail.com>
libbpf-tools/trace_helpers.c
libbpf-tools/trace_helpers.h

index 1a8fafc9ec0398d218292b662701d48dca7c6762..21538af15136827ef534edc7fa194bf0f0232fba 100644 (file)
@@ -6,8 +6,11 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 #include <sys/resource.h>
 #include <time.h>
+#include <bpf/btf.h>
+#include <bpf/libbpf.h>
 #include "trace_helpers.h"
 
 #define min(x, y) ({                            \
@@ -395,3 +398,105 @@ bool is_kernel_module(const char *name)
        fclose(f);
        return found;
 }
+
+bool fentry_exists(const char *name, const char *mod)
+{
+       const char sysfs_vmlinux[] = "/sys/kernel/btf/vmlinux";
+       struct btf *base, *btf = NULL;
+       const struct btf_type *type;
+       const struct btf_enum *e;
+       char sysfs_mod[80];
+       int id = -1, i;
+
+       base = btf__parse(sysfs_vmlinux, NULL);
+       if (libbpf_get_error(base)) {
+               fprintf(stderr, "failed to parse vmlinux BTF at '%s': %s\n",
+                       sysfs_vmlinux, strerror(-libbpf_get_error(base)));
+               goto err_out;
+       }
+       if (mod) {
+               snprintf(sysfs_mod, sizeof(sysfs_mod), "/sys/kernel/btf/%s", mod);
+               btf = btf__parse_split(sysfs_mod, base);
+               if (libbpf_get_error(btf)) {
+                       fprintf(stderr, "failed to load BTF from %s: %s\n",
+                               sysfs_mod, strerror(-libbpf_get_error(btf)));
+                       goto err_out;
+               }
+       } else {
+               btf = base;
+       }
+
+       id = btf__find_by_name_kind(btf, "bpf_attach_type", BTF_KIND_ENUM);
+       if (id < 0)
+               goto err_out;
+       type = btf__type_by_id(btf, id);
+
+       /*
+         * As kernel BTF is exposed starting from 5.4 kernel, but fentry/fexit
+         * is actually supported starting from 5.5, so that's check this gap
+         * first, then check if target func has btf type.
+        */
+       for (id = -1, i = 0, e = btf_enum(type); i < btf_vlen(type); i++, e++) {
+               if (!strcmp(btf__name_by_offset(btf, e->name_off),
+                           "BPF_TRACE_FENTRY")) {
+                       id = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
+                       break;
+               }
+       }
+
+err_out:
+       if (mod)
+               btf__free(btf);
+       btf__free(base);
+       return id > 0;
+}
+
+bool kprobe_exists(const char *name)
+{
+       char sym_name[256];
+       FILE *f;
+       int ret;
+
+       f = fopen("/sys/kernel/debug/tracing/available_filter_functions", "r");
+       if (!f)
+               goto slow_path;
+
+       while (true) {
+               ret = fscanf(f, "%s%*[^\n]\n", sym_name);
+               if (ret == EOF && feof(f))
+                       break;
+               if (ret != 1) {
+                       fprintf(stderr, "failed to read symbol from available_filter_functions\n");
+                       break;
+               }
+               if (!strcmp(name, sym_name)) {
+                       fclose(f);
+                       return true;
+               }
+       }
+
+       fclose(f);
+       return false;
+
+slow_path:
+       f = fopen("/proc/kallsyms", "r");
+       if (!f)
+               return false;
+
+       while (true) {
+               ret = fscanf(f, "%*x %*c %s%*[^\n]\n", sym_name);
+               if (ret == EOF && feof(f))
+                       break;
+               if (ret != 1) {
+                       fprintf(stderr, "failed to read symbol from kallsyms\n");
+                       break;
+               }
+               if (!strcmp(name, sym_name)) {
+                       fclose(f);
+                       return true;
+               }
+       }
+
+       fclose(f);
+       return false;
+}
index f4fbb849841f5a80706e68c47d7f7eba68cca1b8..8dc7c1c0134ad745406862e311f0043cbef0199a 100644 (file)
@@ -43,4 +43,34 @@ int bump_memlock_rlimit(void);
 
 bool is_kernel_module(const char *name);
 
+/*
+ * When attempting to use kprobe/kretprobe, please check out new fentry/fexit
+ * probes, as they provide better performance and usability. But in some
+ * situations we have to fallback to kprobe/kretprobe probes. This helper
+ * is used to detect fentry/fexit support for the specified kernel function.
+ *
+ *     1. A gap between kernel versions, kernel BTF is exposed
+ *        starting from 5.4 kernel. but fentry/fexit is actually
+ *        supported starting from 5.5.
+ *     2. Whether kernel supports module BTF or not
+ *
+ * *name* is the name of a kernel function to be attached to, which can be
+ * from vmlinux or a kernel module.
+ * *mod* is the name of a kernel module, if NULL, it means *name*
+ * belongs to vmlinux.
+ */
+bool fentry_exists(const char *name, const char *mod);
+
+/*
+ * The name of a kernel function to be attached to may be changed between
+ * kernel releases. This helper is used to confirm whether the target kernel
+ * uses a certain function name before attaching.
+ *
+ * It is achieved by scaning
+ *     /sys/kernel/debug/tracing/available_filter_functions
+ * If this file does not exist, it fallbacks to parse /proc/kallsyms,
+ * which is slower.
+ */
+bool kprobe_exists(const char *name);
+
 #endif /* __TRACE_HELPERS_H */