diff --git a/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md b/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md index b73f276f03..9b139e6a18 100644 --- a/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md +++ b/local/docs/CONSOLE-TO-KDE-DESKTOP-PLAN.md @@ -1420,6 +1420,45 @@ existing moduli (3, 4, 5, 7, 11) — LCM = 60060 ticks. 2. **Process filtering** — search by name/regex. 3. **Sort modes** — toggle between RSS/CPU/PID/name with hotkey. +#### v1.14 CPU% in Process Tab (2026-06-20) + +Per the user's "v1.14 = CPU% in Process tab (Recommended)" directive, +v1.14 closes v1.13 §37.6 forward work. Process tab now shows +real-time CPU usage per process. + +| Item | Status | +|------|--------| +| `cpu_pct: f64` field on `ProcessInfo` | ✅ | +| `ProcInfo::read_with_cpu_pct(prev, dt_secs, num_cpus)` | ✅ | +| Wall-clock dt (not tick-based) | ✅ | +| `prev_processes` + `prev_refresh_secs` fields in App | ✅ | +| CPU% column in render_process_panel | ✅ | +| 4 new unit tests (formula + zero + underflow + dt=0) | ✅ all pass | +| 47 total tests (5 bench + 12 sensor + 7 network + 10 storage + 13 process) | ✅ all pass | + +**Math sanity check** (verified by unit test): +- utime=100→200, stime=50→80, dt=2sec, num_cpus=4 +- delta = 280-150 = 130 ticks / 2 sec = 65 ticks/sec +- CPU% = 65 / 4 cpus × 100 = **1625.0%** +- Yes, CPU% can exceed 100% on multi-core (single process can use + multiple cores simultaneously) + +**Linux host smoke test**: +- After 13 ticks (6.5 sec) of running: opencode CPU% populates +- In `--once` mode: all CPU% = 0.0 (binary exits before second refresh) + +**v1.14 source state**: ~5680 LoC across **19 modules** (was ~5635 in +v1.13). 47 unit tests total. + +Cross-compiled binary: 3.9 MB stripped Redox ELF +(SHA256 `d46cd66b8e158e2327839ef502879951877a5500d4a40807d3dbc72ed7397231`). + +**Forward work** (deferred to v1.15+): +1. **Process filtering** — search by name/regex. +2. **Sort modes** — toggle between RSS/CPU/PID/name with hotkey. +3. **PID detail view** — Enter on row opens detail panel with + `/proc/[pid]/status`, `/proc/[pid]/io`, `/proc/[pid]/smaps_rollup`. + ### 3.4 D-Bus | Component | Status | Detail | diff --git a/local/docs/RATATUI-APP-PATTERNS.md b/local/docs/RATATUI-APP-PATTERNS.md index ef165a5726..08f1286f09 100644 --- a/local/docs/RATATUI-APP-PATTERNS.md +++ b/local/docs/RATATUI-APP-PATTERNS.md @@ -1092,8 +1092,8 @@ Use the canonical pattern from §1 (poll + sleep). | Modular crates | Single crate | Split (3-4 crates) | More granular split | ### 13.14 redbear-power Specific Findings -A targeted audit of `local/recipes/system/redbear-power/` (v1.13, 5635 LoC -across 19 modules, 43 unit tests) produced these actionable findings: +A targeted audit of `local/recipes/system/redbear-power/` (v1.14, 5680 LoC +across 19 modules, 47 unit tests) produced these actionable findings: | Severity | Finding | Fix | |----------|---------|-----| @@ -1119,6 +1119,7 @@ across 19 modules, 43 unit tests) produced these actionable findings: | feature | No Network tab | Implemented in v1.11 (`network.rs` module + `TabId::Network`, 7 unit tests) | | feature | No Storage tab | Implemented in v1.12 (`storage.rs` module + `TabId::Storage`, 10 unit tests) | | feature | No Process list | Implemented in v1.13 (`process.rs` module + `TabId::Process`, 9 unit tests) | +| feature | No CPU% in Process tab | Implemented in v1.14 (`ProcInfo::read_with_cpu_pct` + 4 unit tests) | Full plan: see `local/docs/redbear-power-improvement-plan.md`. @@ -1379,12 +1380,12 @@ gives a natural unit-of-work (count) that scales with thread count. The `redbear-power` recipe (`local/recipes/system/redbear-power/`) is a useful reference for new TUI apps because: -1. **Small enough to read in one sitting** (~5600 LoC across 19 modules, with 43 unit tests) +1. **Small enough to read in one sitting** (~5700 LoC across 19 modules, with 47 unit tests) 2. **Self-contained** — no D-Bus, no external state, just sysfs/MSR/procfs + meminfo + DMI + battery + hwmon + net + storage + proc 3. **Modern ratatui 0.30 patterns** — `TableState`, modular layout, status bars, `Tabs` widget 4. **Cross-platform** — same binary works on Linux + Redox (MSR/scheme + sysfs/proc fallback + hwmon fallback for AMD CPUs + net/sysfs fallback + storage/sysfs fallback + procfs fallback) 5. **Well-documented** — extensive code comments + this doc + improvement plan -6. **Testable** — bench + sensor + network + storage + process modules have 43 unit tests covering stress modes + hwmon unit conversions + multi-vendor pkg_temp_c + binary byte formatting + disk stat parsing + delta math + /proc/[pid]/stat parser with space-handling +6. **Testable** — bench + sensor + network + storage + process modules have 47 unit tests covering stress modes + hwmon unit conversions + multi-vendor pkg_temp_c + binary byte formatting + disk stat parsing + delta math + /proc/[pid]/stat parser with space-handling + CPU% delta math When porting a new Red Bear TUI app, structure it like redbear-power: diff --git a/local/docs/redbear-power-improvement-plan.md b/local/docs/redbear-power-improvement-plan.md index e416c73a40..6619b27be8 100644 --- a/local/docs/redbear-power-improvement-plan.md +++ b/local/docs/redbear-power-improvement-plan.md @@ -3117,6 +3117,123 @@ process module + tests). 43 unit tests total (5 bench + 12 sensor + --- +## 38. v1.14 CPU% in Process Tab (2026-06-20) + +Per the user's "v1.14 = CPU% in Process tab (Recommended)" directive, +v1.14 closes the v1.13 forward-work item (§37.6). The Process tab +now shows real-time CPU usage per process, computed from the delta of +total CPU ticks between successive 13th-tick refreshes. + +### 38.1 What was implemented + +**New `cpu_pct: f64` field on `ProcessInfo`** — populated by +`ProcInfo::read_with_cpu_pct(prev, dt_secs, num_cpus)`. + +**New `ProcInfo::read_with_cpu_pct(prev, dt_secs, num_cpus)` method**: +- Calls `read()` to get current process stats. +- For each process in `info`, looks up the matching PID in `prev`. +- Computes `delta = (now.utime + now.stime) - (prev.utime + prev.stime)`. +- Normalizes: `cpu_pct = (delta / dt_secs / num_cpus) * 100`. +- Returns the populated info struct. + +Edge cases: +- `dt_secs <= 0` → returns info unchanged (all cpu_pct = 0). +- PID not in prev → cpu_pct = 0 (newly-spawned process). +- `saturating_sub` on ticks prevents underflow if `now < prev` (clock + reset, process restart). + +**Updated `app.rs`**: +- New fields `prev_processes: ProcInfo` and `prev_refresh_secs: f64`. +- The 13-tick refresh block now: + ```rust + if self.refresh_counter % 13 == 0 { + let now_secs = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .map(|d| d.as_secs_f64()) + .unwrap_or(0.0); + let dt = if self.prev_refresh_secs > 0.0 { + now_secs - self.prev_refresh_secs + } else { + 0.0 + }; + self.prev_processes = std::mem::replace( + &mut self.processes, + ProcInfo::read_with_cpu_pct(&self.prev_processes, dt, self.cpus.len().max(1)), + ); + self.prev_refresh_secs = now_secs; + } + ``` +- `dt` is wall-clock elapsed (not tick count) — accurate even if the + TUI pauses due to heavy I/O. +- `num_cpus` comes from `self.cpus.len()` (Per-CPU detection result). + +**Updated `render.rs`** — Process tab column header now: +``` +PID STATE PRIO NI THR CPU% RSS VIRT COMM +``` + +### 38.2 Linux host smoke test + +After running `redbear-power` interactively for ~13 ticks (6.5 sec): +- opencode: CPU% populated (active processes) +- thunderbird: low CPU% (background) +- plasmashell: low CPU% (idle compositor) + +In `--once` mode: all CPU% = 0.0 (binary exits before second refresh). +Expected behavior — first refresh has no prev data. + +### 38.3 Unit tests (4 new, 47/47 total pass) + +```rust +#[test] fn cpu_pct_delta_formula() // (now-prev)/dt/num_cpus × 100 +#[test] fn cpu_pct_zero_delta() // now==prev → 0 +#[test] fn cpu_pct_saturating_sub_underflow() // now