Dynamische Analysemethoden
Hier beschreibe ich einige dynamische Analysemethoden.
Dynamische Methoden führen das Programm tatsächlich aus, um das Aufrufdiagramm zu bestimmen.
Das Gegenteil von dynamischen Methoden sind statische Methoden, die versuchen, sie allein aus der Quelle zu ermitteln, ohne das Programm auszuführen.
Vorteile dynamischer Methoden:
- fängt Funktionszeiger und virtuelle C ++ - Aufrufe ab. Diese sind in jeder nicht trivialen Software in großer Anzahl vorhanden.
Nachteile dynamischer Methoden:
- Sie müssen das Programm ausführen, das möglicherweise langsam ist oder ein Setup erfordert, das Sie nicht haben, z. B. Cross-Compilation
- Es werden nur Funktionen angezeigt, die tatsächlich aufgerufen wurden. Beispielsweise können einige Funktionen abhängig von den Befehlszeilenargumenten aufgerufen werden oder nicht.
KcacheGrind
https://kcachegrind.github.io/html/Home.html
Testprogramm:
int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }
int main(int argc, char **argv) {
int (*f)(int);
f0(1);
f1(1);
f = pointed;
if (argc == 1)
f(1);
if (argc == 2)
not_called(1);
return 0;
}
Verwendung:
sudo apt-get install -y kcachegrind valgrind
# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c
# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main
# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234
Sie befinden sich jetzt in einem fantastischen GUI-Programm, das viele interessante Leistungsdaten enthält.
Wählen Sie unten rechts die Registerkarte "Anrufdiagramm". Dies zeigt ein interaktives Anrufdiagramm, das mit Leistungsmetriken in anderen Fenstern korreliert, wenn Sie auf die Funktionen klicken.
Um das Diagramm zu exportieren, klicken Sie mit der rechten Maustaste darauf und wählen Sie "Diagramm exportieren". Das exportierte PNG sieht folgendermaßen aus:
Daraus können wir das ersehen:
- Der Wurzelknoten
_start
ist der eigentliche ELF-Einstiegspunkt und enthält das Glibc-Initialisierungs-Boilerplate
f0
, f1
Und f2
wie erwartet voneinander genannt
pointed
wird auch gezeigt, obwohl wir es mit einem Funktionszeiger aufgerufen haben. Es wurde möglicherweise nicht aufgerufen, wenn wir ein Befehlszeilenargument übergeben hätten.
not_called
wird nicht angezeigt, weil es im Lauf nicht aufgerufen wurde, weil wir kein zusätzliches Befehlszeilenargument übergeben haben.
Das Coole daran valgrind
ist, dass keine speziellen Kompilierungsoptionen erforderlich sind.
Daher können Sie es auch dann verwenden, wenn Sie nicht über den Quellcode, sondern nur über die ausführbare Datei verfügen.
valgrind
Dies gelingt Ihnen, indem Sie Ihren Code über eine einfache "virtuelle Maschine" ausführen. Dies macht die Ausführung im Vergleich zur nativen Ausführung extrem langsam.
Wie in der Grafik zu sehen ist, werden auch Zeitinformationen zu jedem Funktionsaufruf abgerufen. Diese können verwendet werden, um das Programm zu profilieren, was wahrscheinlich der ursprüngliche Anwendungsfall dieses Setups ist, und nicht nur, um Aufrufdiagramme anzuzeigen: Wie kann ich ein Profil erstellen? C ++ - Code unter Linux?
Getestet unter Ubuntu 18.04.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
Fügt Rückrufe hinzu , etrace analysiert die ELF-Datei und implementiert alle Rückrufe.
Ich konnte es jedoch leider nicht zum Laufen bringen : Warum funktionieren `-finstrument-Funktionen` bei mir nicht?
Die behauptete Ausgabe hat das Format:
\-- main
| \-- Crumble_make_apple_crumble
| | \-- Crumble_buy_stuff
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | \-- Crumble_prepare_apples
| | | \-- Crumble_skin_and_dice
| | \-- Crumble_mix
| | \-- Crumble_finalize
| | | \-- Crumble_put
| | | \-- Crumble_put
| | \-- Crumble_cook
| | | \-- Crumble_put
| | | \-- Crumble_bake
Wahrscheinlich die effizienteste Methode neben der Unterstützung spezifischer Hardware-Ablaufverfolgung, hat jedoch den Nachteil, dass Sie den Code neu kompilieren müssen.