==> src/sched_report.c <==1/* sched.rs Report: encode and decode. */
2#include "mesh/sched_report.h"
3#include "mesh/parse.h"
4#include "mesh/rust_fmt.h"
5#include "mesh/mt.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10#define GROW(arr, n) ((arr) = mt_realloc((arr), ((n) + 1) * sizeof *(arr)))
11
12void msr_add_prog(msr_report *r, const char *name, const msm_perf *p) {
13 GROW(r->progs, r->nprogs);
14 r->progs[r->nprogs++] = (msr_prog){mt_strdup(name), *p};
15}
16
17void msr_add_fail(msr_report *r, const char *name, uint64_t hash, bool unsupported, const char *why) {
18 GROW(r->fails, r->nfails);
19 r->fails[r->nfails++] = (msr_fail){mt_strdup(name), hash, unsupported, mt_strdup(why)};
20}
21
22void msr_add_rtt(msr_report *r, uint32_t k, float us) {
23 GROW(r->rtt, r->nrtt);
24 r->rtt[r->nrtt++] = (msr_rtt){k, us};
25}
26
27void msr_add_echo(msr_report *r, uint32_t k, uint64_t t, uint64_t held) {
28 GROW(r->echo, r->necho);
29 r->echo[r->necho++] = (msr_echo){k, t, held};
30}
31
32static void opt(mt_buf *b, mof32 v) {
33 if (v.some) mt_printf(b, "%.2f", (double)v.v);
34 else mt_putc(b, '-');
35}
36
37char *msr_encode(const msr_report *r) {
38 mt_buf b = {0};
39 mt_printf(&b, "t %llu\ncaps cpus %u big %u little %u ram_mb %u disk_mb %u heap_kb %u slots_free %u\n", (unsigned long long)r->t_us, r->cpus, r->big,
40 r->little, r->ram_mb, r->disk_free_mb, r->heap_kb, r->slots_free);
41 for (size_t i = 0; i < r->nprogs; i++) {
42 const msm_perf *p = &r->progs[i].perf;
43 mt_printf(&b, "prog %s hash %016llx exec %s run %u task_us ", r->progs[i].name, (unsigned long long)p->hash, p->exec, (unsigned)p->run);
44 opt(&b, p->task_us);
45 mt_printf(&b, " tasks_s %.2f cpu %.3f share ", (double)p->tasks_s, (double)p->cpu);
46 opt(&b, p->share);
47 mt_printf(&b, " rss_mb %.1f\n", (double)p->rss_mb);
48 }
49 for (size_t i = 0; i < r->nrtt; i++) mt_printf(&b, "rtt %u %.1f\n", r->rtt[i].k, (double)r->rtt[i].us);
50 for (size_t i = 0; i < r->necho; i++)
51 mt_printf(&b, "echo %u %llu %llu\n", r->echo[i].k, (unsigned long long)r->echo[i].t, (unsigned long long)r->echo[i].held);
52 for (size_t i = 0; i < r->nfails; i++) {
53 char *why = mt_strdup(r->fails[i].why);
54 for (char *c = why; *c; c++)
55 if (*c == '\n') *c = ' ';
56 mt_printf(&b, "fail %s %016llx %u %s\n", r->fails[i].name, (unsigned long long)r->fails[i].hash, (unsigned)r->fails[i].unsupported, why);
57 free(why);
58 }
59 return mt_take(&b);
60}
61
62/* Rust's `n(x)? as u32`: parsed as u64, then truncated. */
63static bool n32(const char *s, uint32_t *out) {
64 uint64_t v;
65 if (!mp_u64(s, &v)) return false;
66 *out = (uint32_t)v;
67 return true;
68}
69
70static bool of(const char *s, mof32 *out) {
71 if (strcmp(s, "-") == 0) {
72 *out = mof32_none();
73 return true;
74 }
75 float f;
76 if (!mp_f32(s, &f)) return false;
77 *out = mof32_some(f);
78 return true;
79}
80
81static bool is(mt_strs *w, size_t n, const char *const *lit) {
82 if (w->n != n) return false;
83 for (size_t i = 0; i < n; i++)
84 if (lit[i] && strcmp(w->v[i], lit[i]) != 0) return false;
85 return true;
86}
87
88static bool line(msr_report *r, const char *l, mt_strs *w) {
89 if (w->n == 0) return true;
90 static const char *const t[] = {"t", NULL};
91 static const char *const caps[] = {"caps", "cpus", NULL, "big", NULL, "little", NULL, "ram_mb", NULL, "disk_mb", NULL, "heap_kb", NULL, "slots_free", NULL};
92 static const char *const prog[] = {"prog", NULL, "hash", NULL, "exec", NULL, "run", NULL, "task_us", NULL, "tasks_s", NULL, "cpu", NULL, "share", NULL, "rss_mb", NULL};
93 static const char *const rtt[] = {"rtt", NULL, NULL};
94 static const char *const echo[] = {"echo", NULL, NULL, NULL};
95 char **v = w->v;
96 if (is(w, 2, t)) return mp_u64(v[1], &r->t_us);
97 if (is(w, 15, caps))
98 return n32(v[2], &r->cpus) && n32(v[4], &r->big) && n32(v[6], &r->little) && n32(v[8], &r->ram_mb) && n32(v[10], &r->disk_free_mb) &&
99 n32(v[12], &r->heap_kb) && n32(v[14], &r->slots_free);
100 if (is(w, 18, prog)) {
101 msm_perf p = {0};
102 if (!mp_hex_u64(v[3], &p.hash)) return false;
103 msm_set_str(p.exec, sizeof p.exec, v[5]);
104 p.run = strcmp(v[7], "1") == 0;
105 if (!of(v[9], &p.task_us) || !mp_f32(v[11], &p.tasks_s) || !mp_f32(v[13], &p.cpu) || !of(v[15], &p.share) || !mp_f32(v[17], &p.rss_mb)) return false;
106 msr_add_prog(r, v[1], &p);
107 return true;
108 }
109 if (is(w, 3, rtt)) {
110 uint32_t k;
111 float us;
112 if (!n32(v[1], &k) || !mp_f32(v[2], &us)) return false;
113 msr_add_rtt(r, k, us);
114 return true;
115 }
116 if (is(w, 4, echo)) {
117 uint32_t k;
118 uint64_t a, b;
119 if (!n32(v[1], &k) || !mp_u64(v[2], &a) || !mp_u64(v[3], &b)) return false;
120 msr_add_echo(r, k, a, b);
121 return true;
122 }
123 if (w->n >= 4 && strcmp(v[0], "fail") == 0) {
124 uint64_t h;
125 if (!mp_hex_u64(v[2], &h)) return false;
126 /* Rust's l.splitn(5, ' ').nth(4): after the fourth single space. */
127 const char *why = l;
128 for (int i = 0; i < 4 && why; i++) {
129 why = strchr(why, ' ');
130 if (why) why++;
131 }
132 msr_add_fail(r, v[1], h, strcmp(v[3], "1") == 0, why ? why : "");
133 return true;
134 }
135 return false;
136}
137
138bool msr_decode(const char *text, msr_report *out, char **err) {
139 msr_report r = {0};
140 mt_strs ls = mt_lines(text);
141 bool ok = true;
142 for (size_t i = 0; i < ls.n && ok; i++) {
143 mt_strs w = mt_words(ls.v[i]);
144 ok = line(&r, ls.v[i], &w);
145 mt_strs_free(&w);
146 if (!ok) {
147 char *q = rf_debug_str(ls.v[i]);
148 *err = mt_fmt("F_PERF: %s", q);
149 free(q);
150 }
151 }
152 mt_strs_free(&ls);
153 if (!ok) {
154 msr_free(&r);
155 return false;
156 }
157 *out = r;
158 return true;
159}
160
161bool msr_eq(const msr_report *a, const msr_report *b) {
162 if (a->t_us != b->t_us || a->cpus != b->cpus || a->big != b->big || a->little != b->little || a->ram_mb != b->ram_mb || a->disk_free_mb != b->disk_free_mb ||
163 a->heap_kb != b->heap_kb || a->slots_free != b->slots_free || a->nprogs != b->nprogs || a->nfails != b->nfails || a->nrtt != b->nrtt || a->necho != b->necho)
164 return false;
165 for (size_t i = 0; i < a->nprogs; i++)
166 if (strcmp(a->progs[i].name, b->progs[i].name) != 0 || !msm_perf_eq(&a->progs[i].perf, &b->progs[i].perf)) return false;
167 for (size_t i = 0; i < a->nfails; i++)
168 if (strcmp(a->fails[i].name, b->fails[i].name) != 0 || a->fails[i].hash != b->fails[i].hash || a->fails[i].unsupported != b->fails[i].unsupported ||
169 strcmp(a->fails[i].why, b->fails[i].why) != 0)
170 return false;
171 for (size_t i = 0; i < a->nrtt; i++)
172 if (a->rtt[i].k != b->rtt[i].k || a->rtt[i].us != b->rtt[i].us) return false;
173 for (size_t i = 0; i < a->necho; i++)
174 if (a->echo[i].k != b->echo[i].k || a->echo[i].t != b->echo[i].t || a->echo[i].held != b->echo[i].held) return false;
175 return true;
176}
177
178void msr_free(msr_report *r) {
179 for (size_t i = 0; i < r->nprogs; i++) free(r->progs[i].name);
180 for (size_t i = 0; i < r->nfails; i++) {
181 free(r->fails[i].name);
182 free(r->fails[i].why);
183 }
184 free(r->progs);
185 free(r->fails);
186 free(r->rtt);
187 free(r->echo);
188 *r = (msr_report){0};
189}