==> src/snap.c <==1/* crates/mesh-top/src/snap.rs, ported. */
2#include "mesh/top_snap.h"
3
4#include "tui/utf8.h"
5
6#include <math.h>
7#include <stdarg.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12/* ---- byte helpers */
13
14uint32_t mt_get_u32(const uint8_t *b, size_t len, size_t at) {
15 if (at + 4 > len || at + 4 < at) return 0;
16 return (uint32_t)b[at] | (uint32_t)b[at + 1] << 8 | (uint32_t)b[at + 2] << 16 | (uint32_t)b[at + 3] << 24;
17}
18
19uint64_t mt_get_u64(const uint8_t *b, size_t len, size_t at) {
20 if (at + 8 > len || at + 8 < at) return 0;
21 return (uint64_t)mt_get_u32(b, len, at) | (uint64_t)mt_get_u32(b, len, at + 4) << 32;
22}
23
24float mt_get_f32(const uint8_t *b, size_t len, size_t at) {
25 uint32_t u = mt_get_u32(b, len, at);
26 float f;
27 memcpy(&f, &u, sizeof f);
28 return f;
29}
30
31void mt_get_str(const uint8_t *b, size_t len, size_t at, size_t n, char *out) {
32 out[0] = 0;
33 if (at + n > len || at + n < at) return;
34 size_t end = 0;
35 while (end < n && b[at + end]) end++;
36 size_t m;
37 char *s = tui_utf8_lossy(b + at, end, &m);
38 memcpy(out, s, m + 1);
39 free(s);
40}
41
42void mt_put_u32(uint8_t *b, size_t at, uint32_t v) {
43 for (int i = 0; i < 4; i++) b[at + i] = (uint8_t)(v >> (8 * i));
44}
45
46void mt_put_u64(uint8_t *b, size_t at, uint64_t v) {
47 for (int i = 0; i < 8; i++) b[at + i] = (uint8_t)(v >> (8 * i));
48}
49
50void mt_put_f32(uint8_t *b, size_t at, float v) {
51 uint32_t u;
52 memcpy(&u, &v, sizeof u);
53 mt_put_u32(b, at, u);
54}
55
56void mt_put_str(uint8_t *b, size_t at, size_t n, const char *s) {
57 memset(b + at, 0, n);
58 size_t k = strlen(s);
59 if (k > n - 1) k = n - 1;
60 memcpy(b + at, s, k);
61}
62
63/* ---- sections */
64
65mt_section mt_section_new(uint32_t slot, uint32_t nodes, uint32_t view) {
66 mt_section s;
67 s.len = mesh_section_bytes(nodes, view);
68 s.bytes = calloc(1, s.len);
69 if (!s.bytes) abort();
70 mt_put_u32(s.bytes, MESH_SC_SLOT, slot);
71 mt_put_u32(s.bytes, MESH_SC_BYTES, (uint32_t)s.len);
72 mt_put_u32(s.bytes, MESH_SC_NODES, nodes);
73 mt_put_u32(s.bytes, MESH_SC_VIEW, view);
74 return s;
75}
76
77void mt_section_free(mt_section *s) {
78 free(s->bytes);
79 s->bytes = NULL;
80 s->len = 0;
81}
82
83mt_section mt_section_clone(const mt_section *s) {
84 mt_section c = {malloc(s->len ? s->len : 1), s->len};
85 if (!c.bytes) abort();
86 memcpy(c.bytes, s->bytes, s->len);
87 return c;
88}
89
90uint32_t mt_section_slot(const mt_section *s) { return mt_get_u32(s->bytes, s->len, MESH_SC_SLOT); }
91size_t mt_section_nodes(const mt_section *s) { return mt_get_u32(s->bytes, s->len, MESH_SC_NODES); }
92uint8_t *mt_section_entry(const mt_section *s) { return s->bytes + MESH_SC_ENTRY; }
93uint8_t *mt_section_ctl(const mt_section *s) { return s->bytes + MESH_SC_CTL; }
94uint8_t *mt_section_ext(const mt_section *s) { return s->bytes + MESH_SC_EXT; }
95uint8_t *mt_section_node_rec(const mt_section *s, size_t n) { return s->bytes + MESH_SC_NODE_RECS + MESH_NODE_STRIDE * n; }
96uint8_t *mt_section_view(const mt_section *s) { return s->bytes + MESH_SC_NODE_RECS + MESH_NODE_STRIDE * mt_section_nodes(s); }
97size_t mt_section_view_len(const mt_section *s) { return mt_get_u32(s->bytes, s->len, MESH_SC_VIEW); }
98
99void mt_section_name(const mt_section *s, char *out) {
100 mt_get_str(mt_section_entry(s), MESH_PT_BYTES, MESH_PT_NAME, MESH_NAME_BYTES, out);
101}
102
103bool mt_section_name_is(const mt_section *s, const char *name) {
104 char n[3 * MESH_NAME_BYTES + 1];
105 mt_section_name(s, n);
106 return strcmp(n, name) == 0;
107}
108
109uint32_t mt_section_pt(const mt_section *s, size_t off) { return mt_get_u32(mt_section_entry(s), MESH_PT_BYTES, off); }
110uint32_t mt_section_state(const mt_section *s) { return mt_section_pt(s, MESH_PT_STATE); }
111const char *mt_section_state_name(const mt_section *s) {
112 uint32_t st = mt_section_state(s);
113 return st < MESH_STATES_LEN ? MESH_STATES[st] : "?";
114}
115bool mt_section_running(const mt_section *s) { return mt_section_state(s) == MESH_ST_RUNNING; }
116uint64_t mt_section_ptx_hash(const mt_section *s) { return mt_get_u64(mt_section_entry(s), MESH_PT_BYTES, MESH_PT_PTX_HASH); }
117uint32_t mt_section_p(const mt_section *s, size_t off) { return mt_get_u32(mt_section_ctl(s), MESH_SC_CTL_BYTES, off); }
118uint64_t mt_section_p64(const mt_section *s, size_t off) { return mt_get_u64(mt_section_ctl(s), MESH_SC_CTL_BYTES, off); }
119bool mt_section_native(const mt_section *s) {
120 return mt_get_u32(mt_section_ext(s), MESH_PX_BYTES, MESH_PX_EXEC) == MESH_BACKEND_NATIVE;
121}
122uint32_t mt_section_counter(const mt_section *s, size_t i) { return mt_section_p(s, MESH_P_COUNTERS + 4 * i); }
123void mt_section_counter_name(const mt_section *s, size_t i, char *out) {
124 mt_get_str(mt_section_ext(s), MESH_PX_BYTES, MESH_PX_COUNTER_NAMES + MESH_NAME_BYTES * i, MESH_NAME_BYTES, out);
125}
126
127size_t mt_section_fields(const mt_section *s, mt_field *out) {
128 const uint8_t *x = mt_section_ext(s);
129 size_t n = mt_get_u32(x, MESH_PX_BYTES, MESH_PX_FIELD_COUNT);
130 if (n > MESH_MAX_FIELDS) n = MESH_MAX_FIELDS;
131 for (size_t i = 0; i < n; i++) {
132 size_t at = MESH_PX_FIELDS + MESH_FIELD_BYTES * i;
133 mt_get_str(x, MESH_PX_BYTES, at + MESH_FD_NAME, MESH_FD_NAME_MAX + 1, out[i].name);
134 out[i].ty = mt_get_u32(x, MESH_PX_BYTES, at + MESH_FD_TYPE);
135 out[i].off = mt_get_u32(x, MESH_PX_BYTES, at + MESH_FD_OFF);
136 }
137 return n;
138}
139
140void mt_section_field_value(const mt_section *s, const mt_field *f, char *out, size_t cap) {
141 const uint8_t *v = mt_section_view(s);
142 size_t vl = mt_section_view_len(s);
143 size_t size = f->ty == MESH_FT_U64 ? 8 : f->ty == MESH_FT_U8 ? 1 : 4;
144 if (f->off + size > vl) {
145 snprintf(out, cap, "-");
146 return;
147 }
148 switch (f->ty) {
149 case MESH_FT_U32: snprintf(out, cap, "%u", mt_get_u32(v, vl, f->off)); break;
150 case MESH_FT_I32: snprintf(out, cap, "%d", (int32_t)mt_get_u32(v, vl, f->off)); break;
151 case MESH_FT_F32: {
152 float x = mt_get_f32(v, vl, f->off);
153 if (isnan(x)) snprintf(out, cap, "NaN");
154 else if (isinf(x)) snprintf(out, cap, x < 0 ? "-inf" : "inf");
155 else snprintf(out, cap, "%.6f", (double)x);
156 break;
157 }
158 case MESH_FT_U64: snprintf(out, cap, "%llu", (unsigned long long)mt_get_u64(v, vl, f->off)); break;
159 case MESH_FT_U8: snprintf(out, cap, "%u", v[f->off]); break;
160 default: snprintf(out, cap, "?type%u", f->ty); break;
161 }
162}
163
164mt_node mt_section_node(const mt_section *s, size_t n) {
165 mt_node r = {0, 0, 0, 0, 0};
166 if (n >= mt_section_nodes(s)) return r;
167 size_t at = MESH_SC_NODE_RECS + MESH_NODE_STRIDE * n;
168 r.sm = mt_get_u32(s->bytes, s->len, at + MESH_N_SM);
169 r.heartbeat = mt_get_u32(s->bytes, s->len, at + MESH_N_HEARTBEAT);
170 r.tasks = mt_get_u32(s->bytes, s->len, at + MESH_N_TASKS);
171 r.last_op = mt_get_u32(s->bytes, s->len, at + MESH_N_LAST_OP);
172 r.busy_ns = mt_get_u64(s->bytes, s->len, at + MESH_N_BUSY_NS);
173 return r;
174}
175
176uint32_t mt_section_view_u32(const mt_section *s, size_t off) { return mt_get_u32(mt_section_view(s), mt_section_view_len(s), off); }
177float mt_section_view_f32(const mt_section *s, size_t off) { return mt_get_f32(mt_section_view(s), mt_section_view_len(s), off); }
178
179/* ---- snapshots */
180
181static char *fmt(const char *f, ...)
182#if defined(__GNUC__) || defined(__clang__)
183 __attribute__((format(printf, 1, 2)))
184#endif
185 ;
186static char *fmt(const char *f, ...) {
187 va_list ap;
188 va_start(ap, f);
189 char tmp[256];
190 vsnprintf(tmp, sizeof tmp, f, ap);
191 va_end(ap);
192 char *s = malloc(strlen(tmp) + 1);
193 if (!s) abort();
194 strcpy(s, tmp);
195 return s;
196}
197
198bool mt_snapshot_parse(const uint8_t *bytes, size_t len, mt_snapshot *out, char **err) {
199 size_t min = MESH_SN_HEAD_BYTES + MESH_H_BYTES + MESH_SNAP_HOST;
200 *out = (mt_snapshot){NULL, 0, NULL, 0};
201 if (len < min) {
202 *err = fmt("snapshot is %zu bytes, v%u needs at least %zu", len, (unsigned)MESH_SNAP_VERSION, min);
203 return false;
204 }
205 size_t total = mt_get_u32(bytes, len, MESH_SN_TOTAL), count = mt_get_u32(bytes, len, MESH_SN_COUNT);
206 uint32_t version = mt_get_u32(bytes, len, MESH_SN_VERSION);
207 size_t host_at = mt_get_u32(bytes, len, MESH_SN_HOST_AT);
208 if (version != MESH_SNAP_VERSION) {
209 *err = fmt("snapshot version %u, this viewer speaks %u", version, (unsigned)MESH_SNAP_VERSION);
210 return false;
211 }
212 if (total != len || host_at + MESH_SNAP_HOST != total) {
213 *err = fmt("snapshot framing: total %zu, host at %zu, received %zu", total, host_at, len);
214 return false;
215 }
216 if (count > MESH_PROG_SLOTS) {
217 *err = fmt("snapshot has %zu sections, at most %u", count, (unsigned)MESH_PROG_SLOTS);
218 return false;
219 }
220 mt_section *secs = calloc(count ? count : 1, sizeof *secs);
221 if (!secs) abort();
222 size_t at = MESH_SN_HEAD_BYTES + MESH_H_BYTES, n = 0;
223 for (size_t i = 0; i < count; i++) {
224 if (at + MESH_SC_HEAD_BYTES > host_at) {
225 *err = fmt("section %zu starts past the host block", i);
226 goto fail;
227 }
228 uint32_t slot = mt_get_u32(bytes, len, at + MESH_SC_SLOT);
229 size_t sl = mt_get_u32(bytes, len, at + MESH_SC_BYTES);
230 uint32_t nodes = mt_get_u32(bytes, len, at + MESH_SC_NODES), view = mt_get_u32(bytes, len, at + MESH_SC_VIEW);
231 if (slot >= MESH_PROG_SLOTS || (n > 0 && mt_section_slot(&secs[n - 1]) >= slot)) {
232 *err = fmt("section %zu: slot %u out of range or out of order", i, slot);
233 goto fail;
234 }
235 if (nodes > MESH_PROG_MAX_NODES || view > MESH_VIEW_HEAP_END - MESH_VIEW_HEAP) {
236 *err = fmt("section %zu: %u nodes, %u view bytes", i, nodes, view);
237 goto fail;
238 }
239 if (sl != mesh_section_bytes(nodes, view) || at + sl > host_at) {
240 *err = fmt("section %zu: %zu bytes, the layout says %zu", i, sl, mesh_section_bytes(nodes, view));
241 goto fail;
242 }
243 secs[n].bytes = malloc(sl);
244 if (!secs[n].bytes) abort();
245 memcpy(secs[n].bytes, bytes + at, sl);
246 secs[n].len = sl;
247 n++;
248 at += sl;
249 }
250 if (at != host_at) {
251 *err = fmt("%zu stray bytes before the host block", host_at - at);
252 goto fail;
253 }
254 out->bytes = malloc(len);
255 if (!out->bytes) abort();
256 memcpy(out->bytes, bytes, len);
257 out->len = len;
258 out->sections = secs;
259 out->nsections = n;
260 return true;
261fail:
262 for (size_t k = 0; k < n; k++) free(secs[k].bytes);
263 free(secs);
264 return false;
265}
266
267static int by_slot(const void *a, const void *b) {
268 uint32_t x = mt_section_slot(a), y = mt_section_slot(b);
269 return x < y ? -1 : x > y;
270}
271
272mt_snapshot mt_snapshot_assemble(const uint8_t *head, const mt_section *sections, size_t n, const uint8_t *host) {
273 mt_section *sorted = malloc((n ? n : 1) * sizeof *sorted);
274 if (!sorted) abort();
275 if (n) memcpy(sorted, sections, n * sizeof *sorted);
276 /* A stable sort by slot, as Rust's sort_by_key. */
277 for (size_t i = 1; i < n; i++)
278 for (size_t j = i; j > 0 && by_slot(&sorted[j - 1], &sorted[j]) > 0; j--) {
279 mt_section t = sorted[j];
280 sorted[j] = sorted[j - 1];
281 sorted[j - 1] = t;
282 }
283 size_t len = MESH_SN_HEAD_BYTES + MESH_H_BYTES + MESH_SNAP_HOST;
284 for (size_t i = 0; i < n; i++) len += sorted[i].len;
285 uint8_t *b = calloc(1, len);
286 if (!b) abort();
287 memcpy(b + MESH_SN_HEAD_BYTES, head, MESH_H_BYTES);
288 size_t at = MESH_SN_HEAD_BYTES + MESH_H_BYTES;
289 for (size_t i = 0; i < n; i++) {
290 memcpy(b + at, sorted[i].bytes, sorted[i].len);
291 at += sorted[i].len;
292 }
293 size_t host_at = at;
294 memcpy(b + at, host, MESH_SNAP_HOST);
295 mt_put_u32(b, MESH_SN_TOTAL, (uint32_t)len);
296 mt_put_u32(b, MESH_SN_COUNT, (uint32_t)n);
297 mt_put_u32(b, MESH_SN_VERSION, MESH_SNAP_VERSION);
298 mt_put_u32(b, MESH_SN_HOST_AT, (uint32_t)host_at);
299 free(sorted);
300 mt_snapshot s;
301 char *err = NULL;
302 if (!mt_snapshot_parse(b, len, &s, &err)) {
303 fprintf(stderr, "assembled snapshot does not parse: %s\n", err);
304 abort();
305 }
306 free(b);
307 return s;
308}
309
310mt_snapshot mt_snapshot_clone(const mt_snapshot *s) {
311 mt_snapshot c;
312 char *err = NULL;
313 if (!mt_snapshot_parse(s->bytes, s->len, &c, &err)) abort();
314 return c;
315}
316
317void mt_snapshot_free(mt_snapshot *s) {
318 for (size_t i = 0; i < s->nsections; i++) free(s->sections[i].bytes);
319 free(s->sections);
320 free(s->bytes);
321 *s = (mt_snapshot){NULL, 0, NULL, 0};
322}
323
324const mt_section *mt_snapshot_by_name(const mt_snapshot *s, const char *name) {
325 for (size_t i = 0; i < s->nsections; i++)
326 if (mt_section_name_is(&s->sections[i], name)) return &s->sections[i];
327 return NULL;
328}
329
330const mt_section *mt_snapshot_by_slot(const mt_snapshot *s, uint32_t slot) {
331 for (size_t i = 0; i < s->nsections; i++)
332 if (mt_section_slot(&s->sections[i]) == slot) return &s->sections[i];
333 return NULL;
334}
335
336uint32_t mt_snapshot_nodes_online(const mt_snapshot *s) {
337 uint32_t n = 0;
338 for (size_t i = 0; i < s->nsections; i++) n += mt_section_p(&s->sections[i], MESH_P_ONLINE);
339 return n;
340}
341
342uint32_t mt_snapshot_head(const mt_snapshot *s, size_t off) { return mt_get_u32(s->bytes, s->len, MESH_SN_HEAD_BYTES + off); }
343const uint8_t *mt_snapshot_host(const mt_snapshot *s) { return s->bytes + s->len - MESH_SNAP_HOST; }
344uint32_t mt_snapshot_host_u32(const mt_snapshot *s, size_t off) { return mt_get_u32(mt_snapshot_host(s), MESH_SNAP_HOST, off); }
345uint64_t mt_snapshot_host_u64(const mt_snapshot *s, size_t off) { return mt_get_u64(mt_snapshot_host(s), MESH_SNAP_HOST, off); }
346float mt_snapshot_host_f32(const mt_snapshot *s, size_t off) { return mt_get_f32(mt_snapshot_host(s), MESH_SNAP_HOST, off); }
347void mt_snapshot_host_str(const mt_snapshot *s, size_t off, size_t n, char *out) {
348 mt_get_str(mt_snapshot_host(s), MESH_SNAP_HOST, off, n, out);
349}
350void mt_snapshot_hostname(const mt_snapshot *s, char out[97]) { mt_snapshot_host_str(s, MESH_S_HOSTNAME, 32, out); }
351void mt_snapshot_gpu(const mt_snapshot *s, char out[97]) { mt_snapshot_host_str(s, MESH_S_GPU, 32, out); }
352
353float mt_snapshot_rate(const mt_snapshot *s, uint32_t slot) {
354 float r = mt_snapshot_host_f32(s, MESH_S_RATES + 4 * (size_t)slot);
355 return isfinite(r) && r > 0.0f ? r : 0.0f;
356}
357
358bool mt_snapshot_system(const mt_snapshot *s, uint32_t *k) {
359 uint32_t v = mt_snapshot_host_u32(s, MESH_S_SYSTEM);
360 if (v == MESH_PEER_NONE) return false;
361 if (k) *k = v;
362 return true;
363}
364
365uint32_t mt_snapshot_systems(const mt_snapshot *s) {
366 uint32_t n = mt_snapshot_host_u32(s, MESH_S_SYSTEMS);
367 return n < MESH_MAX_SYSTEMS ? n : MESH_MAX_SYSTEMS;
368}
369
370uint64_t mt_snapshot_uptime_ms(const mt_snapshot *s) { return mt_snapshot_host_u64(s, MESH_S_UPTIME_MS); }
371
372mt_member mt_snapshot_member(const mt_snapshot *s, uint32_t k) {
373 const uint8_t *h = mt_snapshot_host(s) + mesh_member(k);
374 size_t n = MESH_MEMBER_BYTES;
375 mt_member m;
376 m.system = k;
377 m.state = mt_get_u32(h, n, MESH_M_STATE);
378 m.cc = mt_get_u32(h, n, MESH_M_CC);
379 m.sms = mt_get_u32(h, n, MESH_M_SMS);
380 m.sms_total = mt_get_u32(h, n, MESH_M_SMS_TOTAL);
381 m.blocks_per_sm = mt_get_u32(h, n, MESH_M_BLOCKS_PER_SM);
382 m.rtt_us = mt_get_f32(h, n, MESH_M_RTT_US);
383 m.wait_us = mt_get_f32(h, n, MESH_M_WAIT_US);
384 m.rounds = mt_get_u32(h, n, MESH_M_ROUNDS);
385 m.round = mt_get_u64(h, n, MESH_M_ROUND);
386 mt_get_str(h, n, MESH_M_GPU, 32, m.gpu);
387 mt_get_str(h, n, MESH_M_ARCH, 16, m.arch);
388 mt_get_str(h, n, MESH_M_HOST, 32, m.host);
389 m.backend = mt_get_u32(h, n, MESH_M_BACKEND);
390 return m;
391}
392
393size_t mt_snapshot_members(const mt_snapshot *s, mt_member *out) {
394 if (!mt_snapshot_system(s, NULL)) return 0;
395 uint32_t n = mt_snapshot_systems(s);
396 for (uint32_t k = 0; k < n; k++) out[k] = mt_snapshot_member(s, k);
397 return n;
398}
399
400bool mt_snapshot_fleet(const mt_snapshot *s, mt_fleet *f) {
401 if (!mt_snapshot_system(s, NULL)) return false;
402 const uint8_t *h = mt_snapshot_host(s);
403 size_t hl = MESH_SNAP_HOST;
404 memset(f, 0, sizeof *f);
405 uint32_t leader = mt_get_u32(h, hl, MESH_S_LEADER);
406 f->has_leader = leader != MESH_PEER_NONE;
407 f->leader = leader;
408 uint32_t n = mt_snapshot_systems(s);
409 f->nmembers = n;
410 for (uint32_t k = 0; k < n; k++) {
411 size_t at = mesh_fmember(k);
412 f->members[k].status = mt_get_u32(h, hl, at + MESH_FM_STATUS);
413 f->members[k].group = mt_get_u32(h, hl, at + MESH_FM_GROUP);
414 f->members[k].applied = mt_get_u64(h, hl, at + MESH_FM_APPLIED);
415 f->members[k].heard_ms = mt_get_u64(h, hl, at + MESH_FM_HEARD_MS);
416 mt_get_str(h, hl, at + MESH_FM_REASON, 40, f->members[k].reason);
417 }
418 uint32_t gstate = mt_get_u32(h, hl, MESH_SG_STATE);
419 if (gstate != 0) {
420 f->has_group = true;
421 mt_fleet_group *g = &f->group;
422 mt_get_str(h, hl, MESH_SG_NAME, MESH_NAME_BYTES, g->name);
423 g->epoch = mt_get_u32(h, hl, MESH_SG_EPOCH);
424 g->cycle = mt_get_u32(h, hl, MESH_SG_CYCLE);
425 g->mask = mt_get_u32(h, hl, MESH_SG_MASK);
426 memcpy(g->order, h + MESH_SG_ORDER, MESH_MAX_SYSTEMS);
427 g->running = gstate == 1;
428 g->local = mt_get_u32(h, hl, MESH_SG_LOCAL);
429 g->rank = mt_get_u32(h, hl, MESH_SG_RANK);
430 g->reform = mt_get_u32(h, hl, MESH_SG_ON_LOSS) == 1;
431 g->auto_ms = mt_get_u32(h, hl, MESH_SG_AUTO_MS);
432 }
433 f->role = mt_get_u32(h, hl, MESH_S_ROLE);
434 f->term = mt_get_u64(h, hl, MESH_S_TERM);
435 f->version = mt_get_u64(h, hl, MESH_S_STATE_VER);
436 f->applied = mt_get_u64(h, hl, MESH_S_APPLIED_VER);
437 f->up = mt_get_u32(h, hl, MESH_S_UP_MASK);
438 f->suspect = mt_get_u32(h, hl, MESH_S_SUSPECT_MASK);
439 f->quorum = mt_get_u32(h, hl, MESH_S_QUORUM);
440 f->flags = mt_get_u32(h, hl, MESH_S_FLEET_FLAGS);
441 f->dead_ms = mt_get_u32(h, hl, MESH_S_DEAD_MS);
442 return true;
443}
444
445mt_jobs mt_snapshot_jobs(const mt_snapshot *s) {
446 const uint8_t *h = mt_snapshot_host(s);
447 size_t hl = MESH_SNAP_HOST;
448 return (mt_jobs){mt_get_u32(h, hl, MESH_S_JOBS_ACTIVE), mt_get_u32(h, hl, MESH_S_JOBS_TOTAL),
449 mt_get_u64(h, hl, MESH_S_JOB_DONE), mt_get_u64(h, hl, MESH_S_JOB_FAILED),
450 mt_get_u32(h, hl, MESH_S_JOB_LEASED), mt_get_f32(h, hl, MESH_S_JOB_RATE),
451 mt_get_u64(h, hl, MESH_S_JOB_DUPES), mt_get_u32(h, hl, MESH_S_JOB_FLAGS),
452 mt_get_u32(h, hl, MESH_S_JOB_HELD)};
453}
454
455static mt_opt_f32 opt(bool on, float v) { return (mt_opt_f32){on, on ? v : 0.0f}; }
456
457bool mt_snapshot_load(const mt_snapshot *s, mt_load *l) {
458 uint64_t at = mt_snapshot_host_u64(s, MESH_S_LOAD_AT_MS);
459 if (at == 0) return false;
460 memset(l, 0, sizeof *l);
461 const uint8_t *h = mt_snapshot_host(s);
462 uint32_t flags = mt_snapshot_host_u32(s, MESH_S_LOAD_FLAGS);
463#define F(o) mt_snapshot_host_f32(s, (o))
464#define U(o) mt_snapshot_host_u32(s, (o))
465 size_t n = U(MESH_S_CPUS);
466 if (n > MESH_MAX_CPUS) n = MESH_MAX_CPUS;
467 uint64_t up = mt_snapshot_uptime_ms(s);
468 l->at_ms = at;
469 l->age_ms = up > at ? up - at : 0;
470 l->samples = U(MESH_S_LOAD_SAMPLES);
471 l->flags = flags;
472 l->share = opt(flags & MESH_LF_GPU, F(MESH_S_GPU_SHARE));
473 l->share10 = opt(flags & MESH_LF_GPU, F(MESH_S_GPU_SHARE10));
474 l->slice_us = F(MESH_S_GPU_SLICE_US);
475 l->gap_us = F(MESH_S_GPU_GAP_US);
476 l->max_gap_us = F(MESH_S_GPU_MAX_GAP_US);
477 l->gaps_s = F(MESH_S_GPU_GAPS_S);
478 l->observed = F(MESH_S_GPU_OBS);
479 l->util_ext = opt(flags & MESH_LF_GPU_EXT, F(MESH_S_GPU_UTIL_EXT));
480 l->queue_us = F(MESH_S_GPU_QUEUE_US);
481 l->inflate = opt(flags & MESH_LF_GPU, F(MESH_S_GPU_INFLATE));
482 l->cpu = F(MESH_S_CPU_UTIL);
483 l->cpu_big = F(MESH_S_CPU_BIG);
484 l->cpu_little = F(MESH_S_CPU_LITTLE);
485 l->iowait = F(MESH_S_CPU_IOWAIT);
486 l->loadavg[0] = F(MESH_S_LOADAVG);
487 l->loadavg[1] = F(MESH_S_LOADAVG + 4);
488 l->loadavg[2] = F(MESH_S_LOADAVG + 8);
489 l->cpus = U(MESH_S_CPUS);
490 l->cpus_big = U(MESH_S_CPUS_BIG);
491 l->cpus_little = U(MESH_S_CPUS_LITTLE);
492 l->has_psi = flags & MESH_LF_PSI;
493 if (l->has_psi) {
494 l->psi[0] = F(MESH_S_PSI_CPU);
495 l->psi[1] = F(MESH_S_PSI_MEM);
496 l->psi[2] = F(MESH_S_PSI_MEM_FULL);
497 l->psi[3] = F(MESH_S_PSI_IO);
498 l->psi[4] = F(MESH_S_PSI_IO_FULL);
499 }
500 l->mem_total_mb = U(MESH_S_MEM_TOTAL_MB);
501 l->mem_avail_mb = U(MESH_S_MEM_AVAIL_MB);
502 l->mem_free = F(MESH_S_MEM_FREE);
503 l->swap_total_mb = U(MESH_S_SWAP_TOTAL_MB);
504 l->swap_used_mb = U(MESH_S_SWAP_USED_MB);
505 l->swap_out_s = F(MESH_S_SWAP_OUT_S);
506 l->compressed_mb = U(MESH_S_MEM_COMPRESSED_MB);
507 l->has_mem_level = flags & MESH_LF_MEM_LEVEL;
508 l->mem_level = l->has_mem_level ? U(MESH_S_MEM_LEVEL) : 0;
509 l->meshd_cpu = F(MESH_S_MESHD_CPU);
510 l->meshd_rss_mb = F(MESH_S_MESHD_RSS_MB);
511 l->meshd_threads = U(MESH_S_MESHD_THREADS);
512 l->disk_free_mb = U(MESH_S_DISK_FREE_MB);
513 l->disk_total_mb = U(MESH_S_DISK_TOTAL_MB);
514 l->has_thermal = flags & MESH_LF_THERMAL;
515 l->thermal = l->has_thermal ? U(MESH_S_THERMAL) : 0;
516 l->temp_c = opt(flags & MESH_LF_TEMP, F(MESH_S_TEMP_C));
517 l->ncores = n;
518 for (size_t i = 0; i < n; i++) {
519 l->cores[i][0] = h[MESH_S_CORE_UTIL + i];
520 l->cores[i][1] = h[MESH_S_CORE_CLASS + i];
521 }
522#undef F
523#undef U
524 return true;
525}
526
527bool mt_snapshot_prog_load(const mt_snapshot *s, uint32_t slot, mt_prog_load *p) {
528 size_t at = mesh_pload(slot);
529 uint32_t flags = mt_snapshot_host_u32(s, at + MESH_PL_FLAGS);
530 if (!(flags & MESH_PLF_LOADED)) return false;
531#define F(o) mt_snapshot_host_f32(s, at + (o))
532 bool g = flags & MESH_PLF_GPU, l = flags & MESH_PLF_LAT;
533 p->flags = flags;
534 p->share = opt(g, F(MESH_PL_SHARE));
535 p->share10 = opt(g, F(MESH_PL_SHARE10));
536 p->slice_us = F(MESH_PL_SLICE_US);
537 p->gap_us = F(MESH_PL_GAP_US);
538 p->max_gap_us = F(MESH_PL_MAX_GAP_US);
539 p->observed = F(MESH_PL_OBS);
540 p->lat_n = mt_snapshot_host_u32(s, at + MESH_PL_LAT_N);
541 p->p50_us = opt(l, F(MESH_PL_P50_US));
542 p->p90_us = opt(l, F(MESH_PL_P90_US));
543 p->p99_us = opt(l, F(MESH_PL_P99_US));
544 p->max_us = opt(l, F(MESH_PL_MAX_US));
545 p->task_us = opt(flags & MESH_PLF_TASK, F(MESH_PL_TASK_US));
546 p->tasks_s = F(MESH_PL_TASKS_S);
547 p->busy = F(MESH_PL_BUSY);
548 p->queue_us = F(MESH_PL_QUEUE_US);
549#undef F
550 return true;
551}
552
553bool mt_snapshot_member_load(const mt_snapshot *s, uint32_t k, mt_member_load *m) {
554 size_t at = mesh_mload(k);
555 uint64_t t = mt_snapshot_host_u64(s, at + MESH_ML_AT_MS);
556 if (t == 0) return false;
557#define F(o) mt_snapshot_host_f32(s, at + (o))
558 uint32_t flags = mt_snapshot_host_u32(s, at + MESH_ML_FLAGS);
559 uint32_t cpus = mt_snapshot_host_u32(s, at + MESH_ML_CPUS);
560 uint64_t up = mt_snapshot_uptime_ms(s);
561 m->age_ms = up > t ? up - t : 0;
562 m->flags = flags;
563 m->share = opt(flags & MESH_LF_GPU, F(MESH_ML_SHARE));
564 m->share10 = opt(flags & MESH_LF_GPU, F(MESH_ML_SHARE10));
565 m->max_gap_us = F(MESH_ML_MAX_GAP_US);
566 m->cpu = F(MESH_ML_CPU);
567 m->cpu_big = F(MESH_ML_CPU_BIG);
568 m->cpu_little = F(MESH_ML_CPU_LITTLE);
569 m->psi_cpu = opt(flags & MESH_LF_PSI, F(MESH_ML_PSI_CPU));
570 m->psi_mem = opt(flags & MESH_LF_PSI, F(MESH_ML_PSI_MEM));
571 m->meshd_cpu = F(MESH_ML_MESHD_CPU);
572 m->mem_avail_mb = mt_snapshot_host_u32(s, at + MESH_ML_MEM_AVAIL_MB);
573 m->mem_total_mb = mt_snapshot_host_u32(s, at + MESH_ML_MEM_TOTAL_MB);
574 m->disk_free_mb = mt_snapshot_host_u32(s, at + MESH_ML_DISK_FREE_MB);
575 m->cpus_big = cpus & 0xffff;
576 m->cpus_little = cpus >> 16;
577#undef F
578 return true;
579}
580
581mt_link_stat mt_snapshot_link(const mt_snapshot *s, uint32_t k) {
582 const uint8_t *h = mt_snapshot_host(s) + mesh_link(k);
583 size_t n = MESH_LINK_BYTES;
584 mt_link_stat l;
585 l.system = k;
586 l.rx_bytes = mt_get_u64(h, n, MESH_L_RX_BYTES);
587 l.tx_bytes = mt_get_u64(h, n, MESH_L_TX_BYTES);
588 for (size_t i = 0; i < MESH_NET_KINDS; i++) {
589 l.rx_frames[i] = mt_get_u64(h, n, MESH_L_RX_FRAMES + 8 * i);
590 l.tx_frames[i] = mt_get_u64(h, n, MESH_L_TX_FRAMES + 8 * i);
591 }
592 mt_get_str(h, n, MESH_L_LOCAL, 32, l.local);
593 mt_get_str(h, n, MESH_L_IFACE, 16, l.iface);
594 return l;
595}
596
597const size_t MT_NIC_COUNTERS[8] = {MESH_NI_RX_BYTES, MESH_NI_TX_BYTES, MESH_NI_RX_PACKETS, MESH_NI_TX_PACKETS,
598 MESH_NI_RX_ERRS, MESH_NI_TX_ERRS, MESH_NI_RX_DROP, MESH_NI_TX_DROP};
599
600void mt_snapshot_net_at(const mt_snapshot *s, uint64_t *at_ms, uint32_t *samples) {
601 *at_ms = mt_snapshot_host_u64(s, MESH_S_NET_AT_MS);
602 *samples = mt_snapshot_host_u32(s, MESH_S_NET_SAMPLES);
603}
604
605static mt_nic nic_at(const mt_snapshot *s, size_t at) {
606 const uint8_t *h = mt_snapshot_host(s) + at;
607 size_t n = MESH_NIC_BYTES;
608 mt_nic x;
609 mt_get_str(h, n, MESH_NI_NAME, 16, x.name);
610 for (int i = 0; i < 8; i++) x.v[i] = mt_get_u64(h, n, MT_NIC_COUNTERS[i]);
611 x.links = mt_get_u32(h, n, MESH_NI_LINKS);
612 x.count = mt_get_u32(h, n, MESH_NI_COUNT);
613 return x;
614}
615
616size_t mt_snapshot_nics(const mt_snapshot *s, mt_nic *out) {
617 size_t n = mt_snapshot_host_u32(s, MESH_S_NET_NICS);
618 if (n > MESH_MAX_NICS) n = MESH_MAX_NICS;
619 for (size_t i = 0; i < n; i++) out[i] = nic_at(s, mesh_nic(i));
620 return n;
621}
622
623mt_nic mt_snapshot_net_total(const mt_snapshot *s) { return nic_at(s, MESH_S_NET_TOTAL); }
624
625void mt_snapshot_ports(const mt_snapshot *s, uint64_t ctl[3], uint64_t http[3]) {
626 ctl[0] = mt_snapshot_host_u64(s, MESH_S_CTL_RX);
627 ctl[1] = mt_snapshot_host_u64(s, MESH_S_CTL_TX);
628 ctl[2] = mt_snapshot_host_u64(s, MESH_S_CTL_REQS);
629 http[0] = mt_snapshot_host_u64(s, MESH_S_HTTP_RX);
630 http[1] = mt_snapshot_host_u64(s, MESH_S_HTTP_TX);
631 http[2] = mt_snapshot_host_u64(s, MESH_S_HTTP_CONNS);
632}
633
634void mt_snapshot_lock_pair(const mt_snapshot *s, char name[3 * MESH_NAME_BYTES + 1], uint64_t *step, uint8_t view[32]) {
635 const uint8_t *h = mt_snapshot_host(s);
636 memcpy(view, h + MESH_S_LOCK_VIEW, 32);
637 mt_snapshot_host_str(s, MESH_S_LOCK_NAME, MESH_NAME_BYTES, name);
638 *step = mt_get_u64(h, MESH_SNAP_HOST, MESH_S_LOCK_STEP);
639}
640
641void mt_cc_label(uint32_t cc, char *out) {
642 if (cc == 0) snprintf(out, 32, "-");
643 else snprintf(out, 32, "%u.%u", cc / 10, cc % 10);
644}
645
646void mt_gpu_label(uint32_t cc, uint32_t backend, char *out) {
647 if (mesh_backend_kind(backend) == MESH_BACKEND_METAL) {
648 if (mesh_backend_family(backend) > 0) snprintf(out, 32, "apple%u", mesh_backend_family(backend));
649 else snprintf(out, 32, "metal");
650 return;
651 }
652 mt_cc_label(cc, out);
653}
654
655void mt_blocks_label(uint32_t blocks, uint32_t backend, char *out) {
656 if (mesh_backend_kind(backend) == MESH_BACKEND_CUDA) snprintf(out, 32, "%u", blocks);
657 else snprintf(out, 32, "-");
658}
659
660/* ---- trace */
661
662bool mt_parse_trace(const uint8_t *b, size_t len, mt_trace_prog **out, size_t *n, char **err) {
663 size_t per = 8 + (size_t)MESH_TRACE_PER_PROG * MESH_TRACE_BYTES;
664 *out = NULL;
665 *n = 0;
666 if (len % per != 0 || len / per > MESH_PROG_SLOTS) {
667 *err = fmt("trace reply is %zu bytes, not a multiple of %zu", len, per);
668 return false;
669 }
670 size_t count = len / per;
671 mt_trace_prog *t = calloc(count ? count : 1, sizeof *t);
672 if (!t) abort();
673 for (size_t c = 0; c < count; c++) {
674 const uint8_t *chunk = b + c * per;
675 uint32_t slot = mt_get_u32(chunk, per, 0), cnt = mt_get_u32(chunk, per, 4);
676 if (slot >= MESH_PROG_SLOTS) {
677 *err = fmt("trace slot %u", slot);
678 mt_trace_free(t, c);
679 return false;
680 }
681 uint32_t first = cnt > MESH_TRACE_PER_PROG ? cnt - MESH_TRACE_PER_PROG : 0;
682 t[c].slot = slot;
683 t[c].n = cnt;
684 t[c].nrecs = cnt - first;
685 t[c].recs = malloc((t[c].nrecs ? t[c].nrecs : 1) * sizeof *t[c].recs);
686 if (!t[c].recs) abort();
687 for (uint32_t i = first; i < cnt; i++) {
688 size_t at = 8 + MESH_TRACE_BYTES * (size_t)(i % MESH_TRACE_PER_PROG);
689 uint32_t w0 = mt_get_u32(chunk, per, at);
690 t[c].recs[i - first] = (mt_rec){w0 & 0xff, w0 >> 8 & 0xff, w0 >> 16, mt_get_u32(chunk, per, at + 4),
691 mt_get_u32(chunk, per, at + 8), mt_get_u32(chunk, per, at + 12)};
692 }
693 }
694 *out = t;
695 *n = count;
696 return true;
697}
698
699void mt_trace_free(mt_trace_prog *t, size_t n) {
700 if (!t) return;
701 for (size_t i = 0; i < n; i++) free(t[i].recs);
702 free(t);
703}
704
705static int cmp_u32(const void *a, const void *b) {
706 uint32_t x = *(const uint32_t *)a, y = *(const uint32_t *)b;
707 return x < y ? -1 : x > y;
708}
709
710bool mt_quantile_us(const mt_rec *recs, size_t n, double q, double *out) {
711 if (n == 0) return false;
712 uint32_t *d = malloc(n * sizeof *d);
713 if (!d) abort();
714 for (size_t i = 0; i < n; i++) d[i] = mt_rec_ns(&recs[i]);
715 qsort(d, n, sizeof *d, cmp_u32);
716 size_t i = (size_t)round((double)(n - 1) * q);
717 *out = (double)d[i] / 1000.0;
718 free(d);
719 return true;
720}