210 lines
11 KiB
Markdown
210 lines
11 KiB
Markdown
# MacMonitor: System Architecture & Technical Specifications
|
||
|
||
This document details the architectural principles, data flow models, memory safety invariants, and subsystem implementation specifics for **MacMonitor**.
|
||
|
||
---
|
||
|
||
## 1. Architectural Principles & Constraints
|
||
|
||
### 1.1 Target Platform Invariants
|
||
- **Instruction Set Architecture:** Intel `x86_64`.
|
||
- **Target OS:** macOS 14.0 (Sonoma) or newer.
|
||
- **Language Stack:**
|
||
- **Presentation & ViewModels:** Modern Swift 5.9+ with SwiftUI, `@Observable`, and Swift Charts.
|
||
- **Telemetry & Kernel Layer:** Objective-C & C interfaces directly communicating with Mach, IOKit, BSD sysctl, CoreAudio, and DisplayServices.
|
||
- **Hybrid Bridge:** Bridging Header (`MacMonitor-Bridging-Header.h`) providing strict separation of concerns and zero overhead between C structures and Swift value types.
|
||
|
||
---
|
||
|
||
## 2. Unidirectional Data Flow & Concurrency Model
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
subgraph KernelSpace ["Kernel / Driver Space"]
|
||
SMC_DEV["AppleSMC Driver"]
|
||
MACH_KERN["Mach Kernel Task/Host"]
|
||
BSD_SYS["BSD Sysctl & libproc"]
|
||
IOK_DRV["IOBlockStorage & IOAccelerator"]
|
||
end
|
||
|
||
subgraph TelemetryLayer ["Low-Level Telemetry Engine (Serial GCD Queue)"]
|
||
DISPATCH["dispatch_source_t Timer<br/>(com.i3omb.macmonitor.telemetry · QOS_CLASS_UTILITY)"]
|
||
PROV1["MMAppleSMCClient"]
|
||
PROV2["MMCPUUsageProvider"]
|
||
PROV3["MMMemoryProvider"]
|
||
PROV4["MMProcessCollector"]
|
||
BATCH["Snapshot Dictionary / DTO Aggregator"]
|
||
end
|
||
|
||
subgraph PresentationLayer ["UI & State Layer (@MainActor)"]
|
||
STORE["SystemTelemetryStore (@Observable)"]
|
||
ALERTS["AlertEngine (Rule Evaluator)"]
|
||
VIEWS["SwiftUI Dashboard / Popover / Charts (60 FPS)"]
|
||
end
|
||
|
||
SMC_DEV <-->|"IOConnectCallStructMethod"| PROV1
|
||
MACH_KERN -->|"host_processor_info / host_statistics64"| PROV2
|
||
BSD_SYS -->|"sysctl / proc_listpids"| PROV3
|
||
BSD_SYS --> PROV4
|
||
IOK_DRV -->|"IORegistryEntryCreateCFProperties"| BATCH
|
||
|
||
DISPATCH -->|"Trigger Tick (1s/2s)"| PROV1 & PROV2 & PROV3 & PROV4
|
||
PROV1 & PROV2 & PROV3 & PROV4 --> BATCH
|
||
|
||
BATCH -->|"Async Dispatch (Snapshot Copy)"| STORE
|
||
STORE -->|"Evaluate Rules"| ALERTS
|
||
STORE -->|"Declarative Bindings"| VIEWS
|
||
```
|
||
|
||
### 2.1 Threading & QoS Architecture
|
||
1. **Background Polling Queue:**
|
||
All hardware and kernel polling operations execute on a dedicated serial queue:
|
||
```objc
|
||
dispatch_queue_t queue = dispatch_queue_create("com.i3omb.macmonitor.telemetry", DISPATCH_QUEUE_SERIAL);
|
||
```
|
||
Configured with quality-of-service `QOS_CLASS_UTILITY` to prevent thermal or CPU contention with foreground user processes.
|
||
2. **Deterministic Sampling Timer:**
|
||
Driven by a Grand Central Dispatch timer source (`dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, ...)`). Timer coalescing leeway is set to 10% of the sample period to conserve battery on MacBooks while maintaining timing accuracy.
|
||
3. **Main Actor State Ingestion:**
|
||
Snapshots are unmarshaled into immutable, `Sendable` Swift structures and dispatched asynchronously to `@MainActor`:
|
||
```swift
|
||
Task { @MainActor in
|
||
self.store.digest(snapshot: batch)
|
||
}
|
||
```
|
||
4. **Mach Virtual Memory Management Invariant:**
|
||
Calls returning kernel-allocated memory arrays (such as `host_processor_info` or `processor_cpu_load_info`) must explicitly deallocate their previous memory pointers using `vm_deallocate(mach_task_self(), ...)` in the subsequent sampling pass to prevent progressive heap bloat.
|
||
|
||
---
|
||
|
||
## 3. Subsystem Implementation Specifications
|
||
|
||
### 3.1 Apple System Management Controller (AppleSMC)
|
||
- **Service Name:** `AppleSMC`
|
||
- **IOKit Connection:** Initiated using `IOServiceGetMatchingService(kIOMainPortDefault, IOServiceMatching("AppleSMC"))` and `IOServiceOpen(service, mach_task_self(), 0, &connect)`.
|
||
- **Selector:** `kSMCHandleYPCEvent` (Index 2) dispatched via `IOConnectCallStructMethod`.
|
||
|
||
#### SMC Data Types & Decoding Algorithms
|
||
| Data Type | FourCC | Byte Length | Encoding Scheme | Decoding Formula |
|
||
| :--- | :---: | :---: | :--- | :--- |
|
||
| `sp78` | `0x73703738` | 2 | Signed 8.8 Fixed-Point | `tempC = ((int16_t)(b[0] << 8 \| b[1])) / 256.0` |
|
||
| `fpe2` | `0x66706532` | 2 | Unsigned 14.2 Fixed-Point | `rpm = ((uint16_t)(b[0] << 8 \| b[1])) / 4.0` |
|
||
| `ui8` | `0x75693820` | 1 | Unsigned 8-bit Integer | `val = (uint8_t)b[0]` |
|
||
| `ui16` | `0x75693136` | 2 | Unsigned 16-bit Big-Endian | `val = (uint16_t)(b[0] << 8 \| b[1])` |
|
||
| `ui32` | `0x75693332` | 4 | Unsigned 32-bit Big-Endian | `val = (uint32_t)(b[0] << 24 \| ... \| b[3])` |
|
||
| `flt ` | `0x666c7420` | 4 | IEEE 754 32-bit Float | `memcpy(&val, b, 4)` |
|
||
|
||
#### Intel SMC Key Catalog
|
||
- **Fans:** `FNum` (Total count), `F0Ac`/`F1Ac` (Current RPM), `F0Mn`/`F1Mn` (Min RPM), `F0Mx`/`F1Mx` (Max RPM), `F0Tg`/`F1Tg` (Target RPM), `F0ID`/`F1ID` (Fan name).
|
||
- **CPU Thermals:** `TC0P` (Package proximity), `TC0D` (Die), `TCXC` (PECI), `TC0C`–`TC15C` (Individual core DTS).
|
||
- **Chassis Thermals:** `TPCD` (PCH Die), `Th0H`–`Th2H` (Heatsinks), `TM0P`–`TM3P` (Memory DIMMs), `TA0P`/`TA1P` (Chassis Ambient), `Tp0P` (Power Supply), `TTLD` (Thunderbolt).
|
||
- **Electrical & RAPL:** `PSTR`/`PDTR` (System Total Watts), `PCPR`/`PCTR` (CPU Package Watts via Intel RAPL), `PG0R` (GPU Watts), `VC0C` (Core Voltage), `IC0C` (Core Current).
|
||
|
||
---
|
||
|
||
### 3.2 Host CPU & Kernel Scheduling Telemetry
|
||
- **Per-Core Tick Counting:**
|
||
Executed via `host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, ...)`.
|
||
Deltas between ticks determine normalized CPU percentages:
|
||
$$\Delta \text{ticks} = \Delta \text{user} + \Delta \text{system} + \Delta \text{idle} + \Delta \text{nice}$$
|
||
$$\text{Core \%} = \frac{\Delta \text{user} + \Delta \text{system} + \Delta \text{nice}}{\Delta \text{ticks}} \times 100\%$$
|
||
- **Kernel Context Switches & Syscalls:**
|
||
Polled via BSD sysctl node statistics:
|
||
- `vm.stats.sys.v_swtch`
|
||
- `vm.stats.sys.v_syscall`
|
||
- `vm.stats.sys.v_intr`
|
||
- `vm_statistics64.faults` (Page faults)
|
||
Rates are computed against elapsed nanoseconds measured using `mach_absolute_time()` and `mach_timebase_info()`.
|
||
- **Unix Load Averages:**
|
||
Queried via standard BSD `getloadavg(double load[], 3)` and normalized against `[NSProcessInfo processInfo].activeProcessorCount`.
|
||
|
||
---
|
||
|
||
### 3.3 Virtual Memory & Pressure Subsystem
|
||
- **RAM Calculation Strategy:**
|
||
Direct query of `host_statistics64(mach_host_self(), HOST_VM_INFO64, ...)` multiplied by `vm_kernel_page_size`.
|
||
- **App Memory:** $(\text{internal\_count} - \text{purgeable\_count}) \times \text{page\_size}$
|
||
- **Wired Memory:** $\text{wire\_count} \times \text{page\_size}$
|
||
- **Compressed Memory:** $\text{compressor\_page\_count} \times \text{page\_size}$
|
||
- **Cached Files:** $(\text{external\_page\_count} + \text{purgeable\_count}) \times \text{page\_size}$
|
||
- **Free Memory:** $\text{free\_count} \times \text{page\_size}$
|
||
- **Swap Statistics:** `sysctlbyname("vm.swapusage", &swap, ...)` reporting `xsu_total`, `xsu_avail`, and `xsu_used`.
|
||
- **Memory Pressure Monitoring:**
|
||
Dedicated GCD dispatch source:
|
||
```objc
|
||
dispatch_source_create(DISPATCH_SOURCE_TYPE_MEMORYPRESSURE, 0,
|
||
DISPATCH_MEMORYPRESSURE_NORMAL |
|
||
DISPATCH_MEMORYPRESSURE_WARN |
|
||
DISPATCH_MEMORYPRESSURE_CRITICAL, queue);
|
||
```
|
||
|
||
---
|
||
|
||
### 3.4 Storage & Disk I/O Engine
|
||
- **Volume Capacity & APFS Snapshots:**
|
||
Queried via `NSFileManager.defaultManager.mountedVolumeURLsIncludingResourceValuesForKeys:`.
|
||
Utilizes `NSURLVolumeAvailableCapacityForImportantUsageKey` and `NSURLVolumeAvailableCapacityForOpportunisticUsageKey` to properly calculate usable storage space without showing local APFS snapshots as consumed capacity.
|
||
- **Real-Time Disk Throughput (IOPS & MB/s):**
|
||
Matches `IOBlockStorageDriver` in IOKit registry. Extracts `IOBlockStorageDriverStatistics`:
|
||
- `Bytes (Read)` and `Bytes (Write)`
|
||
- `Operations (Read)` and `Operations (Write)`
|
||
Deltas over elapsed time yield read/write throughput (MB/s) and IOPS.
|
||
|
||
---
|
||
|
||
### 3.5 High-Performance Process Explorer
|
||
- **Enumeration:** `proc_listpids(PROC_ALL_PIDS, 0, pids, byteSize)` to retrieve active PIDs.
|
||
- **Resource Attribution:**
|
||
- `proc_pidinfo(pid, PROC_PIDTASKINFO, ...)` yields `pti_total_user`, `pti_total_system`, `pti_resident_size`, and `pti_threadnum`.
|
||
- Differential CPU calculation tracks elapsed nanoseconds vs elapsed CPU time per process.
|
||
- **Static Metadata Caching:**
|
||
Executable paths (`proc_pidpath`) and application icons (`NSWorkspace.iconForFile:`) are cached in an internal hash map keyed by PID to eliminate file system traversal overhead during 1 Hz polling loops. Dead PIDs are pruned on each iteration.
|
||
- **Drill-Down Inspector:**
|
||
- Threads: `proc_pidinfo(..., PROC_PIDLISTTHREADS)` + `PROC_PIDTHREADINFO`.
|
||
- File Descriptors: `proc_pidinfo(..., PROC_PIDLISTFDS)` + `proc_pidfdinfo(..., PROC_PIDFDVNODEPATHINFO)`.
|
||
- Sockets: `proc_pidfdinfo(..., PROC_PIDFDSOCKETINFO)`.
|
||
|
||
---
|
||
|
||
### 3.6 GPU & Graphics Telemetry
|
||
- **IOKit Accelerator Service:** Matches `IOAccelerator` nodes in the IORegistry.
|
||
- **Performance Properties:** Inspects `PerformanceStatistics` dictionary:
|
||
- `Device Utilization %`
|
||
- `GPU Core Clock Hz`
|
||
- `vramUsedBytes` / `vramFreeBytes`
|
||
- `Temperature(C)`
|
||
- **Multi-GPU Architecture:** Handles both Intel integrated graphics (Intel Iris/UHD) and discrete AMD Radeon Pro GPUs on 15"/16" MacBook Pros. Correctly accommodates AMD GPU power-down states when automatic graphics switching is idle.
|
||
|
||
---
|
||
|
||
### 3.7 Network Bandwidth & Protocol Control Blocks
|
||
- **64-Bit Network Traffic Counters:**
|
||
Avoids 32-bit integer rollover on high-throughput connections by querying BSD routing tables via `sysctl` with `NET_RT_IFLIST2` and decoding `struct if_data64` (`ifi_ibytes`, `ifi_obytes`, `ifi_ipackets`, `ifi_opackets`).
|
||
- **Socket Table Inspection:**
|
||
Scans active TCP/UDP Protocol Control Blocks via `net.inet.tcp.pcblist_n` and `net.inet.udp.pcblist_n`, parsing `xinpcb_n` to extract local/foreign addresses and connection states.
|
||
|
||
---
|
||
|
||
### 3.8 Display & Screen Brightness
|
||
- **Dynamic Framework Linking:**
|
||
To safely access screen brightness across built-in MacBook screens and Apple external displays, private `DisplayServices.framework` symbols (`DisplayServicesGetBrightness`, `DisplayServicesSetBrightness`) are loaded dynamically via `dlopen`/`dlsym`.
|
||
- **Display Configurations:**
|
||
CoreGraphics APIs (`CGGetActiveDisplayList`, `CGDisplayCopyDisplayMode`) provide active screen resolution, refresh rate (Hz), and built-in panel identification.
|
||
|
||
---
|
||
|
||
### 3.9 Time-Series Visualization (Swift Charts Engine)
|
||
- **Ring Buffer Design:**
|
||
Fixed-capacity circular ring buffers (`RingBuffer<T>`) maintain rolling samples (60s, 5m, 15m, 1h).
|
||
Appending is $O(1)$ with zero runtime dynamic memory reallocations.
|
||
- **Swift Charts Integration:**
|
||
Renders using Apple's declarative `Chart`, `LineMark`, and gradient `AreaMark` components, backed by Metal rasterization.
|
||
|
||
---
|
||
|
||
### 3.10 Intelligence & Alert Rules Engine
|
||
- **Architecture:** Synchronous post-sampling evaluation engine (`AlertEngine`).
|
||
- **Notification Throttle & Cooldown:**
|
||
Each alert identifier maintains a cooldown timestamp. Notifications for persistent abnormal conditions (e.g. CPU temperature > 95°C) are capped at a maximum of 1 alert per 10–15 minutes until normalized, preventing notification floods.
|
||
- **Delivery Channel:** Native `UNUserNotificationCenter` with critical priority sounds for fan stalls and hardware emergencies.
|