This patch adds BPF testcase for testing BPF event filtering.

By utilizing the result of 'perf test LLVM', this patch compiles the
eBPF sample program then test it ability. The BPF script in 'perf test
LLVM' collects half of execution of epoll_pwait(). This patch runs 111
times of it, so the resule should contains 56 samples.

Signed-off-by: Wang Nan <wangn...@huawei.com>
Cc: Arnaldo Carvalho de Melo <a...@redhat.com>
Cc: Alexei Starovoitov <a...@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gr...@gmail.com>
Cc: Daniel Borkmann <dan...@iogearbox.net>
Cc: David Ahern <dsah...@gmail.com>
Cc: He Kuang <heku...@huawei.com>
Cc: Jiri Olsa <jo...@kernel.org>
Cc: Kaixu Xia <xiaka...@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu...@hitachi.com>
Cc: Namhyung Kim <namhy...@kernel.org>
Cc: Peter Zijlstra <a.p.zijls...@chello.nl>
Cc: Zefan Li <lize...@huawei.com>
Cc: pi3or...@163.com
Link: http://lkml.kernel.org/n/ebpf-6yw9eg0ej3l4jnqhinngk...@git.kernel.org
---
 tools/perf/tests/Build          |   1 +
 tools/perf/tests/bpf.c          | 171 ++++++++++++++++++++++++++++++++++++++++
 tools/perf/tests/builtin-test.c |   4 +
 tools/perf/tests/llvm.c         |  19 +++++
 tools/perf/tests/llvm.h         |   1 +
 tools/perf/tests/tests.h        |   1 +
 tools/perf/util/bpf-loader.c    |  14 ++++
 tools/perf/util/bpf-loader.h    |   9 +++
 8 files changed, 220 insertions(+)
 create mode 100644 tools/perf/tests/bpf.c

diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 4afc8c8..d0278a9 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -32,6 +32,7 @@ perf-y += parse-no-sample-id-all.o
 perf-y += kmod-path.o
 perf-y += thread-map.o
 perf-y += llvm.o llvm-src.o
+perf-y += bpf.o
 perf-y += topology.o
 
 $(OUTPUT)tests/llvm-src.c: tests/bpf-script-example.c
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
new file mode 100644
index 0000000..d7cdc84
--- /dev/null
+++ b/tools/perf/tests/bpf.c
@@ -0,0 +1,171 @@
+#include <stdio.h>
+#include <sys/epoll.h>
+#include <util/bpf-loader.h>
+#include <util/evlist.h>
+#include "tests.h"
+#include "llvm.h"
+#include "debug.h"
+#define NR_ITERS       111
+
+#ifdef HAVE_LIBBPF_SUPPORT
+
+static int epoll_pwait_loop(void)
+{
+       int i;
+
+       /* Should fail NR_ITERS times */
+       for (i = 0; i < NR_ITERS; i++)
+               epoll_pwait(-(i + 1), NULL, 0, 0, NULL);
+       return 0;
+}
+
+static struct bpf_object *prepare_bpf(void *obj_buf, size_t obj_buf_sz)
+{
+       struct bpf_object *obj;
+
+       obj = bpf__prepare_load_buffer(obj_buf, obj_buf_sz, "[buffer]");
+       if (IS_ERR(obj)) {
+               fprintf(stderr, " (compile failed)");
+               return NULL;
+       }
+       return obj;
+}
+
+static int do_test(struct bpf_object *obj)
+{
+       struct record_opts opts = {
+               .target = {
+                       .uid = UINT_MAX,
+                       .uses_mmap = true,
+               },
+               .freq         = 0,
+               .mmap_pages   = 256,
+               .default_interval = 1,
+       };
+
+       int i, err = 0, count = 0;
+       char pid[16];
+       char sbuf[STRERR_BUFSIZE];
+       struct perf_evlist *evlist;
+
+       struct parse_events_evlist parse_evlist;
+       struct parse_events_error parse_error;
+
+       bzero(&parse_error, sizeof(parse_error));
+       bzero(&parse_evlist, sizeof(parse_evlist));
+       parse_evlist.error = &parse_error;
+       INIT_LIST_HEAD(&parse_evlist.list);
+
+       err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj);
+       if (err || list_empty(&parse_evlist.list)) {
+               fprintf(stderr, " (Failed to add events selected by BPF)");
+               if (!err)
+                       err = -EINVAL;
+               goto out;
+       }
+
+       snprintf(pid, sizeof(pid), "%d", getpid());
+       pid[sizeof(pid) - 1] = '\0';
+       opts.target.tid = opts.target.pid = pid;
+
+       /* Instead of perf_evlist__new_default, don't add default events */
+       evlist = perf_evlist__new();
+       if (!evlist) {
+               pr_debug("No ehough memory to create evlist\n");
+               return -ENOMEM;
+       }
+
+       err = perf_evlist__create_maps(evlist, &opts.target);
+       if (err < 0) {
+               pr_debug("Not enough memory to create thread/cpu maps\n");
+               goto out_delete_evlist;
+       }
+
+       perf_evlist__splice_list_tail(evlist, &parse_evlist.list);
+       evlist->nr_groups = parse_evlist.nr_groups;
+
+       perf_evlist__config(evlist, &opts);
+
+       err = perf_evlist__open(evlist);
+       if (err < 0) {
+               pr_debug("perf_evlist__open: %s\n",
+                        strerror_r(errno, sbuf, sizeof(sbuf)));
+               goto out_delete_evlist;
+       }
+
+       err = perf_evlist__mmap(evlist, opts.mmap_pages, false);
+       if (err < 0) {
+               pr_debug("perf_evlist__mmap: %s\n",
+                        strerror_r(errno, sbuf, sizeof(sbuf)));
+               goto out_delete_evlist;
+       }
+
+       perf_evlist__enable(evlist);
+       epoll_pwait_loop();
+       perf_evlist__disable(evlist);
+
+       for (i = 0; i < evlist->nr_mmaps; i++) {
+               union perf_event *event;
+
+               while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+                       const u32 type = event->header.type;
+
+                       if (type == PERF_RECORD_SAMPLE)
+                               count ++;
+               }
+       }
+
+       if (count != (NR_ITERS + 1) / 2) {
+               fprintf(stderr, " (filter result incorrect)");
+               err = -EBADF;
+       }
+
+out_delete_evlist:
+       perf_evlist__delete(evlist);
+out:
+       if (err)
+               return TEST_FAIL;
+       return 0;
+}
+
+int test__bpf(void)
+{
+       int err;
+       void *obj_buf;
+       size_t obj_buf_sz;
+       struct bpf_object *obj;
+
+       if (geteuid() != 0) {
+               fprintf(stderr, " (try run as root)");
+               return TEST_SKIP;
+       }
+
+       test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz);
+       if (!obj_buf || !obj_buf_sz) {
+               if (verbose == 0)
+                       fprintf(stderr, " (fix 'perf test LLVM' first)");
+               return TEST_SKIP;
+       }
+
+       obj = prepare_bpf(obj_buf, obj_buf_sz);
+       if (!obj) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       err = do_test(obj);
+       if (err)
+               goto out;
+out:
+       bpf__clear();
+       if (err)
+               return TEST_FAIL;
+       return 0;
+}
+
+#else
+int test__bpf(void)
+{
+       return TEST_SKIP;
+}
+#endif
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index e812a0c..4828716 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -168,6 +168,10 @@ static struct test generic_tests[] = {
                .func = test_session_topology,
        },
        {
+               .desc = "Test BPF filter",
+               .func = test__bpf,
+       },
+       {
                .func = NULL,
        },
 };
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index 236bf39..fd5fdb0 100644
--- a/tools/perf/tests/llvm.c
+++ b/tools/perf/tests/llvm.c
@@ -174,3 +174,22 @@ void test__llvm_cleanup(void)
                        (~((unsigned long)page_size - 1));
        munmap((void *)boundary, buf_end - boundary);
 }
+
+void
+test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz)
+{
+       *p_obj_buf = NULL;
+       *p_obj_buf_sz = 0;
+
+       if (!p_test_llvm__bpf_result) {
+               test__llvm_prepare();
+               test__llvm();
+               test__llvm_cleanup();
+       }
+
+       if (!p_test_llvm__bpf_result)
+               return;
+
+       *p_obj_buf = p_test_llvm__bpf_result->object;
+       *p_obj_buf_sz = p_test_llvm__bpf_result->size;
+}
diff --git a/tools/perf/tests/llvm.h b/tools/perf/tests/llvm.h
index 1e89e46..2fd7ed6 100644
--- a/tools/perf/tests/llvm.h
+++ b/tools/perf/tests/llvm.h
@@ -10,5 +10,6 @@ struct test_llvm__bpf_result {
 
 extern struct test_llvm__bpf_result *p_test_llvm__bpf_result;
 extern const char test_llvm__bpf_prog[];
+void test_llvm__fetch_bpf_obj(void **p_obj_buf, size_t *p_obj_buf_sz);
 
 #endif
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index a848802..15c46e1 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -70,6 +70,7 @@ int test__thread_map(void);
 int test__llvm(void);
 void test__llvm_prepare(void);
 void test__llvm_cleanup(void);
+int test__bpf(void);
 int test_session_topology(void);
 
 #if defined(__arm__) || defined(__aarch64__)
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
index ba6f752..f4c690f 100644
--- a/tools/perf/util/bpf-loader.c
+++ b/tools/perf/util/bpf-loader.c
@@ -34,6 +34,20 @@ struct bpf_prog_priv {
        struct perf_probe_event pev;
 };
 
+struct bpf_object *
+bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name)
+{
+       struct bpf_object *obj;
+
+       obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name);
+       if (!obj) {
+               pr_debug("bpf: failed to load buffer\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       return obj;
+}
+
 struct bpf_object *bpf__prepare_load(const char *filename, bool source)
 {
        struct bpf_object *obj;
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h
index ccd8d7f..d8f1945 100644
--- a/tools/perf/util/bpf-loader.h
+++ b/tools/perf/util/bpf-loader.h
@@ -19,6 +19,8 @@ typedef int (*bpf_prog_iter_callback_t)(struct 
probe_trace_event *tev,
 
 #ifdef HAVE_LIBBPF_SUPPORT
 struct bpf_object *bpf__prepare_load(const char *filename, bool source);
+struct bpf_object *bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz,
+                                           const char *name);
 
 void bpf__clear(void);
 
@@ -41,6 +43,13 @@ bpf__prepare_load(const char *filename __maybe_unused,
        return ERR_PTR(-ENOTSUP);
 }
 
+static inline struct bpf_object *
+bpf__prepare_load_buffer(void *obj_buf __maybe_unused,
+                                          size_t obj_buf_sz __maybe_unused)
+{
+       return ERR_PTR(-ENOTSUP);
+}
+
 static inline void bpf__clear(void) { }
 
 static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 
0;}
-- 
1.8.3.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to