This patch adds BPF testcase for testing BPF event filtering.

By utilizing the result of 'perf test LLVM', this patch further forks
a 'perf record' to load and use it.

The BPF script in 'perf test LLVM' collect half of execution of
epoll_pwait(), 'perf test TESTTARGET_EPOLL_PWAIT_LOOP' run 111 times
of it, so the resuling 'perf.data' should contain 56 samples. This
patch checks the result using 'perf report -D'.

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
---
 tools/perf/tests/bpf.c          | 214 ++++++++++++++++++++++++++++++++++++++++
 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 +
 5 files changed, 239 insertions(+)

diff --git a/tools/perf/tests/bpf.c b/tools/perf/tests/bpf.c
index 1a9eec3..e911ef8 100644
--- a/tools/perf/tests/bpf.c
+++ b/tools/perf/tests/bpf.c
@@ -1,6 +1,8 @@
 #include <stdio.h>
+#include <stdarg.h>
 #include <sys/epoll.h>
 #include "tests.h"
+#include "llvm.h"
 #include "debug.h"
 #define NR_ITERS       111
 
@@ -13,3 +15,215 @@ int testtarget__epoll_pwait_loop(void)
                epoll_pwait(-(i + 1), NULL, 0, 0, NULL);
        return 0;
 }
+
+#ifdef HAVE_LIBBPF_SUPPORT
+
+static int
+vsystem_exec_cmd(int *pres, const char *format, va_list args)
+{
+       char *cmd;
+       int err, res;
+
+       err = vasprintf(&cmd, format, args);
+       if (err < 0) {
+               pr_err("No enough memory for vasprintf\n");
+               return TEST_FAIL;
+       }
+       res = system(cmd);
+       free(cmd);
+       *pres = res;
+       return 0;
+}
+
+#define __printf(a, b) __attribute__((format(printf, a, b)))
+__printf(2, 3) static int
+system_exec_cmd(int *pres, const char *format, ...)
+{
+       va_list args;
+       int err;
+
+       va_start(args, format);
+       err = vsystem_exec_cmd(pres, format, args);
+       va_end(args);
+       return err;
+}
+
+static const char *get_tmpdir(void)
+{
+       const char *tmp;
+
+       tmp = getenv("TMPDIR");
+       if (tmp)
+               return tmp;
+#ifdef P_tmpdir
+       return P_tmpdir;
+#else
+       return "/tmp";
+#endif
+}
+
+#define TMPDIR_TEMPLATE "perf-bpf-test-XXXXXX"
+static int
+prepare_tmpdir(char **p_tmpdir, char **p_cmd_rmdir)
+{
+       int err;
+
+       err = asprintf(p_tmpdir, "%s/%s", get_tmpdir(), TMPDIR_TEMPLATE);
+       if (err < 0) {
+               pr_err("No enough memory for tmpdir\n");
+               return TEST_FAIL;
+       }
+
+       if (mkdtemp(*p_tmpdir) != *p_tmpdir) {
+               pr_err("mkdtemp() failed\n");
+               free(*p_tmpdir);
+               *p_tmpdir = NULL;
+               return TEST_FAIL;
+       }
+
+       err = asprintf(p_cmd_rmdir, "rm -rf %s", *p_tmpdir);
+       if (err < 0) {
+               pr_err("No enough memory for rm command\n");
+               free(*p_tmpdir);
+               *p_tmpdir = NULL;
+               return TEST_FAIL;
+       }
+
+       return 0;
+}
+
+static int
+dump_obj(void *obj_buf, size_t obj_buf_sz, const char *tmpdir)
+{
+       char *obj_name;
+       FILE *fp;
+       int err;
+
+       err = asprintf(&obj_name, "%s/test.o", tmpdir);
+       if (err < 0) {
+               pr_err("No enough memory for string '%s/test.o'\n", tmpdir);
+               return TEST_FAIL;
+       }
+
+       fp = fopen(obj_name, "wb");
+       if (!fp) {
+               pr_err("Create '%s' failed", obj_name);
+               free(obj_name);
+               return TEST_FAIL;
+       }
+
+       err = fwrite(obj_buf, 1, obj_buf_sz, fp);
+       if (err != (int)obj_buf_sz) {
+               pr_err("Dump to '%s' failed", obj_name);
+               free(obj_name);
+               fclose(fp);
+               return TEST_FAIL;
+       }
+
+       fclose(fp);
+       return 0;
+}
+
+#define exec_cmd(errstr, fmt...)               \
+do {                                           \
+       err = system_exec_cmd(&res, fmt);       \
+       if (!err && !res)                       \
+               break;                          \
+       if (verbose == 0)                       \
+               fprintf(stderr, errstr);        \
+       return TEST_FAIL;                       \
+} while(0)
+
+static int
+do_test(const char *tmpdir)
+{
+       char *arg0, *result_path;
+       FILE *result_file;
+       int err, res;
+
+       err = asprintf(&arg0, "/proc/%d/exe", getpid());
+       if (err < 0) {
+               pr_err("No enough memory for '/proc/%d/exe'\n", getpid());
+               return TEST_FAIL;
+       }
+
+       exec_cmd(" (failed to collect samples)",
+               "%s record -e %s/test.o -o %s/perf.data %s test '%s' %s",
+                arg0, tmpdir, tmpdir, arg0, TESTTARGET_EPOLL_PWAIT_LOOP,
+                verbose == 0 ? " > /dev/null 2>&1" : "");
+
+       exec_cmd(" (failed to read perf.data)",
+"%s report -i %s/perf.data -D | grep PERF_RECORD_SAMPLE | wc -l > %s/result",
+                arg0, tmpdir, tmpdir);
+
+       free(arg0);
+       arg0 = NULL;
+
+       err = asprintf(&result_path, "%s/result", tmpdir);
+       if (err < 0) {
+               pr_err("No enough memory for result file name\n");
+               return TEST_FAIL;
+       }
+       result_file = fopen(result_path, "r");
+       if (!result_file) {
+               pr_err("Failed to open '%s'\n", result_path);
+               free(result_path);
+               return TEST_FAIL;
+       }
+       free(result_path);
+
+       err = fscanf(result_file, "%d", &res);
+       fclose(result_file);
+
+       if (err != 1) {
+               pr_err("Failed to read result file\n");
+               return TEST_FAIL;
+       }
+
+       if (res != (NR_ITERS + 1) / 2)
+               fprintf(stderr, " (filter result incorrect)");
+       return 0;
+}
+
+int test__bpf(void)
+{
+       int err;
+       char *tmpdir, *cmd_rmdir;
+       void *obj_buf;
+       size_t obj_buf_sz;
+
+       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;
+       }
+
+       err = prepare_tmpdir(&tmpdir, &cmd_rmdir);
+       if (err)
+               return err;
+
+       err = dump_obj(obj_buf, obj_buf_sz, tmpdir);
+       if (err)
+               goto out;
+
+       err = do_test(tmpdir);
+out:
+       if (system(cmd_rmdir)) {
+               pr_err("'%s' failed\n", cmd_rmdir);
+               return TEST_FAIL;
+       }
+       if (err != TEST_OK)
+               fprintf(stderr, " (use 'perf test -v BPF' to see detail)");
+       return err;
+}
+
+#else
+
+int test__bpf(void)
+{
+       fprintf(stderr, " (disabled)");
+       return TEST_SKIP;
+}
+
+#endif
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 0d0c963..6d59e84 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -183,6 +183,10 @@ static struct test {
                .prepare = test__llvm_prepare,
                .cleanup = test__llvm_cleanup,
        },
+       {
+               .desc = "Test BPF filter",
+               .func = test__bpf,
+       },
        /*
         * Put test targets after all test cases so sequence number will be
         * less confusing.
diff --git a/tools/perf/tests/llvm.c b/tools/perf/tests/llvm.c
index e48eb64..94ed5f2 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 0138a3d..7704bfd 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -65,6 +65,7 @@ int test__thread_map(void);
 int test__llvm(void);
 void test__llvm_prepare(void);
 void test__llvm_cleanup(void);
+int test__bpf(void);
 #define TESTTARGET_EPOLL_PWAIT_LOOP "TESTTARGET: epoll_pwait loop"
 int testtarget__epoll_pwait_loop(void);
 
-- 
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