xref: /linux/tools/testing/selftests/sched_ext/scx_test.h (revision cce3a39cebca074e0994e2fbce7a6062b80e42d8)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Copyright (c) 2023 Meta Platforms, Inc. and affiliates.
4  * Copyright (c) 2023 Tejun Heo <tj@kernel.org>
5  * Copyright (c) 2023 David Vernet <dvernet@meta.com>
6  */
7 
8 #ifndef __SCX_TEST_H__
9 #define __SCX_TEST_H__
10 
11 #include <errno.h>
12 #include <scx/common.h>
13 #include <scx/compat.h>
14 
15 enum scx_test_status {
16 	SCX_TEST_PASS = 0,
17 	SCX_TEST_SKIP,
18 	SCX_TEST_FAIL,
19 };
20 
21 struct scx_test {
22 	/**
23 	 * name - The name of the testcase.
24 	 */
25 	const char *name;
26 
27 	/**
28 	 * description - A description of your testcase: what it tests and is
29 	 * meant to validate.
30 	 */
31 	const char *description;
32 
33 	/*
34 	 * setup - Setup the test.
35 	 * @ctx: A pointer to a context object that will be passed to run and
36 	 *	 cleanup.
37 	 *
38 	 * An optional callback that allows a testcase to perform setup for its
39 	 * run. A test may return SCX_TEST_SKIP to skip the run.
40 	 */
41 	enum scx_test_status (*setup)(void **ctx);
42 
43 	/*
44 	 * run - Run the test.
45 	 * @ctx: Context set in the setup() callback. If @ctx was not set in
46 	 *	 setup(), it is NULL.
47 	 *
48 	 * The main test. Callers should return one of:
49 	 *
50 	 * - SCX_TEST_PASS: Test passed
51 	 * - SCX_TEST_SKIP: Test should be skipped
52 	 * - SCX_TEST_FAIL: Test failed
53 	 *
54 	 * This callback must be defined.
55 	 */
56 	enum scx_test_status (*run)(void *ctx);
57 
58 	/*
59 	 * cleanup - Perform cleanup following the test
60 	 * @ctx: Context set in the setup() callback. If @ctx was not set in
61 	 *	 setup(), it is NULL.
62 	 *
63 	 * An optional callback that allows a test to perform cleanup after
64 	 * being run. This callback is run even if the run() callback returns
65 	 * SCX_TEST_SKIP or SCX_TEST_FAIL. It is not run if setup() returns
66 	 * SCX_TEST_SKIP or SCX_TEST_FAIL.
67 	 */
68 	void (*cleanup)(void *ctx);
69 };
70 
71 void scx_test_register(struct scx_test *test);
72 
73 #define REGISTER_SCX_TEST(__test)			\
74 	__attribute__((constructor))			\
75 	static void ___scxregister##__LINE__(void)	\
76 	{						\
77 		scx_test_register(__test);		\
78 	}
79 
80 #define SCX_ERR(__fmt, ...)						\
81 	do {								\
82 		fprintf(stderr, "ERR: %s:%d\n", __FILE__, __LINE__);	\
83 		fprintf(stderr, __fmt"\n", ##__VA_ARGS__);			\
84 	} while (0)
85 
86 #define SCX_FAIL(__fmt, ...)						\
87 	do {								\
88 		SCX_ERR(__fmt, ##__VA_ARGS__);				\
89 		return SCX_TEST_FAIL;					\
90 	} while (0)
91 
92 #define SCX_FAIL_IF(__cond, __fmt, ...)					\
93 	do {								\
94 		if (__cond)						\
95 			SCX_FAIL(__fmt, ##__VA_ARGS__);			\
96 	} while (0)
97 
98 #define SCX_GT(_x, _y) SCX_FAIL_IF((_x) <= (_y), "Expected %s > %s (%lu > %lu)",	\
99 				   #_x, #_y, (u64)(_x), (u64)(_y))
100 #define SCX_GE(_x, _y) SCX_FAIL_IF((_x) < (_y), "Expected %s >= %s (%lu >= %lu)",	\
101 				   #_x, #_y, (u64)(_x), (u64)(_y))
102 #define SCX_LT(_x, _y) SCX_FAIL_IF((_x) >= (_y), "Expected %s < %s (%lu < %lu)",	\
103 				   #_x, #_y, (u64)(_x), (u64)(_y))
104 #define SCX_LE(_x, _y) SCX_FAIL_IF((_x) > (_y), "Expected %s <= %s (%lu <= %lu)",	\
105 				   #_x, #_y, (u64)(_x), (u64)(_y))
106 #define SCX_EQ(_x, _y) SCX_FAIL_IF((_x) != (_y), "Expected %s == %s (%lu == %lu)",	\
107 				   #_x, #_y, (u64)(_x), (u64)(_y))
108 #define SCX_ASSERT(_x) SCX_FAIL_IF(!(_x), "Expected %s to be true (%lu)",		\
109 				   #_x, (u64)(_x))
110 
111 /*
112  * SCX_ASSERT_ALIVE - Fail the test if the scheduler under test exited
113  * on its own between attach and this check, e.g. from a runtime error.
114  * A scheduler that dies mid-test freezes whatever state the test is
115  * about to assert on, which can make the assertions pass vacuously.
116  * uei.msg carries the actual error text, so print it alongside the
117  * recorded kind and exit code.
118  */
119 #define SCX_ASSERT_ALIVE(__skel, __uei)						\
120 	do {									\
121 		if (UEI_EXITED(__skel, __uei))					\
122 			SCX_FAIL("Scheduler exited unexpectedly"		\
123 				 " (kind=%llu code=%lld msg='%s')",		\
124 				 (unsigned long long)(__skel)->data->__uei.kind, \
125 				 (long long)(__skel)->data->__uei.exit_code,	\
126 				 (__skel)->data->__uei.msg);			\
127 	} while (0)
128 
129 /*
130  * SCX_TIMED_WAIT_UEI - Wait for the scheduler under test to exit, i.e.
131  * its uei to be filled in, for up to @__ms iterations at ~1ms each.
132  * Returns true if the scheduler exited and false on timeout, so a
133  * scheduler that never exits fails the test instead of hanging it.
134  * The bound is iteration-based rather than wall-clock and stretches
135  * on a loaded system instead of failing early.
136  */
137 #define SCX_TIMED_WAIT_UEI(__skel, __uei, __ms)					\
138 	({									\
139 		s64 __left = (__ms);						\
140 										\
141 		while (__left-- > 0 && !UEI_EXITED(__skel, __uei))		\
142 			usleep(1000);						\
143 		UEI_EXITED(__skel, __uei) != 0;					\
144 	})
145 
146 #define SCX_ECODE_VAL(__ecode) ({						\
147         u64 __val = 0;								\
148 	bool __found = false;							\
149 										\
150 	__found = __COMPAT_read_enum("scx_exit_code", #__ecode, &__val);	\
151 	SCX_ASSERT(__found);							\
152 	(s64)__val;								\
153 })
154 
155 /*
156  * SCX_KIND_VAL - Resolve an scx_exit_kind enumerator from kernel BTF.
157  *
158  * Unlike __COMPAT_ENUM_OR_ZERO(), a missing enumerator fails the test
159  * loudly instead of silently resolving to 0 (== SCX_EXIT_NONE), which
160  * would make the comparison vacuous.
161  */
162 #define SCX_KIND_VAL(__kind) ({							\
163 	u64 __val = 0;								\
164 										\
165 	if (!__COMPAT_read_enum("scx_exit_kind", #__kind, &__val))		\
166 		SCX_FAIL("scx_exit_kind.%s not found in kernel BTF", #__kind);	\
167 	__val;									\
168 })
169 
170 /*
171  * EXIT_KIND - Alias of SCX_KIND_VAL() for resolving scx_exit_kind
172  * enumerator values, kept for readability at the EXIT_KIND() call sites.
173  */
174 #define EXIT_KIND(__ent) SCX_KIND_VAL(__ent)
175 
176 #endif  // # __SCX_TEST_H__
177