[PATCH 15/27] perf test: Add 'perf test BPF'

From: Wang Nan
Date: Mon Aug 10 2015 - 02:19:22 EST


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 <wangnan0@xxxxxxxxxx>
Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Cc: Alexei Starovoitov <ast@xxxxxxxxxxxx>
Cc: Brendan Gregg <brendan.d.gregg@xxxxxxxxx>
Cc: Daniel Borkmann <daniel@xxxxxxxxxxxxx>
Cc: David Ahern <dsahern@xxxxxxxxx>
Cc: He Kuang <hekuang@xxxxxxxxxx>
Cc: Jiri Olsa <jolsa@xxxxxxxxxx>
Cc: Kaixu Xia <xiakaixu@xxxxxxxxxx>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@xxxxxxxxxxx>
Cc: Namhyung Kim <namhyung@xxxxxxxxxx>
Cc: Peter Zijlstra <a.p.zijlstra@xxxxxxxxx>
Cc: Zefan Li <lizefan@xxxxxxxxxx>
Cc: pi3orama@xxxxxxx
---
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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/