DOM+layout+strings, FUERA del corpus: 23.540 -> 21.327 ms -9,4%
SunSpider 3d-raytrace, DENTRO: 16.034 -> 15.878 ms -1,0%
Mismo rootfs, mismo script, corridas intercaladas A/B/A/B, mediana de 7,
calentamiento descartado; lo único que cambia entre variantes es un --ro-bind de
/usr/lib/firefox. Los rangos no se solapan en ninguna de las dos, así que ambas
diferencias son reales y no ruido.
Yo esperaba lo contrario: que medir sobre el conjunto de ENTRENAMIENTO inflara
la ganancia. Da el número MÁS BAJO, y la razón es mejor que la predicción:
3d-raytrace es aritmética pura en un bucle caliente, y ese bucle no lo ejecuta
el C++ de SpiderMonkey sino código máquina que el JIT genera en runtime. El PGO
optimiza el intérprete, el GC y el propio JIT — no lo que el JIT emite. Un
benchmark JIT-bound es casi ciego al PGO por construcción.
Donde se ve es en DOM/layout/arranque, que es C++ de punta a punta, y que además
es lo que el usuario percibe: la medición cubre el ciclo completo del proceso
(arrancar, renderizar, capturar, salir), no el régimen de una página cargada.
Corolario anotado: el corpus de entrenamiento está sesgado hacia JS justo donde
el PGO menos rinde. Un corpus con más maquetación probablemente daría más. Es su
propia unidad de trabajo.
«El más caro para el usuario es ffmpeg: sin él, un sitio que sirva H.264 no
reproduce» — el hueco más caro del mapa no existía. De los cuatro «sin receta»
quedan tres y ninguno es un códec.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
El §6.10 preguntó «¿qué NO puede hacer?» y contestó leyendo cadenas de sonames.
Media pregunta. Al hacer la otra mitad —¿se ve el vídeo?— salieron tres cosas:
DOS ERRORES DEL MAPA, los dos por leer una etiqueta en vez de medir:
- «no hay receta de ffmpeg» era falso: la hay, sellada, y ya viajaba en la
clausura de los cuatro escritorios por `mpv`. Lo que faltaba era la lista de
raíces del runner.
- «VA-API sin receta» también: `libva` está y está en las cuatro imágenes. El
hueco es real pero es el DRIVER — las tres mesa van `-Dgallium-va=disabled`.
Y queda anotado que ahora el mapa ya no lo puede ver, porque la librería
presente silencia el aviso sin encender la función.
UN BUG REAL: AV1 no reproducía. Gecko se come el primer elemento de
`LD_LIBRARY_PATH` al lanzar el RDD ⇒ sin appdir ⇒ ffvpx no carga ⇒ se cae al
ffmpeg del sistema, que no trae AV1 por software. Sin error, sin NEEDED
faltante, sin cadena ausente: `readyState=1` para siempre.
Y lo que quedó MEDIDO, por el lanzador de verdad y headless: H.264+AAC, VP9+Opus,
AV1, MP3 y FLAC reproducen, con el tiempo avanzando y no con «se creó el
decodificador» — que en AV1 se creaba igual y no entregaba un cuadro.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
La familia `libavcodec.so.*` estaba clasificada como hueco con el motivo «sin
receta de ffmpeg en el corpus». `recipes/ffmpeg.toml` existe, está sellada,
publica `libavcodec.so.61` —uno de los once sonames que sondea libxul— y ya
viajaba en la clausura de los cuatro escritorios arrastrada por `mpv`. Pasa a
ruido: lo que falta son las OTRAS versiones del soname, y Firefox recorre la
lista hasta que una carga.
`libva` igual: la receta está y ahora también en el rootfs del runner. Sigue
siendo hueco, pero por la otra mitad —las tres mesa van con `-Dgallium-va=disabled`
y `-Dvideo-codecs=` vacío, así que no hay un solo `*_drv_video.so` que cargar—,
y el motivo ahora lo dice.
Con eso el mapa pasa de 47 cadenas sin proveedor a 7 huecos reales.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
Gecko SE COME EL PRIMER ELEMENTO de `LD_LIBRARY_PATH` al lanzar el proceso RDD
—el que decodifica vídeo—. El lanzador ponía `/usr/lib/atuq` una sola vez, o sea
primero, así que el RDD arrancaba sin él, no encontraba `libmozavcodec.so` /
`libmozavutil.so` (el ffvpx bundleado, donde vive dav1d) y anotaba:
PlatformDecoderModule FFVPX: Link result: NoProvidedLib
El navegador NO falla ahí: se cae al ffmpeg del sistema, que cubre
H.264/AAC/VP8/VP9/MP3/FLAC/Opus. Pero AV1 por software NO lo cubre —el
decodificador `av1` de ffmpeg es sólo hwaccel y nuestra receta va sin dav1d—, así
que un vídeo AV1 se quedaba en `readyState=1` para siempre, sin un error, sin un
NEEDED faltante y sin una cadena ausente. Media función apagada en silencio, que
es la forma de fallo de esta casa.
Tres corridas que sólo cambian esa variable, con el mismo artefacto:
LD=/usr/lib/atuq:/usr/lib:/lib → RDD NoProvidedLib AV1 ✗
LD=/usr/lib/atuq:/usr/lib/atuq:/usr/lib → RDD Success AV1 ✓
LD=/relleno:/usr/lib/atuq:/usr/lib → RDD Success AV1 ✓
El arreglo es repetir el appdir. Feo y correcto mientras no haya `patchelf` en el
corpus para grabar `RUNPATH=$ORIGIN`, que borraría la variable entera.
Y el guardián que sale de este punto ciego, `scripts/test-atuq-codecs.sh`: abre
cinco muestras versionadas en `scripts/fixtures/codecs/` y mira si `currentTime`
AVANZA — «se creó el decodificador» no es «decodifica». El veredicto sale por
`dump()` al stdout, así que no necesita ni red ni servidor, y corre headless para
que sirva en el worker. Con `--negative-control` se saltea el lanzador y EXIGE
que AV1 falle: probado en los dos sentidos, 5/5 y control ✓.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
Tres cosas en el mismo fichero, las tres medidas hoy:
1. `ffmpeg` y `libva` entran a las raíces. NO son recetas nuevas: las dos están
selladas en el corpus y ya viven en la clausura de los cuatro escritorios,
arrastradas por `mpv` (verificado con `yupana.membresia`, no leyendo el TOML).
Faltaban acá, o sea que el runner abría un rootfs sin libavcodec y desde ahí
se concluía que la imagen no tiene códecs. El instrumento otra vez, no el
artefacto.
2. El bucle de hidratación salía 1 SIEMPRE: `$VIGENTES` termina en `\n`, `echo`
agrega otro, la última vuelta lee la línea vacía, `[ -n "$d" ]` da 1 y con
`set -e` el script moría sin imprimir una sola línea, justo después de
hidratar bien las 41 raíces. Ahora `continue` en la vacía y un `hydrate` que
falla grita.
3. La comparación del sello NUNCA daba igual: el lado izquierdo lleva su `\n`
final y `$(cat …)` lo recorta ⇒ «DESACTUALIZADO» en cada corrida y 3013
ficheros rehidratados de más. Los dos lados pasan ahora por la misma
sustitución de comandos.
Y `ROOTFS_ONLY=1`, que corta después de hidratar: lo necesita el guardián de
códecs, que corre headless y tiene que usar ESTA lista de raíces y no una copia.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NNFzrqFBEt6W7maGJKf2Xs
El corpus queda entero tras la jornada: los dos únicos no sellados son `ajeno`
(steam-runtime-sniper, xwayland), que son frontera y no deuda.
atuq reconstruido sobre el firefox con PGO (b3:0acf6f58): 340 M, libxul de
227.802.816 bytes — el mismo del motor. La tesis del derivado sostenida: el
navegador propio se rehace en segundos sobre un motor nuevo.
Verificado que arranca desde una hidratación limpia de escritorio-sway, 0
errores de relocación. Y verificado que el PGO viaja EN EL ARTEFACTO y no sólo
en el configure: buildconfig.html dentro del omni.ja de atuq contiene
`profile-use`, junto a `wasi-sysroot` y `lto=cross`.
Nota de método: la CAPTURA no servía para esto. Salió byte a byte idéntica a la
de ayer porque la sección «Configure options» cae bajo el viewport — o sea que
una imagen igual no probaba que nada hubiera cambiado. El artefacto sí.
verificar-repro.sh: REPRODUCEN 1, DERIVA 0, NO-DETERMINISMO 0.
Era el riesgo real de esta unidad, no el rendimiento. El perfil es no
determinista por naturaleza; congelarlo como fuente pineada era el ARGUMENTO de
por qué el build seguiría siendo reproducible, y esto es la MEDICIÓN. Si
-fprofile-use hubiera metido cualquier decisión dependiente del orden o del
timing, habríamos cambiado velocidad por el invariante del proyecto.
El §3.ter daba dos muros —el display y el no-determinismo del perfil— y los dos
eran ciertos. Aparecieron tres más, ninguno visible sin construir:
1. libclang_rt.profile.a no existía: el lab trae clang pero NINGUNA runtime de
compiler-rt. El instrumentado murió en el minuto 38:56.
2. Los sandboxes de Firefox matan la corrida de perfilado con signal 11: el
proceso que renderiza no nace, 0 GET, y el .profraw que queda es el del
padre arrancando. Un perfil de nada, con todo en verde.
3. El worker no puede bajar del mirror: tiene mirror-env.sh pero no la clave.
Y queda escrito cómo se verificó que el PGO llegó, que NO pudo ser como RLBox.
Ahí el artefacto delata la jaula (símbolos w2c_*, cero -> 634). Acá el
discriminante análogo —las secciones .text.hot que clang emite al particionar
por temperatura— NO SIRVE: con lld y ThinLTO el enlazador las fusiona y dan cero
con PGO y sin él. La evidencia válida está un nivel bajo el configure: 189
invocaciones del compilador con -fprofile-use, llvm-profdata encontrado, cero
avisos de perfil que no cuadra, y libxul creciendo 1,7 MB.
Es evidencia de BUILD y no de ARTEFACTO, y conviene decirlo así en vez de
presentarla como si fuera lo mismo.
merged.profdata: 501.521 funciones, 3.734.991 bloques, 5.885.254.799 ejecuciones
registradas. Recogido corriendo firefox-instrumentado sobre el corpus de
entrenamiento de Mozilla (build/pgo: blueprint para maquetación, js-input y
sunspider para el motor JS), 35 de 36 páginas servidas.
VIVE COMO FUENTE PINEADA, NO COMO RECETA QUE LO GENERA. El perfil NO es
determinista —los contadores dependen del timing—, así que una receta que lo
produjera tendría un ArtifactHash estable sobre bytes cambiantes: firefox
consumiría cosas distintas en la misma dirección y dejaría de reproducir sin que
nada lo dijera. Es el modo de fallo del lab fuera de hash_inputs, fabricado a
propósito. Sellarlo una vez y pinearlo por sha256 hace que el insumo sea no
determinista y el build vuelva a serlo.
El blob (17.426.112 bytes, xz) está publicado en hammer/fuentes/<sha256>.tar del
Storage Box y verificado bajándolo de vuelta por el mismo camino que usa hammer.
La URL upstream no resuelve por DNS a propósito: es el patrón que el ADR 0013 ya
prueba, y como la URL nunca estuvo en hash_inputs, mover el objeto de origen no
re-hashea nada.
Dos cosas que costaron y quedan escritas:
· strip_components = 0. hammer recorta un componente al extraer (los releases
GNU traen un proyecto-version/ de más) y este tarball lleva el fichero en la
raíz, así que el default se llevaba lo único que había. El error era
«cp: cannot stat merged.profdata» y no menciona el recorte por ningún lado.
· SIN --with-pgo-jarlog, y es una mitad que falta, no un olvido. Alpine pasa
además un jarlog que reordena omni.ja para acelerar el ARRANQUE; lo emite el
profileserver.py de Mozilla con su extensión Quitter, que la corrida headless
no tiene. Hoy se gana la disposición de CÓDIGO y no el orden del omni.ja.
Y VIAJA UN ARREGLO QUE NO ES DE PGO, porque firefox se reconstruye igual: EL
ARTEFACTO NO ARRANCABA. Medido sobre el sellado: `firefox --version` moría con
«Error loading shared library libnspr4.so / Couldn't load XPCOM». mach install
deja /usr/bin/firefox como symlink pelado, el binario no trae RUNPATH y el
cierre no publica ld-musl-x86_64.path, así que las librerías que viven junto al
binario no las encuentra nadie. atuq andaba sólo porque su receta añade este
mismo lanzador; firefox, que va en las CUATRO imágenes de escritorio, no lo
tenía. Es el NEEDED colgante un escalón más allá —no falta la librería, falta el
modo de encontrarla— y por eso vigia-sonames.py no puede verlo: las librerías SÍ
están en el artefacto y las cuenta como propias.
firefox: b3:8116bdec -> b3:1d730334.
Ayer escribí que tres de los siete huecos eran «promoción y no autoría» porque
libsecret, libcanberra y vulkan-loader ya tienen receta sellada en colas
incoming. Fui a declararlas en los perfiles y lo comprobé antes: en los TRES
casos falta la otra mitad.
vulkan-loader las TRES mesa construyen con -Dvulkan-drivers= VACÍO
-> un cargador sin un solo ICD no es WebGPU
libsecret no hay receta de gnome-keyring ni de ningún servicio de
secretos -> firefox hablaría a org.freedesktop.secrets y no
habría nadie
libcanberra no hay receta de sound-theme-freedesktop -> carga y no suena
Declararlas habría subido el número de paquetes de la imagen sin encender una
sola función, que es la peor clase de verde. Una librería es MEDIA función; la
otra mitad es quien la atiende.
La misma vara sobre libnotify, que sí declaré ayer, medida perfil por perfil:
plasma-workspace en kde, gnome-shell en gnome, cosmic-notifications en cosmic —
completa en tres— y NADIE en sway. Ahí queda a medias y ahora está escrito cuál
es.
Y una trampa anotada para quien vaya a cerrarla: el nombre `mako` YA ESTÁ OCUPADO
en el corpus por el motor de plantillas de Python que usa Mesa para su codegen.
No es el daemon de wlroots. Escribir recipes/mako.toml para el daemon pisaría una
receta viva de la cadena de mesa — la colisión de homónimos que la memoria del
proyecto ya tiene anotada. Me lo comí yo: fui a ver si `mako` estaba sellado, dijo
que sí, y por poco lo cuento como «el daemon ya está».
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgNtJEFetMYbXax5dVUjZZ
Sin esta línea la receta queda sellada y en NINGUNA imagen — la lección de `foot`
que este mismo fichero tiene escrita cuatro renglones más arriba de `atuq`, y que
la clausura no puede ver porque mide lo declarado.
Va en los cuatro perfiles que ya llevan el navegador (kde, gnome, cosmic, sway) y
NO en base/cli/mirada, que no lo llevan. Verificado parseando el TOML y no
leyéndolo: libnotify=1 en esos cuatro, 0 en los otros tres.
Medición al lado, con el vigía de sonames antes y después:
kde 306 -> 307 nodos 2083 -> 2086 sonames 0 sin proveedor
gnome 190 -> 191 657 -> 660 0
cosmic 161 -> 162 498 -> 501 0
sway 201 -> 202 509 -> 512 0
mirada 41 -> 41 231 -> 231 0 (no lleva atuq)
+1 nodo exacto por perfil, que es lo que se agregó, y el que no lo lleva no se
movió. Si hubiera arrastrado algo sin querer, el delta no sería 1.
Y una comprobación que el mapa de §6.10 no da y que es la que decide si el
`dlopen` va a funcionar: que resuelvan las deps de la PROPIA librería. Un dlopen
falla en silencio igual si libnotify está pero su gdk-pixbuf no. Se lo pregunté
al loader musl, no a mí:
ld-musl --list /usr/lib/libnotify.so.4
libgdk_pixbuf-2.0.so.0 => /usr/lib/... libgobject-2.0.so.0 => /usr/lib/...
libglib-2.0.so.0 => /usr/lib/... libgio-2.0.so.0 => /usr/lib/...
libc.so => /lib/ld-musl-x86_64.so.1
Toda la cadena cae dentro del rootfs salvo libc.so, que es la excepción del lab
ya documentada.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgNtJEFetMYbXax5dVUjZZ
`libxul.so` lleva la cadena `libnotify.so.4` adentro y la abre por dlopen cuando
una página pide permiso para notificar. Nadie en el corpus la proveía, así que la
función quedaba apagada SIN UN SOLO MENSAJE: el navegador arranca, la web pide
notificaciones, y no pasa nada. No lo veía ninguna herramienta porque no hay
NEEDED en ningún ELF — es el tercer escalón, y sobrevivió a vigia-sonames con los
cinco perfiles en CERO.
Receta nueva, 0.8.8, SÓLO en variante compartida y eso no es un olvido: a un
`dlopen("libnotify.so.4")` una `.a` no le sirve de nada, así que una receta
estática sería un artefacto que nadie puede consumir.
Dos cosas que la receta se comió y quedan escritas:
1. `libpng-shared` hace falta porque las deps de hammer NO son transitivas: el
`.pc` de gdk-pixbuf-2.0 declara `Requires: libpng` y el meson muere con un
mensaje que nombra a gdk-pixbuf —que sí está— en vez de a lo que falta.
2. El guardián informaba «18 bytes» y parecía una librería vacía: `stat -c%s` no
sigue el symlink, y `libnotify.so.4` apunta a `libnotify.so.4.0.0`. El
artefacto estaba bien y el MENSAJE mentía. Con `-L` son 162.928 bytes. Se
arregla el mensaje porque es lo que alguien va a leer a las tres de la mañana,
y de paso el guardián exige el fichero real, no sólo el nombre.
⚠ Y lo que NO arregla, escrito en la receta para que nadie lea de más: libnotify
no trae daemon, manda org.freedesktop.Notifications por D-Bus. Tenerla resuelve
la mitad —que firefox la encuentre—; la otra mitad es que en la imagen haya
alguien escuchando ese nombre.
Verificado con el propio mapa: `--dlopen` pasa de 25 huecos a 24 y libnotify.so.4
desaparece; el soname viejo `.so.1` se reclasifica de «hueco» a «ruido», que es lo
que ahora es. El guardián normal sigue en CERO con un soname más pedido (66).
Queda pendiente la membresía de perfil en `docs/state/targets.toml`, que es
catálogo compartido y no lo toco sin decidirlo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgNtJEFetMYbXax5dVUjZZ
hammer-9f nombró el punto ciego que ni su vigía ni mi guardián podían cubrir:
una librería que sólo aparece como CADENA LITERAL dentro de un dlopen(). No hay
NEEDED en ningún ELF, así que ningún auditor de readelf la encuentra. La
jerarquía queda:
NEEDED del ejecutable -> lo vemos los dos
NEEDED de un .so dlopeado -> lo vemos los dos
dlopen("libfoo.so.1") literal -> NO LO VE NINGUNO
Y un navegador vive de eso. Firefox sondea ffmpeg, VA-API, vulkan y libnotify
por nombre, y cuando no están NO FALLA: apaga la función y sigue. No hay línea
roja; hay una función que nadie ofrece y nadie reclama. Es la forma que ya costó
caro con OBS y su dlopen("libGL.so.1").
`--dlopen` busca esas cadenas y las cruza contra el rootfs. Es un HEURÍSTICO y se
declara como tal —una cadena no prueba un dlopen y su ausencia no prueba que no
lo haya—, así que no falla nunca: imprime un mapa triado. Lo afirmable es lo
contrario, que es lo útil: si la cadena está y el fichero no, esa función no
existe en esta imagen.
De 85 cadenas, 47 sin proveedor. Siete son huecos de verdad:
códecs del sistema (H.264/AAC) sin receta de ffmpeg — el más caro
notificaciones web sin receta
llavero (libsecret) RECETA YA EXISTE en incoming-gnome
sonidos (libcanberra) receta en incoming-kde
WebGPU (vulkan-loader) receta en incoming-kde
vídeo por hardware (VA-API) sin receta
lectura en voz alta sin receta
Tres de los siete son promoción, no autoría. Ocho son decisiones ya tomadas
(libGL por Wayland-only sin GLX; libcurl porque sólo lo usa el pingsender de
telemetría) y siete son ruido de musl o sonames viejos. El triaje va en una tabla
del propio script, no escondido en un `if`, porque es criterio y no medición: ahí
se puede discutir.
Sin triar: 0. Si aparece una cadena nueva, el informe la marca «SIN TRIAR» en vez
de tragársela.
Lo que esto cambia de fondo: hasta hoy la pregunta era «¿arranca?» y la respuesta
era sí. La que faltaba era «¿y qué NO puede hacer?», que ninguna métrica del repo
respondía porque todas miran presencia y ésta mira ausencia declarada por el
propio binario.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgNtJEFetMYbXax5dVUjZZ
La lista de raíces de `atuq-nested.sh` se mantiene A MANO y la jaula monta
`.dev-fs/alpine` como capa de abajo. Esas dos cosas juntas hacen que una raíz que
falte NO se note: el navegador arranca igual, resolviendo contra el lab. Ayer eso
costó `gcc-libs`. La regla del repo es que cada punto ciego se convierte en un
guardián, así que acá está el guardián en vez del parche.
`scripts/test-atuq-rootfs.py` mira el objeto que la jaula monta de verdad —el
directorio HIDRATADO— y no el grafo de recetas, que es lo que ya cubre
`vigia-sonames.py`. Son preguntas distintas: la lista del runner no sale del
grafo, así que el grafo no puede auditarla.
Lo primero que hizo fue encontrar SIETE sonames más que se estaban resolviendo
contra el lab, y no son cosmética:
libexpat.so.1, libzstd.so.1 <- los pide mesa (iris_dri, libEGL, libgbm)
libdbus-1.so.3 <- lo pide pipewire; lo trae `dbus-shared`, no `dbus`
libbz2.so.1 <- freetype
libudev.so.1 <- libspa-alsa
libsndfile.so.1, libncursesw.so.6
Los siete tienen proveedor en el corpus. Agregados a las raíces: el rootfs pasa
de 7 huecos a CERO, y la única excepción que queda es `libc.so`, que va en una
lista explícita porque ningún artefacto lo provee — las imágenes lo copian del
devfs. Si algún día hay receta que lo provea, esa lista se achica y el guardián
se vuelve más estricto solo.
CONTROL NEGATIVO incluido, que sin él esto no probaría nada:
`--negative-control` esconde libstdc++.so.6 y exige que el guardián lo cace.
Corrido: lo caza, y nombra a quién lo pide (atuq, atuq-bin).
Y `scripts/test-atuq-ruteo.py` vuelve a pasar entero contra el rootfs completo,
o sea que el ruteo por contenedor está probado ahora sobre un rootfs que no le
pide nada al lab salvo el intérprete.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgNtJEFetMYbXax5dVUjZZ
Siete recetas decían «lo vigila `scripts/audit-needed.sh`» y ese script ya no
existe: lo borré yo mismo en 02facebe, bien borrado, porque duplicaba a
`scripts/vigia-sonames.py` — que además ya estaba en el repo y ahora corre en el
latido. Pero las referencias quedaron, escritas por mí unas horas antes.
Es exactamente la etiqueta que se lee como el hecho, y en mi propia letra: un
comentario que afirma que existe un guardián manda a quien dude de una dep de
runtime a correr un script que no está. En el mejor caso pierde diez minutos; en
el peor concluye que no hay con qué comprobarlo.
Ahora apuntan a `vigia-sonames.py` y a `docs/state/sonames.txt`, que es el
fichero que el latido regenera y donde se ve si algo empeora entre cosechas.
Ningún ArtifactHash se mueve: los siete comentarios caen fuera de campos
hasheados. Verificado uno por uno antes y después, incluido
firefox-instrumentado, que tiene un build en vuelo y se habría invalidado.
Lo levantó hammer-99 desde el frente atuq. `recipes/atuq.toml` NO se toca: su
mención es una nota histórica ya corregida por ellos («…y NO audit-needed.sh»),
y mi primer grep casi la «arregla» por no leerla.
Y la advertencia que traían vale para cualquiera con pruebas de runtime: si el
runner monta `.dev-fs/alpine` debajo, `libstdc++.so.6` se resuelve contra el LAB
y la prueba es ciega. Comprobado que la mía NO lo estaba: el rootfs hidratado
trae el del corpus (20.850.968 bytes) y no el del lab (2.804.104), y el bwrap de
la prueba nunca montó .dev-fs.
`atuq` declara `NEEDED libstdc++.so.6` y `libgcc_s.so.1` —lo hereda de firefox,
que va con clang++ y la libstdc++ COMPARTIDA— y su clausura ya estaba bien:
`recipes/atuq.toml` declara `runtime = ["gcc-libs"]` desde 5b9777bd, yupana lee
`deps.runtime` desde 02facebe, y el vigía de sonames da CERO en los cinco
perfiles (rehecho hoy: 41/306/190/161/201 nodos, 0 sin proveedor).
Lo que estaba mal era el INSTRUMENTO. `atuq-nested.sh` no hidrataba `gcc-libs`, y
como la jaula monta `.dev-fs/alpine` como capa de abajo, cada «atuq corre» de
estos días resolvió libstdc++ contra el LAB. Corría, sí: en una máquina con
Alpine debajo, que es exactamente lo que la distro no es. El artefacto estaba
sano y la prueba era ciega — que es peor, porque una prueba ciega dice que sí.
Los dos ficheros ni se parecen, así que la duda se resuelve mirando:
corpus 20.850.968 bytes
lab 2.804.104 bytes
Con `gcc-libs` en las raíces, dentro de la jaula se ve el de 20 MB, y
`scripts/test-atuq-ruteo.py` vuelve a pasar entero — o sea que el ruteo por
contenedor está probado ahora contra la libstdc++ del corpus y no contra la del
lab. Queda una dependencia del lab que NO es de esta prueba y está por diseño: el
intérprete `/lib/ld-musl-x86_64.so.1`, que ningún artefacto provee y que las
imágenes copian del devfs (`scripts/mirada-usb.sh`).
De paso, un puntero muerto: las recetas dicen «lo vigila scripts/audit-needed.sh»
y ese script lo BORRÓ 02facebe al reemplazarlo por el vigía de sonames. Corregido
en atuq.toml. Quedan otras siete recetas apuntándole (firefox, waterfox,
gcc-libs, mesa-llvmpipe, librsvg, spidermonkey, firefox-instrumentado) y no se
tocan desde acá porque son de otro frente.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UgNtJEFetMYbXax5dVUjZZ
libclang_rt.profile.a, 129.178 bytes — la que implementa los __llvm_profile_* y
escribe los .profraw. Hermana nativa de wasi-compiler-rt: mismo tarball de LLVM
22.1.8, mismo patrón, mismo sitio de aterrizaje; cambia el target (x86_64 en vez
de wasm32) y el componente (profile en vez de builtins).
Lo destapó el build del firefox instrumentado, en el minuto 38:56:
ld.lld: error: cannot open
/usr/lib/llvm22/lib/clang/22/lib/x86_64-alpine-linux-musl/libclang_rt.profile.a
El lab trae clang 22.1.8 con su include/ pero el lib/ del resource dir NO EXISTE
— que es exactamente lo que wasi-compiler-rt ya documentaba para el caso wasm.
O sea: el PGO no estaba bloqueado por el display (eso se despejó ayer) sino por
una pieza de toolchain que nunca hizo falta hasta ahora.
Se apaga todo menos profile: compiler-rt trae sanitizers, xray, memprof, orc y
ninguno tiene destinatario acá.
La primera corrida murió en el configure con «CMAKE_C_COMPILER_TARGET must also
be set when COMPILER_RT_DEFAULT_TARGET_ONLY is ON» — un error de cmake que dice
exactamente qué falta, cosa que no se puede dar por supuesta esta noche.
Dos guardianes. El primero exige la RUTA LITERAL del error y no «alguna
libclang_rt»: el layout per-target es una opción de cmake y equivocarla deja la
librería donde clang no mira, que es el mismo fallo que la libstdc++ en
/usr/lib64 de hace unas horas. El segundo es la prueba del consumidor: que
DEFINA __llvm_profile_write_file y compañía, para que un .a vacío no se descubra
al final de una corrida de perfilado.