Bug: Telemetry dictionary field-name drift between providers and SystemTelemetryStore #31

Closed
opened 2026-09-08 14:48:32 +01:00 by gronod · 0 comments
Owner

Purpose

Even where snapshot dictionaries do reach SystemTelemetryStore.decodeSnapshot, the per-field key names emitted by Objective-C providers do not match what the Swift decoder reads. Values silently fall back to 0/defaults, producing partially-populated-but-wrong UI across nine subsystems.

Symptoms

  • Memory & Swap: bar at 0% while Used/Free/Wired/Compressed/Swap bytes are correct.
  • Storage & Disk I/O: volume list correct but all usage bars pegged at 0.0%; filesystem label always shows the "APFS" fallback.
  • Network Traffic: "No network interfaces detected" (every item dropped by compactMap because interfaceName is nil).
  • Process Explorer: rows list correctly but RSS shows 0 KB for every process.
  • CPU/Kernel/Load/Disk cards: will still be wrong even after the provider-ID fix (#30).

Root Cause — emitted key vs decoded key

Subsystem Provider emits Store decodes
CPU (MMCPULoadProvider.m) cores (array of dicts w/ totalPercent), userPercent, systemPercent, idlePercent, totalPercent, frequencyHz, maxFrequencyHz, thermalState perCoreLoad ([NSNumber]), userLoad, systemLoad, idleLoad, totalLoad, cpuFrequencyMHz, isThrottled
Kernel (MMKernelTelemetryProvider.m) contextSwitchesRate, syscallsRate, pageFaultsRate, cowFaultsRate, zeroFillsRate, pageinsRate, pageoutsRate, cumulative_<name> contextSwitchesPerSecpageoutsPerSec, totalContextSwitches/totalSyscalls/totalPageFaults
Load avg (MMLoadAverageProvider.m) load1m, load5m, load15m load1Min, load5Min, load15Min (taskCount,threadCount,machFactor match)
Disk I/O (MMDiskIOProvider.m) readBytesPerSec, writeBytesPerSec, cumulativeReadBytes, cumulativeWriteBytes readBps, writeBps, totalBytesRead, totalBytesWritten
Memory (MMMemoryTelemetryProvider.m) no utilizationPercentage key (emits memoryPressurePercent, appMemoryBytes) utilizationPercentage
Storage (MMStorageTelemetryProvider.m) usedPercent, fsType, availableBytes, devicePath usedPercentage, fileSystem
Fans (MMFanTelemetryProvider.m) fanIndex, utilizationPercent; no name key (F{i}ID never read) index, utilization, name
Processes (MMProcessTelemetryProvider.m) residentBytes, virtualBytes; isStopped/isZombie never emitted residentSize, virtualSize, isStopped, isZombie
Network (MMNetworkBandwidthProvider.m) name, ipv4Address, downloadBytesPerSec, uploadBytesPerSec, downloadPacketsPerSec, uploadPacketsPerSec, cumulativeInBytes/cumulativeOutBytes, isLoopback; no isUp interfaceName, ipAddress, downloadBps, uploadBps, downloadPps, uploadPps, totalBytesIn/totalBytesOut, isUp

Suggested Fix

  • Reconcile the DTO contract per AGENTS.md §4.2: either align decodeSnapshot to the emitted keys (single-file change) or normalize provider dictionaries. Prefer documenting the canonical schema once (e.g. ARCHITECTURE.md or a shared keys header) and converging both sides on it.
  • Memory: compute utilizationPercentage = usedBytes / totalBytes * 100 in the decoder (pressure % is a different metric — do not alias memoryPressurePercent to it).
  • Network: emit or derive isUp (e.g. interface has nonzero counters / appears in getifaddrs with IFF_UP).
  • Processes: populate isStopped/isZombie from pbi_status/pbi_flags in proc_pidinfo, or drop the fields from ProcessItem if not needed.
  • Add per-provider schema-conformance unit tests: feed a synthetic dict through the decode path and assert populated metrics (satisfies AGENTS §5.1 synthetic-hardware rule — no live hardware needed).

Acceptance Criteria

  • Per-core CPU grid, aggregate loads, and frequency display live values.
  • Memory utilization bar reflects used/total.
  • Storage volume bars show real usage %; filesystem type is accurate.
  • Network card lists interfaces with live throughput and IPs.
  • Disk I/O rows show real B/s + IOPS + cumulative totals.
  • Process Explorer shows real RSS/VSZ.
  • Fan rows show index/name/utilization (after SMC fix lands).

Dependencies

  • Depends on #30 — provider identifiers must match before these dicts arrive at all.
### Purpose Even where snapshot dictionaries do reach `SystemTelemetryStore.decodeSnapshot`, the per-field key names emitted by Objective-C providers do not match what the Swift decoder reads. Values silently fall back to `0`/defaults, producing partially-populated-but-wrong UI across nine subsystems. ### Symptoms - Memory & Swap: bar at 0% while Used/Free/Wired/Compressed/Swap bytes are correct. - Storage & Disk I/O: volume list correct but all usage bars pegged at 0.0%; filesystem label always shows the "APFS" fallback. - Network Traffic: "No network interfaces detected" (every item dropped by `compactMap` because `interfaceName` is nil). - Process Explorer: rows list correctly but RSS shows 0 KB for every process. - CPU/Kernel/Load/Disk cards: will still be wrong even after the provider-ID fix (#30). ### Root Cause — emitted key vs decoded key | Subsystem | Provider emits | Store decodes | |---|---|---| | CPU (`MMCPULoadProvider.m`) | `cores` (array of dicts w/ `totalPercent`), `userPercent`, `systemPercent`, `idlePercent`, `totalPercent`, `frequencyHz`, `maxFrequencyHz`, `thermalState` | `perCoreLoad` (`[NSNumber]`), `userLoad`, `systemLoad`, `idleLoad`, `totalLoad`, `cpuFrequencyMHz`, `isThrottled` ✓ | | Kernel (`MMKernelTelemetryProvider.m`) | `contextSwitchesRate`, `syscallsRate`, `pageFaultsRate`, `cowFaultsRate`, `zeroFillsRate`, `pageinsRate`, `pageoutsRate`, `cumulative_<name>` | `contextSwitchesPerSec`…`pageoutsPerSec`, `totalContextSwitches`/`totalSyscalls`/`totalPageFaults` | | Load avg (`MMLoadAverageProvider.m`) | `load1m`, `load5m`, `load15m` | `load1Min`, `load5Min`, `load15Min` (`taskCount`,`threadCount`,`machFactor` match) | | Disk I/O (`MMDiskIOProvider.m`) | `readBytesPerSec`, `writeBytesPerSec`, `cumulativeReadBytes`, `cumulativeWriteBytes` | `readBps`, `writeBps`, `totalBytesRead`, `totalBytesWritten` | | Memory (`MMMemoryTelemetryProvider.m`) | *no* `utilizationPercentage` key (emits `memoryPressurePercent`, `appMemoryBytes`) | `utilizationPercentage` | | Storage (`MMStorageTelemetryProvider.m`) | `usedPercent`, `fsType`, `availableBytes`, `devicePath` | `usedPercentage`, `fileSystem` | | Fans (`MMFanTelemetryProvider.m`) | `fanIndex`, `utilizationPercent`; **no `name` key** (`F{i}ID` never read) | `index`, `utilization`, `name` | | Processes (`MMProcessTelemetryProvider.m`) | `residentBytes`, `virtualBytes`; **`isStopped`/`isZombie` never emitted** | `residentSize`, `virtualSize`, `isStopped`, `isZombie` | | Network (`MMNetworkBandwidthProvider.m`) | `name`, `ipv4Address`, `downloadBytesPerSec`, `uploadBytesPerSec`, `downloadPacketsPerSec`, `uploadPacketsPerSec`, `cumulativeInBytes`/`cumulativeOutBytes`, `isLoopback`; **no `isUp`** | `interfaceName`, `ipAddress`, `downloadBps`, `uploadBps`, `downloadPps`, `uploadPps`, `totalBytesIn`/`totalBytesOut`, `isUp` | ### Suggested Fix - Reconcile the DTO contract per AGENTS.md §4.2: either align `decodeSnapshot` to the emitted keys (single-file change) or normalize provider dictionaries. Prefer documenting the canonical schema once (e.g. `ARCHITECTURE.md` or a shared keys header) and converging both sides on it. - Memory: compute `utilizationPercentage = usedBytes / totalBytes * 100` in the decoder (pressure % is a different metric — do not alias `memoryPressurePercent` to it). - Network: emit or derive `isUp` (e.g. interface has nonzero counters / appears in `getifaddrs` with `IFF_UP`). - Processes: populate `isStopped`/`isZombie` from `pbi_status`/`pbi_flags` in `proc_pidinfo`, or drop the fields from `ProcessItem` if not needed. - Add per-provider schema-conformance unit tests: feed a synthetic dict through the decode path and assert populated metrics (satisfies AGENTS §5.1 synthetic-hardware rule — no live hardware needed). ### Acceptance Criteria - [ ] Per-core CPU grid, aggregate loads, and frequency display live values. - [ ] Memory utilization bar reflects `used/total`. - [ ] Storage volume bars show real usage %; filesystem type is accurate. - [ ] Network card lists interfaces with live throughput and IPs. - [ ] Disk I/O rows show real B/s + IOPS + cumulative totals. - [ ] Process Explorer shows real RSS/VSZ. - [ ] Fan rows show index/name/utilization (after SMC fix lands). ### Dependencies - Depends on #30 — provider identifiers must match before these dicts arrive at all.
gronod added this to the M6: Bugfix & Stability milestone 2026-09-08 14:48:32 +01:00
gronod added the Bug/BackendKind/Bug
Priority
Critical
1
Project/Antigravity
labels 2026-09-08 14:48:55 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: gronod/MacMonitor#31