From 03bdd41b4acfc121c9d5ef12d3343b866c6d8f6a Mon Sep 17 00:00:00 2001 From: sergio Date: Wed, 29 Jul 2026 11:09:54 -0400 Subject: [PATCH] =?UTF-8?q?gnome:=20el=20muro=20de=20input=20NO=20era=20li?= =?UTF-8?q?budev-zero=20=E2=80=94=20el=20fd=20de=20TakeDevice=20era=20bloq?= =?UTF-8?q?ueante?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tres cambios de diagnóstico en `gnome-start` que, juntos, dieron el veredicto: 1. SONDA DE INPUT antes de lanzar el shell. `libinput list-devices` hace lo mismo que `init_libinput()` de mutter (udev_new + create_context + assign_seat) pero abriendo el devnode por su cuenta. Volvió con rc=0 y los tres dispositivos listados ⇒ **la hipótesis libudev-zero del runbook queda REFUTADA**: la enumeración por /sys funciona. 2. El volcado POR HILO corría DESPUÉS del `kill -ABRT`, o sea sobre un /proc//task que ya no existía: salía vacío. Movido antes. Ahí apareció el dato: `tid 201 [Mutter Input Th] wchan=evdev_read syscall=0`. El hilo de input dormido en un `read()` de evdev dentro del kernel. (El core no servía: gdb no desenrolla a través de musl y devuelve `?? ()` para los 11 hilos no principales.) 3. `MUTTER_DEBUG` es una LISTA DE TÓPICOS (g_parse_debug_string sobre meta_debug_keys), no un booleano: con `1` no encendía nada. Ahora `backend,input,kms`, y el tópico `backend` imprime la última línea antes del cuelgue: «Opening and taking control of device file '/dev/input/event0'». Más: ping D-Bus a login1 en el diagnóstico (el daemon respondía: no estaba tildado) y copia de los logs de /tmp —que es tmpfs— a la raíz ext4 para poder sacarlos con debugfs junto al core. La causa está arreglada en tawasuyu (3dd88f582): `TakeDevice` abría sin `O_NONBLOCK`. Receta re-pineada a 2445fa31c y re-sellada b3:7ffa9256. Co-Authored-By: Claude Opus 5 (1M context) --- recipes/arje-logind-compat.toml | 2 +- scripts/gnome/gnome-start-qemu.sh | 80 +++++++++++++++++++++++++++---- 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/recipes/arje-logind-compat.toml b/recipes/arje-logind-compat.toml index 1c3567ad..95a1f148 100644 --- a/recipes/arje-logind-compat.toml +++ b/recipes/arje-logind-compat.toml @@ -29,7 +29,7 @@ version = "0.0.1" [source] # El `commit` es el identificador inmutable (la URL es sólo locator, no entra al hash). repo = "gitea@git.tawasuyu.net:tawasuyu/tawasuyu.git" -commit = "e65e943ef8f6fa77ea9b8565f5d73d9f511ab0df" +commit = "2445fa31c1b3d8b7da037d2434ace976e2444df4" # El monorepo COMMITEA su propio `vendor/` — `[patch.crates-io] smithay = { path = "vendor/smithay" }`, # una copia parcheada de smithay que mirada necesita para el tearing. `cargo vendor` de hammer escribe # en `vendor/` por defecto y lo pisaba: el build moría con diff --git a/scripts/gnome/gnome-start-qemu.sh b/scripts/gnome/gnome-start-qemu.sh index 4102caca..5c0ba981 100755 --- a/scripts/gnome/gnome-start-qemu.sh +++ b/scripts/gnome/gnome-start-qemu.sh @@ -17,6 +17,12 @@ export PATH=/usr/bin:/bin:/usr/sbin:/sbin [ -r /etc/gnome-mode ] && . /etc/gnome-mode say() { echo "== gnome-qemu :: $*" | tee -a /dev/console /dev/ttyS0 2>/dev/null; } dump() { echo "---- $1 ----" > /dev/ttyS0 2>/dev/null; cat "$1" > /dev/ttyS0 2>/dev/null; } +# OJO con el test de vivo: `kill -0` TIENE ÉXITO sobre un ZOMBI (el hijo existe como PID hasta que +# el padre lo cosecha, y acá el padre es este script, que sólo hace wait al final). La primera +# versión usaba kill -0 y por eso reportó "sin wayland-0 tras 45s" cuando en realidad gnome-shell ya +# había muerto en el primer segundo: el diagnóstico decía "0 hilos" porque no quedaba proceso, sólo +# la entrada zombi. Se mira el State de /proc en su lugar. +alive() { s=$(sed -n 's/^State:[[:space:]]*//p' /proc/$1/status 2>/dev/null); case "$s" in ""|Z*) return 1;; *) return 0;; esac; } export LD_LIBRARY_PATH=/usr/lib:/lib:/usr/lib/gnome-shell:/usr/lib/mutter-16 # GI_TYPELIB_PATH es EL env crítico de esta sesión, y el que no tiene análogo en KDE: gnome-shell es @@ -63,7 +69,11 @@ export MUTTER_DEBUG_SEND_KMS_MODIFIERS=0 if [ "${DIAG:-1}" = 1 ]; then export G_MESSAGES_DEBUG=all - export MUTTER_DEBUG=1 + # MUTTER_DEBUG es una LISTA DE TÓPICOS (g_parse_debug_string sobre meta_debug_keys, core/util.c:42), + # no un booleano: con `1` no matcheaba ninguna clave y no encendía nada. `backend` es el que + # imprime «Opening and taking control of device file ''» por cada dispositivo que pasa por + # MetaDevicePool ⇒ dice EXACTAMENTE en cuál se queda parada la hebra de input. + export MUTTER_DEBUG=backend,input,kms export EGL_LOG_LEVEL=debug export LIBGL_DEBUG=verbose fi @@ -179,6 +189,35 @@ if [ "${DIAG:-1}" = 1 ]; then fi say "typelibs visibles: $(ls /usr/lib/girepository-1.0/*.typelib 2>/dev/null | wc -l) sistema + $(ls /usr/lib/gnome-shell/*.typelib 2>/dev/null | wc -l) del shell" + +# ── SONDA DE INPUT — barata, y separa las dos hipótesis del muro ──────────────────────────────── +# El muro es la hebra de input de mutter: arranca y nunca señala `input_thread_initialized`. +# `libinput list-devices` hace EXACTAMENTE lo que hace `init_libinput()` —udev_new + +# libinput_udev_create_context + libinput_udev_assign_seat("seat0")— pero con `open()/close()` +# pelados en vez del `TakeDevice` de logind. O sea que discrimina: +# · la sonda cuelga o no lista nada ⇒ el muro es libudev-zero/libinput (enumeración por /sys) +# · la sonda lista los dispositivos ⇒ libinput anda; el cuelgue está del lado de mutter +# (MetaDevicePool → TakeDevice, o algo anterior en el hilo: input settings, keymap) +# Ojo: `libinput` es un multiplexor de subcomandos y `list-devices` NO es interactivo, pero igual se +# le pone reloj — si el problema es un cuelgue, la sonda cuelga también, y hay que matarla. +say "sonda: /sys/class/input = $(ls /sys/class/input 2>/dev/null | tr '\n' ' ')" +say "sonda: /dev/input = $(ls /dev/input 2>/dev/null | tr '\n' ' ')" +if [ -x /usr/bin/libinput ]; then + ( libinput list-devices >/tmp/libinput-devices.log 2>&1; echo "rc=$?" >>/tmp/libinput-devices.log ) & + LI_PID=$! + i=0 + while [ $i -lt 24 ] && alive $LI_PID; do i=$((i+1)); sleep 0.5; done + if alive $LI_PID; then + say "!! SONDA COLGADA: 'libinput list-devices' no volvió en 12s ⇒ el muro ES libinput/libudev-zero" + say " wchan=$(cat /proc/$LI_PID/wchan 2>/dev/null) syscall=$(cat /proc/$LI_PID/syscall 2>/dev/null)" + kill -9 $LI_PID 2>/dev/null + else + say "sonda 'libinput list-devices' VOLVIÓ (no cuelga) — dispositivos:" + dump /tmp/libinput-devices.log + fi +else + say "sonda: no hay /usr/bin/libinput en la imagen" +fi # GNOME_MODE=headless — EXPERIMENTO DECISIVO, no un modo de producción. El backend headless de # mutter crea el seat con META_SEAT_NATIVE_FLAG_NO_LIBINPUT, o sea que SALTEA `init_libinput()`, # que es justo el último paso del hilo de input antes de señalar `input_thread_initialized` @@ -194,12 +233,6 @@ SHELL_PID=$! # stream EN VIVO al serial (busybox sed no soporta -u ⇒ tail -f directo, sin pipes). ( echo "==== gnome-shell.log (stream vivo) ===="; tail -f /tmp/gnome-shell.log ) > /dev/ttyS0 2>/dev/null & -# OJO con el test de vivo: `kill -0` TIENE ÉXITO sobre un ZOMBI (el hijo existe como PID hasta que -# el padre lo cosecha, y acá el padre es este script, que sólo hace wait al final). La primera -# versión usaba kill -0 y por eso reportó "sin wayland-0 tras 45s" cuando en realidad gnome-shell ya -# había muerto en el primer segundo: el diagnóstico decía "0 hilos" porque no quedaba proceso, sólo -# la entrada zombi. Se mira el State de /proc en su lugar. -alive() { s=$(sed -n 's/^State:[[:space:]]*//p' /proc/$1/status 2>/dev/null); case "$s" in ""|Z*) return 1;; *) return 0;; esac; } i=0 while [ $i -lt 240 ]; do [ -S "$XDG_RUNTIME_DIR/wayland-0" ] && break @@ -222,15 +255,42 @@ else say "syscall: $(cat /proc/$SHELL_PID/syscall 2>/dev/null)" say "fds abiertos:"; ls -l /proc/$SHELL_PID/fd 2>/dev/null > /dev/ttyS0 say "hilos: $(ls /proc/$SHELL_PID/task 2>/dev/null | wc -l)" + # POR HILO, y ANTES del SIGABRT. La primera versión hacía este bucle DESPUÉS de abortar, o sea + # sobre un /proc//task que ya no existía: salía vacío y no dijo nada. Es el dato más barato + # que hay para ubicar la «Mutter Input Thread», porque el core no sirve: gdb no puede desenrollar + # a través de musl (sin CFI) y devuelve `?? ()` para los 11 hilos que no son el principal. + say "--- por hilo (comm / wchan / syscall) ---" + for t in $(ls /proc/$SHELL_PID/task 2>/dev/null); do + say " tid $t [$(cat /proc/$SHELL_PID/task/$t/comm 2>/dev/null)] wchan=$(cat /proc/$SHELL_PID/task/$t/wchan 2>/dev/null) syscall=$(cut -d' ' -f1 /proc/$SHELL_PID/task/$t/syscall 2>/dev/null)" + done + # ¿Sigue vivo y respondiendo el login1? Si el shell está esperando una respuesta D-Bus que no + # llega, esto lo separa en dos: daemon muerto/colgado vs daemon vivo que ya contestó. + say "arje-logind-compat: pids=$(pgrep -f arje-logind-compat 2>/dev/null | tr '\n' ' ')" + for p in $(pgrep -f arje-logind-compat 2>/dev/null); do + say " logind pid $p state=$(sed -n 's/^State:[[:space:]]*//p' /proc/$p/status 2>/dev/null) wchan=$(cat /proc/$p/wchan 2>/dev/null)" + done + ( dbus-send --system --print-reply --dest=org.freedesktop.login1 \ + /org/freedesktop/login1 org.freedesktop.DBus.Properties.Get \ + string:org.freedesktop.login1.Manager string:NAutoVTs >/tmp/login1-ping.log 2>&1 + echo "rc=$?" >>/tmp/login1-ping.log ) & + PING_PID=$! + i=0; while [ $i -lt 16 ] && alive $PING_PID; do i=$((i+1)); sleep 0.5; done + if alive $PING_PID; then + say "!! login1 NO responde (ping D-Bus colgado 8s) ⇒ el daemon se quedó tildado" + kill -9 $PING_PID 2>/dev/null + else + say "login1 responde al ping D-Bus:"; dump /tmp/login1-ping.log + fi + # /tmp es tmpfs ⇒ el log del daemon se pierde con la VM. Copiarlo a la raíz ext4, que sí se + # sincroniza y se puede sacar con debugfs junto al core. + cp /tmp/logind-compat.log /logind-compat.log 2>/dev/null || true + cp /tmp/gnome-shell.log /gnome-shell.log 2>/dev/null || true # Sin gdb en la imagen, la única forma de ver DÓNDE está bloqueado es forzarle un core y leerlo # con el gdb del host (`thread apply all bt`). SIGABRT lo produce y no lo maneja nadie. say "forzando core del proceso colgado (SIGABRT)..." kill -ABRT $SHELL_PID 2>/dev/null sleep 12; sync; sync say "cores: $(ls -la /core.* 2>/dev/null | tr '\n' ' ' || echo NINGUNO)" - for t in $(ls /proc/$SHELL_PID/task 2>/dev/null); do - say " tid $t wchan=$(cat /proc/$SHELL_PID/task/$t/wchan 2>/dev/null) comm=$(cat /proc/$SHELL_PID/task/$t/comm 2>/dev/null)" - done exec /bin/sh fi export WAYLAND_DISPLAY=wayland-0