diff --git a/scripts/diagnostico/cazar-cuelgue-headless.sh b/scripts/diagnostico/cazar-cuelgue-headless.sh new file mode 100755 index 00000000..ced34462 --- /dev/null +++ b/scripts/diagnostico/cazar-cuelgue-headless.sh @@ -0,0 +1,77 @@ +#!/bin/bash +# cazar-cuelgue-headless.sh — cazador de un cuelgue INTERMITENTE de firefox en headless. +# +# ══ EL CASO, Y POR QUÉ ESTE SCRIPT SOBREVIVE AUNQUE NO LO RESOLVIÓ ═════════════════════════════ +# El 2026-09-08, dos corridas del banco de PGO se colgaron: 120.034 y 120.030 ms, o sea EXACTAMENTE +# el `timeout 120` del arnés. No eran ruido de carga —una corrida normal son ~20 s— sino bloqueos +# hasta que las maté. Dos de 56 (~3,5 %). +# +# NO SE REPRODUJO. 202 corridas en cuatro condiciones, CERO cuelgues: +# +# un binario, un sandbox compartido ............... 30 · 0 +# 4 binarios alternando, un sandbox ............... 60 · 0 (descarta churn de caché de página) +# 4 binarios, UN SANDBOX NUEVO POR CORRIDA ........ 56 · 0 (descarta el montaje de namespaces) +# ídem, con LAS PÁGINAS EXACTAS del banco ......... 56 · 0 (descarta la página) +# +# Si la tasa fuera 3,5 %, ver cero en 202 tendría probabilidad ~0,06 %. O sea que la tasa real bajo +# estas condiciones NO es ésa, y lo que faltaba estaba fuera de ellas. +# +# ⚠ UN ERROR DE MÉTODO QUE COSTÓ 146 CORRIDAS, ANOTADO PARA NO REPETIRLO: las tres primeras cazas +# usaron `flex.html` y `tablas.html` porque las tenía a mano, cuando las colgadas habían sido en +# `fuera-del-corpus.html` y `del-corpus.html`. Estuve probando una condición que NO era la +# observada, creyendo que sí. Antes de concluir «no se reproduce», comprobar que el instrumento +# reproduce lo que dice reproducir. +# +# LO QUE SÍ SE APRENDIÓ, que acota al culpable: +# · Es TODO O NADA. En 202 corridas ninguna pasó siquiera de 45 s: o terminan en ~20 s o se +# bloquean hasta el timeout. No es lentitud ocasional, es un bloqueo. +# · Sobrevive la hipótesis que no se puede probar retroactivamente: contención transitoria de otra +# cosa en la máquina (es compartida con otros agentes) durante esas dos ventanas. +# +# ══ PARA QUÉ SIRVE ENTONCES ════════════════════════════════════════════════════════════════════ +# Para que la PRÓXIMA vez que aparezca, se capture en el acto en vez de empezar de cero: `wchan`, +# `syscall` y `state` de cada proceso (distinguen espera de disco de espera de lock), memoria y +# carga del host en ese instante, y el log del navegador de esa corrida —que en las buenas se tira—. +# +# Uso: N=56 UMBRAL=45 scripts/diagnostico/cazar-cuelgue-headless.sh +# (necesita el rootfs hidratado en store/.rootfs/sway-v2 y las páginas en el scratchpad; +# ajustar las rutas de arriba si se mueven) +set -u +RAIZ=/mnt/vvv/hammer; R=$RAIZ/store/.rootfs/sway-v2 +S=/tmp/claude-1001/-mnt-vvv-hammer/9d639cb7-92ff-4a26-bd7f-ec9988b972cf/scratchpad +N=${N:-56}; UMBRAL=${UMBRAL:-45} +FF=($(ls -d $RAIZ/store/8116bdec*-firefox $RAIZ/store/1d730334*-firefox \ + $RAIZ/store/3d199174*-firefox $RAIZ/store/bb33fd56*-firefox)) +PAGS=(fuera-del-corpus.html del-corpus.html) # LAS DEL BANCO: es donde ocurrieron las 2 colgadas +colgadas=0 +for i in $(seq 1 $N); do + art=${FF[$(( i % 4 ))]}; pag=${PAGS[$(( i % 2 ))]} + bwrap --ro-bind "$R" / --ro-bind /usr/lib/musl/lib/libc.so /lib/ld-musl-x86_64.so.1 \ + --ro-bind "$art/usr/lib/firefox" /usr/lib/firefox \ + --dev /dev --proc /proc --tmpfs /run --tmpfs /tmp --unshare-pid \ + --ro-bind "$S/bench" /bench --bind "$S/caza4" /salida --unshare-user --uid 0 --gid 0 \ + --setenv PATH /usr/bin:/bin \ + /bin/sh -c 'export HOME=/tmp/h MOZ_HEADLESS=1 MOZ_DISABLE_CONTENT_SANDBOX=1 LIBGL_ALWAYS_SOFTWARE=1 LANG=C.UTF-8; mkdir -p $HOME + LD_LIBRARY_PATH=/usr/lib/firefox /usr/lib/firefox/firefox --headless --screenshot /salida/x.png "file:///bench/'"$pag"'"' \ + >/dev/null 2>&1 & + BW=$! + t=0 + while kill -0 $BW 2>/dev/null && [ $t -lt $UMBRAL ]; do sleep 1; t=$((t+1)); done + if kill -0 $BW 2>/dev/null; then + colgadas=$((colgadas+1)) + D=$S/caza4/colgada-$i; mkdir -p "$D" + { echo "corrida $i · $(basename $art | cut -c1-12) · $pag · colgada a los ${t}s" + echo "--- memoria del host ---"; head -4 /proc/meminfo + echo "--- carga ---"; cat /proc/loadavg; } > "$D/resumen.txt" + ps -eo pid,ppid,stat,rss,wchan:24,comm 2>/dev/null | grep -iE 'bwrap|firefox|Web Con|Isolated|PID' > "$D/ps.txt" + for p in $(pgrep -P $BW 2>/dev/null; pgrep -f 'usr/lib/firefox/firefox' 2>/dev/null | head -8); do + { echo "--- pid $p comm=$(cat /proc/$p/comm 2>/dev/null) state=$(awk '/^State:/{print $2,$3}' /proc/$p/status 2>/dev/null)" + echo " wchan=$(cat /proc/$p/wchan 2>/dev/null)" + echo " syscall=$(cut -c1-70 /proc/$p/syscall 2>/dev/null)" + echo " rss=$(awk '/^VmRSS:/{print $2,$3}' /proc/$p/status 2>/dev/null)"; } >> "$D/procesos.txt" 2>/dev/null + done + kill -TERM $BW 2>/dev/null; sleep 3; kill -KILL $BW 2>/dev/null + printf 'X' + else printf '.'; fi +done +echo ""; echo "== colgadas: $colgadas de $N"