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

Flamegraphs no Node.js

Atualizado em: 26 de setembro de 2026

Rack de servidores processando fluxos de dados no Node.js

Flamegraphs são visualizações de amostras de CPU agrupadas por pilha de chamadas. Em aplicações Node.js, eles mostram onde o processo gasta tempo executando JavaScript, funções nativas, garbage collection e bibliotecas do sistema. São especialmente úteis para localizar operações síncronas, serialização cara, loops, regex, criptografia e processamento que bloqueia o event loop.

Cada retângulo representa uma função. A largura indica a quantidade de amostras em que aquela função apareceu, incluindo chamadas abaixo dela. A posição vertical representa a pilha: chamadores ficam embaixo e funções chamadas ficam acima. A ordem horizontal não é uma linha do tempo.

Quando usar

Use flamegraphs quando:

  • CPU está alta;
  • throughput caiu;
  • event loop lag aumentou;
  • p99 piorou sob carga;
  • uma rota consome muito processamento;
  • uma mudança de código causou regressão;
  • não está claro qual função é o gargalo.

Para vazamentos de memória, heap snapshots e heap profiler são mais adequados. Flamegraphs respondem principalmente “onde a CPU está gastando tempo?”.

Como ler

Procure barras largas. Uma barra larga no topo indica função folha consumindo CPU. Uma barra larga embaixo pode apenas representar um chamador comum.

A cor normalmente não indica gravidade; em muitos geradores é apenas estética ou categoria. Não conclua que vermelho é erro.

Inclusive e exclusive time

Tempo inclusivo inclui funções chamadas. Tempo exclusivo representa amostras na própria função. Em um flamegraph tradicional, a largura da função inclui descendentes; a parte sem filhos acima aproxima trabalho exclusivo.

Reproduzindo a carga

Antes de perfilar, crie um cenário estável:

  1. inicie a mesma versão usada no problema;
  2. aqueça aplicação, JIT, pools e caches;
  3. gere carga representativa;
  4. confirme CPU e latência;
  5. capture durante o período lento;
  6. repita para verificar consistência.

Um perfil sem carga relevante pode mostrar apenas inicialização ou espera.

Usando 0x

0x é uma ferramenta pré-empacotada para gerar flamegraph:

npm install -D 0x
npx 0x dist/server.js

Em outro terminal, gere carga:

npx autocannon -c 50 -d 30 http://127.0.0.1:3000/api/items

Interrompa o processo de forma controlada e abra o relatório gerado.

Produção com 0x

Profiling adiciona overhead. Em produção, use por período curto, em uma réplica isolada e com autorização. Remova o pod do balanceador se o problema puder ser reproduzido sem tráfego externo, ou mantenha pequena parcela de tráfego quando precisa capturar o comportamento real.

Linux perf

O fluxo oficial usa perf. Instale ferramentas do kernel e execute:

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

Depois gere texto:

perf script > perfs.out

Converta com FlameGraph de Brendan Gregg:

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

Abra o SVG em um navegador e clique em áreas para zoom.

Perfilar processo em execução

perf record -F 99 -p PID -g -- sleep 30

-F 99 coleta aproximadamente 99 amostras por segundo. sleep 30 define duração da captura. Aumentar frequência melhora resolução, mas aumenta overhead e tamanho.

Identificando o PID

pgrep -n node
ps -ef | grep node

Em containers, o processo pode ser PID 1 dentro do namespace e outro PID no host. Execute perf no nível que possui permissões e símbolos adequados.

Flags do Node.js

--perf-basic-prof fornece informações para nomear funções JavaScript. --perf-basic-prof-only-functions gera menos dados e menor overhead. --interpreted-frames-native-stack melhora nomes de frames interpretados.

Sem flags, muitas barras podem aparecer apenas como chamadas genéricas do V8.

Permissões do perf

Linux pode bloquear profiling:

cat /proc/sys/kernel/perf_event_paranoid

Alterar essa configuração afeta segurança do host. Em produção, siga política da infraestrutura e restaure o valor. Containers podem precisar de capabilities e acesso a perf events.

Filtrando internos

É possível remover frames do V8 e libc para reduzir ruído, mas filtre somente depois de gerar uma versão completa. O gargalo pode estar em código nativo ou no próprio runtime.

sed -i -r \
  -e "/(v8::internal::|Builtin:|Stub:|\\[unknown\\])/d" \
  perfs.out

Frames desconhecidos

[unknown], endereços ou nomes quebrados podem indicar símbolos ausentes, versão do perf sem demangle, otimização do V8 ou stacks não capturadas. Compare com 0x, flags adicionais ou outro profiler.

Exemplo de CPU síncrona

function gerarRelatorio(items) {
  return items
    .map((item) => JSON.stringify(normalizar(item)))
    .sort()
    .join('\n');
}

Um flamegraph pode mostrar JSON.stringify, sort e normalizar como barras largas. A solução pode ser streaming, paginação, cache, worker thread ou pré-processamento.

Regex e ReDoS

Expressões regulares vulneráveis aparecem como tempo concentrado em execução de regex, especialmente com payload específico. Reproduza com entrada problemática e substitua a expressão ou limite o tamanho.

Serialização JSON

Respostas grandes podem gastar CPU em serialização. Considere:

  • reduzir campos;
  • paginar;
  • pré-serializar conteúdo estável;
  • usar schema serializers;
  • streaming;
  • compressão fora do processo quando aplicável.

Criptografia

Hash de senha, assinatura e criptografia são intencionalmente caros. Se aparecem largos:

  • verifique parâmetros;
  • limite concorrência;
  • use APIs assíncronas;
  • dimensione thread pool;
  • não reduza segurança sem análise.

Garbage collection

Tempo em GC pode indicar alta taxa de alocação, heap pressionado ou objetos grandes. Um flamegraph mostra CPU do GC, mas heap profiler e traces de GC ajudam a explicar por quê.

Event loop

Uma função larga e síncrona bloqueia todas as conexões daquele processo. Meça event loop utilization e delay junto ao perfil. CPU baixa não elimina bloqueio: uma chamada nativa ou I/O síncrono pode parar a thread.

Worker threads

Se uma tarefa é CPU-bound e não pode ser evitada, mova para worker. Perfilar cada worker separadamente pode ser necessário, pois cada thread tem stacks próprias.

Native addons

Bibliotecas nativas podem aparecer com símbolos C/C++. Instale símbolos de debug quando disponível. Um addon bloqueando a thread principal pode explicar latência mesmo sem função JavaScript larga.

Comparando antes e depois

Use mesma carga, hardware, duração, versão do Node e dados. Compare:

  • largura da função alvo;
  • throughput;
  • p95 e p99;
  • CPU total;
  • event loop delay;
  • GC;
  • erros.

Uma barra menor pode apenas ter movido custo para outro lugar.

Differential flamegraphs

Ferramentas de diff destacam aumento e redução de amostras entre perfis. São úteis para regressões, desde que as capturas tenham condições comparáveis.

Perfil curto ou longo

Capturas curtas isolam pico, mas têm poucas amostras. Capturas longas misturam fases. Escolha janela alinhada ao sintoma e repita.

Continuous profiling

Ferramentas como Pyroscope coletam perfis ao longo do tempo e permitem filtrar por versão, serviço e ambiente. Isso reduz necessidade de reproduzir incidentes raros. Controle overhead e cardinalidade de labels.

Kubernetes

Em clusters:

  • identifique pod e container corretos;
  • garanta permissões de perf;
  • capture uma réplica;
  • correlacione com throttling de CPU;
  • verifique requests e limits;
  • copie artefatos com segurança;
  • remova ferramentas de debug da imagem final quando não usadas.

CPU throttling

Uma aplicação pode parecer CPU-bound porque o container é limitado. Flamegraph mostra onde usa o tempo disponível, enquanto métricas de cgroup mostram throttling. Corrigir código e ajustar limites podem ser necessários.

Benchmark junto com profiling

Use Autocannon ou k6 para manter carga constante durante a captura. O gerador deve rodar em máquina separada em medições sérias.

Overhead

Sampling é menos invasivo que instrumentação completa, mas não é gratuito. Frequência alta, símbolos e duração aumentam overhead. Faça uma captura controle para medir impacto.

Segurança dos perfis

Perfis podem revelar nomes de funções, caminhos, bibliotecas e arquitetura. Trate arquivos como dados internos, limite acesso e defina retenção.

Erros comuns

  • interpretar eixo horizontal como tempo;
  • otimizar barra estreita;
  • capturar sem reproduzir lentidão;
  • ignorar gerador saturado;
  • filtrar internos cedo demais;
  • comparar ambientes diferentes;
  • não validar impacto após otimização;
  • confundir CPU com memória.

Processo de investigação

  1. confirme sintoma com métricas;
  2. reproduza carga;
  3. capture perfil curto;
  4. localize barras largas;
  5. relacione a código e métricas;
  6. crie hipótese;
  7. otimize uma causa;
  8. repita benchmark e perfil;
  9. documente resultado.

Fluxo recomendado

Use profiling por amostragem durante carga estável, leia largura como CPU, investigue funções largas e valide toda otimização com percentis. Combine com Autocannon no Node.js, carga em k6, memória em Heap Snapshots e profiling contínuo em Pyroscope.

Consulte o guia oficial de flamegraphs do Node.js e o repositório oficial FlameGraph de Brendan Gregg.

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