From e74d4496552ad866a5eee9e06bbc44adc29482cc Mon Sep 17 00:00:00 2001 From: Sergio Date: Mon, 7 Sep 2026 20:45:56 +0000 Subject: [PATCH] perfilar.sh: la corrida de entrenamiento PGO, con las tres defensas puestas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Estaba en un scratchpad y se perdía; ahora vive en el repo con el porqué de cada guarda, porque las tres se pagaron durante la primera corrida: 1. --unshare-pid en el bwrap. Sin él los hijos SOBREVIVEN al sandbox: un http.server quedó vivo 19 HORAS, retuvo el puerto y envenenó las corridas siguientes con «Address in use». 2. Puerto aleatorio por corrida. Elimina la colisión de raíz en vez de detectarla: si otro proceso tiene un puerto, este arranque usa otro. 3. Marcador único por corrida, servido desde una copia ESCRIBIBLE del corpus. La versión anterior horneaba el marcador como constante del script, y eso rompía justo lo que el chequeo existe para probar: el servidor huérfano servía el MISMO fichero de marca y el chequeo lo daba por bueno. Verificaba «alguien sirve este contenido», no «este servidor es el mío». Con el marcador por corrida, un servidor ajeno devuelve otra cosa y se aborta. Y queda escrito por qué los sandboxes de Firefox van apagados en el entrenamiento: con ellos los hijos mueren con signal 11, el proceso que renderiza no nace, el servidor registra 0 GET y el único .profraw es el del padre arrancando — un perfil de nada, con todo en verde. Es lo que hace el profileserver.py de Mozilla. Sólo aplica al entrenamiento. La espera al servidor va con reintento y no con un sleep fijo: 2 s concluían «no responde» sobre uno que sí iba a responder. --- scripts/pgo-corpus/perfilar.sh | 66 ++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100755 scripts/pgo-corpus/perfilar.sh diff --git a/scripts/pgo-corpus/perfilar.sh b/scripts/pgo-corpus/perfilar.sh new file mode 100755 index 00000000..b9ff162e --- /dev/null +++ b/scripts/pgo-corpus/perfilar.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# perfilar.sh — corrida de entrenamiento PGO: recorre el corpus con el firefox INSTRUMENTADO y deja +# los `.profraw` en /salida. Pensado para correr dentro de un bwrap con: +# --ro-bind / --ro-bind /pgo --bind /salida --unshare-pid +# +# ══ TRES DEFENSAS QUE NO SON PARANOIA: LAS TRES SE PAGARON ═════════════════════════════════════ +# +# 1. `--unshare-pid` EN EL BWRAP (fuera de este script). Sin él los hijos SOBREVIVEN al sandbox: un +# `http.server` quedó vivo 19 HORAS, retuvo el puerto y envenenó las corridas siguientes. +# +# 2. PUERTO ALEATORIO POR CORRIDA. Elimina la colisión de raíz en vez de detectarla: si otro proceso +# tiene un puerto, este arranque usa otro y no se entera. +# +# 3. MARCADOR ÚNICO POR CORRIDA, servido desde una copia ESCRIBIBLE del corpus. La primera versión +# horneaba el marcador como constante del script, y eso rompía justo lo que el chequeo existe +# para probar: un servidor ajeno servía el MISMO fichero de marca y el chequeo lo daba por bueno. +# Verificaba «alguien sirve este contenido», no «este servidor es el mío». +# +# ⚠ Y LOS SANDBOXES DE FIREFOX VAN APAGADOS. Con ellos los procesos hijo mueren con SIGNAL 11, el +# que renderiza no llega a nacer, el servidor registra 0 GET y el único `.profraw` es el del padre +# arrancando: un perfil de nada, con todo en verde. Es lo mismo que hace el `profileserver.py` de +# Mozilla. Sólo aplica al entrenamiento; el firefox que se distribuye lleva sus sandboxes intactos. +set -u +export HOME=/tmp/home; mkdir -p "$HOME" +export LANG=C.UTF-8 MOZ_HEADLESS=1 LIBGL_ALWAYS_SOFTWARE=1 +export MOZ_DISABLE_CONTENT_SANDBOX=1 MOZ_DISABLE_GMP_SANDBOX=1 MOZ_DISABLE_RDD_SANDBOX=1 MOZ_DISABLE_SOCKET_PROCESS_SANDBOX=1 +export LLVM_PROFILE_FILE=/salida/ff-%p-%m.profraw + +MARCA="pgo-$$-$(cat /proc/sys/kernel/random/uuid 2>/dev/null || echo ${RANDOM:-0}${RANDOM:-0})" +PUERTO=$(( 20000 + $$ % 20000 )) + +# Copia ESCRIBIBLE del corpus (tmpfs): hace falta para poner el marcador dentro de lo que se sirve. +SERVIR=/tmp/servir; mkdir -p "$SERVIR"; cp -a /pgo/. "$SERVIR"/ 2>/dev/null +printf '%s\n' "$MARCA" > "$SERVIR/marca.txt" + +cd "$SERVIR" && /usr/bin/python3 -m http.server "$PUERTO" --bind 127.0.0.1 > /salida/httpd.log 2>&1 & + +# Espera CON REINTENTO (un `sleep` fijo concluía «no responde» sobre un servidor que sí iba a +# responder) y exigiendo el marcador de ESTA corrida. +i=0 +while [ $i -lt 25 ]; do + got=$(/usr/bin/python3 -c "import urllib.request +try: print(urllib.request.urlopen('http://127.0.0.1:$PUERTO/marca.txt',timeout=3).read().decode().strip()) +except Exception: pass" 2>/dev/null) + [ "$got" = "$MARCA" ] && break + i=$((i+1)); sleep 1 +done +[ $i -lt 25 ] || { + echo "!! en $PUERTO no contesta MI servidor (marca leída: '${got:-nada}') — abortando antes de" >&2 + echo " generar perfiles de páginas que nunca se cargaron." >&2 + tail -3 /salida/httpd.log >&2 + exit 1 +} +echo "== servidor propio OK en $PUERTO" + +n=0 +for f in $(cd "$SERVIR" && find . -name '*.html' | sed 's|^\./||' | sort); do + n=$((n+1)) + LD_LIBRARY_PATH=/usr/lib/firefox timeout 200 /usr/lib/firefox/firefox --headless \ + --screenshot /salida/shot.png "http://127.0.0.1:$PUERTO/$f" >/dev/null 2>&1 + printf '.' +done +echo "" +echo "== páginas recorridas: $n" +echo "== GETs servidos: $(grep -c GET /salida/httpd.log 2>/dev/null)" +echo "== .profraw: $(ls /salida/*.profraw 2>/dev/null | wc -l)"