==> 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}