Galaxy: harden system monitor and optional chime

Keep the model-ready sound opt-in and return a controlled error when memory totals are unavailable.
This commit is contained in:
AngusBell97
2026-09-10 16:55:11 -05:00
committed by firestar5683
parent 1b1989f794
commit a3d8c8948e
3 changed files with 14 additions and 8 deletions
+1 -1
View File
@@ -352,7 +352,7 @@ inline static std::unordered_map<std::string, ParamKeyAttributes> 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}},
@@ -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,
@@ -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()