Harden zenmonitor, add non-blocking sampling, headless CLI, and rolling averages#1
Open
HonsW wants to merge 4 commits into
Open
Harden zenmonitor, add non-blocking sampling, headless CLI, and rolling averages#1HonsW wants to merge 4 commits into
HonsW wants to merge 4 commits into
Conversation
…ng averages This is a consolidated set of improvements to zenmonitor, developed and verified incrementally. Runtime sensor values require an AMD Zen host to confirm; everything was built clean (-O2 -Wall) and the pure logic (averaging, filters) was unit-checked against brute-force references. Correctness & safety: - cpu_model(): fix a 1-byte stack buffer overflow (model[48] -> model[49]). - zenpower_init(): check the hwmon "name" read and reset the buffer after free, avoiding a NULL deref / use-after-free. - get_core_fid(): guard against divide-by-zero when the FDID field is 0. - Replace unchecked malloc() with g_malloc(); sprintf -> snprintf; drop dead code and a typo in get_core_count(); fix a signed/unsigned comparison and a shadowed counter in sysfs.c; free the leaked cpu_model() result in the GUI; convert K&R () prototypes to explicit/(void) forms. CPU support: - Accept family 0x19 (Zen 3 / Zen 4) in addition to 0x17, matching the established zenmonitor3 fork (RAPL/energy/FID logic unchanged). UI responsiveness: - Remove the blocking usleep(100ms) from msr_update(); RAPL power is now measured over the real elapsed time between refreshes (monotonic clock), so the GTK main loop is no longer frozen ~1/3 of each cycle. Refresh interval lowered to 200ms now that ticks are cheap. Headless CLI (zenmonitor-cli): - Split the shared core into zenmonitor-lib.c; add a command-line build with stdout output, --delay, --coreid, --refresh-in-place (ncurses), --output-once, and --file (CSV export on exit). makefile gains build-cli/install-cli/all; also drops DESTDIR from the .desktop Exec path. - Signal handler only sets a flag (flush/teardown happen in the main loop); fopen() is checked; the CSV writer iterates by length; the data store frees its per-series arrays. Rolling averages: - Optional GUI columns via --average (e.g. 30s,1m,5m) with a central ring-buffer + running-sum engine (exact windowed mean, O(windows)/tick). --average-only restricts which sensors are averaged. Omitting either keeps the original behaviour. - CLI --daemon mode keeps history resident and atomically rewrites a snapshot file each interval (default under $XDG_RUNTIME_DIR), so short-lived readers (e.g. an xfce4-genmon panel) can show a trailing average. --sensors limits which sensors are handled and skips a backend's per-tick reads when nothing matches. Example genmon script and systemd user unit under data/. The averaging math and the substring filter live once in zenmonitor-lib.c and are shared by the GUI and CLI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BhfyG1chPR37duryk96VTa
Ports the update-interval control from the zenmonitor3 fork onto this codebase: a header-bar button opens an "Update Interval" dialog (spin button, 50-60000 ms) and a new --interval/-i option sets the initial value (default now 1000 ms, previously a fixed 200 ms). The setting is deliberately not persisted - all configuration stays on the command line. Because the interval also converts average windows into sample counts, changing it re-parses the windows against the new cadence and resets the average history (samples taken at different cadences cannot share a window); the Avg columns restart from "---". The window spec is retained unparsed for that purpose. Also fixed while reviewing the change: - apply_interval() only reschedules the timer if one is running, so using the dialog on the "Zen CPU not detected" path cannot start a stray timer or leave a stale source ID. - A negative --interval clamps to the minimum instead of wrapping through the guint cast to the maximum. - CLI: --delay below 0.05s is clamped with a warning; 0 would busy-loop and a negative value wraps through the usleep()/window-math casts. README: document --interval, point the clone URL at this repository, and drop the AUR section (it packages the original upstream project). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BhfyG1chPR37duryk96VTa
- About dialog now links this repository (original-author credit unchanged). - VERSION 1.4.2 -> 1.5.0, reflecting the non-blocking sampling, Zen 3/4 detection, headless CLI, rolling averages, and configurable refresh interval added since 1.4.2. - Make the msr/os sensor arrays and zenpower's sensor list static: they are only referenced within their own files (external access goes through SensorInit pointers), so they no longer leak into the global namespace. - update_data() clears the stored timeout ID when it self-removes, so a later apply_interval() cannot g_source_remove() a dead source. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BhfyG1chPR37duryk96VTa
The CLI's --file mode previously accumulated every sample of every sensor in memory and wrote the CSV only on a clean exit. That grows without bound (hundreds of MB/day at high sample rates) and loses the entire log on any unclean death - the exact runs a hardware monitor is usually logging to diagnose. Now the header is written once after sensor init and one row is appended and flushed per refresh: memory use is constant, tail -f works on the live file, and the log survives up to the last row on crash/SIGKILL (verified by killing -9 mid-run). If the file cannot be opened the CLI fails fast at startup instead of discovering it at exit, and --file now warns that it is ignored in --daemon mode instead of being silently dropped. The SensorDataStore machinery existed only to back the exit dump, so it is removed from zenmonitor-lib.c and the header (along with the now-unneeded time.h includes); the daemon takes its sensor count from the filter pass in init_sensors instead. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BhfyG1chPR37duryk96VTa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidated set of improvements, developed and verified incrementally. Bumps the version to 1.5.0.
Verification note: runtime sensor values require an AMD Zen host and couldn't be confirmed in the build environment. Everything builds clean (
-O2 -Wall, both binaries), and the pure logic (averaging math, substring filter) was unit-checked against brute-force references. The streaming CSV's crash-safety was verified bykill -9mid-run.Correctness & safety
cpu_model(): fix a 1-byte stack buffer overflow (model[48]→model[49]).zenpower_init(): check the hwmonnameread and reset the buffer after free — avoids a NULL deref / use-after-free.get_core_fid(): guard against divide-by-zero when the FDID field is 0.malloc→g_malloc;sprintf→snprintf; drop dead code + a typo inget_core_count(); fix a signed/unsigned comparison and a shadowed counter insysfs.c; free the leakedcpu_model()result in the GUI; convert K&R()prototypes to explicit/(void); make file-local sensor arraysstatic.CPU support
0x19(Zen 3 / Zen 4) alongside0x17, matching the establishedzenmonitor3fork (RAPL/energy/FID logic unchanged).UI responsiveness & refresh interval
usleep(100ms)frommsr_update(). RAPL power is now measured over the real elapsed time between refreshes (monotonic clock), so the GTK main loop is no longer frozen ~⅓ of each cycle.--interval/-isets the initial value (default 1000 ms). Not persisted — all configuration stays on the command line. Changing the interval re-parses the average windows for the new cadence and resets the average history (samples from different cadences can't share a window).Headless CLI (
zenmonitor-cli)zenmonitor-lib.c; add a CLI build with stdout output,--delay(clamped to ≥ 0.05 s),--coreid,--refresh-in-place(ncurses), and--output-once. makefile gainsbuild-cli/install-cli/all; also dropsDESTDIRfrom the.desktopExecpath.--filestreams a CSV log: header once, one row appended + flushed per refresh. Memory use is constant regardless of run length or sample rate,tail -fworks on the live file, and the log survives up to the last row on crash/SIGKILL — suitable for long high-frequency captures. Fails fast if the file can't be opened; warns that it's ignored in--daemonmode.Rolling averages
--average(e.g.30s,1m,5m), backed by a central ring-buffer + running-sum engine (exact windowed mean, O(windows)/tick).--average-onlyrestricts which sensors are averaged. Omitting either keeps the original behavior.--daemonkeeps history resident and atomically rewrites a snapshot file each interval (default under$XDG_RUNTIME_DIR, i.e. tmpfs/RAM), so short-lived readers (e.g. an xfce4-genmon panel) can show a trailing average.--sensorslimits which sensors are handled and skips a backend's per-tick reads when nothing matches. Example genmon script and systemd user unit underdata/.The averaging math and the substring filter live once in
zenmonitor-lib.c, shared by the GUI and CLI.Docs & housekeeping
🤖 Generated with Claude Code