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:
1 parent
076641c867
commit
7c06a8089d
12 files changed
+1417
-4
No files matched your search
+119
-4
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user