web-dev-qa-db-fra.com

Comment générer un graphe d'appel pour du code C++

J'essaie de générer un graphe d'appel avec lequel trouver tous les chemins d'exécution possibles qui remplissent une fonction particulière (afin de ne pas devoir trouver tous les chemins manuellement, car de nombreux chemins mènent à cette fonction. ). Par exemple:

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: ...

J'ai essayé Codeviz et Doxygen, les deux résultats ne montrant que des requêtes de fonction cible, D. Dans mon cas, D est une fonction membre d'une classe dont l'objet sera encapsulé dans un pointeur intelligent. Les clients obtiendront toujours l'objet pointeur intelligent via une fabrique afin d'appeler D.

Est-ce que quelqu'un sait comment y parvenir?

73
shiouming
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();
}

Ensuite

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

Donne une image brillante (il existe un "noeud externe", car main a un lien externe et peut également être appelé de l'extérieur de cette unité de traduction):

Callgraph

Vous voudrez peut-être post-traiter ceci avec c++filt, de manière à pouvoir obtenir les noms non mélangés des fonctions et des classes impliquées. Comme dans ce qui suit

#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    

Donne cette beauté (oh mon dieu, la taille sans optimisations était trop grande!)

Beauty

Cette fonction mystique non nommée, Node0x884c4e0, est un espace réservé supposé être appelé par une fonction dont la définition n'est pas connue.

103

Vous pouvez y parvenir en utilisant doxygen (avec l'option d'utiliser point pour la génération de graphiques).

 enter image description here

Avec Johannes Schaub - litb main.cpp, il génère ceci:

 enter image description here

doxygen/dot sont probablement plus faciles que clang/opt pour installer et exécuter. Je n'ai pas réussi à l'installer moi-même et c'est pourquoi j'ai essayé de trouver une solution alternative!

13
jpo38

Calculer de manière statique un graphe d'appels C++ précis est difficile, car vous avez besoin d'un analyseur syntaxique précis, d'une recherche de nom correcte et d'un bon analyseur point par point qui respecte correctement la sémantique de la langue. Doxygen n’en possède aucun, je ne sais pas pourquoi les gens prétendent l’aimer pour C++; il est facile de construire un exemple C++ de 10 lignes que Doxygen analyse par erreur).

Vous feriez mieux d’utiliser un profileur de synchronisation qui collecte un graphe d’appel de manière dynamique (ceci décrit le nôtre) et exerce simplement beaucoup de cas. Ces profileurs vous montreront le graphe d’appel réel exercé.

EDIT: Je me suis soudainement rappelé Comprendre pour C++ , qui prétend construire des graphes d’appel. Je ne sais pas ce qu'ils utilisent pour un analyseur syntaxique, ou s'ils font l'analyse détaillée correctement. Je n'ai aucune expérience spécifique avec leur produit. 

Je suis impressionné par la réponse de Schaub, en utilisant Clang; Je m'attendrais à ce que Clang dispose de tous les éléments.

6
Ira Baxter

Vous pouvez utiliser CppDepend , il peut générer de nombreux types de graphiques.

  • Graphique de dépendance
  • Graphique d'appel
  • Graphe d'héritage de classe
  • Graphique de couplage
  • Graphe de chemin
  • Graphique de tous les chemins
  • Graphique de cycle

 enter image description here

5
Issam

Pour que la commande clang++ puisse trouver des fichiers d’en-tête standard tels que mpi.h, deux options supplémentaires doivent être utilisées -### -fsyntax-only, c’est-à-dire que la commande complète doit ressembler à:

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

"C++ Bsc Analyzer" peut afficher des graphiques d'appel en lisant le fichier généré par l'utilitaire bscmake.

1
Resonantium

doxygen + graphviz pourrait résoudre la plupart des problèmes lorsque nous voulons générer un graphe d’appel, ensuite remis à la main-d’œuvre.

0
Crawl.W

Scitools Comprendre est un outil fantastique , meilleur que tout ce que je sais pour le reverse engineering , et génère des graphes de haute qualité .

Mais notez que cela coûte assez cher et que la version d’essai a son graphe d’appel de papillons limité à un seul niveau d’appel (à mon humble avis, ils ne s’aident pas à le faire…)

0
franckspike