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.
- Instala
perf
(normalmente disponible a través del paquete linux-tools-common si no está ya instalado) - Intenta ejecutar
perf
: puede que se queje de que faltan módulos del kernel, instálalos también - Ejecuta node con perf habilitado (consulta problemas con la salida de perf para obtener consejos específicos para las versiones de Node.js)
perf record -e cycles:u -g -- node --perf-basic-prof app.js
- 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.
- 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 - Instala stackvis si aún no está instalado
npm i -g stackvis
- 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.
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:
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:
- https://github.com/nodejs/benchmarking/issues/168
- https://github.com/nodejs/diagnostics/issues/148#issuecomment-369348961
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:
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!