Compare commits
21
Commits
3.0.2
..
7c06a8089d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c06a8089d | ||
|
|
076641c867 | ||
|
|
95de11a526 | ||
|
|
c0ca57d355 | ||
|
|
c1a212eb1e | ||
|
|
025a5cd5ba | ||
|
|
7c7fd3b605 | ||
|
|
e96d818469 | ||
|
|
a906005a87 | ||
|
|
05ca759c7b | ||
|
|
b749ed8ad5 | ||
|
|
9dda88635a | ||
|
|
7ad83e1529 | ||
|
|
ee4adb969b | ||
|
|
2e4ba7177a | ||
|
|
cef3398d88 | ||
|
|
9f0f01ed9e | ||
|
|
b12478b3b8 | ||
|
|
1c3f49f905 | ||
|
|
2ba45c8e9a | ||
|
|
bd01f2ab24 |
No files matched your search
@@ -5,4 +5,4 @@ Antonio Bartolini <robynhub@gmail.com>
|
|||||||
|
|
||||||
Piotr Czarnecki <czarnyckm@users.sf.net>
|
Piotr Czarnecki <czarnyckm@users.sf.net>
|
||||||
|
|
||||||
Daniel Menelkir <menelkir@itroll.org>
|
Daniel Menelkir <dmenelkir@gmail.com>
|
||||||
+13
@@ -11,5 +11,18 @@ LIBS = -lg15daemon_client -lg15render -lpthread -lm -lrt
|
|||||||
IMCLUDES = -I
|
IMCLUDES = -I
|
||||||
g15stats_LDFLAGS = -avoid-version
|
g15stats_LDFLAGS = -avoid-version
|
||||||
g15stats_SOURCES = g15stats.c g15stats.h
|
g15stats_SOURCES = g15stats.c g15stats.h
|
||||||
|
EXTRA_DIST = gpu.h tests/gpu-test.c tests/render-test.c tests/runtime-test.c tests/render-layouts.sh tests/render-images.sh
|
||||||
|
noinst_HEADERS += gpu.h
|
||||||
|
AUTOMAKE_OPTIONS = subdir-objects
|
||||||
|
check_PROGRAMS = gpu-test render-test runtime-test
|
||||||
|
TESTS = $(check_PROGRAMS) tests/render-layouts.sh
|
||||||
|
TEST_EXTENSIONS = .sh
|
||||||
|
SH_LOG_COMPILER = $(SHELL)
|
||||||
|
gpu_test_SOURCES = tests/gpu-test.c
|
||||||
|
gpu_test_LDADD = -lpthread
|
||||||
|
render_test_SOURCES = tests/render-test.c
|
||||||
|
render_test_LDADD = -lg15daemon_client -lg15render -lpthread -lm -lrt $(GTOP_LIBS)
|
||||||
|
runtime_test_SOURCES = tests/runtime-test.c
|
||||||
|
runtime_test_LDADD = -lg15render -lpthread -lm -lrt $(GTOP_LIBS)
|
||||||
|
|
||||||
|
|
||||||
@@ -2,6 +2,113 @@ G15Stats - a CPU/Memory/Swap/Network/Battery/Temperature/Fan Speed/CPU Frequenci
|
|||||||
|
|
||||||
The G15Stats requires libgtop development packages to be installed before compilation.
|
The G15Stats requires libgtop development packages to be installed before compilation.
|
||||||
|
|
||||||
|
GPU summary columns:
|
||||||
|
The default --gpu auto collects ALL PCI devices (sorted PCI address) bound
|
||||||
|
to amdgpu or nvidia at startup, including mixed vendors. Use --gpu dddd:bb:ss.f (also -gpu), for example
|
||||||
|
--gpu 0000:01:00.0, to select a particular card. --gpu off restores the original
|
||||||
|
layout and disables collection. An explicit missing/unsupported card shows N/A;
|
||||||
|
auto with no supported card leaves the original layout unchanged. Restart to
|
||||||
|
rediscover cards after hotplug. Intel and the nouveau/radeon drivers are not
|
||||||
|
supported. Missing tools, permissions or individual metrics produce N/A, not 0.
|
||||||
|
|
||||||
|
With one GPU the 160x43 summary retains its existing 4/5 indicator rows and
|
||||||
|
labels. Graphs end at x=96 instead of 153; TOTAL moves to x=98. A divider
|
||||||
|
at x=105 separates the column at x=108..159. With two or more GPUs the left
|
||||||
|
x=0..50 shows compact CPU percent, R used system RAM, T total system RAM,
|
||||||
|
SWP percent, and (in five-row mode) GPU count. Four-row mode uses y=1,10,19,28;
|
||||||
|
five-row mode uses y=1,8,15,22,29. System RAM uses the same explicit units as VRAM.
|
||||||
|
Two GPU columns at x=54..104 and x=108..159 are visible together, with dividers
|
||||||
|
at x=51 and x=105 (the first column's glyphs end before its right divider).
|
||||||
|
Each GPU has five lines at y=1,8,15,22,29: vendor/index plus PCI bus:slot.function,
|
||||||
|
UTL percent, U used VRAM, T total VRAM, TEM temperature in Celsius (C).
|
||||||
|
For example NV1 01:00.0 and AMD2 11:00.0 identify different cards. Indices are
|
||||||
|
one-based positions in the selected PCI-sorted inventory, not NVIDIA ordinals;
|
||||||
|
startup diagnostics map each index to its full domain:bus:slot.function address.
|
||||||
|
More than two GPUs cycle through consecutive pairs every five seconds from
|
||||||
|
startup (1/2, 3/4, ..., then 1/2); the last odd card occupies the first column.
|
||||||
|
All cards are collected continuously, not just the visible pair. The display
|
||||||
|
refresh interval can delay a page change: each draw advances at most one pair,
|
||||||
|
so slow refresh never skips pairs. Inventory is bounded at 64 cards;
|
||||||
|
overflow/enumeration failure is an explicit startup error, never silent omission.
|
||||||
|
Use explicit PCI selection or off to recover. Hotplug requires restart.
|
||||||
|
The bottom info bar at y=37..42 and all non-summary screens retain their geometry.
|
||||||
|
Used and total are on separate lines so each retains an explicit MiB/GiB unit
|
||||||
|
(TiB/PiB for very large values), rounded to one decimal. Other screens do not
|
||||||
|
change. U/T refer to VRAM, not memory-controller utilization or system RAM;
|
||||||
|
on AMD APUs the driver may report only the reserved VRAM carveout.
|
||||||
|
|
||||||
|
AMD reads gpu_busy_percent, mem_info_vram_used/total (bytes), and the selected
|
||||||
|
PCI device's hwmon/temp1_input (millidegrees C). NVIDIA optionally executes
|
||||||
|
/usr/bin/nvidia-smi with pci.bus_id,utilization.gpu,memory.used,memory.total,temperature.gpu
|
||||||
|
and --format=csv,noheader,nounits: one query for all NVIDIA cards, matched by PCI
|
||||||
|
identity (including NVIDIA's eight-digit domain form), not response order. No CUDA/NVML SDK
|
||||||
|
or additional build dependency is needed. Run as an ordinary user.
|
||||||
|
|
||||||
|
Independent AMD workers and one batched NVIDIA worker wait two seconds between
|
||||||
|
queries; drawing uses only a mutex-protected snapshot array. A stalled AMD read
|
||||||
|
does not block another card or NVIDIA. NVIDIA queries share one 750ms monotonic deadline, fixed
|
||||||
|
arguments/environment, no shell, bounded output, and SIGKILL on timeout or
|
||||||
|
parent death. An unreaped child prevents further launches. Samples older than
|
||||||
|
five seconds display N/A per card, including when a sysfs read stalls. Worker
|
||||||
|
shutdown has one shared one-second join/reap budget; a stuck kernel read can
|
||||||
|
outlive that budget in static storage until process exit (no restart after a
|
||||||
|
failed stop). Normal application termination exits the process; no driver waits
|
||||||
|
are added to signal handling. The -r display
|
||||||
|
refresh interval still applies independently. No GPU settings are written.
|
||||||
|
|
||||||
|
Official interface references:
|
||||||
|
https://docs.kernel.org/gpu/amdgpu/thermal.html
|
||||||
|
https://docs.kernel.org/gpu/amdgpu/driver-misc.html
|
||||||
|
https://docs.nvidia.com/deploy/nvidia-smi/index.html
|
||||||
|
|
||||||
|
GPU validation:
|
||||||
|
After configuring/building, make check runs compiled parsing, units, missing-data,
|
||||||
|
expiry, selection, subprocess deadline/reaping/parent-death and descriptor checks,
|
||||||
|
plus real libg15render buffer checks. No daemon, input devices or synthetic GPU
|
||||||
|
sysfs fixtures are used. ./gpu-test auto additionally checks every real GPU,
|
||||||
|
its sysfs vendor identity and background cache (requires all four metrics).
|
||||||
|
Explicit PCI arguments still test one card. ./render-test auto output writes
|
||||||
|
real live 160x43 buffers to output-0.pgm and output-1.pgm (five/four rows), plus
|
||||||
|
output (four rows). ./render-test off output renders the original GPU-off layout.
|
||||||
|
For in-memory cases, ./render-test off output COUNT PAGE [large|unknown] selects
|
||||||
|
1..64 cards and a zero-based pair page. Omit PAGE to export the last pair.
|
||||||
|
The default values exercise LLONG_MAX VRAM and -273C/1000C; large uses
|
||||||
|
100.0/192.0GiB and 1000C, while unknown uses N/A for all GPU metrics.
|
||||||
|
For example ./render-test off output 2 tests worst-width dual-card values;
|
||||||
|
./render-test off output 3 1 tests the odd last page. With no arguments,
|
||||||
|
make check retains the worst-width two-card case. These are rendering inputs,
|
||||||
|
not simulated hardware or claims about the installed GPUs.
|
||||||
|
These do not connect to g15daemon and are not physical LCD tests.
|
||||||
|
|
||||||
|
make check also runs tests/render-layouts.sh for off, single, dual, large,
|
||||||
|
unknown, odd-last and 64-card layouts, each in both summary modes. In addition
|
||||||
|
to isolated-column comparisons, the test compares final GPU text, vertical
|
||||||
|
TOTAL and time-footer cells directly with the installed font's bitmap bits.
|
||||||
|
This independent oracle includes blank cells and checks full glyph bounds;
|
||||||
|
repeating renderString on another canvas alone cannot detect shared clipping.
|
||||||
|
Canvas initialization is tested starting from poisoned storage.
|
||||||
|
|
||||||
|
Startup regression: make check also runs runtime-test through the full main
|
||||||
|
loop for three frames with real libgtop and bitmap rendering. Only daemon
|
||||||
|
transport is replaced by a private socketpair; no keyboard events are sent.
|
||||||
|
./runtime-test auto 21 additionally requires valid live GPU samples after
|
||||||
|
twenty seconds. This is not a physical LCD or real daemon integration test.
|
||||||
|
The bitmap-only client initializes its own canvas, avoiding g15r_initCanvas:
|
||||||
|
a TTF-enabled renderer can otherwise write beyond a non-TTF caller's canvas
|
||||||
|
and corrupt malloc metadata at startup, even with --gpu off.
|
||||||
|
|
||||||
|
Each visual export also writes output-0.bin and output-1.bin: the actual 860
|
||||||
|
packed framebuffer bytes (one means black). With ImageMagick installed, run
|
||||||
|
sh tests/render-images.sh /absolute/output/directory after generating frames
|
||||||
|
there. It compares packed PBM, PGM and native PNG pixels, generates exact
|
||||||
|
nearest-neighbor 8x images, and checks the 8x-to-native round trip. Per-scenario
|
||||||
|
contact sheets use 4x tiles with labels outside the LCD and one column, avoiding
|
||||||
|
very wide sheets that viewers may downscale. No trim or crop is applied.
|
||||||
|
Inspect the individual native and 8x files as well as the contact sheets.
|
||||||
|
The supplied default small font has only three ink columns plus a blank column
|
||||||
|
per character; its N, M and other shapes can look incomplete when enlarged.
|
||||||
|
Use the bitmap checks to distinguish those font shapes from missing pixels.
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
Summary Screen displays 4 or 5 indicators from CPU/Frequency/Memory/NET/Temperature/Fan Speed/Swap,
|
Summary Screen displays 4 or 5 indicators from CPU/Frequency/Memory/NET/Temperature/Fan Speed/Swap,
|
||||||
along with current time.
|
along with current time.
|
||||||
|
|||||||
+41
-11
@@ -3,6 +3,13 @@ G15STATS
|
|||||||
|
|
||||||
A CPU/Memory/Swap/Network/Battery/Temperature/Fan Speed/CPU Frequencies usage meter for G15Daemon.
|
A CPU/Memory/Swap/Network/Battery/Temperature/Fan Speed/CPU Frequencies usage meter for G15Daemon.
|
||||||
|
|
||||||
|
=======
|
||||||
|
Warning
|
||||||
|
=======
|
||||||
|
I'm discontinuing this after someone made a fuzz about a feature he decided he want a decade later.
|
||||||
|
And as far I am concerned, Arch Linux's AUR administrators find this behavior just fine, so I'm not wasting my efforts on this anymore.
|
||||||
|
I can still fix issues as I always did and help via mail, but keep in mind Arch Linux is impossible to be supported.
|
||||||
|
|
||||||
========
|
========
|
||||||
Features
|
Features
|
||||||
========
|
========
|
||||||
@@ -23,6 +30,40 @@ Requirements
|
|||||||
|
|
||||||
- libgtop
|
- libgtop
|
||||||
|
|
||||||
|
GPU monitoring
|
||||||
|
--------------
|
||||||
|
|
||||||
|
The summary automatically collects ALL supported PCI addresses (amdgpu or nvidia),
|
||||||
|
including mixed vendors. ``--gpu dddd:bb:ss.f`` (alias ``-gpu``) selects
|
||||||
|
a card; ``--gpu off`` disables collection and restores the original layout.
|
||||||
|
``--gpu auto`` is the default. Intel, nouveau and radeon are not supported.
|
||||||
|
Missing metrics show N/A. No supported GPU in auto mode leaves the layout intact.
|
||||||
|
An explicitly selected missing card keeps an N/A column. Selection is at startup.
|
||||||
|
|
||||||
|
Each column shows vendor/index and PCI bus:slot.function, UTL %, U used VRAM, T total VRAM, and TEM in C on five
|
||||||
|
lines. VRAM uses explicit MiB/GiB units (TiB/PiB if needed), with one decimal.
|
||||||
|
One GPU retains summary labels and shortened graphs. Two GPUs appear together
|
||||||
|
beside compact CPU/RAM-used/RAM-total/SWP rows (plus GPU count in five-row mode).
|
||||||
|
RAM also has explicit units. More than two cycle in PCI-sorted pairs every five
|
||||||
|
seconds from startup; all continue collecting. NV1/AMD2-style indices are one-based
|
||||||
|
selected-inventory positions, mapped to full PCI addresses in startup diagnostics.
|
||||||
|
The 64-card bound fails explicitly on overflow. The bottom bar and other screens
|
||||||
|
are unchanged. AMD APU VRAM may mean its reserved
|
||||||
|
carveout, not all shared system memory.
|
||||||
|
|
||||||
|
AMD uses read-only PCI-device sysfs/hwmon files. NVIDIA uses the optional
|
||||||
|
``/usr/bin/nvidia-smi`` without a shell or SDK dependency. Independent AMD workers
|
||||||
|
and one PCI-keyed all-NVIDIA query wait two seconds between samples, with a shared
|
||||||
|
750ms NVIDIA deadline and per-card five-second cache expiry. Shutdown has one
|
||||||
|
one-second worker join/reap budget. Display refresh remains controlled by ``-r``. See README for
|
||||||
|
exact layout, interfaces and subprocess lifecycle details. Run as an ordinary user.
|
||||||
|
|
||||||
|
``make check`` covers GPU-off, single-card and multi-card native framebuffers.
|
||||||
|
Final GPU, vertical TOTAL and time-footer glyphs are checked against font bitmap
|
||||||
|
bits independently of the text renderer. See README for packed framebuffer
|
||||||
|
exports and ``tests/render-images.sh``, which generates verified native/nearest-8x
|
||||||
|
PNGs and narrow contact sheets without cropping the LCD area.
|
||||||
|
|
||||||
=======
|
=======
|
||||||
Options
|
Options
|
||||||
=======
|
=======
|
||||||
@@ -50,14 +91,3 @@ Once running, the separate screens can be switched to as follows:
|
|||||||
| ``L3``: Next Screen
|
| ``L3``: Next Screen
|
||||||
| ``L4``: Alternative Screen (Doesn't work on Swap, Memory and Battery Screen)
|
| ``L4``: Alternative Screen (Doesn't work on Swap, Memory and Battery Screen)
|
||||||
| ``L5``: Bottom Info bar mode
|
| ``L5``: Bottom Info bar mode
|
||||||
|
|
||||||
=============
|
|
||||||
How to donate
|
|
||||||
=============
|
|
||||||
|
|
||||||
If you find this repo useful (don't forget to pay a visit to the related
|
|
||||||
repos too), you can buy me a beer:
|
|
||||||
|
|
||||||
:BTC: 3ECzX5UhcFSRv6gBBYLNBc7zGP9UA5Ppmn
|
|
||||||
|
|
||||||
:ETH: 0x7E17Ac09Fa7e6F80284a75577B5c1cbaAD566C1c
|
|
||||||
Vendored
+17
-10
@@ -1,8 +1,8 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Attempt to guess a canonical system name.
|
# Attempt to guess a canonical system name.
|
||||||
# Copyright 1992-2020 Free Software Foundation, Inc.
|
# Copyright 1992-2021 Free Software Foundation, Inc.
|
||||||
|
|
||||||
timestamp='2020-11-19'
|
timestamp='2021-04-21'
|
||||||
|
|
||||||
# This file is free software; you can redistribute it and/or modify it
|
# This file is free software; you can redistribute it and/or modify it
|
||||||
# under the terms of the GNU General Public License as published by
|
# under the terms of the GNU General Public License as published by
|
||||||
@@ -50,7 +50,7 @@ version="\
|
|||||||
GNU config.guess ($timestamp)
|
GNU config.guess ($timestamp)
|
||||||
|
|
||||||
Originally written by Per Bothner.
|
Originally written by Per Bothner.
|
||||||
Copyright 1992-2020 Free Software Foundation, Inc.
|
Copyright 1992-2021 Free Software Foundation, Inc.
|
||||||
|
|
||||||
This is free software; see the source for copying conditions. There is NO
|
This is free software; see the source for copying conditions. There is NO
|
||||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||||
@@ -188,10 +188,9 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
|
|||||||
#
|
#
|
||||||
# Note: NetBSD doesn't particularly care about the vendor
|
# Note: NetBSD doesn't particularly care about the vendor
|
||||||
# portion of the name. We always set it to "unknown".
|
# portion of the name. We always set it to "unknown".
|
||||||
sysctl="sysctl -n hw.machine_arch"
|
|
||||||
UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \
|
UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \
|
||||||
"/sbin/$sysctl" 2>/dev/null || \
|
/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
|
||||||
"/usr/sbin/$sysctl" 2>/dev/null || \
|
/usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
|
||||||
echo unknown))
|
echo unknown))
|
||||||
case "$UNAME_MACHINE_ARCH" in
|
case "$UNAME_MACHINE_ARCH" in
|
||||||
aarch64eb) machine=aarch64_be-unknown ;;
|
aarch64eb) machine=aarch64_be-unknown ;;
|
||||||
@@ -262,6 +261,10 @@ case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
|
|||||||
UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//')
|
UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//')
|
||||||
echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
|
echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
|
||||||
exit ;;
|
exit ;;
|
||||||
|
*:SecBSD:*:*)
|
||||||
|
UNAME_MACHINE_ARCH=$(arch | sed 's/SecBSD.//')
|
||||||
|
echo "$UNAME_MACHINE_ARCH"-unknown-secbsd"$UNAME_RELEASE"
|
||||||
|
exit ;;
|
||||||
*:LibertyBSD:*:*)
|
*:LibertyBSD:*:*)
|
||||||
UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//')
|
UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//')
|
||||||
echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
|
echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
|
||||||
@@ -950,7 +953,7 @@ EOF
|
|||||||
if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
|
if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
|
||||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||||
exit ;;
|
exit ;;
|
||||||
arc:Linux:*:* | arceb:Linux:*:*)
|
arc:Linux:*:* | arceb:Linux:*:* | arc64:Linux:*:*)
|
||||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||||
exit ;;
|
exit ;;
|
||||||
arm*:Linux:*:*)
|
arm*:Linux:*:*)
|
||||||
@@ -996,6 +999,9 @@ EOF
|
|||||||
k1om:Linux:*:*)
|
k1om:Linux:*:*)
|
||||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||||
exit ;;
|
exit ;;
|
||||||
|
loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
|
||||||
|
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||||
|
exit ;;
|
||||||
m32r*:Linux:*:*)
|
m32r*:Linux:*:*)
|
||||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||||
exit ;;
|
exit ;;
|
||||||
@@ -1084,7 +1090,7 @@ EOF
|
|||||||
ppcle:Linux:*:*)
|
ppcle:Linux:*:*)
|
||||||
echo powerpcle-unknown-linux-"$LIBC"
|
echo powerpcle-unknown-linux-"$LIBC"
|
||||||
exit ;;
|
exit ;;
|
||||||
riscv32:Linux:*:* | riscv64:Linux:*:*)
|
riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
|
||||||
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
|
||||||
exit ;;
|
exit ;;
|
||||||
s390:Linux:*:* | s390x:Linux:*:*)
|
s390:Linux:*:* | s390x:Linux:*:*)
|
||||||
@@ -1480,8 +1486,8 @@ EOF
|
|||||||
i*86:rdos:*:*)
|
i*86:rdos:*:*)
|
||||||
echo "$UNAME_MACHINE"-pc-rdos
|
echo "$UNAME_MACHINE"-pc-rdos
|
||||||
exit ;;
|
exit ;;
|
||||||
i*86:AROS:*:*)
|
*:AROS:*:*)
|
||||||
echo "$UNAME_MACHINE"-pc-aros
|
echo "$UNAME_MACHINE"-unknown-aros
|
||||||
exit ;;
|
exit ;;
|
||||||
x86_64:VMkernel:*:*)
|
x86_64:VMkernel:*:*)
|
||||||
echo "$UNAME_MACHINE"-unknown-esx
|
echo "$UNAME_MACHINE"-unknown-esx
|
||||||
@@ -1696,3 +1702,4 @@ exit 1
|
|||||||
# time-stamp-format: "%:y-%02m-%02d"
|
# time-stamp-format: "%:y-%02m-%02d"
|
||||||
# time-stamp-end: "'"
|
# time-stamp-end: "'"
|
||||||
# End:
|
# End:
|
||||||
|
|
||||||
Vendored
+21
-10
@@ -1,8 +1,8 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Configuration validation subroutine script.
|
# Configuration validation subroutine script.
|
||||||
# Copyright 1992-2020 Free Software Foundation, Inc.
|
# Copyright 1992-2021 Free Software Foundation, Inc.
|
||||||
|
|
||||||
timestamp='2020-12-02'
|
timestamp='2021-04-30'
|
||||||
|
|
||||||
# This file is free software; you can redistribute it and/or modify it
|
# This file is free software; you can redistribute it and/or modify it
|
||||||
# under the terms of the GNU General Public License as published by
|
# under the terms of the GNU General Public License as published by
|
||||||
@@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
|
|||||||
version="\
|
version="\
|
||||||
GNU config.sub ($timestamp)
|
GNU config.sub ($timestamp)
|
||||||
|
|
||||||
Copyright 1992-2020 Free Software Foundation, Inc.
|
Copyright 1992-2021 Free Software Foundation, Inc.
|
||||||
|
|
||||||
This is free software; see the source for copying conditions. There is NO
|
This is free software; see the source for copying conditions. There is NO
|
||||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||||
@@ -1165,7 +1165,7 @@ case $cpu-$vendor in
|
|||||||
| alphapca5[67] | alpha64pca5[67] \
|
| alphapca5[67] | alpha64pca5[67] \
|
||||||
| am33_2.0 \
|
| am33_2.0 \
|
||||||
| amdgcn \
|
| amdgcn \
|
||||||
| arc | arceb \
|
| arc | arceb | arc64 \
|
||||||
| arm | arm[lb]e | arme[lb] | armv* \
|
| arm | arm[lb]e | arme[lb] | armv* \
|
||||||
| avr | avr32 \
|
| avr | avr32 \
|
||||||
| asmjs \
|
| asmjs \
|
||||||
@@ -1185,6 +1185,7 @@ case $cpu-$vendor in
|
|||||||
| k1om \
|
| k1om \
|
||||||
| le32 | le64 \
|
| le32 | le64 \
|
||||||
| lm32 \
|
| lm32 \
|
||||||
|
| loongarch32 | loongarch64 | loongarchx32 \
|
||||||
| m32c | m32r | m32rle \
|
| m32c | m32r | m32rle \
|
||||||
| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
|
| m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
|
||||||
| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
|
| m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
|
||||||
@@ -1203,9 +1204,13 @@ case $cpu-$vendor in
|
|||||||
| mips64vr5900 | mips64vr5900el \
|
| mips64vr5900 | mips64vr5900el \
|
||||||
| mipsisa32 | mipsisa32el \
|
| mipsisa32 | mipsisa32el \
|
||||||
| mipsisa32r2 | mipsisa32r2el \
|
| mipsisa32r2 | mipsisa32r2el \
|
||||||
|
| mipsisa32r3 | mipsisa32r3el \
|
||||||
|
| mipsisa32r5 | mipsisa32r5el \
|
||||||
| mipsisa32r6 | mipsisa32r6el \
|
| mipsisa32r6 | mipsisa32r6el \
|
||||||
| mipsisa64 | mipsisa64el \
|
| mipsisa64 | mipsisa64el \
|
||||||
| mipsisa64r2 | mipsisa64r2el \
|
| mipsisa64r2 | mipsisa64r2el \
|
||||||
|
| mipsisa64r3 | mipsisa64r3el \
|
||||||
|
| mipsisa64r5 | mipsisa64r5el \
|
||||||
| mipsisa64r6 | mipsisa64r6el \
|
| mipsisa64r6 | mipsisa64r6el \
|
||||||
| mipsisa64sb1 | mipsisa64sb1el \
|
| mipsisa64sb1 | mipsisa64sb1el \
|
||||||
| mipsisa64sr71k | mipsisa64sr71kel \
|
| mipsisa64sr71k | mipsisa64sr71kel \
|
||||||
@@ -1229,7 +1234,7 @@ case $cpu-$vendor in
|
|||||||
| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
|
| powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
|
||||||
| pru \
|
| pru \
|
||||||
| pyramid \
|
| pyramid \
|
||||||
| riscv | riscv32 | riscv64 \
|
| riscv | riscv32 | riscv32be | riscv64 | riscv64be \
|
||||||
| rl78 | romp | rs6000 | rx \
|
| rl78 | romp | rs6000 | rx \
|
||||||
| s390 | s390x \
|
| s390 | s390x \
|
||||||
| score \
|
| score \
|
||||||
@@ -1682,11 +1687,14 @@ fi
|
|||||||
|
|
||||||
# Now, validate our (potentially fixed-up) OS.
|
# Now, validate our (potentially fixed-up) OS.
|
||||||
case $os in
|
case $os in
|
||||||
# Sometimes we do "kernel-abi", so those need to count as OSes.
|
# Sometimes we do "kernel-libc", so those need to count as OSes.
|
||||||
musl* | newlib* | uclibc*)
|
musl* | newlib* | uclibc*)
|
||||||
;;
|
;;
|
||||||
# Likewise for "kernel-libc"
|
# Likewise for "kernel-abi"
|
||||||
eabi | eabihf | gnueabi | gnueabihf)
|
eabi* | gnueabi*)
|
||||||
|
;;
|
||||||
|
# VxWorks passes extra cpu info in the 4th filed.
|
||||||
|
simlinux | simwindows | spe)
|
||||||
;;
|
;;
|
||||||
# Now accept the basic system types.
|
# Now accept the basic system types.
|
||||||
# The portable systems comes first.
|
# The portable systems comes first.
|
||||||
@@ -1703,12 +1711,12 @@ case $os in
|
|||||||
| nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
|
| nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
|
||||||
| clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
|
| clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
|
||||||
| mirbsd* | netbsd* | dicos* | openedition* | ose* \
|
| mirbsd* | netbsd* | dicos* | openedition* | ose* \
|
||||||
| bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \
|
| bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \
|
||||||
| ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
|
| ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
|
||||||
| bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
|
| bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
|
||||||
| ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
|
| ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
|
||||||
| udi* | lites* | ieee* | go32* | aux* | hcos* \
|
| udi* | lites* | ieee* | go32* | aux* | hcos* \
|
||||||
| chorusrdb* | cegcc* | glidix* \
|
| chorusrdb* | cegcc* | glidix* | serenity* \
|
||||||
| cygwin* | msys* | pe* | moss* | proelf* | rtems* \
|
| cygwin* | msys* | pe* | moss* | proelf* | rtems* \
|
||||||
| midipix* | mingw32* | mingw64* | mint* \
|
| midipix* | mingw32* | mingw64* | mint* \
|
||||||
| uxpv* | beos* | mpeix* | udk* | moxiebox* \
|
| uxpv* | beos* | mpeix* | udk* | moxiebox* \
|
||||||
@@ -1750,6 +1758,8 @@ case $kernel-$os in
|
|||||||
;;
|
;;
|
||||||
kfreebsd*-gnu* | kopensolaris*-gnu*)
|
kfreebsd*-gnu* | kopensolaris*-gnu*)
|
||||||
;;
|
;;
|
||||||
|
vxworks-simlinux | vxworks-simwindows | vxworks-spe)
|
||||||
|
;;
|
||||||
nto-qnx*)
|
nto-qnx*)
|
||||||
;;
|
;;
|
||||||
os2-emx)
|
os2-emx)
|
||||||
@@ -1852,3 +1862,4 @@ exit
|
|||||||
# time-stamp-format: "%:y-%02m-%02d"
|
# time-stamp-format: "%:y-%02m-%02d"
|
||||||
# time-stamp-end: "'"
|
# time-stamp-end: "'"
|
||||||
# End:
|
# End:
|
||||||
|
|
||||||
Regular → Executable
File mode changed.
@@ -2,7 +2,7 @@
|
|||||||
# Guess values for system-dependent variables and create Makefiles.
|
# Guess values for system-dependent variables and create Makefiles.
|
||||||
# Generated by GNU Autoconf 2.71 for g15stats 3.0.
|
# Generated by GNU Autoconf 2.71 for g15stats 3.0.
|
||||||
#
|
#
|
||||||
# Report bugs to <menelkir@itroll.org>.
|
# Report bugs to <dmenelkir@gmail.com>.
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
|
# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
|
||||||
@@ -276,7 +276,7 @@ then :
|
|||||||
printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
|
printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
|
||||||
else
|
else
|
||||||
printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and
|
printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and
|
||||||
$0: menelkir@itroll.org about your system, including any
|
$0: dmenelkir@gmail.com about your system, including any
|
||||||
$0: error possibly output before this message. Then install
|
$0: error possibly output before this message. Then install
|
||||||
$0: a modern shell, or manually run the script under such a
|
$0: a modern shell, or manually run the script under such a
|
||||||
$0: shell if you do have one."
|
$0: shell if you do have one."
|
||||||
@@ -623,7 +623,7 @@ PACKAGE_NAME='g15stats'
|
|||||||
PACKAGE_TARNAME='g15stats'
|
PACKAGE_TARNAME='g15stats'
|
||||||
PACKAGE_VERSION='3.0'
|
PACKAGE_VERSION='3.0'
|
||||||
PACKAGE_STRING='g15stats 3.0'
|
PACKAGE_STRING='g15stats 3.0'
|
||||||
PACKAGE_BUGREPORT='menelkir@itroll.org'
|
PACKAGE_BUGREPORT='dmenelkir@gmail.com'
|
||||||
PACKAGE_URL=''
|
PACKAGE_URL=''
|
||||||
|
|
||||||
ac_default_prefix=/usr
|
ac_default_prefix=/usr
|
||||||
@@ -1485,7 +1485,7 @@ Some influential environment variables:
|
|||||||
Use these variables to override the choices made by `configure' or to help
|
Use these variables to override the choices made by `configure' or to help
|
||||||
it to find libraries and programs with nonstandard names/locations.
|
it to find libraries and programs with nonstandard names/locations.
|
||||||
|
|
||||||
Report bugs to <menelkir@itroll.org>.
|
Report bugs to <dmenelkir@gmail.com>.
|
||||||
_ACEOF
|
_ACEOF
|
||||||
ac_status=$?
|
ac_status=$?
|
||||||
fi
|
fi
|
||||||
@@ -13557,7 +13557,7 @@ $config_headers
|
|||||||
Configuration commands:
|
Configuration commands:
|
||||||
$config_commands
|
$config_commands
|
||||||
|
|
||||||
Report bugs to <menelkir@itroll.org>."
|
Report bugs to <dmenelkir@gmail.com>."
|
||||||
|
|
||||||
_ACEOF
|
_ACEOF
|
||||||
ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
|
ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
AC_INIT([g15stats],[3.0],[menelkir@itroll.org])
|
AC_INIT([g15stats],[3.0],[dmenelkir@gmail.com])
|
||||||
|
|
||||||
PACKAGE=g15stats
|
PACKAGE=g15stats
|
||||||
VERSION=3.0.1
|
VERSION=3.0.1
|
||||||
|
|||||||
Executable
+14
@@ -0,0 +1,14 @@
|
|||||||
|
[main]
|
||||||
|
@type = classic, oneshot
|
||||||
|
@name = g15stats
|
||||||
|
@description = "g15stats"
|
||||||
|
@depends = ()
|
||||||
|
@options = ( log env )
|
||||||
|
|
||||||
|
[start]
|
||||||
|
@build = auto,custom
|
||||||
|
@execute = ( g15stats -d )
|
||||||
|
|
||||||
|
[stop]
|
||||||
|
@build = auto,custom
|
||||||
|
@execute = ( g15stats -k )
|
||||||
Regular → Executable
File mode changed.
Executable
+26
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# $FreeBSD$
|
||||||
|
#
|
||||||
|
# PROVIDE: g15stats
|
||||||
|
# REQUIRE: LOGIN g15daemon
|
||||||
|
# KEYWORD: shutdown
|
||||||
|
#
|
||||||
|
# Add these lines to /etc/rc.conf.local or /etc/rc.conf
|
||||||
|
# to enable this service:
|
||||||
|
#
|
||||||
|
# g15stats_enable (bool): Set to NO by default.
|
||||||
|
# Set it to YES to enable doormand.
|
||||||
|
|
||||||
|
. /etc/rc.subr
|
||||||
|
|
||||||
|
name=g15stats
|
||||||
|
rcvar="${name}_enable"
|
||||||
|
load_rc_config $name
|
||||||
|
|
||||||
|
: ${g15stats_enable:="NO"}
|
||||||
|
|
||||||
|
command=%%PREFIX%%/bin/${name}
|
||||||
|
command_args='-d -i re0'
|
||||||
|
|
||||||
|
run_rc_command "$1"
|
||||||
@@ -6,7 +6,6 @@ PIDFILE=/var/run/${SVCNAME}.pid
|
|||||||
|
|
||||||
depend() {
|
depend() {
|
||||||
need g15daemon
|
need g15daemon
|
||||||
after display-manager
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
|
|||||||
@@ -1,4 +1,2 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
exec 2>&1
|
|
||||||
sv check g15daemon >/dev/null || exit 1
|
|
||||||
exec g15stats
|
exec g15stats
|
||||||
Regular → Executable
File mode changed.
Executable
+25
@@ -0,0 +1,25 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
. /etc/rc.subr
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
start)
|
||||||
|
msg "Starting g15stats..."
|
||||||
|
start_daemon /usr/bin/g15stats -d
|
||||||
|
;;
|
||||||
|
stop)
|
||||||
|
msg "Stopping g15stats..."
|
||||||
|
stop_daemon /usr/bin/g15stats
|
||||||
|
;;
|
||||||
|
restart)
|
||||||
|
$0 stop
|
||||||
|
sleep 1
|
||||||
|
$0 start
|
||||||
|
;;
|
||||||
|
status)
|
||||||
|
status_daemon /usr/bin/g15stats
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "usage: $0 [start|stop|restart|status]"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Vendored
+53
@@ -0,0 +1,53 @@
|
|||||||
|
Debian packaging import and fork provenance
|
||||||
|
==========================================
|
||||||
|
|
||||||
|
Imported on 2026-09-07 from the official Debian archive:
|
||||||
|
https://archive.debian.org/debian/pool/main/g/g15stats/
|
||||||
|
|
||||||
|
SHA-256 (filenames relative to that URL):
|
||||||
|
79289e9f182e086dc5bfe0b1b594fedc36ad7610ee1daec19b05d44c4494d3fa g15stats_1.9.2-2.dsc
|
||||||
|
ce724380069ee62563f2898b1bbd0afb80bb632cb6dbf3d1747871f7cdfefbb3 g15stats_1.9.2-2.diff.gz
|
||||||
|
9dda49a3f09e66ce738ceecb07f2c0fef7ecd5ecbd06e435b340dfca35a37da4 g15stats_1.9.2.orig.tar.gz
|
||||||
|
|
||||||
|
The diff (2316 bytes) and orig tarball (25190 bytes) match the SHA-256
|
||||||
|
checksums in the downloaded .dsc. The .dsc's own checksum was computed
|
||||||
|
locally. The byte-exact .dsc and diff are retained in debian/provenance/;
|
||||||
|
the diff preserves ALL original debian files, the manual and linker fix.
|
||||||
|
The tarball can be downloaded from the URL above and verified independently.
|
||||||
|
Checksum matching alone does not authenticate the uploader's PGP signature.
|
||||||
|
Verification was attempted with Debian 13's debian-keyring,
|
||||||
|
debian-maintainers and debian-nonupload keyrings: gpgv reported
|
||||||
|
"No public key" for DSA key F9935424B1DF9A57 (signature dated 2011-03-15).
|
||||||
|
No successful signature verification is claimed here.
|
||||||
|
|
||||||
|
Archive package: g15stats 1.9.2-2, source format 1.0, maintained by
|
||||||
|
Giacomo Catenazzi <cate@debian.org>. Original Debian packaging copyright:
|
||||||
|
2008 Giacomo A. Catenazzi, GPL-2-or-later. All three historical changelog
|
||||||
|
entries remain unchanged. The archive adds g15stats.1 outside debian/;
|
||||||
|
it is now debian/g15stats.1, extended for the fork, retaining Mike Lampard's
|
||||||
|
author credit. The archive's only upstream source patch adds -lpthread -lm
|
||||||
|
to Makefile.am, credited to Bhavani Shankar in the changelog (#554382).
|
||||||
|
Both libraries already appear in the fork: do not apply that patch twice.
|
||||||
|
|
||||||
|
All nine archived debian files were imported. control, rules, docs, install,
|
||||||
|
manpages, watch and copyright were adapted; changelog was prepended;
|
||||||
|
compat was raised from 5 to 13, with Build-Depends: debhelper (>= 13~).
|
||||||
|
Obsolete CDBS cleanup and SourceForge watching were replaced, not omitted
|
||||||
|
without a record. No archived quilt series existed.
|
||||||
|
|
||||||
|
Upstream snapshot (unmodified tracked files in the orig archive):
|
||||||
|
Menelkir master 95de11a526346b3d9fd33df051b330c36b1c8a16
|
||||||
|
Commit date: 2024-06-09; subject: Update README.rst
|
||||||
|
Upstream: https://gitlab.com/menelkir/g15stats
|
||||||
|
Local fork origin: https://gt.uo1.net/mikhail/g15stats
|
||||||
|
|
||||||
|
Version rationale: git describe gives 3.0.3-13-g95de11a and the tracked
|
||||||
|
Arch PKGBUILD declares 3.0.3. configure.ac instead has stale AC_INIT=3.0
|
||||||
|
and VERSION=3.0.1, including at tag 3.0.3. Therefore this package is
|
||||||
|
3.0.3+git20240609.95de11a-1~local1, NOT Debian's old 1.9.2 nor a new
|
||||||
|
upstream release. The quilt patch aligns the embedded version and modernizes
|
||||||
|
only the build system. Runtime engine files remain untouched.
|
||||||
|
|
||||||
|
The new changelog/patch identity is the configured local Git user,
|
||||||
|
Mikhail Yevchenko <m.ṥῥẚɱ.ѓѐḿởύḙ@uo1.net>; it does not impersonate
|
||||||
|
the historical Debian maintainer or claim upstream authorship.
|
||||||
Vendored
+52
@@ -0,0 +1,52 @@
|
|||||||
|
g15stats for Debian 13 (local fork package)
|
||||||
|
=========================================
|
||||||
|
|
||||||
|
This packaging targets Linux and debhelper compatibility 13. See PROVENANCE
|
||||||
|
for the complete Debian 1.9.2-2 import, licensing and snapshot version choice.
|
||||||
|
All upstream modifications are in debian/patches; do not build in the checkout
|
||||||
|
when you want it to remain pristine.
|
||||||
|
|
||||||
|
Clean source preparation (outside the checkout)
|
||||||
|
----------------------------------------------
|
||||||
|
Export ONLY tracked upstream files at commit
|
||||||
|
95de11a526346b3d9fd33df051b330c36b1c8a16 using git archive, with directory
|
||||||
|
prefix g15stats-3.0.3+git20240609.95de11a/. Compress with xz to produce
|
||||||
|
g15stats_3.0.3+git20240609.95de11a.orig.tar.xz. Extract in a temporary build
|
||||||
|
directory, then copy this debian/ directory into it. Never tar the working
|
||||||
|
checkout: .git, quilt state and untracked build outputs must not be included.
|
||||||
|
|
||||||
|
In a disposable Debian 13 container, install build-essential, debhelper,
|
||||||
|
autoconf, automake, libtool, pkgconf, libgtop2-dev, libg15-dev,
|
||||||
|
libg15render-dev and libg15daemon-client-dev (apt-get build-dep on the staged
|
||||||
|
source directory also resolves Build-Depends). Install lintian for QA.
|
||||||
|
If the already-built local G15 libraries are available, install their runtime
|
||||||
|
and development .deb files instead of the distro G15 libraries. Keep libgtop
|
||||||
|
and other dependencies from Debian 13. Do not install packages on the host.
|
||||||
|
|
||||||
|
Run dpkg-buildpackage -S -us -uc in the staged source directory. In another
|
||||||
|
empty directory/container, extract the resulting .dsc with dpkg-source -x,
|
||||||
|
then run dpkg-buildpackage -b -us -uc. Source packages here are unsigned;
|
||||||
|
dpkg-source --no-check is appropriate only after independently checking the
|
||||||
|
locally produced source artifact checksums. Lint both source and binary
|
||||||
|
.changes with lintian --fail-on error. Keep logs and all artifacts outside
|
||||||
|
the checkout. A second independent extraction/build checks rebuildability;
|
||||||
|
bit-for-bit reproducibility is a separate claim requiring comparison.
|
||||||
|
|
||||||
|
Safe smoke test
|
||||||
|
---------------
|
||||||
|
Install the resulting binary in a disposable container with no USB mappings,
|
||||||
|
no host networking and no privileged mode. Suppress service startup using
|
||||||
|
policy-rc.d before installing g15daemon. Run debian/tests/help, which compares
|
||||||
|
timeout-bounded --help and -h output. These exit before any daemon connection,
|
||||||
|
sensor access or keyboard thread. Do not invoke the normal client or -d.
|
||||||
|
The same test is declared as a superficial autopkgtest.
|
||||||
|
|
||||||
|
Scope and limitations
|
||||||
|
---------------------
|
||||||
|
No service/init, udev rule, keyboard macro or automatic startup is shipped.
|
||||||
|
The existing runtime code has not been safety-certified or rewritten: legacy
|
||||||
|
sensor paths, argument parsing, array bounds, string handling and threading
|
||||||
|
need separate runtime review. Build/CLI tests do not validate hardware, LCD
|
||||||
|
rendering, live daemon interaction, battery support or other architectures.
|
||||||
|
Use --info-rotate, not the incorrectly documented short form -ir; see the
|
||||||
|
manual for the existing parser discrepancy.
|
||||||
Vendored
+48
@@ -0,0 +1,48 @@
|
|||||||
|
g15stats (3.0.3+git20240609.95de11a-1~local1) UNRELEASED; urgency=medium
|
||||||
|
|
||||||
|
* Add cached multi-vendor GPU summary columns: independent read-only amdgpu
|
||||||
|
workers and optional, deadline-bounded batched shell-free nvidia-smi
|
||||||
|
queries.
|
||||||
|
Collect all supported cards by default, display two columns concurrently
|
||||||
|
beside compact CPU/RAM data, and cycle larger inventories in sorted pairs.
|
||||||
|
Document PCI selection, bounds, units, missing metrics and unchanged no-GPU
|
||||||
|
layouts; add multi-adapter isolation, live collector and framebuffer tests.
|
||||||
|
Check complete GPU/TOTAL/footer glyphs against font bits independently;
|
||||||
|
exercise off/single/paged layouts and verify lossless visual exports.
|
||||||
|
* Initialize the bitmap canvas locally, avoiding the optional FreeType
|
||||||
|
canvas ABI mismatch that corrupts the heap during application startup.
|
||||||
|
Add a full-main regression with isolated daemon transport.
|
||||||
|
* Import Debian 1.9.2-2 packaging and manual, retaining its changelog,
|
||||||
|
attribution and checksum-recorded archive provenance.
|
||||||
|
* Package Menelkir master 95de11a526346b3d9fd33df051b330c36b1c8a16,
|
||||||
|
a post-3.0.3 snapshot, rather than mislabelling this fork as 1.9.2.
|
||||||
|
* Use debhelper compat 13, 3.0 (quilt), direct build dependencies,
|
||||||
|
rootless builds and all Debian hardening flags on Debian 13.
|
||||||
|
* Quilt build fixes preserve compiler flags, link libgtop correctly,
|
||||||
|
remove the bare include flag and align the embedded snapshot version.
|
||||||
|
* Extend the archived manual and add isolated CLI smoke tests.
|
||||||
|
Do not install init scripts or automatically start the client.
|
||||||
|
|
||||||
|
-- Mikhail Yevchenko <m.ṥῥẚɱ.ѓѐḿởύḙ@uo1.net> Mon, 07 Sep 2026 15:47:05 +0300
|
||||||
|
|
||||||
|
g15stats (1.9.2-2) unstable; urgency=low
|
||||||
|
|
||||||
|
* Supports binutils-gold linker, writing explicitly our library
|
||||||
|
dependencies. Patch from Bhavani Shankar (Closes: #554382).
|
||||||
|
|
||||||
|
-- Giacomo Catenazzi <cate@debian.org> Tue, 15 Mar 2011 08:04:59 +0100
|
||||||
|
|
||||||
|
g15stats (1.9.2-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* New upstream version (Closes: #530548).
|
||||||
|
* Add support for automake and autoconf
|
||||||
|
|
||||||
|
-- Giacomo Catenazzi <cate@debian.org> Tue, 04 Aug 2009 13:15:38 +0200
|
||||||
|
|
||||||
|
g15stats (1.0-1) unstable; urgency=low
|
||||||
|
|
||||||
|
* Initial release. (Closes: #439792).
|
||||||
|
|
||||||
|
-- Giacomo Catenazzi <cate@debian.org> Tue, 18 Mar 2008 08:09:06 +0100
|
||||||
|
|
||||||
|
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
13
|
||||||
Vendored
+38
@@ -0,0 +1,38 @@
|
|||||||
|
Source: g15stats
|
||||||
|
Section: utils
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Mikhail Yevchenko <m.ṥῥẚɱ.ѓѐḿởύḙ@uo1.net>
|
||||||
|
Build-Depends: debhelper (>= 13~),
|
||||||
|
autoconf,
|
||||||
|
automake,
|
||||||
|
libg15-dev,
|
||||||
|
libg15daemon-client-dev,
|
||||||
|
libg15render-dev,
|
||||||
|
libgtop2-dev,
|
||||||
|
libtool,
|
||||||
|
pkgconf
|
||||||
|
Standards-Version: 4.7.2
|
||||||
|
Rules-Requires-Root: no
|
||||||
|
Homepage: https://gitlab.com/menelkir/g15stats
|
||||||
|
|
||||||
|
Package: g15stats
|
||||||
|
Architecture: linux-any
|
||||||
|
Depends: g15daemon, ${shlibs:Depends}, ${misc:Depends}
|
||||||
|
Description: system statistics display client for G15Daemon
|
||||||
|
Displays system statistics on the LCD of supported Logitech keyboards
|
||||||
|
through g15daemon.
|
||||||
|
.
|
||||||
|
CPU Screen displays graphs of User/System/Nice and Idle time, along with
|
||||||
|
LoadAVG and Uptime.
|
||||||
|
.
|
||||||
|
Memory Screen displays Memory Total & Free, and graph of Used vs
|
||||||
|
Buffered+Cached Memory.
|
||||||
|
.
|
||||||
|
Swap Screen displays Used, Free and Total swap space, along with the number
|
||||||
|
of pages currently paged in/out.
|
||||||
|
.
|
||||||
|
Network Screen displays Total bytes In/Out, history graph, Peak speed.
|
||||||
|
.
|
||||||
|
Additional screens show CPU frequency, battery charge, temperature and
|
||||||
|
fan speed where supported by the system's sensors.
|
||||||
|
|
||||||
Vendored
+40
@@ -0,0 +1,40 @@
|
|||||||
|
This package was debianized by Giacomo A. Catenazzi <cate@debian.org> on
|
||||||
|
Sat, 15 Sep 2007 08:32:51 +0200.
|
||||||
|
|
||||||
|
It was downloaded from http://g15daemon.sourceforge.net/
|
||||||
|
|
||||||
|
Upstream Authors (also see AUTHORS):
|
||||||
|
Mike Lampard <mlampard@users.sourceforge.net>
|
||||||
|
Antonio Bartolini <robynhub@gmail.com>
|
||||||
|
Piotr Czarnecki <czarnyckm@users.sf.net>
|
||||||
|
Daniel Menelkir <dmenelkir@gmail.com>
|
||||||
|
|
||||||
|
Copyright:
|
||||||
|
|
||||||
|
Copyright (c) 2006-2007 Mike Lampard
|
||||||
|
Copyright (c) 2008-2010 Mike Lampard (g15stats.c)
|
||||||
|
|
||||||
|
License:
|
||||||
|
|
||||||
|
g15daemon is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
g15daemon is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
License can be found in the file `/usr/share/common-licenses/GPL-2'.
|
||||||
|
|
||||||
|
The Debian packaging is (C) 2008, Giacomo A. Catenazzi <cate@debian.org> and
|
||||||
|
is licensed under the GPL v2 (or later), see `/usr/share/common-licenses/GPL-2'.
|
||||||
|
|
||||||
|
Local packaging additions are Copyright (c) 2026 Mikhail Yevchenko
|
||||||
|
<m.ṥῥẚɱ.ѓѐḿởύḙ@uo1.net>, licensed under GPL v2 or later.
|
||||||
|
The imported manual names Mike Lampard as author; its Debian archive origin
|
||||||
|
and subsequent local updates are documented in debian/PROVENANCE.
|
||||||
|
The original Debian diff, including the original copyright notice, is retained
|
||||||
|
unchanged in debian/provenance/. No authorship of the fork is claimed here.
|
||||||
|
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
README
|
||||||
|
AUTHORS
|
||||||
|
debian/README.Debian
|
||||||
|
debian/PROVENANCE
|
||||||
Vendored
+119
@@ -0,0 +1,119 @@
|
|||||||
|
.\" Imported from Debian 1.9.2-2; see debian/PROVENANCE for attribution.
|
||||||
|
.TH "g15stats" "1" "7 September 2026" "G15Daemon - g15stats" "G15Daemon User Manual"
|
||||||
|
|
||||||
|
.SH "NAME"
|
||||||
|
g15stats \- system statistics display client for G15Daemon
|
||||||
|
|
||||||
|
.SH "SYNOPSIS"
|
||||||
|
.B g15stats
|
||||||
|
.RI [ options ]
|
||||||
|
|
||||||
|
.SH "DESCRIPTION"
|
||||||
|
The program provides usage meters for the LCD on
|
||||||
|
supported Logitech keyboards, using g15daemon:
|
||||||
|
.PP
|
||||||
|
CPU Screen displays graphs of User/System/Nice and Idle time, along with
|
||||||
|
LoadAVG and Uptime.
|
||||||
|
.PP
|
||||||
|
Memory Screen displays Memory Total & Free, and graph of Used vs
|
||||||
|
Buffered+Cached Memory.
|
||||||
|
.PP
|
||||||
|
Swap Screen displays Used, Free and Total swap space, along with the number
|
||||||
|
of pages currently paged in/out.
|
||||||
|
.PP
|
||||||
|
Network Screen displays Total bytes In/Out, history graph, Peak speed.
|
||||||
|
.PP
|
||||||
|
The summary, CPU frequency, battery, temperature and fan screens display
|
||||||
|
additional statistics where the relevant system sensors are available.
|
||||||
|
|
||||||
|
.SH "OPTIONS"
|
||||||
|
g15stats understands the following options:
|
||||||
|
.TP
|
||||||
|
.BI "\-gpu, --gpu " "auto|off|dddd:bb:ss.f"
|
||||||
|
Select the GPU summary columns. Default auto collects all PCI addresses
|
||||||
|
bound to amdgpu or nvidia at startup, including mixed vendors; off disables collection and retains
|
||||||
|
the original layout. A PCI address selects one card explicitly.
|
||||||
|
No supported card in auto mode leaves the original layout intact; an explicit
|
||||||
|
missing card shows N/A. Restart after hotplug to rediscover devices.
|
||||||
|
Intel, nouveau and radeon are not supported.
|
||||||
|
.br
|
||||||
|
.P
|
||||||
|
.HP
|
||||||
|
\-h,--help show usage information.
|
||||||
|
.P
|
||||||
|
.HP
|
||||||
|
\-d,--daemon run in background.
|
||||||
|
.P
|
||||||
|
.HP
|
||||||
|
\-i,--interface [interface] monitor network interface [interface] ie -i eth0.
|
||||||
|
.P
|
||||||
|
.HP
|
||||||
|
\-nsa,--net-scale-absolute scale net graphs against maximum speed seen.
|
||||||
|
.TP
|
||||||
|
.BR \-u , " --unicore"
|
||||||
|
Display unicore graphs on the CPU screen.
|
||||||
|
.TP
|
||||||
|
.BI "\-r, --refresh " seconds
|
||||||
|
Set the refresh interval from 1 to 300 seconds (default: 1).
|
||||||
|
.TP
|
||||||
|
.BI "\-t, --temperature " id
|
||||||
|
Select the temperature hwmon sensor (0 to 3).
|
||||||
|
.TP
|
||||||
|
.BI "\-f, --fan " id
|
||||||
|
Select the fan hwmon sensor (0 to 3).
|
||||||
|
.TP
|
||||||
|
.BI "\-gt, --global-temp " id
|
||||||
|
Show this temperature probe instead of the maximum on the summary screen.
|
||||||
|
.TP
|
||||||
|
.BR \-df , " --disable-freq"
|
||||||
|
Disable CPU frequency monitoring.
|
||||||
|
.TP
|
||||||
|
.B --info-rotate
|
||||||
|
Rotate the bottom information bar. Use the long option: the fork documents
|
||||||
|
\-ir in its help, but its parser checks \-rc and also treats that prefix as
|
||||||
|
\-r. The documented short form \-ir is not supported correctly.
|
||||||
|
.TP
|
||||||
|
.BR \-vc , " --variable-cpu"
|
||||||
|
Recalculate the CPU count for systems supporting CPU hotplug.
|
||||||
|
|
||||||
|
.SH "KEYS"
|
||||||
|
L2 selects the previous screen; L3 selects the next screen.
|
||||||
|
L4 selects an alternative view where available; L5 changes the bottom bar.
|
||||||
|
.SH "NOTES"
|
||||||
|
Each GPU column shows vendor/index and PCI bus:slot.function, UTL percent,
|
||||||
|
U used VRAM, T total VRAM, and TEM in C.
|
||||||
|
Used and total occupy separate rows with explicit MiB/GiB units (TiB/PiB
|
||||||
|
if needed), rounded to one decimal. On AMD APUs VRAM can mean only the reserved
|
||||||
|
carveout. With one GPU the summary retains its 4/5 indicators and shortened
|
||||||
|
graphs. With two GPUs both columns appear together beside compact CPU percent,
|
||||||
|
R used system RAM, T total system RAM and SWP percent (plus GPU count in five-row
|
||||||
|
mode). RAM has explicit units too. More than two cycle in PCI-sorted pairs every
|
||||||
|
five seconds from startup; all cards continue collecting. NV1/AMD2-style indices
|
||||||
|
are one-based selected-inventory positions, not NVIDIA ordinals; startup diagnostics
|
||||||
|
map them to full PCI addresses. The 64-card bound fails explicitly on overflow.
|
||||||
|
The bottom bar and other screens are unchanged. See README for pixel coordinates.
|
||||||
|
.PP
|
||||||
|
AMD reads device-local sysfs/hwmon metrics. NVIDIA optionally uses
|
||||||
|
/usr/bin/nvidia-smi with no shell or SDK dependency. Independent AMD workers and
|
||||||
|
one batched NVIDIA worker wait two seconds between queries. All NVIDIA cards
|
||||||
|
share one PCI-keyed query with a 750ms deadline;
|
||||||
|
timeouts kill the child, and an unreaped child prevents additional launches.
|
||||||
|
The child is also killed on parent death. Data older than five seconds and
|
||||||
|
unavailable metrics show N/A, never fabricated zero. Display refresh remains
|
||||||
|
controlled by the refresh option. Worker shutdown has one shared one-second
|
||||||
|
join/reap budget; a stuck kernel read can outlive it until process exit.
|
||||||
|
No GPU settings are written.
|
||||||
|
.PP
|
||||||
|
Normal operation requires a running g15daemon. This package does not install
|
||||||
|
or enable a g15stats service. Run the client as an ordinary user.
|
||||||
|
The help options exit before connecting to g15daemon.
|
||||||
|
Sensor availability depends on the kernel and hardware; legacy battery
|
||||||
|
paths may be unavailable on modern systems.
|
||||||
|
|
||||||
|
.SH "AUTHOR"
|
||||||
|
Mike Lampard
|
||||||
|
.SH "SEE ALSO"
|
||||||
|
.BR g15daemon (1),
|
||||||
|
.BR libg15 (3)
|
||||||
|
|
||||||
|
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
usr/bin/g15stats
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
debian/g15stats.1
|
||||||
Vendored
+47
@@ -0,0 +1,47 @@
|
|||||||
|
Description: Preserve Debian flags and use conventional automake linking
|
||||||
|
Remove the bare -I (which consumes the next compiler argument), pass GTOP
|
||||||
|
libraries after objects, and let debhelper install selected documentation.
|
||||||
|
The historical Bhavani Shankar explicit pthread/math linkage fix is already
|
||||||
|
present in this fork and remains intact. Align stale configure versions with
|
||||||
|
the pinned post-3.0.3 snapshot (see debian/PROVENANCE).
|
||||||
|
Author: Mikhail Yevchenko <m.ṥῥẚɱ.ѓѐḿởύḙ@uo1.net>
|
||||||
|
Forwarded: not-needed
|
||||||
|
Last-Update: 2026-09-07
|
||||||
|
|
||||||
|
--- a/configure.ac
|
||||||
|
+++ b/configure.ac
|
||||||
|
@@ -1,7 +1,5 @@
|
||||||
|
-AC_INIT([g15stats],[3.0],[dmenelkir@gmail.com])
|
||||||
|
+AC_INIT([g15stats],[3.0.3+git20240609.95de11a],[dmenelkir@gmail.com])
|
||||||
|
|
||||||
|
-PACKAGE=g15stats
|
||||||
|
-VERSION=3.0.1
|
||||||
|
AC_PREFIX_DEFAULT(/usr)
|
||||||
|
AC_CONFIG_AUX_DIR(config)
|
||||||
|
-AM_INIT_AUTOMAKE($PACKAGE, $VERSION)
|
||||||
|
+AM_INIT_AUTOMAKE([foreign])
|
||||||
|
@@ -26,6 +24,4 @@
|
||||||
|
CPPFLAGS="$CPPFLAGS $GTOP_CFLAGS"
|
||||||
|
-CFLAGS="$CPPFLAGS $GTOP_CFLAGS"
|
||||||
|
-LDFLAGS="$LDFLAGS $GTOP_LIBS"
|
||||||
|
|
||||||
|
AC_CONFIG_FILES([Makefile])
|
||||||
|
AC_OUTPUT
|
||||||
|
--- a/Makefile.am
|
||||||
|
+++ b/Makefile.am
|
||||||
|
@@ -2,12 +2,8 @@
|
||||||
|
-AM_CFLAGS = -Wall @CFLAGS@ -I$(top_builddir) -I$(top_srcdir)
|
||||||
|
+AM_CFLAGS = -Wall
|
||||||
|
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir)
|
||||||
|
METASOURCES = AUTO
|
||||||
|
bin_PROGRAMS = g15stats
|
||||||
|
noinst_HEADERS = g15stats.h
|
||||||
|
|
||||||
|
-docdir = @docdir@
|
||||||
|
-doc_DATA = README ChangeLog AUTHORS NEWS COPYING
|
||||||
|
-
|
||||||
|
-LIBS = -lg15daemon_client -lg15render -lpthread -lm -lrt
|
||||||
|
-IMCLUDES = -I
|
||||||
|
-g15stats_LDFLAGS = -avoid-version
|
||||||
|
+g15stats_LDADD = -lg15daemon_client -lg15render -lpthread -lm -lrt $(GTOP_LIBS)
|
||||||
|
g15stats_SOURCES = g15stats.c g15stats.h
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
modern-build.patch
|
||||||
BIN
Binary file not shown.
+29
@@ -0,0 +1,29 @@
|
|||||||
|
-----BEGIN PGP SIGNED MESSAGE-----
|
||||||
|
Hash: SHA1
|
||||||
|
|
||||||
|
Format: 1.0
|
||||||
|
Source: g15stats
|
||||||
|
Binary: g15stats
|
||||||
|
Architecture: any
|
||||||
|
Version: 1.9.2-2
|
||||||
|
Maintainer: Giacomo Catenazzi <cate@debian.org>
|
||||||
|
Homepage: http://www.g15tools.com
|
||||||
|
Standards-Version: 3.8.2
|
||||||
|
Build-Depends: cdbs, debhelper (>= 5), autotools-dev, libxtst-dev, libfreetype6-dev, libusb-dev, libgtop2-dev, libg15-dev, libg15render-dev (>= 1.2-3), libg15daemon-client-dev, automake, libtool, autoconf
|
||||||
|
Checksums-Sha1:
|
||||||
|
b53ea7414bd47b68a73aaa8f548d36f50b47aa09 25190 g15stats_1.9.2.orig.tar.gz
|
||||||
|
245c96ab8267a31c5e1ebebe91307e8ff99bb976 2316 g15stats_1.9.2-2.diff.gz
|
||||||
|
Checksums-Sha256:
|
||||||
|
9dda49a3f09e66ce738ceecb07f2c0fef7ecd5ecbd06e435b340dfca35a37da4 25190 g15stats_1.9.2.orig.tar.gz
|
||||||
|
ce724380069ee62563f2898b1bbd0afb80bb632cb6dbf3d1747871f7cdfefbb3 2316 g15stats_1.9.2-2.diff.gz
|
||||||
|
Files:
|
||||||
|
beb285f739127e91b97af465e6b53c4a 25190 g15stats_1.9.2.orig.tar.gz
|
||||||
|
bec0a80e18f8905d4d70a1c9fc575043 2316 g15stats_1.9.2-2.diff.gz
|
||||||
|
|
||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
Version: GnuPG v1.4.11 (GNU/Linux)
|
||||||
|
|
||||||
|
iEYEARECAAYFAk1/EkEACgkQ+ZNUJLHfmlclzQCeMMSD3P7ix2G7M8JJnlfTbqBS
|
||||||
|
4U4AnjhLSSPFun2rWQirQ5w9D7ubM3gC
|
||||||
|
=ounU
|
||||||
|
-----END PGP SIGNATURE-----
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
|
||||||
|
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
override_dh_auto_install:
|
||||||
|
dh_auto_install --destdir=debian/tmp
|
||||||
|
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
3.0 (quilt)
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
debian/provenance/g15stats_1.9.2-2.diff.gz
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
Tests: help
|
||||||
|
Depends: @
|
||||||
|
Restrictions: superficial
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
# Help returns before daemon connection, sensor polling or keyboard threads.
|
||||||
|
# Run only in an isolated testbed without USB device passthrough.
|
||||||
|
work=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$work"' EXIT HUP INT TERM
|
||||||
|
timeout 5 g15stats --help > "$work/long"
|
||||||
|
timeout 5 g15stats -h > "$work/short"
|
||||||
|
cmp "$work/long" "$work/short"
|
||||||
|
grep -F 'g15stats 3.0.3+git20240609.95de11a' "$work/long"
|
||||||
|
grep -F -- '--interface' "$work/long"
|
||||||
|
grep -F -- '--refresh' "$work/long"
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
version=4
|
||||||
|
# Local post-3.0.3 snapshot: see PROVENANCE; no upstream signatures available.
|
||||||
|
opts="mode=git, pgpmode=none, dversionmangle=s/\+git.*//" \
|
||||||
|
https://gitlab.com/menelkir/g15stats.git refs/tags/@ANY_VERSION@
|
||||||
|
|
||||||
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
pkgbase = g15stats-openrc
|
||||||
|
pkgdesc = g15stats init script for OpenRC
|
||||||
|
pkgver = 20211101
|
||||||
|
pkgrel = 1
|
||||||
|
url = https://gitlab.com/menelkir/g15stats
|
||||||
|
arch = any
|
||||||
|
license = GPL2
|
||||||
|
depends = g15stats
|
||||||
|
depends = g15daemon-openrc
|
||||||
|
source = https://gitlab.com/menelkir/g15stats/-/raw/master/contrib/init/g15stats.initd
|
||||||
|
source = https://gitlab.com/menelkir/g15stats/-/raw/master/contrib/init/g15stats.confd
|
||||||
|
sha256sums = 136da674e6e7c993dd388e09b245dc43d25fefebb1e379092585cbf87bf55676
|
||||||
|
sha256sums = ca6520f9752ec526e284d4359aea57399949e0ea5e98ee5904524d186d898f01
|
||||||
|
|
||||||
|
pkgname = g15stats-openrc
|
||||||
Vendored
+22
@@ -0,0 +1,22 @@
|
|||||||
|
# Maintainer: Daniel Menelkir <dmenelkir at gmail dot com>
|
||||||
|
|
||||||
|
pkgname=g15stats-openrc
|
||||||
|
pkgver=20211101
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="g15stats init script for OpenRC"
|
||||||
|
arch=('any')
|
||||||
|
url="https://gitlab.com/menelkir/g15stats"
|
||||||
|
license=('GPL2')
|
||||||
|
depends=('g15stats' 'g15daemon-openrc')
|
||||||
|
_filename=('g15stats.initd' 'g15stats.confd')
|
||||||
|
source=("https://gitlab.com/menelkir/g15stats/-/raw/master/contrib/init/g15stats.initd"
|
||||||
|
"https://gitlab.com/menelkir/g15stats/-/raw/master/contrib/init/g15stats.confd")
|
||||||
|
sha256sums=('136da674e6e7c993dd388e09b245dc43d25fefebb1e379092585cbf87bf55676'
|
||||||
|
'ca6520f9752ec526e284d4359aea57399949e0ea5e98ee5904524d186d898f01')
|
||||||
|
|
||||||
|
package() {
|
||||||
|
install -dm755 "$pkgdir/etc/init.d"
|
||||||
|
install -dm755 "$pkgdir/etc/conf.d"
|
||||||
|
install -Dm755 "$srcdir/g15stats.initd" "$pkgdir/etc/init.d/g15stats"
|
||||||
|
install -Dm644 "$srcdir/g15stats.confd" "$pkgdir/etc/conf.d/g15stats"
|
||||||
|
}
|
||||||
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
pkgbase = g15stats-systemd
|
||||||
|
pkgdesc = g15stats init script for systemd
|
||||||
|
pkgver = 20211101
|
||||||
|
pkgrel = 1
|
||||||
|
url = https://gitlab.com/menelkir/g15stats
|
||||||
|
arch = any
|
||||||
|
license = GPL2
|
||||||
|
depends = g15stats
|
||||||
|
depends = g15daemon-systemd
|
||||||
|
source = https://gitlab.com/menelkir/g15stats/-/raw/master/contrib/init/g15stats.service
|
||||||
|
sha256sums = 2723465ba726a4675d442fdb3068353def37bc70ce84cdab6c65f1c450c1dc4a
|
||||||
|
|
||||||
|
pkgname = g15stats-systemd
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
# Maintainer: Daniel Menelkir <dmenelkir at gmail dot com>
|
||||||
|
|
||||||
|
pkgname=g15stats-systemd
|
||||||
|
pkgver=20211101
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="g15stats init script for systemd"
|
||||||
|
arch=('any')
|
||||||
|
url="https://gitlab.com/menelkir/g15stats"
|
||||||
|
license=('GPL2')
|
||||||
|
depends=('g15stats' 'g15daemon-systemd')
|
||||||
|
_filename='g15stats.run'
|
||||||
|
source=("https://gitlab.com/menelkir/g15stats/-/raw/master/contrib/init/g15stats.service")
|
||||||
|
sha256sums=('2723465ba726a4675d442fdb3068353def37bc70ce84cdab6c65f1c450c1dc4a')
|
||||||
|
|
||||||
|
package() {
|
||||||
|
install -D -m 644 "g15stats.service" "${pkgdir}/usr/lib/systemd/system/g15stats.service"
|
||||||
|
}
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
pkgbase = g15stats
|
||||||
|
pkgdesc = A CPU/Mem/Swap/Network monitoring client for G15Daemon.
|
||||||
|
pkgver = 3.0.3
|
||||||
|
pkgrel = 2
|
||||||
|
url = https://gitlab.com/menelkir/g15stats
|
||||||
|
arch = i686
|
||||||
|
arch = x86_64
|
||||||
|
license = GPL
|
||||||
|
depends = g15daemon
|
||||||
|
depends = libgtop
|
||||||
|
depends = libg15
|
||||||
|
depends = libg15render
|
||||||
|
source = https://gitlab.com/menelkir/g15stats/-/archive/3.0.3/g15stats-3.0.3.tar.bz2
|
||||||
|
sha512sums = 6034fec8f2188182c6fd7587ec5f5ef8d06b617875573382385edc12f1aefb3b9bf52b437eafb7c9412676c5ceae42ae8c9c2099270908aea2b949d01df79047
|
||||||
|
|
||||||
|
pkgname = g15stats
|
||||||
|
|
||||||
Vendored
+32
@@ -0,0 +1,32 @@
|
|||||||
|
# Maintainer: Daniel Menelkir <dmenelkir at gmail dot com>
|
||||||
|
# Contributor: Mathieu OTHACEHE <m.othacehe@gmail.com>
|
||||||
|
# Contributor: Brenton <brenton@taylorbyte.com>
|
||||||
|
|
||||||
|
pkgname=g15stats
|
||||||
|
pkgver=3.0.3
|
||||||
|
pkgrel=2
|
||||||
|
pkgdesc="A CPU/Mem/Swap/Network monitoring client for G15Daemon."
|
||||||
|
arch=('i686' 'x86_64')
|
||||||
|
url="https://gitlab.com/menelkir/g15stats"
|
||||||
|
license=('GPL')
|
||||||
|
depends=('g15daemon' 'libgtop' 'libg15' 'libg15render')
|
||||||
|
source=(https://gitlab.com/menelkir/$pkgname/-/archive/$pkgver/$pkgname-$pkgver.tar.bz2)
|
||||||
|
sha512sums=('6034fec8f2188182c6fd7587ec5f5ef8d06b617875573382385edc12f1aefb3b9bf52b437eafb7c9412676c5ceae42ae8c9c2099270908aea2b949d01df79047')
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd $pkgname-$pkgver
|
||||||
|
./configure --prefix=/usr \
|
||||||
|
--sysconfdir=/etc \
|
||||||
|
--localstatedir=/var
|
||||||
|
make
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd $pkgname-$pkgver
|
||||||
|
make DESTDIR="$pkgdir" install
|
||||||
|
echo ""
|
||||||
|
echo "This package doesn't come with an init, you should install one:"
|
||||||
|
echo "g15stats-runit"
|
||||||
|
echo "g15stats-openrc"
|
||||||
|
echo "g15stats-systemd"
|
||||||
|
}
|
||||||
Vendored
+33
@@ -0,0 +1,33 @@
|
|||||||
|
PORTNAME= g15stats
|
||||||
|
PORTVERSION= 3.0.3
|
||||||
|
CATEGORIES= sysutils
|
||||||
|
MASTER_SITES= https://gitlab.com/menelkir/${PORTNAME}/-/archive/${PORTVERSION}/
|
||||||
|
|
||||||
|
MAINTAINER= dmenelkir@gmail.com
|
||||||
|
COMMENT= Resource monitor for G15daemon
|
||||||
|
|
||||||
|
LICENSE= GPLv2
|
||||||
|
|
||||||
|
LIB_DEPENDS+= libg15.so:sysutils/libg15 \
|
||||||
|
libg15render.so:sysutils/libg15render \
|
||||||
|
libgtop-2.0.so:devel/libgtop \
|
||||||
|
libg15daemon_client.so:sysutils/g15daemon
|
||||||
|
|
||||||
|
USES= gmake localbase libtool gnome pkgconfig
|
||||||
|
|
||||||
|
USE_LDCONFIG= yes
|
||||||
|
|
||||||
|
USE_GNOME+= glib20
|
||||||
|
|
||||||
|
GNU_CONFIGURE= yes
|
||||||
|
|
||||||
|
CFLAGS+= -std=c99 -fcommon
|
||||||
|
|
||||||
|
USE_RC_SUBR= g15stats
|
||||||
|
|
||||||
|
INSTALL_TARGET= install-strip
|
||||||
|
|
||||||
|
PLIST_FILES= bin/g15stats \
|
||||||
|
etc/rc.d/g15stats
|
||||||
|
|
||||||
|
.include <bsd.port.mk>
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
TIMESTAMP = 1621364357
|
||||||
|
SHA256 (g15stats-3.0.3.tar.gz) = 000ad7852fcf5b2d9524ea4fb0799654341b4ea23e076848e3b96dcb7d41d250
|
||||||
|
SIZE (g15stats-3.0.3.tar.gz) = 363805
|
||||||
Vendored
+26
@@ -0,0 +1,26 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
# $FreeBSD$
|
||||||
|
#
|
||||||
|
# PROVIDE: g15stats
|
||||||
|
# REQUIRE: LOGIN g15daemon
|
||||||
|
# KEYWORD: shutdown
|
||||||
|
#
|
||||||
|
# Add these lines to /etc/rc.conf.local or /etc/rc.conf
|
||||||
|
# to enable this service:
|
||||||
|
#
|
||||||
|
# g15stats_enable (bool): Set to NO by default.
|
||||||
|
# Set it to YES to enable doormand.
|
||||||
|
|
||||||
|
. /etc/rc.subr
|
||||||
|
|
||||||
|
name=g15stats
|
||||||
|
rcvar="${name}_enable"
|
||||||
|
load_rc_config $name
|
||||||
|
|
||||||
|
: ${g15stats_enable:="NO"}
|
||||||
|
|
||||||
|
command=%%PREFIX%%/bin/${name}
|
||||||
|
command_args='-d -i re0'
|
||||||
|
|
||||||
|
run_rc_command "$1"
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
Resource Monitor for G15Daemon.
|
||||||
|
|
||||||
|
WWW: https://gitlab.com/menelkir/g15stats
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
DIST g15stats-3.0.2.tar.bz2 295556 BLAKE2B d2e96e9d715f7dde5ec6227f5fe6d6d110bfd80d33a831ec1dffda754c4c606fce1134502cb44de691303e2f4f0d7960f806ef37c82002de896a12c90dc0b3b0 SHA512 d0b0aaca5cb551a77ce09e63bf903764cafc060636d0d19baae245e9b397926f15d776aa08076dc71aafaf2c5aa064fea77f589d10fec908c8543217ff3a3d69
|
||||||
Vendored
+46
@@ -0,0 +1,46 @@
|
|||||||
|
# Copyright 2021 Gentoo Authors
|
||||||
|
# Distributed under the terms of the GNU General Public License v2
|
||||||
|
|
||||||
|
EAPI=7
|
||||||
|
|
||||||
|
inherit autotools
|
||||||
|
|
||||||
|
DESCRIPTION="CPU, memory, swap, network stats for G15 Keyboard"
|
||||||
|
HOMEPAGE="https://gitlab.com/menelkir/${PN}"
|
||||||
|
SRC_URI="https://gitlab.com/menelkir/${PN}/-/archive/${PV}/${PN}-${PV}.tar.bz2"
|
||||||
|
|
||||||
|
LICENSE="GPL-2"
|
||||||
|
SLOT="0"
|
||||||
|
KEYWORDS="~amd64 ~ppc ~ppc64 ~x86"
|
||||||
|
IUSE=""
|
||||||
|
|
||||||
|
RDEPEND="app-misc/g15daemon
|
||||||
|
dev-libs/libg15
|
||||||
|
dev-libs/libg15render
|
||||||
|
sys-libs/zlib
|
||||||
|
gnome-base/libgtop"
|
||||||
|
|
||||||
|
DEPEND="${RDEPEND}"
|
||||||
|
BDEPEND="virtual/pkgconfig"
|
||||||
|
|
||||||
|
src_prepare() {
|
||||||
|
default
|
||||||
|
}
|
||||||
|
|
||||||
|
src_configure() {
|
||||||
|
export CPPFLAGS="${CFLAGS}"
|
||||||
|
econf
|
||||||
|
}
|
||||||
|
|
||||||
|
src_install() {
|
||||||
|
default
|
||||||
|
rm "${D}"/usr/share/doc/${PF}/{COPYING,NEWS} || die
|
||||||
|
|
||||||
|
newconfd "${S}/contrib/init/${PN}.confd" ${PN}
|
||||||
|
newinitd "${S}/contrib/init/${PN}.initd" ${PN}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg_postinst() {
|
||||||
|
elog "Remember to set the interface you want monitored in"
|
||||||
|
elog "/etc/conf.d/g15stats"
|
||||||
|
}
|
||||||
Vendored
+48
@@ -0,0 +1,48 @@
|
|||||||
|
# Copyright 2021 Gentoo Authors
|
||||||
|
# Distributed under the terms of the GNU General Public License v2
|
||||||
|
|
||||||
|
EAPI=7
|
||||||
|
|
||||||
|
inherit autotools git-r3
|
||||||
|
|
||||||
|
DESCRIPTION="CPU, memory, swap, network stats for G15 Keyboard"
|
||||||
|
HOMEPAGE="https://gitlab.com/menelkir/${PN}"
|
||||||
|
EGIT_REPO_URI="https://gitlab.com/menelkir/${PN}.git"
|
||||||
|
|
||||||
|
LICENSE="GPL-2"
|
||||||
|
SLOT="0"
|
||||||
|
IUSE=""
|
||||||
|
KEYWORDS=""
|
||||||
|
|
||||||
|
RDEPEND="app-misc/g15daemon
|
||||||
|
dev-libs/libg15
|
||||||
|
dev-libs/libg15render
|
||||||
|
sys-libs/zlib
|
||||||
|
gnome-base/libgtop"
|
||||||
|
|
||||||
|
DEPEND="${RDEPEND}"
|
||||||
|
BDEPEND="virtual/pkgconfig"
|
||||||
|
|
||||||
|
src_prepare() {
|
||||||
|
default
|
||||||
|
mv configure.{in,ac} || die
|
||||||
|
eautoreconf
|
||||||
|
}
|
||||||
|
|
||||||
|
src_configure() {
|
||||||
|
export CPPFLAGS="${CFLAGS}"
|
||||||
|
econf
|
||||||
|
}
|
||||||
|
|
||||||
|
src_install() {
|
||||||
|
default
|
||||||
|
rm "${D}"/usr/share/doc/${PF}/{COPYING,NEWS} || die
|
||||||
|
|
||||||
|
newconfd "${S}/contrib/init/${PN}.confd" ${PN}
|
||||||
|
newinitd "${S}/contrib/init/${PN}.initd" ${PN}
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg_postinst() {
|
||||||
|
elog "Remember to set the interface you want monitored in"
|
||||||
|
elog "/etc/conf.d/g15stats"
|
||||||
|
}
|
||||||
Vendored
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE pkgmetadata SYSTEM "http://www.gentoo.org/dtd/metadata.dtd">
|
||||||
|
<pkgmetadata>
|
||||||
|
<maintainer type="person">
|
||||||
|
<email>robbat2@gentoo.org</email>
|
||||||
|
</maintainer>
|
||||||
|
<maintainer type="person">
|
||||||
|
<email>polynomial-c@gentoo.org</email>
|
||||||
|
<name>Lars Wendler</name>
|
||||||
|
</maintainer>
|
||||||
|
<upstream>
|
||||||
|
<remote-id type="sourceforge">g15daemon</remote-id>
|
||||||
|
</upstream>
|
||||||
|
</pkgmetadata>
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
exec 2>&1
|
||||||
|
sv check g15daemon >/dev/null || exit 1
|
||||||
|
exec g15stats
|
||||||
Vendored
+18
@@ -0,0 +1,18 @@
|
|||||||
|
# Template file for 'g15stats'
|
||||||
|
pkgname=g15stats
|
||||||
|
version=3.0.3
|
||||||
|
revision=1
|
||||||
|
build_style=gnu-configure
|
||||||
|
CFLAGS=" -fcommon" # https://wiki.gentoo.org/wiki/Gcc_10_porting_notes/fno_common
|
||||||
|
makedepends="libgtop-devel g15daemon pkg-config"
|
||||||
|
depends="libgtop g15daemon"
|
||||||
|
short_desc="An usage monitor for G15daemon"
|
||||||
|
maintainer="Daniel Menelkir <dmenelkir@gmail.com>"
|
||||||
|
license="GPL-2.0-or-later"
|
||||||
|
homepage="https://gitlab.com/menelkir/g15stats"
|
||||||
|
distfiles="${homepage}/-/archive/${version}/g15stats-${version}.tar.bz2"
|
||||||
|
checksum=f5bb77d4b8a1030d07efc22919446c4abf876ca3086ff7c50cfcae9d67b64647
|
||||||
|
|
||||||
|
post_install() {
|
||||||
|
vsv g15stats
|
||||||
|
}
|
||||||
+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/uptime.h>
|
||||||
#include <glibtop/sysinfo.h>
|
#include <glibtop/sysinfo.h>
|
||||||
#include "g15stats.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;
|
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){
|
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,
|
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) {
|
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);
|
int end = summary_gpu ? 96 : BAR_END;
|
||||||
drawBar_reversed(canvas, BAR_START, y1, BAR_END, y2, G15_COLOR_BLACK, rbar_current, rbar_total, 5);
|
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) {
|
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);
|
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) {
|
void draw_cpu_screen_multicore(g15canvas *canvas, char *tmpstr, int unicore) {
|
||||||
glibtop_cpu cpu;
|
glibtop_cpu cpu;
|
||||||
int core,ncpumax;
|
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_nice[GLIBTOP_NCPU],b_sys[GLIBTOP_NCPU],b_idle[GLIBTOP_NCPU],
|
||||||
b_irq[GLIBTOP_NCPU],b_iowait[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();
|
init_cpu_count();
|
||||||
|
|
||||||
ncpumax = ncpu;
|
ncpumax = ncpu;
|
||||||
@@ -1694,8 +1782,21 @@ int main(int argc, char *argv[]){
|
|||||||
unsigned char interface[128];
|
unsigned char interface[128];
|
||||||
static char tmpstr[MAX_LINES];
|
static char tmpstr[MAX_LINES];
|
||||||
int unicore = 0;
|
int unicore = 0;
|
||||||
|
const char *gpu_selection = "auto";
|
||||||
|
|
||||||
for (i=0;i<argc;i++) {
|
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)) {
|
if(0==strncmp(argv[i],"-d",2)||0==strncmp(argv[i],"--daemon",8)) {
|
||||||
go_daemon=1;
|
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("%s %s - (c) 2008-2010 Mike Lampard, Piotr Czarnecki\n",PACKAGE_NAME,VERSION);
|
||||||
printf("Usage: %s [Options]\n", PACKAGE_NAME);
|
printf("Usage: %s [Options]\n", PACKAGE_NAME);
|
||||||
printf("Options:\n");
|
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("--daemon (-d) run in background\n");
|
||||||
printf("--unicore (-u) display unicore graphs only on the CPU screen\n");
|
printf("--unicore (-u) display unicore graphs only on the CPU screen\n");
|
||||||
printf("--help (-h) this help text.\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){
|
if((g15screen_fd = new_g15_screen(G15_G15RBUF))<0){
|
||||||
printf("Sorry, cant connect to the G15daemon\n");
|
printf("Sorry, cant connect to the G15daemon\n");
|
||||||
return -1;
|
return -1;
|
||||||
@@ -1795,11 +1905,12 @@ int main(int argc, char *argv[]){
|
|||||||
daemonise(0,0);
|
daemonise(0,0);
|
||||||
|
|
||||||
if(canvas != NULL)
|
if(canvas != NULL)
|
||||||
g15r_initCanvas(canvas);
|
init_canvas(canvas);
|
||||||
else
|
else
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
glibtop_init();
|
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);
|
pthread_create(&keys_thread,NULL,(void*)keyboard_watch,NULL);
|
||||||
|
|
||||||
if(have_nic==1)
|
if(have_nic==1)
|
||||||
@@ -1813,6 +1924,7 @@ int main(int argc, char *argv[]){
|
|||||||
int cycle_old = cycle;
|
int cycle_old = cycle;
|
||||||
while(1) {
|
while(1) {
|
||||||
calc_info_cycle();
|
calc_info_cycle();
|
||||||
|
summary_gpu = cycle == SCREEN_SUMMARY && gpu_count;
|
||||||
switch (cycle) {
|
switch (cycle) {
|
||||||
case SCREEN_SUMMARY:
|
case SCREEN_SUMMARY:
|
||||||
case SCREEN_CPU:
|
case SCREEN_CPU:
|
||||||
@@ -1919,6 +2031,8 @@ int main(int argc, char *argv[]){
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cycle_old = cycle;
|
cycle_old = cycle;
|
||||||
|
if (summary_gpu) draw_gpu_column(canvas);
|
||||||
|
summary_gpu = 0;
|
||||||
print_info_label(canvas, tmpstr);
|
print_info_label(canvas, tmpstr);
|
||||||
|
|
||||||
canvas->mode_xor = 0;
|
canvas->mode_xor = 0;
|
||||||
@@ -1926,6 +2040,7 @@ int main(int argc, char *argv[]){
|
|||||||
g15_send(g15screen_fd,(char *)canvas->buffer,G15_BUFFER_LEN);
|
g15_send(g15screen_fd,(char *)canvas->buffer,G15_BUFFER_LEN);
|
||||||
g15stats_wait(wait_seconds);
|
g15stats_wait(wait_seconds);
|
||||||
}
|
}
|
||||||
|
gpu_stop();
|
||||||
glibtop_close();
|
glibtop_close();
|
||||||
|
|
||||||
close(g15screen_fd);
|
close(g15screen_fd);
|
||||||
|
|||||||
@@ -0,0 +1,443 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Read-only Linux GPU telemetry. Included privately by g15stats.c.
|
||||||
|
*/
|
||||||
|
#ifndef G15STATS_GPU_H
|
||||||
|
#define G15STATS_GPU_H
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <glob.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
long long busy, used, total, temp; /* percent, bytes, bytes, Celsius */
|
||||||
|
} gpu_sample;
|
||||||
|
|
||||||
|
static const gpu_sample gpu_unknown = {-1, -1, -1, LLONG_MIN};
|
||||||
|
#define GPU_MAX 64
|
||||||
|
typedef struct {
|
||||||
|
int backend; /* 0 missing, 1 amdgpu, 2 nvidia */
|
||||||
|
char device[PATH_MAX], bus[32];
|
||||||
|
} gpu_adapter;
|
||||||
|
typedef struct {
|
||||||
|
size_t count;
|
||||||
|
gpu_sample samples[GPU_MAX];
|
||||||
|
} gpu_frame;
|
||||||
|
static gpu_adapter gpu_devices[GPU_MAX]; /* immutable while workers run */
|
||||||
|
static size_t gpu_count;
|
||||||
|
static gpu_sample gpu_cache[GPU_MAX];
|
||||||
|
static long long gpu_stamp[GPU_MAX];
|
||||||
|
static pthread_mutex_t gpu_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
static pthread_t gpu_threads[GPU_MAX];
|
||||||
|
static size_t gpu_thread_count;
|
||||||
|
static atomic_int gpu_stopping;
|
||||||
|
static pid_t gpu_pending = -1; /* NVIDIA worker owns until joined. */
|
||||||
|
static long long gpu_epoch;
|
||||||
|
static size_t gpu_page;
|
||||||
|
static long long gpu_page_due;
|
||||||
|
|
||||||
|
static int gpu_selection_valid(const char *selection)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
if (!strcmp(selection, "auto") || !strcmp(selection, "off")) return 1;
|
||||||
|
if (strlen(selection) != 12) return 0;
|
||||||
|
for (i = 0; i < 12; i++) {
|
||||||
|
if (i == 4 || i == 7) { if (selection[i] != ':') return 0; }
|
||||||
|
else if (i == 10) { if (selection[i] != '.') return 0; }
|
||||||
|
else if (!isxdigit((unsigned char)selection[i])) return 0;
|
||||||
|
}
|
||||||
|
return selection[8] <= '1' && selection[11] <= '7';
|
||||||
|
}
|
||||||
|
|
||||||
|
static long long gpu_milliseconds(void)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
if (clock_gettime(CLOCK_MONOTONIC, &now))
|
||||||
|
return 0;
|
||||||
|
return (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reject partial values, overflow and unsupported textual fields. */
|
||||||
|
static long long gpu_number(const char *text, long long min, long long max,
|
||||||
|
long long missing)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
long long value;
|
||||||
|
while (isspace((unsigned char)*text)) text++;
|
||||||
|
if (!isdigit((unsigned char)*text) && *text != '-') return missing;
|
||||||
|
errno = 0;
|
||||||
|
value = strtoll(text, &end, 10);
|
||||||
|
if (end == text || errno) return missing;
|
||||||
|
while (isspace((unsigned char)*end)) end++;
|
||||||
|
return *end || value < min || value > max ? missing : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long long gpu_file(const char *path, long long min, long long max,
|
||||||
|
long long missing)
|
||||||
|
{
|
||||||
|
char text[96];
|
||||||
|
FILE *file = fopen(path, "re");
|
||||||
|
long long value = missing;
|
||||||
|
if (!file) return missing;
|
||||||
|
if (fgets(text, sizeof(text), file) && !ferror(file)) {
|
||||||
|
/* sysfs values fit in one short line; do not accept truncated input. */
|
||||||
|
if (fgetc(file) == EOF && !ferror(file))
|
||||||
|
value = gpu_number(text, min, max, missing);
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select once, in sorted PCI-address order, not unstable DRM card order.
|
||||||
|
* Explicit PCI selection keeps an N/A column even for missing devices.
|
||||||
|
*/
|
||||||
|
static int gpu_detect(const char *selection)
|
||||||
|
{
|
||||||
|
glob_t paths = {0};
|
||||||
|
size_t i;
|
||||||
|
gpu_count = 0;
|
||||||
|
gpu_epoch = gpu_milliseconds();
|
||||||
|
gpu_page = 0;
|
||||||
|
gpu_page_due = gpu_epoch + 5000;
|
||||||
|
memset(gpu_stamp, 0, sizeof(gpu_stamp));
|
||||||
|
for (i = 0; i < GPU_MAX; i++) gpu_cache[i] = gpu_unknown;
|
||||||
|
if (!strcmp(selection, "off")) return 0;
|
||||||
|
int error = glob("/sys/bus/pci/devices/*", 0, NULL, &paths);
|
||||||
|
if (error && error != GLOB_NOMATCH) {
|
||||||
|
globfree(&paths);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
for (i = 0; i < paths.gl_pathc; i++) {
|
||||||
|
char path[PATH_MAX], driver[PATH_MAX];
|
||||||
|
const char *bus = strrchr(paths.gl_pathv[i], '/') + 1;
|
||||||
|
ssize_t len;
|
||||||
|
int backend;
|
||||||
|
if (strcmp(selection, "auto") && strcasecmp(selection, bus)) continue;
|
||||||
|
snprintf(path, sizeof(path), "%s/driver", paths.gl_pathv[i]);
|
||||||
|
len = readlink(path, driver, sizeof(driver) - 1);
|
||||||
|
if (len < 0) continue;
|
||||||
|
driver[len] = 0;
|
||||||
|
if (!strcmp(strrchr(driver, '/') + 1, "amdgpu"))
|
||||||
|
backend = 1;
|
||||||
|
else if (!strcmp(strrchr(driver, '/') + 1, "nvidia"))
|
||||||
|
backend = 2;
|
||||||
|
else continue;
|
||||||
|
if (gpu_count == GPU_MAX) {
|
||||||
|
globfree(&paths);
|
||||||
|
gpu_count = 0;
|
||||||
|
return -1; /* Never silently truncate the inventory. */
|
||||||
|
}
|
||||||
|
gpu_devices[gpu_count].backend = backend;
|
||||||
|
snprintf(gpu_devices[gpu_count].device, PATH_MAX, "%s", paths.gl_pathv[i]);
|
||||||
|
snprintf(gpu_devices[gpu_count].bus, 32, "%s", bus);
|
||||||
|
gpu_count++;
|
||||||
|
}
|
||||||
|
globfree(&paths);
|
||||||
|
if (!gpu_count && strcmp(selection, "auto")) {
|
||||||
|
memset(&gpu_devices[0], 0, sizeof(gpu_devices[0]));
|
||||||
|
snprintf(gpu_devices[0].bus, 32, "%s", selection);
|
||||||
|
gpu_count = 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpu_sample gpu_amd(const gpu_adapter *adapter)
|
||||||
|
{
|
||||||
|
gpu_sample sample = gpu_unknown;
|
||||||
|
char path[PATH_MAX + 64];
|
||||||
|
glob_t paths = {0};
|
||||||
|
snprintf(path, sizeof(path), "%s/gpu_busy_percent", adapter->device);
|
||||||
|
sample.busy = gpu_file(path, 0, 100, -1);
|
||||||
|
snprintf(path, sizeof(path), "%s/mem_info_vram_used", adapter->device);
|
||||||
|
sample.used = gpu_file(path, 0, LLONG_MAX, -1);
|
||||||
|
snprintf(path, sizeof(path), "%s/mem_info_vram_total", adapter->device);
|
||||||
|
sample.total = gpu_file(path, 1, LLONG_MAX, -1);
|
||||||
|
snprintf(path, sizeof(path), "%s/hwmon/hwmon*/temp1_input", adapter->device);
|
||||||
|
if (!glob(path, 0, NULL, &paths) && paths.gl_pathc) {
|
||||||
|
long long temp = gpu_file(paths.gl_pathv[0], -273000, 1000000, LLONG_MIN);
|
||||||
|
if (temp != LLONG_MIN) sample.temp = temp / 1000;
|
||||||
|
}
|
||||||
|
globfree(&paths);
|
||||||
|
if (sample.total >= 0 && sample.used > sample.total) sample.used = -1;
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpu_sample gpu_csv(char *text)
|
||||||
|
{
|
||||||
|
gpu_sample sample = gpu_unknown;
|
||||||
|
char *fields[4], *cursor = text;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
fields[i] = strsep(&cursor, ",");
|
||||||
|
if (!fields[i]) return sample;
|
||||||
|
}
|
||||||
|
if (cursor) return sample;
|
||||||
|
sample.busy = gpu_number(fields[0], 0, 100, -1);
|
||||||
|
sample.used = gpu_number(fields[1], 0, LLONG_MAX / 1048576, -1);
|
||||||
|
sample.total = gpu_number(fields[2], 1, LLONG_MAX / 1048576, -1);
|
||||||
|
sample.temp = gpu_number(fields[3], -273, 1000, LLONG_MIN);
|
||||||
|
if (sample.used >= 0) sample.used *= 1048576;
|
||||||
|
if (sample.total >= 0) sample.total *= 1048576;
|
||||||
|
if (sample.total >= 0 && sample.used > sample.total) sample.used = -1;
|
||||||
|
return sample;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only the collector calls this. Never wait for a driver in the draw thread.
|
||||||
|
* A killed but uninterruptible child remains owned, preventing process buildup.
|
||||||
|
*/
|
||||||
|
static int gpu_above_stdio(int fd)
|
||||||
|
{
|
||||||
|
int copy;
|
||||||
|
if (fd < 0 || fd >= 3) return fd;
|
||||||
|
copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
||||||
|
close(fd);
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpu_command(const char *executable, char *const args[], char *text, size_t capacity)
|
||||||
|
{
|
||||||
|
int pipes[2], status, nullfd;
|
||||||
|
pid_t pid, parent = getpid(), result;
|
||||||
|
size_t used = 0;
|
||||||
|
long long deadline;
|
||||||
|
long maxfd = sysconf(_SC_OPEN_MAX);
|
||||||
|
char *const env[] = {"LC_ALL=C", NULL};
|
||||||
|
if (gpu_pending > 0) {
|
||||||
|
result = waitpid(gpu_pending, &status, WNOHANG);
|
||||||
|
if (!result || (result < 0 && errno == EINTR)) return 0;
|
||||||
|
gpu_pending = -1;
|
||||||
|
}
|
||||||
|
if (pipe2(pipes, O_CLOEXEC | O_NONBLOCK)) return 0;
|
||||||
|
pipes[0] = gpu_above_stdio(pipes[0]);
|
||||||
|
pipes[1] = gpu_above_stdio(pipes[1]);
|
||||||
|
if (pipes[0] < 0 || pipes[1] < 0) {
|
||||||
|
if (pipes[0] >= 0) close(pipes[0]);
|
||||||
|
if (pipes[1] >= 0) close(pipes[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/* Only the reader is nonblocking. A large all-card response must not
|
||||||
|
* make nvidia-smi fail with EAGAIN when the pipe temporarily fills. */
|
||||||
|
if (fcntl(pipes[1], F_SETFL, 0) < 0) {
|
||||||
|
close(pipes[0]);
|
||||||
|
close(pipes[1]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
nullfd = gpu_above_stdio(open("/dev/null", O_RDWR | O_CLOEXEC));
|
||||||
|
if (nullfd < 0) { close(pipes[0]); close(pipes[1]); return 0; }
|
||||||
|
pid = fork();
|
||||||
|
if (!pid) {
|
||||||
|
/* Async-signal-safe child path: no stdio, allocation or shell. */
|
||||||
|
if (prctl(PR_SET_PDEATHSIG, SIGKILL) || getppid() != parent) _exit(127);
|
||||||
|
if (dup2(nullfd, 0) < 0 || dup2(pipes[1], 1) < 0 || dup2(nullfd, 2) < 0)
|
||||||
|
_exit(127);
|
||||||
|
#ifdef SYS_close_range
|
||||||
|
if (syscall(SYS_close_range, 3U, ~0U, 0) < 0)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
long fd;
|
||||||
|
if (maxfd < 0) _exit(127);
|
||||||
|
for (fd = 3; fd < maxfd; fd++) close((int)fd);
|
||||||
|
}
|
||||||
|
execve(executable, args, env);
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
close(nullfd);
|
||||||
|
close(pipes[1]);
|
||||||
|
if (pid < 0) { close(pipes[0]); return 0; }
|
||||||
|
deadline = gpu_milliseconds() + 750;
|
||||||
|
for (;;) {
|
||||||
|
ssize_t n = read(pipes[0], text + used, capacity - 1 - used);
|
||||||
|
if (n > 0) used += (size_t)n;
|
||||||
|
result = waitpid(pid, &status, WNOHANG);
|
||||||
|
if (result == pid) {
|
||||||
|
/* Drain the final bytes after exit before parsing. */
|
||||||
|
while (used < capacity - 1 &&
|
||||||
|
(n = read(pipes[0], text + used, capacity - 1 - used)) > 0)
|
||||||
|
used += (size_t)n;
|
||||||
|
close(pipes[0]);
|
||||||
|
text[used] = 0;
|
||||||
|
return used < capacity - 1 && WIFEXITED(status) && !WEXITSTATUS(status);
|
||||||
|
}
|
||||||
|
if (result < 0 && errno != EINTR) { close(pipes[0]); return 0; }
|
||||||
|
if (atomic_load(&gpu_stopping) || gpu_milliseconds() >= deadline || used == capacity - 1) break;
|
||||||
|
/* Avoid spinning on POLLHUP if stdout closes before the process exits. */
|
||||||
|
poll(NULL, 0, 20);
|
||||||
|
}
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
gpu_pending = pid;
|
||||||
|
close(pipes[0]);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match numeric PCI identity: NVIDIA commonly prints an eight-digit domain.
|
||||||
|
* Malformed/duplicate rows must never contaminate another adapter's sample. */
|
||||||
|
static int gpu_bus_equal(const char *a, const char *b)
|
||||||
|
{
|
||||||
|
unsigned ad, ab, as, af, bd, bb, bs, bf;
|
||||||
|
int n = 0, m = 0;
|
||||||
|
if (sscanf(a, "%8x:%2x:%2x.%1x %n", &ad, &ab, &as, &af, &n) != 4 || a[n]) return 0;
|
||||||
|
if (sscanf(b, "%8x:%2x:%2x.%1x %n", &bd, &bb, &bs, &bf, &m) != 4 || b[m]) return 0;
|
||||||
|
return ad == bd && ab == bb && as == bs && af == bf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpu_nvidia_parse(char *text, gpu_frame *frame)
|
||||||
|
{
|
||||||
|
char *line;
|
||||||
|
unsigned char seen[GPU_MAX] = {0};
|
||||||
|
size_t i;
|
||||||
|
while ((line = strsep(&text, "\n"))) {
|
||||||
|
char *bus = strsep(&line, ",");
|
||||||
|
if (!line) continue;
|
||||||
|
for (i = 0; i < gpu_count; i++) {
|
||||||
|
if (gpu_devices[i].backend != 2 || !gpu_bus_equal(bus, gpu_devices[i].bus)) continue;
|
||||||
|
frame->samples[i] = seen[i] ? gpu_unknown : gpu_csv(line);
|
||||||
|
seen[i] = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpu_nvidia(gpu_frame *frame)
|
||||||
|
{
|
||||||
|
char text[GPU_MAX * 256];
|
||||||
|
char *const args[] = {"nvidia-smi",
|
||||||
|
"--query-gpu=pci.bus_id,utilization.gpu,memory.used,memory.total,temperature.gpu",
|
||||||
|
"--format=csv,noheader,nounits", NULL};
|
||||||
|
if (gpu_command("/usr/bin/nvidia-smi", args, text, sizeof(text)))
|
||||||
|
gpu_nvidia_parse(text, frame);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gpu_publish(size_t i, gpu_sample sample)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&gpu_lock);
|
||||||
|
gpu_cache[i] = sample;
|
||||||
|
gpu_stamp[i] = gpu_milliseconds();
|
||||||
|
pthread_mutex_unlock(&gpu_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each AMD adapter has an independent worker; one NVIDIA worker queries all
|
||||||
|
* NVIDIA cards within one deadline. A stalled sysfs driver cannot stall peers. */
|
||||||
|
static void *gpu_watch(void *arg)
|
||||||
|
{
|
||||||
|
size_t index = (gpu_adapter *)arg - gpu_devices, i;
|
||||||
|
while (!atomic_load(&gpu_stopping)) {
|
||||||
|
if (gpu_devices[index].backend == 1) {
|
||||||
|
gpu_publish(index, gpu_amd(&gpu_devices[index]));
|
||||||
|
} else {
|
||||||
|
gpu_frame frame = {.count = gpu_count};
|
||||||
|
for (i = 0; i < gpu_count; i++) frame.samples[i] = gpu_unknown;
|
||||||
|
gpu_nvidia(&frame);
|
||||||
|
pthread_mutex_lock(&gpu_lock);
|
||||||
|
for (i = 0; i < gpu_count; i++) if (gpu_devices[i].backend == 2) {
|
||||||
|
gpu_cache[i] = frame.samples[i];
|
||||||
|
gpu_stamp[i] = gpu_milliseconds();
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&gpu_lock);
|
||||||
|
}
|
||||||
|
for (i = 0; i < 100 && !atomic_load(&gpu_stopping); i++) poll(NULL, 0, 20);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpu_start(void)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
int nvidia = 0, failed = 0;
|
||||||
|
atomic_store(&gpu_stopping, 0);
|
||||||
|
for (i = 0; i < gpu_count; i++) {
|
||||||
|
if (!gpu_devices[i].backend || (gpu_devices[i].backend == 2 && nvidia++)) continue;
|
||||||
|
if (pthread_create(&gpu_threads[gpu_thread_count], NULL, gpu_watch, &gpu_devices[i])) failed = 1;
|
||||||
|
else gpu_thread_count++;
|
||||||
|
}
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* One shared shutdown deadline, not one per adapter. A stuck kernel read may
|
||||||
|
* outlive this call; its static storage remains valid until process exit.
|
||||||
|
* Never redetect/restart after a failed stop. */
|
||||||
|
static int gpu_stop(void)
|
||||||
|
{
|
||||||
|
struct timespec deadline;
|
||||||
|
long long end = gpu_milliseconds() + 1000;
|
||||||
|
size_t i;
|
||||||
|
int failed = 0;
|
||||||
|
atomic_store(&gpu_stopping, 1);
|
||||||
|
clock_gettime(CLOCK_REALTIME, &deadline);
|
||||||
|
deadline.tv_sec++;
|
||||||
|
for (i = 0; i < gpu_thread_count; i++) {
|
||||||
|
if (pthread_timedjoin_np(gpu_threads[i], NULL, &deadline)) {
|
||||||
|
pthread_detach(gpu_threads[i]);
|
||||||
|
failed = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gpu_thread_count = 0;
|
||||||
|
if (!failed && gpu_pending > 0) {
|
||||||
|
int status;
|
||||||
|
pid_t result;
|
||||||
|
do {
|
||||||
|
result = waitpid(gpu_pending, &status, WNOHANG);
|
||||||
|
if (result == gpu_pending || (result < 0 && errno == ECHILD)) {
|
||||||
|
gpu_pending = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
poll(NULL, 0, 5);
|
||||||
|
} while (gpu_milliseconds() < end);
|
||||||
|
if (gpu_pending > 0) failed = 1;
|
||||||
|
}
|
||||||
|
return failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpu_frame gpu_snapshot(void)
|
||||||
|
{
|
||||||
|
gpu_frame frame = {.count = gpu_count};
|
||||||
|
size_t i;
|
||||||
|
pthread_mutex_lock(&gpu_lock);
|
||||||
|
for (i = 0; i < gpu_count; i++)
|
||||||
|
frame.samples[i] = !gpu_stamp[i] || gpu_milliseconds() - gpu_stamp[i] > 5000 ? gpu_unknown : gpu_cache[i];
|
||||||
|
pthread_mutex_unlock(&gpu_lock);
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t gpu_page_first(size_t count, unsigned long long elapsed)
|
||||||
|
{
|
||||||
|
return count > 2 ? (elapsed / 5000 % ((count + 1) / 2)) * 2 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Main thread only. Advance at most one pair per actual draw, so -r 10 (or
|
||||||
|
* leaving the summary screen) cannot alias the cycle and hide half the GPUs. */
|
||||||
|
static size_t gpu_display_first(size_t count, long long now)
|
||||||
|
{
|
||||||
|
if (count > 2 && now >= gpu_page_due) {
|
||||||
|
gpu_page = (gpu_page + 1) % ((count + 1) / 2);
|
||||||
|
gpu_page_due = now + 5000;
|
||||||
|
}
|
||||||
|
return gpu_page_first(count, gpu_page * 5000ULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Each line fits the 52-pixel column (13 small-font character cells).
|
||||||
|
* Separate used/total rows retain useful precision, even on large cards.
|
||||||
|
*/
|
||||||
|
static void gpu_memory(char *text, size_t size, long long bytes)
|
||||||
|
{
|
||||||
|
const char *unit = "MiB";
|
||||||
|
double value = (double)bytes / 1048576;
|
||||||
|
if (bytes < 0) { snprintf(text, size, "N/A"); return; }
|
||||||
|
if (value >= 1024) { value /= 1024; unit = "GiB"; }
|
||||||
|
if (value >= 1024) { value /= 1024; unit = "TiB"; }
|
||||||
|
if (value >= 1024) { value /= 1024; unit = "PiB"; }
|
||||||
|
snprintf(text, size, "%.1f%s", value, unit);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,284 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||||
|
#define _GNU_SOURCE 1
|
||||||
|
#include "../gpu.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
static int descriptors(void)
|
||||||
|
{
|
||||||
|
DIR *dir = opendir("/proc/self/fd");
|
||||||
|
struct dirent *entry;
|
||||||
|
int count = 0;
|
||||||
|
assert(dir);
|
||||||
|
while ((entry = readdir(dir)))
|
||||||
|
if (entry->d_name[0] != '.') count++;
|
||||||
|
closedir(dir);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unknown(gpu_sample s)
|
||||||
|
{
|
||||||
|
assert(s.busy == -1 && s.used == -1 && s.total == -1 && s.temp == LLONG_MIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gpu_sample test_command(const char *path, char *const args[])
|
||||||
|
{
|
||||||
|
char text[256];
|
||||||
|
return gpu_command(path, args, text, sizeof(text)) ? gpu_csv(text) : gpu_unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *cache_writer(void *arg)
|
||||||
|
{
|
||||||
|
size_t index = *(size_t *)arg;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < 20000; i++)
|
||||||
|
gpu_publish(index, (gpu_sample){i % 101, i, i + 1, (long long)index});
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cache_concurrency(void)
|
||||||
|
{
|
||||||
|
pthread_t writers[2];
|
||||||
|
size_t indices[2] = {0, 1}, i;
|
||||||
|
int repeat;
|
||||||
|
gpu_count = 2;
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
gpu_publish(i, (gpu_sample){0, 0, 1, (long long)i});
|
||||||
|
assert(!pthread_create(&writers[i], NULL, cache_writer, &indices[i]));
|
||||||
|
}
|
||||||
|
for (repeat = 0; repeat < 20000; repeat++) {
|
||||||
|
gpu_frame frame = gpu_snapshot();
|
||||||
|
assert(frame.count == 2);
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
assert(frame.samples[i].total == frame.samples[i].used + 1);
|
||||||
|
assert(frame.samples[i].busy == frame.samples[i].used % 101);
|
||||||
|
assert(frame.samples[i].temp == (long long)i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (i = 0; i < 2; i++) assert(!pthread_join(writers[i], NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parent_death(void)
|
||||||
|
{
|
||||||
|
pid_t child, grandchild = 0, result;
|
||||||
|
long long deadline;
|
||||||
|
char path[96];
|
||||||
|
int status;
|
||||||
|
assert(!prctl(PR_SET_CHILD_SUBREAPER, 1));
|
||||||
|
child = fork();
|
||||||
|
assert(child >= 0);
|
||||||
|
if (!child) {
|
||||||
|
char *const args[] = {"sleep", "10", NULL};
|
||||||
|
test_command("/usr/bin/sleep", args);
|
||||||
|
_exit(0);
|
||||||
|
}
|
||||||
|
snprintf(path, sizeof(path), "/proc/%ld/task/%ld/children", (long)child, (long)child);
|
||||||
|
deadline = gpu_milliseconds() + 500;
|
||||||
|
do {
|
||||||
|
FILE *file = fopen(path, "r");
|
||||||
|
long id;
|
||||||
|
if (file) {
|
||||||
|
if (fscanf(file, "%ld", &id) == 1) grandchild = (pid_t)id;
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
if (!grandchild) poll(NULL, 0, 5);
|
||||||
|
} while (!grandchild && gpu_milliseconds() < deadline);
|
||||||
|
assert(grandchild > 0);
|
||||||
|
assert(!kill(child, SIGKILL));
|
||||||
|
assert(waitpid(child, &status, 0) == child);
|
||||||
|
deadline = gpu_milliseconds() + 1000;
|
||||||
|
do {
|
||||||
|
result = waitpid(grandchild, &status, WNOHANG);
|
||||||
|
if (!result) poll(NULL, 0, 5);
|
||||||
|
} while (!result && gpu_milliseconds() < deadline);
|
||||||
|
assert(result == grandchild && WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL);
|
||||||
|
assert(!prctl(PR_SET_CHILD_SUBREAPER, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
gpu_sample s;
|
||||||
|
char text[256], memory[32];
|
||||||
|
char *const sleeper[] = {"sleep", "10", NULL};
|
||||||
|
char *const failure[] = {"false", NULL};
|
||||||
|
long long start;
|
||||||
|
int status;
|
||||||
|
pid_t result;
|
||||||
|
int fd_count, repeat;
|
||||||
|
assert(gpu_page_first(0, 5000) == 0);
|
||||||
|
assert(gpu_page_first(2, 5000) == 0);
|
||||||
|
assert(gpu_page_first(3, 5000) == 2);
|
||||||
|
assert(gpu_page_first(3, 10000) == 0);
|
||||||
|
for (repeat = 0; repeat < GPU_MAX; repeat += 2)
|
||||||
|
assert(gpu_page_first(GPU_MAX, (unsigned long long)repeat / 2 * 5000) == (size_t)repeat);
|
||||||
|
gpu_page = 0;
|
||||||
|
gpu_page_due = 5000;
|
||||||
|
assert(gpu_display_first(5, 0) == 0);
|
||||||
|
assert(gpu_display_first(5, 10000) == 2);
|
||||||
|
assert(gpu_display_first(5, 20000) == 4);
|
||||||
|
assert(gpu_display_first(5, 30000) == 0);
|
||||||
|
assert(gpu_display_first(5, 30001) == 0);
|
||||||
|
assert(gpu_selection_valid("auto") && gpu_selection_valid("off"));
|
||||||
|
assert(gpu_selection_valid("0000:AB:1f.7"));
|
||||||
|
assert(!gpu_selection_valid("0000:01:20.0"));
|
||||||
|
assert(!gpu_selection_valid("0000:01:00.8"));
|
||||||
|
assert(!gpu_selection_valid("-001:01:00.0"));
|
||||||
|
assert(!gpu_selection_valid("../../passwd"));
|
||||||
|
assert(!gpu_selection_valid("0000:01:00.0;id"));
|
||||||
|
assert(gpu_number(" 0\n", 0, 100, -1) == 0);
|
||||||
|
assert(gpu_number("100", 0, 100, -1) == 100);
|
||||||
|
assert(gpu_number("101", 0, 100, -1) == -1);
|
||||||
|
assert(gpu_number("12oops", 0, 100, -1) == -1);
|
||||||
|
assert(gpu_number("", 0, 100, -1) == -1);
|
||||||
|
assert(gpu_number("N/A", 0, 100, -1) == -1);
|
||||||
|
assert(gpu_number("999999999999999999999999", 0, LLONG_MAX, -1) == -1);
|
||||||
|
strcpy(text, "100, 12288, 16384, -5\n");
|
||||||
|
s = gpu_csv(text);
|
||||||
|
assert(s.busy == 100 && s.used == 12884901888LL &&
|
||||||
|
s.total == 17179869184LL && s.temp == -5);
|
||||||
|
strcpy(text, "N/A, [Not Supported], 16384, 0\n");
|
||||||
|
s = gpu_csv(text);
|
||||||
|
assert(s.busy == -1 && s.used == -1 && s.total == 17179869184LL && s.temp == 0);
|
||||||
|
strcpy(text, "0, 0, 0, N/A\n");
|
||||||
|
s = gpu_csv(text);
|
||||||
|
assert(s.busy == 0 && s.used == 0 && s.total == -1 && s.temp == LLONG_MIN);
|
||||||
|
strcpy(text, "0, 20, 10, 35\n");
|
||||||
|
assert(gpu_csv(text).used == -1);
|
||||||
|
strcpy(text, "0, 1, 2"); unknown(gpu_csv(text));
|
||||||
|
strcpy(text, "0, 1, 2, 3, 4"); unknown(gpu_csv(text));
|
||||||
|
strcpy(text, "N/A, N/A, N/A, N/A"); unknown(gpu_csv(text));
|
||||||
|
gpu_memory(memory, sizeof(memory), -1); assert(!strcmp(memory, "N/A"));
|
||||||
|
gpu_memory(memory, sizeof(memory), 19 * 1048576); assert(!strcmp(memory, "19.0MiB"));
|
||||||
|
gpu_memory(memory, sizeof(memory), 17179869184LL); assert(!strcmp(memory, "16.0GiB"));
|
||||||
|
gpu_memory(memory, sizeof(memory), LLONG_MAX); assert(strlen(memory) <= 11);
|
||||||
|
assert(!gpu_detect("off") && !gpu_count);
|
||||||
|
assert(!gpu_detect("ffff:ff:1f.7") && gpu_count == 1 && !gpu_devices[0].backend);
|
||||||
|
gpu_cache[0] = s;
|
||||||
|
gpu_stamp[0] = gpu_milliseconds() - 6000;
|
||||||
|
unknown(gpu_snapshot().samples[0]);
|
||||||
|
{
|
||||||
|
gpu_frame frame = {.count = 3};
|
||||||
|
gpu_count = 3;
|
||||||
|
for (repeat = 0; repeat < 3; repeat++) {
|
||||||
|
snprintf(gpu_devices[repeat].bus, 32, "0000:%02x:00.0", repeat + 1);
|
||||||
|
gpu_devices[repeat].backend = repeat == 1 ? 1 : 2;
|
||||||
|
frame.samples[repeat] = gpu_unknown;
|
||||||
|
}
|
||||||
|
frame.samples[1] = (gpu_sample){77, 1, 2, 42};
|
||||||
|
strcpy(text, "00000000:03:00.0, 90, 4, 8, 55\n00000000:01:00.0, N/A, 1, 2, N/A\n");
|
||||||
|
gpu_nvidia_parse(text, &frame);
|
||||||
|
assert(frame.samples[0].busy == -1 && frame.samples[0].used == 1048576);
|
||||||
|
assert(frame.samples[1].busy == 77 && frame.samples[1].used == 1);
|
||||||
|
assert(frame.samples[2].busy == 90 && frame.samples[2].temp == 55);
|
||||||
|
strcpy(text, "00000000:03:00.0, 1, 4, 8, 55\n0000:03:00.0, 2, 4, 8, 55\n");
|
||||||
|
gpu_nvidia_parse(text, &frame);
|
||||||
|
unknown(frame.samples[2]);
|
||||||
|
assert(!gpu_bus_equal("0000:01:00.0junk", "0000:01:00.0"));
|
||||||
|
assert(gpu_bus_equal("00000000:AB:00.0 ", "0000:ab:00.0"));
|
||||||
|
assert(!gpu_bus_equal("0001:01:00.0", "0000:01:00.0"));
|
||||||
|
for (repeat = 0; repeat < 3; repeat++) gpu_publish(repeat, frame.samples[repeat]);
|
||||||
|
gpu_stamp[0] = gpu_milliseconds() - 6000;
|
||||||
|
frame = gpu_snapshot();
|
||||||
|
unknown(frame.samples[0]);
|
||||||
|
assert(frame.samples[1].busy == 77);
|
||||||
|
unknown(frame.samples[2]);
|
||||||
|
/* Missing AMD path is independent of the populated neighboring cache. */
|
||||||
|
snprintf(gpu_devices[0].device, PATH_MAX, "/nonexistent/g15stats-test");
|
||||||
|
gpu_publish(0, gpu_amd(&gpu_devices[0]));
|
||||||
|
assert(gpu_snapshot().samples[1].busy == 77);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Real processes exercise the deadline and reaping, not fake GPU files. */
|
||||||
|
start = gpu_milliseconds();
|
||||||
|
unknown(test_command("/usr/bin/sleep", sleeper));
|
||||||
|
assert(gpu_milliseconds() - start >= 750);
|
||||||
|
assert(gpu_milliseconds() - start < 1500);
|
||||||
|
poll(NULL, 0, 50);
|
||||||
|
unknown(test_command("/usr/bin/false", failure));
|
||||||
|
result = waitpid(-1, &status, WNOHANG);
|
||||||
|
assert(result == -1 && errno == ECHILD);
|
||||||
|
unknown(test_command("/nonexistent/g15stats-test", failure));
|
||||||
|
fd_count = descriptors();
|
||||||
|
for (repeat = 0; repeat < 20; repeat++)
|
||||||
|
unknown(test_command("/usr/bin/false", failure));
|
||||||
|
assert(descriptors() == fd_count);
|
||||||
|
{
|
||||||
|
char payload[12001], output[GPU_MAX * 256];
|
||||||
|
char *const args[] = {"printf", "%s", payload, NULL};
|
||||||
|
memset(payload, 'x', sizeof(payload) - 1);
|
||||||
|
payload[sizeof(payload) - 1] = 0;
|
||||||
|
assert(gpu_command("/usr/bin/printf", args, output, sizeof(output)));
|
||||||
|
assert(!strcmp(payload, output));
|
||||||
|
assert(!gpu_command("/usr/bin/printf", args, output, 256));
|
||||||
|
poll(NULL, 0, 50);
|
||||||
|
unknown(test_command("/usr/bin/false", failure));
|
||||||
|
}
|
||||||
|
parent_death();
|
||||||
|
cache_concurrency();
|
||||||
|
|
||||||
|
if (argc == 2) {
|
||||||
|
gpu_frame frame;
|
||||||
|
size_t i;
|
||||||
|
assert(!gpu_detect(argv[1]) && gpu_count);
|
||||||
|
assert(!gpu_start());
|
||||||
|
start = gpu_milliseconds();
|
||||||
|
do {
|
||||||
|
frame = gpu_snapshot();
|
||||||
|
for (i = 0; i < frame.count; i++) if (frame.samples[i].total <= 0) break;
|
||||||
|
if (i == frame.count) break;
|
||||||
|
poll(NULL, 0, 10);
|
||||||
|
} while (gpu_milliseconds() - start < 1800);
|
||||||
|
for (i = 0; i < frame.count; i++) {
|
||||||
|
char path[PATH_MAX + 32];
|
||||||
|
s = frame.samples[i];
|
||||||
|
if (i) assert(strcmp(gpu_devices[i-1].bus, gpu_devices[i].bus) < 0);
|
||||||
|
snprintf(path, sizeof(path), "%s/vendor", gpu_devices[i].device);
|
||||||
|
FILE *vendor = fopen(path, "r");
|
||||||
|
unsigned id;
|
||||||
|
assert(vendor && fscanf(vendor, "%x", &id) == 1);
|
||||||
|
fclose(vendor);
|
||||||
|
assert(id == (gpu_devices[i].backend == 1 ? 0x1002U : 0x10deU));
|
||||||
|
printf("LIVE %zu %s vendor=%04x backend=%d utilization=%lld used=%lld total=%lld temp=%lld\n",
|
||||||
|
i+1, gpu_devices[i].bus, id, gpu_devices[i].backend, s.busy, s.used, s.total, s.temp);
|
||||||
|
assert(s.busy >= 0 && s.used >= 0 && s.total > 0 && s.temp != LLONG_MIN);
|
||||||
|
}
|
||||||
|
for (repeat = 0; repeat < 100000; repeat++) {
|
||||||
|
frame = gpu_snapshot();
|
||||||
|
assert(frame.count == gpu_count);
|
||||||
|
for (i = 0; i < frame.count; i++) assert(frame.samples[i].total > 0);
|
||||||
|
}
|
||||||
|
start = gpu_milliseconds();
|
||||||
|
assert(!gpu_stop());
|
||||||
|
assert(gpu_milliseconds() - start < 1200);
|
||||||
|
puts("PASS all live adapters, sysfs vendor identities, concurrent snapshots and bounded shutdown");
|
||||||
|
/* Read-only failure injection: point just the stopped AMD collector at
|
||||||
|
* a nonexistent path, then prove the actual NVIDIA peer remains live. */
|
||||||
|
if (gpu_count > 1) {
|
||||||
|
size_t amd = GPU_MAX, nv = GPU_MAX;
|
||||||
|
char saved[PATH_MAX];
|
||||||
|
for (i = 0; i < gpu_count; i++) {
|
||||||
|
if (gpu_devices[i].backend == 1) amd = i;
|
||||||
|
if (gpu_devices[i].backend == 2) nv = i;
|
||||||
|
}
|
||||||
|
if (amd < GPU_MAX && nv < GPU_MAX) {
|
||||||
|
strcpy(saved, gpu_devices[amd].device);
|
||||||
|
strcpy(gpu_devices[amd].device, "/nonexistent/g15stats-test");
|
||||||
|
for (i = 0; i < gpu_count; i++) gpu_publish(i, gpu_unknown);
|
||||||
|
assert(!gpu_start());
|
||||||
|
start = gpu_milliseconds();
|
||||||
|
do {
|
||||||
|
frame = gpu_snapshot();
|
||||||
|
if (frame.samples[nv].total > 0) break;
|
||||||
|
poll(NULL, 0, 10);
|
||||||
|
} while (gpu_milliseconds() - start < 1800);
|
||||||
|
assert(frame.samples[nv].total > 0 && frame.samples[nv].busy >= 0);
|
||||||
|
unknown(frame.samples[amd]);
|
||||||
|
assert(!gpu_stop());
|
||||||
|
strcpy(gpu_devices[amd].device, saved);
|
||||||
|
puts("PASS failed AMD collector isolated from real live NVIDIA peer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
puts("PASS GPU parsing, units, missing data, expiry, selection, deadline, reap and parent death");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Convert existing render-test exports without trimming, resampling or labels
|
||||||
|
# inside the framebuffer. ImageMagick is optional, not a make check dependency.
|
||||||
|
set -eu
|
||||||
|
if [ "$#" -ne 1 ]; then
|
||||||
|
echo "Usage: sh tests/render-images.sh ABSOLUTE_OUTPUT_DIRECTORY" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
cd "$1"
|
||||||
|
for pgm in ./*-[01].pgm; do
|
||||||
|
[ -f "$pgm" ] || continue
|
||||||
|
base=${pgm%.pgm}
|
||||||
|
[ "$(wc -c < "$base.bin")" -eq 860 ]
|
||||||
|
{ printf 'P4\n160 43\n'; cat "$base.bin"; } > "$base.pbm"
|
||||||
|
# Compare the packed 1-bit buffer independently with the PGM export.
|
||||||
|
compare -metric AE "$base.pbm" "$pgm" null: 2> "$base-pixels.log"
|
||||||
|
convert "$pgm" +repage -type TrueColor "PNG24:$base-native.png"
|
||||||
|
convert "$base-native.png" -sample 800% +repage "PNG24:$base-8x.png"
|
||||||
|
convert "$base-8x.png" -sample 160x43! +repage "PNG24:$base-roundtrip.png"
|
||||||
|
compare -metric AE "$pgm" "$base-native.png" null: 2>> "$base-pixels.log"
|
||||||
|
compare -metric AE "$pgm" "$base-roundtrip.png" null: 2>> "$base-pixels.log"
|
||||||
|
rm "$base-roundtrip.png"
|
||||||
|
# Smaller contact tiles avoid shrinking wide sheets in image viewers.
|
||||||
|
# Keep the eightfold originals available for individual pixel inspection.
|
||||||
|
convert "$base-native.png" -sample 400% +repage "PNG24:$base-4x.png"
|
||||||
|
done
|
||||||
|
for group in off nvidia amd single dual large unknown odd maximum; do
|
||||||
|
[ -f "$group-0-4x.png" ] || continue
|
||||||
|
montage -font DejaVu-Sans -pointsize 14 -background '#dddddd' \
|
||||||
|
-label "$group: five-row summary (4x)" "$group-0-4x.png" \
|
||||||
|
-label "$group: four-row summary (4x)" "$group-1-4x.png" \
|
||||||
|
-tile 1x2 -geometry +8+8 "PNG24:contact-$group.png"
|
||||||
|
height=$(identify -format '%h' "contact-$group.png")
|
||||||
|
for variant in 0 1; do
|
||||||
|
top=$((8 + variant * height / 2))
|
||||||
|
convert "contact-$group.png" -crop "640x172+8+$top" +repage \
|
||||||
|
"PNG24:$group-tile-check.png"
|
||||||
|
compare -metric AE "$group-$variant-4x.png" "$group-tile-check.png" \
|
||||||
|
null: 2>> "$group-contact-pixels.log"
|
||||||
|
done
|
||||||
|
rm "$group-tile-check.png"
|
||||||
|
done
|
||||||
|
if [ -f contact-off.png ] && [ -f contact-nvidia.png ] && [ -f contact-amd.png ]; then
|
||||||
|
convert contact-off.png contact-nvidia.png contact-amd.png -append +repage \
|
||||||
|
PNG24:contact-legacy.png
|
||||||
|
fi
|
||||||
|
if [ -f contact-dual.png ] && [ -f contact-large.png ] && [ -f contact-unknown.png ]; then
|
||||||
|
convert contact-dual.png contact-large.png contact-unknown.png -append +repage \
|
||||||
|
PNG24:contact-metrics.png
|
||||||
|
fi
|
||||||
|
if [ -f contact-odd.png ] && [ -f contact-maximum.png ]; then
|
||||||
|
convert contact-odd.png contact-maximum.png -append +repage PNG24:contact-paging.png
|
||||||
|
fi
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Native-pixel checks only: no image conversion, daemon or input-device access.
|
||||||
|
set -eu
|
||||||
|
renderer=${RENDER_TEST:-./render-test}
|
||||||
|
out=$(mktemp -d "${TMPDIR:-/tmp}/g15stats-layouts-XXXXXX")
|
||||||
|
trap 'rm -rf "$out"' EXIT HUP INT TERM
|
||||||
|
"$renderer" off "$out/off"
|
||||||
|
"$renderer" off "$out/single" 1
|
||||||
|
"$renderer" off "$out/dual" 2
|
||||||
|
"$renderer" off "$out/large" 2 0 large
|
||||||
|
"$renderer" off "$out/unknown" 2 0 unknown
|
||||||
|
"$renderer" off "$out/odd" 3 1
|
||||||
|
"$renderer" off "$out/maximum" 64 31
|
||||||
@@ -0,0 +1,230 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
* Exercise the real renderer without any daemon connection or input thread.
|
||||||
|
*/
|
||||||
|
#define main g15stats_application_main
|
||||||
|
#include "../g15stats.c"
|
||||||
|
#undef main
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/* Independent font-bitmap oracle: do not repeat renderString on a second
|
||||||
|
* canvas, since that also repeats any clipping/erasure in the renderer.
|
||||||
|
* The default fonts have zero gap and one blank trailing pixel column.
|
||||||
|
* Compare every cell, including blank pixels, against the font file's bits. */
|
||||||
|
static void assert_text(g15canvas *canvas, const char *text, int size, int x, int y, int right)
|
||||||
|
{
|
||||||
|
g15font *font = g15r_requestG15DefaultFont(size);
|
||||||
|
assert(font && font->default_gap == 0);
|
||||||
|
assert(font->font_height == font->ascender_height);
|
||||||
|
for (; *text; text++) {
|
||||||
|
unsigned char c = (unsigned char)*text;
|
||||||
|
int width = font->glyph[c].width;
|
||||||
|
int stride = (width + 7) / 8;
|
||||||
|
assert(font->active[c] && font->glyph[c].buffer);
|
||||||
|
assert(x >= 0 && x + width <= 160);
|
||||||
|
assert(y >= 0 && y + font->font_height <= 43);
|
||||||
|
for (int dy = 0; dy < font->font_height; dy++)
|
||||||
|
for (int dx = 0; dx < width; dx++) {
|
||||||
|
int bit = !!(font->glyph[c].buffer[dy * stride + dx / 8] &
|
||||||
|
(0x80 >> (dx % 8)));
|
||||||
|
/* A 13-character first-column header's final blank advance
|
||||||
|
* pixel shares x=105 with the next divider; no ink may do so. */
|
||||||
|
if (x + dx >= right) {
|
||||||
|
assert(!text[1] && dx == width - 1 && !bit);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (g15r_getPixel(canvas, x + dx, y + dy) != bit) {
|
||||||
|
fprintf(stderr, "Glyph %c pixel (%d,%d): expected %d, got %d\n",
|
||||||
|
c, x + dx, y + dy, bit,
|
||||||
|
g15r_getPixel(canvas, x + dx, y + dy));
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x += width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
g15canvas canvas, before;
|
||||||
|
char tmp[MAX_LINES];
|
||||||
|
int x, y, variant;
|
||||||
|
size_t i;
|
||||||
|
g15font *font = g15r_requestG15DefaultFont(G15_TEXT_SMALL);
|
||||||
|
assert(font);
|
||||||
|
assert(g15r_testG15FontWidth(font, "T 8192.0PiB") <= 52);
|
||||||
|
assert(g15r_testG15FontWidth(font, "UTL 100%") <= 52);
|
||||||
|
{
|
||||||
|
char lines[5][32];
|
||||||
|
compact_ram_lines(lines, LLONG_MAX, LLONG_MAX);
|
||||||
|
assert(strstr(lines[1], "PiB") && strstr(lines[2], "PiB"));
|
||||||
|
assert(g15r_testG15FontWidth(font, lines[1]) <= 50);
|
||||||
|
assert(g15r_testG15FontWidth(font, lines[2]) <= 50);
|
||||||
|
compact_ram_lines(lines, 19 * 1048576, 17179869184LL);
|
||||||
|
assert(!strcmp(lines[1], "R 19.0MiB"));
|
||||||
|
assert(!strcmp(lines[2], "T 16.0GiB"));
|
||||||
|
}
|
||||||
|
memset(&canvas, 0xa5, sizeof(canvas));
|
||||||
|
init_canvas(&canvas);
|
||||||
|
assert(!canvas.mode_xor && !canvas.mode_reverse && !canvas.mode_cache);
|
||||||
|
for (y = 0; y < 43; y++) for (x = 0; x < 160; x++)
|
||||||
|
assert(!g15r_getPixel(&canvas, x, y));
|
||||||
|
/* Original full-width geometry must remain exact with the column off. */
|
||||||
|
init_canvas(&before);
|
||||||
|
summary_gpu = 0;
|
||||||
|
drawBar_both(&canvas, 1, 8, 37, 100, 63, 100);
|
||||||
|
g15r_drawBar(&before, BAR_START, 1, BAR_END, 8, G15_COLOR_BLACK, 37, 100, 4);
|
||||||
|
drawBar_reversed(&before, BAR_START, 1, BAR_END, 8, G15_COLOR_BLACK, 63, 100, 5);
|
||||||
|
assert(!memcmp(canvas.buffer, before.buffer, sizeof(canvas.buffer)));
|
||||||
|
glibtop_init();
|
||||||
|
/* No device fixtures: sample the actual selected GPU when requested. */
|
||||||
|
assert(!gpu_detect(argc > 1 ? argv[1] : "off"));
|
||||||
|
if (gpu_count && gpu_devices[0].backend) {
|
||||||
|
long long start = gpu_milliseconds();
|
||||||
|
assert(!gpu_start());
|
||||||
|
do {
|
||||||
|
gpu_frame frame = gpu_snapshot();
|
||||||
|
for (i = 0; i < frame.count; i++) if (frame.samples[i].total < 0) break;
|
||||||
|
if (i == frame.count) break;
|
||||||
|
poll(NULL, 0, 10);
|
||||||
|
} while (gpu_milliseconds() - start < 1800);
|
||||||
|
assert(i == gpu_count);
|
||||||
|
assert(!gpu_stop());
|
||||||
|
}
|
||||||
|
/* No-argument make check retains the worst-width fixture. Explicit off
|
||||||
|
* must exercise the application's actual zero-GPU/full-width layout.
|
||||||
|
* Optional count/page/profile arguments are in-memory visual cases only. */
|
||||||
|
if (!gpu_count && (argc == 1 || argc > 3)) {
|
||||||
|
gpu_count = argc > 3 ? (size_t)atoi(argv[3]) : 2;
|
||||||
|
assert(gpu_count >= 1 && gpu_count <= GPU_MAX);
|
||||||
|
gpu_devices[0].backend = 2;
|
||||||
|
gpu_devices[1].backend = 1;
|
||||||
|
strcpy(gpu_devices[0].bus, "0000:01:00.0");
|
||||||
|
strcpy(gpu_devices[1].bus, "0000:11:00.0");
|
||||||
|
gpu_publish(0, (gpu_sample){100, LLONG_MAX, LLONG_MAX, -273});
|
||||||
|
gpu_publish(1, (gpu_sample){100, LLONG_MAX, LLONG_MAX, 1000});
|
||||||
|
for (i = 2; i < gpu_count; i++) {
|
||||||
|
gpu_devices[i].backend = 1;
|
||||||
|
snprintf(gpu_devices[i].bus, 32, "0001:%02x:1f.7", (unsigned)i);
|
||||||
|
gpu_publish(i, i % 2 ? gpu_unknown : (gpu_sample){100, LLONG_MAX, LLONG_MAX, -273});
|
||||||
|
}
|
||||||
|
if (gpu_count > 2) {
|
||||||
|
/* Exercise every pair at the maximum inventory, including the
|
||||||
|
* 13-character AMD64 header and an odd final card. */
|
||||||
|
for (i = 0; i < (gpu_count + 1) / 2; i++) {
|
||||||
|
gpu_page = i;
|
||||||
|
gpu_page_due = LLONG_MAX;
|
||||||
|
g15r_clearScreen(&canvas, G15_COLOR_WHITE);
|
||||||
|
draw_gpu_column(&canvas);
|
||||||
|
for (y = 35; y < 43; y++) for (x = 0; x < 160; x++)
|
||||||
|
assert(!g15r_getPixel(&canvas, x, y));
|
||||||
|
}
|
||||||
|
/* Keep the final pair for the exported preview and pixel checks. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc > 4) {
|
||||||
|
gpu_page = (size_t)atoi(argv[4]);
|
||||||
|
assert(gpu_page < (gpu_count + 1) / 2);
|
||||||
|
gpu_page_due = LLONG_MAX;
|
||||||
|
}
|
||||||
|
if (argc > 5) {
|
||||||
|
assert(!strcmp(argv[5], "large") || !strcmp(argv[5], "unknown"));
|
||||||
|
for (i = 0; i < gpu_count; i++)
|
||||||
|
gpu_publish(i, !strcmp(argv[5], "unknown") ? gpu_unknown :
|
||||||
|
(gpu_sample){100, 100LL << 30, 192LL << 30, 1000});
|
||||||
|
}
|
||||||
|
for (i = 0; i < gpu_count; i++) {
|
||||||
|
char lines[5][32];
|
||||||
|
int row;
|
||||||
|
gpu_lines(i, gpu_cache[i], lines);
|
||||||
|
for (row = 0; row < 5; row++) {
|
||||||
|
assert(g15r_testG15FontWidth(font, lines[row]) <= 52);
|
||||||
|
printf("GPU %zu row %d: %s\n", i + 1, row, lines[row]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (variant = 0; variant < 2; variant++) {
|
||||||
|
cycle = SCREEN_SUMMARY;
|
||||||
|
mode[SCREEN_SUMMARY] = variant;
|
||||||
|
have_temp = have_fan = 0; /* Keep legacy generic hwmon probing out of QA. */
|
||||||
|
summary_gpu = gpu_count != 0;
|
||||||
|
draw_cpu_screen_multicore(&canvas, tmp, 0);
|
||||||
|
before = canvas;
|
||||||
|
draw_gpu_column(&canvas);
|
||||||
|
for (y = 0; y < 43; y++)
|
||||||
|
for (x = 0; x < 160; x++)
|
||||||
|
if (x < (gpu_count > 1 ? 51 : 105) || y >= 35)
|
||||||
|
assert(g15r_getPixel(&canvas, x, y) == g15r_getPixel(&before, x, y));
|
||||||
|
/* Re-render each column on an empty canvas: exact pixels prove neither
|
||||||
|
* neighboring column nor CPU text overlaps it, including edge glyphs. */
|
||||||
|
{
|
||||||
|
gpu_frame frame = gpu_snapshot();
|
||||||
|
size_t first = gpu_page_first(frame.count, gpu_page * 5000ULL);
|
||||||
|
for (i = first; i < frame.count && i < first + 2; i++) {
|
||||||
|
g15canvas isolated;
|
||||||
|
char lines[5][32];
|
||||||
|
int row, left = frame.count == 1 ? 108 : 54 + (int)(i-first) * 54;
|
||||||
|
init_canvas(&isolated);
|
||||||
|
gpu_lines(i, frame.samples[i], lines);
|
||||||
|
for (row = 0; row < 5; row++)
|
||||||
|
g15r_renderString(&isolated, (unsigned char *)lines[row], 0, G15_TEXT_SMALL, left, 1+row*7);
|
||||||
|
for (y = 0; y < 35; y++) for (x = left; x < (left == 54 ? 105 : 160); x++)
|
||||||
|
assert(g15r_getPixel(&canvas, x, y) == g15r_getPixel(&isolated, x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
summary_gpu = 0;
|
||||||
|
print_time_info(&canvas, tmp);
|
||||||
|
assert(g15r_testG15FontWidth(font, tmp) <= 160);
|
||||||
|
assert_text(&canvas, tmp, G15_TEXT_SMALL,
|
||||||
|
80 - (int)strlen(tmp) * 4 / 2, INFO_ROW, 160);
|
||||||
|
if (gpu_count < 2) {
|
||||||
|
const char *label = "TOTAL";
|
||||||
|
for (int row = 0; row < 5; row++) {
|
||||||
|
char letter[2] = {label[row], 0};
|
||||||
|
assert_text(&canvas, letter, G15_TEXT_MED,
|
||||||
|
gpu_count ? 98 : TEXT_RIGHT, row * 7, 160);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
gpu_frame frame = gpu_snapshot();
|
||||||
|
size_t first = gpu_page_first(frame.count, gpu_page * 5000ULL);
|
||||||
|
for (i = first; i < frame.count && i < first + 2; i++) {
|
||||||
|
char lines[5][32];
|
||||||
|
int left = frame.count == 1 ? 108 : 54 + (int)(i-first) * 54;
|
||||||
|
gpu_lines(i, frame.samples[i], lines);
|
||||||
|
for (int row = 0; row < 5; row++)
|
||||||
|
assert_text(&canvas, lines[row], G15_TEXT_SMALL, left, 1+row*7,
|
||||||
|
left == 54 ? 105 : 160);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc > 2) {
|
||||||
|
char path[PATH_MAX];
|
||||||
|
snprintf(path, sizeof(path), "%s-%d.bin", argv[2], variant);
|
||||||
|
FILE *raw = fopen(path, "wb");
|
||||||
|
assert(raw);
|
||||||
|
assert(fwrite(canvas.buffer, 1, 160 * 43 / 8, raw) == 160 * 43 / 8);
|
||||||
|
assert(!fclose(raw));
|
||||||
|
snprintf(path, sizeof(path), "%s-%d.pgm", argv[2], variant);
|
||||||
|
FILE *out = fopen(path, "w");
|
||||||
|
assert(out);
|
||||||
|
fprintf(out, "P2\n160 43\n255\n");
|
||||||
|
for (y = 0; y < 43; y++) {
|
||||||
|
for (x = 0; x < 160; x++) fprintf(out, "%d ", g15r_getPixel(&canvas, x, y) ? 0 : 255);
|
||||||
|
fputc('\n', out);
|
||||||
|
}
|
||||||
|
assert(!fclose(out));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (argc > 2) {
|
||||||
|
FILE *out = fopen(argv[2], "w");
|
||||||
|
assert(out);
|
||||||
|
fprintf(out, "P2\n160 43\n255\n");
|
||||||
|
for (y = 0; y < 43; y++) {
|
||||||
|
for (x = 0; x < 160; x++)
|
||||||
|
fprintf(out, "%d ", g15r_getPixel(&canvas, x, y) ? 0 : 255);
|
||||||
|
fputc('\n', out);
|
||||||
|
}
|
||||||
|
assert(!fclose(out));
|
||||||
|
}
|
||||||
|
glibtop_close();
|
||||||
|
puts("PASS real LCD buffer: initialization, original geometry, both summary variants, independent glyph bits, complete TOTAL/footer, column bounds");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user