So generieren Sie ein aufrufendes Diagramm für C ++ - Code

86

Ich versuche, ein aufrufendes Diagramm zu generieren, mit dem alle möglichen Ausführungspfade ermittelt werden können, die auf eine bestimmte Funktion treffen (damit ich nicht alle Pfade manuell ermitteln muss, da es viele Pfade gibt, die zu dieser Funktion führen ). Zum Beispiel:

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

Ich habe Codeviz und Doxygen ausprobiert. Irgendwie zeigen beide Ergebnisse nur Callees der Zielfunktion D. In meinem Fall ist D eine Mitgliedsfunktion einer Klasse, deren Objekt in einen intelligenten Zeiger eingeschlossen wird. Clients erhalten das Smart-Pointer-Objekt immer über eine Factory, um D aufzurufen.

Weiß jemand, wie man das erreicht?

shiouming
quelle

Antworten:

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

Dann

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

Ergibt ein glänzendes Bild (es gibt einen "externen Knoten", da er maineine externe Verknüpfung hat und möglicherweise auch von außerhalb dieser Übersetzungseinheit aufgerufen wird):

Callgraph

Möglicherweise möchten Sie dies mit nachbearbeiten c++filt, damit Sie die entwirrten Namen der beteiligten Funktionen und Klassen erhalten. Wie im Folgenden

#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    

Ergibt diese Schönheit (oh mein Gott, die Größe ohne aktivierte Optimierungen war zu groß!)

Schönheit

Diese mystische unbenannte Funktion Node0x884c4e0ist ein Platzhalter, von dem angenommen wird, dass er von einer Funktion aufgerufen wird, deren Definition nicht bekannt ist.

Johannes Schaub - litb
quelle
22
Haben Sie dies bei einem Projekt mit mehreren Dateien getan? sieht als Werkzeug sehr cool aus
Dirvine
2
+1 Aus irgendeinem Grund musste ich die Option -n an c ++ filt übergeben, damit sich die Namen entwirren. Ich dachte, ich würde es hier erwähnen, falls jemand anderes vor dem gleichen Problem steht.
Aky
1
Ich erhalte eine Fehlermeldung, wenn ich Folgendes versuche: Pass::print not implemented for pass: 'Print call graph to 'dot' file'!Was ist damit los? Clang 3.8
Arne
2
Gefunden: Ich muss die -analyzeOption aus irgendeinem Grund entfernen . Noch ein F: Kann ich den Ausgabedateinamen auf etwas anderes als setzen ./callgraph.dot?
Arne
2
Die zweite Frage, die ich habe, wie man diesen Befehl für mehrere Dateien in verschiedenen Verzeichnissen ausführt?
Neuling
18

Sie können dies erreichen, indem Sie Sauerstoff verwenden (mit der Option, Punkt für die Diagrammgenerierung zu verwenden).

Geben Sie hier die Bildbeschreibung ein

Mit Johannes Schaub - litb main.cpp wird Folgendes generiert:

Geben Sie hier die Bildbeschreibung ein

doxygen / dot sind wahrscheinlich einfacher zu installieren und auszuführen als clang / opt. Ich habe es nicht geschafft, es selbst zu installieren, und deshalb habe ich versucht, eine alternative Lösung zu finden!

jpo38
quelle
1
Können Sie ein Beispiel hinzufügen, wie doxygen ausgeführt wird, um das von Ihnen eingeschlossene Fenster zu erhalten?
nimble_ninja
@nimble_ninja: Reicht der Screenshot aus dem Doxywizard-Konfigurationsdialog nicht aus?
jpo38
1
Ich wusste nicht, dass es von Doxywizard war. Vielen Dank!
nimble_ninja
1
Beste Methode aller Zeiten! :)
Leslie N
8

Die statische Berechnung eines genauen C ++ - Aufrufdiagramms ist schwierig, da Sie einen präzisen Sprachparser, eine korrekte Namenssuche und einen guten Punkteanalysator benötigen, der die Sprachsemantik richtig berücksichtigt. Doxygen hat keine davon, ich weiß nicht, warum die Leute behaupten, es für C ++ zu mögen; Es ist einfach, ein 10-Zeilen-C ++ - Beispiel zu erstellen, das Doxygen fälschlicherweise analysiert.

Möglicherweise ist es besser, einen Timing-Profiler auszuführen, der ein Anrufdiagramm dynamisch erfasst (dies beschreibt unser), und einfach viele Fälle auszuführen. Solche Profiler zeigen Ihnen das tatsächlich ausgeübte Anrufdiagramm.

EDIT: Ich erinnerte mich plötzlich an Understand for C ++ , das behauptet, Aufrufgraphen zu erstellen. Ich weiß nicht, was sie für einen Parser verwenden oder ob sie die detaillierte Analyse richtig machen. Ich habe keine besonderen Erfahrungen mit ihrem Produkt.

Ich bin beeindruckt von Schaubs Antwort mit Clang; Ich würde erwarten, dass Clang alle Elemente richtig hat.

Ira Baxter
quelle
Leider sind mir nicht alle Anwendungsfälle bekannt, die diese Funktion auslösen können: (Tatsächlich ist es mein oberstes Ziel, die genaue Liste der Anwendungsfälle herauszufinden, die diese Funktion zum Debuggen verwenden. Ich kann es herausfinden die direkten Anrufer mit Code-Indizierungs-Tool, müssen aber alle Ausführungspfade für die weitere Analyse
herausfinden
Was Sie also wirklich wollen, ist die Ausführungsbedingung, unter der eine Methode aufgerufen wird? Dann benötigen Sie ein vollständiges, genaues Anrufdiagramm und die Fähigkeit eines Werkzeugs, entlang des Kontrollflusses in verschiedenen Knoten im Anrufdiagramm zu gehen und bedingte Ausdrücke zu sammeln, bis die gewünschte Methode gefunden wird. Ich kenne keine Standardwerkzeuge, die dies tun (dieser Kommentar 7 Jahre später als die Frage); Dazu benötigen Sie wahrscheinlich eine benutzerdefinierte Analyse-Engine. Clang könnte dazu gedrängt werden; Hierfür könnte unser DMS-Toolkit verwendet werden.
Ira Baxter
5

Sie können CppDepend verwenden , es kann viele Arten von Diagrammen erzeugen

  • Abhängigkeitsdiagramm
  • Graph aufrufen
  • Klassenvererbungsdiagramm
  • Kopplungsdiagramm
  • Pfaddiagramm
  • Alle Pfade Grafik
  • Zyklusdiagramm

Geben Sie hier die Bildbeschreibung ein

Issam
quelle
3

Damit der clang++Befehl Standard-Header-Dateien findet mpi.h, sollten zwei zusätzliche Optionen verwendet werden -### -fsyntax-only, dh der vollständige Befehl sollte wie folgt aussehen:

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

Der "C ++ Bsc Analyzer" kann Anrufdiagramme anzeigen - durch Lesen der vom Dienstprogramm bscmake generierten Datei.

Resonantium
quelle
0

doxygen + graphviz könnte die meisten Probleme lösen, wenn wir ein Anrufdiagramm erstellen möchten , das als nächstes an die Arbeitskräfte übergeben wird.

Crawl.W
quelle
0

Scitools Understand ist ein fantastisches Tool, besser als alles, was ich für das Reverse Engineering weiß , und generiert hochwertige Grafiken .

Beachten Sie jedoch, dass es ziemlich teuer ist und dass in der Testversion das Schmetterlings-Anrufdiagramm auf nur eine Anrufstufe beschränkt ist (IMHO glaube ich, dass sie sich dabei nicht selbst helfen…).

franckspike
quelle