Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Flamegraphs no Node.js

Atualizado em: 7 de setembro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

Flamegraphs no Node.js são visualizações que mostram onde o processo consome tempo de CPU. Em vez de analisar milhares de amostras de stack manualmente, o gráfico agrega pilhas semelhantes e apresenta blocos cuja largura representa a quantidade de amostras associadas a cada função.

Esse formato ajuda a encontrar loops custosos, serialização excessiva, regex lentas, compressão, criptografia, processamento de imagens, transformação de dados e chamadas repetidas. Ele é especialmente útil quando a CPU está alta e o event loop perde capacidade de responder rapidamente.

Neste guia, você aprenderá a gerar flamegraphs com Linux perf, opções do Node.js, ferramentas prontas, perfis de processos em execução, interpretação correta, limitações e um fluxo seguro de otimização.

O que é um flamegraph?

Um flamegraph agrega amostras de pilha coletadas durante um período. O eixo horizontal não representa tempo cronológico; ele mostra a proporção de amostras. O eixo vertical representa profundidade da pilha.

  • Blocos largos consumiram mais amostras de CPU.
  • Blocos empilhados mostram quem chamou quem.
  • A parte superior representa funções mais profundas.
  • A cor normalmente serve apenas para visualização, não para gravidade.

O guia oficial Flame Graphs do Node.js descreve o processo com Linux perf e ferramentas de Brendan Gregg.

Quando usar?

Use flamegraphs quando houver:

  • CPU sustentada alta;
  • throughput abaixo do esperado;
  • event loop bloqueado;
  • picos de latência durante processamento;
  • funções síncronas suspeitas;
  • regressão de performance após mudança;
  • necessidade de comparar duas implementações.

Para medir o efeito externo, consulte Autocannon no Node.js. O gerador de carga reproduz o problema; o flamegraph mostra onde a CPU é utilizada.

Prepare um cenário reproduzível

Antes de coletar, defina:

  • versão do Node.js;
  • commit do código;
  • comando de inicialização;
  • dados e dependências;
  • rota ou job testado;
  • taxa de carga;
  • duração;
  • máquina e limites.

Sem consistência, diferenças no gráfico podem vir do ambiente.

Instalando perf no Linux

Em distribuições baseadas em Debian ou Ubuntu:

sudo apt update
sudo apt install linux-tools-common linux-tools-generic

Dependendo do kernel, pode ser necessário instalar o pacote específico:

sudo apt install linux-tools-$(uname -r)

Confirme:

perf --version

Coletando com perf

perf record \
  -e cycles:u \
  -g \
  -- node \
  --perf-basic-prof \
  --interpreted-frames-native-stack \
  server.js

Enquanto o processo roda, aplique carga. Depois finalize de forma controlada. O perf gera um arquivo perf.data.

Gerando saída textual

perf script > perf.out

Esse arquivo contém as pilhas amostradas. Ele pode ser convertido para o formato de flamegraph.

Ferramentas FlameGraph

Clone o projeto oficial de Brendan Gregg:

git clone https://github.com/brendangregg/FlameGraph.git

O repositório está disponível em FlameGraph no GitHub.

cat perf.out \
  | ./FlameGraph/stackcollapse-perf.pl \
  | ./FlameGraph/flamegraph.pl --colors=js \
  > profile.svg

Abra profile.svg no navegador.

Profiling de processo já em execução

perf record \
  -F 99 \
  -p $(pgrep -n node) \
  -g \
  -- sleep 30

-F 99 coleta 99 amostras por segundo. O comando sleep 30 mantém a coleta ativa por trinta segundos.

Em produção, execute somente com autorização. Profiling pode exigir privilégios e gerar artefatos com nomes de funções e caminhos internos.

Opções do Node.js

As flags ajudam o perf a resolver símbolos JavaScript:

node \
  --perf-basic-prof-only-functions \
  --interpreted-frames-native-stack \
  server.js

--perf-basic-prof-only-functions produz menos dados e tende a ter overhead menor. Teste a combinação na versão de Node.js realmente usada.

Alternativa com CPU profile

O Node.js também gera perfil do V8:

node --cpu-prof server.js

O arquivo pode ser aberto no Chrome DevTools. Essa abordagem é portátil e útil quando Linux perf não está disponível.

Para depuração remota e perfis, veja Inspector no Node.js.

Ferramenta 0x

O 0x automatiza coleta e renderização:

npm install --save-dev 0x
npx 0x server.js

Depois aplique carga e encerre o processo. O 0x gera um relatório navegável. Fixe a versão e valide compatibilidade com seu runtime.

Como ler o gráfico

Comece pelos blocos largos próximos ao topo. Eles representam funções que aparecem frequentemente nas amostras. Clique para ampliar uma região e leia a cadeia de chamadas abaixo.

Perguntas úteis:

  • essa função deveria ser chamada tantas vezes?
  • há conversão ou serialização repetida?
  • o algoritmo cresce mal com a entrada?
  • há trabalho síncrono em uma rota HTTP?
  • uma dependência domina o custo?
  • o trabalho pode ser movido para worker?

Largura não significa bug

Uma função pode ser larga porque representa trabalho essencial. O flamegraph indica onde investigar, não determina automaticamente o que remover. Sempre relacione o bloco ao comportamento do produto e às métricas de latência.

Exemplo: JSON excessivo

Se JSON.stringify aparece muito largo, procure respostas gigantes, serialização repetida ou logs de objetos completos.

const payload = buildLargePayload(data);
const body = JSON.stringify(payload);
res.end(body);

Possíveis ações:

  • paginar resultados;
  • selecionar campos;
  • evitar stringify duplicado;
  • usar streaming quando apropriado;
  • não registrar payload completo.

Exemplo: regex lenta

Uma expressão com backtracking pode dominar CPU. Teste entradas adversariais e simplifique o padrão. Consulte ReDoS no Node.js.

Exemplo: criptografia

Hashing de senhas deve ser caro por segurança. Se aparece no flamegraph, confirme que o custo é esperado e que não está sendo executado desnecessariamente várias vezes. Limite concorrência para evitar saturação.

Worker Threads

Trabalho CPU-bound pode ser transferido:

import { Worker } from 'node:worker_threads';

const worker = new Worker(
  new URL('./worker.js', import.meta.url),
  { workerData: input }
);

Veja Worker Threads no Node.js. A mudança não reduz necessariamente o custo total, mas evita bloquear a thread que atende requisições.

Comparando antes e depois

Repita o mesmo cenário:

  1. registre benchmark inicial;
  2. capture o primeiro flamegraph;
  3. faça uma alteração pequena;
  4. capture outro perfil;
  5. compare throughput e percentis;
  6. verifique se o custo migrou para outra função.

Uma otimização local pode piorar memória, I/O ou latência de cauda.

Overhead

Profiling adiciona custo. Não use os números absolutos coletados como estimativa exata de produção. Use o perfil para proporções e relações entre funções, e valide o resultado com benchmark sem profiler.

Containers

Perf em containers pode exigir capacidades e acesso ao kernel:

docker run --rm \
  --cap-add SYS_ADMIN \
  --security-opt seccomp=unconfined \
  my-app

Conceder capacidades aumenta risco. Prefira ambiente de profiling isolado e políticas mínimas. Em clusters, considere uma réplica temporária dedicada.

Kernel e permissões

O valor de perf_event_paranoid controla acesso:

cat /proc/sys/kernel/perf_event_paranoid

Alterações exigem avaliação de segurança. Não reduza proteção de hosts compartilhados sem entender o impacto.

Funções internas

O gráfico pode conter V8, libc e internals do Node.js. Filtrar torna a visão mais limpa, mas pode esconder o verdadeiro gargalo. Gere primeiro sem filtros e use limpeza apenas para facilitar leitura.

Código otimizado pelo V8

O V8 compila e otimiza funções durante a execução. Nomes podem aparecer ausentes ou diferentes dependendo das flags, versão e perf. Se o gráfico parece incompleto, compare com --cpu-prof ou Inspector.

Sampling versus instrumentação

Flamegraphs por sampling observam pilhas periodicamente e possuem overhead relativamente controlado. Instrumentação mede cada chamada, podendo ser mais detalhada e mais cara. Para CPU geral, sampling costuma ser um bom começo.

Combine com observabilidade

Durante a coleta, observe:

  • CPU por processo;
  • event loop lag;
  • garbage collection;
  • requisições por segundo;
  • p95 e p99;
  • erros;
  • pool do banco;
  • memória.

Consulte Performance Hooks no Node.js e Métricas Prometheus no Node.js.

Dados sensíveis

Perfis geralmente registram nomes de funções, bibliotecas e caminhos. Eles podem revelar arquitetura interna. Armazene artefatos com acesso restrito e prazo de retenção.

Erros comuns

  • Confundir largura com sequência temporal: o eixo horizontal é proporção.
  • Otimizar sem benchmark: melhora visual pode não melhorar usuário.
  • Perfil sem reproduzir o problema: coleta caminho irrelevante.
  • Ignorar overhead: números absolutos ficam distorcidos.
  • Filtrar cedo demais: remove causa real.
  • Usar ambiente diferente: comparação fica inválida.
  • Dar privilégios excessivos: cria risco operacional.

Fluxo recomendado

  1. Confirme CPU alta com métricas.
  2. Reproduza sob carga controlada.
  3. Colete perfil curto e representativo.
  4. Localize blocos largos.
  5. Relacione ao código e ao negócio.
  6. Faça uma mudança por vez.
  7. Repita o benchmark sem profiler.
  8. Documente resultado e ambiente.

Conclusão

Flamegraphs no Node.js transformam amostras de CPU em uma visualização que facilita localizar trabalho síncrono e funções dominantes. Linux perf oferece controle detalhado, enquanto --cpu-prof, Inspector e ferramentas como 0x simplificam a coleta.

O gráfico é um mapa de investigação, não uma resposta automática. Reproduza o problema, valide símbolos, observe métricas e compare antes e depois. Com esse processo, flamegraphs ajudam a otimizar o que realmente limita throughput e latência, sem depender de suposições.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita