Perfetto: capturar el sistema entero y encontrar el frame perdido
Perfetto no perfila tu aplicación: perfila la máquina. Esta lección cubre el ciclo completo de una traza de sistema, desde la captura con el mosaico del dispositivo o desde la línea de órdenes con categorías y presupuesto de memoria, hasta la lectura del cronograma en la interfaz web que procesa el archivo localmente. El núcleo es el diagnóstico del frame perdido: los carriles de cronograma esperado y real que el sistema publica desde Android 12, la clasificación de la culpa entre aplicación y compositor, y el descenso por los estados de planificación de hilos hasta la causa física, sea contención de candado, transacción bloqueante, espera de disco o recolección de basura.
Los perfiladores de proceso comparten un supuesto que casi nunca se enuncia: que la causa del problema está dentro de tu código. Es un supuesto razonable y frecuentemente falso. Un frame se pierde porque el hilo principal no terminó a tiempo, sí, pero también porque el planificador lo dejó listo y sin núcleo durante ocho milisegundos, porque el núcleo en el que corría estaba a la frecuencia mínima, porque un servicio del sistema tardó en responder a una transacción, porque el compositor no llegó a su propia fecha límite o porque otro proceso monopolizó el ancho de banda de memoria. Ninguna de esas causas aparece en una pila de llamadas. Perfetto existe porque el objeto correcto de observación en un problema de fluidez no es el proceso sino el sistema completo, y porque la única manera de atribuir la culpa con honestidad es tener a la vista, en el mismo eje de tiempo, lo que hacían simultáneamente tu aplicación, el compositor, el planificador y el hardware.
- Capturar una traza del sistema con el mosaico del dispositivo y desde la línea de órdenes con control de categorías y de presupuesto.
- Orientarse en el cronograma: carriles de proceso, hilos, estados de planificación y frecuencia de los núcleos.
- Interpretar los carriles de cronograma esperado y real para localizar el frame perdido y su clasificación de culpa.
- Descender del frame perdido a la causa física y distinguir competencia por CPU, bloqueo, espera de disco y recolección.
Capturar: qué se graba y a qué precio
Perfetto es un sistema de captura permanente del propio Android. Un servicio en segundo plano recoge fuentes de datos muy distintas —eventos del planificador del núcleo, marcas de instrumentación del espacio de usuario, contadores de memoria, sucesos del compositor— y las escribe en un búfer circular con un formato binario común. Esa unidad de formato es lo que permite ver en un mismo eje algo tan bajo como un cambio de contexto y algo tan alto como el nombre de una función componible.
La vía más rápida es el mosaico de captura de trazas que aparece en los ajustes de desarrollador: se activan las categorías, se pulsa para empezar, se reproduce el problema y se pulsa para terminar. Sirve para casi todo. La línea de órdenes hace falta cuando el problema exige control fino de la duración, del presupuesto de memoria o del conjunto exacto de categorías, y cuando se quiere automatizar la captura.
# Traza de 20 segundos con las categorias que importan para fluidez.
adb shell perfetto -o /data/misc/perfetto-traces/traza.pftrace \
-t 20s -b 64mb \
sched freq idle am wm gfx view binder_driver dalvik res memory
adb pull /data/misc/perfetto-traces/traza.pftrace .
Las dos opciones numéricas deciden si la traza servirá para algo. El presupuesto de búfer es un búfer circular: si se queda corto, los eventos más antiguos se sobrescriben en silencio y la traza aparece truncada por el principio sin ningún aviso. La categoría de planificación es con diferencia la más voluminosa, porque registra cada cambio de contexto de cada núcleo, y es también la más valiosa: sin ella no se puede distinguir un hilo que calcula de un hilo que espera turno.
La interfaz de Perfetto es una aplicación web que ejecuta el procesador de trazas compilado a WebAssembly dentro de la propia pestaña. El archivo se lee en local, no se sube a ningún servidor, y ese detalle importa porque una traza de sistema contiene los nombres de todos los procesos del dispositivo y a menudo información sensible de la sesión. Además de la vista gráfica, la interfaz expone una consola de consultas sobre un modelo relacional de la traza, con tablas de segmentos, hilos, procesos y contadores. Cuando una pregunta se repite —cuáles son los segmentos más largos del hilo principal, cuántos frames superaron su fecha límite— la consulta escrita a mano es más rápida y más reproducible que cualquier exploración visual.
Leer el cronograma sin ahogarse
Una traza recién abierta abruma porque muestra centenares de carriles de procesos que no interesan. Orientarse consiste en saber cuáles son los cinco que importan y colapsar todo lo demás.
El hilo principal de tu proceso
Ahí viven los segmentos de dibujo del sistema y los tuyos. El segmento de preparación de frame es la unidad de trabajo por fotograma y su longitud es la primera magnitud a mirar.
Los carriles de frames
Desde Android 12 cada frame tiene un carril de cronograma esperado y otro de cronograma real. La comparación entre ambos es el diagnóstico, no la interpretación del observador.
Los estados de planificación
Cada hilo se colorea según esté ejecutándose, listo y esperando núcleo, durmiendo o bloqueado sin interrupción. El color distingue lentitud por cálculo de lentitud por espera.
El compositor del sistema
Su proceso tiene sus propios frames y sus propias fechas límite. Cuando la culpa es suya, optimizar tu código no cambia absolutamente nada.
Los estados de planificación son la lectura que más diagnósticos corrige. Un hilo ejecutándose consume tiempo de núcleo y su problema es de cálculo. Un hilo listo está preparado para trabajar y no tiene núcleo asignado, lo que significa competencia con otros procesos o migración a un núcleo pequeño, y ningún cambio en tu código lo arregla. Un hilo durmiendo espera algo: un candado, una respuesta, un temporizador. Un hilo en espera sin interrupción está casi siempre bloqueado en disco. Cuatro estados, cuatro familias de causa, cuatro clases de solución completamente distintas.
El frame perdido y su cadena causal
Un frame se pierde cuando su fecha límite pasa sin que el contenido esté listo para ser compuesto. El sistema publica esa información directamente: el carril de cronograma esperado dibuja el presupuesto que ese frame tenía, el de cronograma real dibuja lo que tardó de verdad, y cuando el segundo se sale del primero el frame aparece marcado y etiquetado con una clasificación de culpa. Esa clasificación es el primer bifurcador y ahorra horas: si dice que quien incumplió fue el compositor, o si el problema es acumulación de búferes, el trabajo no está en tu hilo principal.
Cuando la culpa es de la aplicación, se selecciona el segmento de preparación de frame correspondiente y se lee su interior. Ahí aparecen las fases conocidas —medida, disposición, dibujo— y los segmentos propios que se hayan instrumentado. El objetivo es encontrar el segmento anómalo: no el más largo en términos absolutos, sino el que dura mucho más que en los frames vecinos que sí llegaron a tiempo. Esa comparación con los frames sanos es el método, porque un frame patológico solo se define por contraste.
Encontrado el segmento anómalo, queda la última pregunta: por qué duró tanto. Aquí el cronograma ofrece firmas reconocibles.
Contención de candado
Aparecen segmentos explícitos de contención en el hilo principal, con el identificador del hilo que retiene el candado. El arreglo está en el otro hilo, no en este.
Transacción bloqueante
Un segmento de transacción del mecanismo de comunicación entre procesos que se alarga: el hilo principal espera a un servicio del sistema que está ocupado o suspendido.
Espera de disco
El hilo entra en espera sin interrupción. Casi siempre es lectura de preferencias, de base de datos o de recursos ejecutada donde no debía.
Recolección de basura
Actividad simultánea del hilo del recolector y pausas cortas repartidas. El problema no es el recolector sino el ritmo de asignación que lo provoca.
flowchart TD
A[Frame marcado en el carril real] --> B{Quien incumplio la fecha limite}
B -->|Compositor o acumulacion de buferes| C[La causa esta fuera de la aplicacion]
B -->|La aplicacion| D[Abrir el segmento de preparacion de frame]
D --> E[Comparar con frames vecinos sanos]
E --> F[Localizar el segmento anomalo]
F --> G{Estado del hilo durante ese segmento}
G -->|Ejecutandose| H[Coste de calculo: ampliar con muestreo]
G -->|Listo sin nucleo| I[Competencia o nucleo pequeno]
G -->|Durmiendo| J[Candado o transaccion bloqueante]
G -->|Espera sin interrupcion| K[Entrada y salida de disco]
style A fill:#f38ba8,color:#11111b
style F fill:#fab387,color:#11111b
style H fill:#a6e3a1,color:#11111bLa trampa más común del análisis de fluidez es buscar la causa dentro del intervalo del frame que falló. Muchas veces no está ahí. Un trabajo pesado ejecutado en el frame anterior puede haber dejado al recolector trabajando durante el siguiente; una escritura lanzada tres frames atrás puede estar bloqueando el disco justo ahora; una tarea en segundo plano que arrancó hace un segundo puede estar ocupando todos los núcleos grandes. Por eso se captura una ventana amplia y se examina el vecindario del frame perdido, no solo su interior. La regla práctica es mirar al menos medio segundo hacia atrás antes de concluir nada.
El salto conceptual que separa una traza de sistema de cualquier perfilador de proceso no es de escala sino de objeto. Un perfilador de proceso responde a la pregunta clásica de la optimización, que es qué está haciendo mi programa, y da por sentado que el programa avanza y que el problema consiste en que avanza despacio porque hace demasiado. Perfetto está construido sobre la observación de que en un sistema operativo moderno esa suposición falla la mayor parte del tiempo: el hilo que produce el trabón casi nunca está calculando de más, casi siempre está esperando. Espera un núcleo que el planificador ha dado a otro proceso, espera un candado que un hilo secundario retiene mientras hace entrada y salida, espera una respuesta de un servicio del sistema que a su vez espera otra cosa, espera a que el disco devuelva una página, espera a que la frecuencia del núcleo suba desde el mínimo al que el gobernador la había dejado. Ninguna de esas esperas es visible en una pila de llamadas, porque una pila de llamadas describe la estructura del trabajo y no su progreso en el tiempo; el hilo bloqueado tiene exactamente la misma pila que el hilo que avanza, y por eso un perfilador de muestreo puede señalar un método que en realidad no cuesta nada y solo tiene la mala suerte de ser donde el hilo se detiene a esperar. La consecuencia metodológica es que el rendimiento de una aplicación interactiva no es una propiedad de su código sino una propiedad de su relación con un sistema compartido, y que la unidad de análisis correcta no es la función sino el intervalo: qué ocurría en toda la máquina entre el instante en que este frame debía empezar y el instante en que debía estar compuesto. Ahí se ve, además, algo que ningún perfilador aislado puede mostrar, y es que la causa de un frame perdido con frecuencia no vive en ese frame ni siquiera en ese proceso. La fluidez no se optimiza, se negocia con un sistema que tiene otros inquilinos, y Perfetto es el instrumento que hace visible esa negociación.
- Captura una traza de veinte segundos mientras desplazas una lista y localiza los frames marcados en el carril de cronograma real. Anota la clasificación de culpa de cada uno.
- Toma un frame perdido de la aplicación, ábrelo y compáralo segmento a segmento con dos frames vecinos que sí llegaron a tiempo. Escribe qué segmento es el anómalo.
- Determina el estado de planificación del hilo principal durante ese segmento y clasifica la causa en una de las cuatro familias. Justifica con lo que ves, no con lo que supones.
- Reduce a la mitad el presupuesto de búfer y repite la captura: comprueba que la traza queda truncada por el principio y que nada te avisa de ello.
- Provoca deliberadamente una lectura de disco en el hilo principal, encuentra la espera sin interrupción en el cronograma y verifica que el muestreo de métodos, por sí solo, no habría revelado la causa.