Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Improve by-name subtest selection logic in prog_tests

Improve subtest selection logic when using -t/-a/-d parameters.
In particular, more than one subtest can be specified or a
combination of tests / subtests.

-a send_signal -d send_signal/send_signal_nmi* - runs send_signal
test without nmi tests

-a send_signal/send_signal_nmi*,find_vma - runs two send_signal
subtests and find_vma test

-a 'send_signal*' -a find_vma -d send_signal/send_signal_nmi* -
runs 2 send_signal test and find_vma test. Disables two send_signal
nmi subtests

-t send_signal -t find_vma - runs two *send_signal* tests and one
*find_vma* test

This will allow us to have granular control over which subtests
to disable in the CI system instead of disabling whole tests.

Also, add new selftest to avoid possible regression when
changing prog_test test name selection logic.

Signed-off-by: Mykola Lysenko <mykolal@fb.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20220409001750.529930-1-mykolal@fb.com

authored by

Mykola Lysenko and committed by
Andrii Nakryiko
61ddff37 07385998

+288 -88
+107
tools/testing/selftests/bpf/prog_tests/arg_parsing.c
··· 1 + // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 + 3 + #include "test_progs.h" 4 + #include "testing_helpers.h" 5 + 6 + static void init_test_filter_set(struct test_filter_set *set) 7 + { 8 + set->cnt = 0; 9 + set->tests = NULL; 10 + } 11 + 12 + static void free_test_filter_set(struct test_filter_set *set) 13 + { 14 + int i, j; 15 + 16 + for (i = 0; i < set->cnt; i++) { 17 + for (j = 0; j < set->tests[i].subtest_cnt; j++) 18 + free((void *)set->tests[i].subtests[j]); 19 + free(set->tests[i].subtests); 20 + free(set->tests[i].name); 21 + } 22 + 23 + free(set->tests); 24 + init_test_filter_set(set); 25 + } 26 + 27 + static void test_parse_test_list(void) 28 + { 29 + struct test_filter_set set; 30 + 31 + init_test_filter_set(&set); 32 + 33 + ASSERT_OK(parse_test_list("arg_parsing", &set, true), "parsing"); 34 + if (!ASSERT_EQ(set.cnt, 1, "test filters count")) 35 + goto error; 36 + if (!ASSERT_OK_PTR(set.tests, "test filters initialized")) 37 + goto error; 38 + ASSERT_EQ(set.tests[0].subtest_cnt, 0, "subtest filters count"); 39 + ASSERT_OK(strcmp("arg_parsing", set.tests[0].name), "subtest name"); 40 + free_test_filter_set(&set); 41 + 42 + ASSERT_OK(parse_test_list("arg_parsing,bpf_cookie", &set, true), 43 + "parsing"); 44 + if (!ASSERT_EQ(set.cnt, 2, "count of test filters")) 45 + goto error; 46 + if (!ASSERT_OK_PTR(set.tests, "test filters initialized")) 47 + goto error; 48 + ASSERT_EQ(set.tests[0].subtest_cnt, 0, "subtest filters count"); 49 + ASSERT_EQ(set.tests[1].subtest_cnt, 0, "subtest filters count"); 50 + ASSERT_OK(strcmp("arg_parsing", set.tests[0].name), "test name"); 51 + ASSERT_OK(strcmp("bpf_cookie", set.tests[1].name), "test name"); 52 + free_test_filter_set(&set); 53 + 54 + ASSERT_OK(parse_test_list("arg_parsing/arg_parsing,bpf_cookie", 55 + &set, 56 + true), 57 + "parsing"); 58 + if (!ASSERT_EQ(set.cnt, 2, "count of test filters")) 59 + goto error; 60 + if (!ASSERT_OK_PTR(set.tests, "test filters initialized")) 61 + goto error; 62 + if (!ASSERT_EQ(set.tests[0].subtest_cnt, 1, "subtest filters count")) 63 + goto error; 64 + ASSERT_EQ(set.tests[1].subtest_cnt, 0, "subtest filters count"); 65 + ASSERT_OK(strcmp("arg_parsing", set.tests[0].name), "test name"); 66 + ASSERT_OK(strcmp("arg_parsing", set.tests[0].subtests[0]), 67 + "subtest name"); 68 + ASSERT_OK(strcmp("bpf_cookie", set.tests[1].name), "test name"); 69 + free_test_filter_set(&set); 70 + 71 + ASSERT_OK(parse_test_list("arg_parsing/arg_parsing", &set, true), 72 + "parsing"); 73 + ASSERT_OK(parse_test_list("bpf_cookie", &set, true), "parsing"); 74 + ASSERT_OK(parse_test_list("send_signal", &set, true), "parsing"); 75 + if (!ASSERT_EQ(set.cnt, 3, "count of test filters")) 76 + goto error; 77 + if (!ASSERT_OK_PTR(set.tests, "test filters initialized")) 78 + goto error; 79 + if (!ASSERT_EQ(set.tests[0].subtest_cnt, 1, "subtest filters count")) 80 + goto error; 81 + ASSERT_EQ(set.tests[1].subtest_cnt, 0, "subtest filters count"); 82 + ASSERT_EQ(set.tests[2].subtest_cnt, 0, "subtest filters count"); 83 + ASSERT_OK(strcmp("arg_parsing", set.tests[0].name), "test name"); 84 + ASSERT_OK(strcmp("arg_parsing", set.tests[0].subtests[0]), 85 + "subtest name"); 86 + ASSERT_OK(strcmp("bpf_cookie", set.tests[1].name), "test name"); 87 + ASSERT_OK(strcmp("send_signal", set.tests[2].name), "test name"); 88 + free_test_filter_set(&set); 89 + 90 + ASSERT_OK(parse_test_list("bpf_cookie/trace", &set, false), "parsing"); 91 + if (!ASSERT_EQ(set.cnt, 1, "count of test filters")) 92 + goto error; 93 + if (!ASSERT_OK_PTR(set.tests, "test filters initialized")) 94 + goto error; 95 + if (!ASSERT_EQ(set.tests[0].subtest_cnt, 1, "subtest filters count")) 96 + goto error; 97 + ASSERT_OK(strcmp("*bpf_cookie*", set.tests[0].name), "test name"); 98 + ASSERT_OK(strcmp("*trace*", set.tests[0].subtests[0]), "subtest name"); 99 + error: 100 + free_test_filter_set(&set); 101 + } 102 + 103 + void test_arg_parsing(void) 104 + { 105 + if (test__start_subtest("test_parse_test_list")) 106 + test_parse_test_list(); 107 + }
+74 -83
tools/testing/selftests/bpf/test_progs.c
··· 3 3 */ 4 4 #define _GNU_SOURCE 5 5 #include "test_progs.h" 6 + #include "testing_helpers.h" 6 7 #include "cgroup_helpers.h" 7 8 #include <argp.h> 8 9 #include <pthread.h> ··· 85 84 int i; 86 85 87 86 for (i = 0; i < sel->blacklist.cnt; i++) { 88 - if (glob_match(name, sel->blacklist.strs[i])) 87 + if (glob_match(name, sel->blacklist.tests[i].name) && 88 + !sel->blacklist.tests[i].subtest_cnt) 89 89 return false; 90 90 } 91 91 92 92 for (i = 0; i < sel->whitelist.cnt; i++) { 93 - if (glob_match(name, sel->whitelist.strs[i])) 93 + if (glob_match(name, sel->whitelist.tests[i].name)) 94 94 return true; 95 95 } 96 96 ··· 99 97 return true; 100 98 101 99 return num < sel->num_set_len && sel->num_set[num]; 100 + } 101 + 102 + static bool should_run_subtest(struct test_selector *sel, 103 + struct test_selector *subtest_sel, 104 + int subtest_num, 105 + const char *test_name, 106 + const char *subtest_name) 107 + { 108 + int i, j; 109 + 110 + for (i = 0; i < sel->blacklist.cnt; i++) { 111 + if (glob_match(test_name, sel->blacklist.tests[i].name)) { 112 + if (!sel->blacklist.tests[i].subtest_cnt) 113 + return false; 114 + 115 + for (j = 0; j < sel->blacklist.tests[i].subtest_cnt; j++) { 116 + if (glob_match(subtest_name, 117 + sel->blacklist.tests[i].subtests[j])) 118 + return false; 119 + } 120 + } 121 + } 122 + 123 + for (i = 0; i < sel->whitelist.cnt; i++) { 124 + if (glob_match(test_name, sel->whitelist.tests[i].name)) { 125 + if (!sel->whitelist.tests[i].subtest_cnt) 126 + return true; 127 + 128 + for (j = 0; j < sel->whitelist.tests[i].subtest_cnt; j++) { 129 + if (glob_match(subtest_name, 130 + sel->whitelist.tests[i].subtests[j])) 131 + return true; 132 + } 133 + } 134 + } 135 + 136 + if (!sel->whitelist.cnt && !subtest_sel->num_set) 137 + return true; 138 + 139 + return subtest_num < subtest_sel->num_set_len && subtest_sel->num_set[subtest_num]; 102 140 } 103 141 104 142 static void dump_test_log(const struct prog_test_def *test, bool failed) ··· 177 135 */ 178 136 static void reset_affinity(void) 179 137 { 180 - 181 138 cpu_set_t cpuset; 182 139 int i, err; 183 140 ··· 237 196 test->subtest_name = NULL; 238 197 } 239 198 240 - bool test__start_subtest(const char *name) 199 + bool test__start_subtest(const char *subtest_name) 241 200 { 242 201 struct prog_test_def *test = env.test; 243 202 ··· 246 205 247 206 test->subtest_num++; 248 207 249 - if (!name || !name[0]) { 208 + if (!subtest_name || !subtest_name[0]) { 250 209 fprintf(env.stderr, 251 210 "Subtest #%d didn't provide sub-test name!\n", 252 211 test->subtest_num); 253 212 return false; 254 213 } 255 214 256 - if (!should_run(&env.subtest_selector, test->subtest_num, name)) 215 + if (!should_run_subtest(&env.test_selector, 216 + &env.subtest_selector, 217 + test->subtest_num, 218 + test->test_name, 219 + subtest_name)) 257 220 return false; 258 221 259 - test->subtest_name = strdup(name); 222 + test->subtest_name = strdup(subtest_name); 260 223 if (!test->subtest_name) { 261 224 fprintf(env.stderr, 262 225 "Subtest #%d: failed to copy subtest name!\n", ··· 572 527 return 0; 573 528 } 574 529 575 - static void free_str_set(const struct str_set *set) 530 + static void free_test_filter_set(const struct test_filter_set *set) 576 531 { 577 - int i; 532 + int i, j; 578 533 579 534 if (!set) 580 535 return; 581 536 582 - for (i = 0; i < set->cnt; i++) 583 - free((void *)set->strs[i]); 584 - free(set->strs); 585 - } 537 + for (i = 0; i < set->cnt; i++) { 538 + free((void *)set->tests[i].name); 539 + for (j = 0; j < set->tests[i].subtest_cnt; j++) 540 + free((void *)set->tests[i].subtests[j]); 586 541 587 - static int parse_str_list(const char *s, struct str_set *set, bool is_glob_pattern) 588 - { 589 - char *input, *state = NULL, *next, **tmp, **strs = NULL; 590 - int i, cnt = 0; 591 - 592 - input = strdup(s); 593 - if (!input) 594 - return -ENOMEM; 595 - 596 - while ((next = strtok_r(state ? NULL : input, ",", &state))) { 597 - tmp = realloc(strs, sizeof(*strs) * (cnt + 1)); 598 - if (!tmp) 599 - goto err; 600 - strs = tmp; 601 - 602 - if (is_glob_pattern) { 603 - strs[cnt] = strdup(next); 604 - if (!strs[cnt]) 605 - goto err; 606 - } else { 607 - strs[cnt] = malloc(strlen(next) + 2 + 1); 608 - if (!strs[cnt]) 609 - goto err; 610 - sprintf(strs[cnt], "*%s*", next); 611 - } 612 - 613 - cnt++; 542 + free((void *)set->tests[i].subtests); 614 543 } 615 544 616 - tmp = realloc(set->strs, sizeof(*strs) * (cnt + set->cnt)); 617 - if (!tmp) 618 - goto err; 619 - memcpy(tmp + set->cnt, strs, sizeof(*strs) * cnt); 620 - set->strs = (const char **)tmp; 621 - set->cnt += cnt; 545 + free((void *)set->tests); 546 + } 622 547 623 - free(input); 624 - free(strs); 625 - return 0; 626 - err: 627 - for (i = 0; i < cnt; i++) 628 - free(strs[i]); 629 - free(strs); 630 - free(input); 631 - return -ENOMEM; 548 + static void free_test_selector(struct test_selector *test_selector) 549 + { 550 + free_test_filter_set(&test_selector->blacklist); 551 + free_test_filter_set(&test_selector->whitelist); 552 + free(test_selector->num_set); 632 553 } 633 554 634 555 extern int extra_prog_load_log_flags; ··· 626 615 } 627 616 case ARG_TEST_NAME_GLOB_ALLOWLIST: 628 617 case ARG_TEST_NAME: { 629 - char *subtest_str = strchr(arg, '/'); 630 - 631 - if (subtest_str) { 632 - *subtest_str = '\0'; 633 - if (parse_str_list(subtest_str + 1, 634 - &env->subtest_selector.whitelist, 635 - key == ARG_TEST_NAME_GLOB_ALLOWLIST)) 636 - return -ENOMEM; 637 - } 638 - if (parse_str_list(arg, &env->test_selector.whitelist, 639 - key == ARG_TEST_NAME_GLOB_ALLOWLIST)) 618 + if (parse_test_list(arg, 619 + &env->test_selector.whitelist, 620 + key == ARG_TEST_NAME_GLOB_ALLOWLIST)) 640 621 return -ENOMEM; 641 622 break; 642 623 } 643 624 case ARG_TEST_NAME_GLOB_DENYLIST: 644 625 case ARG_TEST_NAME_BLACKLIST: { 645 - char *subtest_str = strchr(arg, '/'); 646 - 647 - if (subtest_str) { 648 - *subtest_str = '\0'; 649 - if (parse_str_list(subtest_str + 1, 650 - &env->subtest_selector.blacklist, 651 - key == ARG_TEST_NAME_GLOB_DENYLIST)) 652 - return -ENOMEM; 653 - } 654 - if (parse_str_list(arg, &env->test_selector.blacklist, 655 - key == ARG_TEST_NAME_GLOB_DENYLIST)) 626 + if (parse_test_list(arg, 627 + &env->test_selector.blacklist, 628 + key == ARG_TEST_NAME_GLOB_DENYLIST)) 656 629 return -ENOMEM; 657 630 break; 658 631 } ··· 1488 1493 out: 1489 1494 if (!env.list_test_names && env.has_testmod) 1490 1495 unload_bpf_testmod(); 1491 - free_str_set(&env.test_selector.blacklist); 1492 - free_str_set(&env.test_selector.whitelist); 1493 - free(env.test_selector.num_set); 1494 - free_str_set(&env.subtest_selector.blacklist); 1495 - free_str_set(&env.subtest_selector.whitelist); 1496 - free(env.subtest_selector.num_set); 1496 + 1497 + free_test_selector(&env.test_selector); 1497 1498 1498 1499 if (env.succ_cnt + env.fail_cnt + env.skip_cnt == 0) 1499 1500 return EXIT_NO_TEST;
+10 -5
tools/testing/selftests/bpf/test_progs.h
··· 37 37 #include <bpf/bpf_endian.h> 38 38 #include "trace_helpers.h" 39 39 #include "testing_helpers.h" 40 - #include "flow_dissector_load.h" 41 40 42 41 enum verbosity { 43 42 VERBOSE_NONE, ··· 45 46 VERBOSE_SUPER, 46 47 }; 47 48 48 - struct str_set { 49 - const char **strs; 49 + struct test_filter { 50 + char *name; 51 + char **subtests; 52 + int subtest_cnt; 53 + }; 54 + 55 + struct test_filter_set { 56 + struct test_filter *tests; 50 57 int cnt; 51 58 }; 52 59 53 60 struct test_selector { 54 - struct str_set whitelist; 55 - struct str_set blacklist; 61 + struct test_filter_set whitelist; 62 + struct test_filter_set blacklist; 56 63 bool *num_set; 57 64 int num_set_len; 58 65 };
+89
tools/testing/selftests/bpf/testing_helpers.c
··· 6 6 #include <errno.h> 7 7 #include <bpf/bpf.h> 8 8 #include <bpf/libbpf.h> 9 + #include "test_progs.h" 9 10 #include "testing_helpers.h" 10 11 11 12 int parse_num_list(const char *s, bool **num_set, int *num_set_len) ··· 68 67 *num_set_len = set_len; 69 68 70 69 return 0; 70 + } 71 + 72 + int parse_test_list(const char *s, 73 + struct test_filter_set *set, 74 + bool is_glob_pattern) 75 + { 76 + char *input, *state = NULL, *next; 77 + struct test_filter *tmp, *tests = NULL; 78 + int i, j, cnt = 0; 79 + 80 + input = strdup(s); 81 + if (!input) 82 + return -ENOMEM; 83 + 84 + while ((next = strtok_r(state ? NULL : input, ",", &state))) { 85 + char *subtest_str = strchr(next, '/'); 86 + char *pattern = NULL; 87 + int glob_chars = 0; 88 + 89 + tmp = realloc(tests, sizeof(*tests) * (cnt + 1)); 90 + if (!tmp) 91 + goto err; 92 + tests = tmp; 93 + 94 + tests[cnt].subtest_cnt = 0; 95 + tests[cnt].subtests = NULL; 96 + 97 + if (is_glob_pattern) { 98 + pattern = "%s"; 99 + } else { 100 + pattern = "*%s*"; 101 + glob_chars = 2; 102 + } 103 + 104 + if (subtest_str) { 105 + char **tmp_subtests = NULL; 106 + int subtest_cnt = tests[cnt].subtest_cnt; 107 + 108 + *subtest_str = '\0'; 109 + subtest_str += 1; 110 + tmp_subtests = realloc(tests[cnt].subtests, 111 + sizeof(*tmp_subtests) * 112 + (subtest_cnt + 1)); 113 + if (!tmp_subtests) 114 + goto err; 115 + tests[cnt].subtests = tmp_subtests; 116 + 117 + tests[cnt].subtests[subtest_cnt] = 118 + malloc(strlen(subtest_str) + glob_chars + 1); 119 + if (!tests[cnt].subtests[subtest_cnt]) 120 + goto err; 121 + sprintf(tests[cnt].subtests[subtest_cnt], 122 + pattern, 123 + subtest_str); 124 + 125 + tests[cnt].subtest_cnt++; 126 + } 127 + 128 + tests[cnt].name = malloc(strlen(next) + glob_chars + 1); 129 + if (!tests[cnt].name) 130 + goto err; 131 + sprintf(tests[cnt].name, pattern, next); 132 + 133 + cnt++; 134 + } 135 + 136 + tmp = realloc(set->tests, sizeof(*tests) * (cnt + set->cnt)); 137 + if (!tmp) 138 + goto err; 139 + 140 + memcpy(tmp + set->cnt, tests, sizeof(*tests) * cnt); 141 + set->tests = tmp; 142 + set->cnt += cnt; 143 + 144 + free(tests); 145 + free(input); 146 + return 0; 147 + 148 + err: 149 + for (i = 0; i < cnt; i++) { 150 + for (j = 0; j < tests[i].subtest_cnt; j++) 151 + free(tests[i].subtests[j]); 152 + 153 + free(tests[i].name); 154 + } 155 + free(tests); 156 + free(input); 157 + return -ENOMEM; 71 158 } 72 159 73 160 __u32 link_info_prog_id(const struct bpf_link *link, struct bpf_link_info *info)
+8
tools/testing/selftests/bpf/testing_helpers.h
··· 12 12 size_t insns_cnt, const char *license, 13 13 __u32 kern_version, char *log_buf, 14 14 size_t log_buf_sz); 15 + 16 + /* 17 + * below function is exported for testing in prog_test test 18 + */ 19 + struct test_filter_set; 20 + int parse_test_list(const char *s, 21 + struct test_filter_set *test_set, 22 + bool is_glob_pattern);