1/* @title: Nightmare test framework */
2#include <asm.h>
3#include <crypto/prng.h>
4#include <linker/symbols.h>
5#include <mem/alloc.h>
6#include <sch/sched.h>
7#include <stdatomic.h>
8#include <stdbool.h>
9#include <stddef.h>
10#include <stdint.h>
11#include <thread/thread.h>
12#include <types/types.h>
13
14enum nightmare_role_type {
15 NIGHTMARE_ROLE_GENERIC,
16 NIGHTMARE_ROLE_SLEEPER,
17 NIGHTMARE_ROLE_WAKER,
18 NIGHTMARE_ROLE_MIGRATOR,
19 NIGHTMARE_ROLE_APC_SPAMMER,
20 NIGHTMARE_ROLE_FORKER,
21 NIGHTMARE_ROLE_ALLOCATOR,
22 NIGHTMARE_ROLE_INVALIDATOR,
23 NIGHTMARE_ROLE_MAX
24};
25
26enum nightmare_test_error {
27 NIGHTMARE_ERR_OK, /* test OK */
28 NIGHTMARE_ERR_FAIL, /* test did not succeed */
29 NIGHTMARE_ERR_RETRY, /* test should be retried from the top */
30 NIGHTMARE_ERR_PANIC, /* test panicked */
31};
32
33enum nightmare_state {
34 NIGHTMARE_UNINIT, /* default state, not initialized */
35 NIGHTMARE_READY, /* ready to run */
36 NIGHTMARE_RUNNING, /* running */
37 NIGHTMARE_STOPPED, /* test stopped */
38};
39
40struct nightmare_role {
41 enum nightmare_role_type type;
42 const char *name;
43
44 /* number of threads for this role */
45 size_t count;
46
47 /* the worker function each spawned thread runs */
48 void (*worker)(void *);
49 void *arg;
50};
51
52struct nightmare_watchdog {
53 atomic_uint last_progress;
54 time_t last_kick_ms;
55};
56
57struct nightmare_report {
58 char *buffer;
59 size_t buffer_len;
60
61 void (*write_fn)(struct nightmare_report *r, const char *msg, size_t len);
62
63 uint32_t flags;
64};
65
66struct nightmare_test {
67 const char *name;
68 struct nightmare_watchdog *watchdog;
69 time_t default_runtime_ms;
70 size_t default_threads;
71
72 _Atomic enum nightmare_state state;
73 _Atomic enum nightmare_test_error error;
74 struct nightmare_role roles[NIGHTMARE_ROLE_MAX];
75 size_t role_count;
76
77 void (*reset)(struct nightmare_test *);
78 void (*init)(struct nightmare_test *); /* initialize the test */
79 enum nightmare_test_error (*start)(
80 struct nightmare_test *); /* start the test */
81 void (*stop)(struct nightmare_test *); /* stop the test gracefully */
82 void (*shutdown)(struct nightmare_test *); /* force it to shutdown */
83 void (*report)(struct nightmare_test *,
84 struct nightmare_report *); /* print logs to stdout or
85 * the input param if not NULL */
86
87 size_t message_count;
88 char **messages;
89 void *private;
90};
91
92struct nightmare_local {
93 void *data;
94 size_t len;
95};
96
97struct nightmare_thread {
98 _Atomic(struct thread *) th;
99 struct nightmare_test *test;
100 enum nightmare_role_type role;
101 struct nightmare_local local;
102};
103
104struct nightmare_thread_group {
105 struct nightmare_thread *threads;
106 size_t count;
107};
108
109struct nightmare {
110 struct nightmare_test *test;
111 struct nightmare_thread *self;
112 struct nightmare_watchdog *watchdog;
113};
114
115static inline void nightmare_add_role(struct nightmare_test *t,
116 enum nightmare_role_type type,
117 const char *name, void (*worker)(void *),
118 size_t count, void *arg) {
119 size_t idx = t->role_count++;
120 t->roles[idx].type = type;
121 t->roles[idx].name = name;
122 t->roles[idx].worker = worker;
123 t->roles[idx].count = count;
124 t->roles[idx].arg = arg;
125}
126
127#ifdef TEST_NIGHTMARE
128static inline bool nightmare_should_stop(void) {
129 return atomic_load(&global.nightmare_stop);
130}
131#endif
132
133static inline void nightmare_chaos_pause() {
134 int r = prng_next() % 5;
135 switch (r) {
136 case 0: cpu_relax(); break;
137 case 1: scheduler_yield(); break;
138 default: break;
139 }
140}
141
142static inline void nightmare_kick(struct nightmare_watchdog *w) {
143 atomic_store(&w->last_progress, time_get_ms());
144}
145
146void nightmare_spawn_roles(struct nightmare_test *,
147 struct nightmare_thread_group *);
148void nightmare_join_roles(struct nightmare_thread_group *);
149enum nightmare_test_error nightmare_run(struct nightmare_test *t);
150
151static inline bool nightmare_watchdog_expired(struct nightmare_watchdog *w,
152 time_t timeout_ms) {
153 time_t now = time_get_ms();
154 time_t last = atomic_load(&w->last_progress);
155
156 if (now - last > timeout_ms)
157 return true;
158 return false;
159}
160
161static inline void nightmare_watchdog_init(struct nightmare_watchdog *w) {
162 time_t now = time_get_ms();
163 atomic_store(&w->last_progress, now);
164 w->last_kick_ms = now;
165}
166
167static inline void nightmare_set_local(void *d, size_t l) {
168 struct nightmare_local *lcl = thread_get_current()->private;
169 lcl->data = d;
170 lcl->len = l;
171}
172
173static inline struct nightmare_local *nightmare_get_local() {
174 return thread_get_current()->private;
175}
176
177static inline struct nightmare_thread *nightmare_get_thread() {
178 return container_of(nightmare_get_local(), struct nightmare_thread, local);
179}
180
181LINKER_SECTION_DEFINE(struct nightmare_test, nightmare_tests);
182
183#define NIGHTMARE_THREAD_ENTRY(__name) static void __name(void *__arg)
184#define NIGHTMARE_RESET_FN_NAME(__name) __name##_reset
185#define NIGHTMARE_INIT_FN_NAME(__name) __name##_init
186#define NIGHTMARE_START_FN_NAME(__name) __name##_start
187#define NIGHTMARE_STOP_FN_NAME(__name) __name##_stop
188#define NIGHTMARE_SHUTDOWN_FN_NAME(__name) __name##_shutdown
189#define NIGHTMARE_REPORT_FN_NAME(__name) __name##_report
190
191#define NIGHTMARE_THREAD_ENTRY_INIT() \
192 (void) __arg; \
193 struct nightmare_test *SELF = nightmare_get_thread()->test; \
194 (void) SELF;
195
196#define NIGHTMARE_THREAD_ENTRY_EXIT() \
197 atomic_store(&nightmare_get_thread()->th, NULL)
198
199#define NIGHTMARE_FN_INIT() (void) SELF;
200
201#define NIGHTMARE_DEFINE_RESET(__name) \
202 static void NIGHTMARE_RESET_FN_NAME(__name)(struct nightmare_test * SELF)
203
204#define NIGHTMARE_DEFINE_INIT(__name) \
205 static void NIGHTMARE_INIT_FN_NAME(__name)(struct nightmare_test * SELF)
206
207#define NIGHTMARE_DEFINE_START(__name) \
208 static enum nightmare_test_error NIGHTMARE_START_FN_NAME(__name)( \
209 struct nightmare_test * SELF)
210
211#define NIGHTMARE_DEFINE_STOP(__name) \
212 static void NIGHTMARE_STOP_FN_NAME(__name)(struct nightmare_test * SELF)
213
214#define NIGHTMARE_DEFINE_SHUTDOWN(__name) \
215 static void NIGHTMARE_SHUTDOWN_FN_NAME(__name)(struct nightmare_test * SELF)
216
217#define NIGHTMARE_DEFINE_REPORT(__name) \
218 static void NIGHTMARE_REPORT_FN_NAME(__name)( \
219 struct nightmare_test * SELF, struct nightmare_report * REPORT)
220
221#define NIGHTMARE_IMPL_RESET(__name) NIGHTMARE_DEFINE_RESET(__name)
222#define NIGHTMARE_IMPL_INIT(__name) NIGHTMARE_DEFINE_INIT(__name)
223#define NIGHTMARE_IMPL_START(__name) NIGHTMARE_DEFINE_START(__name)
224#define NIGHTMARE_IMPL_STOP(__name) NIGHTMARE_DEFINE_STOP(__name)
225#define NIGHTMARE_IMPL_SHUTDOWN(__name) NIGHTMARE_DEFINE_SHUTDOWN(__name)
226#define NIGHTMARE_IMPL_REPORT(__name) NIGHTMARE_DEFINE_REPORT(__name)
227
228#define NIGHTMARE_RETURN_ERROR(__err) \
229 do { \
230 atomic_store(&SELF->error, __err); \
231 return __err; \
232 } while (0)
233
234#define NIGHTMARE_SET_STATE(__nm, __state) \
235 do { \
236 atomic_store(&__nm->state, __state); \
237 } while (0)
238
239#define NIGHTMARE_ASSERT(cond) \
240 do { \
241 if (!(cond)) \
242 NIGHTMARE_RETURN_ERROR(NIGHTMARE_ERR_FAIL); \
243 } while (0)
244
245#define NIGHTMARE_PROGRESS() nightmare_kick(SELF->watchdog)
246#define NIGHTMARE_ASSERT_EQ(a, b) NIGHTMARE_ASSERT((a) == (b))
247
248#define NIGHTMARE_DEFINE_TEST(__name, __runtime_ms, __threads) \
249 LINKER_SECTION_OBJECT(struct nightmare_test, nightmare_tests) \
250 __nightmare_test_##name = { \
251 .name = #__name, \
252 .default_runtime_ms = __runtime_ms, \
253 .default_threads = __threads, \
254 .state = NIGHTMARE_UNINIT, \
255 .error = NIGHTMARE_ERR_OK, \
256 .roles = {{0}}, \
257 .role_count = 0, \
258 .reset = NIGHTMARE_RESET_FN_NAME(__name), \
259 .init = NIGHTMARE_INIT_FN_NAME(__name), \
260 .start = NIGHTMARE_START_FN_NAME(__name), \
261 .stop = NIGHTMARE_STOP_FN_NAME(__name), \
262 .shutdown = NIGHTMARE_SHUTDOWN_FN_NAME(__name), \
263 .report = NIGHTMARE_REPORT_FN_NAME(__name), \
264 }
265
266#define NIGHTMARE_ADD_MESSAGE(msg) \
267 do { \
268 SELF->messages = krealloc(SELF->messages, \
269 sizeof(char *) * ++SELF->message_count, ); \
270 SELF->messages[SELF->message_count - 1] = msg; \
271 } while (0)
272