[PATCH v3 6/6] bpf: add new test test_many_kprobe

From: Song Liu
Date: Thu Nov 30 2017 - 18:50:43 EST


The test compares old text based kprobe API with perf_kprobe.

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_kprobe (function name) takes 5.077558 seconds
Cleaning 1000 kprobes with perf_kprobe (function name) takes 81.241354 seconds
Creating 1000 kprobes with perf_kprobe (function addr) takes 5.218255 seconds
Cleaning 1000 kprobes with perf_kprobe (function addr) takes 80.010731 seconds

Signed-off-by: Song Liu <songliubraving@xxxxxx>
Reviewed-by: Josef Bacik <jbacik@xxxxxx>
---
samples/bpf/Makefile | 3 +
samples/bpf/bpf_load.c | 5 +-
samples/bpf/bpf_load.h | 4 +
samples/bpf/test_many_kprobe_user.c | 186 ++++++++++++++++++++++++++++++++++++
4 files changed, 195 insertions(+), 3 deletions(-)
create mode 100644 samples/bpf/test_many_kprobe_user.c

diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 9b4a66e..ec92f35 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -42,6 +42,7 @@ hostprogs-y += xdp_redirect
hostprogs-y += xdp_redirect_map
hostprogs-y += xdp_monitor
hostprogs-y += syscall_tp
+hostprogs-y += test_many_kprobe

# Libbpf dependencies
LIBBPF := ../../tools/lib/bpf/bpf.o
@@ -87,6 +88,7 @@ xdp_redirect-objs := bpf_load.o $(LIBBPF) xdp_redirect_user.o
xdp_redirect_map-objs := bpf_load.o $(LIBBPF) xdp_redirect_map_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)
@@ -172,6 +174,7 @@ HOSTLOADLIBES_xdp_redirect += -lelf
HOSTLOADLIBES_xdp_redirect_map += -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 da19e58..6f38db0 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -663,9 +663,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 95d6be5..6c9d584 100644
--- a/samples/bpf/bpf_load.h
+++ b/samples/bpf/bpf_load.h
@@ -69,6 +69,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..6c111cf
--- /dev/null
+++ b/samples/bpf/test_many_kprobe_user.c
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017 Facebook
+
+#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 = {};
+ 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_kprobe_type;
+ if (func) {
+ attr.kprobe_func = ptr_to_u64(func);
+ attr.probe_offset = 0;
+ } else {
+ attr.kprobe_func = 0;
+ attr.kprobe_addr = ptr_to_u64(addr);
+ }
+ } 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;
+ }
+
+ attr.size = sizeof(attr);
+ 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, false);
+ 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);
+
+ get_perf_kprobe_type_id();
+ if (perf_kprobe_type == -1) {
+ printf("The kernel does support perf_kprobe.\n"
+ "Existing...\n");
+ return 0;
+ }
+
+ /* test perf_kprobe 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_kprobe (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_kprobe (function name) takes %f seconds\n",
+ kprobe_count, (time_get_ns() - start_time) / 1000000000.0);
+
+ /* test perf_kprobe 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_kprobe (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_kprobe (function addr) takes %f seconds\n",
+ kprobe_count, (time_get_ns() - start_time) / 1000000000.0);
+ return 0;
+}
--
2.9.5