Code slicing: Droga do lepszego rozumienia kodu źródłowego
Transkrypt
Code slicing: Droga do lepszego rozumienia kodu źródłowego
Code slicing: Droga do lepszego rozumienia kodu źródłowego (część druga) Bartosz Bogacki Poznan University of Technology, Institute of Computing Science ul. Piotrowo 2, 60-965 Poznan, Poland [email protected] Streszczenie. Kod źródłowy skomplikowanych programów komputerowych może być lepiej zrozumiany przez programistów, gdy zostanie podzielony na mniejsze fragmenty. Poniższy tekst przedstawia podstawowe pojęcia z zakresu statycznej analizy kodu koncentrując się na slicingu kodu. Slicing kodu to metoda służąca do podziału kodu z zachowaniem jego poprawności w ramach rozważanej funkcjonalności. Przedstawiony jest zarys historyczny, znane metody, narzędzia oraz praktyczne zastosowania slicingu kodu. W części pierwszej artykułu przedstawiono podstawy oraz charakterystykę tej popularnej techniki. Część druga prezentuje praktyczne aspekty, narzędzia oraz przykładowe case-study z wykorzystaniem narzędzi Indus/Kaveri. 1 Wprowadzenie Slicing kodu jest stosowany w praktyce w wielu obszarach inżynierii oprogramowania. Najważniejsze obszary przedstawiane w literaturze to zrozumienie kodu źródłowego (ang. code comprehension), debugging, refactoring, inżynieria wsteczna (ang. reverse engineering) oraz metryki związane ze spójnością funkcjonalną kodu. Kosztowna faza utrzymania systemu (ang. maintanance) rozpoczyna się przeważnie od pracy nad zrozumieniem kodu źródłowego. Naturalne jest, iż obniżenie kosztów związanych ze zrozumieniem kodu spowoduje znaczne obniżenie kosztów utrzymania systemu. Wykazano [1], że koszt ponoszony na zrozumienie kodu źródłowego to aż 50-90% kosztu utrzymania. W celu obniżenia tych kosztów należy wspomagać analizę kodu dodatkowymi narzędziami umożliwiającymi szybsze zrozumienie roli oraz zależności poszczególnych fragmentów programu. W pierwszej części artykułu opisane zostały podstawy techniki upraszczającej strukturę kodu źródłowego programu zwanej slicingiem kodu. Pokazano krótko zarys metody zgodnie z pracą [3] oraz istniejące odmiany slicingu. W drugiej części przedstawione zostały przykładowe praktyczne zastosowania tej techniki. Zaprezentowane zostały narzędzia pozwalające programistom szybciej wydobywać wiedzę o programie poprzez wizualizację wycinków kodu zgodnie z zadanym kryterium slicingu. 2 Narzędzia Wiele narzędzi służących do analizy kodu źródłowego wykorzystuje mechanizm slicingu kodu. Najpopularniejsze dwa programy to komercyjny CodeSurfer firmy Grammatech ([5]) oraz akademicki Indus/Kaveri rozwijany przez Kansas State University ([6], [7]). CodeSurfer służy do analizy programów napisanych w języku C. Posiada bogaty zestaw funkcji umożliwiających zarówno wizualizację zależności w kodzie w postaci grafu wywołań funkcji (ang. call graph) jak i dopuszczalnych wartości zmiennych wynikających ze statycznej analizy kodu. Narzędzie to jednak skrupulatnie ukrywa szczegóły slicingu, podając programiście gotową interpretację. Inaczej jest w przypadku programu Kaveri, będącego zestawem plug-in’ów dla środowiska Eclipse i wykorzystującego silnik o nazwie Indus. Program ten prezentuje bezpośrednio wycinki kodu powstałe w wyniku zastosowania wybranej metody slicingu oraz zadanego kryterium. 3.1 CodeSurfer (ANSI C) Program CodeSurfer powstał w wyniku rozwijania akademickiego narzędzia o nazwie Wisconsin Program-Slicing Tool. O ile w akademickiej postaci narzędzie to umożliwiało jedynie tworzenie wycinków (zarówno metodą slicingu „w przód” jak i slicingu wstecz), o tyle komercyjna wersja zawiera dodatkowo pewne mechanizmy dostarczające dodatkowej interpretacji. Rysunek 1 przedstawia kod źródłowy w języku C oraz wycinek utworzony przez Wisconsin Program-Slicing Tool metodą statycznego slicingu wstecz i kryterium slicingu dla zmiennej "i" występującej w wyrażeniu: printf("Sum %d, i %d, Add %d\n", sum, i, Count); Kod należący do wycinka oznaczony jest kolorem czerwonym. Wykorzystując tę funkcjonalność, program CodeSurfer dostarcza kolejne interpretacje uzyskanych informacji. Rysunek 2 przedstawia przykładowy graf wywołań funkcji uzyskany za pomocą programu CodeSurfer. Graf ten wizualizuje wywołania poszczególnych funkcji wykonywane w ciele funkcji main. Nawigacja po kodzie odbywa się z wykorzystaniem węzłów grafu wyołań funckji. Dostępne są również funkcje służące do sprawdzenia prawdopodobnych wartości zmiennej w danym miejscu kodu na podstawie wcześniejszych odwołań do zmiennej, sprawdzenia wyrażeń warunkowych w których wykorzystywana jest modyfikowana zmienna, itp. Dodatkową funkcjonalnością programu jest możliwość obliczenia ponad 20 popularnych metryk kodu (m.in. złożoności cyklomatycznej czy złożoności Halsteada). Rysunek 1. Kod źródłowy oraz wycinek utworzony za pomocą Wisconsin Program-Slicing Tool Rysunek 2. Graf wywołań funkcji utworzony przez program CodeSurfer. 3.2 Indus/Kaveri (Java) Program Indus/Kaveri pozwala na wizualizację wycinka kodu zgodnie z zadaną metodą oraz kryterium slicingu. W wyborze odpowiednich parametrów pomaga bogate menu przedstawione na rysunku 3. Rysunek 3. Wybór parametrów w programie Indus/Kaveri. Tworząc konfigurację należy podjąć m.in. decyzję jakiego typu slicing zostanie zastosowany. Istnieją dwie podstawowe metody: slicing „do przodu” (oznaczone jako forward slice) oraz slicing wstecz (backward slice). Można też wybrać połączenie obu tych metod (complete slice), dające w wyniku unię wycinków utworzonych przez obie te metody. Dodatkowo można zażądać, aby utworzony wycinek był wykonywalny (ang. executable slice). Dokładny opis opcji można znaleźć w dokumentacji programu [8] [9]. Program Indus tworząc wycinki działa na skompilowanym programie, a następnie dokonuje odwzorowania na pierwotny kod źródłowy napisany w języku Java. Wykorzystanie bajtkodu? jako podstawy do analizy programu ma oczywiście zarówno zalety, jak i wady. Podstawową zaletą takiego podejścia jest prostota analizy i uniknięcie wielu problemów wynikających z paradygmatu programowania obiektowego. Wadą jest natomiast uzyskanie wyniku, który może okazać się nieprecyzyjny (np. w skutek przekształceń optymalizacyjnych). Ponieważ prezentacja wyników odbywa się na poziomie kodu źródłowego, a analiza w warstwie bajtkodu, dlatego czasem pojawia się sytuacja, w której fragmenty kodu należące do wycinka nie tworzą kompletnego zbioru wyrażeń w kodzie źródłowym. W programie Indus takie częściowo pokryte wyrażenia są oznaczane kolorem żółtym i nazywane częściowymi elementami wycinka (ang. partial slice element). Wyrażenia, które w całości występują w wycinku, nazywane są całkowitymi elementami wycinka (ang. complete slice element) i oznaczane kolorem zielonym. Rysunek 4 przedstawia przykładowy wycinek dla statycznego slicingu wstecz i kryterium slicingu <8, i> utworzony przez program Indus. Rysunek 4. Przykładowy wycinek utworzony przez program Indus Całkowite elementy wycinka oznaczone zostały na zielono w liniach 1, 3, 6 oraz 8. Częściowe elementy wycinka w liniach 5 i 7 zostały oznaczone na żółto. Wyrażenia z linii 9 nie należą do wycinka. 4 Analiza kodu źródłowego z wykorzystaniem statycznego slicingu Po krótkim wprowadzeniu nadszedł czas na prosty przykład. Rysunek 5 przedstawia kod źródłowy w języku Java. Klasa MaturityOracle ma tylko 1 metodę zawierającą całą logikę biznesową. Metoda ta pobiera argument z linii poleceń, będący liczbą symbolizującą wiek i informuje o tym, czy użytkownik jest osobą dojrzałą. Jeśli liczba lat mieści się w przedziale <16; 18), to powinien pojawić się dodatkowo komunikat informujący, że już niedługo dana osoba będzie dojrzała. Jeśli natomiast liczba lat mieści się w przedziale <0, 16>, wówczas system powinien podać za ile lat osoba stanie się osobą dojrzałą. Rysunek 5. Przykładowy kod w języku Java Programista analizujący tak prostą klasę prawdopodobnie nie straci dużo czasu na określenie wpływu poszczególnych wyrażeń na wartość zmiennej matureCounter. Niemniej jednak może ułatwić sobie zadanie wydając zapytanie do programu Indus o wycinek powstały w wyniku statycznego slicingu wstecz dla kryterium <25, matureCounter>. Wynik takiego zapytania przedstawia rysunek 6. Rysunek 6. Wycinek kodu dla kryterium slicingu <25, matureCounter>. Wycinek obliczony przez program oznaczony jest kolorami zielonym i żółtym. Łatwo zauważyć, że na wartość zmiennej matureCounter w linii 25 nie ma wpływu warunek znajdujący się w linii 21, czyli wyrażenie: if (matureCounter < 2), ponieważ linia 21 nie należy do wycinka. Oznacza to, iż niezależnie od wartości uzyskanej w tym warunku zostanie wypisany komunikat informujący ile lat pozostało użytkownikowi do osiągnięcia wieku dojrzałego. Co więcej, informacja o tym, że wyrażenia z linii 7 należą do wycinka, sygnalizuje możliwość, iż zmienna w tym miejscu może mieć wartość początkową uzyskaną w wyniku przypisania znajdującego się w linii 7, czyli że w przypadku, gdy użytkownik wprowadzi liczbę lat większą od 18, system zawsze poinformuje, że do uzyskania dojrzałości pozostało 0 lat. Rysunek 7 przedstawia wycinek wykonany na kodzie źródłowym poprawionym tak, aby spełniał zadane wcześniej wymagania. Rysunek 7. Wycinek poprawionego kodu dla kryterium slicingu <26, matureCounter> Po dokonaniu poprawki wyrażenie warunkowe z linii 21 należy już do wycinka. Natomiast linia 7 zawierająca przypisanie wartości początkowej przestała należeć do wycinka, gdyż zawsze jego wartość będzie wynikała z wartości wyliczanej w linii 19. 3 Podsumowanie W części pierwszej artykułu przedstawiono podstawy popularnej techniki usprawniającej proces zrozumienia i analizy kodu źródłowego zwanej slicingiem kodu. Wprowadzono pojęcie kryterium slicingu oraz wycinka. Zademonstrowano cztery popularne metody slicingu: statyczny, dynamiczny, warunkowy oraz bezkształtny. Metody te omówiono krótko na przykładzie kodu źródłowego napisanego w języku Java. W części drugiej artykułu przedstawione zostały 2 przykładowe implementacje narzędzi służących do przeprowadzania slicingu kodu. Krótko omówiono komercyjny program operujący na kodzie źródłowym w języku ANSI C – CodeSurfer oraz akademicki zestaw plugin’ów dla Eclipse’a o nazwie Indus/Kaveri działający na kodzie źródłowym napisanym w języku Java. Zademonstrowane zostało działanie statycznego slicingu kodu w praktyce z użyciem narzędzia Indus/Kaveri. Literatura 1. 2. 3. 4. 5. 6. 7. 8. 9. De Lucia, A., Fasolino A.: Understanding Function Behaviors through Program Slicing, Proceedings of the 4th International Workshop on Program Comprehension (WPC '96), 1996. Erlikh, L. : Leveraging legacy system dollars for E-business. (IEEE) IT Pro, May/June 2000, 17-23. Weiser, M.: Program Slicing. Proceeding of the Fifth International Conference in Software Engineering, pages 439-449, 1981. Business Week 3051 (9) : The software trap – automate or else., 142-154. CodeSurfer: http://www.grammatech.com/products/codesurfer/ Indus: http://indus.projects.cis.ksu.edu/ Kaveri: http://freshmeat.net/projects/kaveri/?branch_id=53420&release_id=197620 Indus, Dokumentacja: http://projects.cis.ksu.edu/docman/view.php/12/71/slicer-ug.pdf Kaveri, Dokumentacja: http://projects.cis.ksu.edu/docman/view.php/12/90/Kaveri-ug.pdf