Métodos de análise dinâmica
Aqui, descrevo alguns métodos de análise dinâmica.
Os métodos dinâmicos realmente executam o programa para determinar o gráfico de chamadas.
O oposto dos métodos dinâmicos são os métodos estáticos, que tentam determiná-lo apenas a partir da fonte, sem executar o programa.
Vantagens dos métodos dinâmicos:
- captura ponteiros de função e chamadas virtuais de C ++. Eles estão presentes em grande número em qualquer software não trivial.
Desvantagens dos métodos dinâmicos:
- você tem que executar o programa, que pode ser lento, ou requer uma configuração que você não tem, por exemplo, compilação cruzada
- apenas as funções que foram realmente chamadas serão exibidas. Por exemplo, algumas funções podem ser chamadas ou não dependendo dos argumentos da linha de comando.
KcacheGrind
https://kcachegrind.github.io/html/Home.html
Programa de teste:
int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }
int main(int argc, char **argv) {
int (*f)(int);
f0(1);
f1(1);
f = pointed;
if (argc == 1)
f(1);
if (argc == 2)
not_called(1);
return 0;
}
Uso:
sudo apt-get install -y kcachegrind valgrind
# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c
# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main
# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234
Agora você está dentro de um programa de GUI incrível que contém muitos dados de desempenho interessantes.
No canto inferior direito, selecione a guia "Gráfico de chamadas". Isso mostra um gráfico de chamadas interativo que se correlaciona com as métricas de desempenho em outras janelas conforme você clica nas funções.
Para exportar o gráfico, clique com o botão direito e selecione "Exportar Gráfico". O PNG exportado tem a seguinte aparência:
A partir disso, podemos ver que:
- o nó raiz é
_start
, que é o ponto de entrada ELF real e contém boilerplate de inicialização glibc
f0
, f1
e f2
são chamados conforme o esperado um do outro
pointed
também é mostrado, embora o tenhamos chamado com um ponteiro de função. Ele poderia não ter sido chamado se tivéssemos passado um argumento de linha de comando.
not_called
não é mostrado porque não foi chamado durante a execução, porque não passamos um argumento de linha de comando extra.
A coisa legal sobre valgrind
é que não requer nenhuma opção especial de compilação.
Portanto, você pode usá-lo mesmo que não tenha o código-fonte, apenas o executável.
valgrind
consegue fazer isso executando seu código por meio de uma "máquina virtual" leve. Isso também torna a execução extremamente lenta em comparação com a execução nativa.
Como pode ser visto no gráfico, as informações de tempo sobre cada chamada de função também são obtidas e podem ser usadas para traçar o perfil do programa, que é provavelmente o caso de uso original desta configuração, não apenas para ver os gráficos de chamadas: Como posso traçar o perfil Código C ++ em execução no Linux?
Testado no Ubuntu 18.04.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
adiciona chamadas de retorno , o etrace analisa o arquivo ELF e implementa todos os retornos de chamada.
Não consegui fazer funcionar, mas infelizmente: Por que `-finstrument-functions` não funciona para mim?
A saída reivindicada está no formato:
\-- main
| \-- Crumble_make_apple_crumble
| | \-- Crumble_buy_stuff
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | \-- Crumble_prepare_apples
| | | \-- Crumble_skin_and_dice
| | \-- Crumble_mix
| | \-- Crumble_finalize
| | | \-- Crumble_put
| | | \-- Crumble_put
| | \-- Crumble_cook
| | | \-- Crumble_put
| | | \-- Crumble_bake
Provavelmente o método mais eficiente, além do suporte de rastreamento de hardware específico, mas tem a desvantagem de recompilar o código.