Add an ability to specify messages that should not be found in the test verifier log. Similar to LLVM's FileCheck tool for the following test specification: __success __msg("a") __not_msg("b") __msg("c") void foo(...) { ... } - message "a" is expected to be in the log; - message "b" is not expected after message "a" (but could be present before "a"); - message "c" is expected to be in the log after message "a". Signed-off-by: Eduard Zingerman <eddyz87@xxxxxxxxx> --- tools/testing/selftests/bpf/progs/bpf_misc.h | 9 +++ tools/testing/selftests/bpf/test_loader.c | 82 ++++++++++++++------ 2 files changed, 68 insertions(+), 23 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index 799fff4995d8..f24fcda6fc0b 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -22,7 +22,13 @@ * * __msg Message expected to be found in the verifier log. * Multiple __msg attributes could be specified. + * When multiple messages are specified they are + * matched one after another. + * __not_msg Message not expected to be found in the verifier log. + * Matched from the end of the last checked __msg or + * from log start, if no __msg had been matched yet. * __msg_unpriv Same as __msg but for unprivileged mode. + * __not_msg_unpriv Same as __not_msg but for unprivileged mode. * * __success Expect program load success in privileged mode. * __success_unpriv Expect program load success in unprivileged mode. @@ -59,10 +65,13 @@ * __auxiliary_unpriv Same, but load program in unprivileged mode. */ #define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg))) +#define __not_msg(msg) __attribute__((btf_decl_tag("comment:test_dont_expect_msg=" msg))) #define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) #define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) #define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc))) #define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" msg))) +#define __not_msg_unpriv(msg) \ + __attribute__((btf_decl_tag("comment:test_dont_expect_msg_unpriv=" msg))) #define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv"))) #define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv"))) #define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 37ffa57f28a1..def16d9aeae2 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -17,9 +17,11 @@ #define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure" #define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" #define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" +#define TEST_TAG_DONT_EXPECT_MSG_PFX "comment:test_dont_expect_msg=" #define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv" #define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv" #define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv=" +#define TEST_TAG_DONT_EXPECT_MSG_PFX_UNPRIV "comment:test_dont_expect_msg_unpriv=" #define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" #define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags=" #define TEST_TAG_DESCRIPTION_PFX "comment:test_description=" @@ -45,10 +47,15 @@ enum mode { UNPRIV = 2 }; +struct pattern { + const char *str; + bool expected; +}; + struct test_subspec { char *name; bool expect_failure; - const char **expect_msgs; + struct pattern *expect_msgs; size_t expect_msg_cnt; int retval; bool execute; @@ -98,17 +105,21 @@ static void free_test_spec(struct test_spec *spec) spec->unpriv.expect_msgs = NULL; } -static int push_msg(const char *msg, struct test_subspec *subspec) +static int push_msg(const char *msg, struct test_subspec *subspec, bool expected) { + size_t cnt = subspec->expect_msg_cnt; void *tmp; - tmp = realloc(subspec->expect_msgs, (1 + subspec->expect_msg_cnt) * sizeof(void *)); + tmp = realloc(subspec->expect_msgs, + (1 + subspec->expect_msg_cnt) * sizeof(*subspec->expect_msgs)); if (!tmp) { ASSERT_FAIL("failed to realloc memory for messages\n"); return -ENOMEM; } subspec->expect_msgs = tmp; - subspec->expect_msgs[subspec->expect_msg_cnt++] = msg; + subspec->expect_msgs[cnt].str = msg; + subspec->expect_msgs[cnt].expected = expected; + subspec->expect_msg_cnt++; return 0; } @@ -221,13 +232,25 @@ static int parse_test_spec(struct test_loader *tester, spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; - err = push_msg(msg, &spec->priv); + err = push_msg(msg, &spec->priv, true); if (err) goto cleanup; spec->mode_mask |= PRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV)) { msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX_UNPRIV) - 1; - err = push_msg(msg, &spec->unpriv); + err = push_msg(msg, &spec->unpriv, true); + if (err) + goto cleanup; + spec->mode_mask |= UNPRIV; + } else if (str_has_pfx(s, TEST_TAG_DONT_EXPECT_MSG_PFX)) { + msg = s + sizeof(TEST_TAG_DONT_EXPECT_MSG_PFX) - 1; + err = push_msg(msg, &spec->priv, false); + if (err) + goto cleanup; + spec->mode_mask |= PRIV; + } else if (str_has_pfx(s, TEST_TAG_DONT_EXPECT_MSG_PFX_UNPRIV)) { + msg = s + sizeof(TEST_TAG_DONT_EXPECT_MSG_PFX_UNPRIV) - 1; + err = push_msg(msg, &spec->unpriv, false); if (err) goto cleanup; spec->mode_mask |= UNPRIV; @@ -316,7 +339,7 @@ static int parse_test_spec(struct test_loader *tester, } if (!spec->unpriv.expect_msgs) { - size_t sz = spec->priv.expect_msg_cnt * sizeof(void *); + size_t sz = spec->priv.expect_msg_cnt * sizeof(*spec->priv.expect_msgs); spec->unpriv.expect_msgs = malloc(sz); if (!spec->unpriv.expect_msgs) { @@ -375,33 +398,46 @@ static void emit_verifier_log(const char *log_buf, bool force) fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log_buf); } +static void show_log_and_msgs(struct test_loader *tester, struct test_subspec *subspec, int n) +{ + struct pattern *pat; + int i; + + if (env.verbosity == VERBOSE_NONE) + emit_verifier_log(tester->log_buf, true /*force*/); + for (i = 0; i < n; i++) { + pat = &subspec->expect_msgs[i]; + fprintf(stderr, " MATCHED MSG: %s'%s'\n", pat->expected ? "" : "!", pat->str); + } +} + static void validate_case(struct test_loader *tester, struct test_subspec *subspec, struct bpf_object *obj, struct bpf_program *prog, int load_err) { - int i, j; + int i; for (i = 0; i < subspec->expect_msg_cnt; i++) { + struct pattern *pat = &subspec->expect_msgs[i]; char *match; - const char *expect_msg; - - expect_msg = subspec->expect_msgs[i]; - - match = strstr(tester->log_buf + tester->next_match_pos, expect_msg); - if (!ASSERT_OK_PTR(match, "expect_msg")) { - /* if we are in verbose mode, we've already emitted log */ - if (env.verbosity == VERBOSE_NONE) - emit_verifier_log(tester->log_buf, true /*force*/); - for (j = 0; j < i; j++) - fprintf(stderr, - "MATCHED MSG: '%s'\n", subspec->expect_msgs[j]); - fprintf(stderr, "EXPECTED MSG: '%s'\n", expect_msg); + + match = strstr(tester->log_buf + tester->next_match_pos, pat->str); + if (pat->expected && !match) { + PRINT_FAIL("Expected log message not found\n"); + show_log_and_msgs(tester, subspec, i); + fprintf(stderr, " EXPECTED MSG: '%s'\n", pat->str); return; } - - tester->next_match_pos = match - tester->log_buf + strlen(expect_msg); + if (!pat->expected && match) { + PRINT_FAIL("Unexpected log message found\n"); + show_log_and_msgs(tester, subspec, i); + fprintf(stderr, "UNEXPECTED MSG: '%s'\n", pat->str); + return; + } + if (pat->expected) + tester->next_match_pos = match - tester->log_buf + strlen(pat->str); } } -- 2.42.0