Skip to content

Commit 7d743e4

Browse files
eddyz87Alexei Starovoitov
authored andcommitted
selftests/bpf: __jited test tag to check disassembly after jit
Allow to verify jit behaviour by writing tests as below: SEC("tp") __arch_x86_64 __jited(" endbr64") __jited(" nopl (%rax,%rax)") __jited(" xorq %rax, %rax") ... __naked void some_test(void) { asm volatile (... ::: __clobber_all); } Allow regular expressions in patterns, same way as in __msg. By default assume that each __jited pattern has to be matched on the next consecutive line of the disassembly, e.g.: __jited(" endbr64") # matched on line N __jited(" nopl (%rax,%rax)") # matched on line N+1 If match occurs on a wrong line an error is reported. To override this behaviour use __jited("..."), e.g.: __jited(" endbr64") # matched on line N __jited("...") # not matched __jited(" nopl (%rax,%rax)") # matched on any line >= N Signed-off-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent b991fc5 commit 7d743e4

File tree

2 files changed

+161
-23
lines changed

2 files changed

+161
-23
lines changed

tools/testing/selftests/bpf/progs/bpf_misc.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,39 @@
3636
* Regular expressions could be specified same way as in __msg.
3737
* __xlated_unpriv Same as __xlated but for unprivileged mode.
3838
*
39+
* __jited Match a line in a disassembly of the jited BPF program.
40+
* Has to be used after __arch_* macro.
41+
* For example:
42+
*
43+
* __arch_x86_64
44+
* __jited(" endbr64")
45+
* __jited(" nopl (%rax,%rax)")
46+
* __jited(" xorq %rax, %rax")
47+
* ...
48+
* __naked void some_test(void)
49+
* {
50+
* asm volatile (... ::: __clobber_all);
51+
* }
52+
*
53+
* Regular expressions could be included in patterns same way
54+
* as in __msg.
55+
*
56+
* By default assume that each pattern has to be matched on the
57+
* next consecutive line of disassembly, e.g.:
58+
*
59+
* __jited(" endbr64") # matched on line N
60+
* __jited(" nopl (%rax,%rax)") # matched on line N+1
61+
*
62+
* If match occurs on a wrong line an error is reported.
63+
* To override this behaviour use literal "...", e.g.:
64+
*
65+
* __jited(" endbr64") # matched on line N
66+
* __jited("...") # not matched
67+
* __jited(" nopl (%rax,%rax)") # matched on any line >= N
68+
*
69+
* __jited_unpriv Same as __jited but for unprivileged mode.
70+
*
71+
*
3972
* __success Expect program load success in privileged mode.
4073
* __success_unpriv Expect program load success in unprivileged mode.
4174
*
@@ -76,11 +109,13 @@
76109
*/
77110
#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))
78111
#define __xlated(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg)))
112+
#define __jited(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg)))
79113
#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure")))
80114
#define __success __attribute__((btf_decl_tag("comment:test_expect_success")))
81115
#define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc)))
82116
#define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
83117
#define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg)))
118+
#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg)))
84119
#define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
85120
#define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))
86121
#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))

tools/testing/selftests/bpf/test_loader.c

Lines changed: 126 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "disasm_helpers.h"
1111
#include "unpriv_helpers.h"
1212
#include "cap_helpers.h"
13+
#include "jit_disasm_helpers.h"
1314

1415
#define str_has_pfx(str, pfx) \
1516
(strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0)
@@ -33,6 +34,8 @@
3334
#define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv"
3435
#define TEST_BTF_PATH "comment:test_btf_path="
3536
#define TEST_TAG_ARCH "comment:test_arch="
37+
#define TEST_TAG_JITED_PFX "comment:test_jited="
38+
#define TEST_TAG_JITED_PFX_UNPRIV "comment:test_jited_unpriv="
3639

3740
/* Warning: duplicated in bpf_misc.h */
3841
#define POINTER_VALUE 0xcafe4all
@@ -68,6 +71,7 @@ struct test_subspec {
6871
bool expect_failure;
6972
struct expected_msgs expect_msgs;
7073
struct expected_msgs expect_xlated;
74+
struct expected_msgs jited;
7175
int retval;
7276
bool execute;
7377
};
@@ -124,6 +128,8 @@ static void free_test_spec(struct test_spec *spec)
124128
free_msgs(&spec->unpriv.expect_msgs);
125129
free_msgs(&spec->priv.expect_xlated);
126130
free_msgs(&spec->unpriv.expect_xlated);
131+
free_msgs(&spec->priv.jited);
132+
free_msgs(&spec->unpriv.jited);
127133

128134
free(spec->priv.name);
129135
free(spec->unpriv.name);
@@ -237,6 +243,21 @@ static int push_msg(const char *substr, struct expected_msgs *msgs)
237243
return __push_msg(substr, false, msgs);
238244
}
239245

246+
static int push_disasm_msg(const char *regex_str, bool *on_next_line, struct expected_msgs *msgs)
247+
{
248+
int err;
249+
250+
if (strcmp(regex_str, "...") == 0) {
251+
*on_next_line = false;
252+
return 0;
253+
}
254+
err = __push_msg(regex_str, *on_next_line, msgs);
255+
if (err)
256+
return err;
257+
*on_next_line = true;
258+
return 0;
259+
}
260+
240261
static int parse_int(const char *str, int *val, const char *name)
241262
{
242263
char *end;
@@ -320,6 +341,18 @@ enum arch {
320341
ARCH_RISCV64 = 0x4,
321342
};
322343

344+
static int get_current_arch(void)
345+
{
346+
#if defined(__x86_64__)
347+
return ARCH_X86_64;
348+
#elif defined(__aarch64__)
349+
return ARCH_ARM64;
350+
#elif defined(__riscv) && __riscv_xlen == 64
351+
return ARCH_RISCV64;
352+
#endif
353+
return 0;
354+
}
355+
323356
/* Uses btf_decl_tag attributes to describe the expected test
324357
* behavior, see bpf_misc.h for detailed description of each attribute
325358
* and attribute combinations.
@@ -332,9 +365,13 @@ static int parse_test_spec(struct test_loader *tester,
332365
const char *description = NULL;
333366
bool has_unpriv_result = false;
334367
bool has_unpriv_retval = false;
368+
bool unpriv_jit_on_next_line;
369+
bool jit_on_next_line;
370+
bool collect_jit = false;
335371
int func_id, i, err = 0;
336372
u32 arch_mask = 0;
337373
struct btf *btf;
374+
enum arch arch;
338375

339376
memset(spec, 0, sizeof(*spec));
340377

@@ -399,6 +436,30 @@ static int parse_test_spec(struct test_loader *tester,
399436
if (err)
400437
goto cleanup;
401438
spec->mode_mask |= UNPRIV;
439+
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_JITED_PFX))) {
440+
if (arch_mask == 0) {
441+
PRINT_FAIL("__jited used before __arch_*");
442+
goto cleanup;
443+
}
444+
if (collect_jit) {
445+
err = push_disasm_msg(msg, &jit_on_next_line,
446+
&spec->priv.jited);
447+
if (err)
448+
goto cleanup;
449+
spec->mode_mask |= PRIV;
450+
}
451+
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_JITED_PFX_UNPRIV))) {
452+
if (arch_mask == 0) {
453+
PRINT_FAIL("__unpriv_jited used before __arch_*");
454+
goto cleanup;
455+
}
456+
if (collect_jit) {
457+
err = push_disasm_msg(msg, &unpriv_jit_on_next_line,
458+
&spec->unpriv.jited);
459+
if (err)
460+
goto cleanup;
461+
spec->mode_mask |= UNPRIV;
462+
}
402463
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) {
403464
err = push_msg(msg, &spec->priv.expect_xlated);
404465
if (err)
@@ -459,16 +520,20 @@ static int parse_test_spec(struct test_loader *tester,
459520
} else if (str_has_pfx(s, TEST_TAG_ARCH)) {
460521
val = s + sizeof(TEST_TAG_ARCH) - 1;
461522
if (strcmp(val, "X86_64") == 0) {
462-
arch_mask |= ARCH_X86_64;
523+
arch = ARCH_X86_64;
463524
} else if (strcmp(val, "ARM64") == 0) {
464-
arch_mask |= ARCH_ARM64;
525+
arch = ARCH_ARM64;
465526
} else if (strcmp(val, "RISCV64") == 0) {
466-
arch_mask |= ARCH_RISCV64;
527+
arch = ARCH_RISCV64;
467528
} else {
468529
PRINT_FAIL("bad arch spec: '%s'", val);
469530
err = -EINVAL;
470531
goto cleanup;
471532
}
533+
arch_mask |= arch;
534+
collect_jit = get_current_arch() == arch;
535+
unpriv_jit_on_next_line = true;
536+
jit_on_next_line = true;
472537
} else if (str_has_pfx(s, TEST_BTF_PATH)) {
473538
spec->btf_custom_path = s + sizeof(TEST_BTF_PATH) - 1;
474539
}
@@ -521,6 +586,8 @@ static int parse_test_spec(struct test_loader *tester,
521586
clone_msgs(&spec->priv.expect_msgs, &spec->unpriv.expect_msgs);
522587
if (spec->unpriv.expect_xlated.cnt == 0)
523588
clone_msgs(&spec->priv.expect_xlated, &spec->unpriv.expect_xlated);
589+
if (spec->unpriv.jited.cnt == 0)
590+
clone_msgs(&spec->priv.jited, &spec->unpriv.jited);
524591
}
525592

526593
spec->valid = true;
@@ -575,16 +642,29 @@ static void emit_xlated(const char *xlated, bool force)
575642
fprintf(stdout, "XLATED:\n=============\n%s=============\n", xlated);
576643
}
577644

645+
static void emit_jited(const char *jited, bool force)
646+
{
647+
if (!force && env.verbosity == VERBOSE_NONE)
648+
return;
649+
fprintf(stdout, "JITED:\n=============\n%s=============\n", jited);
650+
}
651+
578652
static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
579653
void (*emit_fn)(const char *buf, bool force))
580654
{
655+
const char *log = log_buf, *prev_match;
581656
regmatch_t reg_match[1];
582-
const char *log = log_buf;
657+
int prev_match_line;
658+
int match_line;
583659
int i, j, err;
584660

661+
prev_match_line = -1;
662+
match_line = 0;
663+
prev_match = log;
585664
for (i = 0; i < msgs->cnt; i++) {
586665
struct expect_msg *msg = &msgs->patterns[i];
587-
const char *match = NULL;
666+
const char *match = NULL, *pat_status;
667+
bool wrong_line = false;
588668

589669
if (!msg->is_regex) {
590670
match = strstr(log, msg->substr);
@@ -598,19 +678,41 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
598678
}
599679
}
600680

601-
if (!match) {
681+
if (match) {
682+
for (; prev_match < match; ++prev_match)
683+
if (*prev_match == '\n')
684+
++match_line;
685+
wrong_line = msg->on_next_line && prev_match_line >= 0 &&
686+
prev_match_line + 1 != match_line;
687+
}
688+
689+
if (!match || wrong_line) {
602690
PRINT_FAIL("expect_msg\n");
603691
if (env.verbosity == VERBOSE_NONE)
604692
emit_fn(log_buf, true /*force*/);
605693
for (j = 0; j <= i; j++) {
606694
msg = &msgs->patterns[j];
695+
if (j < i)
696+
pat_status = "MATCHED ";
697+
else if (wrong_line)
698+
pat_status = "WRONG LINE";
699+
else
700+
pat_status = "EXPECTED ";
701+
msg = &msgs->patterns[j];
607702
fprintf(stderr, "%s %s: '%s'\n",
608-
j < i ? "MATCHED " : "EXPECTED",
703+
pat_status,
609704
msg->is_regex ? " REGEX" : "SUBSTR",
610705
msg->substr);
611706
}
612-
return;
707+
if (wrong_line) {
708+
fprintf(stderr,
709+
"expecting match at line %d, actual match is at line %d\n",
710+
prev_match_line + 1, match_line);
711+
}
712+
break;
613713
}
714+
715+
prev_match_line = match_line;
614716
}
615717
}
616718

@@ -769,20 +871,6 @@ static int get_xlated_program_text(int prog_fd, char *text, size_t text_sz)
769871
return err;
770872
}
771873

772-
static bool run_on_current_arch(int arch_mask)
773-
{
774-
if (arch_mask == 0)
775-
return true;
776-
#if defined(__x86_64__)
777-
return arch_mask & ARCH_X86_64;
778-
#elif defined(__aarch64__)
779-
return arch_mask & ARCH_ARM64;
780-
#elif defined(__riscv) && __riscv_xlen == 64
781-
return arch_mask & ARCH_RISCV64;
782-
#endif
783-
return false;
784-
}
785-
786874
/* this function is forced noinline and has short generic name to look better
787875
* in test_progs output (in case of a failure)
788876
*/
@@ -807,7 +895,7 @@ void run_subtest(struct test_loader *tester,
807895
if (!test__start_subtest(subspec->name))
808896
return;
809897

810-
if (!run_on_current_arch(spec->arch_mask)) {
898+
if ((get_current_arch() & spec->arch_mask) == 0) {
811899
test__skip();
812900
return;
813901
}
@@ -884,6 +972,21 @@ void run_subtest(struct test_loader *tester,
884972
validate_msgs(tester->log_buf, &subspec->expect_xlated, emit_xlated);
885973
}
886974

975+
if (subspec->jited.cnt) {
976+
err = get_jited_program_text(bpf_program__fd(tprog),
977+
tester->log_buf, tester->log_buf_sz);
978+
if (err == -EOPNOTSUPP) {
979+
printf("%s:SKIP: jited programs disassembly is not supported,\n", __func__);
980+
printf("%s:SKIP: tests are built w/o LLVM development libs\n", __func__);
981+
test__skip();
982+
goto tobj_cleanup;
983+
}
984+
if (!ASSERT_EQ(err, 0, "get_jited_program_text"))
985+
goto tobj_cleanup;
986+
emit_jited(tester->log_buf, false /*force*/);
987+
validate_msgs(tester->log_buf, &subspec->jited, emit_jited);
988+
}
989+
887990
if (should_do_test_run(spec, subspec)) {
888991
/* For some reason test_verifier executes programs
889992
* with all capabilities restored. Do the same here.

0 commit comments

Comments
 (0)