==> src/testkit.c <==1/* The minimal driver around the executor. */
2#include "mesh/exec_wasm_testkit.h"
3
4#include "mesh/abi/base.h"
5#include "mesh/abi/control_block.h"
6#include "mesh/abi/ext.h"
7#include "mesh/abi/ops.h"
8#include "mesh/abi/pipe_table.h"
9#include "mesh/abi/program_table.h"
10#include "mesh/abi/ring.h"
11#include "mesh/abi/view_heap.h"
12#include "mesh/lease.h"
13#include "mesh/manifest.h"
14
15#include <sched.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <ftw.h>
20#include <sys/stat.h>
21#include <time.h>
22#include <unistd.h>
23
24static char *dupf(const char *fmt, const char *a, const char *b) {
25 size_t n = strlen(fmt) + strlen(a) + strlen(b) + 1;
26 char *s = malloc(n);
27 if (s) snprintf(s, n, fmt, a, b);
28 return s;
29}
30
31bool mesh_kit_open(mesh_kit *kit, const char *cache, char **err) {
32 memset(kit, 0, sizeof *kit);
33 kit->mb = mesh_mailbox_test();
34 snprintf(kit->root, sizeof kit->root, "/tmp/mesh-kit-XXXXXX");
35 if (!mkdtemp(kit->root)) {
36 if (err) *err = strdup("mkdtemp failed");
37 return false;
38 }
39 kit->heap = MESH_VIEW_HEAP;
40 kit->cache = cache ? strdup(cache) : NULL;
41 return mesh_wasm_backend_open(kit->mb.base, cache, &kit->b, err);
42}
43
44static bool write_file(const char *path, const void *b, size_t n) {
45 FILE *f = fopen(path, "wb");
46 if (!f) return false;
47 bool ok = fwrite(b, 1, n, f) == n;
48 return fclose(f) == 0 && ok;
49}
50
51static bool read_file(const char *path, uint8_t **out, size_t *n) {
52 FILE *f = fopen(path, "rb");
53 if (!f) return false;
54 fseek(f, 0, SEEK_END);
55 long len = ftell(f);
56 fseek(f, 0, SEEK_SET);
57 *out = malloc(len > 0 ? (size_t)len : 1);
58 *n = len > 0 ? (size_t)len : 0;
59 bool ok = *out && fread(*out, 1, *n, f) == *n;
60 fclose(f);
61 return ok;
62}
63
64bool mesh_kit_load(mesh_kit *kit, uint32_t slot, const char *manifest, const char *wasm_path, char **err) {
65 uint8_t *b;
66 size_t n;
67 if (!read_file(wasm_path, &b, &n)) {
68 if (err) *err = dupf("%s%s: cannot read", wasm_path, "");
69 return false;
70 }
71 bool ok = mesh_kit_load_bytes(kit, slot, manifest, b, n, err);
72 free(b);
73 return ok;
74}
75
76void mesh_kit_reads(mesh_kit *kit, uint32_t slot, uint32_t mask) {
77 size_t e = mesh_prog_entry(slot);
78 uint32_t g = mesh_mailbox_get(kit->mb, e + MESH_PT_GEN);
79 mesh_mailbox_publish(kit->mb, e + MESH_PT_GEN, g + 1);
80 mesh_mailbox_set(kit->mb, e + MESH_PT_READS_MASK, mask);
81 mesh_mailbox_publish(kit->mb, e + MESH_PT_GEN, g + 2);
82}
83
84bool mesh_kit_load_bytes(mesh_kit *kit, uint32_t slot, const char *manifest, const uint8_t *wasm, size_t n, char **err) {
85 mesh_manifest m;
86 char *why = NULL;
87 if (!mesh_manifest_parse(manifest, strlen(manifest), &m, &why)) {
88 if (err) *err = why;
89 else free(why);
90 return false;
91 }
92 char *dir = dupf("%s/%s", kit->root, m.name);
93 mkdir(dir, 0755);
94 char *toml = dupf("%s/%s", dir, "program.toml");
95 char *img = malloc(strlen(dir) + strlen(m.name) + 8);
96 sprintf(img, "%s/%s.wasm", dir, m.name);
97 bool ok = write_file(toml, manifest, strlen(manifest)) && write_file(img, wasm, n);
98 free(toml);
99 free(img);
100 free(dir);
101 if (!ok) {
102 mesh_manifest_free(&m);
103 if (err) *err = strdup("cannot write the catalog");
104 return false;
105 }
106 mesh_payload src = {strdup(m.name), strdup(manifest), NULL, 0};
107 mesh_prepared prep;
108 const mesh_compiler *comp = kit->b->ops->compiler(kit->b);
109 if (!comp->ops->prepare(comp, &src, NULL, kit->root, &prep, err)) {
110 mesh_manifest_free(&m);
111 return false;
112 }
113 mesh_kit_slot *s = &kit->slot[slot];
114 memset(s, 0, sizeof *s);
115 if (!kit->b->ops->load(kit->b, &prep, &s->k, err)) {
116 mesh_prepared_free(&prep);
117 mesh_manifest_free(&m);
118 return false;
119 }
120 s->used = true;
121 s->name = strdup(m.name);
122 s->nodes = m.nodes ? m.nodes : 1;
123 s->view_bytes = m.view_bytes;
124 s->view = (kit->heap + 15) & ~(size_t)15;
125 kit->heap = s->view + m.view_bytes;
126 /* The table entry, as the driver writes it before a launch. */
127 mesh_mailbox mb = kit->mb;
128 size_t e = mesh_prog_entry(slot);
129 mesh_mailbox_zero(mb, e, MESH_PT_BYTES);
130 mesh_mailbox_zero(mb, mesh_prog_ctl(slot), MESH_PROG_CTL_BYTES);
131 uint8_t name[MESH_NAME_BYTES] = {0};
132 memcpy(name, m.name, strlen(m.name) < MESH_NAME_MAX ? strlen(m.name) : MESH_NAME_MAX);
133 mesh_mailbox_write(mb, e + MESH_PT_NAME, name, MESH_NAME_BYTES, MESH_NAME_BYTES);
134 mesh_mailbox_set(mb, e + MESH_PT_VIEW_OFF, (uint32_t)s->view);
135 mesh_mailbox_set(mb, e + MESH_PT_VIEW_BYTES, m.view_bytes);
136 mesh_mailbox_set(mb, e + MESH_PT_NODES, s->nodes);
137 mesh_mailbox_set(mb, e + MESH_PT_PEER, m.peer);
138 mesh_mailbox_set(mb, e + MESH_PT_INPUT_OFF, m.input_off);
139 mesh_mailbox_set(mb, e + MESH_PT_INPUT_LEN, m.input_len);
140 if (m.has_request) {
141 mesh_mailbox_set(mb, e + MESH_PT_REQ_OFF, m.request.req_off);
142 mesh_mailbox_set(mb, e + MESH_PT_REQ_MAX, m.request.req_max);
143 mesh_mailbox_set(mb, e + MESH_PT_RESP_OFF, m.request.resp_off);
144 mesh_mailbox_set(mb, e + MESH_PT_RESP_MAX, m.request.resp_max);
145 }
146 mesh_mailbox_zero(mb, mesh_pipe_entry(slot), MESH_PP_BYTES);
147 if (m.inbox) {
148 mesh_mailbox_set(mb, mesh_pipe_entry(slot) + MESH_PP_IN_OFF, m.inbox->off);
149 mesh_mailbox_set(mb, mesh_pipe_entry(slot) + MESH_PP_IN_CAP, m.inbox->cap);
150 mesh_mailbox_set(mb, mesh_pipe_entry(slot) + MESH_PP_IN_REC, m.inbox->rec);
151 }
152 mesh_mailbox_set(mb, mesh_prog_ext(slot) + MESH_PX_PARAM, m.param);
153 mesh_mailbox_set(mb, mesh_prog_ext(slot) + MESH_PX_EXEC, MESH_BACKEND_WASM);
154 /* reads = [names]: the slots holding them now ("*": all). */
155 uint32_t mask = 0;
156 for (size_t i = 0; i < m.nreads; i++) {
157 if (strcmp(m.reads[i], "*") == 0) mask = MESH_READS_ALL;
158 for (uint32_t t = 0; t < 32; t++)
159 if (kit->slot[t].used && kit->slot[t].name && strcmp(kit->slot[t].name, m.reads[i]) == 0) mask |= 1u << t;
160 }
161 mesh_mailbox_set(mb, e + MESH_PT_READS_MASK, mask);
162 mesh_mailbox_publish(mb, e + MESH_PT_STATE, MESH_ST_RUNNING);
163 mesh_manifest_free(&m);
164 mesh_prepared_free(&prep);
165 if (!kit->b->ops->new_stream(kit->b, slot, s->k, &s->st, err)) return false;
166 if (!kit->b->ops->launch(kit->b, s->st, s->k, s->nodes, 0, slot, err)) return false;
167 uint64_t t0 = mesh_host_ns();
168 while (mesh_mailbox_get(mb, mesh_prog_ctl(slot) + MESH_P_ONLINE) < s->nodes) {
169 if (mesh_host_ns() - t0 > 2000000000ull) {
170 if (err) *err = strdup("the lanes did not come online");
171 return false;
172 }
173 sched_yield();
174 }
175 return true;
176}
177
178void mesh_kit_connect(mesh_kit *kit, uint32_t producer, uint32_t consumer) {
179 size_t p = mesh_pipe_entry(producer), c = mesh_pipe_entry(consumer);
180 mesh_mailbox_set(kit->mb, p + MESH_PP_FEEDS_MASK, mesh_mailbox_get(kit->mb, p + MESH_PP_FEEDS_MASK) | 1u << consumer);
181 mesh_mailbox_set(kit->mb, c + MESH_PP_FED_MASK, mesh_mailbox_get(kit->mb, c + MESH_PP_FED_MASK) | 1u << producer);
182}
183
184static void publish(mesh_kit *kit, uint32_t slot, uint32_t op, uint32_t arg) {
185 mesh_kit_slot *s = &kit->slot[slot];
186 uint32_t t = s->next++;
187 size_t e = mesh_prog_ctl(slot) + MESH_P_RING + MESH_ENTRY_BYTES * (size_t)(t % MESH_PROG_RING);
188 mesh_mailbox_set(kit->mb, e + MESH_E_OP, op);
189 mesh_mailbox_set(kit->mb, e + MESH_E_ARG, arg);
190 mesh_mailbox_set(kit->mb, e + MESH_E_SLOT, t % MESH_PROG_RESULTS);
191 mesh_mailbox_publish(kit->mb, e + MESH_E_SEQ, t + 1);
192}
193
194static bool wait_mark(mesh_kit *kit, uint32_t slot, uint32_t t, uint32_t *fault, unsigned timeout_ms) {
195 size_t mark = mesh_prog_ctl(slot) + MESH_P_MARKS + 4 * (size_t)(t % MESH_PROG_RESULTS);
196 uint64_t t0 = mesh_host_ns();
197 *fault = 0;
198 while (mesh_mailbox_get(kit->mb, mark) != t + 1) {
199 uint32_t f = mesh_mailbox_get(kit->mb, mesh_prog_ctl(slot) + MESH_P_FAULT);
200 if (f) {
201 *fault = f;
202 return false;
203 }
204 if (mesh_host_ns() - t0 > (uint64_t)timeout_ms * 1000000ull) return false;
205 sched_yield();
206 }
207 return true;
208}
209
210bool mesh_kit_task(mesh_kit *kit, uint32_t slot, uint32_t op, uint32_t arg, uint32_t *result, uint32_t *fault,
211 unsigned timeout_ms) {
212 uint32_t t = kit->slot[slot].next;
213 publish(kit, slot, op, arg);
214 if (!wait_mark(kit, slot, t, fault, timeout_ms)) return false;
215 *result = mesh_mailbox_get(kit->mb, mesh_prog_ctl(slot) + MESH_P_RESULTS + 4 * (size_t)(t % MESH_PROG_RESULTS));
216 return true;
217}
218
219bool mesh_kit_tasks(mesh_kit *kit, uint32_t slot, uint32_t op, uint32_t arg, uint32_t n, uint32_t *fault, unsigned timeout_ms) {
220 mesh_kit_slot *s = &kit->slot[slot];
221 uint32_t first = s->next;
222 for (uint32_t i = 0; i < n; i++) {
223 /* At most a ring of tickets in flight (as the publisher keeps it). */
224 if (i >= MESH_PROG_RING && !wait_mark(kit, slot, first + i - MESH_PROG_RING, fault, timeout_ms)) return false;
225 publish(kit, slot, op, arg);
226 }
227 for (uint32_t i = n > MESH_PROG_RING ? n - MESH_PROG_RING : 0; i < n; i++)
228 if (!wait_mark(kit, slot, first + i, fault, timeout_ms)) return false;
229 return true;
230}
231
232long mesh_kit_request(mesh_kit *kit, uint32_t slot, const void *req, uint32_t n, char *out, size_t cap, unsigned timeout_ms) {
233 mesh_mailbox mb = kit->mb;
234 size_t e = mesh_prog_entry(slot), c = mesh_prog_ctl(slot);
235 size_t req_at = kit->slot[slot].view + mesh_mailbox_get(mb, e + MESH_PT_REQ_OFF);
236 size_t resp_at = kit->slot[slot].view + mesh_mailbox_get(mb, e + MESH_PT_RESP_OFF);
237 uint32_t req_max = mesh_mailbox_get(mb, e + MESH_PT_REQ_MAX);
238 if (n > req_max) n = req_max;
239 mesh_mailbox_write(mb, req_at, req, n, (n + 3) & ~3u);
240 mesh_mailbox_set(mb, c + MESH_P_REQ_LEN, n);
241 mesh_mailbox_publish64(mb, c + MESH_P_REQ_SEQ, mesh_mailbox_get64(mb, c + MESH_P_REQ_SEQ) + 1);
242 uint32_t r, fault;
243 if (!mesh_kit_task(kit, slot, MESH_OP_REQ, 0, &r, &fault, timeout_ms)) return -1;
244 uint32_t len = mesh_mailbox_get(mb, c + MESH_P_RESP_LEN);
245 if (len > cap) len = (uint32_t)cap;
246 for (uint32_t i = 0; i < len; i++) out[i] = (char)mesh_mailbox_get8(mb, resp_at + i);
247 return len;
248}
249
250void mesh_kit_unload(mesh_kit *kit, uint32_t slot) {
251 mesh_kit_slot *s = &kit->slot[slot];
252 if (!s->used) return;
253 mesh_mailbox_publish(kit->mb, mesh_prog_ctl(slot) + MESH_P_SHUTDOWN, 1);
254 bool done = false;
255 char *err = NULL;
256 while (s->st && kit->b->ops->query(kit->b, s->st, s->k, &done, &err) && !done) sched_yield();
257 free(err);
258 err = NULL;
259 if (s->k) kit->b->ops->unload(kit->b, s->k, &err);
260 free(err);
261 mesh_stream_drop(s->st);
262 size_t e = mesh_prog_entry(slot);
263 mesh_mailbox_zero(kit->mb, e, MESH_PT_BYTES);
264 mesh_mailbox_zero(kit->mb, mesh_prog_ctl(slot), MESH_PROG_CTL_BYTES);
265 free(s->name);
266 memset(s, 0, sizeof *s);
267}
268
269size_t mesh_kit_view(const mesh_kit *kit, uint32_t slot, size_t off) { return kit->slot[slot].view + off; }
270
271static int remove_one(const char *path, const struct stat *st, int flag, struct FTW *ftw) {
272 (void)st, (void)flag, (void)ftw;
273 return remove(path);
274}
275
276void mesh_kit_close(mesh_kit *kit) {
277 for (uint32_t s = 0; s < 32; s++) mesh_kit_unload(kit, s);
278 mesh_backend_destroy(kit->b);
279 /* The catalog: remove what we wrote. */
280 nftw(kit->root, remove_one, 16, FTW_DEPTH | FTW_PHYS);
281 free(kit->mb.base);
282 free(kit->cache);
283}
284
285char *mesh_kit_artifact(const char *id) {
286 char exe[4096];
287 ssize_t n = readlink("/proc/self/exe", exe, sizeof exe - 1);
288 if (n <= 0) return NULL;
289 exe[n] = 0;
290 char *t = strstr(exe, "/test/");
291 if (!t) {
292 fprintf(stderr, "mesh-kit: %s is not a sexc test binary\n", exe);
293 return NULL;
294 }
295 *t = 0;
296 size_t len = strlen(exe) + strlen(id) + 16;
297 char *path = malloc(len);
298 snprintf(path, len, "%s/wasm/%s.wasm", exe, id);
299 struct stat st;
300 if (stat(path, &st) != 0) {
301 fprintf(stderr, "mesh-kit: %s is not built: run `sexc build` (with this profile) first\n", path);
302 free(path);
303 return NULL;
304 }
305 return path;
306}