==> src/perf.c <==1/* This member's F_PERF (load.rs perf_report and its frame): its CPUs and RAM, each loaded program's
2 * measured performance (the pload records just sampled), its reconcile failures, the link RTTs
3 * measured from the others' echoes, and the echoes of theirs; put on the perf board and broadcast. */
4#include "fleetd_int.h"
5
6#include "mesh/abi.h"
7#include "mesh/buf.h"
8#include "mesh/prog_table.h"
9#include "mesh/samplers.h"
10
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15static mesh_fleetd *PERF; /* the hook has no context: one fleet member per process */
16
17static uint32_t u32_at(const uint8_t *b, size_t o) {
18 uint32_t v;
19 memcpy(&v, b + o, 4);
20 return v;
21}
22static float f32_at(const uint8_t *b, size_t o) {
23 float v;
24 memcpy(&v, b + o, 4);
25 return v;
26}
27
28static void perf_report(mesh_shared *sh, const mesh_host_sample *h, const uint8_t *cls, size_t ncls, const uint8_t (*recs)[MESH_PLOAD_BYTES],
29 size_t n) {
30 mesh_fleetd *f = PERF;
31 mpr_relay *r = f ? atomic_load(&f->relay) : NULL;
32 if (!r) return;
33 mesh_mailbox mb = sh->mb;
34 msr_report rep = {.t_us = (uint64_t)mesh_mono_us(), .ram_mb = h->mem_total_mb, .disk_free_mb = h->disk_free_mb};
35 for (size_t i = 0; i < h->ncpus && i < MESH_MAX_CPUS; i++) rep.cpus += h->have[i];
36 for (size_t i = 0; i < ncls; i++) {
37 rep.big += cls[i] == MESH_CORE_BIG;
38 rep.little += cls[i] == MESH_CORE_LITTLE;
39 }
40 rep.heap_kb = mesh_host_block_get_u32(&sh->host, MESH_S_HEAP_RUN) * (uint32_t)(MESH_VIEW_PAGE / 1024);
41 uint32_t progs = mesh_host_block_get_u32(&sh->host, MESH_S_PROGRAMS);
42 rep.slots_free = progs < MESH_PROG_SLOTS ? MESH_PROG_SLOTS - progs : 0;
43 pthread_mutex_lock(&f->mu);
44 for (size_t i = 0; i < f->nfails; i++) msr_add_fail(&rep, f->fails[i].name, f->fails[i].hash, f->fails[i].unsupported, f->fails[i].why);
45 pthread_mutex_unlock(&f->mu);
46 for (uint32_t s = 0; s < n; s++) {
47 const uint8_t *rec = recs[s];
48 uint32_t fl = u32_at(rec, MESH_PL_FLAGS);
49 uint32_t st = mesh_mailbox_get(mb, mesh_prog_entry(s) + MESH_PT_STATE);
50 if (!(fl & MESH_PLF_LOADED) || st == MESH_ST_EMPTY) continue;
51 msm_perf p;
52 memset(&p, 0, sizeof p);
53 p.hash = mesh_mailbox_get64(mb, mesh_prog_entry(s) + MESH_PT_PTX_HASH);
54 uint32_t ex = mesh_mailbox_get(mb, mesh_prog_ext(s) + MESH_PX_EXEC);
55 snprintf(p.exec, sizeof p.exec, "%s", ex < 4 ? MESH_BACKENDS[ex] : "?");
56 p.run = st == MESH_ST_RUNNING;
57 float task = 0;
58 bool has_task = false;
59 if (fl & MESH_PLF_TASK) task = f32_at(rec, MESH_PL_TASK_US), has_task = true;
60 else if (fl & MESH_PLF_LAT) task = f32_at(rec, MESH_PL_P50_US), has_task = true;
61 p.task_us = (mof32){has_task && task > 0, has_task && task > 0 ? task : 0};
62 p.tasks_s = f32_at(rec, MESH_PL_TASKS_S);
63 p.cpu = fl & MESH_PLF_HOST ? f32_at(rec, MESH_PL_CPU10) : 0;
64 p.share = (mof32){(fl & MESH_PLF_GPU) != 0, fl & MESH_PLF_GPU ? f32_at(rec, MESH_PL_SHARE10) : 0};
65 if (fl & MESH_PLF_HOST) {
66 uint64_t rss;
67 memcpy(&rss, rec + MESH_PL_RSS, 8);
68 p.rss_mb = (float)rss / 1048576.0f;
69 }
70 char name[MESH_NAME_BYTES + 1];
71 mesh_pt_name_of(mb, s, name);
72 msr_add_prog(&rep, name, &p);
73 }
74 uint32_t k[MESH_MAX_SYSTEMS];
75 uint64_t t[MESH_MAX_SYSTEMS], held[MESH_MAX_SYSTEMS];
76 size_t ne = mpr_board_echoes(r, k, t, held);
77 for (size_t i = 0; i < ne; i++) msr_add_echo(&rep, k[i], t[i], held[i]);
78 float us[MESH_MAX_SYSTEMS];
79 size_t nr = mpr_board_rtts(r, k, us);
80 for (size_t i = 0; i < nr; i++) msr_add_rtt(&rep, k[i], us[i]);
81 char *text = msr_encode(&rep);
82 size_t len = strlen(text);
83 if (len > MESH_F_PERF_MAX) len = MESH_F_PERF_MAX;
84 uint8_t *fr = mesh_xmalloc(5 + len);
85 fr[0] = MESH_F_PERF;
86 uint32_t l = (uint32_t)len;
87 memcpy(fr + 1, &l, 4);
88 memcpy(fr + 5, text, len);
89 mesh_shared_broadcast(sh, fr, 5 + len);
90 free(fr);
91 free(text);
92 mpr_board_own(r, &rep);
93 msr_free(&rep);
94}
95
96void fd_perf_set(mesh_fleetd *f) {
97 PERF = f;
98 mesh_load_perf = f ? perf_report : NULL;
99}