Linux – Criando graficos de chamadas C baseado no dump do GCC RTL

ImageEgyptPerlGraphvizCleitonBueno

Recentemente navegando pela internet, vi um post muito legal no blog do Sergio Prado, que pode ser visto em “Gerando um gráfico de dependência de pacotes com o Buildroot“, onde o comando dot que faz parte do Graphviz, foi possível gerar um gráfico com todas as dependências do pacote do processo de build, todo sistema como um todo ou com pacotes isolados, vale a pena conferir o post, pena que não conheci esta ferramenta antes.

Porém, já utilizei uma ferramenta parecida que também utiliza o dot do Graphviz, só que esta gera gráficos das chamadas que o código C realiza, é uma ferramenta já antiga, escrita em Perl por Andreas Gustafsson chamada Egypt. Vamos ver como este cara funciona e como utilizá-lo.

Para fazer o Egypt funcionar, você precisa instalar o graphviz, habilitar o RTL (Register Transfer Language) na compilação do GCC, sendo muito breve, RTL é uma representação intermediaria do código mais próximo ao assembly, e quando compila com esta flag setada é gerado vários arquivos (.c.rtl, c.00.rtl ou c.00.expand) de cada objeto, sendo mais fácil o processamento olhando para este cara que já possui otimização do que o C propriamente dito, do que o código .c propriamente dito.

Então agora, vamos preparar nosso ambiente, abre o terminal e instale os pacotes abaixo:

bueno@vm010:~$ sudo apt-get update && sudo apt-get install graphviz
Ign http://old-releases.ubuntu.com natty-updates/main Translation-en
Lendo listas de pacotes... Pronto
Lendo listas de pacotes... Pronto
Construindo árvore de dependências
Lendo informação de estado... Pronto
Os pacotes extra a seguir serão instalados:
libcdt4 libcgraph5 libgraph4 libgvc5 libgvpr1 libpathplan4
Pacotes sugeridos:
graphviz-doc
Os NOVOS pacotes a seguir serão instalados:
graphviz libcdt4 libcgraph5 libgraph4 libgvc5 libgvpr1 libpathplan4
0 pacotes atualizados, 7 pacotes novos instalados, 0 a serem removidos e 337 não atualizados.
à preciso baixar 1.114 kB de arquivos.
Depois desta operação, 3.297 kB adicionais de espaço em disco serão usados.
Você quer continuar [S/n]? S
...
Selecionando pacote previamente não selecionado libcdt4.
(Lendo banco de dados ... 165053 ficheiros e directórios actualmente instalados.)
Desempacotando libcdt4 (de .../libcdt4_2.26.3-5ubuntu1_i386.deb) ...
Selecionando pacote previamente não selecionado libcgraph5.
Desempacotando libcgraph5 (de .../libcgraph5_2.26.3-5ubuntu1_i386.deb) ...
Selecionando pacote previamente não selecionado libgraph4.
Desempacotando libgraph4 (de .../libgraph4_2.26.3-5ubuntu1_i386.deb) ...
Selecionando pacote previamente não selecionado libpathplan4.
Desempacotando libpathplan4 (de .../libpathplan4_2.26.3-5ubuntu1_i386.deb) ...
Selecionando pacote previamente não selecionado libgvc5.
Desempacotando libgvc5 (de .../libgvc5_2.26.3-5ubuntu1_i386.deb) ...
Selecionando pacote previamente não selecionado libgvpr1.
Desempacotando libgvpr1 (de .../libgvpr1_2.26.3-5ubuntu1_i386.deb) ...
Selecionando pacote previamente não selecionado graphviz.
Desempacotando graphviz (de .../graphviz_2.26.3-5ubuntu1_i386.deb) ...
Processando gatilhos para man-db ...
Configurando libcdt4 (2.26.3-5ubuntu1) ...
Configurando libcgraph5 (2.26.3-5ubuntu1) ...
Configurando libgraph4 (2.26.3-5ubuntu1) ...
Configurando libpathplan4 (2.26.3-5ubuntu1) ...
Configurando libgvc5 (2.26.3-5ubuntu1) ...
Configurando libgvpr1 (2.26.3-5ubuntu1) ...
Configurando graphviz (2.26.3-5ubuntu1) ...
Processando gatilhos para libc-bin ...
ldconfig deferred processing now taking place
bueno@vm010:~$

  Vamos agora baixar o Egypt, descompactar e instalar.

bueno@vm010:~$ cd /tmp/
bueno@vm010:/tmp$ wget http://www.gson.org/egypt/download/egypt-1.10.tar.gz
--2014-12-01 23:42:02-- http://www.gson.org/egypt/download/egypt-1.10.tar.gz
Resolvendo www.gson.org... 46.246.126.150
Conectando-se a www.gson.org|46.246.126.150|:80... conectado.
A requisição HTTP foi enviada, aguardando resposta... 200 OK
Tamanho: 4670 (4,6K) [application/x-tgz]
Salvando em: âegypt-1.10.tar.gzâ

100%[======================================>] 4.670 8,14K/s em 0,6s

2014-12-01 23:42:04 (8,14 KB/s) - âegypt-1.10.tar.gzâ

bueno@vm010:/tmp$ tar -xvzf egypt-1.10.tar.gz
egypt-1.10
egypt-1.10/CHANGES
egypt-1.10/MANIFEST
egypt-1.10/egypt
egypt-1.10/Makefile.PL
egypt-1.10/META.yml
bueno@vm010:/tmp$
bueno@vm010:/tmp$ cd egypt-1.10/
bueno@vm010:/tmp/egypt-1.10$ perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for egypt
bueno@vm010:/tmp/egypt-1.10$ make
cp egypt blib/script/egypt
/usr/bin/perl -MExtUtils::MY -e 'MY->fixin(shift)' -- blib/script/egypt
Manifying blib/man1/egypt.1p
bueno@vm010:/tmp/egypt-1.10$ sudo make install
Installing /usr/local/man/man1/egypt.1p
Installing /usr/local/bin/egypt
Appending installation info to /usr/local/lib/perl/5.10.1/perllocal.pod
bueno@vm010:/tmp/egypt-1.10$

Fácil, não? Agora vamos criar 3 arquivos (main.c, myapp.c e myapp.h) onde irei criar uma aplicação simples com algumas funções e realizar chamadas. Segue o conteúdo de cada arquivo.

bueno@vm010:~/C$ cat main.c
#include <stdio.h>
#include "myapp.h"

int main (void) {

int numero1=5, numero2=9;

imprimir(INICIO);

printf("nnSomando os numeros %d e %d = %dn",numero1,numero2,soma(numero1,numero2));
printf("nnMultiplicando os numeros %d e %d = %.1fn",numero1,numero2,multiplicacao(numero1,numero2));
printf("nnVerificando o maior numero entre %d e %d. Resultado: %dn",numero1,numero2,maior(numero1,numero2));
printf("n");

imprimir(FIM);

return 0;
}

bueno@vm010:~/C$ cat myapp.h
#ifndef _MYAPP_
#define _MYAPP_
#include <stdio.h>

enum msg_t {
INICIO,
FIM
};

int soma(int a, int b);
int maior(int a, int b);
void imprimir(char opcao);
float multiplicacao(int a, int b);
#endif

bueno@vm010:~/C$ cat myapp.c

int soma (int a, int b)
{
return(a+b);
}

int maior (int a, int b)
{
return a > b ? a : b;
}

void imprimir (char opcao)
{
if(!opcao) {
printf("n#################################");
printf("n# O programa esta comecando.... #");
printf("n#################################");
}
else {
printf("n############################");
printf("n# Encerrando o programa... #");
printf("n############################");
}
}

float multiplicacao (int a, int b)
{
return a*b;
}

  Agora vamos adicionar a flag -fdump-rtl-expand ao nosso GCC em versões antigas deve-se adicionar -dr, e vamos compilar nosso projeto.

bueno@vm010:~/C$ gcc -Wall -fdump-rtl-expand -O2 main.c myapp.c -o principal

bueno@vm010:~/C$ ls
main.c  main.c.144r.expand  myapp.c  myapp.c.144r.expand  myapp.h  principal

Podemos ver nossos .c e .h, o principal que é o binário final e os arquivos 144r.expand gerados de cada objeto um do main.o e outro do myapp.o, vamos testar nossa aplicação.

bueno@vm010:~/C$ ./principal

#################################
# O programa esta comecando.... #
#################################

Somando os numeros 5 e 9 = 14

Multiplicando os numeros 5 e 9 = 45.0

Verificando o maior numero entre 5 e 9. Resultado: 9

############################
# Encerrando o programa... #
############################

bueno@vm010:~/C$

  Agora vamos ao código do Egypt, para gerar o gráfico.

bueno@vm010:~/C$ egypt main.c.144r.expand myapp.c.144r.expand | dot -Grankdir=LR -Tsvg -o meuPrograma.svg

  Foi gerado um arquivo meuPrograma.svg, abrindo é para aparecer algo como a Figura 1.

EgyptLeftToRightFigura 1 – Primeiro Grafico com Egypt e Dot

Sobre o comando egypt, é bem padrão, interessante são os parâmetros -Grankdir do dot que pode ser LR (Left to Right), RL (Right to Left) e BT (Bottom to Top) seria a orientação do gráfico gerado, e -T o formato que pode ser png, svg, git e dentre outros.

Isso tudo pode ser visto facilmente digitando man dot no terminal do Linux.

Vamos executar o mesmo comando só que trocando LR por BT, e nosso gráfico ficara como na Figura 2. EgyptTopToBotton

Figura 2 – Gráfico do Egypt com orientação BT (Bottom to Top)

Agora, vou modificar a função imprimir criando mais uma condição e chamar dentro da função soma o imprimir e vamos ver o resultado em código e em gráficos. Alteração dos códigos

bueno@vm010:~/C$ cat myapp.c
#include "myapp.h"

int soma (int a, int b)
{
imprimir(SOMA);
return(a+b);
}

int maior (int a, int b)
{
return a > b ? a : b;
}

void imprimir (char opcao)
{
if(!opcao) {
printf("n#################################");
printf("n# O programa esta comecando.... #");
printf("n#################################");
}
else if (opcao == FIM) {
printf("n############################");
printf("n# Encerrando o programa... #");
printf("n############################");
}
else {
printf("n#######################");
printf("n# Somando, aguarde... #");
printf("n#######################");
}
}

float multiplicacao (int a, int b)
{
return a*b;
}

#ifndef _MYAPP_
#define _MYAPP_
#include <stdio.h>

enum msg_t {
INICIO,
FIM,
SOMA
};

int soma(int a, int b);
int maior(int a, int b);
void imprimir(char opcao);
float multiplicacao(int a, int b);
#endif

  Compilando e testando o código:

bueno@vm010:~/C$ gcc -Wall -fdump-rtl-expand -O2 main.c myapp.c -o principal
bueno@vm010:~/C$ ./principal

#################################
# O programa esta comecando.... #
#################################
#######################
# Somando, aguarde... #
#######################

Somando os números 5 e 9 = 14

Multiplicando os números 5 e 9 = 45.0

Verificando o maior numero entre 5 e 9. Resultado: 9

############################
# Encerrando o programa... #
############################bueno@vm010:~/C$

  Gerando o gráfico novamente.

bueno@vm010:~/C$ egypt main.c.144r.expand myapp.c.144r.expand | dot -Grankdir=LR -Tsvg -o meuPrograma.svg

  Visualizando o gráfico gerado na Figura 3. EgyptTopToBottonAdicionandoImprimirNoSoma

Figura 3 – Gráfico do Egypt com função chamando função

Como podemos ver na Figura 3, agora além do main a função soma() também faz chamada a função imprimir().

Imagina em um projeto grande? Alias, você pode fazer com o projeto inteiro, ou selecionar apenas os objetos dos arquivos que você deseja analisar as chamadas.

Espero que tenham gostado, até a próxima!

Referências

Gerando um gráfico de dependência de pacotes com o Buildroot

http://www.graphviz.org/

http://www.gson.org/egypt/

http://www.gson.org/egypt/egypt.html

Share Button

CC BY-NC-SA 4.0 Linux – Criando graficos de chamadas C baseado no dump do GCC RTL by Cleiton Bueno is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.