Plasterkowanie Leszek Mierzejewski Cel referatu • kilka definicji • struktury danych i algorytmy plasterkowania dla programów imperatywnych • analiza zależności w programach obiektowych • reprezentacja metod, klas i obiektów • plasterki spełniające różne kryteria • zastosowania Definicje • Plasterkowanie polega na takim okrojeniu analizowanego programu, żeby w zadanym miejscu programu oryginalnego i okrojonego, zadane zmienne miały te same wartości. • Zastosowania: – debugowanie, – testowanie, – analiza, zrozumienie programu, – porównywanie programów, – bezpieczeństwo, – zrównoleglanie. • Plasterkowanie programu wprowadził Mark Weiser. • Publikacja z 1979 roku • Kryterium plasterkowania jest parą <p, V>, gdzie p jest punktem programu, a V zbiorem zmiennych z p. • Backward slice stanowi podzbiór programu, który wpływa na p. • Forward slice stanowi podzbiór programu, na który wpływa p. • Chop, pomiędzy punktami programu p i q, stanowi podzbiór programu zależny od p, i który wpływa na q. Backward slice void main () { int sum = 0; int i = 1; while (i < 11) { sum = sum + i; i = i + 1; } printf („%d\n”, sum); printf („%d\n”, i); } Backward slice dla wyrażenia printf(„%d\n”, i) Backward slice void main () { int sum = 0; int i = 1; while (i < 11) { sum = sum + i; i = i + 1; } printf („%d\n”, sum); printf („%d\n”, i); } Backward slice dla wyrażenia printf(„%d\n”, i) Forward slice void main () { int sum = 0; int i = 1; while (i < 11) { sum = sum + i; i = i + 1; } printf („%d\n”, sum); printf („%d\n”, i); } Forward slice dla wyrażenia sum = 0 Forward slice void main () { int sum = 0; int i = 1; while (i < 11) { sum = sum + i; i = i + 1; } printf („%d\n”, sum); printf („%d\n”, i); } Forward slice dla wyrażenia sum = 0 Chop void main () { int sum = 0; int i = 1; while (i < 11) { sum = sum + i; i = i + 1; } printf („%d\n”, sum); printf („%d\n”, i); } Chop pomiędzy wyrażeniem sum = 0 i wyrażeniem printf(„%d\n”, i) Chop Brak przepływu informacji void main () { int sum = 0; int i = 1; while (i < 11) { sum = sum + i; i = i + 1; } printf („%d\n”, sum); printf („%d\n”, i); } Chop pomiędzy wyrażeniem sum = 0 i wyrażeniem printf(„%d\n”, i) Program Dependence Graph • graf skierowany, reprezentujący procedurę • wierzchołki – wierzchołki Control Flow Graph • krawędzie – zależności kontrolne – zależności przepływu danych • Backward slice dla punktu p jest podgrafem uzyskanym przez przejście grafu PDG przeciwnie do kierunku, rozpoczynając z wierzchołka p • Forward slice dla punktu p jest podgrafem uzyskanym przez przejście grafu PDG z wierzchołka p. • Chop dla punktów p i q jest podgrafem PDG zawartym pomiędzy wierzchołkami p i q. System Dependence Graph • System Dependence Graph – PDG dla każdej procedury – krawędzie pomiędzy procedurami łączą: • wywołania procedur (calls) z wejściami procedur (entries) • przekazywane parametry (actual parameters) z otrzymywanymi parametrami (formal parameters) • wyniki procedury (results) ze zmiennymi pobierającymi rezultat (call-site return values) • wyznaczenie plasterka jest problemem przejścia grafu Backward slice void main () { int add (int x, int y) { int sum = 0; return x + y; int i = 1; } while (i < 11) { sum = add(sum,i); i = add(i,1); } printf („%d\n”, sum); printf („%d\n”, i); } Backward slice dla wyrażenia printf(„%d\n”, i) Backward slice void main () { int add (int x, int y) { int sum = 0; return x + y; int i = 1; } while (i < 11) { sum = add(sum,i); i = add(i,1); } printf („%d\n”, sum); printf („%d\n”, i); } Backward slice dla wyrażenia printf(„%d\n”, i) Plasterkowanie programów obiektowych Class Dependency Graph (ClDG) L.D. Larsen, M.J. Harrold, 1994. • rozszerza SDG • zawiera wejściowy wierzchołek dla każdej klasy, z którego wychodzą krawędzie do wierzchołków wejściowych metod • każdy wierzchołek wejściowy rozrasta się poprzez dodanie wierzchołków reprezentujących parametry i atrybuty Object-Oriented Program Dependency Graph J.L. Chen, F.J. Wang, Y.L. Chen • dwa rodzaje plasterków: state slice i behaviour slice. • State slice dla obiektu jest zbiorem komunikatów i wyrażeń kontrolnych, które mogą wpływać na stan obiektu. • Behaviour slice jest zbiorem atrybutów i metod zdefiniowanych w powiązanych klasach, które mogą wpływać na zachowanie obiektu. PDG metody • • • • • • • • Reprezentowana przez control flow graph (CFG) CFG=<S, E, Sentry, Sexit> E={<s1, s2> | s1,s2 S i po wykonaniu s1 można wykonać s2}. Def(s) zmienne, których wartości są zdefiniowane (modyfikowane) w punkcie s. (Parametry formalne in są związane z wierzchołkiem wejścia.) Ref(s) zmienne, do których wartości jest odwołanie w punkcie s. Def(s,x) zmienne użyte do zdefiniowania zmiennej x. Dep_D(s,x)={(x,s’,y) | yDef(s,x) yDef(s’) ścieżka z s’ do s, na której y nie jest przedefiniowane. Dep_R(s)={(x,s’,x) | jeśli s jest wyrażeniem kontrolnym (if, while), x jest zmienną warunkową użytą w s i x Def(s’) istnieje ścieżka z s’ od s, na której x nie jest przedefiniowany}. public class Point { int X; int Y; public Point (int x, int y) { 1: X=x; 2: Y=y; } public void Move (int z) { 3: X=X+z; 4: Y=Y+z; public class Main { public void main (String[] args) { 8: Point P1=new Point(10,20); 9: Point P2=new Point(0,0); 10: P1.Trans(10); 11: P2.Move(100); 12: P1.X=P1.X+P2.X++; 13: System.out.println (P1.X); 14: System.out.println (P2.X); } } } public void Trans (int z) { 5: if (z>0) 6: Move(z); else 7: System.out.println (“error”); } } Def(12)={P1.X, P2.X} Ref(12)={P1.X, P2.X} Def(12, P2.X)={P2.X} Def(12, P1.X)={P1.X, P2.X} Dep_D(14,P2.X)={P2.X,12,P2.X} Dep_R(5)={wejście Trans} Definicja 1 Program Dependence Graph dla metody M jest grafem skierowanym ze znacznikami. PDG=<S’,E’,T>, gdzie S’=S (S jest zbiorem wierzchołków grafu CFG), krawędzie E’=E1 E2, gdzie E1 zbiór krawędzi wyrażających zależności kontrolne, E2={<s1, s2> | (x,s2,y) Dep_D(s1,x) (x,s2,x) Dep_R(s1)} jest zbiorem krawędzi wyrażających zależności danych. T jest zbiorem znaczników. Znacznik na krawędzi <s1,s2>: •(*,*), jeśli <s1,s2> E1, •(x,y), jeśli (x,s2,y) Dep_D(s1), •(x,x), jeśli (x,s2,x) Dep_R(s1). Entry (X,X) public void Move (int z) { 3: X=X+z; 4: Y=Y+z; } (X,z) (Y,z) 3 (Y,Y) 4 PDG Move Analiza zależności • Rodzaje parametrów: in, out, in out, formalne, aktualne. • Zależności pomiędzy parametrami opisane są za pomocą zbiorów zależności parametrów. • Metody mogą współdziałać ze sobą tylko za pomocą parametrów. • Call Graph – graf skierowany, opisuje powiązania pomiędzy metodami – wierzchołki – metody – krawędzie – komunikaty, jeśli metoda p wywołuje q, to <p,q> należy do grafu. Def(M) = In(Sexit) – {v|v jest zmienną lokalną lub parametrem typu in}; if v Def(M) then if v jest parametrem formalnym then Def(s)=Def(s) {a}; a jest odpowiadającym parametrem aktualnym. else Def(s)=Def(s) {v}; // v jest zmienną globalną if y Dep_PM(M,x) then a i b są parametreami aktualnymi odpowiadającymi zmiennym x i y Def(s,a)=Def(s) {b}; Analiza zależności (polimorfizm) • W Javie polimorfizm jest wspierany przez abstrakcyjne metody. • Obiekt, których typ może zmienić się podczas wykonania programu jest nazywany obiektem polimorficznym • Polimorficzny obiekt jest reprezentowany jako drzewo, w którym korzeń reprezentuje polimorficzny obiekt, a liście reprezentują obiekty możliwych typów. • Java jest językiem typowanym statycznie. • Polimorfizm jest reprezentowany przez zbiory zależności parametrów. • Kiedy obiekt wywołuje metodę to rozważamy następujące przypadki: Obiekt ma określony typ. Obiekt nie ma określonego typu. Metoda nie polimorficzna. Koniec. Bierzemy wszystkie możliwe metody. Metoda polimorficzna. Zamieniamy na odpowiadającą metodę. Dla wszystkich możliwych typów obiektu zmieniamy polimorficzną metodę na jej odpowiedniki. PDG klasy Klasa: • pola danych • PDG dla każdej metody • podklasa dziedziczy PDG rodzica Reprezentacja obiektu: • pola danych obiektu jako parametry • drzewo – korzeń - obiekt – dzieci - pola danych PDG Move PDG Trans PDG Point PDG klasy Point Partial Slicing • Dla podanego kryterium plasterkowania <s, v> partial slicing plasterkuje tylko wybrane części programu (klasę, kilka metod, obiekt). • Użyteczny w dużych systemach, lub gdy mamy niekompletny kod. • Plasterkujemy metodę, pomijając wywołania innych metod. Analizujemy je w drugiej fazie. • Plasterkowanie metody sprowadza się do plasterkowania kilku pojedyńczych metod. • Tylko parametry out i in out mają wpływ na stronę wywołującą. • Zależności pomiędzy parametrami można wyznaczyć przez interfejsy. Input: Program P, kryterium plasterkowania <s0, v>. while PL != do Output: Plasterek spełniający kryterium plasterkowania Usuń element (P,x) z PL; <s0, v>: Dep_S. if P jest interesującą nas częścią then Init: W={(s0, v)}; Dep_S = ; PL = ; Dep_S = Dep_S plasterek P dla x; Algorithm: if plasterek P dla x zawiera plasterek repeat Mi dla z then Usuń element (s, x) z W; PL = PL {(Mi,z)}; for all krawędź <s, s’>, która nie była wybrana do end; Zaznacz jako wybraną krawędź <s, s’>; Niech T będzie znacznikiem dla <s, s’>; if T=(x, y) then // data dependence W = W {(s’, y)}; Dep_S = Dep_S {s’}; if y jest nazwą metody then PL = PL {(y, y)}; if y jest aktualnym parametrem metody M then Niech y’ będzie odpowiadającym parametrem formalnym. PL = PL {(M,y’)}; if T=(*,*) then // control dependence Dep_S = Dep_S {s’}; for all yi Ref(s’) do W = W {(s’,y)}; if y jest nazwą metody then PL {(y,y)}; while W != ; public class Point { int X; int Y; public Point (int x, int y) { 1: X=x; 2: Y=y; } public void Move (int z) { 3: X=X+z; 4: Y=Y+z; public class Main { public void main (String[] args) { 8: Point P1=new Point(10,20); 9: Point P2=new Point(0,0); 10: P1.Trans(10); 11: P2.Move(100); 12: P1.X=P1.X+P2.X++; 13: System.out.println (P1.X); 14: System.out.println (P2.X); } } } public void Trans (int z) { 5: if (z>0) 6: Move(z); else 7: System.out.println (“error”); } } Kryterium plasterkowania <13,P1.X> metody Move i Trans Object Slicing • Głównie użyteczny przy debugowaniu i analizie programu. • Kryterium plasterkowania <s, v, Object> • Wyznacza wyrażenia metod wybranego obiektu spełniające kryterium <s, v> • Korzystamy z algorytmu dla częściowego plasterkowania. • Wyznaczamy plasterki metod obiektu Object (mogą być wywoływane pośrednio lub bezpośrednio) • Wynikiem jest suma wszystkich tych plasterków. public class Point { int X; int Y; public Point (int x, int y) { 1: X=x; 2: Y=y; } public void Move (int z) { 3: X=X+z; 4: Y=Y+z; public class Main { public void main (String[] args) { 8: Point P1=new Point(10,20); 9: Point P2=new Point(0,0); 10: P1.Trans(10); 11: P2.Move(100); 12: P1.X=P1.X+P2.X++; 13: System.out.println (P1.X); 14: System.out.println (P2.X); } } } public void Trans (int z) { 5: if (z>0) 6: Move(z); else 7: System.out.println (“error”); } } Kryterium plasterkowania <14,P2.X,P2> Class Slicing • Kryterium plasterkowania <s, v, Class> • Wyznacza pola danych i wyrażenia metod należących do klasy Class, które spełniają kryterium plasterkowania <s, v> • Algorytm: – plasterkujemy każdą instancję, zaznaczając użyte pola danych (kosztowne) – plasterkujemy z punktu s, w trakcie zaznaczając wyrażenia i pola danych klasy Class. public class Point { int X; int Y; public Point (int x, int y) { 1: X=x; 2: Y=y; } public void Move (int z) { 3: X=X+z; 4: Y=Y+z; public class Main { public void main (String[] args) { 8: Point P1=new Point(10,20); 9: Point P2=new Point(0,0); 10: P1.Trans(10); 11: P2.Move(100); 12: P1.X=P1.X+P2.X++; 13: System.out.println (P1.X); 14: System.out.println (P2.X); } } } public void Trans (int z) { 5: if (z>0) 6: Move(z); else 7: System.out.println (“error”); } } Kryterium plasterkowania <14,P2.X,Point> Zastosowania • Zrozumienie programu – Co wpływa na co? • Zrównoleglanie (dekompozycja) – Odzielenie osobnie wyliczanych zadań • Specjalizacja – plasterek = wyspecjalizowany program • Porównywanie programów – Wykrywanie rożnic semantycznych • Testowanie – Które fragmenty nie wymagają powtórnego testowania? • Bezpieczeństwo – Wykrywanie niebezpiecznych fragmentów programu. Aplikacje i projekty • • • • http://www.grammatech.com/products/codesufer/ http://www.infosun.fmi.uni-passau.de/st/staff/krinke/slicing http://www.cs.wisc.edu/wpis.html http://hissa.ncsl.gov/~jimmy/unravel.html Bibliografia • Z. Chen, B. Xu, “Slicing Object-Oriented Java Programs” • Andrea De Lucia, “Program Slicing: Methods and Applications” • www.grammatech.com Dekompozycja void char_count(FILE *f) { int lines = 0; int chars; BOOL eof_flag = FALSE; int n; extern void scan_line(FILE *f, BOOL *bp, int *ip); scan_line(f, &eof_flag, &n); chars = n; while (eof_flag == FALSE) { lines = lines + 1; scan_line(f, &eof_flag, &n); chars = chars + n; } printf(„lines = %d\n”, lines); printf(„chars = %d\n”, chars); } void line_count(FILE *f) { int lines = 0; int chars; BOOL eof_flag = FALSE; int n; extern void scan_line(FILE *f, BOOL *bp, int *ip); scan_line(f, &eof_flag, &n); chars = n; while (eof_flag == FALSE) { lines = lines + 1; scan_line(f, &eof_flag, &n); chars = chars + n; } printf(„lines = %d\n”, lines); printf(„chars = %d\n”, chars); }