]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
perf test: Add 'perf test BPF'
authorWang Nan <wangnan0@huawei.com>
Fri, 6 Nov 2015 13:49:43 +0000 (13:49 +0000)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Fri, 6 Nov 2015 20:50:03 +0000 (17:50 -0300)
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 its ability. The BPF script in 'perf test
LLVM' lets only 50% samples generated by epoll_pwait() to be captured.
This patch runs that system call for 111 times, so the result should
contain 56 samples.

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1446817783-86722-8-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/tests/Build
tools/perf/tests/bpf.c [new file with mode: 0644]
tools/perf/tests/builtin-test.c
tools/perf/tests/tests.h
tools/perf/util/bpf-loader.c
tools/perf/util/bpf-loader.h

index a47b21193fb2a057542410170c2d916d02ef74f0..f41ebf8849feb1d5fe20e2601de952c85e3bac8c 100644 (file)
@@ -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-base.o llvm-src-kbuild.o
+perf-y += bpf.o
 perf-y += topology.o
 
 $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c
diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
new file mode 100644 (file)
index 0000000..ec16f78
--- /dev/null
@@ -0,0 +1,209 @@
+#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 {
+       enum test_llvm__testcase prog_id;
+       const char *desc;
+       const char *name;
+       const char *msg_compile_fail;
+       const char *msg_load_fail;
+       int (*target_func)(void);
+       int expect_result;
+} bpf_testcase_table[] = {
+       {
+               LLVM_TESTCASE_BASE,
+               "Test basic BPF filtering",
+               "[basic_bpf_test]",
+               "fix 'perf test LLVM' first",
+               "load bpf object failed",
+               &epoll_pwait_loop,
+               (NR_ITERS + 1) / 2,
+       },
+};
+
+static int do_test(struct bpf_object *obj, int (*func)(void),
+                  int expect)
+{
+       struct record_opts opts = {
+               .target = {
+                       .uid = UINT_MAX,
+                       .uses_mmap = true,
+               },
+               .freq         = 0,
+               .mmap_pages   = 256,
+               .default_interval = 1,
+       };
+
+       char pid[16];
+       char sbuf[STRERR_BUFSIZE];
+       struct perf_evlist *evlist;
+       int i, ret = TEST_FAIL, err = 0, count = 0;
+
+       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)) {
+               pr_debug("Failed to add events selected by BPF\n");
+               if (!err)
+                       return TEST_FAIL;
+       }
+
+       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 TEST_FAIL;
+       }
+
+       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);
+       (*func)();
+       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 != expect)
+               pr_debug("BPF filter result incorrect\n");
+
+       ret = TEST_OK;
+
+out_delete_evlist:
+       perf_evlist__delete(evlist);
+       return ret;
+}
+
+static struct bpf_object *
+prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name)
+{
+       struct bpf_object *obj;
+
+       obj = bpf__prepare_load_buffer(obj_buf, obj_buf_sz, name);
+       if (IS_ERR(obj)) {
+               pr_debug("Compile BPF program failed.\n");
+               return NULL;
+       }
+       return obj;
+}
+
+static int __test__bpf(int index)
+{
+       int ret;
+       void *obj_buf;
+       size_t obj_buf_sz;
+       struct bpf_object *obj;
+
+       ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz,
+                                      bpf_testcase_table[index].prog_id,
+                                      true);
+       if (ret != TEST_OK || !obj_buf || !obj_buf_sz) {
+               pr_debug("Unable to get BPF object, %s\n",
+                        bpf_testcase_table[index].msg_compile_fail);
+               if (index == 0)
+                       return TEST_SKIP;
+               else
+                       return TEST_FAIL;
+       }
+
+       obj = prepare_bpf(obj_buf, obj_buf_sz,
+                         bpf_testcase_table[index].name);
+       if (!obj) {
+               ret = TEST_FAIL;
+               goto out;
+       }
+
+       ret = do_test(obj,
+                     bpf_testcase_table[index].target_func,
+                     bpf_testcase_table[index].expect_result);
+out:
+       bpf__clear();
+       return ret;
+}
+
+int test__bpf(void)
+{
+       unsigned int i;
+       int err;
+
+       if (geteuid() != 0) {
+               pr_debug("Only root can run BPF test\n");
+               return TEST_SKIP;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) {
+               err = __test__bpf(i);
+
+               if (err != TEST_OK)
+                       return err;
+       }
+
+       return TEST_OK;
+}
+
+#else
+int test__bpf(void)
+{
+       pr_debug("Skip BPF test because BPF support is not compiled\n");
+       return TEST_SKIP;
+}
+#endif
index 66f72d3d66771ab4c9ad1cec7a53b92d8bcb8f1b..7b0120abc137db8137fb0d35a00793ab4652b8d5 100644 (file)
@@ -165,6 +165,10 @@ static struct test generic_tests[] = {
                .desc = "Test topology in session",
                .func = test_session_topology,
        },
+       {
+               .desc = "Test BPF filter",
+               .func = test__bpf,
+       },
        {
                .func = NULL,
        },
index c80486969f83412621336a33ee95edef87d2fb31..3c8734a3abbc5ba24c9feb90c3cbdf3c5c1dfc7e 100644 (file)
@@ -66,6 +66,7 @@ int test__fdarray__add(void);
 int test__kmod_path__parse(void);
 int test__thread_map(void);
 int test__llvm(void);
+int test__bpf(void);
 int test_session_topology(void);
 
 #if defined(__arm__) || defined(__aarch64__)
index e3afa1b60bb56bcd13db678ec0ea4d70b9197327..4c50411371db31208c361115331695571fe545b7 100644 (file)
@@ -34,10 +34,32 @@ struct bpf_prog_priv {
        struct perf_probe_event pev;
 };
 
+static bool libbpf_initialized;
+
+struct bpf_object *
+bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name)
+{
+       struct bpf_object *obj;
+
+       if (!libbpf_initialized) {
+               libbpf_set_print(libbpf_warning,
+                                libbpf_info,
+                                libbpf_debug);
+               libbpf_initialized = true;
+       }
+
+       obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name);
+       if (IS_ERR(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;
-       static bool libbpf_initialized;
 
        if (!libbpf_initialized) {
                libbpf_set_print(libbpf_warning,
index 5eb3629eed8bdd6e6d1797013c33f21a63ceee97..9caf3ae4acf3d018a87d92bb62f16c4337bd6388 100644 (file)
@@ -34,6 +34,9 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source);
 int bpf__strerror_prepare_load(const char *filename, bool source,
                               int err, char *buf, size_t size);
 
+struct bpf_object *bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz,
+                                           const char *name);
+
 void bpf__clear(void);
 
 int bpf__probe(struct bpf_object *obj);
@@ -55,6 +58,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;}