Pobieranie wykresu przepływu sterowania z kodu ANSI C

Buduję narzędzie do testowania aplikacji ansi C. Wystarczy załadować kod, wyświetlić wykres przepływu sterowania, uruchomić test, zaznaczyć wszystkie wierzchołki, które zostały trafione. Próbuję zbudować CFG sam z parsowania kodu. Niestety, jeśli kod jest zagnieżdżony, to się psuje. GCC daje możliwość pobierania CFG ze skompilowanego kodu. Mogę napisać parser dla jego wyjścia, ale potrzebuję numerów linii do ustawiania punktów przerwania. Czy istnieje sposób na uzyskanie numerów linii podczas wyprowadzania wykresu przepływu sterowania za pomocą -fdump-tree-cfg lub -fdump-tree-vcg?

Author: s.dallapalma, 2013-05-06

3 answers

Dla wykresu przepływu sterowania programu C można spojrzeć na istniejące parsery Pythona dla C:

Wykresy Wywołania są ściśle powiązane z wykresami przepływu sterowania. Istnieje kilka metod tworzenia wykresów połączeń (zależności funkcyjne) dla kodu C. Może to okazać się pomocne w postępie z generowaniem wykresu przepływu sterowania. Sposoby tworzenia wykresów zależności W C:

Następujące narzędzia niestety wymagają, aby kod może być kompilowany, ponieważ zależy od wyjścia z gcc:

  • [118]} CodeViz (GPL v2) (słaby punkt: potrzebuje kompilowalnego źródła, ponieważ używa gcc do zrzutu plików cdepn)
  • gcc +egypt +dot (GPL v*, Perl = GPL / Artistic license, EPL v1) (egypt używa {[2] } do produkcji RTL, więc nie działa na żaden błędny kod źródłowy, a nawet jeśli chcesz skupić się na pojedynczym pliku z większego projektu. Dlatego nie jest to bardzo przydatne w porównaniu z bardziej solidne łańcuchy narzędzi oparte na cflow. Zauważ, że Egipt ma domyślnie dobrą obsługę wyłączania wywołań bibliotek z wykresu, aby uczynić go czystszym.

Również wykresy zależności plików dla C / C++ mogą być tworzone za pomocą crowfood.

 15
Author: Ioannis Filippidis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-05-03 19:40:50

Więc zrobiłem trochę więcej badań i nie jest trudno uzyskać numery linii dla węzłów. Wystarczy dodać opcję lineno do jednej z tych opcji, aby ją uzyskać. Więc użyj -fdump-tree-cfg-lineno lub -fdump-tree-vcg-lineno. Zajęło mi trochę czasu, aby sprawdzić, czy te liczby są wiarygodne . W przypadku wykresu w formacie VCG Etykieta każdego węzła zawiera dwie liczby. Są to numery linii dla początku i końca części kodu reprezentowanej przez ten węzeł.

 7
Author: Eloar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-09 06:15:52

Metody analizy dynamicznej

W tej odpowiedzi opiszę kilka metod analizy dynamicznej.

Dynamiczne metody 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:

  • łapie Wskaźniki funkcji i wirtualne wywołania C++. Są one obecne w dużych ilościach w każdym nietrywialne oprogramowanie.

Wady metod dynamicznych:

  • musisz uruchomić program, który może być powolny lub wymagać konfiguracji, której nie masz, np. cross-compilation
  • pokażą się tylko funkcje, które zostały faktycznie wywołane. Na przykład, niektóre funkcje mogą być wywoływane lub nie w zależności od argumentów wiersza poleceń.

KcacheGrind

Https://kcachegrind.github.io/html/Home.html

Test program:

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

Użycie:

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

Zostajesz teraz w niesamowitym programie graficznym, który zawiera wiele interesujących danych o wydajności.

W prawym dolnym rogu wybierz zakładkę "Call graph". To pokazuje interaktywny wykres połączeń, który koreluje z metrykami wydajności w innych oknach podczas klikania funkcji.

Aby wyeksportować Wykres, kliknij go prawym przyciskiem myszy i wybierz "Eksportuj Wykres". Wyeksportowany PNG wygląda jak to:

Z tego widać, że:

    Jest to węzeł główny
  • , który jest faktycznym punktem wejścia ELF i zawiera kocioł inicjalizacji glibc
  • f0, f1 i f2 są wywoływane zgodnie z oczekiwaniami od siebie
  • pointed jest również pokazana, mimo że nazwaliśmy ją wskaźnikiem funkcji. Może nie zostać wywołana, gdybyśmy przekazali argument wiersza poleceń.
  • not_called nie jest wyświetlany, ponieważ nie został wywołany w uruchomieniu, ponieważ nie przekazaliśmy dodatkowego argumentu wiersza poleceń.

Najfajniejsze w valgrind jest to, że nie wymaga żadnych specjalnych opcji kompilacji.

Dlatego możesz go używać nawet jeśli nie masz kodu źródłowego, tylko wykonywalnego.

valgrind udaje się to zrobić, uruchamiając kod za pomocą lekkiej "maszyny wirtualnej".

Testowane na Ubuntu 18.04.

gcc -finstrument-functions + etrace

Https://github.com/elcritch/etrace

-finstrument-functions dodaje wywołania zwrotne , etrace przetwarza plik ELF i implementuje wszystkie wywołania zwrotne.

Niestety nie udało mi się go uruchomić: dlaczego `-finstrument-functions` nie działa dla mnie?

Deklarowane wyjście ma 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 oprócz obsługi konkretnego śledzenia sprzętowego, ale ma tę zaletę, że musisz przekompilować kod.

 1
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-30 14:21:49