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