Add mixed-vendor GPU summary and fix startup heap corruption

Collect AMD amdgpu sysfs and optional NVIDIA nvidia-smi stats in the
background, auto-detecting supported adapters in PCI order and rotating
additional pairs alongside CPU/RAM. Explicit PCI selection and off mode
remain supported; missing metrics render as N/A.

Initialize the bitmap-only canvas locally instead of the ABI-dependent
libg15render initializer: the installed TTF-enabled renderer wrote an
8-byte FreeType pointer past the smaller bitmap-only allocation, corrupting
the heap before GPU threads started. Add collector, rendering and full-main
regression tests.
This commit is contained in:
Mikhail Yevchenko committed 2026-09-07 17:06:32 +00:00
1 parent 076641c867
commit 7c06a8089d
12 files changed
+1417 -4

No files matched your search

+77
View File
@@ -0,0 +1,77 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Full application startup/draw loop, real libgtop and bitmap renderer.
* Only daemon transport is replaced. No TCP, USB, or input-device access.
* Default: GPU off, three frames. Optional: runtime-test auto 21 verifies
* real read-only GPU samples over twenty seconds (not a physical LCD test).
*/
#define main g15stats_application_main
#include "../g15stats.c"
#undef main
#include <assert.h>
static unsigned frames, target = 3;
static int peer = -1;
/* Catch the ABI-unsafe initializer even with a non-TTF renderer in CI.
* Do not stub any bitmap drawing or host statistics functions. */
void g15r_initCanvas(g15canvas *canvas)
{
(void)canvas;
fputs("FAIL: bitmap client called ABI-dependent g15r_initCanvas\n", stderr);
abort();
}
int new_g15_screen(int type)
{
int pair[2];
assert(type == G15_G15RBUF);
assert(!socketpair(AF_UNIX, SOCK_STREAM, 0, pair));
peer = pair[1]; /* No key events: actual keyboard thread blocks in recv. */
return pair[0];
}
int g15_send(int fd, char *buffer, unsigned int length)
{
unsigned ink = 0;
gpu_frame frame = gpu_snapshot();
(void)fd;
assert(length == G15_BUFFER_LEN);
for (unsigned i = 0; i < 160 * 43 / 8; i++) ink += !!buffer[i];
assert(ink);
fprintf(stderr, "runtime frame %u: %u ink bytes, %zu GPUs\n",
++frames, ink, frame.count);
if (frames == target) {
glibtop_cpu cpu;
glibtop_mem mem;
glibtop_get_cpu(&cpu);
glibtop_get_mem(&mem);
assert(cpu.total > 0 && mem.total > 0 && mem.free <= mem.total);
fprintf(stderr, "host stats: CPU ticks=%llu RAM total=%llu free=%llu\n",
(unsigned long long)cpu.total, (unsigned long long)mem.total,
(unsigned long long)mem.free);
for (size_t i = 0; i < frame.count; i++) {
gpu_sample s = frame.samples[i];
fprintf(stderr, "%s: busy=%lld used=%lld total=%lld temp=%lld\n",
gpu_devices[i].bus, s.busy, s.used, s.total, s.temp);
assert(s.busy >= 0 && s.used >= 0 && s.total > 0 && s.temp != LLONG_MIN);
}
assert(!gpu_stop());
/* Full main is intentionally endless. Terminate without closing the
* peer first (which would wake the legacy keyboard loop with EOF). */
fputs("PASS full main, isolated transport, real renderer/collectors\n", stderr);
_Exit(0);
}
return length;
}
int main(int argc, char **argv)
{
char *args[] = {"g15stats", "--gpu", argc > 1 ? argv[1] : "off", NULL};
if (argc > 2) {
long n = gpu_number(argv[2], 3, 30, -1);
assert(n >= 3);
target = (unsigned)n;
}
alarm(target + 10); /* Failure bound, not a passing timeout. */
return g15stats_application_main(3, args);
}