diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/perf/tests/Build | 1 | ||||
-rw-r--r-- | tools/perf/tests/bpf.c | 209 | ||||
-rw-r--r-- | tools/perf/tests/builtin-test.c | 4 | ||||
-rw-r--r-- | tools/perf/tests/tests.h | 1 | ||||
-rw-r--r-- | tools/perf/util/bpf-loader.c | 24 | ||||
-rw-r--r-- | tools/perf/util/bpf-loader.h | 10 |
6 files changed, 248 insertions, 1 deletions
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build index a47b21193fb2..f41ebf8849fe 100644 --- a/tools/perf/tests/Build +++ b/tools/perf/tests/Build | |||
@@ -32,6 +32,7 @@ perf-y += parse-no-sample-id-all.o | |||
32 | perf-y += kmod-path.o | 32 | perf-y += kmod-path.o |
33 | perf-y += thread-map.o | 33 | perf-y += thread-map.o |
34 | perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o | 34 | perf-y += llvm.o llvm-src-base.o llvm-src-kbuild.o |
35 | perf-y += bpf.o | ||
35 | perf-y += topology.o | 36 | perf-y += topology.o |
36 | 37 | ||
37 | $(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c | 38 | $(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 index 000000000000..ec16f7812c8b --- /dev/null +++ b/tools/perf/tests/bpf.c | |||
@@ -0,0 +1,209 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <sys/epoll.h> | ||
3 | #include <util/bpf-loader.h> | ||
4 | #include <util/evlist.h> | ||
5 | #include "tests.h" | ||
6 | #include "llvm.h" | ||
7 | #include "debug.h" | ||
8 | #define NR_ITERS 111 | ||
9 | |||
10 | #ifdef HAVE_LIBBPF_SUPPORT | ||
11 | |||
12 | static int epoll_pwait_loop(void) | ||
13 | { | ||
14 | int i; | ||
15 | |||
16 | /* Should fail NR_ITERS times */ | ||
17 | for (i = 0; i < NR_ITERS; i++) | ||
18 | epoll_pwait(-(i + 1), NULL, 0, 0, NULL); | ||
19 | return 0; | ||
20 | } | ||
21 | |||
22 | static struct { | ||
23 | enum test_llvm__testcase prog_id; | ||
24 | const char *desc; | ||
25 | const char *name; | ||
26 | const char *msg_compile_fail; | ||
27 | const char *msg_load_fail; | ||
28 | int (*target_func)(void); | ||
29 | int expect_result; | ||
30 | } bpf_testcase_table[] = { | ||
31 | { | ||
32 | LLVM_TESTCASE_BASE, | ||
33 | "Test basic BPF filtering", | ||
34 | "[basic_bpf_test]", | ||
35 | "fix 'perf test LLVM' first", | ||
36 | "load bpf object failed", | ||
37 | &epoll_pwait_loop, | ||
38 | (NR_ITERS + 1) / 2, | ||
39 | }, | ||
40 | }; | ||
41 | |||
42 | static int do_test(struct bpf_object *obj, int (*func)(void), | ||
43 | int expect) | ||
44 | { | ||
45 | struct record_opts opts = { | ||
46 | .target = { | ||
47 | .uid = UINT_MAX, | ||
48 | .uses_mmap = true, | ||
49 | }, | ||
50 | .freq = 0, | ||
51 | .mmap_pages = 256, | ||
52 | .default_interval = 1, | ||
53 | }; | ||
54 | |||
55 | char pid[16]; | ||
56 | char sbuf[STRERR_BUFSIZE]; | ||
57 | struct perf_evlist *evlist; | ||
58 | int i, ret = TEST_FAIL, err = 0, count = 0; | ||
59 | |||
60 | struct parse_events_evlist parse_evlist; | ||
61 | struct parse_events_error parse_error; | ||
62 | |||
63 | bzero(&parse_error, sizeof(parse_error)); | ||
64 | bzero(&parse_evlist, sizeof(parse_evlist)); | ||
65 | parse_evlist.error = &parse_error; | ||
66 | INIT_LIST_HEAD(&parse_evlist.list); | ||
67 | |||
68 | err = parse_events_load_bpf_obj(&parse_evlist, &parse_evlist.list, obj); | ||
69 | if (err || list_empty(&parse_evlist.list)) { | ||
70 | pr_debug("Failed to add events selected by BPF\n"); | ||
71 | if (!err) | ||
72 | return TEST_FAIL; | ||
73 | } | ||
74 | |||
75 | snprintf(pid, sizeof(pid), "%d", getpid()); | ||
76 | pid[sizeof(pid) - 1] = '\0'; | ||
77 | opts.target.tid = opts.target.pid = pid; | ||
78 | |||
79 | /* Instead of perf_evlist__new_default, don't add default events */ | ||
80 | evlist = perf_evlist__new(); | ||
81 | if (!evlist) { | ||
82 | pr_debug("No ehough memory to create evlist\n"); | ||
83 | return TEST_FAIL; | ||
84 | } | ||
85 | |||
86 | err = perf_evlist__create_maps(evlist, &opts.target); | ||
87 | if (err < 0) { | ||
88 | pr_debug("Not enough memory to create thread/cpu maps\n"); | ||
89 | goto out_delete_evlist; | ||
90 | } | ||
91 | |||
92 | perf_evlist__splice_list_tail(evlist, &parse_evlist.list); | ||
93 | evlist->nr_groups = parse_evlist.nr_groups; | ||
94 | |||
95 | perf_evlist__config(evlist, &opts); | ||
96 | |||
97 | err = perf_evlist__open(evlist); | ||
98 | if (err < 0) { | ||
99 | pr_debug("perf_evlist__open: %s\n", | ||
100 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
101 | goto out_delete_evlist; | ||
102 | } | ||
103 | |||
104 | err = perf_evlist__mmap(evlist, opts.mmap_pages, false); | ||
105 | if (err < 0) { | ||
106 | pr_debug("perf_evlist__mmap: %s\n", | ||
107 | strerror_r(errno, sbuf, sizeof(sbuf))); | ||
108 | goto out_delete_evlist; | ||
109 | } | ||
110 | |||
111 | perf_evlist__enable(evlist); | ||
112 | (*func)(); | ||
113 | perf_evlist__disable(evlist); | ||
114 | |||
115 | for (i = 0; i < evlist->nr_mmaps; i++) { | ||
116 | union perf_event *event; | ||
117 | |||
118 | while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) { | ||
119 | const u32 type = event->header.type; | ||
120 | |||
121 | if (type == PERF_RECORD_SAMPLE) | ||
122 | count ++; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | if (count != expect) | ||
127 | pr_debug("BPF filter result incorrect\n"); | ||
128 | |||
129 | ret = TEST_OK; | ||
130 | |||
131 | out_delete_evlist: | ||
132 | perf_evlist__delete(evlist); | ||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | static struct bpf_object * | ||
137 | prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name) | ||
138 | { | ||
139 | struct bpf_object *obj; | ||
140 | |||
141 | obj = bpf__prepare_load_buffer(obj_buf, obj_buf_sz, name); | ||
142 | if (IS_ERR(obj)) { | ||
143 | pr_debug("Compile BPF program failed.\n"); | ||
144 | return NULL; | ||
145 | } | ||
146 | return obj; | ||
147 | } | ||
148 | |||
149 | static int __test__bpf(int index) | ||
150 | { | ||
151 | int ret; | ||
152 | void *obj_buf; | ||
153 | size_t obj_buf_sz; | ||
154 | struct bpf_object *obj; | ||
155 | |||
156 | ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, | ||
157 | bpf_testcase_table[index].prog_id, | ||
158 | true); | ||
159 | if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { | ||
160 | pr_debug("Unable to get BPF object, %s\n", | ||
161 | bpf_testcase_table[index].msg_compile_fail); | ||
162 | if (index == 0) | ||
163 | return TEST_SKIP; | ||
164 | else | ||
165 | return TEST_FAIL; | ||
166 | } | ||
167 | |||
168 | obj = prepare_bpf(obj_buf, obj_buf_sz, | ||
169 | bpf_testcase_table[index].name); | ||
170 | if (!obj) { | ||
171 | ret = TEST_FAIL; | ||
172 | goto out; | ||
173 | } | ||
174 | |||
175 | ret = do_test(obj, | ||
176 | bpf_testcase_table[index].target_func, | ||
177 | bpf_testcase_table[index].expect_result); | ||
178 | out: | ||
179 | bpf__clear(); | ||
180 | return ret; | ||
181 | } | ||
182 | |||
183 | int test__bpf(void) | ||
184 | { | ||
185 | unsigned int i; | ||
186 | int err; | ||
187 | |||
188 | if (geteuid() != 0) { | ||
189 | pr_debug("Only root can run BPF test\n"); | ||
190 | return TEST_SKIP; | ||
191 | } | ||
192 | |||
193 | for (i = 0; i < ARRAY_SIZE(bpf_testcase_table); i++) { | ||
194 | err = __test__bpf(i); | ||
195 | |||
196 | if (err != TEST_OK) | ||
197 | return err; | ||
198 | } | ||
199 | |||
200 | return TEST_OK; | ||
201 | } | ||
202 | |||
203 | #else | ||
204 | int test__bpf(void) | ||
205 | { | ||
206 | pr_debug("Skip BPF test because BPF support is not compiled\n"); | ||
207 | return TEST_SKIP; | ||
208 | } | ||
209 | #endif | ||
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index 66f72d3d6677..7b0120abc137 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c | |||
@@ -166,6 +166,10 @@ static struct test generic_tests[] = { | |||
166 | .func = test_session_topology, | 166 | .func = test_session_topology, |
167 | }, | 167 | }, |
168 | { | 168 | { |
169 | .desc = "Test BPF filter", | ||
170 | .func = test__bpf, | ||
171 | }, | ||
172 | { | ||
169 | .func = NULL, | 173 | .func = NULL, |
170 | }, | 174 | }, |
171 | }; | 175 | }; |
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index c80486969f83..3c8734a3abbc 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h | |||
@@ -66,6 +66,7 @@ int test__fdarray__add(void); | |||
66 | int test__kmod_path__parse(void); | 66 | int test__kmod_path__parse(void); |
67 | int test__thread_map(void); | 67 | int test__thread_map(void); |
68 | int test__llvm(void); | 68 | int test__llvm(void); |
69 | int test__bpf(void); | ||
69 | int test_session_topology(void); | 70 | int test_session_topology(void); |
70 | 71 | ||
71 | #if defined(__arm__) || defined(__aarch64__) | 72 | #if defined(__arm__) || defined(__aarch64__) |
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index e3afa1b60bb5..4c50411371db 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c | |||
@@ -34,10 +34,32 @@ struct bpf_prog_priv { | |||
34 | struct perf_probe_event pev; | 34 | struct perf_probe_event pev; |
35 | }; | 35 | }; |
36 | 36 | ||
37 | static bool libbpf_initialized; | ||
38 | |||
39 | struct bpf_object * | ||
40 | bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, const char *name) | ||
41 | { | ||
42 | struct bpf_object *obj; | ||
43 | |||
44 | if (!libbpf_initialized) { | ||
45 | libbpf_set_print(libbpf_warning, | ||
46 | libbpf_info, | ||
47 | libbpf_debug); | ||
48 | libbpf_initialized = true; | ||
49 | } | ||
50 | |||
51 | obj = bpf_object__open_buffer(obj_buf, obj_buf_sz, name); | ||
52 | if (IS_ERR(obj)) { | ||
53 | pr_debug("bpf: failed to load buffer\n"); | ||
54 | return ERR_PTR(-EINVAL); | ||
55 | } | ||
56 | |||
57 | return obj; | ||
58 | } | ||
59 | |||
37 | struct bpf_object *bpf__prepare_load(const char *filename, bool source) | 60 | struct bpf_object *bpf__prepare_load(const char *filename, bool source) |
38 | { | 61 | { |
39 | struct bpf_object *obj; | 62 | struct bpf_object *obj; |
40 | static bool libbpf_initialized; | ||
41 | 63 | ||
42 | if (!libbpf_initialized) { | 64 | if (!libbpf_initialized) { |
43 | libbpf_set_print(libbpf_warning, | 65 | libbpf_set_print(libbpf_warning, |
diff --git a/tools/perf/util/bpf-loader.h b/tools/perf/util/bpf-loader.h index 5eb3629eed8b..9caf3ae4acf3 100644 --- a/tools/perf/util/bpf-loader.h +++ b/tools/perf/util/bpf-loader.h | |||
@@ -34,6 +34,9 @@ struct bpf_object *bpf__prepare_load(const char *filename, bool source); | |||
34 | int bpf__strerror_prepare_load(const char *filename, bool source, | 34 | int bpf__strerror_prepare_load(const char *filename, bool source, |
35 | int err, char *buf, size_t size); | 35 | int err, char *buf, size_t size); |
36 | 36 | ||
37 | struct bpf_object *bpf__prepare_load_buffer(void *obj_buf, size_t obj_buf_sz, | ||
38 | const char *name); | ||
39 | |||
37 | void bpf__clear(void); | 40 | void bpf__clear(void); |
38 | 41 | ||
39 | int bpf__probe(struct bpf_object *obj); | 42 | int bpf__probe(struct bpf_object *obj); |
@@ -55,6 +58,13 @@ bpf__prepare_load(const char *filename __maybe_unused, | |||
55 | return ERR_PTR(-ENOTSUP); | 58 | return ERR_PTR(-ENOTSUP); |
56 | } | 59 | } |
57 | 60 | ||
61 | static inline struct bpf_object * | ||
62 | bpf__prepare_load_buffer(void *obj_buf __maybe_unused, | ||
63 | size_t obj_buf_sz __maybe_unused) | ||
64 | { | ||
65 | return ERR_PTR(-ENOTSUP); | ||
66 | } | ||
67 | |||
58 | static inline void bpf__clear(void) { } | 68 | static inline void bpf__clear(void) { } |
59 | 69 | ||
60 | static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;} | 70 | static inline int bpf__probe(struct bpf_object *obj __maybe_unused) { return 0;} |