1/* @title: Nightmare test harness */
2#pragma once
3#include <atomic.h>
4#include <cmdline.h>
5#include <compiler/core.h>
6#include <compiler/diagnostic.h>
7#include <crypto/prng.h>
8#include <enforce.h>
9#include <linker/symbols.h>
10#include <math/fixed.h>
11#include <scaled_param.h>
12#include <smp/percpu.h>
13#include <stdbool.h>
14#include <stddef.h>
15#include <stdint.h>
16#include <string.h>
17#include <test/conc.h>
18#include <types/types.h>
19
20struct nightmare_ctx;
21struct thread;
22
23enum nightmare_exit_code {
24 NIGHTMARE_EXIT_OK = 0,
25 NIGHTMARE_EXIT_FINDING = 3,
26 NIGHTMARE_EXIT_FAIL = 4,
27 NIGHTMARE_EXIT_STALL = 5,
28 NIGHTMARE_EXIT_SKIP = 6,
29};
30
31enum nightmare_seed_policy : uint8_t {
32 NIGHTMARE_SEED_IGNORED = 0,
33 NIGHTMARE_SEED_OPTIONAL,
34 NIGHTMARE_SEED_REQUIRED,
35};
36
37enum nightmare_seed_mode : uint8_t {
38 NIGHTMARE_SEED_SPLIT = 0,
39 NIGHTMARE_SEED_SEEDFUL,
40 NIGHTMARE_SEED_SEEDLESS,
41};
42
43enum nightmare_requires : uint32_t {
44 NIGHTMARE_REQ_NONE = 0,
45 NIGHTMARE_REQ_SMP = 1 << 0,
46 NIGHTMARE_REQ_PREEMPT = 1 << 1,
47 NIGHTMARE_REQ_ASAN = 1 << 2,
48 NIGHTMARE_REQ_INJECT = 1 << 3,
49};
50
51enum nightmare_result : uint8_t {
52 NIGHTMARE_RESULT_OK = 0,
53 NIGHTMARE_RESULT_FINDING,
54 NIGHTMARE_RESULT_FAIL,
55 NIGHTMARE_RESULT_STALL,
56 NIGHTMARE_RESULT_SKIP,
57};
58
59enum nightmare_skip_reason : uint8_t {
60 NIGHTMARE_SKIP_NONE = 0,
61 NIGHTMARE_SKIP_NOT_COMPILED,
62 NIGHTMARE_SKIP_NO_SUCH_NIGHTMARE,
63 NIGHTMARE_SKIP_NEEDS_SMP,
64 NIGHTMARE_SKIP_NEEDS_PREEMPT,
65 NIGHTMARE_SKIP_NEEDS_ASAN,
66 NIGHTMARE_SKIP_NEEDS_INJECT,
67 NIGHTMARE_SKIP_RAM_LOW,
68 NIGHTMARE_SKIP_SEED_UNUSED,
69 NIGHTMARE_SKIP_SEED_MISSING,
70 NIGHTMARE_SKIP_SERVICE_MISSING,
71 NIGHTMARE_SKIP_PREPARE_REFUSED,
72};
73
74enum nightmare_finding_tier : uint8_t {
75 NIGHTMARE_TIER_AMBIGUOUS = 0,
76 NIGHTMARE_TIER_CONFIDENT,
77};
78
79struct nightmare_verdict {
80 enum nightmare_result result;
81 enum nightmare_skip_reason skip_reason;
82 const char *reason;
83 const char *msg;
84};
85
86struct nightmare_finding_site {
87 const char *kind;
88 enum nightmare_finding_tier tier;
89 const char *file;
90 uint32_t line;
91};
92
93struct nightmare_ctx {
94 const struct nightmare *nm;
95 fx32_32_t intensity;
96 size_t intensity_val;
97 size_t worker_count;
98 uint64_t seed;
99 bool seed_present;
100 enum nightmare_seed_mode seed_mode;
101 time_ms_t soft_deadline_ms;
102 time_ms_t hard_deadline_ms;
103 void *private;
104};
105
106struct nightmare_ops {
107 struct nightmare_verdict (*prepare)(struct nightmare_ctx *);
108 void (*worker)(struct nightmare_ctx *, struct test_conc_worker *);
109 struct nightmare_verdict (*quiesce_check)(struct nightmare_ctx *);
110 void (*probe)(struct nightmare_ctx *);
111 struct nightmare_verdict (*finish)(struct nightmare_ctx *);
112};
113
114struct nightmare {
115 const char *name;
116 const char *fname;
117 const char *desc;
118 const struct nightmare_ops *ops;
119 enum nightmare_seed_policy seed_policy;
120 enum nightmare_requires requires;
121 const char *const *perturb;
122 fx32_32_t intensity;
123 struct scaled_param intensity_desc;
124 time_ms_t default_duration_ms;
125 size_t min_mem_mib;
126} cc_aligned(8);
127
128LINKER_SECTION_EXTERN(struct nightmare, nightmares);
129
130#define NIGHTMARE_DEFINE(id, ...) \
131 cc_wno_override_init_start extern struct nightmare __nightmare_##id; \
132 LINKER_SECTION_OBJECT(struct nightmare, nightmares) \
133 __nightmare_##id = {.name = #id, \
134 .fname = __RELFILE__, \
135 .seed_policy = NIGHTMARE_SEED_IGNORED, \
136 .requires = NIGHTMARE_REQ_NONE, \
137 .intensity = NIGHTMARE_INTENSITY_SENTINEL, \
138 __VA_ARGS__}; \
139 cc_wno_override_init_end
140
141#define NIGHTMARE(id) (&__nightmare_##id)
142#define NIGHTMARE_EXTERN(id) extern struct nightmare __nightmare_##id
143#define NIGHTMARE_PERTURB(...) ((const char *const[]) {__VA_ARGS__, NULL})
144
145#define NIGHTMARE_INTENSITY_SENTINEL ((fx32_32_t) - 1LL)
146#define NIGHTMARE_INTENSITY_DEFAULT FX(0.5)
147#define NIGHTMARE_INTENSITY_CORES(min_, def_, max_, unit_) \
148 .intensity_desc = {.curve = SCALE_CORE_MULTIPLIER, \
149 .min_val = (min_), \
150 .def_val = (def_), \
151 .max_val = (max_), \
152 .unit = (unit_)}
153
154#define NIGHTMARE_WORKER(id) \
155 static void id(struct nightmare_ctx *NM_CTX cc_unused, \
156 struct test_conc_worker *NM_SELF cc_unused)
157
158#define NIGHTMARE_OPTIONS_DEFINE(id, struct_type, instance, ...) \
159 static void *__nightmare_options_resolve_##id(const char *path, \
160 size_t path_len) { \
161 return path_len == sizeof(#id) - 1 && \
162 strncmp(path, #id, path_len) == 0 \
163 ? &(instance) \
164 : NULL; \
165 } \
166 CMDLINE_SCHEMA_DEFINE(__nightmare_options_##id, "nightmare", #id, \
167 "Nightmare subject options", \
168 __nightmare_options_resolve_##id, __VA_ARGS__)
169
170#define NIGHTMARE_OK \
171 ((struct nightmare_verdict) {.result = NIGHTMARE_RESULT_OK})
172#define NIGHTMARE_FAIL(reason_, msg_) \
173 ((struct nightmare_verdict) { \
174 .result = NIGHTMARE_RESULT_FAIL, .reason = (reason_), .msg = (msg_)})
175#define NIGHTMARE_SKIP(reason_) \
176 ((struct nightmare_verdict) {.result = NIGHTMARE_RESULT_SKIP, \
177 .skip_reason = (reason_)})
178
179/* Subjects keep their own spelling; the counter itself is shared. */
180#define NIGHTMARE_PROGRESS() TEST_PROGRESS()
181
182#define NIGHTMARE_FINDING(kind_, fmt, ...) \
183 nightmare_finding_at( \
184 &(const struct nightmare_finding_site) {.kind = (kind_), \
185 .tier = \
186 NIGHTMARE_TIER_AMBIGUOUS, \
187 .file = __RELFILE__, \
188 .line = __LINE__}, \
189 0, (fmt), ##__VA_ARGS__)
190
191#define NIGHTMARE_FINDING_TIER(kind_, tier_, discriminator_, fmt, ...) \
192 nightmare_finding_at( \
193 &(const struct nightmare_finding_site) {.kind = (kind_), \
194 .tier = (tier_), \
195 .file = __RELFILE__, \
196 .line = __LINE__}, \
197 (discriminator_), (fmt), ##__VA_ARGS__)
198
199void nightmare_run(void);
200bool nightmare_must_stop(void);
201void nightmare_stop_after_finding(void);
202bool nightmare_must_park(void);
203void nightmare_park(struct test_conc_worker *worker);
204void nightmare_finding_at(const struct nightmare_finding_site *site,
205 uint64_t discriminator, const char *fmt, ...)
206 cc_printf_like(3, 4);
207void nightmare_request_external_fail(const char *kind, uint64_t discriminator,
208 const char *fmt, ...) cc_printf_like(3, 4);
209void nightmare_report_stall(const struct test_stall_evidence *evidence);
210