From a3d8c8948ef3e035a7c59775868fbff4cecd65a2 Mon Sep 17 00:00:00 2001 From: AngusBell97 <124716116+AngusBell97@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:55:11 -0500 Subject: [PATCH] Galaxy: harden system monitor and optional chime Keep the model-ready sound opt-in and return a controlled error when memory totals are unavailable. --- common/params_keys.h | 2 +- starpilot/system/the_galaxy/system_monitor.py | 10 +++++----- .../system/the_galaxy/tests/test_system_monitor.py | 10 ++++++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 4a9fd1c775..899b8c76bc 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -352,7 +352,7 @@ inline static std::unordered_map keys = { {"DrivingModelVersion", {PERSISTENT, STRING, "v15", "v15", 1}}, {"DynamicPathWidth", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}}, {"DynamicPedalsOnUI", {PERSISTENT, BOOL, "1", "0", 1, SETTINGS_SIMPLE}}, - {"GpuModelReadySound", {PERSISTENT, BOOL, "1", "1", 2, SETTINGS_SIMPLE}}, + {"GpuModelReadySound", {PERSISTENT, BOOL, "0", "0", 2, SETTINGS_SIMPLE}}, {"EngageVolume", {PERSISTENT, INT, "101", "101", 2, SETTINGS_SIMPLE}}, {"EVTuning", {PERSISTENT, BOOL, "0", "0", 3}}, {"Fahrenheit", {PERSISTENT, BOOL, "0", "0", 3}}, diff --git a/starpilot/system/the_galaxy/system_monitor.py b/starpilot/system/the_galaxy/system_monitor.py index ed964c7560..5389075d66 100644 --- a/starpilot/system/the_galaxy/system_monitor.py +++ b/starpilot/system/the_galaxy/system_monitor.py @@ -70,20 +70,20 @@ class SystemMonitor: user = pwd.getpwuid(uid).pw_name except KeyError: user = str(uid) - # Only executable/module names: never expose arguments or environment secrets. info = {'name': name.removeprefix('/data/openpilot/'), 'user': user, 'kernel': not args} names[identity] = info cpu = None if cpu_capacity and identity in self.previous and current >= self.previous[identity]: - # Same aggregate tick window as the total, including any core hotplug. - # 100% means all measured CPU capacity, not one fully occupied core. cpu = round(min(100, (current - self.previous[identity]) / cpu_capacity * 100), 1) rows.append({'pid': identity[0], **info, 'state': fields[0], 'cpu': cpu, 'memoryMiB': round(max(0, int(fields[21])) * self.page / 1048576, 1)}) except (OSError, ValueError, IndexError): - continue # Process exited or is inaccessible during this snapshot. + continue memory = {line.split(':')[0]: int(line.split()[1]) for line in (self.root / 'meminfo').read_text().splitlines() if ':' in line} - total = memory['MemTotal'] / 1024 + total_kib = memory.get('MemTotal') + if total_kib is None or total_kib <= 0: + raise OSError('MemTotal is unavailable') + total = total_kib / 1024 available = memory.get('MemAvailable', memory.get('MemFree', 0)) / 1024 disk = shutil.disk_usage('/data' if Path('/data').exists() else '/') self.cached = {'sampledAt': time.time(), 'sampleSeconds': round(elapsed, 2) if elapsed else None, diff --git a/starpilot/system/the_galaxy/tests/test_system_monitor.py b/starpilot/system/the_galaxy/tests/test_system_monitor.py index 8a87e71485..3525322815 100644 --- a/starpilot/system/the_galaxy/tests/test_system_monitor.py +++ b/starpilot/system/the_galaxy/tests/test_system_monitor.py @@ -60,7 +60,6 @@ def test_processes_use_same_total_capacity_as_overall(tmp_path, monkeypatch, cor capacity = core_count * 2 * monitor.hz used = capacity // 4 fixture(tmp_path, active=100 + used, idle=900 + capacity - used) - # /proc/stat has one row per currently online core. with (tmp_path / 'stat').open('a') as f: for i in range(1, core_count): f.write(f'cpu{i} 100 0 0 900 0 0 0 0\n') @@ -79,7 +78,6 @@ def test_cpu_capacity_is_measured_across_core_hotplug(tmp_path, monkeypatch): fixture(tmp_path); proc(tmp_path) monitor = SystemMonitor(tmp_path); monitor.sample() now[0] = 2 - # Four cores for one second, then eight for one second: 12 core-seconds. capacity = 12 * monitor.hz fixture(tmp_path, active=100 + monitor.hz, idle=900 + capacity - monitor.hz) with (tmp_path / 'stat').open('a') as f: @@ -101,3 +99,11 @@ def test_missing_or_reset_capacity_has_no_process_percentage(tmp_path, monkeypat sample = monitor.sample() assert sample['cpuPercent'] is None assert sample['processes'][0]['cpu'] is None + + +def test_missing_total_memory_fails_closed(tmp_path): + fixture(tmp_path) + (tmp_path / 'meminfo').write_text('MemAvailable: 512000 kB\n') + + with pytest.raises(OSError, match='MemTotal'): + SystemMonitor(tmp_path).sample()