Comment générer un graphe appelant pour le code C ++

87

J'essaie de générer un graphe d'appel avec lequel découvrir tous les chemins d'exécution possibles qui frappent une fonction particulière (pour ne pas avoir à comprendre tous les chemins manuellement, car il existe de nombreux chemins qui 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, d'une manière ou d'une autre, les deux résultats ne montrent rien d'autre que des noms de fonction cible, D. Dans mon cas, D est une fonction membre d'une classe dont l'objet sera enveloppé 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?

shiouming
la source

Réponses:

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

ensuite

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

Donne une image brillante (il y a un "nœud externe", car il maina un lien externe et peut également être appelé de l'extérieur de cette unité de traduction):

Callgraph

Vous souhaiterez peut-être post-traiter cela avec c++filt, afin de 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 activées était trop grande!)

Beauté

Cette fonction mystique sans nom Node0x884c4e0, est un espace réservé supposé être appelé par toute fonction dont la définition n'est pas connue.

Johannes Schaub - litb
la source
22
Avez-vous fait cela sur un projet multi-fichiers? semble très cool comme un outil
dirvine
2
+1 Pour une raison quelconque, j'ai dû passer l'option -n à c ++ filt pour que les noms soient démêlés. Je pensais que je le mentionnerais ici au cas où quelqu'un d'autre serait confronté au même problème.
Aky
1
J'obtiens une erreur en essayant ceci: Pass::print not implemented for pass: 'Print call graph to 'dot' file'!Qu'est-ce qui se passe avec ça? clang 3.8
Arne
2
Trouvé: je dois supprimer l' -analyzeoption pour une raison quelconque. Un autre Q: puis-je définir le nom du fichier de sortie sur autre chose que ./callgraph.dot?
Arne du
2
La deuxième question que je me pose, comment exécuter cette commande pour plusieurs fichiers dans différents répertoires?
Débutant
18

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

entrez la description de l'image ici

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

entrez la description de l'image ici

doxygen / dot sont probablement plus faciles que clang / opt à 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!

jpo38
la source
1
Pourriez-vous ajouter un exemple de la façon d'exécuter doxygen pour obtenir la fenêtre que vous avez incluse?
nimble_ninja
@nimble_ninja: La capture d'écran de la boîte de dialogue de configuration de doxywizard n'est-elle pas suffisante?
jpo38
1
Je ne savais pas que c'était de doxywizard. Merci!
nimble_ninja
1
Meilleure méthode jamais! :)
Leslie N
Pas vraiment viable pour un gros projet, a fonctionné pendant 24H, gigaoctets de documentation HTML, toujours pas fait .. sauter celui-ci. J'ai juste besoin de graphes d'appels pour quelques fonctions spécifiques (l'arbre complet vers / depuis / entre main () <=> SQL_COMMIT ()).
Gizmo
9

Le calcul statique d'un graphe d'appel C ++ précis est difficile, car vous avez besoin d'un analyseur de langage précis, d'une recherche de nom correcte et d'un bon analyseur de points à respecter qui honore correctement la sémantique du langage. Doxygen n'en a 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 peut-être mieux d'exécuter un profileur de synchronisation qui collecte un graphe d'appel de manière dynamique (cela décrit le nôtre) et d'exercer simplement de nombreux cas. Ces profileurs vous montreront le graphique d'appel réel exercé.

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

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

Ira Baxter
la source
Malheureusement, je ne connais pas tous les cas d'utilisation qui peuvent déclencher cette fonction :(. En fait, mon objectif ultime est de trouver la liste exacte des cas d'utilisation qui utilisent cette fonction à des fins de débogage. Je suis en mesure de le découvrir. les appelants directs avec l'outil d'indexation de code, mais doivent comprendre tous les chemins d'exécution pour une analyse plus approfondie.
shiouming
Donc ce que vous voulez vraiment, c'est la condition d'exécution sous laquelle une méthode est appelée? Ensuite, vous avez besoin d'un graphe d'appel complet et précis et de la capacité d'un outil pour parcourir le flux de contrôle dans divers nœuds du graphe d'appel, en collectant des expressions conditionnelles, jusqu'à ce que la méthode souhaitée soit rencontrée. Je ne connais aucun outil standard qui fera cela (ce commentaire 7 ans plus tard que la question); vous aurez probablement besoin d'un moteur d'analyse personnalisé pour ce faire. Clang pourrait être pressé dans cela; notre boîte à outils DMS pourrait être utilisée pour cela.
Ira Baxter
5

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

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

entrez la description de l'image ici

Issam
la source
3

Pour que la clang++commande trouve des fichiers d'en-tête standard comme mpi.hdeux 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
Mabalenk
la source
1

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

Résonantium
la source
0

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

Crawl.W
la source
0

Scitools Understanding est un outil fantastique , meilleur que tout ce que je connais pour la rétro-ingénierie , et génère des graphiques de haute qualité .

Mais notez que c'est assez cher et que la version d'essai a son graphique d'appel papillon limité à un seul niveau d'appel (à mon humble avis, je crois qu'ils ne s'aident pas à le faire ...)

franckspike
la source