diff --git a/docs/26-atuq-envoltorio-gecko.md b/docs/26-atuq-envoltorio-gecko.md index 508c0835..cc2ba4e6 100644 --- a/docs/26-atuq-envoltorio-gecko.md +++ b/docs/26-atuq-envoltorio-gecko.md @@ -2231,10 +2231,33 @@ Tres cosas, y la tercera no la esperaba nadie: del arranque de las 17:53— o sea que **el gancho de cierre no llegó a correr ni una sola vez**, incluso en las que pintaron. Pintar no es terminar el arranque. -⇒ **la pregunta de quién limpia el contador sigue SIN medir**, y el §6.10.terdecies decía que esta -serie la iba a cerrar. No la cierra: ninguna corrida nuestra llegó nunca al gancho, así que las dos -candidatas del 16 siguen vivas y hace falta una corrida que deje al navegador **terminar de -arrancar** (y salir limpio) para preguntárselo. +⇒ la pregunta de quién limpia el contador **no la cierra esta serie** —el §6.10.terdecies decía que +sí—, y lo que hacía falta era una corrida que dejara al navegador **seguir vivo** después de pintar. + +##### Lo que limpia el contador: **sobrevivir un rato después de pintar** (2026-09-15) + +El par aísla la variable: mismo pin, mismo perfil, misma imagen, y la única diferencia es cuánto vive +el navegador después del primer cuadro. + +| corrida | pin | antes | pintó | vida después de pintar | después | +|---|---|---|---|---|---| +| S1 18:27 | `--crashes 0` | 5 | +97 s | corta — `--until-paint` corta la observación | **1** | +| cierre-1 19:06 | `--crashes 0` | 4 | +120 s | **~360 s** (ventana de 480 s, sin `--until-paint`) | **ausente** | + +⇒ **pintar no alcanza; sobrevivir un rato después de pintar, sí.** Y explica A sin anomalía: A +también siguió viva minutos después del primer cuadro, porque los volcados por el serial tardan. + +⚠ **Lo que de esto NO está medido**, y se dice antes de que la tabla dé a entender otra cosa: si ese +mismo arranque además **selló** el éxito. El volcado del «después» sólo grepeaba el contador, así que +de `cierre-1` hay la limpieza y no el sello — **ahora `last_success` va en los dos volcados y se +guarda con la medición**, que es lo que faltaba para no tener que repetir la corrida. Si el sello se +mueve, es un gancho que sella y limpia a la vez; si no, son dos. + +⚠ **Y el 16 sigue sin explicación, con una contradicción medida encima**: desapareció en una corrida +que fue diálogo + `ATUQ-EXIT=0`, y las fases k2/k3 del umbral fueron diálogo + `ATUQ-EXIT=0` también +y no limpiaron nada (quedaron en 4 y 5). Mismo par de condiciones, resultado opuesto ⇒ «el diálogo +que se cierra limpio limpia el contador» no se sostiene. Conviene no dejar que la respuesta buena +—la de la tabla de arriba— tape esta, que es de otra pregunta. **Y la consecuencia para el instrumento es lo más caro de la serie:** con `--until-paint`, **cada corrida suma uno**, pinte o no pinte. Cuatro corridas envenenan cualquier perfil, así que una serie diff --git a/docs/state/primera-pintura.json b/docs/state/primera-pintura.json index 6531e83b..f0000831 100644 --- a/docs/state/primera-pintura.json +++ b/docs/state/primera-pintura.json @@ -1,21 +1,5 @@ { "corridas": [ - { - "medido": "2026-09-14T19:14:09Z", - "imagen": "/mnt/cosecha/takana-cosmic-qemu.img", - "segundos_primera_pintura": null, - "pantalla": null, - "pantallas_fotografiadas": 1, - "presupuesto_s": 420, - "ventana_s": 480, - "cadencia_s": 30, - "lanzamiento": "perfil del usuario", - "video": "virtio-gpu solo (-vga none)", - "mem_mb": 2560, - "vcpus": 2, - "acel": "tcg", - "guion": "scripts/cosmic/atuq-en-imagen.py" - }, { "medido": "2026-09-14T19:23:09Z", "imagen": "/mnt/cosecha/takana-cosmic-qemu.img", @@ -515,6 +499,25 @@ "recent_crashes_fijado": null, "recent_crashes_antes": 3, "recent_crashes_despues": 4 + }, + { + "medido": "2026-09-15T19:06:49Z", + "imagen": "/mnt/cosecha/takana-cosmic-qemu.img", + "segundos_primera_pintura": 120, + "pantalla": "gpu0", + "pantallas_fotografiadas": 1, + "presupuesto_s": 420, + "ventana_s": 480, + "cadencia_s": 120, + "lanzamiento": "perfil del usuario", + "video": "virtio-gpu solo (-vga none)", + "mem_mb": 2560, + "vcpus": 2, + "acel": "tcg", + "guion": "scripts/cosmic/atuq-en-imagen.py", + "recent_crashes_fijado": 0, + "recent_crashes_antes": 4, + "recent_crashes_despues": null } ] } diff --git a/scripts/cosmic/atuq-en-imagen.py b/scripts/cosmic/atuq-en-imagen.py index df58d881..2c1ee7ce 100755 --- a/scripts/cosmic/atuq-en-imagen.py +++ b/scripts/cosmic/atuq-en-imagen.py @@ -169,6 +169,21 @@ def leer_contador_de_caidas(texto): return None +def leer_sello_de_exito(texto): + """Saca `toolkit.startup.last_success` de un volcado de `prefs.js`. + + Es la OTRA MITAD del cálculo del contador de caídas: el contador sube mientras este sello no se + mueve, así que un contador que no cambió sólo deja de ser ambiguo —«no subió» contra «subió y se + limpió»— si se lee junto con el sello. Medido el 2026-09-15: pasó un día entero sin moverse. + """ + for linea in texto.splitlines(): + if "user_pref" in linea and "toolkit.startup.last_success" in linea: + m = re.search(r"last_success\"?\s*,\s*(\d+)", linea) + if m: + return int(m.group(1)) + return None + + def anotar_medicion(segundos, a, pantalla=None, extra=None): """Deja el tiempo de primera pintura en `docs/state/primera-pintura.json`. @@ -265,6 +280,12 @@ def main(): "(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("--watch-prefs", action="store_true", + help="en CADA captura lee también `recent_crashes` y `last_success` del perfil y " + "los imprime con el segundo. Es lo que mide CUÁNTO tarda un arranque en " + "darse por bueno: el valor de `last_success` es la hora de ARRANQUE, no la " + "de escritura, así que el momento en que cambia sólo se sabe mirándolo " + "seguido. Ignora `--until-paint`: lo interesante pasa DESPUÉS de pintar") 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 " @@ -416,6 +437,7 @@ def main(): "last_success)|browser.sessionstore.max_resumed' %s/prefs.js" % d, 60) print(volcado.strip()) estado["recent_crashes_antes"] = leer_contador_de_caidas(volcado) + estado["last_success_antes"] = leer_sello_de_exito(volcado) # ⚠ EL AVISO QUE FALTABA. Medido el 2026-09-15: el arranque que encuentra el # anterior sin terminar SUMA UNO y compara DESPUÉS, así que con 3 guardado esta # corrida abre el diálogo de Modo de resolución de problemas y no el navegador — @@ -542,16 +564,25 @@ def main(): medidas = {p: pintado(f) for p, f in tomas.items()} px = max((v for v in medidas.values() if v is not None), default=None) donde = next((p for p, v in medidas.items() if v and v > 100_000), None) + vigilancia = "" + if a.watch_prefs and perfil_d: + # Una sola lectura, corta, por captura. `tr` junta las dos líneas para que + # entren en el renglón del avance: lo que importa es CUÁNDO cambian. + vigilancia = " prefs: " + " ".join( + l.strip() for l in ser.cmd( + "grep -hE 'toolkit.startup.(recent_crashes|last_success)' " + "%s/prefs.js | tr -d '\"' | tr '\\n' ' '" % perfil_d, 60).splitlines() + if "toolkit.startup" in l and "grep" not in l) if primera is None and px and px > 100_000: primera = t - print(f" +{t}s ██ PRIMERA PINTURA — {px} px de la página EN {donde}", flush=True) - if a.until_paint: + print(f" +{t}s ██ PRIMERA PINTURA — {px} px de la página EN {donde}{vigilancia}", flush=True) + if a.until_paint and not a.watch_prefs: break else: vivos = ser.cmd("for c in /proc/[0-9]*/comm; do cat $c 2>/dev/null; done | grep -c atuq") v = vivos.splitlines()[1].strip() if len(vivos.splitlines()) > 1 else "?" detalle = " ".join(f"{p}={medidas[p]}" for p in PANTALLAS) - print(f" +{t}s px de la página: {detalle} procesos atuq: {v}", flush=True) + print(f" +{t}s px de la página: {detalle} procesos atuq: {v}{vigilancia}", flush=True) capturar(qmp_sock, out, "%s-despues" % fase) print("== captura DESPUES de la fase", flush=True) @@ -580,10 +611,14 @@ def main(): # 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 || " + # ⚠ `last_success` VA EN LOS DOS VOLCADOS. Es la otra mitad del cálculo, y + # sin él en el «después» una limpieza no dice si el arranque además SELLÓ el + # éxito: son dos preguntas que se contestan con el mismo grep y una salía muda. + volcado = ser.cmd("grep -E 'toolkit.startup.(recent_crashes|last_success)' %s/prefs.js || " "echo '(el pref no está: arranque sin caídas pendientes)'" % perfil_d, 60) estado["recent_crashes_despues"] = leer_contador_de_caidas(volcado) + estado["last_success_despues"] = leer_sello_de_exito(volcado) print("---- el contador de caídas, DESPUÉS ----") print(volcado.strip()) # ⚠ SÓLO LA FASE BASE SE ANOTA. La serie de `primera-pintura.json` es una TASA del