Commit | Line | Data |
---|---|---|
5875cf4c | 1 | // SPDX-License-Identifier: GPL-2.0 |
a43783ae | 2 | #include <errno.h> |
ba1fae43 WN |
3 | #include <stdio.h> |
4 | #include <sys/epoll.h> | |
7a8ef4c4 ACM |
5 | #include <sys/types.h> |
6 | #include <sys/stat.h> | |
bafae98e | 7 | #include <fcntl.h> |
6a7d550e | 8 | #include <util/util.h> |
ba1fae43 WN |
9 | #include <util/bpf-loader.h> |
10 | #include <util/evlist.h> | |
6a7d550e WN |
11 | #include <linux/bpf.h> |
12 | #include <linux/filter.h> | |
877a7a11 | 13 | #include <linux/kernel.h> |
a2630536 | 14 | #include <api/fs/fs.h> |
6a7d550e | 15 | #include <bpf/bpf.h> |
ba1fae43 WN |
16 | #include "tests.h" |
17 | #include "llvm.h" | |
18 | #include "debug.h" | |
19 | #define NR_ITERS 111 | |
a2630536 | 20 | #define PERF_TEST_BPF_PATH "/sys/fs/bpf/perf_test" |
ba1fae43 WN |
21 | |
22 | #ifdef HAVE_LIBBPF_SUPPORT | |
23 | ||
e0337f4f | 24 | static int epoll_pwait_loop(void) |
ba1fae43 WN |
25 | { |
26 | int i; | |
27 | ||
28 | /* Should fail NR_ITERS times */ | |
29 | for (i = 0; i < NR_ITERS; i++) | |
e0337f4f | 30 | epoll_pwait(-(i + 1), NULL, 0, 0, NULL); |
ba1fae43 WN |
31 | return 0; |
32 | } | |
33 | ||
bbb7d492 WN |
34 | #ifdef HAVE_BPF_PROLOGUE |
35 | ||
36 | static int llseek_loop(void) | |
37 | { | |
38 | int fds[2], i; | |
39 | ||
40 | fds[0] = open("/dev/null", O_RDONLY); | |
41 | fds[1] = open("/dev/null", O_RDWR); | |
42 | ||
43 | if (fds[0] < 0 || fds[1] < 0) | |
44 | return -1; | |
45 | ||
46 | for (i = 0; i < NR_ITERS; i++) { | |
47 | lseek(fds[i % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET); | |
48 | lseek(fds[(i + 1) % 2], i, (i / 2) % 2 ? SEEK_CUR : SEEK_SET); | |
49 | } | |
50 | close(fds[0]); | |
51 | close(fds[1]); | |
52 | return 0; | |
53 | } | |
54 | ||
55 | #endif | |
56 | ||
ba1fae43 WN |
57 | static struct { |
58 | enum test_llvm__testcase prog_id; | |
59 | const char *desc; | |
60 | const char *name; | |
61 | const char *msg_compile_fail; | |
62 | const char *msg_load_fail; | |
63 | int (*target_func)(void); | |
64 | int expect_result; | |
a2630536 | 65 | bool pin; |
ba1fae43 WN |
66 | } bpf_testcase_table[] = { |
67 | { | |
13cb2d0f ACM |
68 | .prog_id = LLVM_TESTCASE_BASE, |
69 | .desc = "Basic BPF filtering", | |
70 | .name = "[basic_bpf_test]", | |
71 | .msg_compile_fail = "fix 'perf test LLVM' first", | |
72 | .msg_load_fail = "load bpf object failed", | |
e0337f4f | 73 | .target_func = &epoll_pwait_loop, |
13cb2d0f | 74 | .expect_result = (NR_ITERS + 1) / 2, |
a2630536 JS |
75 | }, |
76 | { | |
13cb2d0f ACM |
77 | .prog_id = LLVM_TESTCASE_BASE, |
78 | .desc = "BPF pinning", | |
79 | .name = "[bpf_pinning]", | |
80 | .msg_compile_fail = "fix kbuild first", | |
81 | .msg_load_fail = "check your vmlinux setting?", | |
e0337f4f | 82 | .target_func = &epoll_pwait_loop, |
13cb2d0f ACM |
83 | .expect_result = (NR_ITERS + 1) / 2, |
84 | .pin = true, | |
ba1fae43 | 85 | }, |
bbb7d492 WN |
86 | #ifdef HAVE_BPF_PROLOGUE |
87 | { | |
13cb2d0f ACM |
88 | .prog_id = LLVM_TESTCASE_BPF_PROLOGUE, |
89 | .desc = "BPF prologue generation", | |
90 | .name = "[bpf_prologue_test]", | |
91 | .msg_compile_fail = "fix kbuild first", | |
92 | .msg_load_fail = "check your vmlinux setting?", | |
93 | .target_func = &llseek_loop, | |
94 | .expect_result = (NR_ITERS + 1) / 4, | |
bbb7d492 WN |
95 | }, |
96 | #endif | |
7b6982ce | 97 | { |
13cb2d0f ACM |
98 | .prog_id = LLVM_TESTCASE_BPF_RELOCATION, |
99 | .desc = "BPF relocation checker", | |
100 | .name = "[bpf_relocation_test]", | |
101 | .msg_compile_fail = "fix 'perf test LLVM' first", | |
102 | .msg_load_fail = "libbpf error when dealing with relocation", | |
7b6982ce | 103 | }, |
ba1fae43 WN |
104 | }; |
105 | ||
106 | static int do_test(struct bpf_object *obj, int (*func)(void), | |
107 | int expect) | |
108 | { | |
109 | struct record_opts opts = { | |
110 | .target = { | |
111 | .uid = UINT_MAX, | |
112 | .uses_mmap = true, | |
113 | }, | |
114 | .freq = 0, | |
115 | .mmap_pages = 256, | |
116 | .default_interval = 1, | |
117 | }; | |
118 | ||
119 | char pid[16]; | |
120 | char sbuf[STRERR_BUFSIZE]; | |
63503dba | 121 | struct evlist *evlist; |
ba1fae43 WN |
122 | int i, ret = TEST_FAIL, err = 0, count = 0; |
123 | ||
5d9cdc11 | 124 | struct parse_events_state parse_state; |
ba1fae43 WN |
125 | struct parse_events_error parse_error; |
126 | ||
127 | bzero(&parse_error, sizeof(parse_error)); | |
5d9cdc11 ACM |
128 | bzero(&parse_state, sizeof(parse_state)); |
129 | parse_state.error = &parse_error; | |
130 | INIT_LIST_HEAD(&parse_state.list); | |
ba1fae43 | 131 | |
5d9cdc11 ACM |
132 | err = parse_events_load_bpf_obj(&parse_state, &parse_state.list, obj, NULL); |
133 | if (err || list_empty(&parse_state.list)) { | |
ba1fae43 | 134 | pr_debug("Failed to add events selected by BPF\n"); |
ad0dd7ae | 135 | return TEST_FAIL; |
ba1fae43 WN |
136 | } |
137 | ||
138 | snprintf(pid, sizeof(pid), "%d", getpid()); | |
139 | pid[sizeof(pid) - 1] = '\0'; | |
140 | opts.target.tid = opts.target.pid = pid; | |
141 | ||
142 | /* Instead of perf_evlist__new_default, don't add default events */ | |
0f98b11c | 143 | evlist = evlist__new(); |
ba1fae43 | 144 | if (!evlist) { |
042cfb5f | 145 | pr_debug("Not enough memory to create evlist\n"); |
ba1fae43 WN |
146 | return TEST_FAIL; |
147 | } | |
148 | ||
149 | err = perf_evlist__create_maps(evlist, &opts.target); | |
150 | if (err < 0) { | |
151 | pr_debug("Not enough memory to create thread/cpu maps\n"); | |
152 | goto out_delete_evlist; | |
153 | } | |
154 | ||
5d9cdc11 ACM |
155 | perf_evlist__splice_list_tail(evlist, &parse_state.list); |
156 | evlist->nr_groups = parse_state.nr_groups; | |
ba1fae43 | 157 | |
e68ae9cf | 158 | perf_evlist__config(evlist, &opts, NULL); |
ba1fae43 WN |
159 | |
160 | err = perf_evlist__open(evlist); | |
161 | if (err < 0) { | |
162 | pr_debug("perf_evlist__open: %s\n", | |
c8b5f2c9 | 163 | str_error_r(errno, sbuf, sizeof(sbuf))); |
ba1fae43 WN |
164 | goto out_delete_evlist; |
165 | } | |
166 | ||
f74b9d3a | 167 | err = perf_evlist__mmap(evlist, opts.mmap_pages); |
ba1fae43 WN |
168 | if (err < 0) { |
169 | pr_debug("perf_evlist__mmap: %s\n", | |
c8b5f2c9 | 170 | str_error_r(errno, sbuf, sizeof(sbuf))); |
ba1fae43 WN |
171 | goto out_delete_evlist; |
172 | } | |
173 | ||
174 | perf_evlist__enable(evlist); | |
175 | (*func)(); | |
176 | perf_evlist__disable(evlist); | |
177 | ||
178 | for (i = 0; i < evlist->nr_mmaps; i++) { | |
179 | union perf_event *event; | |
2f54f3a4 | 180 | struct perf_mmap *md; |
ba1fae43 | 181 | |
2f54f3a4 | 182 | md = &evlist->mmap[i]; |
b9bae2c8 | 183 | if (perf_mmap__read_init(md) < 0) |
2f54f3a4 KL |
184 | continue; |
185 | ||
0019dc87 | 186 | while ((event = perf_mmap__read_event(md)) != NULL) { |
ba1fae43 WN |
187 | const u32 type = event->header.type; |
188 | ||
189 | if (type == PERF_RECORD_SAMPLE) | |
190 | count ++; | |
191 | } | |
2f54f3a4 | 192 | perf_mmap__read_done(md); |
ba1fae43 WN |
193 | } |
194 | ||
ad0dd7ae | 195 | if (count != expect) { |
6703c977 | 196 | pr_debug("BPF filter result incorrect, expected %d, got %d samples\n", expect, count); |
ad0dd7ae WN |
197 | goto out_delete_evlist; |
198 | } | |
ba1fae43 WN |
199 | |
200 | ret = TEST_OK; | |
201 | ||
202 | out_delete_evlist: | |
c12995a5 | 203 | evlist__delete(evlist); |
ba1fae43 WN |
204 | return ret; |
205 | } | |
206 | ||
207 | static struct bpf_object * | |
208 | prepare_bpf(void *obj_buf, size_t obj_buf_sz, const char *name) | |
209 | { | |
210 | struct bpf_object *obj; | |
211 | ||
212 | obj = bpf__prepare_load_buffer(obj_buf, obj_buf_sz, name); | |
213 | if (IS_ERR(obj)) { | |
214 | pr_debug("Compile BPF program failed.\n"); | |
215 | return NULL; | |
216 | } | |
217 | return obj; | |
218 | } | |
219 | ||
916d4092 | 220 | static int __test__bpf(int idx) |
ba1fae43 WN |
221 | { |
222 | int ret; | |
223 | void *obj_buf; | |
224 | size_t obj_buf_sz; | |
225 | struct bpf_object *obj; | |
226 | ||
227 | ret = test_llvm__fetch_bpf_obj(&obj_buf, &obj_buf_sz, | |
916d4092 | 228 | bpf_testcase_table[idx].prog_id, |
7b6982ce | 229 | true, NULL); |
ba1fae43 WN |
230 | if (ret != TEST_OK || !obj_buf || !obj_buf_sz) { |
231 | pr_debug("Unable to get BPF object, %s\n", | |
916d4092 ACM |
232 | bpf_testcase_table[idx].msg_compile_fail); |
233 | if (idx == 0) | |
ba1fae43 WN |
234 | return TEST_SKIP; |
235 | else | |
236 | return TEST_FAIL; | |
237 | } | |
238 | ||
239 | obj = prepare_bpf(obj_buf, obj_buf_sz, | |
916d4092 | 240 | bpf_testcase_table[idx].name); |
7b6982ce WN |
241 | if ((!!bpf_testcase_table[idx].target_func) != (!!obj)) { |
242 | if (!obj) | |
243 | pr_debug("Fail to load BPF object: %s\n", | |
244 | bpf_testcase_table[idx].msg_load_fail); | |
245 | else | |
246 | pr_debug("Success unexpectedly: %s\n", | |
247 | bpf_testcase_table[idx].msg_load_fail); | |
ba1fae43 WN |
248 | ret = TEST_FAIL; |
249 | goto out; | |
250 | } | |
251 | ||
a2630536 | 252 | if (obj) { |
7b6982ce WN |
253 | ret = do_test(obj, |
254 | bpf_testcase_table[idx].target_func, | |
255 | bpf_testcase_table[idx].expect_result); | |
a2630536 JS |
256 | if (ret != TEST_OK) |
257 | goto out; | |
258 | if (bpf_testcase_table[idx].pin) { | |
259 | int err; | |
260 | ||
261 | if (!bpf_fs__mount()) { | |
262 | pr_debug("BPF filesystem not mounted\n"); | |
263 | ret = TEST_FAIL; | |
264 | goto out; | |
265 | } | |
266 | err = mkdir(PERF_TEST_BPF_PATH, 0777); | |
267 | if (err && errno != EEXIST) { | |
268 | pr_debug("Failed to make perf_test dir: %s\n", | |
269 | strerror(errno)); | |
270 | ret = TEST_FAIL; | |
271 | goto out; | |
272 | } | |
273 | if (bpf_object__pin(obj, PERF_TEST_BPF_PATH)) | |
274 | ret = TEST_FAIL; | |
275 | if (rm_rf(PERF_TEST_BPF_PATH)) | |
276 | ret = TEST_FAIL; | |
277 | } | |
278 | } | |
279 | ||
ba1fae43 WN |
280 | out: |
281 | bpf__clear(); | |
282 | return ret; | |
283 | } | |
284 | ||
77a0cf68 WN |
285 | int test__bpf_subtest_get_nr(void) |
286 | { | |
287 | return (int)ARRAY_SIZE(bpf_testcase_table); | |
288 | } | |
289 | ||
290 | const char *test__bpf_subtest_get_desc(int i) | |
291 | { | |
292 | if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table)) | |
293 | return NULL; | |
294 | return bpf_testcase_table[i].desc; | |
295 | } | |
296 | ||
6a7d550e WN |
297 | static int check_env(void) |
298 | { | |
299 | int err; | |
300 | unsigned int kver_int; | |
301 | char license[] = "GPL"; | |
302 | ||
303 | struct bpf_insn insns[] = { | |
304 | BPF_MOV64_IMM(BPF_REG_0, 1), | |
305 | BPF_EXIT_INSN(), | |
306 | }; | |
307 | ||
308 | err = fetch_kernel_version(&kver_int, NULL, 0); | |
309 | if (err) { | |
310 | pr_debug("Unable to get kernel version\n"); | |
311 | return err; | |
312 | } | |
313 | ||
314 | err = bpf_load_program(BPF_PROG_TYPE_KPROBE, insns, | |
315 | sizeof(insns) / sizeof(insns[0]), | |
316 | license, kver_int, NULL, 0); | |
317 | if (err < 0) { | |
318 | pr_err("Missing basic BPF support, skip this test: %s\n", | |
319 | strerror(errno)); | |
320 | return err; | |
321 | } | |
322 | close(err); | |
323 | ||
324 | return 0; | |
325 | } | |
326 | ||
81f17c90 | 327 | int test__bpf(struct test *test __maybe_unused, int i) |
ba1fae43 | 328 | { |
ba1fae43 WN |
329 | int err; |
330 | ||
77a0cf68 WN |
331 | if (i < 0 || i >= (int)ARRAY_SIZE(bpf_testcase_table)) |
332 | return TEST_FAIL; | |
333 | ||
ba1fae43 WN |
334 | if (geteuid() != 0) { |
335 | pr_debug("Only root can run BPF test\n"); | |
336 | return TEST_SKIP; | |
337 | } | |
338 | ||
6a7d550e WN |
339 | if (check_env()) |
340 | return TEST_SKIP; | |
341 | ||
77a0cf68 WN |
342 | err = __test__bpf(i); |
343 | return err; | |
344 | } | |
ba1fae43 | 345 | |
77a0cf68 WN |
346 | #else |
347 | int test__bpf_subtest_get_nr(void) | |
348 | { | |
349 | return 0; | |
350 | } | |
ba1fae43 | 351 | |
77a0cf68 WN |
352 | const char *test__bpf_subtest_get_desc(int i __maybe_unused) |
353 | { | |
354 | return NULL; | |
ba1fae43 WN |
355 | } |
356 | ||
81f17c90 | 357 | int test__bpf(struct test *test __maybe_unused, int i __maybe_unused) |
ba1fae43 WN |
358 | { |
359 | pr_debug("Skip BPF test because BPF support is not compiled\n"); | |
360 | return TEST_SKIP; | |
361 | } | |
362 | #endif |