Como gerar um gráfico de chamada para código C ++

87

Estou tentando gerar um gráfico de chamada com o qual descobrir todos os caminhos de execução possíveis que estão atingindo uma função específica (para que eu não tenha que descobrir todos os caminhos manualmente, pois há muitos caminhos que levam a esta função ) Por exemplo:

path 1: A -> B -> C -> D  
path 2: A -> B -> X -> Y -> D  
path 3: A -> G -> M -> N -> O -> P -> S -> D  
...  
path n: ...

Eu tentei Codeviz e Doxygen, de alguma forma, ambos os resultados mostram nada além de chamadas de função de destino, D. No meu caso, D é uma função de membro de uma classe cujo objeto será envolvido em um ponteiro inteligente. Os clientes sempre obterão o objeto de ponteiro inteligente por meio de uma fábrica para invocar D.

Alguém sabe como alcançar isso?

shiouming
fonte

Respostas:

118
static void D() { }
static void Y() { D(); }
static void X() { Y(); }
static void C() { D(); X(); }
static void B() { C(); }
static void S() { D(); }
static void P() { S(); }
static void O() { P(); }
static void N() { O(); }
static void M() { N(); }
static void G() { M(); }
static void A() { B(); G(); }

int main() {
  A();
}

Então

$ clang++ -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
$ dot -Tpng -ocallgraph.png callgraph.dot

Rende uma imagem brilhante (há um "nó externo", porque maintem ligação externa e pode ser chamado de fora dessa unidade de tradução também):

Callgraph

Você pode querer pós-processar isso com c++filt, para que possa obter os nomes não mutáveis ​​das funções e classes envolvidas. Como no seguinte

#include <vector>

struct A { 
  A(int);
  void f(); // not defined, prevents inlining it!
};

int main() {
  std::vector<A> v;
  v.push_back(42);
  v[0].f();
}

$ clang++ -S -emit-llvm main1.cpp -o - |
   opt -analyze -std-link-opts -dot-callgraph
$ cat callgraph.dot | 
   c++filt | 
   sed 's,>,\\>,g; s,-\\>,->,g; s,<,\\<,g' | 
   gawk '/external node/{id=$1} $1 != id' | 
   dot -Tpng -ocallgraph.png    

Rende essa beleza (nossa, o tamanho sem otimizações ativadas era muito grande!)

Beleza

Essa função mística sem nome,, Node0x884c4e0é um marcador de posição considerado como sendo chamado por qualquer função cuja definição não seja conhecida.

Johannes Schaub - litb
fonte
22
Você fez isso em um projeto de vários arquivos? parece muito legal como uma ferramenta
dirvine
2
+1 Por algum motivo, tive que passar a opção -n para c ++ filt para que os nomes fossem desmontados. Pensei em mencioná-lo aqui, caso mais alguém enfrente o mesmo problema.
Aky
1
Recebo um erro ao tentar isso: Pass::print not implemented for pass: 'Print call graph to 'dot' file'!O que há com isso? clang 3.8
Arne
2
Encontrei: tenho que remover a -analyzeopção por algum motivo. Outro P: posso definir o nome do arquivo de saída para algo diferente de ./callgraph.dot?
Arne,
2
A segunda pergunta que tenho, como executar este comando para vários arquivos em diretórios diferentes?
Novato
18

Você pode conseguir isso usando doxygen (com a opção de usar ponto para geração de gráficos).

insira a descrição da imagem aqui

Com Johannes Schaub - litb main.cpp, ele gera este:

insira a descrição da imagem aqui

doxygen / dot é provavelmente mais fácil do que clang / opt para instalar e executar. Não consegui instalar sozinho e por isso tentei encontrar uma solução alternativa!

jpo38
fonte
1
Você poderia adicionar um exemplo de como executar o doxygen para obter a janela incluída?
nimble_ninja
@nimble_ninja: A captura de tela da caixa de diálogo de configuração do doxywizard não é suficiente?
jpo38
1
Eu não sabia que era de doxywizard. Obrigado!
nimble_ninja
1
Melhor método de todos! :)
Leslie N
Realmente não é viável para um grande projeto, rodou por 24H, gigabytes de documentação HTML, ainda não pronto .. pulando este. Eu só preciso de gráficos de chamadas para algumas funções específicas (a árvore completa para / de / entre main () <=> SQL_COMMIT ()).
Gizmo
9

É difícil calcular estaticamente um gráfico de chamadas C ++ preciso, porque você precisa de um analisador de idioma preciso, pesquisa de nome correto e um bom analisador de pontos que respeite a semântica do idioma de maneira adequada. O Doxygen não tem nenhum desses, não sei por que as pessoas afirmam gostar dele para C ++; é fácil construir um exemplo C ++ de 10 linhas que o Doxygen analisa erroneamente).

Você pode ser melhor executando um criador de perfil de tempo que coleta um gráfico de chamadas dinamicamente (este descreve o nosso) e simplesmente exercita muitos casos. Esses perfis mostrarão o gráfico de chamadas real exercido.

EDIT: De repente, me lembrei do Understand for C ++ , que afirma construir gráficos de chamadas. Não sei o que eles usam como analisador, ou se fazem a análise detalhada corretamente; Não tenho experiência específica com seu produto.

Estou impressionado com a resposta de Schaub, usando Clang; Eu esperaria que o Clang tivesse todos os elementos corretos.

Ira Baxter
fonte
Infelizmente, não estou ciente de todos os casos de uso que podem acionar essa função :(. Na verdade, meu objetivo final é descobrir a lista exata de casos de uso que utilizam essa função para fins de depuração. Consigo descobrir os chamadores diretos com a ferramenta de indexação de código, mas precisam descobrir todos os caminhos de execução para análise posterior.
shiouming
Então, o que você realmente deseja é a condição de execução sob a qual um método é chamado? Em seguida, você precisa de um gráfico de chamadas completo e preciso e da capacidade de uma ferramenta para percorrer o fluxo de controle em vários nós no gráfico de chamadas, coletando expressões condicionais, até que o método desejado seja encontrado. Não conheço nenhuma ferramenta pronta para uso que faça isso (este comentário 7 anos depois da pergunta); você provavelmente precisará de um mecanismo de análise customizado para fazer isso. O Clang pode ser pressionado para isso; nosso kit de ferramentas DMS pode ser usado para isso.
Ira Baxter
5

Você pode usar CppDepend , ele pode gerar muitos tipos de gráficos

  • Gráfico de Dependência
  • Gráfico de chamadas
  • Gráfico de herança de classe
  • Gráfico de acoplamento
  • Gráfico de caminho
  • Gráfico de todos os caminhos
  • Gráfico de Ciclo

insira a descrição da imagem aqui

Issam
fonte
3

Para que o clang++comando encontre arquivos de cabeçalho padrão, mpi.hduas opções adicionais devem ser usadas -### -fsyntax-only, ou seja, o comando completo deve ser semelhante a:

clang++ -### -fsyntax-only -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
mabalenk
fonte
1

O "C ++ Bsc Analyzer" pode exibir gráficos de chamadas - lendo o arquivo gerado pelo utilitário bscmake.

Resonantium
fonte
0

doxygen + graphviz pode resolver a maioria dos problemas quando queremos gerar o gráfico de chamadas, próximo à mão de obra.

Crawl.W
fonte
0

Scitools Understand é uma ferramenta fantástica , melhor do que tudo que eu conheço para engenharia reversa , e gera gráficos de alta qualidade .

Mas observe que é muito caro e que a versão de teste tem seu gráfico de chamada de borboleta limitado a apenas um nível de chamada (IMHO, eu acredito que eles não se ajudam a fazer isso ...)

Franckspike
fonte