skip to content
==> 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}