Come generare un grafico di chiamata per il codice C ++


87

Sto cercando di generare un grafico di chiamata con il quale scoprire tutti i possibili percorsi di esecuzione che stanno colpendo una particolare funzione (in modo da non dover calcolare manualmente tutti i percorsi, poiché ci sono molti percorsi che portano a questa funzione ). Per esempio:

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

Ho provato Codeviz e Doxygen, in qualche modo entrambi i risultati non mostrano altro che chiamate della funzione di destinazione, D. Nel mio caso, D è una funzione membro di una classe il cui oggetto sarà racchiuso in un puntatore intelligente. I client otterranno sempre l'oggetto puntatore intelligente tramite una factory per richiamare D.

Qualcuno sa come ottenere questo risultato?

Risposte:


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

Poi

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

Restituisce un'immagine brillante (c'è un "nodo esterno", perché mainha un collegamento esterno e potrebbe essere chiamato anche dall'esterno dell'unità di traduzione):

Callgraph

Potresti voler postelaborare questo con c++filt, in modo da poter ottenere i nomi non confusi delle funzioni e delle classi coinvolte. Come nel seguito

#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    

Resa questa bellezza (oh mio, la dimensione senza ottimizzazioni attivate era troppo grande!)

bellezza

Quella funzione mistica senza nome,, Node0x884c4e0è un segnaposto che si presume venga chiamato da qualsiasi funzione la cui definizione non è nota.


22
Hai fatto questo su un progetto multi file? sembra molto bello come strumento
dirvine

2
+1 Per qualche motivo ho dovuto passare l'opzione -n ​​a c ++ filt affinché i nomi venissero distrutti. Ho pensato di menzionarlo qui nel caso in cui qualcun altro dovesse affrontare lo stesso problema.
Aky

1
Ricevo un errore quando provo questo: che Pass::print not implemented for pass: 'Print call graph to 'dot' file'!succede? clang 3.8
Arne

2
Trovato: devo rimuovere l' -analyzeopzione per qualche motivo. Un altro D: posso impostare il nome del file di output su qualcosa di diverso da ./callgraph.dot?
Arne

2
La seconda domanda che ho, come eseguire questo comando per più file in directory diverse?
Newbie

18

Puoi ottenerlo usando doxygen (con l'opzione di usare il punto per la generazione di grafici).

inserisci qui la descrizione dell'immagine

Con Johannes Schaub - litb main.cpp, genera questo:

inserisci qui la descrizione dell'immagine

doxygen / dot sono probabilmente più facili da installare ed eseguire rispetto a clang / opt. Non sono riuscito a installarlo da solo ed è per questo che ho provato a trovare una soluzione alternativa!


1
Potresti aggiungere un esempio di come eseguire doxygen per ottenere la finestra che hai incluso?
nimble_ninja

@nimble_ninja: lo screenshot della finestra di dialogo di configurazione di doxywizard non è sufficiente?
jpo38

1
Non sapevo che provenisse da Doxywizard. Grazie!
nimble_ninja

1
Il miglior metodo di sempre! :)
Leslie N

Non proprio fattibile per un grande progetto, eseguito per 24 ore, gigabyte di documentazione HTML, ancora non fatto .. saltando questo. Ho solo bisogno di chiamare i grafici per alcune funzioni specifiche (l'albero completo da / a / tra main () <=> SQL_COMMIT ()).
Gizmo

9

Calcolare staticamente un grafico delle chiamate C ++ accurato è difficile, perché è necessario un parser di linguaggio preciso, una ricerca del nome corretta e un buon analizzatore di punti che onori adeguatamente la semantica del linguaggio. Doxygen non ha nessuno di questi, non so perché la gente afferma di apprezzarlo per C ++; è facile costruire un esempio C ++ di 10 righe che Doxygen analizza erroneamente).

Potrebbe essere meglio eseguire un profiler di temporizzazione che raccoglie dinamicamente un grafico di chiamata (questo descrive il nostro) ed esercita semplicemente un sacco di casi. Tali profiler ti mostreranno il grafico delle chiamate effettive esercitato.

EDIT: improvvisamente mi sono ricordato di Understand per C ++ , che afferma di costruire grafici di chiamata. Non so cosa usano per un parser o se eseguono correttamente l'analisi dettagliata; Non ho esperienza specifica con il loro prodotto.

Sono impressionato dalla risposta di Schaub, usando Clang; Mi aspetto che Clang abbia tutti gli elementi giusti.


Sfortunatamente non sono a conoscenza di tutti i casi d'uso che possono attivare quella funzione :(. In effetti, il mio obiettivo finale è trovare l'elenco esatto dei casi d'uso che utilizzano quella funzione a scopo di debug. Sono in grado di scoprirlo i chiamanti diretti con lo strumento di indicizzazione del codice, ma hanno bisogno di capire tutti i percorsi di esecuzione per ulteriori analisi.
shiouming

Quindi quello che vuoi veramente è la condizione di esecuzione in cui viene chiamato un metodo? Quindi è necessario un grafico delle chiamate completo e accurato e la capacità di uno strumento di camminare lungo il flusso di controllo in vari nodi nel grafico delle chiamate, raccogliendo espressioni condizionali, fino a quando non si incontra il metodo desiderato. Non conosco alcuno strumento disponibile in commercio che lo farà (questo commento 7 anni dopo la domanda); probabilmente avrai bisogno di un motore di analisi personalizzato per farlo. Clang potrebbe essere premuto in questo; il nostro toolkit DMS potrebbe essere utilizzato per questo.
Ira Baxter

5

Puoi usare CppDepend , può generare molti tipi di grafici

  • Grafico delle dipendenze
  • Chiama grafico
  • Grafico dell'ereditarietà delle classi
  • Grafico di accoppiamento
  • Grafico del percorso
  • Grafico di tutti i percorsi
  • Grafico del ciclo

inserisci qui la descrizione dell'immagine


3

Affinché il clang++comando trovi i file di intestazione standard mpi.h, è necessario utilizzare due opzioni aggiuntive -### -fsyntax-only, ovvero il comando completo dovrebbe avere il seguente aspetto:

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

1

L '"C ++ Bsc Analyzer" può visualizzare i grafici delle chiamate - leggendo il file generato dall'utilità bscmake.


0

doxygen + graphviz potrebbe risolvere la maggior parte dei problemi quando vogliamo generare il grafico delle chiamate, il prossimo passo alla manodopera.


0

Scitools Understand è uno strumento fantastico , migliore di tutto ciò che so per il reverse engineering e genera grafici di alta qualità .

Ma nota che è piuttosto costoso e che la versione di prova ha il suo grafico a farfalla limitato a un solo livello di chiamata (IMHO credo che non si aiutino a farlo ...)

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.