Commit Graph
8 Commits
Author SHA1 Message Date
Sergio 17f8f4fb80 takana: la ruta del repo es /mnt/vvv/takana
El directorio se movio de verdad, asi que las referencias absolutas dentro del
repo ya eran incorrectas. Tambien los ejemplos que usaban ~/hammer.

NO se tocan docs/evidencia/: son REGISTRO de lo que se corrio ese dia, y
reescribir una ruta ahi adentro falsifica la evidencia. Que nombren una ruta que
ya no existe es correcto: existia cuando se midio.
2026-09-09 20:08:02 +00:00
SergioandClaude Opus 5 0d7beefb60 cazadores: setsid + matar el GRUPO — mataban el bwrap y el firefox sobrevivía
Segunda vez el mismo día: un firefox colgado de la verificación A/B se quedó con el lock de
build de la granja. La causa es del arnés, no de firefox: 'kill $BW' mata el bwrap EXTERIOR,
que no es el init del namespace de PID, así que el proceso de dentro sigue vivo — y si el
arnés corría bajo flock, hereda el fd del lock y deja a la granja muda hasta que alguien mire.

Medido con control negativo y positivo:
  matar sólo el bwrap  → queda 1 firefox vivo   (reproduce la fuga)
  setsid + matar grupo → quedan 0                (arreglado)

Se documenta además que el 'flock' del uso lleva '-o', que no es opcional.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QPJteswQvP1L7zSBrQQe2
2026-09-08 18:12:31 +00:00
SergioandClaude Opus 5 20a719f9c0 cuelgue headless: verificación A/B y evidencia — 98 segfaults contra 0
arnés viejo (sólo CONTENT)  98 segfaults · 1 colgada · 49/50 screenshots
  arnés nuevo (los cinco)      0 segfaults · 0 colgadas · 50/50 screenshots

Queda dicho en la cabecera qué prueba esto y qué no. El MECANISMO sí: 98 → 0, exactamente 2
por corrida, en el 100% de las corridas; un efecto determinista se refuta con pocas muestras.
El CUELGUE no: 1 contra 0 en 50 pares no es significativo — con la tasa medida (3 en 130 ≈
2,3%) ver cero en 50 tiene ~31% de probabilidad aunque nada hubiera cambiado, y harían falta
~200 corridas para un negativo convincente.

Lo que sí sostiene: las tres colgadas observadas tienen la MISMA huella exacta (7 fallos de
lanzamiento de pestaña, 1 de rdd, 6 messageManager is null, cero screenshot) y las tres
cayeron donde los ayudantes revientan. Ninguna apareció sin el mecanismo.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QPJteswQvP1L7zSBrQQe2
2026-09-08 16:48:03 +00:00
SergioandClaude Opus 5 b221f06c01 arneses: los CINCO sandboxes de Gecko apagados, no un subconjunto a ojo
Cada arnés headless tenía su propio subconjunto de MOZ_DISABLE_*_SANDBOX, elegido en su
momento sin medir: banco 1 de 5, perfilar 4, codecs 3, ruteo 3, nativo 2, inicio 2. Con
cualquier subconjunto incompleto el sandbox de Firefox sigue intentando montar su
user-namespace dentro del de bwrap, falla con 'uid_map: EPERM' y deja un ayudante
'Sandbox Forked' muerto de SIGSEGV por cada intento — en el 100% de las corridas, después
de escribir el PNG y por eso invisible.

Medido: 2 cadáveres por corrida con sólo CONTENT apagado, 0 con los cinco. Verificado sobre
el arnés ya editado: segv=0 EPERM=0 screenshot=sí.

Acá no se pierde seguridad: bwrap ya es la jaula, el sandbox de Gecko es redundante y lo
único que hace dentro es fallar.

⚠ Cambia las condiciones de medición del banco de PGO: las cifras de docs/26 se tomaron con
el arnés viejo. Los cocientes del PGO comparan dos firefox bajo el mismo arnés y deberían
aguantar; la línea base absoluta de arranque no tiene por qué. Anotado en la cabecera.

No se tocan scripts/wlr/dunst-headless.sh (otro agente trabajando en él) ni los cazadores,
que conservan el entorno viejo a propósito para poder reproducir la condición original.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QPJteswQvP1L7zSBrQQe2
2026-09-08 16:11:28 +00:00
SergioandClaude Opus 5 b405fc77df cuelgue headless RESUELTO: el sandbox de Firefox no puede montar su userns dentro de bwrap
No era la presión de memoria. La caza bajo presión (4 niveles × 20 corridas, con el nivel de
presión MEDIDO por MemAvailable y PSI) reprodujo el cuelgue por primera vez en 282 corridas
—2 de 80, 2,5%, compatible con el 3,5% original— pero SIN dosis-respuesta: los dos cuelgues
cayeron en el nivel con PSI 0,00 y los dos niveles apretados dieron cero.

Tres hipótesis muertas, cada una con su medición:
  · OOM se lleva al hijo   → oom_kill de /proc/vmstat no se movió (delta=0) y los hijos vivían
  · presión de memoria     → sin dosis-respuesta
  · fork server de Gecko   → A/B del pref: el proceso forkserver desaparece (35 muestras → 0,
                             o sea que el pref hizo efecto) y siguen los mismos 2 segfaults

La causa apareció comparando el log de una colgada con el de una BUENA, que es lo que faltaba:
en las 6 buenas TAMBIÉN revientan 2 hijos con SIGSEGV, después de escribir el PNG y por eso
invisibles. Muestreando /proc adentro, se llaman 'Sandbox Forked': los ayudantes que el sandbox
propio de Firefox forkea para montar su user-namespace, que no puede montar porque ya estamos
dentro del de bwrap ('writing /proc/self/uid_map: EPERM').

Las tres cantidades bajan juntas hasta cero, que es forma de cadena causal:

  sandbox completo            segv=7  SIN screenshot  EPERM=9  SandboxForked=725
  content.level=0             segv=2  con screenshot  EPERM=2  SandboxForked=68
  los 3 prefs .level a 0      segv=1  con screenshot  EPERM=1  SandboxForked=33
  las 5 MOZ_DISABLE_*         segv=0  con screenshot  EPERM=0  SandboxForked=0

Y el sandbox completo cuelga DETERMINISTA (rc=124 a los 180 s). Comparte familia con el
intermitente pero no está probado que sean el mismo: al determinista le faltan los 'Failed to
launch'. Queda dicho como lo que es.

Arreglo para cualquier firefox headless en bwrap (donde el sandbox de Gecko no aporta nada,
porque bwrap ya es la jaula):
  MOZ_DISABLE_{CONTENT,GMP,RDD,SOCKET_PROCESS,UTILITY}_SANDBOX=1

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QPJteswQvP1L7zSBrQQe2
2026-09-08 15:59:23 +00:00
SergioandClaude Opus 5 8ff6ae7b3a pgo: el control que faltaba — el arranque eran 16 s y diluía todos los porcentajes
El banco medía el ciclo COMPLETO del proceso (arrancar → renderizar → capturar → salir)
y nunca se midió cuánto de eso era arranque. Con una página vacía como control, y las 4
cargas × 2 variantes en UNA sola sesión intercalada (restar entre corridas de días
distintos no vale: la misma variante deriva ~1%):

  maquetación   trabajo 7281 → 4744 ms   -34,8%   (publicado: -10,9%)
  carga ajena   trabajo 4550 → 4634 ms    +1,8%   = cero, y es buena señal
  SunSpider     trabajo  -34 →   -6 ms   bajo el ruido

Dos correcciones al documento:

1. El -10,9% es correcto para el ciclo completo pero se leía como 'firefox 11% más
   rápido'. Sobre el trabajo de la página el PGO rinde -34,8%: tres veces más. El error
   subestimaba el propio resultado.

2. La explicación que di del SunSpider era aire. Su trabajo mide -34 ms, NEGATIVO: la
   página con el benchmark tardó menos que la vacía. No había nada que medir, y yo le
   colgué encima una teoría sobre el JIT. La teoría puede ser cierta; esta medición
   nunca la probó.

Las tablas viejas se dejan sin retocar, con un aviso arriba: el error de método enseña
más que el número corregido. Todo el rigor estadístico estaba puesto sobre una cantidad
que no era la que yo creía medir, y ninguna repetición lo habría revelado — sólo un
control, que costó siete corridas de una página vacía.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014QPJteswQvP1L7zSBrQQe2
2026-09-08 14:57:26 +00:00
Sergio 8c1378cb1f cazador: evidencia a favor de la hipótesis de presión de memoria
Al montar otro banco, la carga estaba en 3,34 (contra 0,13 de las cuatro cazas)
porque otro agente arrancó un cargo, y kswapd0 consumía CPU con CERO memoria
libre: recuperación de páginas bajo presión. Cada corrida mapea un libxul de
227 MB.

Es exactamente la condición que las cazas NO tenían y que los dos bancos con
cuelgues SÍ. No es prueba —el estado de aquellas ventanas no se reconstruye—
pero es la primera pista concreta, y cambia dónde buscar: cazar BAJO PRESIÓN de
memoria, no con la máquina ociosa.
2026-09-08 14:17:21 +00:00
Sergio 11e2c5f3a2 cazador del cuelgue en headless: NO reproducido en 202 corridas, y qué descarta
Dos corridas del banco de PGO se colgaron en 120.034 y 120.030 ms — exactamente
el timeout del arnés, o sea bloqueos y no lentitud. 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é)
  4 binarios, UN SANDBOX NUEVO POR CORRIDA .. 56 · 0  (descarta los 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%. La tasa real
bajo estas condiciones no es ésa, y lo que falta está fuera de ellas.

UN ERROR DE MÉTODO QUE COSTÓ 146 CORRIDAS, anotado en el script 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ó, y acota: 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
degradación, es bloqueo. Sobrevive la hipótesis que no se puede probar
retroactivamente: contención transitoria de otra cosa en la máquina —que es
compartida con otros agentes— durante esas dos ventanas.

El script queda para que la PRÓXIMA vez se capture en el acto (wchan, syscall,
state, memoria y carga del host, y el log del navegador de esa corrida) en vez
de empezar de cero.
2026-09-08 10:33:13 +00:00