Eventos desconhecidos em nodejs / v8 flamegrafam usando perf_events

Eu tento fazer alguns perfis de nodejs usando o Linux perf_events como descrito por Brendan Gregg aqui .

Fluxo de trabalho está seguindo:

  1. execute node> 0.11.13 com --perf-basic-prof , que cria o arquivo /tmp/perf-(PID).map onde o mapeamento de símbolos JavaScript é gravado.
  2. Capture pilhas usando o perf record -F 99 -p `pgrep -n node` -g -- sleep 30
  3. Dobre pilhas usando o script stackcollapse-perf.pl deste repository
  4. Gerar gráfico de chama svg usando o script flamegraph.pl

Eu recebo o seguinte resultado (que parece muito bom no começo): insira a descrição da imagem aqui

O problema é que há muitos elementos [unknown] , que suponho que devam ser chamadas de function do meu nodejs. Eu assumo que todo o processo falha em algum lugar no ponto 3, onde os dados perf devem ser dobrados usando mapeamentos gerados pelo nó / v8 executado com --perf-basic-prof . /tmp/perf-PID.map arquivo /tmp/perf-PID.map é criado e alguns mapeamentos são gravados nele durante a execução do nó.

Como resolver este problema?

Eu estou usando o CentOS 6.5 x64, e já tentei isso com o nó 0.11.13, 0.11.14 (ambos pré-construídos e compilados também) sem sucesso.

Primeiro de tudo, o que “[desconhecido]” significa é que o sampler não conseguiu descobrir o nome da function, porque é uma function de sistema ou biblioteca. Se assim for, tudo bem – você não se importa, porque está procurando por coisas responsáveis ​​pelo tempo no seu código, não pelo código do sistema.

Na verdade, estou sugerindo que essa é uma daquelas perguntas XY . Mesmo que você receba uma resposta direta ao que perguntou, é provável que seja de pouca utilidade. Aqui estão as razões do porquê:

1. A criação de perfil de CPU é de pouca utilidade em um programa vinculado a E / S

As duas torres à esquerda em seu gráfico de chama estão fazendo E / S, então elas provavelmente ocupam muito mais tempo na parede do que a grande pilha à direita. Se esse gráfico de chama fosse derivado de amostras de tempo de parede, em vez de amostras de tempo de CPU, ele poderia se parecer mais com o segundo gráfico abaixo, que informa onde o tempo realmente vai:

insira a descrição da imagem aqui

O que era uma grande pilha de aparência suculenta à direita encolheu, por isso não é nem de longe tão significativo. Por outro lado, as torres de E / S são muito largas. Qualquer uma dessas faixas laranjas largas, se estiver em seu código, representa uma chance de economizar muito tempo, se alguma das E / S puder ser evitada.

2. Se o programa é limitado pela CPU ou pela I / O, as oportunidades de aceleração podem se esconder facilmente dos charts de chama

Suponha que exista alguma function Foo que esteja realmente fazendo algo que desperdiçaria, que se você soubesse, poderia consertar. Suponha que no gráfico de chama, é uma cor vermelha escura. Suponha que seja chamado a partir de numerosos lugares no código, por isso nem tudo é coletado em um ponto no gráfico de chama. Pelo contrário, aparece em vários locais pequenos mostrados aqui por contornos pretos:

insira a descrição da imagem aqui

Observe que, se todos esses retângulos foram coletados, você pode ver que é responsável por 11% do tempo, o que significa que vale a pena olhar. Se você pudesse reduzir seu tempo pela metade, você poderia economizar 5,5% no total. Se o que está fazendo realmente puder ser totalmente evitado, você poderá economizar 11% no total. Cada um desses pequenos retângulos encolheria a nada e puxaria o resto do gráfico para a direita, com ele.

Agora mostrarei o método que uso . Eu pego um número moderado de amostras de pilha aleatória e examino cada uma para rotinas que podem ser aceleradas. Isso corresponde a tomar amostras no gráfico de chama assim:

insira a descrição da imagem aqui

As linhas verticais delgadas representam vinte amostras de pilha de tempo random. Como você pode ver, três deles estão marcados com um X. Esses são os que passam por Foo . Esse é o número certo, porque 11% vezes 20 é 2.2.

(Confuso? OK, aqui está uma pequena probabilidade para você. Se você apostar uma moeda 20 vezes, e tem 11% de chance de vir à tona, quantos chefes você obteria? Tecnicamente, é uma distribuição binomial. obter é 2, os próximos números mais prováveis ​​são 1 e 3. (Se você receber apenas 1 você continua até chegar em 2.) Aqui está a distribuição 🙂

insira a descrição da imagem aqui

(O número médio de amostras que você deve levar para ver Foo duas vezes é 2 / 0.11 = 18.2 amostras.)

Olhando para essas 20 amostras pode parecer um pouco assustador, porque eles correm entre 20 e 50 níveis de profundidade. No entanto, basicamente você pode ignorar todo o código que não é seu . Basta examiná-los para o seu código . Você verá exatamente como está gastando tempo e terá uma medição muito aproximada de quanto. As deep stacks são más notícias e boas notícias – elas significam que o código pode ter muito espaço para acelerações, e elas mostram o que são.

Qualquer coisa que você veja que você pode acelerar, se você ver em mais de uma amostra , lhe dará uma velocidade saudável, garantida. A razão pela qual você precisa vê-lo em mais de uma amostra é, se você só vê-lo em uma amostra, você só sabe que seu tempo não é zero. Se você vê em mais de uma amostra, você ainda não sabe quanto tempo leva, mas você sabe que não é pequeno. Aqui estão as statistics.

De um modo geral, é uma má ideia discordar de um especialista no assunto, mas (com o maior respeito) aqui vamos nós!

SO pede a resposta para fazer o seguinte:

“Por favor, não se esqueça de responder a pergunta. Forneça detalhes e compartilhe sua pesquisa!”

Então a questão era, pelo menos a minha interpretação é, por que existem frameworks [desconhecidos] na saída do script perf (e como eu transformo esses frameworks [desconhecidos] em nomes significativos)? Esta questão poderia ser sobre “como melhorar o desempenho do meu sistema?” mas eu não vejo dessa maneira neste caso em particular. Existe um problema genuíno aqui sobre como os dados do registro perf foram processados.

A resposta para a pergunta é que, embora a configuração de pré-requisito esteja correta: a versão do nó correta, o argumento correto estava presente para gerar os nomes de function (–perf-basic-prof), o arquivo de mapa perf gerado deve ser de propriedade root para o script perf produzir o resultado esperado.

É isso aí!

Escrevendo alguns novos scripts hoje eu bati em apon isso me direcionando para essa pergunta.

Aqui estão algumas referências adicionais:

https://yunong.io/2015/11/23/generating-node-js-flame-graphs/

https://github.com/jrudolph/perf-map-agent/blob/d8bb58676d3d15eeaaf3ab3f201067e321c77560/bin/create-java-perf-map.sh#L22

[arquivos não-root podem às vezes ser forçados] http://www.spinics.net/lists/linux-perf-users/msg02588.html