skip to content
==> src/load_proc.c <==
1/* load.rs's parsers, line for line. */
2#include "mesh/load_proc.h"
3#include "mesh/abi/host_load.h"
4#include "mesh/buf.h"
5#include "mesh/rsfmt.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10void mesh_cpu_times_since(const mesh_cpu_times *now, const mesh_cpu_times *prev, float *busy, float *io) {
11    uint64_t dt = now->total > prev->total ? now->total - prev->total : 0;
12    if (dt == 0) { *busy = 0; *io = 0; return; }
13    uint64_t b = now->busy > prev->busy ? now->busy - prev->busy : 0;
14    uint64_t i = now->iowait > prev->iowait ? now->iowait - prev->iowait : 0;
15    if (b > dt) b = dt;
16    if (i > dt) i = dt;
17    *busy = (float)b / (float)dt;
18    *io = (float)i / (float)dt;
19}
20
21/* The next line of text (without its \n); NULL at the end. */
22static const char *next_line(const char **p, size_t *n) {
23    if (!**p) return NULL;
24    const char *l = *p, *e = strchr(l, '\n');
25    *n = e ? (size_t)(e - l) : strlen(l);
26    *p = e ? e + 1 : l + *n;
27    if (*n && l[*n - 1] == '\r') (*n)--; /* str::lines */
28    return l;
29}
30
31static bool is_ws(char c) { return c == ' ' || (c >= 9 && c <= 13); }
32
33/* split_whitespace over [s, s+n): the next word. */
34static bool word(const char **s, const char *end, const char **w, size_t *wn) {
35    while (*s < end && is_ws(**s)) (*s)++;
36    if (*s >= end) return false;
37    *w = *s;
38    while (*s < end && !is_ws(**s)) (*s)++;
39    *wn = (size_t)(*s - *w);
40    return true;
41}
42
43static char *quoted(const char *s, size_t n) {
44    mesh_buf b = MESH_BUF_INIT;
45    mesh_rs_debug_str(&b, s, n);
46    return mesh_buf_take(&b);
47}
48
49int mesh_parse_proc_stat(const char *text, mesh_cpu_times *all, mesh_cpu_times *per, bool *have, size_t *n, char **err) {
50    bool got_all = false;
51    *n = 0;
52    for (size_t i = 0; i < MESH_MAX_CPUS; i++) have[i] = false;
53    const char *p = text, *l;
54    size_t ln;
55    while ((l = next_line(&p, &ln))) {
56        if (ln < 3 || strncmp(l, "cpu", 3) != 0) continue;
57        const char *rest = l + 3, *end = l + ln;
58        const char *sp = memchr(rest, ' ', (size_t)(end - rest));
59        char *q = quoted(l, ln);
60        if (!sp) { *err = mesh_buf_fmt("/proc/stat: %s", q); free(q); return -1; }
61        uint64_t v[16];
62        size_t k = 0;
63        const char *s = sp + 1, *w;
64        size_t wn;
65        while (word(&s, end, &w, &wn)) {
66            uint64_t x;
67            int r = mesh_rs_parse_u64(w, wn, &x);
68            if (r) { *err = mesh_buf_fmt("/proc/stat %s: %s", q, mesh_rs_parse_error(r)); free(q); return -1; }
69            if (k < 16) v[k] = x;
70            k++;
71        }
72        if (k < 8) { *err = mesh_buf_fmt("/proc/stat %s: %zu fields, want at least 8", q, k); free(q); return -1; }
73        uint64_t total = 0;
74        for (int i = 0; i < 8; i++) total += v[i];
75        mesh_cpu_times t = {total - v[3] - v[4], total, v[4]};
76        size_t idn = (size_t)(sp - rest);
77        if (idn == 0) { *all = t; got_all = true; }
78        else {
79            uint64_t id;
80            if (mesh_rs_parse_u64(rest, idn, &id)) {
81                char *qi = quoted(rest, idn);
82                *err = mesh_buf_fmt("/proc/stat: cpu id %s", qi);
83                free(qi);
84                free(q);
85                return -1;
86            }
87            if (id < MESH_MAX_CPUS) {
88                if (*n <= id) *n = (size_t)id + 1;
89                per[id] = t;
90                have[id] = true;
91            }
92        }
93        free(q);
94    }
95    if (!got_all) { *err = mesh_xstrdup("/proc/stat has no cpu line"); return -1; }
96    if (*n == 0) { *err = mesh_xstrdup("/proc/stat lists no cpuN line"); return -1; }
97    return 0;
98}
99
100void mesh_classify(const uint64_t *perf, const bool *have, size_t n, uint8_t *out) {
101    uint64_t max = 0;
102    for (size_t i = 0; i < n; i++) if (have[i] && perf[i] > max) max = perf[i];
103    for (size_t i = 0; i < n; i++) {
104        if (max == 0) out[i] = MESH_CORE_UNKNOWN;
105        else if (!have[i]) out[i] = MESH_CORE_UNKNOWN;
106        else out[i] = perf[i] * 10 >= max * 9 ? MESH_CORE_BIG : MESH_CORE_LITTLE;
107    }
108}
109
110static float class_mean(const float *util, const bool *have, const uint8_t *cls, size_t n, int want) {
111    float sum = 0;
112    size_t k = 0;
113    for (size_t i = 0; i < n; i++) {
114        if (want >= 0 && cls[i] != want) continue;
115        if (!have[i]) continue;
116        sum += util[i];
117        k++;
118    }
119    return k ? sum / (float)k : 0.0f;
120}
121
122void mesh_class_util(const float *util, const bool *have, const uint8_t *cls, size_t n, float *all, float *big, float *little) {
123    *all = class_mean(util, have, cls, n, -1);
124    *big = class_mean(util, have, cls, n, MESH_CORE_BIG);
125    *little = class_mean(util, have, cls, n, MESH_CORE_LITTLE);
126}
127
128int mesh_parse_psi(const char *text, mesh_psi *out, char **err) {
129    memset(out, 0, sizeof *out);
130    bool some = false;
131    const char *p = text, *l;
132    size_t ln;
133    while ((l = next_line(&p, &ln))) {
134        const char *s = l, *end = l + ln, *w;
135        size_t wn;
136        const char *kind = "";
137        size_t kn = 0;
138        if (word(&s, end, &w, &wn)) { kind = w; kn = wn; }
139        bool ha = false, ht = false;
140        float avg10 = 0;
141        uint64_t total = 0;
142        while (word(&s, end, &w, &wn)) {
143            const char *eq = memchr(w, '=', wn);
144            if (!eq) continue;
145            size_t keyn = (size_t)(eq - w), vn = wn - keyn - 1;
146            if (keyn == 5 && !strncmp(w, "avg10", 5)) {
147                float a;
148                ha = mesh_rs_parse_f32(eq + 1, vn, &a) == 0;
149                if (ha) avg10 = a;
150            } else if (keyn == 5 && !strncmp(w, "total", 5)) {
151                uint64_t t;
152                ht = mesh_rs_parse_u64(eq + 1, vn, &t) == 0;
153                if (ht) total = t;
154            }
155        }
156        char *q = quoted(l, ln);
157        if (!ha || !ht) { *err = mesh_buf_fmt("PSI line %s: no avg10/total", q); free(q); return -1; }
158        if (kn == 4 && !strncmp(kind, "some", 4)) { out->some_avg10 = avg10 / 100.0f; out->some_total = total; some = true; }
159        else if (kn == 4 && !strncmp(kind, "full", 4)) { out->full_avg10 = avg10 / 100.0f; out->full_total = total; }
160        else { *err = mesh_buf_fmt("PSI line %s", q); free(q); return -1; }
161        free(q);
162    }
163    if (!some) { *err = mesh_xstrdup("PSI: no `some` line"); return -1; }
164    return 0;
165}
166
167float mesh_psi_rate(uint64_t now, uint64_t prev, uint64_t dt_us) {
168    if (dt_us == 0) return 0;
169    double r = (double)(now > prev ? now - prev : 0) / (double)dt_us;
170    return (float)(r < 1.0 ? r : 1.0);
171}
172
173int mesh_parse_meminfo(const char *text, mesh_meminfo *m, char **err) {
174    memset(m, 0, sizeof *m);
175    int have = 0;
176    const char *p = text, *l;
177    size_t ln;
178    while ((l = next_line(&p, &ln))) {
179        const char *colon = memchr(l, ':', ln);
180        if (!colon) continue;
181        size_t kn = (size_t)(colon - l);
182        uint64_t *dst = NULL;
183        int bit = 0;
184        if (kn == 8 && !strncmp(l, "MemTotal", 8)) { dst = &m->total_kb; bit = 1; }
185        else if (kn == 12 && !strncmp(l, "MemAvailable", 12)) { dst = &m->avail_kb; bit = 2; }
186        else if (kn == 9 && !strncmp(l, "SwapTotal", 9)) dst = &m->swap_total_kb;
187        else if (kn == 8 && !strncmp(l, "SwapFree", 8)) dst = &m->swap_free_kb;
188        if (!dst) continue;
189        const char *s = colon + 1, *w;
190        size_t wn;
191        uint64_t x;
192        if (!word(&s, l + ln, &w, &wn) || mesh_rs_parse_u64(w, wn, &x)) {
193            char *q = quoted(l, ln);
194            *err = mesh_buf_fmt("/proc/meminfo %s", q);
195            free(q);
196            return -1;
197        }
198        *dst = x;
199        have |= bit;
200    }
201    if (have != 3) { *err = mesh_xstrdup("/proc/meminfo: no MemTotal or MemAvailable"); return -1; }
202    return 0;
203}
204
205int mesh_parse_loadavg(const char *text, float out[3], char **err) {
206    const char *s = text, *end = text + strlen(text), *w;
207    size_t wn, k = 0;
208    while (k < 3 && word(&s, end, &w, &wn)) {
209        int r = mesh_rs_parse_f32(w, wn, &out[k]);
210        if (r) {
211            char *q = quoted(text, strlen(text));
212            *err = mesh_buf_fmt("/proc/loadavg %s: %s", q, mesh_rs_parse_error(r));
213            free(q);
214            return -1;
215        }
216        k++;
217    }
218    if (k != 3) {
219        char *q = quoted(text, strlen(text));
220        *err = mesh_buf_fmt("/proc/loadavg %s", q);
221        free(q);
222        return -1;
223    }
224    return 0;
225}
226
227bool mesh_proc_field(const char *text, const char *key, uint64_t *out) {
228    const char *p = text, *l;
229    size_t ln, keyn = strlen(key);
230    while ((l = next_line(&p, &ln))) {
231        /* split_once(':') or else split_once(' ') */
232        const char *sep = memchr(l, ':', ln);
233        if (!sep) sep = memchr(l, ' ', ln);
234        if (!sep) continue;
235        const char *ks = l, *ke = sep;
236        while (ks < ke && is_ws(*ks)) ks++;
237        while (ke > ks && is_ws(ke[-1])) ke--;
238        if ((size_t)(ke - ks) != keyn || strncmp(ks, key, keyn)) continue;
239        const char *s = sep + 1, *w;
240        size_t wn;
241        uint64_t x;
242        if (!word(&s, l + ln, &w, &wn) || mesh_rs_parse_u64(w, wn, &x)) return false;
243        *out = x;
244        return true;
245    }
246    return false;
247}