Jak wygenerować wykres wywoływania dla kodu C ++

87

Próbuję wygenerować wykres wywoływania, za pomocą którego znajdę wszystkie możliwe ścieżki wykonania, które trafiają w określoną funkcję (aby nie musieć ręcznie wymyślać wszystkich ścieżek, ponieważ istnieje wiele ścieżek prowadzących do tej funkcji ). Na przykład:

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

Wypróbowałem Codeviz i Doxygen, w jakiś sposób oba wyniki pokazują tylko wywołania funkcji docelowej, D. W moim przypadku D jest funkcją składową klasy, której obiekt zostanie umieszczony w inteligentnym wskaźniku. Klienci zawsze uzyskają obiekt inteligentnego wskaźnika za pośrednictwem fabryki, aby wywołać D.

Czy ktoś wie, jak to osiągnąć?

shiouming
źródło

Odpowiedzi:

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

Następnie

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

Daje błyskotliwy obraz (istnieje „węzeł zewnętrzny”, ponieważ mainma zewnętrzne połączenie i może być wywoływany również spoza tej jednostki tłumaczeniowej):

Callgraph

Możesz chcieć postprocesować to za pomocą c++filt, aby uzyskać niezarządzane nazwy funkcji i klas. Jak poniżej

#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    

Daje to piękno (ojej, rozmiar bez włączonej optymalizacji był za duży!)

Piękno

Ta mistyczna nienazwana funkcja Node0x884c4e0jest symbolem zastępczym, który zakłada wywołanie przez dowolną funkcję, której definicja nie jest znana.

Johannes Schaub - litb
źródło
22
Czy zrobiłeś to w projekcie z wieloma plikami? wygląda bardzo fajnie jako narzędzie
dirvine
2
+1 Z jakiegoś powodu musiałem przekazać opcję -n do c ++ filtrując nazwy do rozplątania. Pomyślałem, że wspomnę o tym tutaj na wypadek, gdyby ktoś inny miał ten sam problem.
Aky
1
Podczas próby pojawia się błąd: o Pass::print not implemented for pass: 'Print call graph to 'dot' file'!co chodzi? brzęk 3.8
Arne
2
Znalazłem: -analyzez jakiegoś powodu muszę usunąć tę opcję. Kolejne pytanie: czy mogę ustawić nazwę pliku wyjściowego na inną niż./callgraph.dot ?
Arne,
2
Drugie pytanie, które mam, jak uruchomić to polecenie dla wielu plików w różnych katalogach?
Nowicjusz
18

Możesz to osiągnąć używając doxygen (z opcją wykorzystania kropki do generowania wykresów).

wprowadź opis obrazu tutaj

Z Johannesem Schaubem - litb main.cpp, generuje to:

wprowadź opis obrazu tutaj

Doxygen / dot są prawdopodobnie łatwiejsze niż clang / opt do zainstalowania i uruchomienia. Sam nie udało mi się go zainstalować i dlatego starałem się znaleźć alternatywne rozwiązanie!

jpo38
źródło
1
Czy możesz dodać przykład, jak uruchomić doxygen, aby uzyskać okno, które umieściłeś?
nimble_ninja
@nimble_ninja: Czy zrzut ekranu z okna konfiguracji doxywizard nie jest wystarczający?
jpo38
1
Nie wiedziałem, że to od doxywizard. Dzięki!
nimble_ninja
1
Najlepsza metoda na świecie! :)
Leslie N
Niezbyt opłacalne dla dużego projektu, działało przez 24 godziny, gigabajty dokumentacji HTML, wciąż nie jest gotowe… pomijam ten. Potrzebuję tylko wykresów połączeń dla kilku konkretnych funkcji (kompletne drzewo do / z / między main () <=> SQL_COMMIT ()).
Gizmo
9

Statyczne obliczanie dokładnego wykresu wywołań C ++ jest trudne, ponieważ potrzebujesz precyzyjnego parsera języka, poprawnego wyszukiwania nazw i dobrego analizatora punktów do zrozumienia, który poprawnie honoruje semantykę języka. Doxygen nie ma żadnego z nich, nie wiem, dlaczego ludzie twierdzą, że lubią go w C ++; łatwo jest zbudować 10 liniowy przykład C ++, który Doxygen błędnie analizuje).

Może lepiej będzie, jeśli uruchomisz profiler czasowy, który dynamicznie zbiera wykres wywołań (to opisuje nasz) i po prostu przećwiczyć wiele przypadków. Takie programy profilujące pokażą Ci rzeczywisty wykres wykonywanych połączeń.

EDYCJA: Nagle przypomniałem sobie, że rozumiem C ++ , które twierdzi, że konstruuje wykresy wywołań. Nie wiem, czego używają do analizatora składni, ani czy dobrze wykonują szczegółową analizę; Nie mam konkretnego doświadczenia z ich produktem.

Jestem pod wrażeniem odpowiedzi Schauba za pomocą Clang; Spodziewałbym się, że Clang ma wszystkie elementy w porządku.

Ira Baxter
źródło
Niestety nie znam wszystkich przypadków użycia, które mogą wywołać tę funkcję :(. W rzeczywistości moim ostatecznym celem jest znalezienie dokładnej listy przypadków użycia, które wykorzystują tę funkcję do celów debugowania. Jestem w stanie się dowiedzieć bezpośrednio dzwoniący z narzędziem do indeksowania kodu, ale muszą znaleźć wszystkie ścieżki wykonania do dalszej analizy.
shiouming
Więc to, czego naprawdę chcesz, to warunek wykonania, w którym wywoływana jest metoda? Następnie potrzebujesz pełnego, dokładnego wykresu wywołań i możliwości narzędzia do przechodzenia przez przepływ sterowania w różnych węzłach wykresu wywołań, zbierając wyrażenia warunkowe, aż do napotkania żądanej metody. Nie znam żadnych gotowych narzędzi, które to zrobią (ten komentarz 7 lat później niż pytanie); prawdopodobnie będziesz potrzebować do tego niestandardowego mechanizmu analizy. Clang może zostać w to wciśnięty; nasz zestaw narzędzi DMS może być do tego wykorzystany.
Ira Baxter
5

Możesz użyć CppDepend , może generować wiele rodzajów wykresów

  • Wykres zależności
  • Call Graph
  • Wykres dziedziczenia klas
  • Wykres sprzężenia
  • Wykres ścieżki
  • Wykres wszystkich ścieżek
  • Wykres cyklu

wprowadź opis obrazu tutaj

Issam
źródło
3

Aby clang++polecenie wyszukało standardowe pliki nagłówkowe, np. mpi.hDwie dodatkowe opcje -### -fsyntax-only, czyli pełne polecenie powinno wyglądać następująco:

clang++ -### -fsyntax-only -S -emit-llvm main1.cpp -o - | opt -analyze -dot-callgraph
mabalenk
źródło
1

„Analizator C ++ Bsc” może wyświetlać wykresy wywołań - czytając plik wygenerowany przez narzędzie bscmake.

Resonantium
źródło
0

Doxygen + graphviz może rozwiązać większość problemów, gdy chcemy wygenerować wykres połączeń, przekazany następnie pracownikom.

Crawl.W
źródło
0

Scitools Understand to fantastyczne narzędzie, lepsze niż wszystko, co wiem o inżynierii odwrotnej , i generuje wysokiej jakości wykresy .

Ale pamiętaj, że jest dość drogi i że wersja próbna ma wykres wywołania motyla ograniczony tylko do jednego poziomu połączenia (IMHO uważam, że nie pomagają sobie w tym…)

franckspike
źródło