1/* @title: Nightmare test harness */
2#pragma once
3#include <cmdline.h>
4#include <compiler.h>
5#include <crypto/prng.h>
6#include <kassert.h>
7#include <linker/symbols.h>
8#include <math/fixed.h>
9#include <scaled_param.h>
10#include <smp/percpu.h>
11#include <stdatomic.h>
12#include <stdbool.h>
13#include <stddef.h>
14#include <stdint.h>
15#include <string.h>
16#include <types/types.h>
17
18struct nightmare_ctx;
19struct nightmare_worker;
20struct thread;
21
22enum nightmare_exit_code {
23 NIGHTMARE_EXIT_OK = 0,
24 NIGHTMARE_EXIT_FINDING = 3,
25 NIGHTMARE_EXIT_FAIL = 4,
26 NIGHTMARE_EXIT_STALL = 5,
27 NIGHTMARE_EXIT_SKIP = 6,
28};
29
30#define NIGHTMARE_INTENSITY_SENTINEL ((fx32_32_t) - 1LL)
31#define NIGHTMARE_INTENSITY_DEFAULT FX(0.5)
32
33enum nightmare_seed_policy : uint8_t {
34 NIGHTMARE_SEED_IGNORED = 0,
35 NIGHTMARE_SEED_OPTIONAL,
36 NIGHTMARE_SEED_REQUIRED,
37};
38
39enum nightmare_seed_mode : uint8_t {
40 NIGHTMARE_SEED_SPLIT = 0,
41 NIGHTMARE_SEED_SEEDFUL,
42 NIGHTMARE_SEED_SEEDLESS,
43};
44
45enum nightmare_requires : uint32_t {
46 NIGHTMARE_REQ_NONE = 0,
47 NIGHTMARE_REQ_SMP = 1 << 0,
48 NIGHTMARE_REQ_PREEMPT = 1 << 1,
49 NIGHTMARE_REQ_ASAN = 1 << 2,
50 NIGHTMARE_REQ_INJECT = 1 << 3,
51};
52
53enum nightmare_result : uint8_t {
54 NIGHTMARE_RESULT_OK = 0,
55 NIGHTMARE_RESULT_FINDING,
56 NIGHTMARE_RESULT_FAIL,
57 NIGHTMARE_RESULT_STALL,
58 NIGHTMARE_RESULT_SKIP,
59};
60
61enum nightmare_skip_reason : uint8_t {
62 NIGHTMARE_SKIP_NONE = 0,
63 NIGHTMARE_SKIP_NOT_COMPILED,
64 NIGHTMARE_SKIP_NO_SUCH_NIGHTMARE,
65 NIGHTMARE_SKIP_NEEDS_SMP,
66 NIGHTMARE_SKIP_NEEDS_PREEMPT,
67 NIGHTMARE_SKIP_NEEDS_ASAN,
68 NIGHTMARE_SKIP_NEEDS_INJECT,
69 NIGHTMARE_SKIP_RAM_LOW,
70 NIGHTMARE_SKIP_SEED_UNUSED,
71 NIGHTMARE_SKIP_SEED_MISSING,
72 NIGHTMARE_SKIP_SERVICE_MISSING,
73 NIGHTMARE_SKIP_PREPARE_REFUSED,
74};
75
76enum nightmare_stop : uint8_t {
77 NM_RUN = 0,
78 NM_STOP_BUDGET,
79 NM_STOP_FINDING,
80 NM_STOP_FAIL,
81 NM_STOP_STALL,
82};
83
84enum nightmare_finding_tier : uint8_t {
85 NIGHTMARE_TIER_AMBIGUOUS = 0,
86 NIGHTMARE_TIER_CONFIDENT,
87};
88
89struct nightmare_verdict {
90 enum nightmare_result result;
91 enum nightmare_skip_reason skip_reason;
92 const char *reason;
93 const char *msg;
94};
95
96#define NIGHTMARE_OK \
97 ((struct nightmare_verdict) {.result = NIGHTMARE_RESULT_OK})
98#define NIGHTMARE_FAIL(reason_, msg_) \
99 ((struct nightmare_verdict) { \
100 .result = NIGHTMARE_RESULT_FAIL, .reason = (reason_), .msg = (msg_)})
101#define NIGHTMARE_SKIP(reason_) \
102 ((struct nightmare_verdict) {.result = NIGHTMARE_RESULT_SKIP, \
103 .skip_reason = (reason_)})
104
105struct nightmare_rng {
106 uint64_t state;
107};
108
109struct nightmare_worker {
110 size_t index;
111 const char *role;
112 struct nightmare_rng rng;
113 _Atomic(struct thread *) th;
114 atomic_bool parked;
115};
116
117struct nightmare_ctx {
118 const struct nightmare *nm;
119 fx32_32_t intensity;
120 size_t intensity_val;
121 size_t worker_count;
122 uint64_t seed;
123 bool seed_present;
124 enum nightmare_seed_mode seed_mode;
125 time_ms_t soft_deadline_ms;
126 time_ms_t hard_deadline_ms;
127 void *private;
128};
129
130struct nightmare_ops {
131 struct nightmare_verdict (*prepare)(struct nightmare_ctx *);
132 void (*worker)(struct nightmare_ctx *, struct nightmare_worker *);
133 struct nightmare_verdict (*quiesce_check)(struct nightmare_ctx *);
134 void (*probe)(struct nightmare_ctx *);
135 struct nightmare_verdict (*finish)(struct nightmare_ctx *);
136};
137
138struct nightmare {
139 const char *name;
140 const char *fname;
141 const char *desc;
142 const struct nightmare_ops *ops;
143 enum nightmare_seed_policy seed_policy;
144 enum nightmare_requires requires;
145 const char *const *perturb;
146 fx32_32_t intensity;
147 struct scaled_param intensity_desc;
148 time_ms_t default_duration_ms;
149 size_t min_mem_mib;
150} __aligned(8);
151
152LINKER_SECTION_DEFINE(struct nightmare, nightmares);
153
154#define NIGHTMARE_DECLARE(id, ...) \
155 extern struct nightmare __nightmare_##id; \
156 LINKER_SECTION_OBJECT(struct nightmare, nightmares) \
157 __nightmare_##id = {.name = #id, \
158 .fname = __RELFILE__, \
159 .seed_policy = NIGHTMARE_SEED_IGNORED, \
160 .requires = NIGHTMARE_REQ_NONE, \
161 .intensity = NIGHTMARE_INTENSITY_SENTINEL, \
162 __VA_ARGS__}
163
164#define NIGHTMARE(id) (&__nightmare_##id)
165#define NIGHTMARE_DEFINE(id) extern struct nightmare __nightmare_##id
166#define NIGHTMARE_PERTURB(...) ((const char *const[]) {__VA_ARGS__, NULL})
167
168#define NIGHTMARE_INTENSITY_CORES(min_, def_, max_, unit_) \
169 .intensity_desc = {.curve = SCALE_CORE_MULTIPLIER, \
170 .min_val = (min_), \
171 .def_val = (def_), \
172 .max_val = (max_), \
173 .unit = (unit_)}
174
175#define NIGHTMARE_WORKER(id) \
176 static void id(struct nightmare_ctx *NM_CTX, \
177 struct nightmare_worker *NM_SELF)
178
179#define NIGHTMARE_OPTIONS_DECLARE(id, struct_type, instance, ...) \
180 static void *__nightmare_options_resolve_##id(const char *path, \
181 size_t path_len) { \
182 return path_len == sizeof(#id) - 1 && \
183 strncmp(path, #id, path_len) == 0 \
184 ? &(instance) \
185 : NULL; \
186 } \
187 CMDLINE_SCHEMA_DECLARE(__nightmare_options_##id, "nightmare", #id, \
188 "Nightmare subject options", \
189 __nightmare_options_resolve_##id, __VA_ARGS__)
190
191struct nightmare_progress_counter {
192 _Atomic uint64_t count;
193};
194
195PERCPU_DEFINE(nightmare_progress, struct nightmare_progress_counter);
196
197static inline void nightmare_progress_tick(void) {
198 /* No contract enforced at this level, tests do whatever,
199 * this is a heuristic anyways */
200 atomic_fetch_add_explicit(&PERCPU_PTR(TOPC_NONE, nightmare_progress)->count,
201 1, memory_order_relaxed);
202}
203
204#define NIGHTMARE_PROGRESS() nightmare_progress_tick()
205
206struct nightmare_finding_site {
207 const char *kind;
208 enum nightmare_finding_tier tier;
209 const char *file;
210 uint32_t line;
211};
212
213#define NIGHTMARE_FINDING(kind_, fmt, ...) \
214 nightmare_finding_at( \
215 &(const struct nightmare_finding_site) {.kind = (kind_), \
216 .tier = \
217 NIGHTMARE_TIER_AMBIGUOUS, \
218 .file = __RELFILE__, \
219 .line = __LINE__}, \
220 0, (fmt), ##__VA_ARGS__)
221
222#define NIGHTMARE_FINDING_TIER(kind_, tier_, discriminator_, fmt, ...) \
223 nightmare_finding_at( \
224 &(const struct nightmare_finding_site) {.kind = (kind_), \
225 .tier = (tier_), \
226 .file = __RELFILE__, \
227 .line = __LINE__}, \
228 (discriminator_), (fmt), ##__VA_ARGS__)
229
230struct nightmare_stall_evidence {
231 time_ms_t silent_ms;
232 uint64_t progress;
233 cpu_id_t observer_cpu;
234};
235
236void nightmare_run(void);
237bool nightmare_must_stop(void);
238bool nightmare_must_stop_irq(void);
239void nightmare_stop_after_finding(void);
240bool nightmare_must_park(void);
241void nightmare_park(struct nightmare_worker *worker);
242static inline uint64_t nightmare_rand(struct nightmare_rng *rng) {
243 return prng_splitmix64_next(state: &rng->state);
244}
245uint64_t nightmare_progress_sum_irq(void);
246void nightmare_finding_at(const struct nightmare_finding_site *site,
247 uint64_t discriminator, const char *fmt, ...)
248 __printf_like(3, 4);
249void nightmare_request_external_fail(const char *kind, uint64_t discriminator,
250 const char *fmt, ...) __printf_like(3, 4);
251void nightmare_report_stall(const struct nightmare_stall_evidence *evidence);
252