Commit 35cbf7f9 authored by Andrii Nakryiko's avatar Andrii Nakryiko

selftests/bpf: Support custom per-test flags and multiple expected messages

Extend __flag attribute by allowing to specify one of the following:
 * BPF_F_STRICT_ALIGNMENT
 * BPF_F_ANY_ALIGNMENT
 * BPF_F_TEST_RND_HI32
 * BPF_F_TEST_STATE_FREQ
 * BPF_F_SLEEPABLE
 * BPF_F_XDP_HAS_FRAGS
 * Some numeric value

Extend __msg attribute by allowing to specify multiple exepcted messages.
All messages are expected to be present in the verifier log in the
order of application.
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Signed-off-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20230301175417.3146070-2-eddyz87@gmail.com

[ Eduard: added commit message, formatting, comments ]
parent 07e2193f
...@@ -2,10 +2,33 @@ ...@@ -2,10 +2,33 @@
#ifndef __BPF_MISC_H__ #ifndef __BPF_MISC_H__
#define __BPF_MISC_H__ #define __BPF_MISC_H__
/* This set of attributes controls behavior of the
* test_loader.c:test_loader__run_subtests().
*
* __msg Message expected to be found in the verifier log.
* Multiple __msg attributes could be specified.
*
* __success Expect program load success in privileged mode.
*
* __failure Expect program load failure in privileged mode.
*
* __log_level Log level to use for the program, numeric value expected.
*
* __flag Adds one flag use for the program, the following values are valid:
* - BPF_F_STRICT_ALIGNMENT;
* - BPF_F_TEST_RND_HI32;
* - BPF_F_TEST_STATE_FREQ;
* - BPF_F_SLEEPABLE;
* - BPF_F_XDP_HAS_FRAGS;
* - A numeric value.
* Multiple __flag attributes could be specified, the final flags
* value is derived by applying binary "or" to all specified values.
*/
#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg))) #define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" msg)))
#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure"))) #define __failure __attribute__((btf_decl_tag("comment:test_expect_failure")))
#define __success __attribute__((btf_decl_tag("comment:test_expect_success"))) #define __success __attribute__((btf_decl_tag("comment:test_expect_success")))
#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl))) #define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
#define __flag(flag) __attribute__((btf_decl_tag("comment:test_prog_flags="#flag)))
/* Convenience macro for use with 'asm volatile' blocks */ /* Convenience macro for use with 'asm volatile' blocks */
#define __naked __attribute__((naked)) #define __naked __attribute__((naked))
......
...@@ -13,12 +13,15 @@ ...@@ -13,12 +13,15 @@
#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success" #define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success"
#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg=" #define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg="
#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level=" #define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags="
struct test_spec { struct test_spec {
const char *name; const char *name;
bool expect_failure; bool expect_failure;
const char *expect_msg; const char **expect_msgs;
size_t expect_msg_cnt;
int log_level; int log_level;
int prog_flags;
}; };
static int tester_init(struct test_loader *tester) static int tester_init(struct test_loader *tester)
...@@ -67,7 +70,8 @@ static int parse_test_spec(struct test_loader *tester, ...@@ -67,7 +70,8 @@ static int parse_test_spec(struct test_loader *tester,
for (i = 1; i < btf__type_cnt(btf); i++) { for (i = 1; i < btf__type_cnt(btf); i++) {
const struct btf_type *t; const struct btf_type *t;
const char *s; const char *s, *val;
char *e;
t = btf__type_by_id(btf, i); t = btf__type_by_id(btf, i);
if (!btf_is_decl_tag(t)) if (!btf_is_decl_tag(t))
...@@ -82,14 +86,48 @@ static int parse_test_spec(struct test_loader *tester, ...@@ -82,14 +86,48 @@ static int parse_test_spec(struct test_loader *tester,
} else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) { } else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) {
spec->expect_failure = false; spec->expect_failure = false;
} else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) {
spec->expect_msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; void *tmp;
const char **msg;
tmp = realloc(spec->expect_msgs,
(1 + spec->expect_msg_cnt) * sizeof(void *));
if (!tmp) {
ASSERT_FAIL("failed to realloc memory for messages\n");
return -ENOMEM;
}
spec->expect_msgs = tmp;
msg = &spec->expect_msgs[spec->expect_msg_cnt++];
*msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1;
} else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) { } else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) {
val = s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1;
errno = 0; errno = 0;
spec->log_level = strtol(s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1, NULL, 0); spec->log_level = strtol(val, &e, 0);
if (errno) { if (errno || e[0] != '\0') {
ASSERT_FAIL("failed to parse test log level from '%s'", s); ASSERT_FAIL("failed to parse test log level from '%s'", s);
return -EINVAL; return -EINVAL;
} }
} else if (str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX)) {
val = s + sizeof(TEST_TAG_PROG_FLAGS_PFX) - 1;
if (strcmp(val, "BPF_F_STRICT_ALIGNMENT") == 0) {
spec->prog_flags |= BPF_F_STRICT_ALIGNMENT;
} else if (strcmp(val, "BPF_F_ANY_ALIGNMENT") == 0) {
spec->prog_flags |= BPF_F_ANY_ALIGNMENT;
} else if (strcmp(val, "BPF_F_TEST_RND_HI32") == 0) {
spec->prog_flags |= BPF_F_TEST_RND_HI32;
} else if (strcmp(val, "BPF_F_TEST_STATE_FREQ") == 0) {
spec->prog_flags |= BPF_F_TEST_STATE_FREQ;
} else if (strcmp(val, "BPF_F_SLEEPABLE") == 0) {
spec->prog_flags |= BPF_F_SLEEPABLE;
} else if (strcmp(val, "BPF_F_XDP_HAS_FRAGS") == 0) {
spec->prog_flags |= BPF_F_XDP_HAS_FRAGS;
} else /* assume numeric value */ {
errno = 0;
spec->prog_flags |= strtol(val, &e, 0);
if (errno || e[0] != '\0') {
ASSERT_FAIL("failed to parse test prog flags from '%s'", s);
return -EINVAL;
}
}
} }
} }
...@@ -101,7 +139,7 @@ static void prepare_case(struct test_loader *tester, ...@@ -101,7 +139,7 @@ static void prepare_case(struct test_loader *tester,
struct bpf_object *obj, struct bpf_object *obj,
struct bpf_program *prog) struct bpf_program *prog)
{ {
int min_log_level = 0; int min_log_level = 0, prog_flags;
if (env.verbosity > VERBOSE_NONE) if (env.verbosity > VERBOSE_NONE)
min_log_level = 1; min_log_level = 1;
...@@ -119,7 +157,11 @@ static void prepare_case(struct test_loader *tester, ...@@ -119,7 +157,11 @@ static void prepare_case(struct test_loader *tester,
else else
bpf_program__set_log_level(prog, spec->log_level); bpf_program__set_log_level(prog, spec->log_level);
prog_flags = bpf_program__flags(prog);
bpf_program__set_flags(prog, prog_flags | spec->prog_flags);
tester->log_buf[0] = '\0'; tester->log_buf[0] = '\0';
tester->next_match_pos = 0;
} }
static void emit_verifier_log(const char *log_buf, bool force) static void emit_verifier_log(const char *log_buf, bool force)
...@@ -135,17 +177,26 @@ static void validate_case(struct test_loader *tester, ...@@ -135,17 +177,26 @@ static void validate_case(struct test_loader *tester,
struct bpf_program *prog, struct bpf_program *prog,
int load_err) int load_err)
{ {
if (spec->expect_msg) { int i, j;
for (i = 0; i < spec->expect_msg_cnt; i++) {
char *match; char *match;
const char *expect_msg;
expect_msg = spec->expect_msgs[i];
match = strstr(tester->log_buf, spec->expect_msg); match = strstr(tester->log_buf + tester->next_match_pos, expect_msg);
if (!ASSERT_OK_PTR(match, "expect_msg")) { if (!ASSERT_OK_PTR(match, "expect_msg")) {
/* if we are in verbose mode, we've already emitted log */ /* if we are in verbose mode, we've already emitted log */
if (env.verbosity == VERBOSE_NONE) if (env.verbosity == VERBOSE_NONE)
emit_verifier_log(tester->log_buf, true /*force*/); emit_verifier_log(tester->log_buf, true /*force*/);
fprintf(stderr, "EXPECTED MSG: '%s'\n", spec->expect_msg); for (j = 0; j < i; j++)
fprintf(stderr, "MATCHED MSG: '%s'\n", spec->expect_msgs[j]);
fprintf(stderr, "EXPECTED MSG: '%s'\n", expect_msg);
return; return;
} }
tester->next_match_pos = match - tester->log_buf + strlen(expect_msg);
} }
} }
......
...@@ -427,6 +427,7 @@ int get_bpf_max_tramp_links(void); ...@@ -427,6 +427,7 @@ int get_bpf_max_tramp_links(void);
struct test_loader { struct test_loader {
char *log_buf; char *log_buf;
size_t log_buf_sz; size_t log_buf_sz;
size_t next_match_pos;
struct bpf_object *obj; struct bpf_object *obj;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment