]>
Commit | Line | Data |
---|---|---|
25763b3c | 1 | // SPDX-License-Identifier: GPL-2.0-only |
6882804c | 2 | /* Copyright (c) 2017 Facebook |
6882804c | 3 | */ |
fd27b183 | 4 | #define _GNU_SOURCE |
3f306588 | 5 | #include "test_progs.h" |
88dadc63 | 6 | #include "cgroup_helpers.h" |
fe8d662a | 7 | #include "bpf_rlimit.h" |
766f2a59 | 8 | #include <argp.h> |
fd27b183 AN |
9 | #include <pthread.h> |
10 | #include <sched.h> | |
9fb156bb | 11 | #include <signal.h> |
fd27b183 | 12 | #include <string.h> |
9fb156bb | 13 | #include <execinfo.h> /* backtrace */ |
635599ba | 14 | #include <linux/membarrier.h> |
6882804c | 15 | |
74339a8f YS |
16 | /* Adapted from perf/util/string.c */ |
17 | static bool glob_match(const char *str, const char *pat) | |
18 | { | |
19 | while (*str && *pat && *pat != '*') { | |
20 | if (*str != *pat) | |
21 | return false; | |
22 | str++; | |
23 | pat++; | |
24 | } | |
25 | /* Check wild card */ | |
26 | if (*pat == '*') { | |
27 | while (*pat == '*') | |
28 | pat++; | |
29 | if (!*pat) /* Tail wild card matches all */ | |
30 | return true; | |
31 | while (*str) | |
32 | if (glob_match(str++, pat)) | |
33 | return true; | |
34 | } | |
35 | return !*str && !*pat; | |
36 | } | |
37 | ||
3220fb66 | 38 | #define EXIT_NO_TEST 2 |
b8c50df0 | 39 | #define EXIT_ERR_SETUP_INFRA 3 |
3220fb66 | 40 | |
0ff97e56 | 41 | /* defined in test_progs.h */ |
b65053cd | 42 | struct test_env env = {}; |
0ff97e56 AN |
43 | |
44 | struct prog_test_def { | |
45 | const char *test_name; | |
46 | int test_num; | |
47 | void (*run_test)(void); | |
48 | bool force_log; | |
0ff97e56 | 49 | int error_cnt; |
cd9c21d7 | 50 | int skip_cnt; |
0ff97e56 | 51 | bool tested; |
88dadc63 | 52 | bool need_cgroup_cleanup; |
3a516a0a | 53 | |
f90415e9 | 54 | char *subtest_name; |
3a516a0a AN |
55 | int subtest_num; |
56 | ||
57 | /* store counts before subtest started */ | |
3a516a0a | 58 | int old_error_cnt; |
0ff97e56 AN |
59 | }; |
60 | ||
2b5cf9fb AN |
61 | /* Override C runtime library's usleep() implementation to ensure nanosleep() |
62 | * is always called. Usleep is frequently used in selftests as a way to | |
63 | * trigger kprobe and tracepoints. | |
64 | */ | |
65 | int usleep(useconds_t usec) | |
66 | { | |
41078907 AN |
67 | struct timespec ts = { |
68 | .tv_sec = usec / 1000000, | |
69 | .tv_nsec = (usec % 1000000) * 1000, | |
70 | }; | |
4e1fd25d | 71 | |
41078907 | 72 | return syscall(__NR_nanosleep, &ts, NULL); |
2b5cf9fb AN |
73 | } |
74 | ||
3a516a0a AN |
75 | static bool should_run(struct test_selector *sel, int num, const char *name) |
76 | { | |
b65053cd AN |
77 | int i; |
78 | ||
79 | for (i = 0; i < sel->blacklist.cnt; i++) { | |
74339a8f | 80 | if (glob_match(name, sel->blacklist.strs[i])) |
b65053cd AN |
81 | return false; |
82 | } | |
3a516a0a | 83 | |
b65053cd | 84 | for (i = 0; i < sel->whitelist.cnt; i++) { |
74339a8f | 85 | if (glob_match(name, sel->whitelist.strs[i])) |
b65053cd AN |
86 | return true; |
87 | } | |
88 | ||
89 | if (!sel->whitelist.cnt && !sel->num_set) | |
3a516a0a AN |
90 | return true; |
91 | ||
92 | return num < sel->num_set_len && sel->num_set[num]; | |
93 | } | |
94 | ||
95 | static void dump_test_log(const struct prog_test_def *test, bool failed) | |
96 | { | |
946152b3 SF |
97 | if (stdout == env.stdout) |
98 | return; | |
99 | ||
100 | fflush(stdout); /* exports env.log_buf & env.log_cnt */ | |
101 | ||
a8fdaad5 | 102 | if (env.verbosity > VERBOSE_NONE || test->force_log || failed) { |
3a516a0a | 103 | if (env.log_cnt) { |
d80507d1 | 104 | env.log_buf[env.log_cnt] = '\0'; |
946152b3 | 105 | fprintf(env.stdout, "%s", env.log_buf); |
3a516a0a | 106 | if (env.log_buf[env.log_cnt - 1] != '\n') |
946152b3 | 107 | fprintf(env.stdout, "\n"); |
3a516a0a | 108 | } |
3a516a0a | 109 | } |
946152b3 SF |
110 | |
111 | fseeko(stdout, 0, SEEK_SET); /* rewind */ | |
3a516a0a AN |
112 | } |
113 | ||
cd9c21d7 SF |
114 | static void skip_account(void) |
115 | { | |
116 | if (env.test->skip_cnt) { | |
117 | env.skip_cnt++; | |
118 | env.test->skip_cnt = 0; | |
119 | } | |
120 | } | |
121 | ||
fd27b183 AN |
122 | static void stdio_restore(void); |
123 | ||
124 | /* A bunch of tests set custom affinity per-thread and/or per-process. Reset | |
125 | * it after each test/sub-test. | |
126 | */ | |
127 | static void reset_affinity() { | |
128 | ||
129 | cpu_set_t cpuset; | |
130 | int i, err; | |
131 | ||
132 | CPU_ZERO(&cpuset); | |
133 | for (i = 0; i < env.nr_cpus; i++) | |
134 | CPU_SET(i, &cpuset); | |
135 | ||
136 | err = sched_setaffinity(0, sizeof(cpuset), &cpuset); | |
137 | if (err < 0) { | |
138 | stdio_restore(); | |
139 | fprintf(stderr, "Failed to reset process affinity: %d!\n", err); | |
b8c50df0 | 140 | exit(EXIT_ERR_SETUP_INFRA); |
fd27b183 AN |
141 | } |
142 | err = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset); | |
143 | if (err < 0) { | |
144 | stdio_restore(); | |
145 | fprintf(stderr, "Failed to reset thread affinity: %d!\n", err); | |
b8c50df0 | 146 | exit(EXIT_ERR_SETUP_INFRA); |
fd27b183 AN |
147 | } |
148 | } | |
149 | ||
811d7e37 MKL |
150 | static void save_netns(void) |
151 | { | |
152 | env.saved_netns_fd = open("/proc/self/ns/net", O_RDONLY); | |
153 | if (env.saved_netns_fd == -1) { | |
154 | perror("open(/proc/self/ns/net)"); | |
b8c50df0 | 155 | exit(EXIT_ERR_SETUP_INFRA); |
811d7e37 MKL |
156 | } |
157 | } | |
158 | ||
159 | static void restore_netns(void) | |
160 | { | |
161 | if (setns(env.saved_netns_fd, CLONE_NEWNET) == -1) { | |
162 | stdio_restore(); | |
163 | perror("setns(CLONE_NEWNS)"); | |
b8c50df0 | 164 | exit(EXIT_ERR_SETUP_INFRA); |
811d7e37 MKL |
165 | } |
166 | } | |
167 | ||
3a516a0a AN |
168 | void test__end_subtest() |
169 | { | |
170 | struct prog_test_def *test = env.test; | |
d38835b7 | 171 | int sub_error_cnt = test->error_cnt - test->old_error_cnt; |
3a516a0a | 172 | |
3a516a0a AN |
173 | dump_test_log(test, sub_error_cnt); |
174 | ||
99c4fd8b YS |
175 | fprintf(env.stdout, "#%d/%d %s/%s:%s\n", |
176 | test->test_num, test->subtest_num, test->test_name, test->subtest_name, | |
5ed31472 | 177 | sub_error_cnt ? "FAIL" : (test->skip_cnt ? "SKIP" : "OK")); |
f90415e9 | 178 | |
f667d1d6 YS |
179 | if (sub_error_cnt) |
180 | env.fail_cnt++; | |
181 | else if (test->skip_cnt == 0) | |
182 | env.sub_succ_cnt++; | |
183 | skip_account(); | |
184 | ||
f90415e9 AN |
185 | free(test->subtest_name); |
186 | test->subtest_name = NULL; | |
3a516a0a AN |
187 | } |
188 | ||
189 | bool test__start_subtest(const char *name) | |
190 | { | |
191 | struct prog_test_def *test = env.test; | |
192 | ||
f90415e9 | 193 | if (test->subtest_name) |
3a516a0a | 194 | test__end_subtest(); |
3a516a0a AN |
195 | |
196 | test->subtest_num++; | |
197 | ||
198 | if (!name || !name[0]) { | |
946152b3 SF |
199 | fprintf(env.stderr, |
200 | "Subtest #%d didn't provide sub-test name!\n", | |
3a516a0a AN |
201 | test->subtest_num); |
202 | return false; | |
203 | } | |
204 | ||
205 | if (!should_run(&env.subtest_selector, test->subtest_num, name)) | |
206 | return false; | |
207 | ||
f90415e9 AN |
208 | test->subtest_name = strdup(name); |
209 | if (!test->subtest_name) { | |
210 | fprintf(env.stderr, | |
211 | "Subtest #%d: failed to copy subtest name!\n", | |
212 | test->subtest_num); | |
213 | return false; | |
214 | } | |
d38835b7 | 215 | env.test->old_error_cnt = env.test->error_cnt; |
3a516a0a AN |
216 | |
217 | return true; | |
218 | } | |
219 | ||
0ff97e56 AN |
220 | void test__force_log() { |
221 | env.test->force_log = true; | |
222 | } | |
223 | ||
cd9c21d7 SF |
224 | void test__skip(void) |
225 | { | |
226 | env.test->skip_cnt++; | |
227 | } | |
228 | ||
d38835b7 SF |
229 | void test__fail(void) |
230 | { | |
231 | env.test->error_cnt++; | |
232 | } | |
233 | ||
88dadc63 SF |
234 | int test__join_cgroup(const char *path) |
235 | { | |
236 | int fd; | |
237 | ||
238 | if (!env.test->need_cgroup_cleanup) { | |
239 | if (setup_cgroup_environment()) { | |
240 | fprintf(stderr, | |
241 | "#%d %s: Failed to setup cgroup environment\n", | |
242 | env.test->test_num, env.test->test_name); | |
243 | return -1; | |
244 | } | |
245 | ||
246 | env.test->need_cgroup_cleanup = true; | |
247 | } | |
248 | ||
249 | fd = create_and_get_cgroup(path); | |
250 | if (fd < 0) { | |
251 | fprintf(stderr, | |
252 | "#%d %s: Failed to create cgroup '%s' (errno=%d)\n", | |
253 | env.test->test_num, env.test->test_name, path, errno); | |
254 | return fd; | |
255 | } | |
256 | ||
257 | if (join_cgroup(path)) { | |
258 | fprintf(stderr, | |
259 | "#%d %s: Failed to join cgroup '%s' (errno=%d)\n", | |
260 | env.test->test_num, env.test->test_name, path, errno); | |
261 | return -1; | |
262 | } | |
263 | ||
264 | return fd; | |
265 | } | |
266 | ||
3f306588 | 267 | int bpf_find_map(const char *test, struct bpf_object *obj, const char *name) |
8d48f5e4 AS |
268 | { |
269 | struct bpf_map *map; | |
270 | ||
271 | map = bpf_object__find_map_by_name(obj, name); | |
272 | if (!map) { | |
3e2671fb | 273 | fprintf(stdout, "%s:FAIL:map '%s' not found\n", test, name); |
d38835b7 | 274 | test__fail(); |
8d48f5e4 AS |
275 | return -1; |
276 | } | |
277 | return bpf_map__fd(map); | |
278 | } | |
279 | ||
173965fb YS |
280 | static bool is_jit_enabled(void) |
281 | { | |
282 | const char *jit_sysctl = "/proc/sys/net/core/bpf_jit_enable"; | |
283 | bool enabled = false; | |
284 | int sysctl_fd; | |
285 | ||
286 | sysctl_fd = open(jit_sysctl, 0, O_RDONLY); | |
287 | if (sysctl_fd != -1) { | |
288 | char tmpc; | |
289 | ||
290 | if (read(sysctl_fd, &tmpc, sizeof(tmpc)) == 1) | |
291 | enabled = (tmpc != '0'); | |
292 | close(sysctl_fd); | |
293 | } | |
294 | ||
295 | return enabled; | |
296 | } | |
297 | ||
615741d8 | 298 | int compare_map_keys(int map1_fd, int map2_fd) |
3ced9b60 YS |
299 | { |
300 | __u32 key, next_key; | |
81f77fd0 SL |
301 | char val_buf[PERF_MAX_STACK_DEPTH * |
302 | sizeof(struct bpf_stack_build_id)]; | |
3ced9b60 YS |
303 | int err; |
304 | ||
305 | err = bpf_map_get_next_key(map1_fd, NULL, &key); | |
306 | if (err) | |
307 | return err; | |
308 | err = bpf_map_lookup_elem(map2_fd, &key, val_buf); | |
309 | if (err) | |
310 | return err; | |
311 | ||
312 | while (bpf_map_get_next_key(map1_fd, &key, &next_key) == 0) { | |
313 | err = bpf_map_lookup_elem(map2_fd, &next_key, val_buf); | |
314 | if (err) | |
315 | return err; | |
316 | ||
317 | key = next_key; | |
318 | } | |
319 | if (errno != ENOENT) | |
320 | return -1; | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
615741d8 | 325 | int compare_stack_ips(int smap_fd, int amap_fd, int stack_trace_len) |
79b45350 YS |
326 | { |
327 | __u32 key, next_key, *cur_key_p, *next_key_p; | |
328 | char *val_buf1, *val_buf2; | |
329 | int i, err = 0; | |
330 | ||
331 | val_buf1 = malloc(stack_trace_len); | |
332 | val_buf2 = malloc(stack_trace_len); | |
333 | cur_key_p = NULL; | |
334 | next_key_p = &key; | |
335 | while (bpf_map_get_next_key(smap_fd, cur_key_p, next_key_p) == 0) { | |
336 | err = bpf_map_lookup_elem(smap_fd, next_key_p, val_buf1); | |
337 | if (err) | |
338 | goto out; | |
339 | err = bpf_map_lookup_elem(amap_fd, next_key_p, val_buf2); | |
340 | if (err) | |
341 | goto out; | |
342 | for (i = 0; i < stack_trace_len; i++) { | |
343 | if (val_buf1[i] != val_buf2[i]) { | |
344 | err = -1; | |
345 | goto out; | |
346 | } | |
347 | } | |
348 | key = *next_key_p; | |
349 | cur_key_p = &key; | |
350 | next_key_p = &next_key; | |
351 | } | |
352 | if (errno != ENOENT) | |
353 | err = -1; | |
354 | ||
355 | out: | |
356 | free(val_buf1); | |
357 | free(val_buf2); | |
358 | return err; | |
359 | } | |
360 | ||
615741d8 | 361 | int extract_build_id(char *build_id, size_t size) |
81f77fd0 SL |
362 | { |
363 | FILE *fp; | |
364 | char *line = NULL; | |
365 | size_t len = 0; | |
366 | ||
367 | fp = popen("readelf -n ./urandom_read | grep 'Build ID'", "r"); | |
368 | if (fp == NULL) | |
369 | return -1; | |
370 | ||
371 | if (getline(&line, &len, fp) == -1) | |
372 | goto err; | |
929f9338 | 373 | pclose(fp); |
81f77fd0 SL |
374 | |
375 | if (len > size) | |
376 | len = size; | |
377 | memcpy(build_id, line, len); | |
378 | build_id[len] = '\0'; | |
9f56bb53 | 379 | free(line); |
81f77fd0 SL |
380 | return 0; |
381 | err: | |
929f9338 | 382 | pclose(fp); |
81f77fd0 SL |
383 | return -1; |
384 | } | |
385 | ||
9f7fa225 AN |
386 | static int finit_module(int fd, const char *param_values, int flags) |
387 | { | |
388 | return syscall(__NR_finit_module, fd, param_values, flags); | |
389 | } | |
390 | ||
391 | static int delete_module(const char *name, int flags) | |
392 | { | |
393 | return syscall(__NR_delete_module, name, flags); | |
394 | } | |
395 | ||
635599ba AN |
396 | /* |
397 | * Trigger synchronize_rcu() in kernel. | |
398 | */ | |
399 | int kern_sync_rcu(void) | |
400 | { | |
401 | return syscall(__NR_membarrier, MEMBARRIER_CMD_SHARED, 0, 0); | |
402 | } | |
403 | ||
9f7fa225 AN |
404 | static void unload_bpf_testmod(void) |
405 | { | |
635599ba AN |
406 | if (kern_sync_rcu()) |
407 | fprintf(env.stderr, "Failed to trigger kernel-side RCU sync!\n"); | |
9f7fa225 AN |
408 | if (delete_module("bpf_testmod", 0)) { |
409 | if (errno == ENOENT) { | |
410 | if (env.verbosity > VERBOSE_NONE) | |
411 | fprintf(stdout, "bpf_testmod.ko is already unloaded.\n"); | |
412 | return; | |
413 | } | |
414 | fprintf(env.stderr, "Failed to unload bpf_testmod.ko from kernel: %d\n", -errno); | |
86ce322d | 415 | return; |
9f7fa225 AN |
416 | } |
417 | if (env.verbosity > VERBOSE_NONE) | |
418 | fprintf(stdout, "Successfully unloaded bpf_testmod.ko.\n"); | |
419 | } | |
420 | ||
421 | static int load_bpf_testmod(void) | |
422 | { | |
423 | int fd; | |
424 | ||
425 | /* ensure previous instance of the module is unloaded */ | |
426 | unload_bpf_testmod(); | |
427 | ||
428 | if (env.verbosity > VERBOSE_NONE) | |
429 | fprintf(stdout, "Loading bpf_testmod.ko...\n"); | |
430 | ||
431 | fd = open("bpf_testmod.ko", O_RDONLY); | |
432 | if (fd < 0) { | |
433 | fprintf(env.stderr, "Can't find bpf_testmod.ko kernel module: %d\n", -errno); | |
434 | return -ENOENT; | |
435 | } | |
436 | if (finit_module(fd, "", 0)) { | |
437 | fprintf(env.stderr, "Failed to load bpf_testmod.ko into the kernel: %d\n", -errno); | |
438 | close(fd); | |
439 | return -EINVAL; | |
440 | } | |
441 | close(fd); | |
442 | ||
443 | if (env.verbosity > VERBOSE_NONE) | |
444 | fprintf(stdout, "Successfully loaded bpf_testmod.ko.\n"); | |
445 | return 0; | |
446 | } | |
447 | ||
766f2a59 | 448 | /* extern declarations for test funcs */ |
0b6e71c3 | 449 | #define DEFINE_TEST(name) extern void test_##name(void); |
3f306588 | 450 | #include <prog_tests/tests.h> |
766f2a59 | 451 | #undef DEFINE_TEST |
3f306588 | 452 | |
766f2a59 | 453 | static struct prog_test_def prog_test_defs[] = { |
0ff97e56 AN |
454 | #define DEFINE_TEST(name) { \ |
455 | .test_name = #name, \ | |
456 | .run_test = &test_##name, \ | |
766f2a59 AN |
457 | }, |
458 | #include <prog_tests/tests.h> | |
459 | #undef DEFINE_TEST | |
460 | }; | |
0ff97e56 | 461 | const int prog_test_cnt = ARRAY_SIZE(prog_test_defs); |
766f2a59 AN |
462 | |
463 | const char *argp_program_version = "test_progs 0.1"; | |
464 | const char *argp_program_bug_address = "<bpf@vger.kernel.org>"; | |
465 | const char argp_program_doc[] = "BPF selftests test runner"; | |
466 | ||
467 | enum ARG_KEYS { | |
8160bae2 AN |
468 | ARG_TEST_NUM = 'n', |
469 | ARG_TEST_NAME = 't', | |
b65053cd | 470 | ARG_TEST_NAME_BLACKLIST = 'b', |
766f2a59 | 471 | ARG_VERIFIER_STATS = 's', |
329e38f7 | 472 | ARG_VERBOSE = 'v', |
643e7233 | 473 | ARG_GET_TEST_CNT = 'c', |
c1f1f365 | 474 | ARG_LIST_TEST_NAMES = 'l', |
74339a8f YS |
475 | ARG_TEST_NAME_GLOB_ALLOWLIST = 'a', |
476 | ARG_TEST_NAME_GLOB_DENYLIST = 'd', | |
766f2a59 | 477 | }; |
16e910d4 | 478 | |
766f2a59 | 479 | static const struct argp_option opts[] = { |
8160bae2 AN |
480 | { "num", ARG_TEST_NUM, "NUM", 0, |
481 | "Run test number NUM only " }, | |
b65053cd AN |
482 | { "name", ARG_TEST_NAME, "NAMES", 0, |
483 | "Run tests with names containing any string from NAMES list" }, | |
484 | { "name-blacklist", ARG_TEST_NAME_BLACKLIST, "NAMES", 0, | |
485 | "Don't run tests with names containing any string from NAMES list" }, | |
766f2a59 AN |
486 | { "verifier-stats", ARG_VERIFIER_STATS, NULL, 0, |
487 | "Output verifier statistics", }, | |
329e38f7 | 488 | { "verbose", ARG_VERBOSE, "LEVEL", OPTION_ARG_OPTIONAL, |
a8fdaad5 | 489 | "Verbose output (use -vv or -vvv for progressively verbose output)" }, |
643e7233 JDB |
490 | { "count", ARG_GET_TEST_CNT, NULL, 0, |
491 | "Get number of selected top-level tests " }, | |
c1f1f365 JDB |
492 | { "list", ARG_LIST_TEST_NAMES, NULL, 0, |
493 | "List test names that would run (without running them) " }, | |
74339a8f YS |
494 | { "allow", ARG_TEST_NAME_GLOB_ALLOWLIST, "NAMES", 0, |
495 | "Run tests with name matching the pattern (supports '*' wildcard)." }, | |
496 | { "deny", ARG_TEST_NAME_GLOB_DENYLIST, "NAMES", 0, | |
497 | "Don't run tests with name matching the pattern (supports '*' wildcard)." }, | |
766f2a59 AN |
498 | {}, |
499 | }; | |
500 | ||
329e38f7 AN |
501 | static int libbpf_print_fn(enum libbpf_print_level level, |
502 | const char *format, va_list args) | |
503 | { | |
a8fdaad5 | 504 | if (env.verbosity < VERBOSE_VERY && level == LIBBPF_DEBUG) |
329e38f7 | 505 | return 0; |
3e2671fb | 506 | vfprintf(stdout, format, args); |
0ff97e56 | 507 | return 0; |
329e38f7 AN |
508 | } |
509 | ||
f25d5416 AN |
510 | static void free_str_set(const struct str_set *set) |
511 | { | |
512 | int i; | |
513 | ||
514 | if (!set) | |
515 | return; | |
516 | ||
517 | for (i = 0; i < set->cnt; i++) | |
518 | free((void *)set->strs[i]); | |
519 | free(set->strs); | |
520 | } | |
521 | ||
74339a8f | 522 | static int parse_str_list(const char *s, struct str_set *set, bool is_glob_pattern) |
b65053cd AN |
523 | { |
524 | char *input, *state = NULL, *next, **tmp, **strs = NULL; | |
74339a8f | 525 | int i, cnt = 0; |
b65053cd AN |
526 | |
527 | input = strdup(s); | |
528 | if (!input) | |
529 | return -ENOMEM; | |
530 | ||
b65053cd AN |
531 | while ((next = strtok_r(state ? NULL : input, ",", &state))) { |
532 | tmp = realloc(strs, sizeof(*strs) * (cnt + 1)); | |
533 | if (!tmp) | |
534 | goto err; | |
535 | strs = tmp; | |
536 | ||
74339a8f YS |
537 | if (is_glob_pattern) { |
538 | strs[cnt] = strdup(next); | |
539 | if (!strs[cnt]) | |
540 | goto err; | |
541 | } else { | |
542 | strs[cnt] = malloc(strlen(next) + 2 + 1); | |
543 | if (!strs[cnt]) | |
544 | goto err; | |
545 | sprintf(strs[cnt], "*%s*", next); | |
546 | } | |
b65053cd AN |
547 | |
548 | cnt++; | |
549 | } | |
550 | ||
74339a8f YS |
551 | tmp = realloc(set->strs, sizeof(*strs) * (cnt + set->cnt)); |
552 | if (!tmp) | |
553 | goto err; | |
554 | memcpy(tmp + set->cnt, strs, sizeof(*strs) * cnt); | |
555 | set->strs = (const char **)tmp; | |
556 | set->cnt += cnt; | |
557 | ||
b65053cd | 558 | free(input); |
74339a8f | 559 | free(strs); |
b65053cd AN |
560 | return 0; |
561 | err: | |
74339a8f YS |
562 | for (i = 0; i < cnt; i++) |
563 | free(strs[i]); | |
b65053cd AN |
564 | free(strs); |
565 | free(input); | |
566 | return -ENOMEM; | |
567 | } | |
568 | ||
a8fdaad5 AN |
569 | extern int extra_prog_load_log_flags; |
570 | ||
766f2a59 | 571 | static error_t parse_arg(int key, char *arg, struct argp_state *state) |
6882804c | 572 | { |
766f2a59 AN |
573 | struct test_env *env = state->input; |
574 | ||
575 | switch (key) { | |
8160bae2 | 576 | case ARG_TEST_NUM: { |
3a516a0a | 577 | char *subtest_str = strchr(arg, '/'); |
8160bae2 | 578 | |
3a516a0a AN |
579 | if (subtest_str) { |
580 | *subtest_str = '\0'; | |
581 | if (parse_num_list(subtest_str + 1, | |
cd49291c AN |
582 | &env->subtest_selector.num_set, |
583 | &env->subtest_selector.num_set_len)) { | |
3a516a0a AN |
584 | fprintf(stderr, |
585 | "Failed to parse subtest numbers.\n"); | |
586 | return -EINVAL; | |
587 | } | |
588 | } | |
cd49291c AN |
589 | if (parse_num_list(arg, &env->test_selector.num_set, |
590 | &env->test_selector.num_set_len)) { | |
3a516a0a AN |
591 | fprintf(stderr, "Failed to parse test numbers.\n"); |
592 | return -EINVAL; | |
593 | } | |
8160bae2 AN |
594 | break; |
595 | } | |
74339a8f | 596 | case ARG_TEST_NAME_GLOB_ALLOWLIST: |
3a516a0a AN |
597 | case ARG_TEST_NAME: { |
598 | char *subtest_str = strchr(arg, '/'); | |
599 | ||
600 | if (subtest_str) { | |
601 | *subtest_str = '\0'; | |
b65053cd | 602 | if (parse_str_list(subtest_str + 1, |
74339a8f YS |
603 | &env->subtest_selector.whitelist, |
604 | key == ARG_TEST_NAME_GLOB_ALLOWLIST)) | |
b65053cd AN |
605 | return -ENOMEM; |
606 | } | |
74339a8f YS |
607 | if (parse_str_list(arg, &env->test_selector.whitelist, |
608 | key == ARG_TEST_NAME_GLOB_ALLOWLIST)) | |
b65053cd AN |
609 | return -ENOMEM; |
610 | break; | |
611 | } | |
74339a8f | 612 | case ARG_TEST_NAME_GLOB_DENYLIST: |
b65053cd AN |
613 | case ARG_TEST_NAME_BLACKLIST: { |
614 | char *subtest_str = strchr(arg, '/'); | |
615 | ||
616 | if (subtest_str) { | |
617 | *subtest_str = '\0'; | |
618 | if (parse_str_list(subtest_str + 1, | |
74339a8f YS |
619 | &env->subtest_selector.blacklist, |
620 | key == ARG_TEST_NAME_GLOB_DENYLIST)) | |
3a516a0a AN |
621 | return -ENOMEM; |
622 | } | |
74339a8f YS |
623 | if (parse_str_list(arg, &env->test_selector.blacklist, |
624 | key == ARG_TEST_NAME_GLOB_DENYLIST)) | |
3a516a0a | 625 | return -ENOMEM; |
8160bae2 | 626 | break; |
3a516a0a | 627 | } |
766f2a59 AN |
628 | case ARG_VERIFIER_STATS: |
629 | env->verifier_stats = true; | |
630 | break; | |
329e38f7 | 631 | case ARG_VERBOSE: |
a8fdaad5 | 632 | env->verbosity = VERBOSE_NORMAL; |
329e38f7 AN |
633 | if (arg) { |
634 | if (strcmp(arg, "v") == 0) { | |
a8fdaad5 AN |
635 | env->verbosity = VERBOSE_VERY; |
636 | extra_prog_load_log_flags = 1; | |
637 | } else if (strcmp(arg, "vv") == 0) { | |
638 | env->verbosity = VERBOSE_SUPER; | |
639 | extra_prog_load_log_flags = 2; | |
329e38f7 AN |
640 | } else { |
641 | fprintf(stderr, | |
642 | "Unrecognized verbosity setting ('%s'), only -v and -vv are supported\n", | |
643 | arg); | |
644 | return -EINVAL; | |
645 | } | |
646 | } | |
b4fe9fec KS |
647 | |
648 | if (env->verbosity > VERBOSE_NONE) { | |
649 | if (setenv("SELFTESTS_VERBOSE", "1", 1) == -1) { | |
650 | fprintf(stderr, | |
651 | "Unable to setenv SELFTESTS_VERBOSE=1 (errno=%d)", | |
652 | errno); | |
653 | return -1; | |
654 | } | |
655 | } | |
656 | ||
329e38f7 | 657 | break; |
643e7233 JDB |
658 | case ARG_GET_TEST_CNT: |
659 | env->get_test_cnt = true; | |
660 | break; | |
c1f1f365 JDB |
661 | case ARG_LIST_TEST_NAMES: |
662 | env->list_test_names = true; | |
663 | break; | |
766f2a59 AN |
664 | case ARGP_KEY_ARG: |
665 | argp_usage(state); | |
666 | break; | |
667 | case ARGP_KEY_END: | |
668 | break; | |
669 | default: | |
670 | return ARGP_ERR_UNKNOWN; | |
671 | } | |
672 | return 0; | |
673 | } | |
674 | ||
946152b3 SF |
675 | static void stdio_hijack(void) |
676 | { | |
677 | #ifdef __GLIBC__ | |
678 | env.stdout = stdout; | |
679 | env.stderr = stderr; | |
680 | ||
a8fdaad5 | 681 | if (env.verbosity > VERBOSE_NONE) { |
946152b3 SF |
682 | /* nothing to do, output to stdout by default */ |
683 | return; | |
684 | } | |
685 | ||
686 | /* stdout and stderr -> buffer */ | |
687 | fflush(stdout); | |
688 | ||
689 | stdout = open_memstream(&env.log_buf, &env.log_cnt); | |
690 | if (!stdout) { | |
691 | stdout = env.stdout; | |
692 | perror("open_memstream"); | |
693 | return; | |
694 | } | |
695 | ||
696 | stderr = stdout; | |
697 | #endif | |
698 | } | |
699 | ||
700 | static void stdio_restore(void) | |
701 | { | |
702 | #ifdef __GLIBC__ | |
703 | if (stdout == env.stdout) | |
704 | return; | |
705 | ||
706 | fclose(stdout); | |
707 | free(env.log_buf); | |
708 | ||
709 | env.log_buf = NULL; | |
710 | env.log_cnt = 0; | |
711 | ||
712 | stdout = env.stdout; | |
713 | stderr = env.stderr; | |
714 | #endif | |
715 | } | |
716 | ||
0b6e71c3 AN |
717 | /* |
718 | * Determine if test_progs is running as a "flavored" test runner and switch | |
719 | * into corresponding sub-directory to load correct BPF objects. | |
720 | * | |
721 | * This is done by looking at executable name. If it contains "-flavor" | |
722 | * suffix, then we are running as a flavored test runner. | |
723 | */ | |
724 | int cd_flavor_subdir(const char *exec_name) | |
725 | { | |
726 | /* General form of argv[0] passed here is: | |
727 | * some/path/to/test_progs[-flavor], where -flavor part is optional. | |
728 | * First cut out "test_progs[-flavor]" part, then extract "flavor" | |
729 | * part, if it's there. | |
730 | */ | |
731 | const char *flavor = strrchr(exec_name, '/'); | |
732 | ||
733 | if (!flavor) | |
734 | return 0; | |
735 | flavor++; | |
736 | flavor = strrchr(flavor, '-'); | |
737 | if (!flavor) | |
738 | return 0; | |
739 | flavor++; | |
2b10af31 JDB |
740 | if (env.verbosity > VERBOSE_NONE) |
741 | fprintf(stdout, "Switching to flavor '%s' subdirectory...\n", flavor); | |
742 | ||
0b6e71c3 AN |
743 | return chdir(flavor); |
744 | } | |
745 | ||
9fb156bb AN |
746 | #define MAX_BACKTRACE_SZ 128 |
747 | void crash_handler(int signum) | |
748 | { | |
749 | void *bt[MAX_BACKTRACE_SZ]; | |
750 | size_t sz; | |
751 | ||
752 | sz = backtrace(bt, ARRAY_SIZE(bt)); | |
753 | ||
754 | if (env.test) | |
755 | dump_test_log(env.test, true); | |
756 | if (env.stdout) | |
757 | stdio_restore(); | |
758 | ||
759 | fprintf(stderr, "Caught signal #%d!\nStack trace:\n", signum); | |
760 | backtrace_symbols_fd(bt, sz, STDERR_FILENO); | |
761 | } | |
762 | ||
766f2a59 AN |
763 | int main(int argc, char **argv) |
764 | { | |
765 | static const struct argp argp = { | |
766 | .options = opts, | |
767 | .parser = parse_arg, | |
768 | .doc = argp_program_doc, | |
769 | }; | |
9fb156bb AN |
770 | struct sigaction sigact = { |
771 | .sa_handler = crash_handler, | |
772 | .sa_flags = SA_RESETHAND, | |
773 | }; | |
766f2a59 AN |
774 | int err, i; |
775 | ||
9fb156bb AN |
776 | sigaction(SIGSEGV, &sigact, NULL); |
777 | ||
766f2a59 AN |
778 | err = argp_parse(&argp, argc, argv, 0, NULL, &env); |
779 | if (err) | |
780 | return err; | |
781 | ||
0b6e71c3 AN |
782 | err = cd_flavor_subdir(argv[0]); |
783 | if (err) | |
784 | return err; | |
785 | ||
bad2e478 AN |
786 | /* Use libbpf 1.0 API mode */ |
787 | libbpf_set_strict_mode(LIBBPF_STRICT_ALL); | |
788 | ||
329e38f7 AN |
789 | libbpf_set_print(libbpf_print_fn); |
790 | ||
43b987d2 MV |
791 | srand(time(NULL)); |
792 | ||
0ff97e56 | 793 | env.jit_enabled = is_jit_enabled(); |
fd27b183 AN |
794 | env.nr_cpus = libbpf_num_possible_cpus(); |
795 | if (env.nr_cpus < 0) { | |
796 | fprintf(stderr, "Failed to get number of CPUs: %d!\n", | |
797 | env.nr_cpus); | |
798 | return -1; | |
799 | } | |
173965fb | 800 | |
811d7e37 | 801 | save_netns(); |
946152b3 | 802 | stdio_hijack(); |
9f7fa225 | 803 | env.has_testmod = true; |
26d82640 | 804 | if (!env.list_test_names && load_bpf_testmod()) { |
9f7fa225 AN |
805 | fprintf(env.stderr, "WARNING! Selftests relying on bpf_testmod.ko will be skipped.\n"); |
806 | env.has_testmod = false; | |
807 | } | |
0ff97e56 AN |
808 | for (i = 0; i < prog_test_cnt; i++) { |
809 | struct prog_test_def *test = &prog_test_defs[i]; | |
8160bae2 | 810 | |
0ff97e56 | 811 | env.test = test; |
8160bae2 AN |
812 | test->test_num = i + 1; |
813 | ||
3a516a0a AN |
814 | if (!should_run(&env.test_selector, |
815 | test->test_num, test->test_name)) | |
8160bae2 AN |
816 | continue; |
817 | ||
643e7233 JDB |
818 | if (env.get_test_cnt) { |
819 | env.succ_cnt++; | |
820 | continue; | |
821 | } | |
822 | ||
c1f1f365 JDB |
823 | if (env.list_test_names) { |
824 | fprintf(env.stdout, "%s\n", test->test_name); | |
825 | env.succ_cnt++; | |
826 | continue; | |
827 | } | |
828 | ||
8160bae2 | 829 | test->run_test(); |
3a516a0a AN |
830 | /* ensure last sub-test is finalized properly */ |
831 | if (test->subtest_name) | |
832 | test__end_subtest(); | |
833 | ||
0ff97e56 | 834 | test->tested = true; |
0ff97e56 | 835 | |
3a516a0a | 836 | dump_test_log(test, test->error_cnt); |
0ff97e56 | 837 | |
946152b3 SF |
838 | fprintf(env.stdout, "#%d %s:%s\n", |
839 | test->test_num, test->test_name, | |
f667d1d6 YS |
840 | test->error_cnt ? "FAIL" : (test->skip_cnt ? "SKIP" : "OK")); |
841 | ||
842 | if (test->error_cnt) | |
843 | env.fail_cnt++; | |
844 | else | |
845 | env.succ_cnt++; | |
846 | skip_account(); | |
88dadc63 | 847 | |
fd27b183 | 848 | reset_affinity(); |
811d7e37 | 849 | restore_netns(); |
88dadc63 SF |
850 | if (test->need_cgroup_cleanup) |
851 | cleanup_cgroup_environment(); | |
766f2a59 | 852 | } |
26d82640 | 853 | if (!env.list_test_names && env.has_testmod) |
9f7fa225 | 854 | unload_bpf_testmod(); |
946152b3 | 855 | stdio_restore(); |
643e7233 JDB |
856 | |
857 | if (env.get_test_cnt) { | |
858 | printf("%d\n", env.succ_cnt); | |
859 | goto out; | |
860 | } | |
861 | ||
c1f1f365 JDB |
862 | if (env.list_test_names) |
863 | goto out; | |
864 | ||
3e2671fb AN |
865 | fprintf(stdout, "Summary: %d/%d PASSED, %d SKIPPED, %d FAILED\n", |
866 | env.succ_cnt, env.sub_succ_cnt, env.skip_cnt, env.fail_cnt); | |
0ff97e56 | 867 | |
643e7233 | 868 | out: |
f25d5416 AN |
869 | free_str_set(&env.test_selector.blacklist); |
870 | free_str_set(&env.test_selector.whitelist); | |
3a516a0a | 871 | free(env.test_selector.num_set); |
f25d5416 AN |
872 | free_str_set(&env.subtest_selector.blacklist); |
873 | free_str_set(&env.subtest_selector.whitelist); | |
3a516a0a | 874 | free(env.subtest_selector.num_set); |
811d7e37 | 875 | close(env.saved_netns_fd); |
6882804c | 876 | |
6c92bd5c | 877 | if (env.succ_cnt + env.fail_cnt + env.skip_cnt == 0) |
3220fb66 | 878 | return EXIT_NO_TEST; |
6c92bd5c | 879 | |
d38835b7 | 880 | return env.fail_cnt ? EXIT_FAILURE : EXIT_SUCCESS; |
6882804c | 881 | } |