ref:26a75f114bf0a6f730857d41b48807030745dd7f

test(e2e): judge desktop soak memory against the first half's peak

A 45-stream soak with two guest reboots (162k frames, 1080p60 video, NVENC) kept descriptors at 46 and threads at 28, while RSS rose from 239 MB after the first stream to about 300 MB by the ninth and stayed between 300 and 332 MB for the remaining 36 streams. Comparing against the first stream reported that warm-up as a leak; compare the last stream with the highest RSS in the first half instead. Refs #12 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BPNw4PCgkEfhyCjQT19wsb
SHA: 26a75f114bf0a6f730857d41b48807030745dd7f
Author: Cole Christensen <cole.christensen@gmail.com>
Date: 2026-09-13 11:17
Parents: 68c58c9
1 files changed +12 -8
Type
tests/e2e/qemu/desktop/desktop_stream.sh +12 −8
@@ -18,8 +18,10 @@
# SESSIONS streams in a row against the same Sunshine and VM (default: 1)
# RESTART_VM_EVERY restart the guest with QMP system_reset after every N sessions (default: 0 = never)
# SAMPLE_SECONDS resource sampling interval (default: 5)
# MAX_RSS_GROWTH_MB allowed growth of Sunshine's RSS between the end of the first and the last session (default: 64)
# MAX_FD_GROWTH allowed growth of Sunshine's open descriptors between the same points (default: 8)
# MAX_RSS_GROWTH_MB allowed growth of Sunshine's RSS at the end of the last session over the highest RSS at the end
# of any session in the first half (default: 32). Allocators and encoder pools settle during the
# first streams, so growth against the first stream alone isn't a leak.
# MAX_FD_GROWTH allowed growth of Sunshine's open descriptors over the end of the first session (default: 8)
# ARTIFACTS output directory (default: a new directory under /tmp); gets summary-<n>.json, resources.csv,
# result.json and the logs
set -euo pipefail
@@ -40,7 +42,7 @@
sessions="${SESSIONS:-1}"
restart_every="${RESTART_VM_EVERY:-0}"
sample_seconds="${SAMPLE_SECONDS:-5}"
max_rss_growth_mb="${MAX_RSS_GROWTH_MB:-32}"
max_rss_growth_mb="${MAX_RSS_GROWTH_MB:-64}"
max_fd_growth="${MAX_FD_GROWTH:-8}"
artifacts="${ARTIFACTS:-$(mktemp -d "${TMPDIR:-/tmp}/sunshine-desktop.XXXXXX")}"
port="$((21000 + (RANDOM % 190) * 100))"
@@ -140,8 +142,8 @@
# 4. streams
timeout_s=$(( frames / stream_fps * 3 + 120 ))
first_end_rss=""
first_end_fds=""
first_half_max_rss=0
for ((session = 1; session <= sessions; session++)); do
echo "${session}" > "${work}/session"
set +e
@@ -160,9 +162,11 @@
rss=$(awk '/^VmRSS/ {print $2}' "/proc/${sunshine_pid}/status")
fds=$(ls "/proc/${sunshine_pid}/fd" | wc -l)
if (( session == 1 )); then
first_end_rss=${rss}
first_end_fds=${fds}
fi
if (( session <= (sessions + 1) / 2 && rss > first_half_max_rss )); then
first_half_max_rss=${rss}
fi
echo "desktop: stream ${session}/${sessions}: $(cat "${artifacts}/summary-${session}.json") rss=${rss}kB fds=${fds}" >&2
if (( restart_every > 0 && session % restart_every == 0 && session < sessions )); then
sleep 5
@@ -180,7 +184,7 @@
sampler_pid=""
cp -f "${work}/sunshine.log" "${work}/vm/qemu.log" "${artifacts}/" 2>/dev/null || true
rss_growth_kb=$(( rss - first_end_rss ))
rss_growth_kb=$(( rss - first_half_max_rss ))
fd_growth=$(( fds - first_end_fds ))
python3 - "${artifacts}" "${sessions}" "${rss_growth_kb}" "${fd_growth}" <<'EOF'
import glob, json, statistics, sys
@@ -194,7 +198,7 @@
"latency_p50_ms_median": statistics.median(p50) if p50 else None,
"latency_p95_ms_median": statistics.median(p95) if p95 else None,
"latency_p95_ms_max": max(p95) if p95 else None,
"rss_growth_kb_over_first_half_peak": rss_growth_kb,
"rss_growth_kb_after_first_session": rss_growth_kb,
"fd_growth_after_first_session": fd_growth,
}
json.dump(result, open(f"{artifacts}/result.json", "w"), indent=1)
@@ -203,7 +207,7 @@
if (( sessions > 1 )); then
if (( rss_growth_kb > max_rss_growth_mb * 1024 )); then
echo "desktop: FAIL: Sunshine RSS grew ${rss_growth_kb} kB after the first stream" >&2
echo "desktop: FAIL: Sunshine RSS at the end is ${rss_growth_kb} kB above its peak in the first half of the streams" >&2
exit 1
fi
if (( fd_growth > max_fd_growth )); then