Skip to content

Gráficos de llamas

¿Para qué sirve un gráfico de llamas?

Los gráficos de llamas son una forma de visualizar el tiempo de CPU dedicado a las funciones. Pueden ayudarte a determinar dónde dedicas demasiado tiempo a realizar operaciones síncronas.

Cómo crear un gráfico de llamas

Es posible que hayas oído que crear un gráfico de llamas para Node.js es difícil, pero eso no es cierto (ya no). ¡Las máquinas virtuales Solaris ya no son necesarias para los gráficos de llamas!

Los gráficos de llamas se generan a partir de la salida de perf, que no es una herramienta específica de Node. Aunque es la forma más potente de visualizar el tiempo de CPU empleado, puede tener problemas con la forma en que se optimiza el código JavaScript en Node.js 8 y superior. Consulta la sección problemas con la salida de perf a continuación.

Utilizar una herramienta preempaquetada

Si quieres un único paso que produzca un gráfico de llamas localmente, prueba 0x

Para diagnosticar implementaciones de producción, lee estas notas: 0x servidores de producción.

Crear un gráfico de llamas con las herramientas perf del sistema

El propósito de esta guía es mostrar los pasos necesarios para crear un gráfico de llamas y mantenerte en control de cada paso.

Si quieres entender mejor cada paso, echa un vistazo a las siguientes secciones donde entramos en más detalles.

Ahora vamos a ponernos manos a la obra.

  1. Instala perf (normalmente disponible a través del paquete linux-tools-common si no está ya instalado)
  2. Intenta ejecutar perf: puede que se queje de que faltan módulos del kernel, instálalos también
  3. Ejecuta node con perf habilitado (consulta problemas con la salida de perf para obtener consejos específicos para las versiones de Node.js)
bash
perf record -e cycles:u -g -- node --perf-basic-prof app.js
  1. No hagas caso de las advertencias a menos que digan que no puedes ejecutar perf por falta de paquetes; es posible que recibas algunas advertencias sobre la imposibilidad de acceder a las muestras del módulo del kernel que no buscas de todos modos.
  2. Ejecuta perf script > perfs.out para generar el archivo de datos que visualizarás en un momento. Es útil aplicar un poco de limpieza para obtener un gráfico más legible
  3. Instala stackvis si aún no está instalado npm i -g stackvis
  4. Ejecuta stackvis perf < perfs.out > flamegraph.htm

Ahora abre el archivo del gráfico de llamas en tu navegador favorito y observa cómo arde. Está codificado por colores para que puedas centrarte primero en las barras naranjas más saturadas. Es probable que representen funciones que consumen mucha CPU.

Vale la pena mencionar que, si haces clic en un elemento de un gráfico de llamas, se mostrará un zoom de sus alrededores encima del gráfico.

Usando perf para tomar muestras de un proceso en ejecución

Esto es genial para registrar datos de gráficos de llamas de un proceso que ya está en ejecución y que no quieres interrumpir. Imagina un proceso en producción con un problema difícil de reproducir.

bash
perf record -F99 -p `pgrep -n node` -- sleep 3

¿Para qué sirve ese sleep 3? Sirve para mantener perf en ejecución - a pesar de que la opción -p apunte a un pid diferente, el comando necesita ser ejecutado en un proceso y terminar con él. perf se ejecuta durante la vida del comando que le pasas, tanto si estás o no perfilando realmente ese comando. sleep 3 asegura que perf se ejecute durante 3 segundos.

¿Por qué -F (frecuencia de perfilado) está establecido en 99? Es un valor predeterminado razonable. Puedes ajustarlo si quieres. -F99 le dice a perf que tome 99 muestras por segundo, para más precisión aumenta el valor. Valores más bajos deberían producir menos salida con resultados menos precisos. La precisión que necesitas depende de cuánto tiempo realmente se ejecutan tus funciones que consumen CPU. Si estás buscando la razón de una ralentización notable, 99 frames por segundo deberían ser más que suficientes.

Después de obtener esos 3 segundos de registro de perf, procede a generar el gráfico de llamas con los dos últimos pasos de arriba.

Filtrado de funciones internas de Node.js

Normalmente, solo quieres observar el rendimiento de tus llamadas, por lo que filtrar las funciones internas de Node.js y V8 puede hacer que el gráfico sea mucho más fácil de leer. Puedes limpiar tu archivo perf con:

bash
sed -i -r \
    -e '/(_libc_start|LazyCompile) |v8::internal::BuiltIn|Stub|LoadIC:\\[\\[' \
    -e '/^$/d' \
    perf.data > perf.out

Si lees tu gráfico de llamas y parece extraño, como si faltara algo en la función clave que ocupa la mayor parte del tiempo, intenta generar tu gráfico de llamas sin los filtros; tal vez tengas un caso raro de un problema con el propio Node.js.

Opciones de creación de perfiles de Node.js

--perf-basic-prof-only-functions y --perf-basic-prof son las dos que son útiles para depurar tu código JavaScript. Otras opciones se utilizan para la creación de perfiles del propio Node.js, lo que está fuera del alcance de esta guía.

--perf-basic-prof-only-functions produce menos salida, por lo que es la opción con la menor sobrecarga.

¿Por qué las necesito en absoluto?

Bueno, sin estas opciones, seguirás obteniendo un gráfico de llamas, pero con la mayoría de las barras etiquetadas como v8::Function::Call.

Problemas con la salida de Perf

Cambios en la canalización de V8 de Node.js 8.x

Node.js 8.x y versiones superiores se envían con nuevas optimizaciones a la canalización de compilación de JavaScript en el motor V8, lo que hace que los nombres/referencias de las funciones sean inalcanzables para perf a veces. (Se llama Turbofan)

El resultado es que es posible que los nombres de tus funciones no aparezcan correctamente en el gráfico de llamas.

Verás ByteCodeHandler: donde esperarías nombres de función.

0x tiene algunas mitigaciones para eso incorporadas.

Para obtener más detalles, consulta:

Node.js 10+

Node.js 10.x soluciona el problema con Turbofan utilizando el indicador --interpreted-frames-native-stack.

Ejecuta node --interpreted-frames-native-stack --perf-basic-prof-only-functions para obtener los nombres de función en el gráfico de llamas independientemente de qué canalización utilizó V8 para compilar tu JavaScript.

Etiquetas rotas en el gráfico de llamas

Si estás viendo etiquetas como esta:

bash
node`_ZN2v88internal11interpreter17BytecodeGenerator15VisitStatementsEPMS0_8Zone

significa que el perf de Linux que estás usando no se compiló con soporte para demangle, consulta https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1396654 por ejemplo.

Ejemplos

¡Practica la captura de gráficos de llamas tú mismo con un ejercicio de gráficos de llamas!