atuq: el umbral son 4 — y mostrar el diálogo NO limpia el contador, como este documento decía

Medido en los dos lados, partiendo del perfil en 2 y con tres fases más en un arranque: el que
encuentra 2 guardado PINTA (y queda en 3); el que encuentra 3 lo sube a 4, compara 4 > 3 y abre el
diálogo (y queda en 4); el siguiente queda en 5 y vuelve a abrir el diálogo. O sea que la
comparación va DESPUÉS del incremento del propio arranque: contando desde la última corrida que
terminó bien, son cuatro arranques interrumpidos seguidos y el quinto lanzamiento ya no abre el
navegador.

`toolkit.startup.last_success` vale lo mismo en las tres fases (1789494795, el arranque de la última
corrida sana): el contador sube mientras esa marca no se mueve. Con las dos cifras juntas, un
contador que no cambió deja de ser ambiguo entre «no subió» y «subió y se limpió».

⚠ Y eso refuta una atribución que yo había escrito ayer y que estaba en tres sitios: mostrar el
diálogo NO limpia el contador (k2 lo dejó en 4, k3 en 5). Queda retirada del SDD y del comentario
del arnés. La desaparición del 16 entre las 17:12 y las 17:20 pasa a ser un hecho SIN explicación
medida, con dos candidatas escritas y ninguna dada por buena — y una de ellas es mía y fea: todas
las corridas que pintaron llevaban `--crashes 0` pinneado, que es el valor por DEFECTO, y un pref
igual al default no se persiste. El «ausente» puede ser eso y no una limpieza. Queda dicho cómo se
cierra: dejar el contador en 2 SIN pinnearlo, correr hasta que pinte y leerlo después.

Un discriminador que salió gratis: `ATUQ-EXIT=137` es «la matamos» (128+9) y `ATUQ-EXIT=0` es «se
fue sola» por el diálogo. El código de salida contesta lo que una captura no podía.

Medido por la otra sesión del frente (work/umbral-1.txt), con la predicción escrita antes de mirar.
This commit is contained in:
Sergio
2026-09-15 18:20:23 +00:00
parent cd678efd06
commit 7997464337
3 changed files with 212 additions and 133 deletions
+31 -10
View File
@@ -255,13 +255,21 @@ def main():
"que es la única forma de subirle el nivel de log sin recompilar")
ap.add_argument("--crashes", type=int, metavar="N",
help="fija `toolkit.startup.recent_crashes` del perfil del usuario antes de "
"lanzar: 0 lo limpia, >3 reproduce el diálogo de Modo de resolución de "
"problemas. Sólo tiene efecto con --as-user")
"lanzar: 0 lo limpia, 4 o más reproduce el diálogo de Modo de resolución "
"de problemas (medido: con 3 guardado todavía pinta, porque el arranque "
"suma ANTES de comparar). Sólo tiene efecto con --as-user. ⚠ 0 es el valor "
"por DEFECTO y un pref igual al default no se persiste: si lo que querés "
"medir es quién limpia el contador, NO lo pinnees")
ap.add_argument("--xulstore", metavar="ANCHOxALTO",
help="agrega una SEGUNDA fase en el mismo arranque con `xulstore.json` sembrado "
"(p. ej. 1152x720): la ventana arranca con un tamaño YA PERSISTIDO. Es el "
"control del 117x70 — si con semilla sale grande, lo que falla es que nadie "
"le da tamaño en el primer arranque")
ap.add_argument("--repeat", type=int, metavar="N",
help="corre N fases IDÉNTICAS en el mismo arranque. Con `--wait` corto (60 s, o "
"sea antes de que pinte) mide el contador de caídas: cada fase lee "
"`recent_crashes` ANTES de lanzar, así que la serie 0,1,2… dice cuánto suma "
"un arranque interrumpido — y si no sube, el envenenamiento vino de otro lado")
ap.add_argument("--out", default=os.path.join(RAIZ, "work/atuq-imagen"))
a = ap.parse_args()
@@ -400,8 +408,12 @@ def main():
# que CADA corrida fallida puede sumar uno: el instrumento degrada el sujeto y
# la tasa medida deja de ser del producto. Sin este número impreso, una corrida
# no se puede interpretar.
volcado = ser.cmd("grep -E 'toolkit.startup.(recent_crashes|max_resumed)|"
"browser.sessionstore.max_resumed' %s/prefs.js" % d, 60)
# ⚠ `last_success` VA EN EL MISMO VOLCADO. Es la otra mitad del cálculo: sin
# él, un `recent_crashes` que no se movió es ambiguo entre «no subió» y «subió y
# lo limpió el gancho de cierre», que son conclusiones opuestas sobre la misma
# cifra.
volcado = ser.cmd("grep -E 'toolkit.startup.(recent_crashes|max_resumed|"
"last_success)|browser.sessionstore.max_resumed' %s/prefs.js" % d, 60)
print(volcado.strip())
estado["recent_crashes_antes"] = leer_contador_de_caidas(volcado)
else:
@@ -455,8 +467,12 @@ def main():
línea de orden del shell del serial y ahí se mata la sesión entera (medido en el hub,
sale 144 y lo que sigue no corre).
"""
# ⚠ `-9` A PROPÓSITO. Un `kill` a secas le da a Gecko su apagado ordenado, y un
# apagado ordenado es EXACTAMENTE la variable bajo prueba: lo que hace este arnés al
# terminar es matar la VM entera, que no avisa. Con `-KILL` la fase siguiente
# encuentra el mismo estado que encontraría tras una corrida matada de verdad.
ser.cmd("for p in /proc/[0-9]*; do grep -q atuq $p/comm 2>/dev/null && "
"kill ${p#/proc/}; done; true", 60)
"kill -9 ${p#/proc/}; done; true", 60)
time.sleep(8)
salida = ser.cmd("for c in /proc/[0-9]*/comm; do cat $c 2>/dev/null; done | grep -c atuq")
# El serial hace ECO de la orden, así que la respuesta es la ÚLTIMA línea que sea un
@@ -477,10 +493,14 @@ def main():
fases = [("base", None)]
if a.xulstore:
fases.append(("semilla-%s" % a.xulstore, a.xulstore))
if a.repeat:
# N fases iguales: lo que varía entre ellas no es la configuración sino el ESTADO que
# deja la anterior en el perfil, que es justo lo que se quiere medir.
fases = [("k%d" % i, None) for i in range(1, a.repeat + 1)]
for fase, semilla in fases:
print("\n===== FASE «%s» =====" % fase, flush=True)
if semilla:
if fase != fases[0][0]:
matar_al_navegador()
perfil_d = perfil_de_la_fase(semilla)
log_atuq = "/var/log/cosmic/atuq-%s.log" % fase
@@ -545,10 +565,11 @@ def main():
rc[0] = 1
if perfil_d and a.as_user:
# ⚠ EL ESTADO FINAL ES PARTE DE LA PRUEBA. Gecko BORRA el contador después de
# mostrar el diálogo, así que un perfil envenenado se ve limpio en cuanto se
# lo mira DESPUÉS: sin este segundo volcado, la corrida siguiente parece
# «arreglada sola» y la anterior parece intermitente. Se lee con el navegador
# ⚠ EL ESTADO FINAL ES PARTE DE LA PRUEBA: el número cambia DENTRO de la
# corrida —lo escribe el ARRANQUE al encontrar el anterior sin terminar, no el
# kill— así que sin el «después» no se puede encadenar una corrida con la
# siguiente, que es lo que convierte esto en una serie legible (medido:
# 2 → 3 → 4 en tres fases de un mismo arranque). Se lee con el navegador
# todavía vivo, o sea que refleja el último volcado de prefs de Gecko.
volcado = ser.cmd("grep -E 'toolkit.startup.recent_crashes' %s/prefs.js || "
"echo '(el pref no está: arranque sin caídas pendientes)'"