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