diff --git a/system/proclogd.py b/system/proclogd.py index b008f8ed9b..ad59979f98 100755 --- a/system/proclogd.py +++ b/system/proclogd.py @@ -128,7 +128,8 @@ _SMAPS_KEYS = {b'Pss:', b'Pss_Anon:', b'Pss_Shmem:'} _smaps_path: str | None = None # auto-detected on first call # per-VMA smaps is expensive (kernel walks page tables for every VMA). -# cache results and only refresh every N cycles to keep CPU low. +# Cache results and stagger refreshes across N cycles so all large processes +# aren't scanned in the same cycle. _smaps_cache: dict[int, SmapsData] = {} _smaps_cycle = 0 _SMAPS_EVERY = 20 # refresh every 20th cycle (40s at 0.5Hz) @@ -158,8 +159,8 @@ def _read_smaps(pid: int) -> SmapsData: def _get_smaps_cached(pid: int) -> SmapsData: - """Return cached smaps data, refreshing every _SMAPS_EVERY cycles.""" - if _smaps_cycle == 0 or pid not in _smaps_cache: + """Return cached smaps data, refreshing each PID in its assigned cycle.""" + if pid % _SMAPS_EVERY == _smaps_cycle: _smaps_cache[pid] = _read_smaps(pid) return _smaps_cache.get(pid, {'pss': 0, 'pss_anon': 0, 'pss_shmem': 0}) diff --git a/system/tests/test_proclogd.py b/system/tests/test_proclogd.py new file mode 100644 index 0000000000..91ec84f29f --- /dev/null +++ b/system/tests/test_proclogd.py @@ -0,0 +1,20 @@ +from openpilot.system import proclogd + + +def test_smaps_refreshes_are_staggered(monkeypatch): + monkeypatch.setattr(proclogd, "_smaps_cycle", 3) + monkeypatch.setattr(proclogd, "_smaps_cache", {24: {"pss": 1, "pss_anon": 2, "pss_shmem": 3}}) + + refreshed = {"pss": 4, "pss_anon": 5, "pss_shmem": 6} + reads = [] + + def read_smaps(pid): + reads.append(pid) + return refreshed + + monkeypatch.setattr(proclogd, "_read_smaps", read_smaps) + + assert proclogd._get_smaps_cached(23) == refreshed + assert proclogd._get_smaps_cached(24) == {"pss": 1, "pss_anon": 2, "pss_shmem": 3} + assert proclogd._get_smaps_cached(25) == {"pss": 0, "pss_anon": 0, "pss_shmem": 0} + assert reads == [23]