==> src/mock.c <==1/* crates/mesh-top/tests/support/mock.rs, ported: the world, its snapshot, trace and commands. */
2#include "mesh/top_mock.h"
3
4#include <math.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9
10const char *const MT_MOCK_CATALOG[MT_MOCK_CATALOG_LEN] = {"doom", "gemm", "hello", "http", "infer", "life", "train"};
11
12static void *xmalloc(size_t n) {
13 void *p = malloc(n ? n : 1);
14 if (!p) abort();
15 return p;
16}
17static char *xfmt(const char *f, ...)
18#if defined(__GNUC__) || defined(__clang__)
19 __attribute__((format(printf, 1, 2)))
20#endif
21 ;
22static char *xfmt(const char *f, ...) {
23 va_list ap, cp;
24 va_start(ap, f);
25 va_copy(cp, ap);
26 int n = vsnprintf(NULL, 0, f, cp);
27 va_end(cp);
28 char *s = xmalloc((size_t)n + 1);
29 vsnprintf(s, (size_t)n + 1, f, ap);
30 va_end(ap);
31 return s;
32}
33
34/* ---- the catalog */
35
36typedef struct {
37 uint32_t nodes, view_bytes, snap_bytes, policy, hz, fanout;
38 const char *counters[4];
39 mt_mock_field fields[8];
40 size_t nfields;
41 float rate;
42 uint32_t task_ns;
43} spec;
44
45static mt_mock_prog make(const char *name, uint32_t slot, const spec *s) {
46 mt_mock_prog p;
47 memset(&p, 0, sizeof p);
48 p.slot = slot;
49 snprintf(p.name, sizeof p.name, "%s", name);
50 p.state = MESH_ST_RUNNING;
51 p.nodes = s->nodes;
52 p.view_bytes = s->view_bytes;
53 p.snap_bytes = s->snap_bytes;
54 p.policy = s->policy;
55 p.hz = s->hz;
56 p.fanout = s->fanout;
57 p.peer = MESH_PEER_MODE_NONE;
58 memcpy(p.counters, s->counters, sizeof p.counters);
59 memcpy(p.fields, s->fields, sizeof p.fields);
60 p.nfields = s->nfields;
61 p.hash = mesh_fnv1a64((const uint8_t *)name, strlen(name));
62 p.regs = 36;
63 p.loads = 1;
64 p.rate = s->rate;
65 p.task_ns = s->task_ns;
66 return p;
67}
68
69bool mt_mock_catalog_prog(const char *name, uint32_t slot, mt_mock_prog *out) {
70 if (strcmp(name, "doom") == 0) {
71 spec s = {4, MESH_DOOM_VIEW_BYTES, MESH_DOOM_SNAP_BYTES, MESH_POLICY_PERIODIC, 30, 4, {"frames", "keys", "stalls", "follows"},
72 {{"pos_x", MESH_FT_F32, 0x0}, {"pos_y", MESH_FT_F32, 0x4}, {"dir_x", MESH_FT_F32, 0x8}, {"dir_y", MESH_FT_F32, 0xc},
73 {"frame", MESH_FT_U32, 0x20}, {"keys", MESH_FT_U32, 0x28}}, 6, 120.0f, 300000};
74 *out = make(name, slot, &s);
75 } else if (strcmp(name, "train") == 0) {
76 spec s = {1, 0x1000, 0xd00, MESH_POLICY_CONTINUOUS, 0, 1, {"steps", "timeouts", "inits", ""},
77 {{"a", MESH_FT_F32, 0x0}, {"b", MESH_FT_F32, 0x4}, {"c", MESH_FT_F32, 0x8}, {"d", MESH_FT_F32, 0xc},
78 {"next_step", MESH_FT_U32, 0x1c}, {"err", MESH_FT_U32, 0x20}}, 6, 50.0f, 19000};
79 *out = make(name, slot, &s);
80 out->peer = MESH_PEER_MODE_LOCKSTEP;
81 } else if (strcmp(name, "infer") == 0) {
82 spec s = {1, 0x1000, 0x400, MESH_POLICY_PERIODIC, 10, 1, {"runs", "", "", ""},
83 {{"a", MESH_FT_F32, 0x0}, {"b", MESH_FT_F32, 0x4}, {"runs", MESH_FT_U32, 0x10}, {"train_step", MESH_FT_U32, 0x14}}, 4,
84 10.0f, 10000};
85 *out = make(name, slot, &s);
86 } else if (strcmp(name, "http") == 0) {
87 spec s = {1, 0x5000, 0x40, MESH_POLICY_ON_DEMAND, 0, 1, {"", "", "", ""},
88 {{"served", MESH_FT_U32, 0x0}, {"last_code", MESH_FT_U32, 0x4}}, 2, 2.0f, 61000};
89 *out = make(name, slot, &s);
90 } else if (strcmp(name, "hello") == 0) {
91 spec s = {2, 0x1000, 0x40, MESH_POLICY_ON_DEMAND, 0, 1, {"tasks", "inits", "", ""},
92 {{"magic", MESH_FT_U32, 0}, {"last_arg", MESH_FT_U32, 4}, {"last_result", MESH_FT_U32, 8}}, 3, 400.0f, 7000};
93 *out = make(name, slot, &s);
94 } else if (strcmp(name, "gemm") == 0) {
95 spec s = {1, 0x8000, 0x40, MESH_POLICY_PERIODIC, 1, 1, {"gemms", "gflop", "plain", "checks"},
96 {{"gflops", MESH_FT_F32, 0x10}, {"best", MESH_FT_F32, 0x14}}, 2, 1.0f, 8000000};
97 *out = make(name, slot, &s);
98 out->has_native_pid = true;
99 out->native_pid = 40000 + slot;
100 } else if (strcmp(name, "life") == 0) {
101 spec s = {3, 0x1000, 0x80, MESH_POLICY_PERIODIC, 20, 3, {"generations", "births", "deaths", ""},
102 {{"generation", MESH_FT_U32, 0}, {"alive", MESH_FT_U32, 4}, {"density", MESH_FT_F32, 8}, {"seed", MESH_FT_U64, 0x10},
103 {"rule", MESH_FT_U8, 0x18}}, 5, 60.0f, 300000};
104 *out = make(name, slot, &s);
105 } else {
106 return false;
107 }
108 return true;
109}
110
111/* ---- the world */
112
113void mt_mock_default_assign(uint32_t n, uint32_t fanout, uint8_t out[MESH_PLACE_PARTS_MAX]) {
114 memset(out, MESH_PART_NONE, MESH_PLACE_PARTS_MAX);
115 if (n < 1) n = 1;
116 if (n > fanout) n = fanout;
117 uint32_t b = 0;
118 for (uint32_t k = 0; k < n; k++) {
119 uint32_t c = fanout / n + (k < fanout % n ? 1 : 0);
120 for (uint32_t i = 0; i < c; i++) out[b++] = (uint8_t)k;
121 }
122}
123
124static void log_push(mt_mock_world *w, char *line) {
125 w->log = realloc(w->log, (w->nlog + 1) * sizeof *w->log);
126 if (!w->log) abort();
127 w->log[w->nlog++] = line;
128}
129
130static uint64_t elapsed_ms(const mt_mock_world *w) { return mt_since(mt_now(), w->started) / 1000000u; }
131
132static void say(mt_mock_world *w, const char *s) {
133 uint64_t ms = elapsed_ms(w);
134 log_push(w, xfmt("[%6llu.%03llu] INFO %s", (unsigned long long)(ms / 1000), (unsigned long long)(ms % 1000), s));
135}
136
137void mt_mock_world_init(mt_mock_world *w, const char *host, uint32_t system) {
138 memset(w, 0, sizeof *w);
139 snprintf(w->host, sizeof w->host, "%s", host);
140 w->system = system;
141 const char *names[5] = {"doom", "train", "infer", "http", "hello"};
142 for (uint32_t i = 0; i < 5; i++) mt_mock_catalog_prog(names[i], i, &w->progs[w->nprogs++]);
143 w->epoch = 5;
144 w->prog_seq = 5;
145 w->pos[0] = MESH_DOOM_START[0];
146 w->pos[1] = MESH_DOOM_START[1];
147 w->dir[0] = MESH_DOOM_START[2];
148 w->dir[1] = MESH_DOOM_START[3];
149 w->step0 = mt_now();
150 w->started = mt_now();
151 w->place[0] = MESH_PLACE_LOCAL;
152 memset(w->assign, MESH_PART_NONE, sizeof w->assign);
153 w->n = system == MESH_PEER_NONE ? 1 : (system + 1 > 2 ? system + 1 : 2);
154 snprintf(w->gpu, sizeof w->gpu, "NVIDIA GB10 (mock)");
155 w->cc = 121;
156 w->blocks_per_sm = 24;
157 snprintf(w->arch, sizeof w->arch, "aarch64");
158 for (uint32_t k = 0; k < MESH_MAX_SYSTEMS; k++) w->waits[k] = 12.0f + 30.0f * (float)k;
159 for (int i = 0; i < 5; i++) {
160 char *s = xfmt("load %s: jit 1.2 ms, blink 1840 us", names[i]);
161 say(w, s);
162 free(s);
163 }
164}
165
166void mt_mock_world_free(mt_mock_world *w) {
167 for (size_t i = 0; i < w->nlog; i++) free(w->log[i]);
168 free(w->log);
169 w->log = NULL;
170 w->nlog = 0;
171}
172
173static void split_assign(const mt_mock_world *w, uint8_t out[MESH_PLACE_PARTS_MAX]) {
174 if (w->assign[0] == MESH_PART_NONE) mt_mock_default_assign(w->n, 4, out);
175 else memcpy(out, w->assign, MESH_PLACE_PARTS_MAX);
176}
177
178mt_mock_prog *mt_mock_prog_named(mt_mock_world *w, const char *name) {
179 for (size_t i = 0; i < w->nprogs; i++)
180 if (strcmp(w->progs[i].name, name) == 0) return &w->progs[i];
181 return NULL;
182}
183
184static bool running(mt_mock_world *w, const char *name) {
185 mt_mock_prog *p = mt_mock_prog_named(w, name);
186 return p && p->state == MESH_ST_RUNNING;
187}
188
189static int64_t ticks(const mt_mock_world *w, const mt_mock_prog *p) {
190 return (int64_t)(mt_secs_f64(mt_since(mt_now(), w->started)) * (double)p->rate);
191}
192
193static uint32_t done(const mt_mock_world *w, const mt_mock_prog *p) {
194 if (p->has_frozen) return p->frozen;
195 int64_t d = p->offset + ticks(w, p);
196 return (uint32_t)(d > 0 ? d : 0);
197}
198
199uint32_t mt_mock_train_step(const mt_mock_world *w) {
200 if (w->has_frozen_step) return w->frozen_step;
201 float f = mt_secs_f32(mt_since(mt_now(), w->step0)) * 400.0f;
202 uint32_t s = f >= 4294967295.0f ? UINT32_MAX : f > 0 ? (uint32_t)f : 0;
203 return s / 8 * 8;
204}
205
206void mt_mock_params_at(uint32_t step, float out[4]) {
207 float k = 1.0f - expf(-(float)step / 3000.0f);
208 out[0] = 0.0f;
209 out[1] = 2.8f * k;
210 out[2] = 0.0f;
211 out[3] = -2.0f * k;
212}
213
214static uint32_t gt_now(const mt_mock_world *w) { return (uint32_t)mt_since(mt_now(), w->started); }
215
216/* ---- trace */
217
218uint8_t *mt_mock_trace(const mt_mock_world *w, size_t *len) {
219 uint32_t now = gt_now(w);
220 size_t per = 8 + (size_t)MESH_TRACE_PER_PROG * MESH_TRACE_BYTES;
221 uint8_t *out = calloc(w->nprogs ? w->nprogs : 1, per);
222 if (!out) abort();
223 for (size_t i = 0; i < w->nprogs; i++) {
224 const mt_mock_prog *p = &w->progs[i];
225 uint8_t *chunk = out + i * per, *ring = chunk + 8;
226 uint32_t n = done(w, p);
227 float pf = 1e9f / p->rate * (float)p->fanout;
228 uint32_t period = pf >= 4294967295.0f ? UINT32_MAX : pf > 0 ? (uint32_t)pf : 0;
229 uint32_t newest = !p->has_frozen ? now : now - 2000000000u;
230 uint32_t lim = n < MESH_TRACE_PER_PROG ? n : MESH_TRACE_PER_PROG;
231 for (uint32_t k = 0; k < lim; k++) {
232 uint32_t t = n - 1 - k;
233 uint32_t batch = k / p->fanout;
234 uint32_t jitter = ((t * 2654435761u) >> 20) % (p->task_ns / 4 + 1);
235 uint32_t end = newest - (batch * period + jitter);
236 uint32_t dur = p->task_ns - jitter / 2;
237 uint32_t node = t % p->nodes;
238 size_t at = MESH_TRACE_BYTES * (size_t)(t % MESH_TRACE_PER_PROG);
239 mt_put_u32(ring, at, p->slot | node << 8 | MESH_OP_TASK << 16);
240 mt_put_u32(ring, at + 4, t);
241 mt_put_u32(ring, at + 8, end - dur);
242 mt_put_u32(ring, at + 12, end);
243 }
244 mt_put_u32(chunk, 0, p->slot);
245 mt_put_u32(chunk, 4, n);
246 }
247 *len = w->nprogs * per;
248 return out;
249}
250
251/* ---- views */
252
253static uint32_t map_at(int32_t x, int32_t y) {
254 if (x < 0 || x >= 16 || y < 0 || y >= 16) return 1;
255 return mesh_doom_map_cell((size_t)x, (size_t)y);
256}
257
258static void doom_frame(const mt_mock_world *w, uint8_t *v) {
259 float rows = 48.0f;
260 float px = w->pos[0], py = w->pos[1], dx = w->dir[0], dy = w->dir[1];
261 float plx = -dy * 0.66f, ply = dx * 0.66f;
262 mt_put_f32(v, MESH_DOOM_D_POS_X, px);
263 mt_put_f32(v, MESH_DOOM_D_POS_Y, py);
264 mt_put_f32(v, MESH_DOOM_D_DIR_X, dx);
265 mt_put_f32(v, MESH_DOOM_D_DIR_Y, dy);
266 mt_put_f32(v, MESH_DOOM_D_PLANE_X, plx);
267 mt_put_f32(v, MESH_DOOM_D_PLANE_Y, ply);
268 mt_put_f32(v, MESH_DOOM_D_ROWS, rows);
269 mt_put_u32(v, MESH_DOOM_D_MAX_STEPS, 64);
270 mt_put_u32(v, MESH_DOOM_D_FRAME, (uint32_t)w->snaps);
271 mt_put_u32(v, MESH_DOOM_D_KEYS, w->keys);
272 for (int y = 0; y < 16; y++)
273 for (int x = 0; x < 16; x++) mt_put_u32(v, MESH_DOOM_D_MAP + 4 * (size_t)(16 * y + x), map_at(x, y));
274 for (size_t c = 0; c < MESH_DOOM_WIDTH; c++) {
275 float cam = 2.0f * (float)c / (float)MESH_DOOM_WIDTH - 1.0f;
276 float rx = dx + plx * cam, ry = dy + ply * cam;
277 int32_t mx = (int32_t)px, my = (int32_t)py;
278 float ddx = fabsf(1.0f / rx), ddy = fabsf(1.0f / ry);
279 int32_t sx, sy;
280 float sdx, sdy;
281 if (rx < 0.0f) {
282 sx = -1;
283 sdx = (px - (float)mx) * ddx;
284 } else {
285 sx = 1;
286 sdx = ((float)mx + 1.0f - px) * ddx;
287 }
288 if (ry < 0.0f) {
289 sy = -1;
290 sdy = (py - (float)my) * ddy;
291 } else {
292 sy = 1;
293 sdy = ((float)my + 1.0f - py) * ddy;
294 }
295 uint32_t side = 0, kind = 0;
296 for (int i = 0; i < 64; i++) {
297 if (sdx < sdy) {
298 sdx += ddx;
299 mx += sx;
300 side = 0;
301 } else {
302 sdy += ddy;
303 my += sy;
304 side = 1;
305 }
306 kind = map_at(mx, my);
307 if (kind != 0) break;
308 }
309 float dist = side == 0 ? sdx - ddx : sdy - ddy;
310 dist = fmaxf(dist, 1e-3f);
311 mt_put_f32(v, MESH_DOOM_D_HEIGHT + 4 * c, rows / dist);
312 mt_put_u32(v, MESH_DOOM_D_KIND + 4 * c, kind);
313 mt_put_u32(v, MESH_DOOM_D_SIDE + 4 * c, side);
314 mt_put_f32(v, MESH_DOOM_D_DIST + 4 * c, dist);
315 }
316}
317
318static void doom_view(const mt_mock_world *w, uint8_t *v) {
319 doom_frame(w, v);
320 uint32_t mode = w->place[0], sys = w->place[1];
321 if (mode == MESH_PLACE_LOCAL) return;
322 size_t show = MESH_DOOM_D_SHOW;
323 uint32_t seq = (uint32_t)w->snaps;
324 size_t k = seq & 1;
325 size_t data = mesh_doom_show_data(seq & 1);
326 const uint32_t arrays[4] = {MESH_DOOM_D_HEIGHT, MESH_DOOM_D_KIND, MESH_DOOM_D_SIDE, MESH_DOOM_D_DIST};
327 size_t n = 4 * (size_t)MESH_DOOM_WIDTH;
328 for (size_t i = 0; i < 4; i++) memmove(v + data + i * n, v + arrays[i], n);
329 uint32_t by_part = 0;
330 if (mode == MESH_PLACE_SPLIT) {
331 uint8_t a[MESH_PLACE_PARTS_MAX];
332 split_assign(w, a);
333 for (uint32_t b = 0; b < 4; b++) by_part += ((uint32_t)a[b] & 0xf) << (4 * b);
334 } else {
335 for (uint32_t b = 0; b < 4; b++) by_part += (sys & 0xf) << (4 * b);
336 }
337 mt_put_u32(v, show + MESH_SH_BUF + MESH_SH_BUF_BYTES * k + MESH_SHB_FRAME, seq);
338 mt_put_u32(v, show + MESH_SH_BUF + MESH_SH_BUF_BYTES * k + MESH_SHB_SYS, by_part);
339 mt_put_u32(v, show + MESH_SH_MODE, mode);
340 mt_put_u32(v, show + MESH_SH_SYS, sys);
341 mt_put_u32(v, show + MESH_SH_LAT_US, mode == MESH_PLACE_SPLIT ? 1250 : 0);
342 mt_put_u32(v, show + MESH_SH_SEQ, seq);
343 if (mode == MESH_PLACE_PIN && sys != w->system)
344 for (size_t i = 0; i < 4; i++) memset(v + arrays[i], 0, n);
345}
346
347static void train_view(uint32_t step, uint8_t *v) {
348 float p[4];
349 mt_mock_params_at(step, p);
350 for (size_t i = 0; i < 4; i++) mt_put_f32(v, MESH_TRAIN_T_PARAMS + 4 * i, p[i]);
351 mt_put_u32(v, MESH_TRAIN_T_NEXT_STEP, step);
352 mt_put_u32(v, MESH_TRAIN_T_ROUND, step / 8);
353 for (size_t i = 0; i < 256; i++) {
354 float x = -1.0f + 2.0f * (float)i / 255.0f;
355 mt_put_f32(v, MESH_TRAIN_T_X + 4 * i, x);
356 mt_put_f32(v, MESH_TRAIN_T_Y + 4 * i, sinf(3.14159265358979323846f * x));
357 }
358 for (uint32_t t = step > 256 ? step - 256 : 0; t < step; t++) {
359 float loss = 0.5f * expf(-(float)t / 900.0f) + 0.004f + 0.002f * fabsf(sinf((float)t * 0.7f));
360 mt_put_f32(v, MESH_TRAIN_T_LOSS + 4 * (size_t)(t % 256), loss);
361 }
362}
363
364static void infer_view(uint32_t step, uint32_t runs, uint8_t *v) {
365 float p[4];
366 mt_mock_params_at(step, p);
367 for (size_t i = 0; i < 4; i++) mt_put_f32(v, MESH_INFER_I_PARAMS + 4 * i, p[i]);
368 mt_put_u32(v, MESH_INFER_I_RUNS, runs);
369 mt_put_u32(v, MESH_INFER_I_TRAIN_STEP, step);
370 for (size_t i = 0; i < MESH_INFER_POINTS; i++) {
371 float x = -1.0f + 2.0f * (float)i / (float)(MESH_INFER_POINTS - 1);
372 mt_put_f32(v, MESH_INFER_I_X + 4 * i, x);
373 mt_put_f32(v, MESH_INFER_I_Y + 4 * i, p[0] + p[1] * x + p[2] * x * x + p[3] * x * x * x);
374 }
375}
376
377/* ---- the host block: network and load */
378
379const char *mt_mock_iface(uint32_t a, uint32_t b) {
380 uint32_t lo = a < b ? a : b, hi = a > b ? a : b;
381 return lo == 0 && hi == 1 ? "enp1s0f0np0" : "enP7s7";
382}
383
384static bool is_down(const mt_mock_world *w, uint32_t k) {
385 for (size_t i = 0; i < w->ndown; i++)
386 if (w->down[i] == k) return true;
387 return false;
388}
389
390static uint64_t link_at(const mt_mock_world *w, uint32_t k, uint64_t t, uint64_t frames[MESH_NET_KINDS]) {
391 memset(frames, 0, MESH_NET_KINDS * sizeof *frames);
392 if (w->system == MESH_PEER_NONE || k == w->system || k >= w->n || is_down(w, k)) return 0;
393 uint64_t rounds = MT_MOCK_ROUNDS_PER_S * t / 1000, pings = 4 * t / 1000;
394 uint64_t place = w->place[0] == MESH_PLACE_SPLIT ? 30 * t / 1000 : 0;
395 frames[0] = rounds;
396 frames[1] = place;
397 frames[2] = 0;
398 frames[3] = pings;
399 return rounds * MT_MOCK_ROUND_BYTES + pings + place * 2048;
400}
401
402typedef struct {
403 const char *name;
404 uint64_t bytes;
405 uint32_t mask;
406} nic_sum;
407
408static void net(const mt_mock_world *w, uint64_t t, uint8_t *h) {
409 nic_sum ifs[2];
410 size_t nifs = 0;
411 uint64_t sample = t / 1000 * 1000;
412 for (uint32_t k = 0; k < MESH_MAX_SYSTEMS; k++) {
413 uint64_t frames[MESH_NET_KINDS], sf[MESH_NET_KINDS];
414 uint64_t bytes = link_at(w, k, t, frames);
415 if (bytes == 0) continue;
416 size_t at = mesh_link(k);
417 mt_put_u64(h, at + MESH_L_RX_BYTES, bytes);
418 mt_put_u64(h, at + MESH_L_TX_BYTES, bytes);
419 for (size_t i = 0; i < MESH_NET_KINDS; i++) {
420 mt_put_u64(h, at + MESH_L_RX_FRAMES + 8 * i, frames[i]);
421 mt_put_u64(h, at + MESH_L_TX_FRAMES + 8 * i, frames[i]);
422 }
423 const char *name = mt_mock_iface(w->system, k);
424 char local[64];
425 snprintf(local, sizeof local, "192.168.%u.%u:%u", strcmp(name, "enP7s7") == 0 ? 1u : 177u, 11 + w->system, 40000 + k);
426 mt_put_str(h, at + MESH_L_LOCAL, 32, local);
427 mt_put_str(h, at + MESH_L_IFACE, 16, name);
428 size_t e = 0;
429 while (e < nifs && strcmp(ifs[e].name, name) != 0) e++;
430 if (e == nifs) {
431 ifs[nifs].name = name;
432 ifs[nifs].bytes = 0;
433 ifs[nifs].mask = 0;
434 nifs++;
435 }
436 ifs[e].bytes += link_at(w, k, sample, sf);
437 ifs[e].mask |= 1u << k;
438 }
439 /* BTreeMap order: by name. */
440 if (nifs == 2 && strcmp(ifs[0].name, ifs[1].name) > 0) {
441 nic_sum t2 = ifs[0];
442 ifs[0] = ifs[1];
443 ifs[1] = t2;
444 }
445 if (sample > 0) {
446 uint64_t total[8] = {0};
447 for (size_t i = 0; i < nifs; i++) {
448 uint64_t lan = strcmp(ifs[i].name, "enP7s7") == 0 ? 50 * sample : 0;
449 uint64_t b = ifs[i].bytes;
450 uint64_t v[8] = {2 * b + lan, 2 * b + lan, b / 70, b / 70, 0, 0, 0, 0};
451 size_t at = mesh_nic(i);
452 mt_put_str(h, at + MESH_NI_NAME, 16, ifs[i].name);
453 for (size_t j = 0; j < 8; j++) {
454 mt_put_u64(h, at + MT_NIC_COUNTERS[j], v[j]);
455 total[j] += v[j];
456 }
457 mt_put_u32(h, at + MESH_NI_LINKS, ifs[i].mask);
458 mt_put_u32(h, at + MESH_NI_COUNT, 1);
459 }
460 mt_put_u32(h, MESH_S_NET_NICS, (uint32_t)nifs);
461 mt_put_str(h, MESH_S_NET_TOTAL + MESH_NI_NAME, 16, "total");
462 for (size_t j = 0; j < 8; j++) mt_put_u64(h, MESH_S_NET_TOTAL + MT_NIC_COUNTERS[j], total[j] + (j < 2 ? 50 * sample : 0));
463 mt_put_u32(h, MESH_S_NET_TOTAL + MESH_NI_COUNT, 2);
464 mt_put_u64(h, MESH_S_NET_AT_MS, sample);
465 mt_put_u32(h, MESH_S_NET_SAMPLES, (uint32_t)(sample / 1000));
466 }
467 uint64_t reqs = 15 * t / 1000;
468 mt_put_u64(h, MESH_S_CTL_RX, 5 * reqs);
469 mt_put_u64(h, MESH_S_CTL_TX, 3000 * reqs);
470 mt_put_u64(h, MESH_S_CTL_REQS, reqs);
471 mt_put_u64(h, MESH_S_HTTP_CONNS, w->http_served);
472}
473
474static void load(const mt_mock_world *w, uint64_t t, uint8_t *h) {
475 float share = 0.48f + 0.1f * (float)(w->system == MESH_PEER_NONE ? 0 : w->system);
476 mt_put_u64(h, MESH_S_LOAD_AT_MS, t > 400 ? (t - 400 > 1 ? t - 400 : 1) : 1);
477 mt_put_u32(h, MESH_S_LOAD_SAMPLES, (uint32_t)(t / 1000 + w->snaps));
478 mt_put_u32(h, MESH_S_LOAD_FLAGS, MESH_LF_GPU | MESH_LF_PSI | MESH_LF_CLASSES | MESH_LF_TEMP);
479 const struct {
480 size_t o;
481 float v;
482 } fs[] = {{MESH_S_GPU_SHARE, share}, {MESH_S_GPU_SHARE10, share}, {MESH_S_GPU_SLICE_US, 2041.0f}, {MESH_S_GPU_GAP_US, 2232.0f},
483 {MESH_S_GPU_MAX_GAP_US, 2338.0f}, {MESH_S_GPU_GAPS_S, 704.0f}, {MESH_S_GPU_OBS, 3.0f}, {MESH_S_GPU_INFLATE, 1.0f / share},
484 {MESH_S_CPU_UTIL, 0.07f}, {MESH_S_CPU_BIG, 0.113f}, {MESH_S_CPU_LITTLE, 0.022f}, {MESH_S_LOADAVG, 1.27f},
485 {MESH_S_PSI_CPU, 0.0016f}, {MESH_S_PSI_MEM, 0.0f}, {MESH_S_PSI_IO, 0.0002f}, {MESH_S_MEM_FREE, 0.709f},
486 {MESH_S_MESHD_CPU, 0.012f}, {MESH_S_MESHD_RSS_MB, 161.8f}, {MESH_S_TEMP_C, 68.9f}};
487 for (size_t i = 0; i < sizeof fs / sizeof fs[0]; i++) mt_put_f32(h, fs[i].o, fs[i].v);
488 const struct {
489 size_t o;
490 uint32_t v;
491 } us[] = {{MESH_S_CPUS, 20}, {MESH_S_CPUS_BIG, 10}, {MESH_S_CPUS_LITTLE, 10}, {MESH_S_MEM_TOTAL_MB, 124543},
492 {MESH_S_MEM_AVAIL_MB, 88294}, {MESH_S_SWAP_TOTAL_MB, 32767}, {MESH_S_SWAP_USED_MB, 5717},
493 {MESH_S_MESHD_THREADS, 7}, {MESH_S_DISK_FREE_MB, 196622}, {MESH_S_DISK_TOTAL_MB, 937290}};
494 for (size_t i = 0; i < sizeof us / sizeof us[0]; i++) mt_put_u32(h, us[i].o, us[i].v);
495 for (size_t i = 0; i < 20; i++) {
496 h[MESH_S_CORE_UTIL + i] = i == 9 ? 100 : (uint8_t)(i * 3);
497 h[MESH_S_CORE_CLASS + i] = i % 10 < 5 ? MESH_CORE_LITTLE : MESH_CORE_BIG;
498 }
499 uint32_t members = w->system == MESH_PEER_NONE ? 1 : w->n;
500 for (uint32_t k = 0; k < members; k++) {
501 if (is_down(w, k)) continue;
502 size_t m = mesh_mload(k);
503 mt_put_u64(h, m + MESH_ML_AT_MS, t > 300 ? (t - 300 > 1 ? t - 300 : 1) : 1);
504 float ks = 0.48f + 0.1f * (float)k;
505 const struct {
506 size_t o;
507 float v;
508 } mf[] = {{MESH_ML_SHARE, ks}, {MESH_ML_SHARE10, ks}, {MESH_ML_MAX_GAP_US, 2338.0f}, {MESH_ML_CPU, 0.07f + 0.01f * (float)k},
509 {MESH_ML_CPU_BIG, 0.11f}, {MESH_ML_CPU_LITTLE, 0.02f}, {MESH_ML_PSI_CPU, 0.0016f}, {MESH_ML_PSI_MEM, 0.0f},
510 {MESH_ML_MESHD_CPU, 0.012f}};
511 for (size_t i = 0; i < sizeof mf / sizeof mf[0]; i++) mt_put_f32(h, m + mf[i].o, mf[i].v);
512 const struct {
513 size_t o;
514 uint32_t v;
515 } mu[] = {{MESH_ML_MEM_AVAIL_MB, 88294}, {MESH_ML_MEM_TOTAL_MB, 124543}, {MESH_ML_DISK_FREE_MB, 196622},
516 {MESH_ML_CPUS, 10 | 10 << 16}, {MESH_ML_FLAGS, MESH_LF_GPU | MESH_LF_PSI | MESH_LF_CLASSES}};
517 for (size_t i = 0; i < sizeof mu / sizeof mu[0]; i++) mt_put_u32(h, m + mu[i].o, mu[i].v);
518 }
519 for (size_t i = 0; i < w->nprogs; i++) {
520 const mt_mock_prog *p = &w->progs[i];
521 if (p->state == MESH_ST_EMPTY) continue;
522 size_t at = mesh_pload(p->slot);
523 uint32_t host = p->has_native_pid && p->state == MESH_ST_RUNNING ? MESH_PLF_HOST : 0;
524 mt_put_u32(h, at + MESH_PL_FLAGS, MESH_PLF_LOADED | MESH_PLF_GPU | MESH_PLF_LAT | MESH_PLF_TASK | host);
525 if (host) {
526 mt_put_f32(h, at + MESH_PL_CPU, 0.975f);
527 mt_put_f32(h, at + MESH_PL_CPU10, 0.97f);
528 mt_put_u64(h, at + MESH_PL_RSS, (uint64_t)17 << 20);
529 mt_put_u32(h, at + MESH_PL_UNITS, 1);
530 }
531 mt_put_u32(h, at + MESH_PL_LAT_N, 1280);
532 const struct {
533 size_t o;
534 float v;
535 } pf[] = {{MESH_PL_SHARE, share}, {MESH_PL_SHARE10, share}, {MESH_PL_SLICE_US, 2042.0f}, {MESH_PL_GAP_US, 2232.0f},
536 {MESH_PL_MAX_GAP_US, 2334.0f}, {MESH_PL_OBS, 1.0f}, {MESH_PL_P50_US, 812.0f}, {MESH_PL_P90_US, 1450.0f},
537 {MESH_PL_P99_US, 2390.0f}, {MESH_PL_MAX_US, 4120.0f}, {MESH_PL_TASK_US, 830.0f}, {MESH_PL_TASKS_S, 120.0f},
538 {MESH_PL_BUSY, 0.1f}};
539 for (size_t k = 0; k < sizeof pf / sizeof pf[0]; k++) mt_put_f32(h, at + pf[k].o, pf[k].v);
540 }
541}
542
543/* ---- synth */
544
545mt_snapshot mt_mock_synth(mt_mock_world *w) {
546 w->snaps++;
547 float t = mt_secs_f32(mt_since(mt_now(), w->started));
548 uint32_t step = mt_mock_train_step(w);
549 mt_section *sections = xmalloc(w->nprogs * sizeof *sections);
550 float rates[MESH_PROG_SLOTS] = {0};
551 uint8_t lock_view[32] = {0};
552 float total_rate = 0.0f;
553 for (size_t pi = 0; pi < w->nprogs; pi++) {
554 mt_mock_prog p = w->progs[pi];
555 mt_section s = mt_section_new(p.slot, p.nodes, p.snap_bytes);
556 bool run = p.state == MESH_ST_RUNNING;
557 uint8_t *e = mt_section_entry(&s);
558 mt_put_u32(e, MESH_PT_STATE, p.state);
559 mt_put_u32(e, MESH_PT_GEN, 2 * p.loads);
560 mt_put_str(e, MESH_PT_NAME, MESH_NAME_BYTES, p.name);
561 mt_put_u32(e, MESH_PT_VIEW_OFF, (uint32_t)(MESH_VIEW_HEAP + 0x10000 * (size_t)p.slot));
562 mt_put_u32(e, MESH_PT_VIEW_BYTES, p.view_bytes);
563 mt_put_u32(e, MESH_PT_SNAP_BYTES, p.snap_bytes);
564 mt_put_u32(e, MESH_PT_NODES, p.nodes);
565 mt_put_u32(e, MESH_PT_POLICY, p.policy);
566 mt_put_u32(e, MESH_PT_HZ, p.hz);
567 mt_put_u32(e, MESH_PT_FANOUT, p.fanout);
568 mt_put_u32(e, MESH_PT_PEER, p.peer);
569 mt_put_u64(e, MESH_PT_PTX_HASH, p.hash);
570 mt_put_u32(e, MESH_PT_REGS, p.regs);
571 mt_put_u32(e, MESH_PT_CODE_BYTES, 13216);
572 mt_put_u32(e, MESH_PT_VIEW_ABI, 1);
573 mt_put_u32(e, MESH_PT_RT_ABI, MESH_RT_ABI);
574 mt_put_u32(e, MESH_PT_LOADS, p.loads);
575 uint32_t d = done(w, &p);
576 uint8_t *c = mt_section_ctl(&s);
577 mt_put_u32(c, MESH_P_ONLINE, run ? p.nodes : 0);
578 mt_put_u32(c, MESH_P_HALTED, run ? 0 : p.nodes);
579 mt_put_u32(c, MESH_P_DONE, d);
580 mt_put_u32(c, MESH_P_TRACE_N, d);
581 mt_put_u32(c, MESH_P_LAUNCHES, p.loads);
582 if (p.has_native_pid) {
583 mt_put_u32(c, MESH_P_PID, run ? p.native_pid : 0);
584 mt_put_u32(c, MESH_P_SPAWNS, 3);
585 mt_put_u32(c, MESH_P_CRASHES, 1);
586 }
587 for (size_t i = 0; i < 4; i++) {
588 const char *nm = p.counters[i];
589 uint32_t v = 0;
590 if (!*nm) v = 0;
591 else if (i == 0) v = d / p.fanout;
592 else if (strcmp(p.name, "doom") == 0 && i == 1) v = w->keys;
593 else if ((strcmp(p.name, "hello") == 0 || strcmp(p.name, "train") == 0) && strcmp(nm, "inits") == 0) v = 1;
594 mt_put_u32(c, MESH_P_COUNTERS + 4 * i, v);
595 }
596 uint8_t *x = mt_section_ext(&s);
597 for (size_t i = 0; i < 4; i++) mt_put_str(x, MESH_PX_COUNTER_NAMES + MESH_NAME_BYTES * i, MESH_NAME_BYTES, p.counters[i]);
598 mt_put_u32(x, MESH_PX_FIELD_COUNT, (uint32_t)p.nfields);
599 if (p.has_native_pid) mt_put_u32(x, MESH_PX_EXEC, MESH_BACKEND_NATIVE);
600 if (strcmp(p.name, "doom") == 0) {
601 mt_put_u32(x, MESH_PX_PLACE_CAP, MESH_PLACE_CAP_SPLIT);
602 mt_put_u32(x, MESH_PX_PLACE, w->place[0]);
603 mt_put_u32(x, MESH_PX_PLACE_SYS, w->place[1]);
604 uint8_t a[MESH_PLACE_PARTS_MAX];
605 if (w->place[0] == MESH_PLACE_SPLIT) split_assign(w, a);
606 else memset(a, MESH_PART_NONE, sizeof a);
607 memcpy(x + MESH_PX_PLACE_ASSIGN, a, MESH_PLACE_PARTS_MAX);
608 }
609 for (size_t i = 0; i < p.nfields; i++) {
610 size_t at = MESH_PX_FIELDS + MESH_FIELD_BYTES * i;
611 mt_put_str(x, at + MESH_FD_NAME, MESH_FD_NAME_MAX + 1, p.fields[i].name);
612 mt_put_u32(x, at + MESH_FD_TYPE, p.fields[i].type);
613 mt_put_u32(x, at + MESH_FD_OFF, (uint32_t)p.fields[i].off);
614 }
615 for (size_t n = 0; n < p.nodes; n++) {
616 uint8_t *r = mt_section_node_rec(&s, n);
617 mt_put_u32(r, MESH_N_SM, (uint32_t)((p.slot * 7 + n * 5) % 48));
618 bool frozen = !run;
619 for (size_t k = 0; k < w->nstalled; k++) frozen |= strcmp(w->stalled[k].name, p.name) == 0 && w->stalled[k].node == n;
620 mt_put_u32(r, MESH_N_HEARTBEAT, frozen ? 1000 : (uint32_t)w->snaps * 13 + (uint32_t)n);
621 mt_put_u32(r, MESH_N_TASKS, d / p.nodes);
622 mt_put_u32(r, MESH_N_LAST_OP, MESH_OP_TASK);
623 float dutyf = p.rate * (float)p.task_ns / 1e9f / (float)p.nodes;
624 dutyf = dutyf < 0.05f ? 0.05f : dutyf > 0.95f ? 0.95f : dutyf;
625 double duty = (double)dutyf;
626 double ph = (double)n * 0.37 + (double)t * 0.8;
627 double tt = run ? (double)t : 0.0;
628 double busy = 1e9 * (1.0 + duty * tt - 0.04 * cos(ph));
629 mt_put_u64(r, MESH_N_BUSY_NS, busy > 0 ? (busy >= 1.8446744073709552e19 ? UINT64_MAX : (uint64_t)busy) : 0);
630 }
631 uint8_t *v = mt_section_view(&s);
632 if (strcmp(p.name, "doom") == 0) doom_view(w, v);
633 else if (strcmp(p.name, "train") == 0) {
634 train_view(step, v);
635 memcpy(lock_view, v, 32);
636 } else if (strcmp(p.name, "infer") == 0) infer_view(step, d, v);
637 else if (strcmp(p.name, "http") == 0) {
638 mt_put_u32(v, MESH_HTTP_HV_SERVED, w->http_served);
639 mt_put_u32(v, MESH_HTTP_HV_LAST_CODE, 200);
640 } else if (strcmp(p.name, "hello") == 0) {
641 memcpy(v, "HELO", 4);
642 mt_put_u32(v, 4, w->hello_last[0]);
643 mt_put_u32(v, 8, w->hello_last[1]);
644 } else if (strcmp(p.name, "life") == 0) {
645 mt_put_u32(v, 0, d);
646 mt_put_u32(v, 4, 37 + d % 11);
647 mt_put_f32(v, 8, 0.29f);
648 mt_put_u64(v, 0x10, 0x5eed1234abcdu);
649 v[0x18] = 23;
650 for (size_t i = 0x20; i < mt_section_view_len(&s); i++) v[i] = ((uint32_t)i * 7 + d) % 5 == 0 ? '#' : '.';
651 }
652 if (run) {
653 rates[p.slot] = p.rate + (strcmp(p.name, "train") == 0 ? 0.0f : 0.1f * p.rate * sinf(t * 0.9f));
654 total_rate += rates[p.slot];
655 }
656 sections[pi] = s;
657 }
658 static uint8_t head[MESH_H_BYTES];
659 memset(head, 0, sizeof head);
660 mt_put_u32(head, MESH_H_MAGIC, MESH_MAGIC);
661 mt_put_u32(head, MESH_H_VERSION, MESH_VERSION);
662 mt_put_u32(head, MESH_H_PEER, w->system);
663 mt_put_u32(head, MESH_H_EPOCH, w->epoch);
664 mt_put_u32(head, MESH_H_PROG_SEQ, w->prog_seq);
665 mt_put_u32(head, MESH_H_SMS, 48);
666 mt_put_u32(head, MESH_H_SYSTEMS, w->n);
667 mt_put_u32(head, MESH_H_CC, w->cc);
668 mt_put_u32(head, MESH_H_BLOCKS_PER_SM, w->blocks_per_sm);
669 mt_put_u32(head, MESH_H_SMS_TOTAL, 48);
670 uint8_t *h = calloc(1, MESH_SNAP_HOST);
671 if (!h) abort();
672 uint64_t uptime = w->has_clock_ms ? w->clock_ms : elapsed_ms(w);
673 mt_put_u64(h, MESH_S_UPTIME_MS, uptime);
674 mt_put_u32(h, MESH_S_EPOCH, w->epoch);
675 mt_put_u32(h, MESH_S_SYSTEM, w->system);
676 mt_put_u32(h, MESH_S_PEER_STATE, w->system == MESH_PEER_NONE ? 0 : 2);
677 mt_put_u32(h, MESH_S_PROGRAMS, (uint32_t)w->nprogs);
678 mt_put_u32(h, MESH_S_SMS_TOTAL, 48);
679 mt_put_u32(h, MESH_S_GREEN, 1);
680 mt_put_f32(h, MESH_S_TASKS_PER_S, total_rate);
681 uint32_t used = 0;
682 for (size_t i = 0; i < w->nprogs; i++) used += (w->progs[i].view_bytes + (uint32_t)MESH_VIEW_PAGE - 1) / (uint32_t)MESH_VIEW_PAGE;
683 mt_put_u32(h, MESH_S_HEAP_FREE, (uint32_t)MESH_VIEW_PAGES - used);
684 mt_put_u32(h, MESH_S_SMS, 48);
685 mt_put_f32(h, MESH_S_PEER_RTT_US, 38.5f);
686 uint32_t natives = 0;
687 for (size_t i = 0; i < w->nprogs; i++) natives += w->progs[i].has_native_pid && w->progs[i].state == MESH_ST_RUNNING;
688 mt_put_u32(h, MESH_S_WORKERS, natives);
689 mt_put_f32(h, MESH_S_EXEC_CPU, 0.975f * (float)natives);
690 mt_put_u64(h, MESH_S_EXEC_RSS, ((uint64_t)17 << 20) * natives);
691 mt_put_u32(h, MESH_S_CPUS, 20);
692 if (mt_mock_prog_named(w, "train")) {
693 mt_put_u64(h, MESH_S_LOCK_STEP, step);
694 memcpy(h + MESH_S_LOCK_VIEW, lock_view, 32);
695 mt_put_str(h, MESH_S_LOCK_NAME, MESH_NAME_BYTES, "train");
696 }
697 mt_put_u32(h, MESH_S_HTTP_REQUESTS, w->http_served);
698 mt_put_u32(h, MESH_S_BLINK_US, 1840);
699 mt_put_str(h, MESH_S_HOSTNAME, 32, w->host);
700 mt_put_str(h, MESH_S_GPU, 32, w->gpu);
701 mt_put_str(h, MESH_S_PROGRAMS_DIR, 64, "out/programs");
702 mt_put_str(h, MESH_S_LAST_HTTP, 64, w->last_http);
703 char addr[32];
704 snprintf(addr, sizeof addr, "0.0.0.0:%u", (unsigned)MESH_HTTP_PORT);
705 mt_put_str(h, MESH_S_HTTP_ADDR, 64, addr);
706 for (size_t i = 0; i < MESH_PROG_SLOTS; i++) mt_put_f32(h, MESH_S_RATES + 4 * i, rates[i]);
707 mt_put_u64(h, MESH_S_LAST_BLINK_MS, 1200);
708 mt_put_u32(h, MESH_S_SYSTEMS, w->n);
709 mt_put_str(h, MESH_S_ARCH, 16, w->arch);
710 if (w->system != MESH_PEER_NONE) {
711 uint32_t linked = 0;
712 for (uint32_t k = 0; k < w->n; k++) {
713 size_t m = mesh_member(k);
714 bool up = !is_down(w, k);
715 if (up) linked |= 1u << k;
716 mt_put_u32(h, m + MESH_M_STATE, up ? MESH_MS_UP : MESH_MS_ABSENT);
717 bool me = k == w->system;
718 mt_put_u32(h, m + MESH_M_CC, me ? w->cc : 121);
719 mt_put_u32(h, m + MESH_M_SMS, 48);
720 mt_put_u32(h, m + MESH_M_SMS_TOTAL, 48);
721 mt_put_u32(h, m + MESH_M_BLOCKS_PER_SM, me ? w->blocks_per_sm : 24);
722 mt_put_f32(h, m + MESH_M_RTT_US, me ? 0.0f : 38.5f);
723 mt_put_f32(h, m + MESH_M_WAIT_US, me ? 0.0f : w->waits[k]);
724 mt_put_u32(h, m + MESH_M_ROUNDS, me ? 0 : 400);
725 mt_put_u64(h, m + MESH_M_ROUND, step);
726 mt_put_str(h, m + MESH_M_GPU, 32, me ? w->gpu : "NVIDIA GB10 (mock)");
727 mt_put_str(h, m + MESH_M_ARCH, 16, me ? w->arch : "aarch64");
728 char host[64];
729 if (me) snprintf(host, sizeof host, "%s", w->host);
730 else snprintf(host, sizeof host, "mock-%u", k);
731 mt_put_str(h, m + MESH_M_HOST, 32, host);
732 }
733 mt_put_u32(h, MESH_S_LINKED, linked);
734 }
735 net(w, uptime, h);
736 load(w, uptime, h);
737 mt_snapshot snap = mt_snapshot_assemble(head, sections, w->nprogs, h);
738 for (size_t i = 0; i < w->nprogs; i++) mt_section_free(§ions[i]);
739 free(sections);
740 free(h);
741 return snap;
742}
743
744/* ---- commands */
745
746static const char *dir_name(const char *arg, char *buf, size_t cap) {
747 snprintf(buf, cap, "%s", arg);
748 size_t l = strlen(buf);
749 while (l && buf[l - 1] == '/') buf[--l] = 0;
750 const char *suf = "/program.toml";
751 size_t sl = strlen(suf);
752 while (l >= sl && strcmp(buf + l - sl, suf) == 0) {
753 l -= sl;
754 buf[l] = 0;
755 }
756 const char *slash = strrchr(buf, '/');
757 return slash ? slash + 1 : buf;
758}
759
760static char *doom_key(mt_mock_world *w, const char *k) {
761 float dx = w->dir[0], dy = w->dir[1];
762 float a = 0.0f;
763 float vx = 0, vy = 0;
764 bool walk = false;
765 if (strcmp(k, "w") == 0) walk = true, vx = dx, vy = dy;
766 else if (strcmp(k, "s") == 0) walk = true, vx = -dx, vy = -dy;
767 else if (strcmp(k, "q") == 0) walk = true, vx = dy, vy = -dx;
768 else if (strcmp(k, "e") == 0) walk = true, vx = -dy, vy = dx;
769 else if (strcmp(k, "a") == 0) a = -atan2f(MESH_DOOM_TURN_SIN, MESH_DOOM_TURN_COS);
770 else if (strcmp(k, "d") == 0) a = atan2f(MESH_DOOM_TURN_SIN, MESH_DOOM_TURN_COS);
771 else return xfmt("err unknown key %s", k);
772 if (walk) {
773 float nx = w->pos[0] + vx * MESH_DOOM_MOVE, ny = w->pos[1] + vy * MESH_DOOM_MOVE;
774 if (map_at((int32_t)nx, (int32_t)ny) == 0) {
775 w->pos[0] = nx;
776 w->pos[1] = ny;
777 }
778 } else {
779 float s = sinf(a), c = cosf(a);
780 w->dir[0] = dx * c - dy * s;
781 w->dir[1] = dx * s + dy * c;
782 }
783 return xfmt("ok");
784}
785
786static char *command(mt_mock_world *w, const char *cmd, const char *arg) {
787 char name[64];
788 {
789 const char *p = arg;
790 while (*p == ' ') p++;
791 size_t l = 0;
792 while (p[l] && p[l] != ' ' && l < sizeof name - 1) l++;
793 memcpy(name, p, l);
794 name[l] = 0;
795 }
796 char *r = NULL;
797 if (strcmp(cmd, "place") == 0 && strcmp(name, "doom") == 0) {
798 char words[3][32] = {"", "", ""};
799 int nw = sscanf(arg, "%*s %31s %31s %31s", words[0], words[1], words[2]);
800 uint64_t k = 0;
801 if (nw == 1 && strcmp(words[0], "local") == 0) {
802 w->place[0] = MESH_PLACE_LOCAL;
803 w->place[1] = 0;
804 r = xfmt("ok local");
805 } else if (nw == 1 && strcmp(words[0], "split") == 0) {
806 mt_mock_default_assign(w->n, 4, w->assign);
807 w->place[0] = MESH_PLACE_SPLIT;
808 w->place[1] = 0;
809 r = xfmt("ok split");
810 } else if (nw == 2 && strcmp(words[0], "pin") == 0 && mt_parse_u64(words[1], &k) && k < w->n) {
811 w->place[0] = MESH_PLACE_PIN;
812 w->place[1] = (uint32_t)k;
813 r = xfmt("ok pin %s", words[1]);
814 } else {
815 r = xfmt("err usage: place <name> [local | pin <k> | split]");
816 }
817 } else if (strcmp(cmd, "start") == 0 || strcmp(cmd, "stop") == 0) {
818 bool start = strcmp(cmd, "start") == 0;
819 if (strcmp(name, "train") == 0) {
820 if (start) {
821 if (w->has_frozen_step) {
822 uint32_t f = w->frozen_step;
823 w->has_frozen_step = false;
824 w->step0 = mt_now() - mt_from_secs_f32((float)f / 400.0f);
825 }
826 } else if (!w->has_frozen_step) {
827 w->frozen_step = mt_mock_train_step(w);
828 w->has_frozen_step = true;
829 }
830 }
831 mt_mock_prog *p = mt_mock_prog_named(w, name);
832 if (p) {
833 uint32_t d = done(w, p);
834 int64_t t = ticks(w, p);
835 if (start) {
836 if (p->has_frozen) {
837 p->offset = (int64_t)p->frozen - t;
838 p->has_frozen = false;
839 }
840 } else {
841 p->has_frozen = true;
842 p->frozen = d;
843 }
844 p->state = start ? MESH_ST_RUNNING : MESH_ST_LOADED;
845 r = xfmt("ok");
846 } else {
847 r = xfmt("err %s is not loaded", name);
848 }
849 } else if (strcmp(cmd, "load") == 0) {
850 char buf[256];
851 const char *n = dir_name(arg, buf, sizeof buf);
852 bool has_slot = false;
853 uint32_t slot = 0;
854 for (uint32_t s = 0; s < MESH_PROG_SLOTS && !has_slot; s++) {
855 bool used = false;
856 for (size_t i = 0; i < w->nprogs; i++) used |= w->progs[i].slot == s;
857 if (!used) {
858 has_slot = true;
859 slot = s;
860 }
861 }
862 mt_mock_prog p;
863 if (mt_mock_prog_named(w, n)) r = xfmt("err name exists");
864 else if (has_slot && mt_mock_catalog_prog(n, slot, &p)) {
865 w->progs[w->nprogs++] = p;
866 for (size_t i = 1; i < w->nprogs; i++)
867 for (size_t j = i; j > 0 && w->progs[j - 1].slot > w->progs[j].slot; j--) {
868 mt_mock_prog t = w->progs[j];
869 w->progs[j] = w->progs[j - 1];
870 w->progs[j - 1] = t;
871 }
872 w->epoch++;
873 r = xfmt("ok slot %u blink 1840", slot);
874 } else if (!has_slot) r = xfmt("err no slot");
875 else r = xfmt("err manifest: no program.toml in %s", arg);
876 } else if (strcmp(cmd, "upgrade") == 0) {
877 mt_mock_prog *p = mt_mock_prog_named(w, name);
878 if (p) {
879 p->has_retained = true;
880 p->retained = p->hash;
881 p->loads++;
882 char hn[64];
883 snprintf(hn, sizeof hn, "%s%u", p->name, p->loads);
884 p->hash = mesh_fnv1a64((const uint8_t *)hn, strlen(hn));
885 w->epoch++;
886 r = xfmt("ok blink 2100 view kept");
887 } else r = xfmt("err %s is not loaded", name);
888 } else if (strcmp(cmd, "rollback") == 0) {
889 mt_mock_prog *p = mt_mock_prog_named(w, name);
890 if (!p) r = xfmt("err %s is not loaded", name);
891 else if (!p->has_retained) r = xfmt("err %s has no retained image", name);
892 else {
893 uint64_t h = p->retained;
894 p->retained = p->hash;
895 p->hash = h;
896 p->loads++;
897 w->epoch++;
898 r = xfmt("ok blink 900");
899 }
900 } else if (strcmp(cmd, "unload") == 0) {
901 size_t i = 0;
902 while (i < w->nprogs && strcmp(w->progs[i].name, name) != 0) i++;
903 if (i < w->nprogs) {
904 memmove(&w->progs[i], &w->progs[i + 1], (w->nprogs - i - 1) * sizeof *w->progs);
905 w->nprogs--;
906 r = xfmt("ok");
907 } else r = xfmt("err %s is not loaded", name);
908 } else if (strcmp(cmd, "reset") == 0 || strcmp(cmd, "train-reset") == 0) {
909 const char *n = strcmp(cmd, "train-reset") == 0 ? "train" : name;
910 mt_mock_prog *p = mt_mock_prog_named(w, n);
911 if (p) {
912 p->offset = -ticks(w, p);
913 if (p->has_frozen) p->frozen = 0;
914 if (strcmp(n, "train") == 0) {
915 w->step0 = mt_now();
916 if (w->has_frozen_step) w->frozen_step = 0;
917 }
918 r = xfmt("ok");
919 } else r = xfmt("err %s is not loaded", n);
920 } else if (strcmp(cmd, "task") == 0) {
921 uint64_t a[8];
922 size_t na = 0;
923 char copy[256];
924 snprintf(copy, sizeof copy, "%s", arg);
925 char *save = NULL, *tok = strtok_r(copy, " ", &save);
926 for (tok = tok ? strtok_r(NULL, " ", &save) : NULL; tok && na < 8; tok = strtok_r(NULL, " ", &save)) {
927 uint64_t v;
928 if (mt_parse_u64(tok, &v) && v <= UINT32_MAX) a[na++] = v;
929 }
930 if (strcmp(name, "hello") == 0 && na == 2 && a[0] == 0) {
931 uint32_t x = (uint32_t)a[1];
932 w->hello_last[0] = x;
933 w->hello_last[1] = x * 3 + 7;
934 r = xfmt("ok %u", x * 3 + 7);
935 } else if (strcmp(name, "hello") == 0 && na == 2 && a[0] == 1) r = xfmt("ok 0");
936 else if (strcmp(name, "hello") == 0 && na == 2) r = xfmt("ok %u", (unsigned)MESH_R_BAD_OP);
937 else r = xfmt("err usage: task <name> <op> <arg>");
938 } else if (strcmp(cmd, "input") == 0 || strcmp(cmd, "key") == 0) {
939 char prog[64] = "", k[64] = "";
940 if (strcmp(cmd, "key") == 0) {
941 snprintf(prog, sizeof prog, "doom");
942 snprintf(k, sizeof k, "%s", arg);
943 } else {
944 const char *sp = strchr(arg, ' ');
945 if (sp) {
946 snprintf(prog, sizeof prog, "%.*s", (int)(sp - arg), arg);
947 snprintf(k, sizeof k, "%s", sp + 1);
948 } else {
949 snprintf(prog, sizeof prog, "%s", arg);
950 }
951 }
952 if (strcmp(prog, "doom") != 0 || !mt_mock_prog_named(w, prog)) return xfmt("err %s has no input queue", prog);
953 w->keys++;
954 r = doom_key(w, k);
955 } else {
956 return xfmt("err unknown command %s", cmd);
957 }
958 if (strncmp(r, "ok", 2) == 0 && strcmp(cmd, "task") != 0 && strcmp(cmd, "input") != 0 && strcmp(cmd, "key") != 0) {
959 w->prog_seq++;
960 char *s = xfmt("%s %s: %s", cmd, arg, r);
961 say(w, s);
962 free(s);
963 } else if (strncmp(r, "err", 3) == 0) {
964 uint64_t ms = elapsed_ms(w);
965 log_push(w, xfmt("[%6llu.%03llu] ERROR %s %s: %s", (unsigned long long)(ms / 1000), (unsigned long long)(ms % 1000), cmd, arg, r));
966 }
967 return r;
968}
969
970static uint8_t *http(mt_mock_world *w, const char *path, size_t *len) {
971 w->http_served++;
972 snprintf(w->last_http, sizeof w->last_http, "GET %s HTTP/1.0", path);
973 size_t cap = 8192 + strlen(path) * 2;
974 char *body = xmalloc(cap);
975 size_t n = (size_t)snprintf(body, cap, "mock http for %s on %s\n", path, w->host);
976 for (int i = 0; i < 60; i++) n += (size_t)snprintf(body + n, cap - n, "line %02d: the GPU would compute this\n", i);
977 char *resp = xfmt("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nContent-Length: %zu\r\n\r\n%s", n, body);
978 free(body);
979 *len = strlen(resp);
980 return (uint8_t *)resp;
981}
982
983void mt_mock_request(mt_mock_world *w, const char *line, uint8_t **bytes, size_t *len, char **text) {
984 *bytes = NULL;
985 *text = NULL;
986 *len = 0;
987 /* trim */
988 const char *s = line;
989 size_t l = strlen(s);
990 while (l && (*s == ' ' || (*s >= '\t' && *s <= '\r'))) s++, l--;
991 while (l && (s[l - 1] == ' ' || (s[l - 1] >= '\t' && s[l - 1] <= '\r'))) l--;
992 char *req = xmalloc(l + 1);
993 memcpy(req, s, l);
994 req[l] = 0;
995 char *sp = strchr(req, ' ');
996 const char *arg = "";
997 if (sp) {
998 *sp = 0;
999 arg = sp + 1;
1000 }
1001 const char *cmd = req;
1002 if (strcmp(cmd, "snap") == 0) {
1003 mt_snapshot snap = mt_mock_synth(w);
1004 *bytes = snap.bytes;
1005 *len = snap.len;
1006 snap.bytes = NULL;
1007 for (size_t i = 0; i < snap.nsections; i++) free(snap.sections[i].bytes);
1008 free(snap.sections);
1009 } else if (strcmp(cmd, "trace") == 0) {
1010 *bytes = mt_mock_trace(w, len);
1011 } else if (strcmp(cmd, "programs") == 0) {
1012 size_t cap = 8192, n = 0;
1013 char *o = xmalloc(cap);
1014 o[0] = 0;
1015 for (size_t i = 0; i < w->nprogs; i++) {
1016 const mt_mock_prog *p = &w->progs[i];
1017 char rate[32];
1018 snprintf(rate, sizeof rate, "%.1f", (double)(p->state == MESH_ST_RUNNING ? p->rate : 0.0f));
1019 n += (size_t)snprintf(o + n, cap - n, "%u %s %s %u %u %s %s %016llx %u 0x%zx+0x%x\n", p->slot, p->name, MESH_STATES[p->state],
1020 p->nodes, 0u, MESH_POLICIES[p->policy], rate, (unsigned long long)p->hash, p->regs,
1021 (size_t)MESH_VIEW_HEAP + 0x10000 * (size_t)p->slot, p->view_bytes);
1022 n += (size_t)snprintf(o + n, cap - n, "fields %s ", p->name);
1023 for (size_t f = 0; f < p->nfields; f++) n += (size_t)snprintf(o + n, cap - n, "%s%s", f ? " " : "", p->fields[f].name);
1024 n += (size_t)snprintf(o + n, cap - n, "\ncounters %s %s %s %s %s\n", p->name, p->counters[0], p->counters[1], p->counters[2], p->counters[3]);
1025 }
1026 *bytes = (uint8_t *)o;
1027 *len = n;
1028 } else if (strcmp(cmd, "catalog") == 0) {
1029 char *o = xmalloc(256);
1030 size_t n = 0;
1031 for (int i = 0; i < MT_MOCK_CATALOG_LEN; i++) n += (size_t)snprintf(o + n, 256 - n, "%s\n", MT_MOCK_CATALOG[i]);
1032 *bytes = (uint8_t *)o;
1033 *len = n;
1034 } else if (strcmp(cmd, "log") == 0) {
1035 uint64_t n = 50;
1036 if (!mt_parse_u64(arg, &n)) n = 50;
1037 size_t from = w->nlog > n ? w->nlog - n : 0, total = 0;
1038 for (size_t i = from; i < w->nlog; i++) total += strlen(w->log[i]) + 1;
1039 char *o = xmalloc(total + 1);
1040 size_t k = 0;
1041 for (size_t i = from; i < w->nlog; i++) k += (size_t)sprintf(o + k, "%s\n", w->log[i]);
1042 *bytes = (uint8_t *)o;
1043 *len = k;
1044 } else if (strcmp(cmd, "peek") == 0) {
1045 char name[64];
1046 uint64_t off, plen;
1047 char offs[32], lens[32];
1048 if (sscanf(arg, "%63s %31s %31s", name, offs, lens) != 3 || !mt_parse_u64(offs, &off) || !mt_parse_u64(lens, &plen)) {
1049 *text = xfmt("err usage: peek <name> <off> <len>");
1050 } else {
1051 mt_snapshot snap = mt_mock_synth(w);
1052 const mt_section *p = mt_snapshot_by_name(&snap, name);
1053 if (!p) *text = xfmt("err %s is not loaded", name);
1054 else if (off + plen > mt_section_view_len(p)) *text = xfmt("err peek out of the snap bytes");
1055 else {
1056 *bytes = xmalloc((size_t)plen);
1057 memcpy(*bytes, mt_section_view(p) + off, (size_t)plen);
1058 *len = (size_t)plen;
1059 }
1060 mt_snapshot_free(&snap);
1061 }
1062 } else if (strcmp(cmd, "http") == 0) {
1063 if (running(w, "http")) *bytes = http(w, arg, len);
1064 else *text = xfmt("err http is not running");
1065 } else {
1066 *text = command(w, cmd, arg);
1067 }
1068 free(req);
1069}