==> src/load.c <==1/* ui.rs's "8 load" section, ported. */
2#include "mesh/top_ui_load.h"
3
4#include "tui/sparkline.h"
5
6#include <string.h>
7
8static mt_opt_f32 some(float v) { return (mt_opt_f32){true, v}; }
9static const mt_opt_f32 NONE = {false, 0.0f};
10
11mt_load_row mt_load_row_from_load(const mt_load *l) {
12 mt_load_row r = {0};
13 r.share = l->share10;
14 r.cpu = l->cpu;
15 r.big = l->cpu_big;
16 r.little = l->cpus_little > 0 ? some(l->cpu_little) : NONE;
17 r.has_psi = l->has_psi;
18 if (l->has_psi) {
19 r.psi[0] = l->psi[0];
20 r.psi[1] = l->psi[1];
21 }
22 r.mem_free = some(l->mem_free);
23 r.meshd = l->meshd_cpu;
24 return r;
25}
26
27mt_load_row mt_load_row_from_member(const mt_member_load *l) {
28 mt_load_row r = {0};
29 r.share = l->share10;
30 r.cpu = l->cpu;
31 r.big = l->cpu_big;
32 r.little = l->cpus_little > 0 ? some(l->cpu_little) : NONE;
33 r.has_psi = l->psi_cpu.some && l->psi_mem.some;
34 if (r.has_psi) {
35 r.psi[0] = l->psi_cpu.v;
36 r.psi[1] = l->psi_mem.v;
37 }
38 r.mem_free = l->mem_total_mb > 0 ? some((float)l->mem_avail_mb / (float)l->mem_total_mb) : NONE;
39 r.meshd = l->meshd_cpu;
40 return r;
41}
42
43size_t mt_load_cols_header(mt_load_cols lt, const char **out) {
44 switch (lt) {
45 case MT_LOAD_COLS_NONE: return 0;
46 case MT_LOAD_COLS_ONE: out[0] = "GPU CPU"; return 1;
47 case MT_LOAD_COLS_PAIR: out[0] = "GPU CPU"; out[1] = "meshd"; return 2;
48 default:
49 out[0] = "GPU got";
50 out[1] = "CPU B/L";
51 out[2] = "PSI c/m";
52 out[3] = "meshd";
53 return 4;
54 }
55}
56
57size_t mt_load_cols_widths(mt_load_cols lt, tui_constraint *out) {
58 switch (lt) {
59 case MT_LOAD_COLS_NONE: return 0;
60 case MT_LOAD_COLS_ONE: out[0] = TUI_LENGTH(8); return 1;
61 case MT_LOAD_COLS_PAIR: out[0] = TUI_LENGTH(8); out[1] = TUI_LENGTH(5); return 2;
62 default:
63 out[0] = TUI_LENGTH(7);
64 out[1] = TUI_LENGTH(11);
65 out[2] = TUI_LENGTH(9);
66 out[3] = TUI_LENGTH(5);
67 return 4;
68 }
69}
70
71/* pct(x) without its trailing '%' (trim_end_matches). */
72static const char *pct_bare(mt_frame *f, float x) {
73 char *s = tui_arena_strdup(f->a, mt_pct(f, x));
74 size_t n = strlen(s);
75 while (n && s[n - 1] == '%') s[--n] = 0;
76 return s;
77}
78
79size_t mt_load_cells(mt_frame *f, const mt_load_row *r, mt_load_cols lt, tui_table_cell *out) {
80 if (!r) {
81 const char *h[4];
82 size_t n = mt_load_cols_header(lt, h);
83 for (size_t i = 0; i < n; i++) out[i] = mt_cell_span(f, mt_fgs("-", MT_FAINT));
84 return n;
85 }
86 tui_line pair = MT_LINE(f, mt_share_span(f, r->share), tui_span_raw(" "), mt_styled(mt_pct(f, r->cpu), mt_busy_style(r->cpu)));
87 tui_table_cell meshd = mt_cell_span(f, mt_styled(mt_pct(f, r->meshd), mt_busy_style(r->meshd)));
88 switch (lt) {
89 case MT_LOAD_COLS_NONE: return 0;
90 case MT_LOAD_COLS_ONE: out[0] = mt_cell_line(f, pair); return 1;
91 case MT_LOAD_COLS_PAIR:
92 out[0] = mt_cell_line(f, pair);
93 out[1] = meshd;
94 return 2;
95 default: break;
96 }
97 tui_line cpu;
98 if (r->little.some)
99 cpu = MT_LINE(f, mt_styled(mt_pct(f, r->cpu), mt_busy_style(r->cpu)),
100 mt_fgs(MT_FMT(f, " %s/%s", pct_bare(f, r->big), pct_bare(f, r->little.v)), MT_DIM));
101 else
102 cpu = tui_line_span(f->a, mt_styled(mt_pct(f, r->cpu), mt_busy_style(r->cpu)));
103 tui_line psi;
104 if (r->has_psi) {
105 float c = r->psi[0], m = r->psi[1];
106 psi = MT_LINE(f, mt_styled(mt_ff(f, c * 100.0f, 1), mt_busy_style(c * 10.0f)), mt_fgs("/", MT_DIM),
107 mt_styled(MT_FMT(f, "%s%%", mt_ff(f, m * 100.0f, 1)), mt_busy_style(m * 10.0f)));
108 } else if (r->mem_free.some) {
109 psi = tui_line_span(f->a, mt_styled(MT_FMT(f, "mem %s", mt_pct(f, r->mem_free.v)), mt_busy_style(1.0f - r->mem_free.v)));
110 } else {
111 psi = tui_line_span(f->a, mt_fgs("-", MT_FAINT));
112 }
113 out[0] = mt_cell_span(f, mt_share_span(f, r->share));
114 out[1] = mt_cell_line(f, cpu);
115 out[2] = mt_cell_line(f, psi);
116 out[3] = mt_cell_span(f, mt_styled(mt_pct(f, r->meshd), mt_busy_style(r->meshd)));
117 return 4;
118}
119
120const char *mt_gb_pair(mt_frame *f, uint32_t a_mb, uint32_t b_mb) {
121 return MT_FMT(f, "%s/%sG", mt_ff(f, (float)a_mb / 1024.0f, 0), mt_ff(f, (float)b_mb / 1024.0f, 0));
122}
123
124static void cpu_spans(mt_frame *f, const mt_load *l, tui_line *out) {
125 tui_line_push(f->a, out, mt_styled(mt_pct(f, l->cpu), mt_busy_style(l->cpu)));
126 if (l->cpus_little > 0) {
127 tui_line_push(f->a, out, mt_label(" big "));
128 tui_line_push(f->a, out, mt_styled(mt_pct(f, l->cpu_big), mt_busy_style(l->cpu_big)));
129 tui_line_push(f->a, out, mt_label(" little "));
130 tui_line_push(f->a, out, mt_styled(mt_pct(f, l->cpu_little), mt_busy_style(l->cpu_little)));
131 }
132}
133
134static void pressure_spans(mt_frame *f, const mt_load *l, tui_line *out) {
135 if (l->has_psi) {
136 const float *p = l->psi;
137 tui_line_push(f->a, out, mt_label(" PSI cpu "));
138 tui_line_push(f->a, out, mt_styled(MT_FMT(f, "%s%%", mt_ff(f, p[0] * 100.0f, 1)), mt_busy_style(p[0] * 10.0f)));
139 tui_line_push(f->a, out, mt_label(" mem "));
140 tui_line_push(f->a, out, mt_styled(MT_FMT(f, "%s%%", mt_ff(f, p[1] * 100.0f, 1)), mt_busy_style(p[1] * 10.0f)));
141 tui_line_push(f->a, out, mt_label(" io "));
142 tui_line_push(f->a, out, mt_styled(MT_FMT(f, "%s%%", mt_ff(f, p[3] * 100.0f, 1)), mt_busy_style(p[3] * 10.0f)));
143 } else if (l->has_mem_level) {
144 uint32_t lv = l->mem_level;
145 const char *t = lv == 1 ? "normal" : lv == 2 ? "warn" : lv == 4 ? "critical" : MT_FMT(f, "?%u", lv);
146 tui_line_push(f->a, out, mt_label(" mem pressure "));
147 tui_line_push(f->a, out, mt_fgs(t, lv == 1 ? MT_GOOD : lv == 2 ? MT_WARN : MT_BAD));
148 }
149}
150
151void mt_load_lines(mt_frame *f, const mt_load *l, bool compact, mt_lines *out) {
152 if (compact) {
153 tui_line s = MT_LINE(f, mt_label("GPU "), mt_share_span(f, l->share), mt_label(" CPU "));
154 cpu_spans(f, l, &s);
155 pressure_spans(f, l, &s);
156 tui_line_push(f->a, &s, mt_label(" meshd "));
157 tui_line_push(f->a, &s, mt_styled(mt_pct(f, l->meshd_cpu), mt_busy_style(l->meshd_cpu)));
158 mt_lines_push(f, out, s);
159 return;
160 }
161 tui_line g = MT_LINE(f, mt_label("GPU got "), mt_share_span(f, l->share), mt_label(" (10 s "), mt_share_span(f, l->share10),
162 mt_label(")"));
163 if (!l->share.some) {
164 tui_line_push(f->a, &g, mt_fgs("nothing observed (no idle node, no lease)", MT_DIM));
165 } else {
166 tui_line_push(f->a, &g, mt_label(" slice "));
167 tui_line_push(f->a, &g, mt_val(mt_fmt_us(f, l->slice_us)));
168 tui_line_push(f->a, &g, mt_label(" gap "));
169 tui_line_push(f->a, &g, mt_val(mt_fmt_us(f, l->gap_us)));
170 tui_line_push(f->a, &g, mt_label(" max "));
171 tui_line_push(f->a, &g, mt_fgs(mt_fmt_us(f, l->max_gap_us), l->max_gap_us > 10000.0f ? MT_BAD : MT_FG));
172 tui_line_push(f->a, &g, mt_label(" ×"));
173 tui_line_push(f->a, &g, mt_val(l->inflate.some ? mt_ff(f, l->inflate.v, 2) : "-"));
174 }
175 if (l->util_ext.some) {
176 tui_line_push(f->a, &g, mt_label(" busy "));
177 tui_line_push(f->a, &g, mt_fgs(mt_pct(f, l->util_ext.v), MT_DIM));
178 }
179 tui_line c = tui_line_span(f->a, mt_label("CPU "));
180 cpu_spans(f, l, &c);
181 tui_line_push(f->a, &c, mt_label(" load "));
182 tui_line_push(f->a, &c, mt_val(mt_ff(f, l->loadavg[0], 2)));
183 pressure_spans(f, l, &c);
184 tui_line m = MT_LINE(f, mt_label("mem "), mt_styled(mt_gb_pair(f, l->mem_avail_mb, l->mem_total_mb), mt_busy_style(1.0f - l->mem_free)),
185 mt_label(" avail"), mt_label(" meshd "), mt_styled(mt_pct(f, l->meshd_cpu), mt_busy_style(l->meshd_cpu)),
186 mt_fgs(MT_FMT(f, " %sM", mt_ff(f, l->meshd_rss_mb, 0)), MT_DIM), mt_label(" disk "),
187 mt_val(MT_FMT(f, "%sG", mt_ff(f, (float)l->disk_free_mb / 1024.0f, 0))), mt_label(" free"));
188 if (l->has_thermal) {
189 uint32_t t = l->thermal;
190 tui_line_push(f->a, &m, mt_label(" thermal "));
191 tui_line_push(f->a, &m, mt_fgs(t < MESH_THERMAL_STATES_LEN ? MESH_THERMAL_STATES[t] : "?", t == 0 ? MT_GOOD : t == 1 ? MT_WARN : MT_BAD));
192 } else if (l->temp_c.some) {
193 float t = l->temp_c.v;
194 tui_line_push(f->a, &m, mt_label(" "));
195 tui_line_push(f->a, &m, mt_fgs(MT_FMT(f, "%s°C", mt_ff(f, t, 0)), t >= 85.0f ? MT_BAD : MT_DIM));
196 }
197 mt_lines_push(f, out, g);
198 mt_lines_push(f, out, c);
199 mt_lines_push(f, out, m);
200}
201
202void mt_draw_load_box(mt_frame *f, tui_rect area, const mt_snapshot *s, const mt_lines *lines) {
203 if (area.height == 0 || area.width == 0) return;
204 mt_load l;
205 const char *age = "no sample ";
206 if (mt_snapshot_load(s, &l)) age = l.age_ms > 3000 ? MT_FMT(f, "stale %ss ", mt_ff(f, (float)l.age_ms / 1000.0f, 0)) : "";
207 tui_line title = MT_LINE(f, mt_fgb(" Load ", MT_FG), mt_fgs(age, MT_WARN));
208 tui_block b = tui_block_bordered();
209 b.set = &TUI_BORDER_ROUNDED;
210 b.border_style = tui_fg(MT_FAINT);
211 tui_block_add_title(f->a, &b, title, TUI_TITLE_DEFAULT);
212 tui_rect inner = tui_block_inner(&b, area);
213 mt_block(f, &b, area);
214 mt_par(f, mt_text(f, lines), inner);
215}
216
217/* One cell per logical CPU: a block whose height is its busy share, big cores bright, little ones dim. */
218static tui_span core_bar(uint8_t u, uint8_t c) {
219 static const char *const BARS[9] = {" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"};
220 if (u == 0xff) return mt_fgs("·", MT_FAINT);
221 size_t i = ((size_t)u * 8 + 50) / 100;
222 static const uint8_t little[3] = {110, 150, 200}, big[3] = {110, 190, 255};
223 tui_color col = u >= 90 ? MT_BAD : u >= 60 ? MT_WARN : mt_rgb(c == MESH_CORE_LITTLE ? little : big, c == MESH_CORE_LITTLE ? 0.7f : 1.0f);
224 return mt_styled(BARS[i < 8 ? i : 8], tui_fg_bg(col, MT_FAINT));
225}
226
227typedef struct {
228 const char *name;
229 uint16_t w;
230 uint8_t tier;
231} col;
232
233void mt_draw_load(mt_frame *f, tui_rect area, const mt_app *app) {
234 uint16_t n = (uint16_t)app->nmachines;
235 const mt_machine *m = mt_app_focused(app);
236 const char *pnames[MESH_PROG_SLOTS];
237 mt_prog_load ploads[MESH_PROG_SLOTS];
238 size_t nprogs = 0;
239 if (m->has_snap)
240 for (size_t i = 0; i < m->snap.nsections; i++) {
241 const mt_section *p = &m->snap.sections[i];
242 if (mt_snapshot_prog_load(&m->snap, mt_section_slot(p), &ploads[nprogs])) pnames[nprogs++] = mt_sec_name(f, p);
243 }
244 tui_rect r[4];
245 tui_constraint cons[4] = {TUI_LENGTH(tui_min16((uint16_t)(n + 3), area.height)), TUI_LENGTH(4), TUI_LENGTH(nprogs + 3), TUI_MAX(12)};
246 tui_vsplit(area, 4, cons, r);
247 tui_rect mach = r[0], cores = r[1], prog = r[2], hist = r[3];
248 /* Columns by tier: 0 always, 1 from 130 columns, 2 from 150. */
249 int tier = area.width >= 150 ? 2 : area.width >= 130 ? 1 : 0;
250 static const col cols[22] = {
251 {"machine", 14, 0}, {"GPU got", 7, 0}, {"10 s", 5, 0}, {"slice", 7, 1}, {"gap", 7, 1}, {"max gap", 7, 0},
252 {"gaps/s", 6, 2}, {"seen", 5, 2}, {"×lat", 5, 1}, {"queue", 6, 2}, {"busy ext", 8, 2}, {"CPU", 5, 0},
253 {"big", 5, 0}, {"little", 6, 0}, {"load", 5, 1}, {"PSI cpu/mem/io", 15, 0}, {"mem avail", 9, 0}, {"swap", 8, 2},
254 {"meshd", 5, 0}, {"rss", 5, 1}, {"disk free", 9, 1}, {"temp", 0, 1},
255 };
256 bool keep[22];
257 const char *header[22];
258 tui_constraint widths[22];
259 size_t nh = 0;
260 for (size_t i = 0; i < 22; i++) {
261 keep[i] = cols[i].tier <= tier;
262 if (keep[i]) {
263 widths[nh] = cols[i].w == 0 ? TUI_FILL(1) : TUI_LENGTH(cols[i].w);
264 header[nh++] = cols[i].name;
265 }
266 }
267 mt_rows rows = {0};
268 for (size_t i = 0; i < app->nmachines; i++) {
269 const mt_machine *mi = &app->machines[i];
270 tui_span name = mt_fgb(mt_name(f, mi), MT_MACHINE[i % 8]);
271 mt_load l;
272 if (!mi->has_snap || !mt_snapshot_load(&mi->snap, &l)) {
273 tui_table_cell c2[2] = {mt_cell_span(f, name), mt_cell_span(f, mt_fgs(mi->has_snap ? "no load sample" : mi->status, MT_DIM))};
274 mt_rows_push(f, &rows, mt_row(f, c2, 2));
275 continue;
276 }
277 bool g = l.share.some;
278 const char *psi;
279 if (l.has_psi)
280 psi = MT_FMT(f, "%s/%s/%s%%", mt_ff(f, l.psi[0] * 100.0f, 1), mt_ff(f, l.psi[1] * 100.0f, 1), mt_ff(f, l.psi[3] * 100.0f, 1));
281 else if (l.has_mem_level && l.mem_level == 1)
282 psi = "mem normal";
283 else if (l.has_mem_level && l.mem_level == 2)
284 psi = "mem warn";
285 else if (l.has_mem_level && l.mem_level == 4)
286 psi = "mem critical";
287 else
288 psi = "-";
289 const char *temp;
290 if (l.has_thermal)
291 temp = l.thermal < MESH_THERMAL_STATES_LEN ? MESH_THERMAL_STATES[l.thermal] : "?";
292 else if (l.temp_c.some)
293 temp = MT_FMT(f, "%s°C", mt_ff(f, l.temp_c.v, 0));
294 else
295 temp = "-";
296 tui_table_cell all[22] = {
297 mt_cell_span(f, name),
298 mt_cell_span(f, mt_share_span(f, l.share)),
299 mt_cell_span(f, mt_share_span(f, l.share10)),
300 mt_cell_str(f, g ? mt_fmt_us(f, l.slice_us) : "-"),
301 mt_cell_str(f, g ? mt_fmt_us(f, l.gap_us) : "-"),
302 mt_cell_span(f, mt_fgs(mt_fmt_us(f, l.max_gap_us), l.max_gap_us > 10000.0f ? MT_BAD : MT_FG)),
303 mt_cell_str(f, mt_ff(f, l.gaps_s, 0)),
304 mt_cell_span(f, mt_fgs(mt_ff(f, l.observed, 1), MT_DIM)),
305 mt_cell_str(f, l.inflate.some ? mt_ff(f, l.inflate.v, 2) : "-"),
306 mt_cell_str(f, l.queue_us > 0.0f ? mt_fmt_us(f, l.queue_us) : "-"),
307 mt_cell_str(f, l.util_ext.some ? mt_pct(f, l.util_ext.v) : "-"),
308 mt_cell_span(f, mt_styled(mt_pct(f, l.cpu), mt_busy_style(l.cpu))),
309 mt_cell_span(f, mt_styled(mt_pct(f, l.cpu_big), mt_busy_style(l.cpu_big))),
310 mt_cell_span(f, l.cpus_little > 0 ? mt_styled(mt_pct(f, l.cpu_little), mt_busy_style(l.cpu_little)) : mt_fgs("-", MT_FAINT)),
311 mt_cell_str(f, mt_ff(f, l.loadavg[0], 2)),
312 mt_cell_str(f, psi),
313 mt_cell_span(f, mt_styled(mt_gb_pair(f, l.mem_avail_mb, l.mem_total_mb), mt_busy_style(1.0f - l.mem_free))),
314 mt_cell_str(f, mt_gb_pair(f, l.swap_used_mb, l.swap_total_mb)),
315 mt_cell_span(f, mt_styled(mt_pct(f, l.meshd_cpu), mt_busy_style(l.meshd_cpu))),
316 mt_cell_str(f, MT_FMT(f, "%sM", mt_ff(f, l.meshd_rss_mb, 0))),
317 mt_cell_str(f, MT_FMT(f, "%sG", mt_ff(f, (float)l.disk_free_mb / 1024.0f, 0))),
318 mt_cell_str(f, temp),
319 };
320 tui_table_cell kept[22];
321 size_t nk = 0;
322 for (size_t c = 0; c < 22; c++)
323 if (keep[c]) kept[nk++] = all[c];
324 mt_rows_push(f, &rows, mt_row(f, kept, nk));
325 }
326 tui_line title = MT_LINE(f, mt_fgb(" load ", MT_FG),
327 mt_label("GPU got: the share of wall time our work ran (measured inside our kernels / per Metal command "
328 "buffer); ×lat = 1 / share "));
329 tui_block pb = mt_panel(f, title, MT_FAINT);
330 tui_table_row hdr = mt_header(f, header, nh);
331 tui_table t = tui_table_new(rows.rows, rows.n, widths, nh);
332 t.header = &hdr;
333 t.column_spacing = 1;
334 t.block = &pb;
335 mt_table(f, &t, mach);
336
337 /* The focused machine's cores. */
338 if (cores.height > 0) {
339 tui_line ctitle;
340 mt_lines lines = {0};
341 mt_load l;
342 if (m->has_snap && mt_snapshot_load(&m->snap, &l)) {
343 tui_line big = tui_line_span(f->a, mt_label(MT_FMT(f, "%s%s ", mt_pad(f, "big", 7, '<'), mt_pad(f, MT_FMT(f, "%u", l.cpus_big), 3, '>'))));
344 tui_line little = tui_line_span(f->a, mt_label(MT_FMT(f, "%s%s ", mt_pad(f, "little", 7, '<'), mt_pad(f, MT_FMT(f, "%u", l.cpus_little), 3, '>'))));
345 tui_line other = tui_line_span(f->a, mt_label(MT_FMT(f, "%s%s ", mt_pad(f, "cpus", 7, '<'), mt_pad(f, MT_FMT(f, "%u", l.cpus), 3, '>'))));
346 for (size_t i = 0; i < l.ncores; i++) {
347 tui_span bar = core_bar(l.cores[i][0], l.cores[i][1]);
348 if (l.cores[i][1] == MESH_CORE_BIG) tui_line_push(f->a, &big, bar);
349 else if (l.cores[i][1] == MESH_CORE_LITTLE) tui_line_push(f->a, &little, bar);
350 else tui_line_push(f->a, &other, bar);
351 }
352 if (l.cpus_little > 0) {
353 mt_lines_push(f, &lines, big);
354 mt_lines_push(f, &lines, little);
355 } else if (l.cpus_big > 0) {
356 mt_lines_push(f, &lines, big);
357 } else {
358 mt_lines_push(f, &lines, other);
359 }
360 ctitle = MT_LINE(f, mt_fgb(MT_FMT(f, " %s cores ", mt_name(f, m)), MT_FG), mt_label("busy per logical CPU, last second "));
361 } else {
362 ctitle = tui_line_span(f->a, mt_fgb(" cores ", MT_FG));
363 mt_lines_push(f, &lines, tui_line_span(f->a, mt_label("no load sample")));
364 }
365 tui_block cb = mt_panel(f, ctitle, MT_FAINT);
366 tui_paragraph p = tui_paragraph_new(mt_text(f, &lines));
367 p.block = &cb;
368 tui_paragraph_render(f->a, &p, cores, f->buf);
369 }
370
371 /* The focused machine's programs. */
372 if (prog.height > 0) {
373 static const char *const ph[15] = {"program", "GPU got", "10 s", "slice", "max gap", "seen", "p50", "p90",
374 "p99", "max", "n", "per task", "tasks/s", "busy", "queue"};
375 mt_rows prows = {0};
376 for (size_t i = 0; i < nprogs; i++) {
377 const mt_prog_load *l = &ploads[i];
378#define Q(x) ((x).some ? mt_fmt_us(f, (x).v) : "-")
379 tui_table_cell c[15] = {
380 mt_cell_span(f, mt_fgb(pnames[i], MT_FG)),
381 mt_cell_span(f, mt_share_span(f, l->share)),
382 mt_cell_span(f, mt_share_span(f, l->share10)),
383 mt_cell_str(f, l->share.some ? mt_fmt_us(f, l->slice_us) : "-"),
384 mt_cell_str(f, mt_fmt_us(f, l->max_gap_us)),
385 mt_cell_span(f, mt_fgs(mt_ff(f, l->observed, 1), MT_DIM)),
386 mt_cell_str(f, Q(l->p50_us)),
387 mt_cell_str(f, Q(l->p90_us)),
388 mt_cell_str(f, Q(l->p99_us)),
389 mt_cell_str(f, Q(l->max_us)),
390 mt_cell_span(f, mt_fgs(MT_FMT(f, "%u", l->lat_n), MT_DIM)),
391 mt_cell_str(f, Q(l->task_us)),
392 mt_cell_span(f, mt_fgs(mt_fmt_rate(f, l->tasks_s), MT_WARN)),
393 mt_cell_span(f, mt_styled(mt_pct(f, l->busy), mt_busy_style(l->busy))),
394 mt_cell_str(f, l->queue_us > 0.0f ? mt_fmt_us(f, l->queue_us) : "-"),
395 };
396#undef Q
397 mt_rows_push(f, &prows, mt_row(f, c, 15));
398 }
399 static const tui_constraint pw[15] = {
400 {TUI_C_LENGTH, 15, 0}, {TUI_C_LENGTH, 7, 0}, {TUI_C_LENGTH, 5, 0}, {TUI_C_LENGTH, 7, 0}, {TUI_C_LENGTH, 7, 0},
401 {TUI_C_LENGTH, 5, 0}, {TUI_C_LENGTH, 7, 0}, {TUI_C_LENGTH, 7, 0}, {TUI_C_LENGTH, 7, 0}, {TUI_C_LENGTH, 7, 0},
402 {TUI_C_LENGTH, 5, 0}, {TUI_C_LENGTH, 8, 0}, {TUI_C_LENGTH, 8, 0}, {TUI_C_LENGTH, 5, 0}, {TUI_C_FILL, 1, 0},
403 };
404 tui_line ptitle = MT_LINE(f, mt_fgb(MT_FMT(f, " %s programs ", mt_name(f, m)), MT_FG),
405 mt_label("task time from the trace (10 s), per task = node busy / tasks, seen = node-s observed idle per s "));
406 tui_block pb2 = mt_panel(f, ptitle, MT_FAINT);
407 tui_table_row phdr = mt_header(f, ph, 15);
408 tui_table pt = tui_table_new(prows.rows, prows.n, pw, 15);
409 pt.header = &phdr;
410 pt.column_spacing = 1;
411 pt.block = &pb2;
412 mt_table(f, &pt, prog);
413 }
414
415 /* GPU share and CPU history, one column per sample. */
416 if (hist.height > 2) {
417 size_t w = tui_sat_sub(hist.width, 2);
418 size_t nh2 = m->nload_hist;
419 uint64_t *share = tui_arena_alloc(f->a, (nh2 ? nh2 : 1) * sizeof *share);
420 for (size_t i = 0; i < nh2; i++) share[i] = m->load_hist[i][0];
421 size_t from = nh2 > w ? nh2 - w : 0;
422 uint64_t cpu = nh2 ? m->load_hist[nh2 - 1][1] : 0;
423 tui_line htitle = MT_LINE(f, mt_fgb(MT_FMT(f, " %s GPU got, %% per second ", mt_name(f, m)), MT_FG),
424 mt_label(MT_FMT(f, "CPU now %llu%% ", (unsigned long long)cpu)));
425 tui_block hb = mt_panel(f, htitle, MT_FAINT);
426 tui_sparkline sp = {0};
427 sp.data = share + from;
428 sp.n = nh2 - from;
429 sp.has_max = true;
430 sp.max = 100;
431 sp.style = tui_fg(MT_ACCENT);
432 sp.block = &hb;
433 tui_sparkline_render(f->a, &sp, hist, f->buf);
434 }
435}