Add top-processes drawer to the CPU widget
Clicking the CPU pill opens a popup listing the highest-CPU processes, refreshed live while open. services/topproc.sh computes top-style per-process CPU% and mem% from two /proc snapshots and prints one ranked frame per run; SysStats streams it only while procPollEnabled (drawer open), so the bar pays nothing at rest. CLAUDE.md documents the architecture and the runtime constraints discovered here (helper scripts must be git-tracked for `nix run`; single-shot + exit to flush stdout; bash+coreutils only; StdioCollector/Timer don't fire in this Quickshell build; fixed-size popup to avoid stale-buffer on resize under Sway). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
79
services/topproc.sh
Executable file
79
services/topproc.sh
Executable file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env bash
|
||||
# Print one frame of the top CPU-consuming processes, then exit.
|
||||
#
|
||||
# Single-shot by design: SysStats re-runs it every procInterval while the CPU
|
||||
# drawer is open (Process.running re-armed by a Timer, like dfProc). Exiting
|
||||
# per frame guarantees stdout is flushed — a long-running loop kept its frame
|
||||
# delimiter stuck in bash's block buffer and the QML side never saw a full
|
||||
# frame. discover.sh is the reference for the bash-script + parse pattern.
|
||||
#
|
||||
# IMPORTANT: the flake wrapper only puts bash + coreutils on PATH, so this uses
|
||||
# ONLY bash builtins and coreutils (nproc, sort, head, sleep, cat) — no awk, no
|
||||
# getconf. Keep it that way or add the tool to runtimeInputs in flake.nix.
|
||||
#
|
||||
# Output: one "P <cpu%> <mem%> <name>" line per process, highest CPU first.
|
||||
# cpu% is top-style: 100 == one fully-used core.
|
||||
|
||||
intv="${1:-2}"
|
||||
n="${2:-6}"
|
||||
ncpu="$(nproc 2>/dev/null || echo 1)"
|
||||
pg_kb=4 # page size is 4096B on all targeted arches (x86_64/aarch64)
|
||||
|
||||
# MemTotal in kB, read without awk.
|
||||
memkb=0
|
||||
while read -r key val _; do
|
||||
if [ "$key" = "MemTotal:" ]; then memkb="$val"; break; fi
|
||||
done < /proc/meminfo
|
||||
|
||||
# Aggregate CPU jiffies: sum of the fields on the leading "cpu" line.
|
||||
cpu_total() {
|
||||
local line f s=0
|
||||
read -r line < /proc/stat
|
||||
# shellcheck disable=SC2086
|
||||
set -- $line # collapses the double space; $1 == "cpu"
|
||||
shift
|
||||
for f in "$@"; do s=$(( s + f )); done
|
||||
echo "$s"
|
||||
}
|
||||
|
||||
# Read pid -> (utime+stime) jiffies and rss pages into the given assoc arrays.
|
||||
# /proc/<pid>/stat is "PID (comm) STATE ...": strip through ") " so comm spaces
|
||||
# and parens can't shift field positions, then utime=14, stime=15, rss=24.
|
||||
snapshot() {
|
||||
local -n _jif="$1" _rss="$2"
|
||||
local f l pid rest
|
||||
for f in /proc/[0-9]*/stat; do
|
||||
# 2>/dev/null first so a process vanishing mid-scan is silent.
|
||||
IFS= read -r l 2>/dev/null < "$f" || continue
|
||||
pid="${l%% *}"
|
||||
rest="${l#*) }"
|
||||
# shellcheck disable=SC2086
|
||||
set -- $rest
|
||||
_jif["$pid"]=$(( ${12:-0} + ${13:-0} ))
|
||||
_rss["$pid"]="${22:-0}"
|
||||
done
|
||||
}
|
||||
|
||||
declare -A j1 j2 rss
|
||||
t1="$(cpu_total)"
|
||||
snapshot j1 rss
|
||||
sleep "$intv"
|
||||
t2="$(cpu_total)"
|
||||
snapshot j2 rss
|
||||
dt=$(( t2 - t1 ))
|
||||
[ "$dt" -gt 0 ] || exit 0
|
||||
|
||||
# Emit "<cpu> <pid> <rss>" for each process seen in both snapshots, sort by cpu
|
||||
# desc, take the top n, then resolve name + mem% for just those.
|
||||
for pid in "${!j2[@]}"; do
|
||||
[ -n "${j1[$pid]:-}" ] || continue
|
||||
d=$(( ${j2[$pid]} - ${j1[$pid]} ))
|
||||
cpu=$(( 100 * ncpu * d / dt ))
|
||||
printf '%d %s %s\n' "$cpu" "$pid" "${rss[$pid]:-0}"
|
||||
done | sort -rn | head -n "$n" | while read -r cpu pid r; do
|
||||
name="$(cat "/proc/$pid/comm" 2>/dev/null)"
|
||||
[ -n "$name" ] || name="$pid"
|
||||
mem=0
|
||||
[ "$memkb" -gt 0 ] && mem=$(( r * pg_kb * 100 / memkb ))
|
||||
printf 'P %s %s %s\n' "$cpu" "$mem" "$name"
|
||||
done
|
||||
Reference in New Issue
Block a user