Skip to content

feat: display system swap usage in the TUI memory section#226

Merged
inureyes merged 1 commit into
mainfrom
feature/issue-220-swap-tui-memory
May 24, 2026
Merged

feat: display system swap usage in the TUI memory section#226
inureyes merged 1 commit into
mainfrom
feature/issue-220-swap-tui-memory

Conversation

@inureyes

Copy link
Copy Markdown
Member

Summary

Surface system swap usage in the TUI memory section so users can spot swap pressure at a glance. Most important on Apple Silicon, where over-sized models spill unified memory into swap and silently degrade AI inference throughput. The data was already collected and exported on every platform; this PR is the long-missing rendering and end-to-end demoability work.

What changed

  • src/ui/renderers/memory_renderer.rs: added print_swap_bar and conditional rendering. The swap row is drawn directly below the Mem bar only when swap_total_bytes > 0 (mirrors src/api/metrics/memory.rs:76); the bar segment color flips from magenta (idle) to red (active swap pressure) when swap_used_bytes > 0.
  • src/ui/widgets.rs: new BarSegment::swap_used and BarSegment::swap_used_active helpers alongside memory_used/memory_buffers/memory_cache.
  • src/ui/help.rs: documented the new swap row in the resource-gauge legend using the same colored-keyword styling as the memory row.
  • src/network/metrics_parser.rs: extended the per-host memory accumulator to route swap_* lines into the same MemoryInfo row as memory_* lines, populating swap_{total,used,free}_bytes. Without this, cluster/remote mode would silently drop every swap series and the new UI would render inert against real remote hosts.
  • src/mock/templates/apple_silicon.rs and src/mock/templates/nvidia.rs: replaced hardcoded zero swap with realistic non-zero values (4 GB on Apple Silicon, 32 GB on NVIDIA) and now emit all_smi_swap_{total,used,free}_bytes series guarded by the same swap_total_bytes > 0 condition as the real exporter. Lets reviewers exercise the new UI end-to-end against all-smi-mock-server.
  • README.md: documented the new swap row, the hide-when-zero behaviour, and the active-swap red emphasis under the Memory Monitoring section.

Test plan

  • cargo check --lib --tests (clean)
  • cargo clippy --lib --tests -- -D warnings (clean)
  • cargo fmt --check (clean)
  • cargo test --lib ui::renderers::memory_renderer (9 tests pass — includes 5 new tests for swap-row visible/hidden/idle/active/narrow-width)
  • cargo test --lib network::metrics_parser (48 tests pass — includes new test_parse_swap_metrics)
  • cargo test --features mock --bin all-smi-mock-server mock::templates::nvidia (12 tests pass — includes 3 new tests for swap emission/omission/render)

Closes #220

Surface swap usage in the interactive view so users can spot swap pressure at a glance. This is most important on Apple Silicon, where an over-sized model or mismatched inference-engine parameters spill unified memory into swap and silently degrade throughput. The data plumbing was already in place — `MemoryInfo` carries `swap_{total,used,free}_bytes` across every platform and the Prometheus exporter already publishes `all_smi_swap_*` series — but the TUI never rendered them.

UI changes:

- `print_memory_info` now draws a second `Swap` bar directly below the existing `Mem` bar whenever `swap_total_bytes > 0`. The bar is hidden on hosts with no swap so non-swap deployments stay visually uncluttered, mirroring the API exporter guard at `src/api/metrics/memory.rs:76`.
- The swap segment switches color from magenta (idle: swap exists but is unused) to red (active: `swap_used_bytes > 0`) so active swap pressure is immediately distinguishable — the core signal requested in the issue.
- `BarSegment` gains `swap_used` (Magenta) and `swap_used_active` (Red) helpers alongside the existing `memory_used`/`memory_buffers`/`memory_cache` constructors.
- The help overlay's resource-gauge legend documents the new Swap row with the same colored-keyword styling as the Memory row.

Cluster/remote mode plumbing:

The Prometheus metrics parser previously routed only `memory_*` lines into the per-host memory accumulator. `swap_*` lines were silently dropped, which would have left every cluster-mode `MemoryInfo` row with `swap_total_bytes == 0` and the new UI inert against real remote hosts. Routing now accepts both prefixes, the field map gains `swap_{total,used,free}_bytes`, and a new `test_parse_swap_metrics` test covers the round-trip.

Mock server:

The Apple Silicon and NVIDIA mock templates previously hardcoded swap to zero and did not emit `all_smi_swap_*` series at all, so the new UI could not be demoed via `all-smi-mock-server`. Both templates now seed realistic non-zero swap (4 GB on Apple Silicon with non-zero usage, 32 GB on NVIDIA with variable usage), and the template builders emit `all_smi_swap_{total,used,free}_bytes` series guarded by the same `swap_total_bytes > 0` condition as the real exporter. Three new mock-template tests cover emission-when-present, omission-when-zero, and placeholder resolution.

Tests:

- `memory_renderer`: five new tests covering swap-row visible when total > 0, hidden when total == 0, visible-with-idle-bar when total > 0 but used == 0, red ANSI SGR present when actively swapping, and no panic at narrow terminal widths.
- `metrics_parser`: extended `test_parse_memory_metrics` to assert that absent swap series leave the swap fields at zero, plus a new `test_parse_swap_metrics` that asserts the swap series populate the same `MemoryInfo` row as the matching memory series.
- `nvidia` mock template: three new tests for swap emission/omission and placeholder resolution.

Verification: `cargo check --lib --tests`, `cargo clippy --lib --tests -- -D warnings`, `cargo fmt --check`, and targeted `cargo test --lib ui::renderers::memory_renderer` / `cargo test --lib network::metrics_parser` / `cargo test --features mock --bin all-smi-mock-server mock::templates::nvidia` all pass.
@inureyes inureyes added type:enhancement New feature or request priority:medium Medium priority issue device:apple-silicon Apple Silicon related mode:view View mode related status:review Under review labels May 24, 2026
@inureyes

Copy link
Copy Markdown
Member Author

Implementation Review Summary

Intent

Surface system swap usage in the TUI memory section so users can spot swap pressure at a glance — primarily for Apple Silicon AI inference workloads where over-sized models silently spill unified memory into swap.

Verification

  • All stated requirements implemented (swap row visible when total > 0, hidden when total == 0, red emphasis when active, mock demoable, tests, help legend, README)
  • No placeholder/mock code remaining (mock template emits real metric series with realistic values)
  • Integrated into project code flow (both local-mode and cluster-mode call sites in frame_renderer.rs:603,756 automatically inherit the new row via the existing print_memory_info entry point; cluster-mode metrics parser routes swap_* lines into the same MemoryInfo accumulator as memory_*)
  • Project conventions followed (BarSegment constructors mirror memory_used/memory_buffers/memory_cache; bar layout mirrors the Mem row's 5-char pad + full-width gauge; help legend uses the same colored-keyword styling as the Memory row)
  • Existing modules reused (draw_bar_multi, BarSegment, print_colored_text, update_metric_field! macro)
  • No unintended structural changes (single new private helper print_swap_bar, two new public BarSegment constructors, one extended else if branch in the metrics parser)
  • Tests pass (cargo test --lib ui::renderers::memory_renderer 9/9, cargo test --lib network::metrics_parser 48/48, cargo test --features mock --bin all-smi-mock-server mock::templates::nvidia 12/12, cargo clippy --lib --tests -- -D warnings clean, cargo fmt --check clean)

Notes

The optional secondary indicator described in the issue (compact Swp indicator in local_header.rs) was intentionally deferred — the issue marks it explicitly as optional and the requester's primary need (visible swap signal in the detailed memory section) is fully addressed by the Swap bar.

The metrics-parser extension was not listed in the issue's "Affected files" but turned out to be required: without it, cluster/remote mode would silently drop every all_smi_swap_* series and the new UI would render inert against real remote hosts. Caught while planning the integration check.

@inureyes

Copy link
Copy Markdown
Member Author

Security & Performance Review

Security

  • No injection vectors (no shell exec, no SQL, no user-controlled string interpolation)
  • No data leaks (output is local TUI only)
  • No new attack surface in cluster-mode parser (swap_* lines are routed through the same already-vetted process_memory_metrics path as memory_* lines, with the same MAX_DEVICES_PER_TYPE cap)
  • No new privileges required

Performance

  • Hot-path overhead is O(1) per metric line (one additional starts_with("swap_") test) and O(1) per memory row (one if info.swap_total_bytes > 0 test)
  • No new allocations in the hot path beyond what the existing bar code already does
  • Mock-template emission is gated by swap_total_bytes > 0, so zero-swap hosts pay zero serialisation cost
  • Narrow-terminal safety: the right_padding arithmetic now uses saturating_sub, eliminating the pre-existing panic risk at very narrow widths (caught while adding the swap row)
  • No new threads, locks, or shared mutable state

Findings

None.

@inureyes inureyes added status:done Completed and removed status:review Under review labels May 24, 2026
@inureyes inureyes merged commit 7ee7605 into main May 24, 2026
4 checks passed
@inureyes inureyes deleted the feature/issue-220-swap-tui-memory branch May 24, 2026 06:39
@inureyes inureyes self-assigned this May 24, 2026
inureyes added a commit that referenced this pull request May 24, 2026
Refs #221, #222, #224, #225, #226, #227.

Align config path active discovery with the implicit loader's first-existing-candidate semantics, correct record output help and resolver docs against the binary default, and make the swap-color regression test derive crossterm's current Red SGR sequence instead of hard-coding older encodings.
inureyes added a commit that referenced this pull request May 24, 2026
Refs #221, #222, #224, #225, #226, #227.

Align config path active discovery with the implicit loader's first-existing-candidate semantics, correct record output help and resolver docs against the binary default, and make the swap-color regression test derive crossterm's current Red SGR sequence instead of hard-coding older encodings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

device:apple-silicon Apple Silicon related mode:view View mode related priority:medium Medium priority issue status:done Completed type:enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Display system swap usage in the TUI memory section (view mode)

1 participant