Metody analizy dynamicznej
Tutaj opiszę kilka metod analizy dynamicznej.
Metody dynamiczne faktycznie uruchamiają program w celu określenia wykresu wywołania.
Przeciwieństwem metod dynamicznych są metody statyczne, które próbują określić je z samego źródła, bez uruchamiania programu.
Zalety metod dynamicznych:
- przechwytuje wskaźniki funkcji i wirtualne wywołania C ++. Są one obecne w dużych ilościach w każdym nietrywialnym oprogramowaniu.
Wady metod dynamicznych:
- musisz uruchomić program, który może być powolny lub wymagać instalacji, której nie masz, np. kompilacji krzyżowej
- pokażą się tylko funkcje, które zostały faktycznie wywołane. Np. Niektóre funkcje można wywołać lub nie, w zależności od argumentów wiersza poleceń.
KcacheGrind
https://kcachegrind.github.io/html/Home.html
Program testowy:
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;
}
Stosowanie:
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
Jesteś teraz w niesamowitym programie GUI, który zawiera wiele interesujących danych dotyczących wydajności.
W prawym dolnym rogu wybierz kartę „Wykres połączeń”. Pokazuje interaktywny wykres wywołań, który jest skorelowany z metrykami wydajności w innych oknach po kliknięciu funkcji.
Aby wyeksportować wykres, kliknij go prawym przyciskiem myszy i wybierz „Eksportuj wykres”. Wyeksportowany plik PNG wygląda następująco:
Z tego widać, że:
- węzeł główny to
_start
, który jest faktycznym punktem wejścia ELF i zawiera konfigurację standardową biblioteki glibc
f0
, f1
I f2
są nazywane zgodnie z oczekiwaniami od siebie
pointed
jest również wyświetlany, mimo że wywołaliśmy go wskaźnikiem funkcji. Mogłoby nie zostać wywołane, gdybyśmy przekazali argument wiersza poleceń.
not_called
nie jest wyświetlany, ponieważ nie został wywołany w przebiegu, ponieważ nie przekazaliśmy dodatkowego argumentu wiersza poleceń.
Fajne valgrind
jest to, że nie wymaga żadnych specjalnych opcji kompilacji.
Dlatego możesz go użyć, nawet jeśli nie masz kodu źródłowego, tylko plik wykonywalny.
valgrind
udaje się to zrobić, uruchamiając kod za pośrednictwem lekkiej „maszyny wirtualnej”. Powoduje to również, że wykonywanie jest wyjątkowo wolne w porównaniu do wykonywania natywnego.
Jak widać na wykresie, uzyskiwane są również informacje o czasie każdego wywołania funkcji, które można wykorzystać do profilowania programu, co jest prawdopodobnie oryginalnym przypadkiem użycia tej konfiguracji, a nie tylko do wyświetlania wykresów połączeń: Jak mogę profilować Kod C ++ działający w systemie Linux?
Testowane na Ubuntu 18.04.
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
dodaje wywołania zwrotne , etrace analizuje plik ELF i implementuje wszystkie wywołania zwrotne.
Niestety, nie udało mi się go uruchomić: Dlaczego `-finstrument-functions` nie działa dla mnie?
Zgłoszone dane wyjściowe mają 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
Prawdopodobnie najbardziej wydajna metoda poza obsługą specyficznego śledzenia sprzętu, ale ma tę wadę, że trzeba ponownie skompilować kod.