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

+119 -4
View File
@@ -55,6 +55,53 @@ This is a simple stats client showing graphs for CPU, MEM & Swap usage, Network
#include <glibtop/uptime.h>
#include <glibtop/sysinfo.h>
#include "g15stats.h"
#include "gpu.h"
/* Main-thread-only summary geometry; other screens retain their full width. */
static int summary_gpu;
static void init_canvas(g15canvas *canvas)
{
/* We use bitmap fonts only. libg15render's g15r_initCanvas can write a
* FreeType handle beyond sizeof(g15canvas) when the library was built
* with TTF_SUPPORT but its consumer was not. Initialize our own storage
* instead; the bitmap API uses only the common buffer/mode prefix. */
memset(canvas, 0, sizeof(*canvas));
}
static void gpu_lines(size_t index, gpu_sample sample, char lines[5][32])
{
char memory[24];
snprintf(lines[0], 32, "%s%zu %.7s",
gpu_devices[index].backend == 1 ? "AMD" : gpu_devices[index].backend == 2 ? "NV" : "GPU",
index + 1, gpu_devices[index].bus + 5);
snprintf(lines[1], sizeof(lines[1]), "UTL N/A");
if (sample.busy >= 0)
snprintf(lines[1], sizeof(lines[1]), "UTL %lld%%", sample.busy);
gpu_memory(memory, sizeof(memory), sample.used);
snprintf(lines[2], sizeof(lines[2]), "U %s", memory);
gpu_memory(memory, sizeof(memory), sample.total);
snprintf(lines[3], sizeof(lines[3]), "T %s", memory);
snprintf(lines[4], sizeof(lines[4]), "TEM N/A");
if (sample.temp != LLONG_MIN)
snprintf(lines[4], sizeof(lines[4]), "TEM %lldC", sample.temp);
}
static void draw_gpu_column(g15canvas *canvas)
{
gpu_frame frame = gpu_snapshot();
size_t first = gpu_display_first(frame.count, gpu_milliseconds());
size_t i;
for (i = first; i < frame.count && i < first + 2; i++) {
char lines[5][32];
int row, x = frame.count == 1 ? 108 : 54 + (int)(i - first) * 54;
gpu_lines(i, frame.samples[i], lines);
g15r_drawLine(canvas, x - 3, 0, x - 3, 34, G15_COLOR_BLACK);
for (row = 0; row < 5; row++)
g15r_renderString(canvas, (unsigned char *)lines[row], 0,
G15_TEXT_SMALL, x, 1 + row * 7);
}
}
int g15screen_fd;
@@ -213,7 +260,7 @@ void print_vert_label_logic(g15canvas *canvas, char *label, unsigned int sx){
}
void print_vert_label(g15canvas *canvas, char *label){
print_vert_label_logic(canvas, label, TEXT_RIGHT);
print_vert_label_logic(canvas, label, summary_gpu ? 98 : TEXT_RIGHT);
}
void drawBar_reversed (g15canvas * canvas, int x1, int y1, int x2, int y2, int color,
@@ -264,8 +311,9 @@ void drawBar_reversed (g15canvas * canvas, int x1, int y1, int x2, int y2, int c
}
void drawBar_both(g15canvas *canvas, int y1, int y2, int bar_current, int bar_total, int rbar_current, int rbar_total) {
g15r_drawBar(canvas, BAR_START, y1, BAR_END, y2, G15_COLOR_BLACK, bar_current, bar_total, 4);
drawBar_reversed(canvas, BAR_START, y1, BAR_END, y2, G15_COLOR_BLACK, rbar_current, rbar_total, 5);
int end = summary_gpu ? 96 : BAR_END;
g15r_drawBar(canvas, BAR_START, y1, end, y2, G15_COLOR_BLACK, bar_current, bar_total, 4);
drawBar_reversed(canvas, BAR_START, y1, end, y2, G15_COLOR_BLACK, rbar_current, rbar_total, 5);
}
void drawLine_both(g15canvas *canvas, int y1, int y2) {
@@ -826,6 +874,42 @@ void draw_cpu_screen_unicore(g15canvas *canvas, char *tmpstr, int drawgraph, int
draw_cpu_screen_unicore_logic(canvas, cpu, tmpstr, drawgraph, printlabels, 0);
}
/* Two 52px GPU columns leave 51px for a compact CPU/system-RAM summary.
* Keep this separate from the legacy geometry and generic sensor probing. */
static void compact_ram_lines(char lines[5][32], long long used, long long total)
{
char value[24];
gpu_memory(value, sizeof(value), used);
snprintf(lines[1], 32, "R %s", value);
gpu_memory(value, sizeof(value), total);
snprintf(lines[2], 32, "T %s", value);
}
static void draw_compact_summary(g15canvas *canvas)
{
glibtop_cpu cpu;
glibtop_mem mem;
glibtop_swap swap;
static guint64 last_total, last_idle;
char lines[5][32];
int row, rows = mode[SCREEN_SUMMARY] ? 4 : 5;
glibtop_get_cpu(&cpu);
glibtop_get_mem(&mem);
glibtop_get_swap(&swap);
snprintf(lines[0], 32, "CPU N/A");
if (cpu.total > last_total && cpu.idle >= last_idle && cpu.idle - last_idle <= cpu.total - last_total)
snprintf(lines[0], 32, "CPU %.0f%%", 100.0 * (1.0 - (double)(cpu.idle - last_idle) / (cpu.total - last_total)));
last_total = cpu.total;
last_idle = cpu.idle;
compact_ram_lines(lines, mem.total - mem.free, mem.total);
snprintf(lines[3], 32, "SWP %.0f%%", swap.total ? 100.0 * swap.used / swap.total : 0.0);
snprintf(lines[4], 32, "GPUs %zu", gpu_count);
g15r_clearScreen(canvas, G15_COLOR_WHITE);
for (row = 0; row < rows; row++)
g15r_renderString(canvas, (unsigned char *)lines[row], 0,
G15_TEXT_SMALL, 1, 1 + row * (rows == 5 ? 7 : 9));
}
void draw_cpu_screen_multicore(g15canvas *canvas, char *tmpstr, int unicore) {
glibtop_cpu cpu;
int core,ncpumax;
@@ -839,6 +923,10 @@ void draw_cpu_screen_multicore(g15canvas *canvas, char *tmpstr, int unicore) {
b_nice[GLIBTOP_NCPU],b_sys[GLIBTOP_NCPU],b_idle[GLIBTOP_NCPU],
b_irq[GLIBTOP_NCPU],b_iowait[GLIBTOP_NCPU];
if (summary_gpu && gpu_count > 1 && cycle == SCREEN_SUMMARY) {
draw_compact_summary(canvas);
return;
}
init_cpu_count();
ncpumax = ncpu;
@@ -1694,8 +1782,21 @@ int main(int argc, char *argv[]){
unsigned char interface[128];
static char tmpstr[MAX_LINES];
int unicore = 0;
const char *gpu_selection = "auto";
for (i=0;i<argc;i++) {
if (!strcmp(argv[i], "--gpu") || !strcmp(argv[i], "-gpu")) {
if (++i >= argc) {
fprintf(stderr, "--gpu requires auto, off or a PCI address\n");
return 1;
}
gpu_selection = argv[i];
if (!gpu_selection_valid(gpu_selection)) {
fprintf(stderr, "Invalid GPU selection; use auto, off or dddd:bb:ss.f\n");
return 1;
}
continue;
}
if(0==strncmp(argv[i],"-d",2)||0==strncmp(argv[i],"--daemon",8)) {
go_daemon=1;
}
@@ -1722,6 +1823,8 @@ int main(int argc, char *argv[]){
printf("%s %s - (c) 2008-2010 Mike Lampard, Piotr Czarnecki\n",PACKAGE_NAME,VERSION);
printf("Usage: %s [Options]\n", PACKAGE_NAME);
printf("Options:\n");
printf("--gpu (-gpu) [auto|off|dddd:bb:ss.f] GPU columns (default: auto, all supported GPUs).\n");
printf(" Two GPUs side by side; more cycle in PCI-sorted pairs every 5 seconds.\n");
printf("--daemon (-d) run in background\n");
printf("--unicore (-u) display unicore graphs only on the CPU screen\n");
printf("--help (-h) this help text.\n");
@@ -1785,6 +1888,13 @@ int main(int argc, char *argv[]){
}
}
}
if (gpu_detect(gpu_selection)) {
fprintf(stderr, "GPU enumeration failed or exceeds %d adapters; use --gpu PCI or off\n", GPU_MAX);
return 1;
}
for (size_t gpu = 0; gpu < gpu_count; gpu++)
fprintf(stderr, "GPU %zu: %s (%s)\n", gpu + 1, gpu_devices[gpu].bus,
gpu_devices[gpu].backend == 1 ? "AMD" : gpu_devices[gpu].backend == 2 ? "NV" : "unsupported/missing");
if((g15screen_fd = new_g15_screen(G15_G15RBUF))<0){
printf("Sorry, cant connect to the G15daemon\n");
return -1;
@@ -1795,11 +1905,12 @@ int main(int argc, char *argv[]){
daemonise(0,0);
if(canvas != NULL)
g15r_initCanvas(canvas);
init_canvas(canvas);
else
return -1;
glibtop_init();
if (gpu_start()) fprintf(stderr, "GPU collector unavailable; affected cards display N/A\n");
pthread_create(&keys_thread,NULL,(void*)keyboard_watch,NULL);
if(have_nic==1)
@@ -1813,6 +1924,7 @@ int main(int argc, char *argv[]){
int cycle_old = cycle;
while(1) {
calc_info_cycle();
summary_gpu = cycle == SCREEN_SUMMARY && gpu_count;
switch (cycle) {
case SCREEN_SUMMARY:
case SCREEN_CPU:
@@ -1919,6 +2031,8 @@ int main(int argc, char *argv[]){
break;
}
cycle_old = cycle;
if (summary_gpu) draw_gpu_column(canvas);
summary_gpu = 0;
print_info_label(canvas, tmpstr);
canvas->mode_xor = 0;
@@ -1926,6 +2040,7 @@ int main(int argc, char *argv[]){
g15_send(g15screen_fd,(char *)canvas->buffer,G15_BUFFER_LEN);
g15stats_wait(wait_seconds);
}
gpu_stop();
glibtop_close();
close(g15screen_fd);