diff --git a/.claude/skills/anland-mobilegl-plasma/SKILL.md b/.claude/skills/anland-mobilegl-plasma/SKILL.md index 21be40a3..b8fbaecd 100644 --- a/.claude/skills/anland-mobilegl-plasma/SKILL.md +++ b/.claude/skills/anland-mobilegl-plasma/SKILL.md @@ -126,7 +126,7 @@ timeout 25 mgrun offscreen glmark2-es2-wayland -s 1280x720 --run-forever > /tmp/ sleep 8; grep -a "presented through" /tmp/mgl-run.client.log ``` Chrome: `bash scripts/chrome-launch.sh [flags]` (log `/tmp/mgl-chrome-.client.log`, screenshot `ch-.png`; `CHROME_GPU=in-process` puts the GPU back in the browser, which cannot recover from a device loss). -Device loss: `setprop debug.mobilegl.inject_device_lost_pid ` (as root) loses the server session of the client whose pid - as it sees itself in the container - is ``, once per value, on any backend and whether or not it presents; `debug.mobilegl.inject_device_lost_at N` instead loses whichever session makes the N-th device check (Espryt presents and read-backs only). `bash scripts/chrome-recover.sh [rounds]` checks that Chrome repaints after such a loss without a restart (battery-guarded, stops below 40%). +Device loss: `setprop debug.mobilegl.inject_device_lost_pid ` (as root) loses the server session of the client whose pid - as it sees itself in the container - is ``, once per value, on any backend and whether or not it presents; `debug.mobilegl.inject_device_lost_at N` instead loses whichever session makes the N-th device check (Espryt presents and read-backs only). `bash scripts/chrome-recover.sh [rounds]` checks that Chrome repaints after such a loss without a restart (battery-guarded, stops below 40%). `bash scripts/kwin-recover.sh [rounds]` loses KWin's own session (default twice) and checks that KWin restarts compositing on a fresh session - same KWin, plasmashell and client processes, the screen still animating (glmark2 on Wayland, glxgears on X11, `CHROME=1` adds Chrome) - which needs the anland backend + kwin.patch reset handling deployed (`kwin-stage.sh build`). Probes: `scripts/probes/texfmt.c` (texture formats keep alpha?), `dmahash.c` (hash a process's dma-bufs); build in the container, e.g. `gcc -O1 /root/texfmt.c -o /usr/local/bin/mgl-texfmt -lEGL`, run with `mgrun offscreen`. A result counts only if ALL hold: diff --git a/.claude/skills/anland-mobilegl-plasma/scripts/kwin-recover.sh b/.claude/skills/anland-mobilegl-plasma/scripts/kwin-recover.sh new file mode 100644 index 00000000..087ee47c --- /dev/null +++ b/.claude/skills/anland-mobilegl-plasma/scripts/kwin-recover.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash +# kwin-recover.sh [rounds] - does the desktop keep working when KWin's OWN MobileGL session is lost? +# Starts animated clients (glmark2-es2-wayland: Wayland, zero-copy shared images; glxgears: X11 through +# Xwayland's GLX; with CHROME=1 also Chrome on an animated WebGL page), then per round loses the server +# session of KWin with the pid-targeted debug knob (debug.mobilegl.inject_device_lost_pid, KWin's pid as +# it sees itself in the container), waits, and checks. A round PASSES when: +# - the server latched KWin's session (logcat "injected:pid="); +# - KWin, plasmashell and every test client are the SAME processes (nothing restarted); +# - KWin restarted compositing on a fresh session (its journal: "compositing restarted on a fresh +# MobileGL session"; its client log: one more "RECOVERED"); +# - the screen still animates (shot.sh hashes differ) and the clients keep presenting; +# - no native crash (logcat -b crash). +# Between rounds the knob is reset to 0 while the clients run, which re-arms it for the same pid. +# Stops early below MIN_BATTERY percent (the phone drains faster under load than USB charges it). +# +# Deploy first (same MobileGL commit on both sides; see SKILL.md): +# bash scripts/build-android.sh && bash scripts/cyc.sh pre # server -> APK, restarts the session +# bash scripts/xbuild.sh && bash scripts/xdeploy.sh # client -> container +# ANLAND_WORKTREE= bash scripts/kwin-stage.sh build # kwin.patch + anland backend +# (wait for "== ALL DONE ==" in /root/kwin-build.log), then: adb shell 'su -c "sh /data/local/tmp/anl/run-plasma.sh"' +# Env: ROUNDS (default 2), SETTLE (s after the loss, default 10), REARM (s with the knob at 0 between +# rounds, default 4), MIN_BATTERY (default 40), CHROME (1: add Chrome), CHANNEL (ct.sh channel, +# default krec), TAG (screenshot/log tag, default krec). +set -euo pipefail +here=$(cd "$(dirname "$0")" && pwd) +. "$here/env.sh" +ROUNDS=${1:-${ROUNDS:-2}} +SETTLE=${SETTLE:-10} +REARM=${REARM:-4} +MIN_BATTERY=${MIN_BATTERY:-40} +CH=${CHANNEL:-krec} +TAG=${TAG:-krec} +COMPOSITOR_LOG=/tmp/mobilegl-compositor.client.log + +ct() { bash "$here/ct.sh" "$CH"; } + +battery() { adb_ shell dumpsys battery | tr -d '\r' | awk '/ level:/ {print $2}'; } +stop_clients() { + ct <<'IN' || true +pkill -x glmark2-es2-way 2>/dev/null; pkill -x glmark2-es2-wayland 2>/dev/null; pkill -x glxgears 2>/dev/null +true +IN +} +cleanup() { + adb_ shell "su -c 'setprop debug.mobilegl.inject_device_lost_pid 0'" || true + stop_clients +} +check_battery() { + local level; level=$(battery) + echo "battery: $level%" + if [ -n "$level" ] && [ "$level" -lt "$MIN_BATTERY" ]; then + echo "STOP: battery below $MIN_BATTERY%"; exit 3 + fi +} +trap cleanup EXIT + +check_battery +adb_ shell "su -c 'sh $DEV_TOOLS/status.sh'" || true +adb_ shell "su -c 'setprop debug.mobilegl.inject_device_lost_pid 0'" + +# The clients, each with its own log (mgrun's MGLOG), in the background for the whole run. +ct <<'IN' +rm -f /tmp/mgl-krec-gm.client.log /tmp/mgl-krec-gx.client.log /tmp/krec-gm.out /tmp/krec-gx.out +MGLOG=/tmp/mgl-krec-gm.log nohup timeout 900 mgrun offscreen glmark2-es2-wayland -s 960x540 --run-forever \ + > /tmp/krec-gm.out 2>&1 < /dev/null & +MGLOG=/tmp/mgl-krec-gx.log nohup timeout 900 mgrun offscreen glxgears -info > /tmp/krec-gx.out 2>&1 < /dev/null & +sleep 8 +echo "glmark2: $(pgrep -x glmark2-es2-way || pgrep -x glmark2-es2-wayland) glxgears: $(pgrep -x glxgears)" +grep -aE "presented through|could not be presented" /tmp/mgl-krec-gm.client.log | head -2 +grep -aE "presented through|readback" /tmp/mgl-krec-gx.client.log | head -2 +IN +if [ "${CHROME:-0}" = 1 ]; then + ct <<'IN' +cat > /tmp/mgl-krec-spin.html <<'HTML' + + + +HTML +chmod 644 /tmp/mgl-krec-spin.html +IN + WAIT=${WAIT:-20} CHANNEL=$CH bash "$here/chrome-launch.sh" "$TAG" --new-window "file:///tmp/mgl-krec-spin.html" +fi + +# KWin, plasmashell and the clients, as pids inside the container; how often KWin recovered so far. +snapshot() { + ct </dev/null || true) +f=\$(grep -ac "FPS" /tmp/krec-gm.out 2>/dev/null || true) +echo "\${k:-none} \${p:-none} \${g:-none} \${x:-none} \${c:-none} \${r:-0} \${f:-0}" +IN +} + +pass=0 +for round in $(seq 1 "$ROUNDS"); do + echo "=== round $round/$ROUNDS" + check_battery + read -r kwin plasma gm gx chrome recovered fps < <(snapshot) + echo "kwin=$kwin plasmashell=$plasma glmark2=$gm glxgears=$gx chrome=$chrome recoveries=$recovered fps-lines=$fps" + if [ "$kwin" = none ]; then echo "FAIL: KWin is not running"; break; fi + bash "$here/shot.sh" "$TAG-r$round-pre" 2 1 + adb_ logcat -c + adb_ logcat -b crash -c || true + adb_ shell "su -c 'setprop debug.mobilegl.inject_device_lost_pid $kwin'" + sleep "$SETTLE" + adb_ shell "su -c 'setprop debug.mobilegl.inject_device_lost_pid 0'" + bash "$here/shot.sh" "$TAG-r$round-post" 3 1 | tee "$ANL_DIR/$TAG-r$round-post.md5" + read -r kwin2 plasma2 gm2 gx2 chrome2 recovered2 fps2 < <(snapshot) + echo "after: kwin=$kwin2 plasmashell=$plasma2 glmark2=$gm2 glxgears=$gx2 chrome=$chrome2 recoveries=$recovered2 fps-lines=$fps2" + latched=$(adb_ logcat -d | grep -c "injected:pid=$kwin" || true) + adb_ logcat -d | grep -E "inject_device_lost_pid|SessionLatch|session #[0-9]+ (started|pid)|owner=server|lease" \ + | cut -c1-220 | head -14 || true + ct </dev/null | grep -aE "kwin_wayland" | grep -aiE "reset|restarted|MobileGL swap|context|fatal|abort" | tail -10 +echo "--- clients:"; grep -aE "DEVICE LOST|lost|Fatal" /tmp/mgl-krec-gm.client.log /tmp/mgl-krec-gx.client.log | tail -4 +IN + crashes=$(adb_ logcat -b crash -d | grep -c "Fatal signal\|Abort message" || true) + hashes=$(awk '{print $1}' "$ANL_DIR/$TAG-r$round-post.md5" | sort -u | wc -l) + verdict=PASS + [ "$latched" -ge 1 ] || { echo " - the server never latched KWin's session (pid $kwin)"; verdict=FAIL; } + [ "$kwin2" = "$kwin" ] || { echo " - KWin is a different process ($kwin -> $kwin2): it restarted"; verdict=FAIL; } + [ "$plasma2" = "$plasma" ] || { echo " - plasmashell restarted ($plasma -> $plasma2)"; verdict=FAIL; } + [ "$gm2" = "$gm" ] && [ "$gx2" = "$gx" ] || { echo " - a test client restarted or died"; verdict=FAIL; } + [ "${CHROME:-0}" != 1 ] || [ "$chrome2" = "$chrome" ] || { echo " - Chrome restarted"; verdict=FAIL; } + [ "$recovered2" -gt "$recovered" ] || { echo " - KWin's client log shows no new RECOVERED session"; verdict=FAIL; } + [ "$fps2" -gt "$fps" ] || { echo " - glmark2 stopped reporting frames"; verdict=FAIL; } + [ "$hashes" -ge 2 ] || { echo " - the screen does not change after the loss (frozen)"; verdict=FAIL; } + [ "$crashes" -eq 0 ] || { echo " - native crash(es):"; adb_ logcat -b crash -d | tail -8; verdict=FAIL; } + echo "round $round: $verdict" + [ "$verdict" = PASS ] && pass=$((pass + 1)) + [ "$verdict" = PASS ] || break + # Re-arm the knob for the same pid: a session must observe 0 before the next value counts. + sleep "$REARM" +done +echo "=== $pass/$ROUNDS rounds passed; screenshots in $ANL_DIR/$TAG-r*-*.png" +[ "$pass" = "$ROUNDS" ]