diff --git a/src/main/crash-reporting/linux-cgroup-memory-limit.test.ts b/src/main/crash-reporting/linux-cgroup-memory-limit.test.ts index 8b668c36aec..293aaaf7380 100644 --- a/src/main/crash-reporting/linux-cgroup-memory-limit.test.ts +++ b/src/main/crash-reporting/linux-cgroup-memory-limit.test.ts @@ -200,6 +200,31 @@ describe('cgroup-capped linux crash memory details', () => { expect(details.systemMemoryPressureSignal).toBe('mem-available') }) + it('keeps the plain label when the ceiling is not below host RAM', () => { + setSystemMemoryInfoReaderForTest(() => NO_HOST_PRESSURE) + // A container whose memory.max was set to the whole machine: a ceiling, but + // not one that made the 20 GB beside it unreachable, so it explains nothing. + setLinuxCgroupMemoryLimitReaderForTest(() => ({ maxBytes: 32_005 * 1024 * 1024 })) + + const details = getSystemMemoryDetails('linux') + + expect(details.systemMemoryCgroupMaxMB).toBe(32_005) + expect(details.systemMemoryPressureSignal).toBe('mem-available') + }) + + it('caps on memory.high alone, which throttles us long before memory.max would', () => { + setSystemMemoryInfoReaderForTest(() => NO_HOST_PRESSURE) + setLinuxCgroupMemoryLimitReaderForTest(() => ({ + maxBytes: undefined, + highBytes: 2_147_483_648 + })) + + const details = getSystemMemoryDetails('linux') + + expect(details.systemMemoryCgroupHighMB).toBe(2_048) + expect(details.systemMemoryPressureSignal).toBe('mem-available-cgroup-capped') + }) + it('adds nothing on a host with no v2 memory controller', () => { setSystemMemoryInfoReaderForTest(() => NO_HOST_PRESSURE) setLinuxCgroupMemoryLimitReaderForTest(() => undefined) diff --git a/src/main/crash-reporting/linux-memory-pressure-stall.test.ts b/src/main/crash-reporting/linux-memory-pressure-stall.test.ts index 45b999f15b5..f87349581d8 100644 --- a/src/main/crash-reporting/linux-memory-pressure-stall.test.ts +++ b/src/main/crash-reporting/linux-memory-pressure-stall.test.ts @@ -162,6 +162,21 @@ describe('stall in linux crash memory details', () => { expect(getSystemMemoryDetails('linux').systemMemoryPressureSignal).toBe('mem-available-stalled') }) + it('does not let a thrashing host label our own calm cgroup as stalled', () => { + setSystemMemoryInfoReaderForTest(() => NO_HOST_PRESSURE) + // A sibling cgroup is the hog: the host stalls, we do not, and whatever + // killed us was not this. Taking the higher of the two would misname it. + setLinuxMemoryPressureStallReaderForTest(() => ({ + host: { fullAvg10: 90 }, + cgroup: { fullAvg10: 1.2 } + })) + + const details = getSystemMemoryDetails('linux') + + expect(details.systemMemoryStallFullAvg10Pct).toBe(90) + expect(details.systemMemoryPressureSignal).toBe('mem-available') + }) + it('lets a cgroup ceiling outrank stall, since it explains the stall as well', () => { setSystemMemoryInfoReaderForTest(() => ({ ...NO_HOST_PRESSURE, total: 32_000 * 1024 })) setLinuxCgroupMemoryLimitReaderForTest(() => ({ maxBytes: 2_147_483_648 })) diff --git a/src/main/crash-reporting/pre-gone-host-memory.ts b/src/main/crash-reporting/pre-gone-host-memory.ts index 0db56796750..39b3e5aa30e 100644 --- a/src/main/crash-reporting/pre-gone-host-memory.ts +++ b/src/main/crash-reporting/pre-gone-host-memory.ts @@ -62,8 +62,8 @@ function withCarriedSwapVolume(sample: PreGoneSystemMemorySample): PreGoneSystem function commitHostMemorySample(nowMs: number): boolean { try { const details = getSystemMemoryDetails() - // Why not `length === 0`: the signal label is appended unconditionally, so a - // reading that resolved no memory field at all still arrives with one key. + // Why not `length === 0`: any reading that resolved at least one field also + // carries the signal label, so a lone label key means nothing was measured. if (!Object.keys(details).some((key) => key !== PRESSURE_SIGNAL_KEY)) { return false } diff --git a/src/main/crash-reporting/system-memory-details.ts b/src/main/crash-reporting/system-memory-details.ts index b72c915c816..744f7c58dd9 100644 --- a/src/main/crash-reporting/system-memory-details.ts +++ b/src/main/crash-reporting/system-memory-details.ts @@ -136,7 +136,7 @@ function cgroupCeilingBelowHostRam(details: CrashReportDetails): boolean { return total === undefined || lowest < total } -/** The cgroup's own stall is what systemd-oomd reads; the host figure is the fallback. */ +/** Our own cgroup's stall is nearest the kill; a busy host with a calm cgroup is a sibling's. */ function stallIsHigh(details: CrashReportDetails): boolean { const fullAvg10 = numericDetail(details, 'CgroupStallFullAvg10Pct') ?? numericDetail(details, 'StallFullAvg10Pct')