The test compares old text based kprobe API with PERF_TYPE_PROBE.

Here is a sample output of this test:

Creating 1000 kprobes with text-based API takes 6.979683 seconds
Cleaning 1000 kprobes with text-based API takes 84.897687 seconds
Creating 1000 kprobes with PERF_TYPE_PROBE (function name) takes 5.077558 
seconds
Cleaning 1000 kprobes with PERF_TYPE_PROBE (function name) takes 81.241354 
seconds
Creating 1000 kprobes with PERF_TYPE_PROBE (function addr) takes 5.218255 
seconds
Cleaning 1000 kprobes with PERF_TYPE_PROBE (function addr) takes 80.010731 
seconds

Signed-off-by: Song Liu <songliubrav...@fb.com>
Reviewed-by: Josef Bacik <jba...@fb.com>
---
 samples/bpf/Makefile                |   3 +
 samples/bpf/bpf_load.c              |   5 +-
 samples/bpf/bpf_load.h              |   4 +
 samples/bpf/test_many_kprobe_user.c | 184 ++++++++++++++++++++++++++++++++++++
 4 files changed, 193 insertions(+), 3 deletions(-)
 create mode 100644 samples/bpf/test_many_kprobe_user.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 3b4945c..1b729d6 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -44,6 +44,7 @@ hostprogs-y += xdp_redirect_map
 hostprogs-y += xdp_redirect_cpu
 hostprogs-y += xdp_monitor
 hostprogs-y += syscall_tp
+hostprogs-y += test_many_kprobe
 
 # Libbpf dependencies
 LIBBPF := ../../tools/lib/bpf/bpf.o
@@ -92,6 +93,7 @@ xdp_redirect_map-objs := bpf_load.o $(LIBBPF) 
xdp_redirect_map_user.o
 xdp_redirect_cpu-objs := bpf_load.o $(LIBBPF) xdp_redirect_cpu_user.o
 xdp_monitor-objs := bpf_load.o $(LIBBPF) xdp_monitor_user.o
 syscall_tp-objs := bpf_load.o $(LIBBPF) syscall_tp_user.o
+test_many_kprobe-objs := bpf_load.o $(LIBBPF) test_many_kprobe_user.o
 
 # Tell kbuild to always build the programs
 always := $(hostprogs-y)
@@ -182,6 +184,7 @@ HOSTLOADLIBES_xdp_redirect_map += -lelf
 HOSTLOADLIBES_xdp_redirect_cpu += -lelf
 HOSTLOADLIBES_xdp_monitor += -lelf
 HOSTLOADLIBES_syscall_tp += -lelf
+HOSTLOADLIBES_test_many_kprobe += -lelf
 
 # Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on 
cmdline:
 #  make samples/bpf/ LLC=~/git/llvm/build/bin/llc 
CLANG=~/git/llvm/build/bin/clang
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index a47cb1c..590e6f0 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -639,9 +639,8 @@ void read_trace_pipe(void)
        }
 }
 
-#define MAX_SYMS 300000
-static struct ksym syms[MAX_SYMS];
-static int sym_cnt;
+struct ksym syms[MAX_SYMS];
+int sym_cnt;
 
 static int ksym_cmp(const void *p1, const void *p2)
 {
diff --git a/samples/bpf/bpf_load.h b/samples/bpf/bpf_load.h
index e7a8a21..16bc263 100644
--- a/samples/bpf/bpf_load.h
+++ b/samples/bpf/bpf_load.h
@@ -67,6 +67,10 @@ static inline __u64 ptr_to_u64(const void *ptr)
        return (__u64) (unsigned long) ptr;
 }
 
+#define MAX_SYMS 300000
+extern struct ksym syms[MAX_SYMS];
+extern int sym_cnt;
+
 int load_kallsyms(void);
 struct ksym *ksym_search(long key);
 int set_link_xdp_fd(int ifindex, int fd, __u32 flags);
diff --git a/samples/bpf/test_many_kprobe_user.c 
b/samples/bpf/test_many_kprobe_user.c
new file mode 100644
index 0000000..70b680e
--- /dev/null
+++ b/samples/bpf/test_many_kprobe_user.c
@@ -0,0 +1,184 @@
+/* Copyright (c) 2017 Facebook
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2 of the GNU General Public
+ * License as published by the Free Software Foundation.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <linux/version.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <time.h>
+#include "libbpf.h"
+#include "bpf_load.h"
+#include "perf-sys.h"
+
+#define MAX_KPROBES 1000
+
+#define DEBUGFS "/sys/kernel/debug/tracing/"
+
+int kprobes[MAX_KPROBES] = {0};
+int kprobe_count;
+int perf_event_fds[MAX_KPROBES];
+const char license[] = "GPL";
+
+static __u64 time_get_ns(void)
+{
+       struct timespec ts;
+
+       clock_gettime(CLOCK_MONOTONIC, &ts);
+       return ts.tv_sec * 1000000000ull + ts.tv_nsec;
+}
+
+static int kprobe_api(char *func, void *addr, bool use_new_api)
+{
+       int efd;
+       struct perf_event_attr attr = {};
+       struct probe_desc pd;
+       char buf[256];
+       int err, id;
+
+       attr.sample_type = PERF_SAMPLE_RAW;
+       attr.sample_period = 1;
+       attr.wakeup_events = 1;
+
+       if (use_new_api) {
+               attr.type = PERF_TYPE_PROBE;
+               if (func) {
+                       pd.func = ptr_to_u64(func);
+                       pd.offset = 0;
+               } else {
+                       pd.func = 0;
+                       pd.offset = ptr_to_u64(addr);
+               }
+
+               attr.probe_desc = ptr_to_u64(&pd);
+       } else {
+               attr.type = PERF_TYPE_TRACEPOINT;
+               snprintf(buf, sizeof(buf),
+                        "echo 'p:%s %s' >> 
/sys/kernel/debug/tracing/kprobe_events",
+                        func, func);
+               err = system(buf);
+               if (err < 0) {
+                       printf("failed to create kprobe '%s' error '%s'\n",
+                              func, strerror(errno));
+                       return -1;
+               }
+
+               strcpy(buf, DEBUGFS);
+               strcat(buf, "events/kprobes/");
+               strcat(buf, func);
+               strcat(buf, "/id");
+               efd = open(buf, O_RDONLY, 0);
+               if (efd < 0) {
+                       printf("failed to open event %s\n", func);
+                       return -1;
+               }
+
+               err = read(efd, buf, sizeof(buf));
+               if (err < 0 || err >= sizeof(buf)) {
+                       printf("read from '%s' failed '%s'\n", func,
+                              strerror(errno));
+                       return -1;
+               }
+
+               close(efd);
+               buf[err] = 0;
+               id = atoi(buf);
+               attr.config = id;
+       }
+
+       efd = sys_perf_event_open(&attr, -1/*pid*/, 0/*cpu*/,
+                                 -1/*group_fd*/, 0);
+
+       return efd;
+}
+
+static int select_kprobes(void)
+{
+       int fd;
+       int i;
+
+       load_kallsyms();
+
+       kprobe_count = 0;
+       for (i = 0; i < sym_cnt; i++) {
+               if (strstr(syms[i].name, "."))
+                       continue;
+               fd = kprobe_api(syms[i].name, NULL, true);
+               if (fd < 0)
+                       continue;
+               close(fd);
+               kprobes[kprobe_count] = i;
+               if (++kprobe_count >= MAX_KPROBES)
+                       break;
+       }
+
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int i;
+       __u64 start_time;
+
+       select_kprobes();
+
+       /* clean all trace_kprobe */
+       i = system("echo \"\" > /sys/kernel/debug/tracing/kprobe_events");
+
+       /* test text based API */
+       start_time = time_get_ns();
+       for (i = 0; i < kprobe_count; i++)
+               perf_event_fds[i] = kprobe_api(syms[kprobes[i]].name,
+                                              NULL, false);
+       printf("Creating %d kprobes with text-based API takes %f seconds\n",
+              kprobe_count, (time_get_ns() - start_time) / 1000000000.0);
+
+       start_time = time_get_ns();
+       for (i = 0; i < kprobe_count; i++)
+               if (perf_event_fds[i] > 0)
+                       close(perf_event_fds[i]);
+       i = system("echo \"\" > /sys/kernel/debug/tracing/kprobe_events");
+       printf("Cleaning %d kprobes with text-based API takes %f seconds\n",
+              kprobe_count, (time_get_ns() - start_time) / 1000000000.0);
+
+       /* test PERF_TYPE_PROBE API, with function names */
+       start_time = time_get_ns();
+       for (i = 0; i < kprobe_count; i++)
+               perf_event_fds[i] = kprobe_api(syms[kprobes[i]].name,
+                                              NULL, true);
+       printf("Creating %d kprobes with PERF_TYPE_PROBE (function name) takes 
%f seconds\n",
+              kprobe_count, (time_get_ns() - start_time) / 1000000000.0);
+
+       start_time = time_get_ns();
+       for (i = 0; i < kprobe_count; i++)
+               if (perf_event_fds[i] > 0)
+                       close(perf_event_fds[i]);
+       printf("Cleaning %d kprobes with PERF_TYPE_PROBE (function name) takes 
%f seconds\n",
+              kprobe_count, (time_get_ns() - start_time) / 1000000000.0);
+
+       /* test PERF_TYPE_PROBE API, with function address */
+       start_time = time_get_ns();
+       for (i = 0; i < kprobe_count; i++)
+               perf_event_fds[i] = kprobe_api(
+                       NULL, (void *)(syms[kprobes[i]].addr), true);
+       printf("Creating %d kprobes with PERF_TYPE_PROBE (function addr) takes 
%f seconds\n",
+              kprobe_count, (time_get_ns() - start_time) / 1000000000.0);
+
+       start_time = time_get_ns();
+       for (i = 0; i < kprobe_count; i++)
+               if (perf_event_fds[i] > 0)
+                       close(perf_event_fds[i]);
+       printf("Cleaning %d kprobes with PERF_TYPE_PROBE (function addr) takes 
%f seconds\n",
+              kprobe_count, (time_get_ns() - start_time) / 1000000000.0);
+       return 0;
+}
-- 
2.9.5

Reply via email to