19.02.2014 Aufrufe

Vorlesung Software-Reengineering ¨Uberblick I - Informatik - FB3 ...

Vorlesung Software-Reengineering ¨Uberblick I - Informatik - FB3 ...

Vorlesung Software-Reengineering ¨Uberblick I - Informatik - FB3 ...

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>Vorlesung</strong> <strong>Software</strong>-<strong>Reengineering</strong><br />

Prof. Dr. Rainer Koschke<br />

Arbeitsgruppe <strong>Software</strong>technik<br />

Fachbereich Mathematik und <strong>Informatik</strong><br />

Universität Bremen<br />

Wintersemester 2013/14<br />

Überblick I<br />

Einführung<br />

Statische Programmanalyse<br />

Dynamische Analyse<br />

Program Slicing<br />

Metriken<br />

Klonerkennung<br />

Refactoring<br />

Codetransformation


Überblick II<br />

<strong>Software</strong>-Visualisierung<br />

Einführung in das <strong>Software</strong>-<strong>Reengineering</strong> I<br />

Einführung<br />

Administrativa<br />

Lernziele<br />

Motivation<br />

Wichtige Begriffe<br />

Wartung<br />

Reverse Engineering<br />

Restrukturierung<br />

<strong>Reengineering</strong><br />

Wrapping<br />

Business Process <strong>Reengineering</strong><br />

Ziele und Aufgaben<br />

Unterschiede zur Vorwärtsentwicklung<br />

4 / 437


Organisatorisches<br />

<strong>Vorlesung</strong>:<br />

montags, 10:30 – 12:00 Uhr, MZH 1450<br />

donnerstags, 14:00 s.t. – 15:30 Uhr, MZH 1450<br />

Erreichbar: TAB 2.57, Telefon 218-64481, koschke@tzi.de<br />

Sprechstunde nach Vereinbarung<br />

Video im Netz http://mlecture.uni-bremen.de<br />

bitte bei Stud.IP anmelden unter<br />

https://elearning.uni-bremen.de/<br />

Buch zur <strong>Vorlesung</strong> bei Stud.IP<br />

Literatur: Folien zur <strong>Vorlesung</strong> und verwendete Artikel<br />

http://www.informatik.uni-bremen.de/st/<br />

lehredetails.php?id=&lehre_id=313<br />

5 / 437<br />

Scheinbedingungen<br />

Modulprüfung: 30 min mündliche Prüfung<br />

ansonsten<br />

1 erfolgreiche Bearbeitung von praktischen Aufgaben (1-2<br />

Personen): Eclipse-Plugins für<br />

1 Abhängigkeitsanalyse<br />

2 Metrikberechnung<br />

3 Implementierung zweier Refactorings<br />

2 Fachgespräch (einzeln, benotet; zählt zu einem Drittel)<br />

6 / 437


Übersicht über diese <strong>Vorlesung</strong><br />

Statische<br />

Programmanalysen und<br />

-repräsentationen<br />

Dynamische Analysen<br />

Program-Slicing<br />

Refactoring und<br />

Transformationen<br />

<strong>Software</strong>-Produkt-<br />

Metriken<br />

Erkennung duplizierten<br />

Codes und anderer Bad<br />

Smells<br />

<strong>Software</strong>-Visualisierung<br />

Analyse und<br />

Restrukturierung von<br />

Vererbungshierarchien<br />

Merkmalsuche<br />

(Feature-Location)<br />

<strong>Software</strong>-Clustering, Architekturrekonstruktion<br />

und -validierung<br />

<strong>Reengineering</strong>-Projekte<br />

7 / 437<br />

Wegweiser<br />

Was versteht man unter <strong>Reengineering</strong> genau?<br />

Welche Gebiete des <strong>Reengineering</strong>s gibt es?<br />

Was ist der Unterschied zur klassischen<br />

Vorwärtsentwicklung?<br />

8 / 437


Lehman und Beladys (1980) Hypothesen<br />

<strong>Software</strong>-Evolution<br />

Gesetz des fortgesetzten Wandels<br />

Gesetz der ansteigenden Komplexität<br />

. . .<br />

⇒ ständige Anpassung erforderlich<br />

⇒ Komplexität muss kontrolliert und begrenzt werden<br />

11 / 437<br />

Wunsch<br />

Gewählte Lösung antizipiert mögliche Änderungen.<br />

Änderungen werden auf der adäquaten Ebene vorgenommen.<br />

Dokumentation wird mitgeführt.<br />

12 / 437


Wirklichkeit<br />

Die Zukunft lässt sich nur begrenzt vorhersagen.<br />

Ursprüngliche Systemstruktur wird ignoriert.<br />

Dokumentation ist unvollständig oder obsolet.<br />

Mitarbeiter verlassen das Projekt (und mit ihnen verschwindet<br />

das ganze Wissen).<br />

13 / 437<br />

Legacy System<br />

Legacy:<br />

“A sum of money, or a specified article, given to another<br />

by will; anything handed down by an ancestor to a<br />

predecessor.”<br />

– Oxford English Dictionary<br />

Definition<br />

Legacy System: <strong>Software</strong>-System, das geerbt wurde und einen<br />

Wert darstellt.<br />

14 / 437


Staged <strong>Software</strong> Life Cycle Model<br />

Initial Development<br />

First running version<br />

Loss of evolvabilty<br />

Evolution<br />

Servicing<br />

Evolution Changes<br />

Servicing Patches<br />

Servicing discontinued<br />

Switch−off<br />

Phase−Out<br />

Close−Down<br />

– Rajlich und Bennett (2000)<br />

15 / 437<br />

Versioned Staged Model (Rajlich und Bennett 2000)<br />

First running version<br />

Initial Development<br />

Version 1<br />

Evolution Changes<br />

Servicing<br />

Servicing Patches<br />

Phase−Out<br />

Version 2<br />

Close−Down<br />

Evolution Changes<br />

Servicing Patches<br />

Servicing<br />

Phase−Out<br />

Close−Down<br />

16 / 437


Begriffe<br />

<strong>Software</strong>-Wartung<br />

<strong>Software</strong>-Evolution<br />

<strong>Reengineering</strong><br />

<strong>Software</strong>-<strong>Reengineering</strong><br />

Business-Process-<br />

<strong>Reengineering</strong><br />

Renovation<br />

Reclamation<br />

Refactoring<br />

Reverse Engineering<br />

Restrukturierung<br />

Wrapping<br />

18 / 437<br />

<strong>Software</strong>-Wartung<br />

ANSI/IEEE Standard 729-1983:<br />

“Modification of a software product after delivery to<br />

correct faults, to improve performance or other attributes,<br />

or to adapt the product to a changed environment.”<br />

Häufiger Sprachgebrauch: Änderungen am System nach dessen<br />

Auslieferung.<br />

Schließt Anpassungen an neue Anforderungen ein.<br />

Besserer Begriff hierfür: <strong>Software</strong>-Evolution.<br />

19 / 437


Aufwand für <strong>Software</strong>-Wartung<br />

korrektiv<br />

22%<br />

12%<br />

perfektiv<br />

adaptiv<br />

25 %<br />

41%<br />

erweiternd<br />

– Lientz und Swanson (1980)<br />

20 / 437<br />

Lientz und Swanson (1980) haben den Aufwand für verschiedene<br />

Wartungsarten anhand von 487 <strong>Software</strong>-Organisationen näher<br />

untersucht und festgestellt, dass ca. 80% der so genannten Wartung<br />

tatsächlich Erweiterungen sind (neue Funktionalität bzw. Anpassungen<br />

an neue Hardware- oder <strong>Software</strong>-Plattformen).


Aufwand im <strong>Software</strong>-Lifecycle<br />

Verstehen 40%<br />

Spezifikation<br />

20% Entwickeln 20%<br />

Test<br />

40%<br />

Entwurf<br />

20%<br />

Kodierung<br />

20%<br />

Ändern 40%<br />

Boehm (1981) Fjeldstad und Hamlen (1979)<br />

21 / 437<br />

Eine typische Verteilung des Aufwands für Aktivitäten in der<br />

Erstentwicklung wurde von Boehm (1981) anhand groß angelegter<br />

empirischer Studien erhoben.<br />

Der Aufwand für die Erstentwicklung ist jedoch vergleichsweise gering,<br />

wenn man ihn mit dem Aufwand für Wartung vergleicht. Arthur (1988)<br />

hat insgesamt sechs Untersuchungen aus den Siebzigern zum Anteil der<br />

Wartung am <strong>Software</strong>-Lifecycle zusammen getragen. Der Aufwand liegt<br />

in diesen Untersuchungen zwischen 60 und 80 Prozent. Die Garnter<br />

Group, eine große Unternehmensberatung, sagt für die Zukunft sogar<br />

einen ansteigenden Aufwand voraus, der bis zu 95% der Gesamtkosten für<br />

<strong>Software</strong> einnehmen wird (Moad 1990).<br />

Fjeldstad und Hamlen (1979) haben den Aufwand für die einzelnen<br />

Wartungsaktivitäten empirisch näher untersucht und dabei heraus<br />

gefunden, dass Wartungsprogrammierer ca. 50% ihrer Zeit allein mit der<br />

Analyse beschäftigt sind, bevor sie eine Änderung tatsächlich vornehmen<br />

und testen können. Bei korrektiver Wartung (also Fehlerbeseitigung) liegt<br />

der Aufwand für die Analyse gar bei 60%.


Aufwand für <strong>Software</strong>-Evolution<br />

US Air Force System (Boehm, 1975):<br />

$ 30 / Statement bei Erstentwicklung<br />

$ 4000 / Statement in der Wartung<br />

22 / 437<br />

Jahr-2000-Problem<br />

Beseitigung des Jahr-2000-Problems (geschätzt von Cassell, 1997)<br />

500.000.000.000 - 1.000.000.000.000 DM<br />

23 / 437


Reverse Engineering<br />

License Restrictions:<br />

“Customer may not reverse engineer, disassemble,<br />

decompile, or translate the <strong>Software</strong>, or otherwise<br />

attempt to derive the source code of the <strong>Software</strong>.”<br />

“To me the flow of time is irrelevant. You decide what<br />

you want. I then merely make sure that it has already<br />

happened.”<br />

– The Hitch Hiker’s Guide to the Galaxy<br />

24 / 437<br />

Reverse Engineering (Chikofsky und Cross II. 1990)<br />

Definition<br />

Reverse Engineering: Identifikation der Systemkomponenten und<br />

deren Beziehungen mit dem Ziel, das System in einer anderen<br />

Form oder auf höherem Abstraktionsniveau zu beschreiben.<br />

Forward<br />

Engineering<br />

Anforderungen Entwurf Code<br />

Reverse<br />

Engineering<br />

26 / 437


Architekturrekonstruktion<br />

Definition<br />

Architekturrekonstruktion: Reverse Engineering mit dem Ziel,<br />

eine Beschreibung der Architektur des Systems zu erstellen.<br />

Anforderungen Entwurf Code<br />

Architecture<br />

Reconstruction<br />

27 / 437<br />

Restrukturierung (Chikofsky und Cross II. 1990)<br />

Definition<br />

Restrukturierung: Transformation einer Repräsentation in eine<br />

andere auf derselben Abstraktionsebene, ohne Änderung der<br />

Funktionalität des Systems.<br />

Anforderungen Entwurf Code<br />

Restrukturierung<br />

28 / 437


<strong>Reengineering</strong> (Chikofsky und Cross II. 1990)<br />

Definition<br />

<strong>Reengineering</strong>: Untersuchung (Reverse Engineering) und<br />

Änderung des Systems, um es in neuer Form zu implementieren.<br />

Synonyme: Renovation, Reclamation.<br />

Forward<br />

Engineering<br />

Anforderungen Entwurf Code<br />

Restrukturierung<br />

Reverse<br />

Engineering<br />

29 / 437<br />

<strong>Reengineering</strong>-Varianten<br />

Reines <strong>Reengineering</strong>:<br />

das System soll lediglich restrukturiert werden<br />

keine Funktionalität kommt hinzu / wird geändert<br />

Erweiterndes <strong>Reengineering</strong>:<br />

System wird zunächst analysiert und/oder restrukturiert, um<br />

dann Funktionalität zu ändern oder hinzuzufügen<br />

30 / 437


Wrapping<br />

Das System erhält eine neue Schnittstelle, bleibt aber ansonsten<br />

unangetastet.<br />

Interimslösung, wenn System bald ausgewechselt werden soll.<br />

Notwendig, wenn das System Subsystem per Subsystem<br />

geändert werden muss.<br />

Oft eingesetzt, um zeichenorientierte Anwendungen mit einer<br />

graphischen Benutzerschnittstelle zu versehen.<br />

Organisatorische Gründe:<br />

altes“ Wartungspersonal behält Kontrolle über Wartung<br />

”<br />

ihres“ Systems<br />

”<br />

” junges“ Wartungspersonal hat ” moderne“ Sicht 31 / 437<br />

Business Process <strong>Reengineering</strong><br />

“Business process reengineering is the search for, and the<br />

implementation of, radical change in business process to<br />

achieve breakthrough results.”<br />

– T.A. Stewart, Fortune Magazine’93.<br />

32 / 437


Business Process <strong>Reengineering</strong><br />

Etwas sachlicher:<br />

Wiedergewinnung der tatsächlichen Abläufe der<br />

Geschäftsprozesse (Workflow) (z.B. Bestellungswesen,<br />

Auftragsabwicklung etc.)<br />

Überarbeitung und Neudefinition der Abläufe<br />

33 / 437<br />

Ziele des Reverse Engineerings<br />

Kontrolle der Komplexität<br />

Gewinnung alternativer Sichten<br />

Wiedergewinnung verlorener Information<br />

Erkennung von Seiteneffekten<br />

Schaffung höherer Abstraktionen<br />

Unterstützung von Wiederverwendung<br />

34 / 437


Häufige <strong>Reengineering</strong>-Aufgaben<br />

Plattformanpassung<br />

Änderung der Programmiersprache<br />

neuer Standard, neue Sprache, neues Paradigma<br />

Benutzerschnittstelle zeichenorientiert hin zu graphisch<br />

orientiert<br />

Mainframe hin zu Client-Server-Architektur<br />

Datenbankumstellung<br />

Präventive Maßnahmen, wie z.B. Verbesserung des Information<br />

Hidings oder Remodularisierung, sind eher selten.<br />

35 / 437<br />

Mass Changes<br />

Änderungen, die weite Teile des Codes bzw. sehr viele Systeme<br />

betreffen.<br />

Einführung des Euros<br />

Legacy to Internet Interoperability (Electronic Commerce)<br />

Änderung von Repräsentationsformen:<br />

Y2K Problem<br />

Erweiterung des Bar-Codes<br />

Unix-Datum<br />

→ Wunsch nach hohem Automatisierungsgrad<br />

36 / 437


<strong>Reengineering</strong> in der Praxis<br />

Gegenwärtig zur Verfügung stehende Werkzeuge:<br />

grep<br />

symbolische Debugger<br />

Cross-Reference-Tools (fertige Parser, die Basisinformationen<br />

extrahieren; z.T. mit Visualisierung durch Graphen)<br />

UML-CASE-Tools, die Klassendiagramme extrahieren<br />

programmierbare Analyse- und Transformationsumgebungen<br />

basierend auf abstrakten Syntaxbäumen (z.B. Refine von<br />

Reasoning Systems, DMS von Semantic Designs, RainCode)<br />

37 / 437<br />

<strong>Software</strong> Engineering<br />

“<strong>Software</strong> engineering is reengineering on<br />

the empty system.”<br />

“Is it?”<br />

38 / 437


Unterschiede Forward Eng. / <strong>Reengineering</strong><br />

Forward Engineering auf<br />

grüner Wiese<br />

Problem noch unklar<br />

Aussagen über Aufwand,<br />

Dauer, Zuverlässigkeit etc.<br />

sind schwierig<br />

System existiert nicht<br />

Entwurf hat viele<br />

Freiheiten<br />

im sauberen Entwurf gibt<br />

es keine versteckten<br />

Abhängigkeiten<br />

<strong>Reengineering</strong><br />

Problem weitgehend klar<br />

Idealerweise: Daten aus der<br />

Vergangenheit existieren, die<br />

Grundlage für Schätzungen<br />

darstellen<br />

System existiert<br />

Genaue Struktur/Qualität<br />

bekannt?<br />

Lösung ist durch<br />

existierendes System<br />

beschränkt<br />

Änderungen können<br />

globale Auswirkungen<br />

haben (viele versteckte<br />

Abhängigkeiten)<br />

39 / 437<br />

<strong>Software</strong> Engineering & <strong>Reengineering</strong><br />

<strong>Reengineering</strong> beginnt oft bereits während der Erstentwicklung:<br />

neue Anforderungen treffen ein<br />

Missverständnisse und Unklarheiten werden sichtbar<br />

der Entwurf hat sich als unzureichend erwiesen<br />

Integration anderer Komponenten macht Umstrukturierungen<br />

notwendig<br />

40 / 437


Weiterführende Literatur<br />

Chikofsky und Cross II. (1990): “Reverse Engineering and<br />

Design Recovery: A Taxonomy”, IEEE <strong>Software</strong><br />

definiert Terminologie; ist die begriffliche Grundlage<br />

Baumöl u. a. (1996): “Einordnung und Terminologie des<br />

<strong>Reengineering</strong>”<br />

führt z.T. deutsche Begriffe ein<br />

41 / 437<br />

Bücher I<br />

Demeyer u. a. (2002) stellen eine Reihe von Vorgehensweisen<br />

bei typischen Problemen des <strong>Reengineering</strong>s vor<br />

Müller (1997) bietet eine Einführung in verschiedene Aspekte<br />

des <strong>Reengineering</strong>s (Programmverstehen, Metriken,<br />

Sprachkonversion, Restrukturierung, Wiederverwendung,<br />

Migration zu objektorientierten Systemen,<br />

Managementaspekte)<br />

obwohl meine <strong>Vorlesung</strong> 1999 in völliger Unkenntnis dieses<br />

Buches entstanden ist, ist doch eine große Überlappung der<br />

Inhalte festzustellen (das Buch beschreibt aber weniger die<br />

konkreten Techniken)<br />

Fowler (2000) beschreibt so genannte Bad Smells<br />

(Code-Anomalien) und zugehörige Refactorings, um sie zu<br />

beseitigen<br />

42 / 437


Bücher II<br />

Seacord u. a. (2003) beschreiben Methoden zur<br />

Modernisierung von Anwendungssystemen; in erster Linie<br />

Prozess- und Managementfragen werden erläutert<br />

Simon u. a. (2006) beschreiben, wie man die Wartbarkeit von<br />

Systemen messen kann; das Ergebnis ist eine Einstufung in<br />

Analogie zu CMMI jedoch für die innere Produktqualität<br />

Sneed u. a. (2005) beschreiben organisatorische Aspekte und<br />

verschiedene Prozesse für die Wartung und Weiterentwicklung<br />

von <strong>Software</strong><br />

Masak (2006) diskutiert verschiedene Aspekte der Wartung<br />

und Evolution, insbesondere Beobachtungen, Metriken,<br />

Anti-Patterns und Management<br />

Pigoski (1996) behandelt Probleme und Managementlösungen<br />

der <strong>Software</strong>-Wartung<br />

Lehner (1989) beschreibt Probleme und Managementlösungen<br />

der <strong>Software</strong>-Wartung<br />

43 / 437<br />

Statische Programmanalyse I<br />

Statische Programmanalyse<br />

Compiler versus <strong>Reengineering</strong>-Werkzeug<br />

Lexikalische Ebene<br />

Syntaktische Ebene<br />

Abstrakte Syntax<br />

Traversierungen von ASTs<br />

Quellennahe Repräsentation<br />

Kontrollfluss<br />

Kontrollabhängigkeit<br />

Datenabhängigkeit<br />

Datenflussanalyse: Bestimmung von Set<br />

Static Single Assignment Form<br />

Aliasing<br />

Analysemöglichkeiten<br />

Wiederholungsfragen<br />

44 / 437


Wegweiser<br />

Kontext<br />

Am Anfang steht die Analyse:<br />

statische Analyse<br />

dynamische Analyse<br />

Welche Informationen kann man wie aus<br />

Programmen statisch extrahieren?<br />

Welche Formen der statischen<br />

Programmrepräsentationen und -analysen gibt<br />

es?<br />

Worin unterscheidet sich die Programmanalyse<br />

im Compilerbau von der im <strong>Reengineering</strong>?<br />

45 / 437<br />

Compiler-Struktur<br />

Programmtext<br />

mit<br />

Makros<br />

Präprozessor<br />

Lexer<br />

Programmtext<br />

Tokenstrom<br />

Parser<br />

abstrakter<br />

Syntaxbaum<br />

semantische<br />

Analyse<br />

annotierter<br />

abstrakter<br />

Syntaxbaum<br />

Front-End<br />

Zwischensprachengenerator<br />

Zwischendarstellung<br />

Analysen u.<br />

Optimierungen<br />

optimierte<br />

Zwischendarstellung<br />

Middle-<br />

End<br />

Codegenerator<br />

Code<br />

Back-End<br />

46 / 437


Analysator- und Transformator-Struktur<br />

Programmtext<br />

Programmtext<br />

mit<br />

Makros<br />

Präprozessor<br />

Lexer<br />

Tokenstrom<br />

Parser<br />

abstrakter<br />

Syntaxbaum<br />

semantische<br />

Analyse<br />

annotierter<br />

abstrakter<br />

Syntaxbaum<br />

Front-End<br />

Zwischensprachengenerator<br />

Zwischendarstellung<br />

Analysen<br />

Transformationen<br />

annotierte<br />

Zwischendarstellung<br />

Middle-<br />

End<br />

Unparser Programmtext Back-End Wissensbasis<br />

47 / 437<br />

Phasen eines Compilers<br />

• Wissen über das Programm nimmt zu<br />

– syntaktische Dekomposition<br />

– semantische Attributierung<br />

– Namensbindung<br />

– Kontrollflussinformation<br />

– Datenflussinformation<br />

• Abstraktion nimmt ab<br />

– abstrakter Syntaxbaum<br />

– maschinennahe einheitliche Zwischensprache, z.B. Register<br />

Transfer Language (RTL) von Gnu GCC<br />

– Maschinensprache


Unterschiede Compiler / Analysator<br />

lokal ↔ global<br />

optimistisch ↔ pessimistisch<br />

quellennah ↔ sprachenunabhängig<br />

48 / 437<br />

Token (Lexem)<br />

Definition<br />

Ein Token (auch: Lexem) ist die kleinste unteilbare Einheit, die in<br />

einer Programmiersprache von Bedeutung ist:<br />

Schlüsselwörter<br />

Bezeichner<br />

Literale (Zahlen, Zeichenketten, einzelne Zeichen)<br />

Operatoren<br />

Trennzeichen (z.B. ;)<br />

50 / 437


Üblicherweise keine Tokens:<br />

• Whitespace<br />

• Kommentare<br />

Übliche Attribute:<br />

• Typ (meist codiert als Zahl oder Aufzählungswert)<br />

• Quellcodeposition (Dateiname, Zeile, Spalte)<br />

• Wert (textuelle Repräsentation)<br />

Der Tokenstrom<br />

1 { i n t x ;<br />

2 i n t y ;<br />

3 y = 0 ;<br />

4 x = y + 1 ;<br />

5 }<br />

Zeile Spalte Typ-Code Typ Wert<br />

1 1 15 ’{’ {<br />

1 2 5 INT int<br />

1 6 4 ID x<br />

1 7 17 ’;’ ;<br />

2 2 5 INT int<br />

2 6 4 ID y<br />

2 7 17 ’;’ ;<br />

3 2 4 ID y<br />

3 4 18 ’=’ =<br />

3 6 6 NUMBER 0<br />

3 7 17 ’;’ ;<br />

4 2 4 ID x<br />

4 4 18 ’=’ =<br />

4 6 4 ID y<br />

4 8 19 ’+’ +<br />

4 10 6 NUMBER 1<br />

4 11 17 ’;’ ;<br />

5 1 16 ’}’ }<br />

51 / 437


Reguläre Grammatik<br />

1 // R e g u l a r e x p r e s s i o n s to be used i n token d e f i n i t i o n s<br />

2 fragment SPACE : ’ ’ | ’ \ t ’ ;<br />

3 fragment LETTER LOWER : ( ’ a ’ . . ’ z ’ ) ;<br />

4 fragment LETTER UPPER : ( ’A ’ . . ’Z ’ ) ;<br />

5 fragment DIGIT : ( ’ 0 ’ . . ’ 9 ’ ) ;<br />

6 fragment LETTERS : (LETTER LOWER | LETTER UPPER) ;<br />

7<br />

8 // Whitespace to be i g n o r e d<br />

9 NEWLINE : ( ’ \ r ’ ? ’ \ n ’)+ { $ c h a n n e l = HIDDEN ; } ;<br />

10 WHITESPACE : SPACE+ { $ c h a n n e l = HIDDEN ; } ;<br />

11 COMMENT : ’ // ’ . ∗ ’ \ n ’ { $ c h a n n e l = HIDDEN ; } ;<br />

12<br />

13 // Token d e f i n i t i o n s<br />

14 NUMBER : DIGIT +;<br />

15 INT : ’ i n t ’ ;<br />

16 ID : (LETTERS | ’ ’ ) ( LETTERS | DIGIT | ’ ’ )∗ ;<br />

52 / 437<br />

Generierung des Lexers<br />

Reguläre Grammatik<br />

// R e g u l a r e x p r e s s i o n s to be used i n token d e f i n i t i o n s<br />

fragment SPACE : ’ ’ | ’ \ t ’ ;<br />

fragment LETTER LOWER : ( ’ a ’ . . ’ z ’ ) ;<br />

. . .<br />

INT : ’ i n t ’ ;<br />

ID : (LETTERS | ’ ’ ) ( LETTERS | DIGIT | ’ ’ )∗ ;<br />

⇓<br />

Lexergenerator: antlr, (f)lex, javaCC, . . .<br />

⇓<br />

Lexer (z.B. in C, C++, Java, Ada, . . . )<br />

53 / 437


Verwendung des Lexers (AntLR)<br />

1 F i l e R e a d e r r e a d e r = new F i l e R e a d e r ( a r g s [ 0 ] ) ;<br />

2 ANTLRReaderStream i n p u t = new ANTLRReaderStream ( r e a d e r ) ;<br />

3 SATLexer l e x e r = new SATLexer ( i n p u t ) ;<br />

4 r e a d e r . c l o s e ( ) ;<br />

5 CommonTokenStream tokenStream = new CommonTokenStream ( l e x e r ) ;<br />

6<br />

7 f o r ( Object o b j : tokenStream . getTokens ( ) ) {<br />

8 CommonToken token = ( CommonToken ) o b j ;<br />

9 i f ( token . g e t C h a n n e l ( ) != CommonToken . HIDDEN CHANNEL) {<br />

10 System . out . p r i n t f<br />

11 ( ”%d:%d token : %d name : %s v a l u e ( t e x t ) : %s \ n” ,<br />

12 token . g e t L i n e ( ) ,<br />

13 token . g e t C h a r P o s i t i o n I n L i n e ( ) + 1 ,<br />

14 token . getType ( ) ,<br />

15 SATParser . tokenNames [ token . getType ( ) ] ,<br />

16 token . getText ( ) ) ;<br />

17 }<br />

18 }<br />

54 / 437<br />

Syntaxanalyse<br />

Grammatiken definieren Programmiersprachensyntax mit Regeln<br />

(BNF oder EBNF)<br />

Grammatikregel<br />

α 0 : α 1 α 2 . . . α n ; α i ∈ Σ ∪ Ω<br />

Definition<br />

Nichtterminale Ω: Symbole, die auf linker Seite einer Regel<br />

auftreten<br />

Definition<br />

Terminale Σ: Symbole, die auf keiner linken Seite einer Regel<br />

auftreten<br />

→ Tokens<br />

56 / 437


• es gibt verschiedene Grammatikklassen (LALR(k), LL(k), LR(k),<br />

. . . )<br />

• nicht für jede Programmiersprache gibt es eine offizielle Grammatik<br />

(z.B. VisualBasic 6)<br />

Beispielgrammatik in EBNF<br />

1 b l o c k : ’ { ’ d e c l s stmts ’ } ’ ;<br />

2<br />

3 d e c l s : d e c l ’ ; ’ d e c l s | ;<br />

4 d e c l : t y p e ID ;<br />

5 t y p e : INT ;<br />

6<br />

7 stmts : stmt o p t s t m t s ;<br />

8 o p t s t m t s : stmts | ;<br />

9<br />

10 stmt : ID ’= ’ e x p r ’ ; ’<br />

11 | b l o c k<br />

12 ;<br />

13<br />

14 e x p r : term ( ( ’+ ’ | ’− ’ ) term )∗ ;<br />

15 term : p r i m a r y ( ( ’ ∗ ’ | ’ / ’ ) p r i m a r y )∗ ;<br />

16 p r i m a r y : NUMBER<br />

17 | ID<br />

18 | ’ ( ’ e x p r ’ ) ’<br />

19 ;<br />

57 / 437


Konkreter Syntaxbaum<br />

Definition<br />

Konkreter Syntaxbaum (auch: Ableitungsbaum oder<br />

Parsebaum): Herleitung einer Eingabe anhand angewandter<br />

Grammatikregeln.<br />

Innere Knoten: Nichtterminale der angewandten Regeln<br />

Blätter: Terminale oder das leere Wort ɛ<br />

⇒ Links-Rechts-Traversierung mit Ausgabe der Blätter liefert das<br />

Originalprogramm<br />

58 / 437<br />

Beispiel für konkreten Syntaxbaum<br />

1 { i n t x ;<br />

2 i n t y ;<br />

3 y = 0 ;<br />

4 x = y + 1 ;<br />

5 }<br />

Tokenstrom:<br />

’{’<br />

INT<br />

ID (“x”)<br />

’;’<br />

INT<br />

ID (“y”)<br />

’;’<br />

ID (“y”)<br />

’=’<br />

NUMBER (“0”)<br />

’;’<br />

ID (“x”)<br />

’=’<br />

ID (“y”)<br />

’+’<br />

NUMBER (“1”)<br />

’;’<br />

’}’<br />

60 / 437


Beispiel eines konkreten Syntaxbaums<br />

61 / 437<br />

Der Parser<br />

Grammatik<br />

1 b l o c k : ’ { ’ d e c l s stmts ’ } ’ ;<br />

2<br />

3 . . .<br />

4 p r i m a r y : NUMBER<br />

5 | ID<br />

6 | ’ ( ’ e x p r ’ ) ’<br />

7 ;<br />

⇓<br />

Parsergenerator: AntLR, yacc, bison, cocktail95, JavaCC, . . .<br />

⇓<br />

Parser<br />

62 / 437


Verwendung des Parsers (AntLR)<br />

1 tokenStream = new TokenRewriteStream ( l e x e r ) ;<br />

2 SATParser p a r s e r = new SATParser ( tokenStream ) ;<br />

3<br />

4 SATParser . b l o c k r e t u r n r = p a r s e r . b l o c k ( ) ;<br />

5 CommonTree c t = ( CommonTree ) r . g e t T r e e ( ) ;<br />

6<br />

7 System . out . p r i n t l n ( ”#l e a v e s : ” + c t . g e t C h i l d C o u n t ( ) ) ;<br />

8 System . out . p r i n t l n ( c t . t o S t r i n g T r e e ( ) ) ;<br />

63 / 437<br />

Datenstrukturen für konkreten Syntaxbaum<br />

1 p u b l i c a b s t r a c t c l a s s Node { }<br />

2<br />

3 p u b l i c a b s t r a c t c l a s s D e c l s e x t e n d s Node { }<br />

4<br />

5 p u b l i c c l a s s D e c l s 1 e x t e n d s D e c l s {<br />

6 p r i v a t e Decl d e c l ;<br />

7 p r i v a t e CommonToken s e m i c o l o n ;<br />

8 p r i v a t e D e c l s d e c l s ;<br />

9<br />

10 p u b l i c D e c l s 1 ( Decl d e c l , CommonToken s e m i c o l o n , D e c l s d e c l s ) {<br />

11 t h i s . d e c l = d e c l ;<br />

12 t h i s . s e m i c o l o n = s e m i c o l o n ;<br />

13 t h i s . d e c l s = d e c l s ;<br />

14 }<br />

15 }<br />

16<br />

17 p u b l i c c l a s s D e c l s 2 e x t e n d s D e c l s { }<br />

64 / 437


Abstrakter Syntaxbaum<br />

Definition<br />

Abstrakter Syntaxbaum (engl. Abstract Syntax Tree AST) ist<br />

eine strukturerhaltende (homomorphe) Abstraktion des konkreten<br />

Syntaxbaums.<br />

65 / 437<br />

Wovon abstrahiert wird, ist anwendungsspezifisch.<br />

Einfache Abstraktionen:<br />

• Vermeidung von Ketten<br />

• Sequenzen ersetzen Links- und Rechtsrekursionen<br />

• irrelevante Terminale<br />

Weitergehende Abstraktionen:<br />

• ”<br />

charakteristische“ Blätter werden zu inneren Knoten<br />

• Normalisierungen (z.B. a != b wird zu !(a==b))<br />

• Unifizierungen (s.u.)


Beispiel eines abstrakten Syntaxbaums<br />

1 { i n t x ;<br />

2 i n t y ;<br />

3 y = 0 ;<br />

4 x = y + 1 ;<br />

5 }<br />

decls<br />

block<br />

1:1<br />

stmts<br />

0:0<br />

0:0<br />

var_decl y<br />

var_decl x<br />

=<br />

=<br />

1:6<br />

2:6<br />

3:4<br />

4:4<br />

int<br />

int<br />

id y<br />

literal 0<br />

id x<br />

+<br />

1:2<br />

2:2<br />

3:2<br />

3:6<br />

4:2<br />

4:8<br />

id y<br />

4:6<br />

literal 1<br />

4:10<br />

66 / 437<br />

Abstrakter Syntaxgraph (Attributierung des AST)<br />

Definition<br />

Syntaktische Kanten repräsentieren syntaktische Dekomposition<br />

(bilden Baum).<br />

Definition<br />

Semantische Kanten: alle ergänzenden Kanten, die semantische<br />

Verweise auf andere Knoten repräsentieren.<br />

Z.B. Namensverwendung → Deklaration<br />

Definition<br />

Abstract Syntax Graph (ASG): syntaktische + semantische<br />

Kanten<br />

67 / 437


Beispiel eines abstrakten Syntaxbaums<br />

block<br />

1:1<br />

decls<br />

stmts<br />

0:0<br />

0:0<br />

var_decl y<br />

var_decl x<br />

=<br />

=<br />

1:6<br />

2:6<br />

3:4<br />

4:4<br />

decl<br />

decl<br />

int<br />

1:2<br />

int<br />

2:2<br />

decl<br />

id y<br />

3:2<br />

literal 0<br />

3:6<br />

id x<br />

4:2<br />

+<br />

4:8<br />

id y<br />

literal 1<br />

4:6<br />

4:10<br />

68 / 437<br />

Abstrakte Syntax definiert Struktur aller ASTs<br />

Decls<br />

Node<br />

Seq<br />

# children : list of Node<br />

Stmts<br />

Type<br />

Int<br />

Expr<br />

Binary<br />

+ left : Expr<br />

+ right : Expr<br />

Plus<br />

Minus<br />

Atomic<br />

Literal<br />

+ value : int<br />

Unary<br />

+ operand : Expr<br />

ID<br />

+ decl : Decl // semantic<br />

+ name : string<br />

Stmt<br />

Assign<br />

+ lhs : Expr<br />

+ rhs : Expr<br />

Decl<br />

Var_Decl<br />

+ name : string<br />

+ type : Type<br />

Block<br />

+ decls : Decls<br />

+ stmts : Stmts<br />

69 / 437


Traversierungen des ASTs<br />

Definition<br />

Explizite Traversierungen: Besuchsreihenfolge wird vom<br />

Programmierer selbst festgelegt bzw. ist vordefiniert.<br />

Tiefensuche<br />

Postorder<br />

Preorder<br />

Breitensuche<br />

→ z.B. Entwurfsmuster Visitor<br />

Definition<br />

Implizite Traversierungen: Besuchsreihenfolge ergibt sich aus der<br />

Berechnung.<br />

→ z.B. Attributgrammatiken<br />

70 / 437<br />

Visitor Pattern<br />

Problem: ein Algorithmus soll alle Knoten im AST besuchen und<br />

behandeln. Beispiel: Unparse.<br />

Anforderungen:<br />

alle Knoten im AST müssen besucht werden<br />

jeder Knotentyp muss individuell behandelt werden können<br />

die Besuchsreihenfolge kann variieren<br />

weitere solche Algorithmen sollen einfach hinzugefügt werden<br />

können, ohne dass die Klassen der AST-Knoten geändert<br />

werden müssen<br />

Lösung: Visitor-Pattern<br />

71 / 437


AST Visitor Pattern<br />

...<br />

<br />

Visitor<br />

+visit_stmts()<br />

+visit_var_decl()<br />

+visit_plus()<br />

...<br />

+visit_block()<br />

Simple_Visitor<br />

+visit_stmts()<br />

+visit_var_decl()<br />

+visit_plus()<br />

...<br />

+visit_block()<br />

+visit_stmts()<br />

+visit_var_decl()<br />

+visit_plus()<br />

...<br />

+visit_block()<br />

Node<br />

visits<br />

* * +accept(visitor)<br />

<br />

Unparse_Visitor<br />

Seq<br />

# children : list of Node<br />

Type<br />

Expr<br />

Stmt<br />

Int<br />

Binary<br />

+ left : Expr<br />

+ right : Expr<br />

Atomic<br />

Unary<br />

Decls<br />

+ operand : Expr<br />

Assign<br />

+ lhs : Expr<br />

+ rhs : Expr<br />

+ accept(visitor)<br />

Decl<br />

Block<br />

Stmts<br />

+ accept(visitor v)<br />

+ decls : Decls<br />

+ stmts : Stmts<br />

+ accept(visitor)<br />

+ accept(visitor v)<br />

+ accept(visitor v)<br />

Plus<br />

+ accept(visitor v)<br />

Minus<br />

+ accept(visitor v)<br />

Literal<br />

+ value : int<br />

+ accept(visitor)<br />

ID<br />

+ decl : Decl // semantic<br />

+ name : string<br />

+ accept(visitor)<br />

Var_Decl<br />

+ name : string<br />

+ type : Type<br />

+ accept(visitor)<br />

v.visit_minus(this)<br />

v.visit_plus(this)<br />

72 / 437<br />

Beteiligte:<br />

• AST-Knoten<br />

– werden besucht<br />

– deklarieren eine accept-Operation, die die entsprechende<br />

visit-Operation aufrufen<br />

• Visitor (Interface):<br />

– Schnittstelle für alle Visitors<br />

– deklariert eine visit-Operation für jeden konkreten<br />

AST-Knotentyp<br />

• Simple-Visitor<br />

– implementiert Visitor<br />

– Standardimplementierung mit fester Besuchsreihenfolge<br />

• konkreter Visitor<br />

– leitet von Simple-Visitor ab<br />

– überschreibt ererbte visit-Operation abhängig von Aufgabe


Unparse mit Visitor: besuchbare AST-Knoten<br />

1 p u b l i c a b s t r a c t c l a s s Node {<br />

2 p r i v a t e i n t l i n e ;<br />

3 p r i v a t e i n t column ;<br />

4 p u b l i c a b s t r a c t v o i d a c c e p t ( V i s i t o r v i s i t o r ) ;<br />

5 }<br />

6<br />

7 p u b l i c a b s t r a c t c l a s s Expr e x t e n d s Node { }<br />

8<br />

9 p u b l i c a b s t r a c t c l a s s B i n a r y e x t e n d s Expr {<br />

10 p u b l i c Expr l e f t ;<br />

11 p u b l i c Expr r i g h t ;<br />

12 }<br />

13<br />

14 p u b l i c c l a s s P l u s e x t e n d s B i n a r y {<br />

15 p u b l i c v o i d a c c e p t ( V i s i t o r v i s i t o r ) {<br />

16 v i s i t o r . v i s i t p l u s ( t h i s ) ;<br />

17 }<br />

18 }<br />

73 / 437<br />

Unparse mit Visitor: Visitor-Interface<br />

1 p u b l i c i n t e r f a c e V i s i t o r {<br />

2<br />

3 p u b l i c v o i d v i s i t m i n u s ( Minus node ) ;<br />

4 p u b l i c v o i d v i s i t p l u s ( P l u s node ) ;<br />

5<br />

6 . . .<br />

7<br />

8 p u b l i c v o i d b e f o r e m u l t ( Mult node ) ;<br />

9 p u b l i c v o i d b e f o r e p l u s ( P l u s node ) ;<br />

10<br />

11 . . .<br />

12 p u b l i c v o i d a f t e r m i n u s ( Minus node ) ;<br />

13 p u b l i c v o i d a f t e r p l u s ( P l u s node ) ;<br />

14 }<br />

74 / 437


Unparse mit Visitor: Standardimplementierung<br />

1 p u b l i c c l a s s S i m p l e V i s i t o r implements V i s i t o r {<br />

2<br />

3 p r i v a t e v o i d v i s i t b i n a r y ( B i n a r y node ) {<br />

4 node . g e t L e f t ( ) . a c c e p t ( t h i s ) ;<br />

5 node . g e t R i g h t ( ) . a c c e p t ( t h i s ) ;<br />

6 }<br />

7 p u b l i c v o i d v i s i t m i n u s ( Minus node ) {<br />

8 b e f o r e m i n u s ( node ) ;<br />

9 v i s i t b i n a r y ( node ) ;<br />

10 a f t e r m i n u s ( node ) ;<br />

11 }<br />

12 p u b l i c v o i d v i s i t p l u s ( P l u s node ) {<br />

13 b e f o r e p l u s ( node ) ;<br />

14 v i s i t b i n a r y ( node ) ;<br />

15 a f t e r p l u s ( node ) ;<br />

16 }<br />

17 . . .<br />

75 / 437<br />

Unparse mit Visitor: Standardimplementierung<br />

1 . . .<br />

2 p u b l i c v o i d b e f o r e m i n u s ( Minus node ) { }<br />

3 p u b l i c v o i d b e f o r e p l u s ( P l u s node ) { }<br />

4 . . .<br />

5 p u b l i c v o i d a f t e r m i n u s ( Minus node ) { }<br />

6 p u b l i c v o i d a f t e r p l u s ( P l u s node ) { }<br />

7 . . .<br />

76 / 437


Unparse mit Visitor: Unparse-Implementierung<br />

1 p u b l i c c l a s s Unparser e x t e n d s S i m p l e V i s i t o r {<br />

2<br />

3 p r i v a t e v o i d v i s i t b i n a r y ( B i n a r y node , S t r i n g op ) {<br />

4 node . l e f t . a c c e p t ( t h i s ) ;<br />

5 System . out . p r i n t ( op ) ;<br />

6 node . r i g h t . a c c e p t ( t h i s ) ;<br />

7 }<br />

8<br />

9 p u b l i c v o i d v i s i t m i n u s ( Minus node ) {<br />

10 v i s i t b i n a r y ( node , ” − ” ) ;<br />

11 }<br />

12<br />

13 p u b l i c v o i d v i s i t p l u s ( P l u s node ) {<br />

14 v i s i t b i n a r y ( node , ” + ” ) ;<br />

15 }<br />

16 . . .<br />

77 / 437<br />

Attributgrammatiken<br />

Probleme des Visitor-Musters:<br />

Visitor ist programmatisch und erfordert viel Kodieraufwand<br />

Visitor reicht nur für einfache Aufgaben aus<br />

Attributgrammatiken für den Syntaxbaum:<br />

beschreiben Berechnung von Knotenattributen<br />

Berechnung ist deklarativ<br />

Traversierungsreihenfolge ergibt sich aus<br />

Berechnungsvorschrift<br />

automatische Code-Generierung ist möglich<br />

78 / 437


Attributgrammatiken<br />

Definition<br />

Eine Attributgrammatik besteht aus:<br />

Beispiel:<br />

Attributen für Grammatiksymbole (Nichtterminal und<br />

Terminal) bzw. Baumknoten<br />

deklarativen Regeln zur Berechnung der Attribute<br />

1 e x p r r e t u r n s [ Expr t r e e ] :<br />

2 t=term o= ’+ ’ e=e x p r { $ t r e e = new P l u s ( $t . t r e e , $e . t r e e ) ; }<br />

3 | t=term o= ’− ’ e=e x p r { $ t r e e = new Minus ( $t . t r e e , $e . t r e e ) ; }<br />

4 | t=term { $ t r e e = $t . t r e e ; }<br />

5 ;<br />

79 / 437<br />

Arten von Attributen<br />

Definition<br />

Ein Attribut eines Nichtterminals ist synthetisiert, wenn es nur<br />

von Attributen von Symbolen der rechten Seite seiner Regeln<br />

abhängt.<br />

1 e x p r r e t u r n s [ Expr t r e e ] :<br />

2 t=term o= ’+ ’ e=e x p r { $ t r e e = new P l u s ( $t . t r e e , $e . t r e e ) ; }<br />

3 | t=term o= ’− ’ e=e x p r { $ t r e e = new Minus ( $t . t r e e , $e . t r e e ) ; }<br />

4 | t=term { $ t r e e = $t . t r e e ; }<br />

5 ;<br />

80 / 437


Arten von Attributen<br />

Definition<br />

Ein Attribut eines Nichtterminals ist ererbt, wenn es nur von<br />

Attributen von Symbolen<br />

der linken Seite und<br />

von ”<br />

Geschwistersymbolen“<br />

der Regeln abhängt, in denen das Symbol auf einer rechten Seite<br />

auftritt.<br />

Ein ”<br />

Geschwistersymbol“ ist ein Symbol, das auf der selben<br />

rechten Seite auftritt.<br />

81 / 437<br />

Arten von Attributen<br />

1 e x p r r e t u r n s [ Expr t r e e ]<br />

2 : t=term e=e x p r c o n t i n u e d [ $t . t r e e ] { $ t r e e = $e . t r e e ; }<br />

3 ;<br />

4<br />

5 e x p r c o n t i n u e d [ Expr i n h ] r e t u r n s [ Expr t r e e ]<br />

6 : o= ’+ ’<br />

7 t=term<br />

8 e=e x p r c o n t i n u e d<br />

9 [ new P l u s ( $o . l i n e , $o . column , $inh , $t . t r e e ) ]<br />

10 { $ t r e e = $e . t r e e ; }<br />

11 | o= ’− ’<br />

12 t=term<br />

13 e=e x p r c o n t i n u e d<br />

14 [ new Minus ( $o . l i n e , $o . column , $inh , $t . t r e e ) ]<br />

15 { $ t r e e = $e . t r e e ; }<br />

16 | { $ t r e e = $ i n h ; }<br />

17 ;<br />

82 / 437


Datenfluss zwischen Attributen<br />

expr<br />

tree<br />

term<br />

tree<br />

expr_continued<br />

inh<br />

tree<br />

+ term expr_continued<br />

tree<br />

inh<br />

tree<br />

− term expr_continued<br />

tree<br />

ɛ<br />

inh<br />

tree<br />

83 / 437<br />

Anforderungen an Zwischendarstellungen<br />

möglichst quellennah ↔ möglichst vereinheitlicht<br />

84 / 437


widersprüchliche Anforderungen:<br />

1. möglichst quellennah, da Informationen an Wartungsprogrammierer<br />

ausgegeben werden sollen bzw. tatsächlicher Code wieder generiert<br />

werden soll<br />

⇒ alle spezifischen Konstrukte müssen dargestellt werden<br />

2. möglichst vereinheitlicht, um das Schreiben von Programmanalysen<br />

für verschiedene Programmiersprachen zu vereinfachen<br />

⇒ möglichst wenige, allgemeine Konstrukte<br />

Quellennahe Betrachtung<br />

while P loop<br />

A;<br />

end loop;<br />

cond<br />

P<br />

while<br />

A<br />

body<br />

85 / 437


Einfache allgemeine Konstrukte<br />

while P loop<br />

A;<br />

end loop;<br />

ist äquivalent zu:<br />

loop<br />

if P then<br />

A;<br />

else<br />

exit;<br />

end if;<br />

end loop;<br />

P<br />

loop<br />

body then<br />

if<br />

cond else<br />

A<br />

exit<br />

86 / 437<br />

Vereinigung der Sichten<br />

P<br />

loop<br />

body then<br />

if<br />

cond else<br />

A<br />

exit<br />

cond<br />

P<br />

while<br />

A<br />

body<br />

inheritance<br />

hierarchy:<br />

loop<br />

is a<br />

for while<br />

is a loop<br />

real body<br />

cond while<br />

body A<br />

then<br />

P<br />

cond if<br />

else exit<br />

syntaktische Kante<br />

Annotation<br />

87 / 437


Kontrollflussinformation<br />

main<br />

entry<br />

intraprozedural: Flussgraph<br />

Knoten: Grundblöcke<br />

Kanten: (bedingter/unbedingter)<br />

Kontrollfluss<br />

interprozedural: Aufrufgraph<br />

Multigraph<br />

Knoten: Prozeduren<br />

Kanten: Aufruf (eine für jede<br />

Aufrufstelle); Schleifen → Rekursion<br />

t<br />

a = 1;<br />

b = 2;<br />

a>0 f<br />

exit<br />

main<br />

c = f(c);<br />

f<br />

89 / 437<br />

Intraprozeduraler Kontrollfluss ergibt sich aus syntaktischer Struktur und<br />

etwaigen Gotos, Exits, Continues etc.<br />

Interprozeduraler Kontrollfluss ergibt sich aus expliziten Aufrufen im<br />

Programmcode sowie aus Aufrufen über Funktionszeiger


Intra- und interprozeduraler Kontrollfluss<br />

main<br />

int f (int p)<br />

entry<br />

entry<br />

t<br />

a>0 f<br />

c→ p<br />

r = 0;<br />

x = p;<br />

a = 1;<br />

b = 2;<br />

c = f(c);<br />

f<br />

x>0<br />

exit<br />

r→ c<br />

t<br />

r = r + p;<br />

x = x - 1;<br />

exit & return r<br />

90 / 437<br />

Fragestellungen zum Kontrollfluss<br />

Was wird garantiert vorher ausgeführt?<br />

Was wird garantiert nachher ausgeführt?<br />

91 / 437


• Fragestellungen:<br />

– Welche Prozeduren sind lokal zueinander?<br />

Genauer: Gibt es eine Prozedur D, über nur die ein Aufruf von<br />

N erfolgt?<br />

– Welcher Block D im Flussgraph muss in jedem Falle passiert<br />

werden, damit Block N ausgeführt werden kann?<br />

→ Antwort: D ist der Dominator von N<br />

Dominanz<br />

Definition<br />

Ein Knoten D dominiert einen Knoten N (D dom N), wenn D auf<br />

allen Pfaden vom Startknoten zu N liegt.<br />

Ein Knoten D dominiert N strikt, wenn D dom N ∧ D N .<br />

Ein Knoten D ist der direkte Dominator von N (idom(N)=D),<br />

wenn<br />

1 D ist ein strikter Dominator von N: D dom N (D N) und<br />

2 alle weiteren Dominatoren von N dominieren D:<br />

∀D ′ dom N : (D ′ dom D ∨ D ′ = D)<br />

92 / 437


Dominanz<br />

Graph<br />

4<br />

2<br />

5<br />

9<br />

1<br />

3<br />

6<br />

7<br />

8<br />

Dominanzbaum<br />

1<br />

2 5 10 3<br />

4 9 6 7<br />

10<br />

11<br />

12<br />

8 11 12<br />

93 / 437<br />

Postdominanz<br />

Definition<br />

Ein Knoten D postdominiert einen Knoten N, wenn jeder Pfad<br />

von N zum Endknoten den Knoten D enthält (entspricht<br />

Dominanz des umgekehrten Graphen).<br />

Graph<br />

1<br />

Postdominanzbaum<br />

2<br />

3<br />

exit<br />

4<br />

5<br />

9<br />

6<br />

7<br />

8<br />

1 11 10 3 6<br />

12 9 2 4 7<br />

10<br />

exit 11<br />

12<br />

8<br />

5<br />

94 / 437


Kontrollabhängigkeit<br />

Definition<br />

Knoten X ist kontrollabhängig von Bedingung B, wenn B<br />

entscheiden kann, ob X ausgeführt wird:<br />

1 B muss mehrere direkte Nachfolger haben und<br />

2 B hat einen Pfad zum Endknoten, der X vermeidet (d.h. B<br />

kann nicht von X postdominiert werden) und<br />

3 B hat einen Pfad zu X, d.h. insgesamt hat B mindestens zwei<br />

Pfade:<br />

einer führt zu X<br />

einer umgeht X<br />

95 / 437<br />

Kontrollabhängigkeit<br />

Definition<br />

Ein Knoten X ist direkt kontrollabhängig von einem Knoten B<br />

genau dann, wenn<br />

1 es einen nicht-leeren Pfad von B nach X gibt, so dass X jeden<br />

Knoten auf dem Pfad (ohne B) postdominiert und<br />

2 entweder X = B oder X postdominiert B nicht<br />

B<br />

a<br />

X<br />

für alle a:<br />

X postdominiert a<br />

B=X<br />

a<br />

für alle a:<br />

X postdominiert a<br />

exit<br />

exit<br />

transitiver Kontrollfluss<br />

96 / 437


Kontrollabhängigkeit<br />

Definition<br />

Ein Knoten X ist direkt kontrollabhängig von einer Kante (B, S)<br />

genau dann, wenn<br />

1 es einen nicht-leeren Pfad beginnend mit (B, S) nach X gibt,<br />

so dass X jeden Knoten auf dem Pfad (ohne B) postdominiert<br />

und<br />

2 entweder X = B oder X postdominiert B nicht<br />

B<br />

S<br />

X<br />

B=X<br />

a<br />

a<br />

S<br />

für alle a:<br />

X postdominiert a für alle a:<br />

X postdominiert a<br />

exit<br />

exit<br />

transitiver Kontrollfluss<br />

direkter Kontrollfluss<br />

97 / 437<br />

Kontrollabhängigkeit<br />

1 f u n c t i o n CD ( (B, S ) : Edge ) r e t u r n s e t o f nodes<br />

2 b e g i n −− c o n t r o l dependency<br />

3<br />

4 depends := ∅ ;<br />

5 X := S ;<br />

6<br />

7 w h i l e X pdom (B) l o o p<br />

8 depends := depends ∪ {X } ;<br />

9 X := pdom (X ) ;<br />

10 end l o o p ;<br />

11<br />

12 r e t u r n depends ;<br />

13 end CD;<br />

98 / 437


Kontrollabhängigkeit<br />

Beispiel für Kontrollabhängigkeit<br />

KFG<br />

if (b1) {<br />

do {<br />

a;<br />

} while<br />

(b2);<br />

}<br />

f<br />

b1<br />

t<br />

a<br />

b2<br />

f<br />

exit<br />

t<br />

Postdominanz<br />

exit<br />

b1 b2<br />

a<br />

(b1, a)<br />

(b1, exit)<br />

(b2, a)<br />

(b2, exit)<br />

99 / 437<br />

(b1, a) a, b2<br />

(b1, exit) –<br />

(b2, a) a, b2<br />

(b2, exit) –


Kontrollabhängigkeit<br />

Für strukturierte Programme: eine Anweisung ist kontrollabhängig<br />

von der Bedingung der nächstumgebenden Schleife oder bedingten<br />

Anweisung.<br />

1 w h i l e ( a ) {<br />

2 i f ( b ) {<br />

3 x = y ; // k o n t r o l l a b h ä n g i g von b<br />

4 }<br />

5 z = 1 ; // k o n t r o l l a b h ä n g i g von a<br />

6 do {<br />

7 z = z + 1 ;<br />

8 }<br />

9 w h i l e ( z < 10)<br />

10 }<br />

100 / 437<br />

N.B.: Bedingungen in repeat-Schleifen sind von sich und dem<br />

umgebenden Konstrukt abhängig (siehe voriges Beispiel).


Repräsentation der Kontrollabhängigkeit<br />

b1<br />

t<br />

t<br />

f<br />

t<br />

t<br />

a<br />

t<br />

t<br />

b2<br />

f<br />

exit<br />

101 / 437<br />

Datenabhängigkeiten<br />

Datenflussrelevante Operationen:<br />

Set = Setzen eines Wertes<br />

Use = Verwendung eines Wertes<br />

Datenabhängigkeiten<br />

Set-Use (Datenabhängigkeit)<br />

Use-Set (Anti-Dependency)<br />

Set-Set (Output-Dependency)<br />

103 / 437


• Set-Use Beziehung (Datenabhängigkeit)<br />

A setzt den Wert, der von B verwendet wird<br />

• Use-Set Beziehung (Anti-Dependency)<br />

A liest den Wert und B überschreibt ihn danach<br />

• Set-Set Beziehung (Output-Dependency)<br />

der von A gesetzte Wert wird von B überschrieben<br />

Codetransformationen müssen diese Beziehungen erhalten.<br />

Datenabhängigkeit (Set-Use)<br />

a := 10;<br />

b := a + 1;<br />

a := 2 * a;<br />

c := a + b;<br />

104 / 437


Wichtigste Beziehung: Set-Use<br />

Zwischen der 2. und 3. Anweisung besteht eine ”<br />

Use-Set“, zwischen der<br />

1. und 3. Anweisung eine ”<br />

Set-Set“-Beziehung.<br />

Datenflussprobleme<br />

Gültige Definitionen (Reaching Definitions)<br />

. . . beantwortet die Frage, welche Definition einer Variablen (d.h.<br />

Zuweisungen an diese Variable) welche Verwendungen der selben<br />

Variablen erreichen.<br />

Definition<br />

Eine Definition D erreicht einen Punkt P, falls es einen Weg von<br />

D nach P gibt, auf dem keine weitere Zuweisung an die in D<br />

gesetzte Variable V erfolgt.<br />

105 / 437


Beispielprogramm<br />

1 v o i d printSum ( f l o a t a [ ] ) {<br />

2 i n t i ;<br />

3 f l o a t s ;<br />

4 f l o a t v ;<br />

5<br />

6 i = 0 ;<br />

7 s = 0 . 0 ;<br />

8 v = 0 . 0 ;<br />

9<br />

10 w h i l e ( i < a . l e n g t h ) {<br />

11 v = a [ i ] ;<br />

12 i = i + 1 ;<br />

13 s = s + v ;<br />

14 }<br />

15<br />

16 p r i n t ( ”sum” , s ) ;<br />

17 }<br />

106 / 437<br />

Grundlage Kontrollflussgraph<br />

B0<br />

entry<br />

B1<br />

6: i = 0<br />

7: s = 0.0<br />

8: v = 0.0<br />

B2<br />

B4<br />

10: i < a.length<br />

16: print("sum", s)<br />

f<br />

t<br />

B3<br />

11: v = a[i]<br />

12: i = i + 1<br />

13: s = s + v<br />

B5<br />

exit<br />

107 / 437


Zuweisungen im Kontrollflussgraph<br />

I. Allg.: Konservative Annahme:<br />

Jeder Aufruf einer unbekannten Methode liest und ändert alle<br />

Referenzparameter sowie das betroffene Objekt und liest alle<br />

Werteparameter.<br />

Im Beispiel:<br />

Anweisung zugewiesene Variablen (Set) gelesene Variablen (Use)<br />

6 i<br />

7 s<br />

8 v<br />

10 i, a<br />

11 v i, a<br />

12 i i<br />

13 s s, v<br />

16 s<br />

108 / 437<br />

Lokale Datenflussanalyse<br />

Lokale Information pro Grundblock B:<br />

GEN(B) = Menge aller Zuweisungen in B, die das Ende von<br />

B erreichen (lokale Analyse im Grundblock).<br />

KILL(B) = Menge aller Zuweisungen im gesamten<br />

Kontrollflussgraphen (KFG), deren Variable in B neu gesetzt<br />

wird und nicht in GEN(B) enthalten ist (flussinsensitive<br />

Analyse im KFG).<br />

Im Beispiel:<br />

Block GEN KILL<br />

B0<br />

B1 6,7,8 11,12,13<br />

B2<br />

B3 11,12,13 6,7,8<br />

B4<br />

B5<br />

109 / 437


Globale Datenflussanalyse über Kontrollflussgraph<br />

Grundblock hängt von seinen Vorgängern ab:<br />

in(B) = Definitionen, die den Eingang von B erreichen<br />

out(B) = Definitionen, die den Ausgang von B erreichen<br />

Datenflussgleichungen:<br />

in(B) =<br />

⋃<br />

B ′ ist Vorgänger von B<br />

out(B ′ )<br />

out(B) = (in(B) − KILL(B)) ∪ GEN(B)<br />

110 / 437<br />

→ rekursives Problem<br />

→ gesucht ist der kleinste Fixpunkt


Iterative Lösung der Gleichungen<br />

1 f o r each b l o c k B l o o p<br />

2 out (B) := GEN(B ) ;<br />

3 end l o o p ;<br />

4<br />

5 s t a b l e := f a l s e ;<br />

6 w h i l e ¬ s t a b l e l o o p<br />

7 s t a b l e := t r u e ;<br />

8 f o r each b l o c k B l o o p<br />

9 i n (B) := ⋃ B ′ →B out(B′ ) ;<br />

10 o l d o u t := out (B ) ;<br />

11 out (B) := ( i n (B) − KILL (B) ) ∪ GEN(B ) ;<br />

12 i f out (B) o l d o u t then<br />

13 s t a b l e := f a l s e ;<br />

14 end i f ;<br />

15 end l o o p ;<br />

16 end l o o p ;<br />

111 / 437<br />

Iterative Lösung der Gleichungen mit Worklist<br />

N = Menge aller Grundblöcke im Kontrollflussgraph<br />

1 f o r each b l o c k B l o o p<br />

2 out (B) := GEN(B ) ;<br />

3 end l o o p ;<br />

4 wl := N − {entry} ;<br />

5<br />

6 l o o p<br />

7 B := f i r s t ( wl ) ;<br />

8 wl := wl − {B} ;<br />

9 e f f e c t := ∅ ;<br />

10 f o r each P ∈ P r e d e c e s s o r s (B) l o o p<br />

11 e f f e c t := e f f e c t ∪ out (P ) ;<br />

12 end l o o p ;<br />

13 i f i n (B) e f f e c t then<br />

14 i n (B) := e f f e c t ;<br />

15 out (B) := ( i n (B) − KILL (B) ) ∪ GEN(B ) ;<br />

16 wl := wl ∪ S u c c e s s o r s (B ) ;<br />

17 end i f ;<br />

18 e x i t when wl = ∅ ;<br />

19 end l o o p ;<br />

112 / 437


Beispiel<br />

B wl B1 B2 B3 B4<br />

gen kill gen kill gen kill gen kill<br />

6-8 11-13 ∅ ∅ 11-13 6-8 ∅ ∅<br />

in out in out in out in out<br />

– B1,B2,B3,B4 ∅ 6-8 ∅ ∅ ∅ 11-13 ∅ ∅<br />

113 / 437<br />

Beispiel für Worklist-Algorithmus<br />

B wl B1 B2 B3<br />

gen kill gen kill gen kill gen<br />

6-8 11-13 ∅ ∅ 11-13 6-8 ∅<br />

in out in out in out in<br />

– B1,B2,B3,B4 ∅ 6-8 ∅ ∅ ∅ 11-13 ∅<br />

B1 B1,B2,B3,B4 ∅ 6-8 ∅ ∅ ∅ 11-13 ∅<br />

B2 B2,B3,B4 ∅ 6-8 6-8,11-13 6-8,11-13 ∅ 11-13 ∅<br />

B3 B3,B4 ∅ 6-8 6-8,11-13 6-8,11-13 6-8,11-13 11-13 ∅<br />

B4 B4,B2 ∅ 6-8 6-8,11-13 6-8,11-13 6-8,11-13 11-13 6-8,11-13<br />

B2 B2 ∅ 6-8 6-8,11-13 6-8,11-13 6-8,11-13 11-13 6-8,11-13


Terminierung des Algorithmus<br />

Der Algorithmus terminiert, weil<br />

wenn keine Menge mehr wächst, ist Abbruchbedingung erfüllt<br />

ansonsten: eine Menge wird größer<br />

die Mengen wachsen monoton<br />

die Mengen sind nach oben begrenzt (durch alle Definitionen<br />

der Methode) und können nicht beliebig wachsen<br />

114 / 437<br />

Repräsentation der Datenabhängigkeit<br />

x < 0<br />

a := 1; y > 0<br />

a := 2; a := 3;<br />

z < y<br />

. . . := a;<br />

. . . := a;<br />

. . . := a;<br />

115 / 437


Repräsentation der Datenabhängigkeit<br />

Definition<br />

Ein Programm ist in der Static Single Assignment Form (SSA),<br />

wenn jede Variablenverwendung (Use) nur eine Definition (Set)<br />

hat.<br />

116 / 437<br />

Wunsch nach einer kompakteren Darstellung: Nur eine Definition für jede<br />

Verwendung.<br />

Wird erreicht durch eingeführte künstliche Zuweisungen, sogenannte<br />

φ-Knoten:<br />

• am Anfang der Grundblöcke, an denen Kontrollflüsse<br />

zusammenlaufen,<br />

• auf denen verschiedene Definitionen der gleichen Variablen liegen.


Static Single Assignment Form<br />

x < 0<br />

a := 1; y > 0<br />

a := 2; a := 3;<br />

a := φ(a,a)<br />

z < y<br />

a := φ(a,a)<br />

. . . := a;<br />

. . . := a;<br />

. . . := a;<br />

117 / 437<br />

Beispiel der SSA<br />

1 x , y , z : T; x 0 := i n i t ; y 0 := i n i t ; z 0 := i n i t ;<br />

2 i f cond1 then<br />

3 y 1 := 4 ;<br />

4 x 1 := 5 ;<br />

5 e l s e<br />

6 y 2 := 3 ;<br />

7 end i f ;<br />

8 x 2 := φ( x 0 , x 1 ) ; y 3 := φ( y 1 , y 2 ) ;<br />

9 w h i l e z 1 := φ( z 0 , z 5 ) ; cond2 l o o p<br />

10 i f y 3 > 0 then<br />

11 z 2 := x 2 + z 1 ;<br />

12 end i f ;<br />

13 z 3 := φ( z 1 , z 2 )<br />

14 i f y 3


Beispielanwendung<br />

Problem: Finde alle potenziell lokal uninitialisierten Variablen<br />

Lösung mit SSA:<br />

für jede initiale Definition D:<br />

propagiere D längs der Use-Kanten und markiere alle dabei<br />

erreichten Verwendungen von Variablen<br />

jede markierte Verwendung einer Variablen ist potenziell<br />

undefiniert<br />

119 / 437<br />

Einfügepunkte für φ-Knoten<br />

φ-Knoten werden eingefügt . . .<br />

am Anfang der Grundblöcke, an denen Kontrollflüsse<br />

zusammenlaufen,<br />

auf denen verschiedene Definitionen der gleichen Variablen<br />

liegen.<br />

→ Wie lassen sich diese Punkte effizient algorithmisch bestimmen?<br />

121 / 437


Einfügepunkte für φ-Knoten (Cytron u. a. 1991)<br />

entry<br />

1: a = init<br />

b<br />

2: a = 1<br />

. . .<br />

3: a = 2<br />

b ′<br />

c 1 ¬b dom c 1<br />

b dom c 1<br />

′ b ′ dom c 2<br />

′<br />

c 1<br />

′ c 2<br />

′<br />

→ c 1 , b ′ ∈ Dominanzgrenzen({entry, b, b ′ })<br />

122 / 437<br />

Dominanzgrenzen<br />

Definition<br />

Die Dominanzgrenze (engl. Dominance Frontier) DF (B) eines<br />

Blocks B ist die Menge aller Blöcke C wie folgt:<br />

1 B dom C ′ ∧ C ′ ist unmittelbarer Vorgänger von C im<br />

Kontrollflussgraph und<br />

2 entweder B = C oder ¬B dom C<br />

(d.h. B ist kein strikter Dominator von C)<br />

123 / 437


Anschaulich: Dominanzgrenze enthält alle Blöcke, die aus dem<br />

Dominanzteilbaum mit der Wurzel B (exklusive B selbst) heraus führen.<br />

Dominanzgrenzen<br />

b0<br />

b0<br />

b1<br />

b2<br />

b3<br />

b5<br />

b4<br />

b3<br />

b2<br />

b1<br />

b4<br />

b5<br />

b6<br />

b0<br />

b1<br />

b2<br />

b3<br />

b4<br />

b5<br />

b6<br />

DF<br />

∅<br />

b1, b6<br />

b2, b5<br />

b2, b5<br />

b3<br />

b1, b6<br />

∅<br />

b6<br />

dom<br />

control<br />

124 / 437


0<br />

b1<br />

b2<br />

b3<br />

b4<br />

b5<br />

b6<br />

DF<br />

∅<br />

b1, b6<br />

b2, b5<br />

b2, b5<br />

b3<br />

b1, b6<br />

∅<br />

SSA-Aufbau<br />

1 f o r V ∈ Variables l o o p<br />

2 wl := {B|B contains Set(V )} ∪ {entry}<br />

3 done := ∅<br />

4 w h i l e wl ∅ l o o p<br />

5 s e l e c t any B ∈ wl<br />

6 wl := wl − {B}<br />

7 f o r B ′ ∈ DF (B) l o o p<br />

8 i f B ′ done then<br />

9 n := #p r e d e c e s s o r s (B ′ )<br />

10 i n s e r t ”V ← φ(V , . . . , V )” i n t o B ′ with n arguments<br />

11 wl := wl ∪ { B ′ }<br />

12 done := done ∪ { B ′ }<br />

13 end i f<br />

14 end l o o p<br />

15 end l o o p<br />

16 end l o o p<br />

125 / 437


• für jedes Set(V) in einem Block B müssen φ-Knoten in allen<br />

Blöcken B ′ ∈ DF (B) eingefügt werden<br />

• φ-Knoten sind auch Zuweisungen<br />

Varianten von Set<br />

Sicherheit der Überschreibung:<br />

Starke Aktualisierung (must-set): v = 1;<br />

Schwache Aktualisierung (may-set):<br />

p = &v; if (foo()) p = &w; *p = 1;<br />

Umfang der Überschreibung:<br />

Vollständige Aktualisierung: v = 1;<br />

Teilweise Aktualisierung: a[i] = 1;<br />

126 / 437


Wichtige Mengen beim Aufbau von SSA<br />

Anweisung A:<br />

Must-Set (A)<br />

May-Set (A)<br />

May-Use (A)<br />

1 a 1 = 1 ; // Must−Set ( 1 ) = { a }<br />

2 f (&a ) ; // May−Set ( 2 ) = { a } , May−Use ( 2 ) = { a }<br />

3 a 2 = φ(a 1 , a f ) // a f r e p r ä s e n t i e r t den Wert aus f<br />

4 . . . a 2 . . . // May−Use ( 4 ) = { a }<br />

127 / 437<br />

• Must-Set (A):<br />

Menge von Variablen, die sicher von A verändert werden<br />

→ identifiziert die Definitionen<br />

• May-Set (A):<br />

Menge von Variablen, die möglicherweise von A verändert werden<br />

→ hier muss ein φ-Knoten eingefügt werden, um schwache<br />

Aktualisierung zu repräsentieren<br />

• May-Use (A):<br />

Menge von Variablen, die möglicherweise von A verwendet werden<br />

→ bei allen Verwendungen müssen Verweise auf Definition<br />

eingetragen werden<br />

N.B.: May-Set und Must-Set sind disjunkt.


Probleme beim Aufbau von SSA<br />

1 a := 1 ;<br />

2 b := 2 ;<br />

3 c := a + b ;<br />

128 / 437<br />

Was ist der Wert von c?


Probleme beim Aufbau von SSA<br />

Definition<br />

Aliasing: Zwei Namen sind Aliase, wenn sie auf überlappende<br />

Speicherbereiche verweisen.<br />

1 y := 5 ;<br />

2<br />

3 i f . . . then<br />

4 x := 6 ;<br />

5 end i f ;<br />

6 . . .<br />

7 . . . = y ;<br />

1 y := 5 ;<br />

2<br />

3 i f . . . then<br />

4 x := 6 ; −− i f I s A l i a s ( x , y ) then y := x ; end i f ;<br />

5 end i f ;<br />

6 . . .<br />

7 . . . = y ;<br />

1 y := 5 ;<br />

2<br />

3 i f . . . then<br />

4 x := 6 ; −− i f I s A l i a s ( x , y ) then y := x ; end i f ;<br />

5 end i f ;<br />

6 . Problem: . . y := φ( y , y ) ;<br />

7 . x . .:= = ...;– y ; falls Alias(x,y), dann ist Zuweisung an x auch Zuweisung an<br />

y<br />

1 1 := 5 ;<br />

2<br />

3 i f . . . then<br />

4 x 1 := 6 ; −− i f I s A l i a s ( x , y ) then y 2 := x 1 ; end i f ;<br />

5 end i f ;<br />

6 . . . y 3 := φ( y 1 , y 2 ) ;<br />

7 . . . = y 3 ;<br />

129 / 437


Aliasing-Varianten<br />

parameter-induziertes Aliasing durch Referenzparameter<br />

p (x, x) (bzw. p(&x, &x) in C und C++)<br />

p (g), wobei g eine globalere Variable ist und in p verwendet<br />

wird<br />

Arraykomponenten<br />

a [2 * i] und a [ j ]<br />

130 / 437<br />

• parameter-induziertes Aliasing durch Referenzparameter<br />

– Lösung: Propagierung der Alias-Info über den Call-Graphen<br />

– (Ansonsten konservative Annahme: zwei Referenzparameter<br />

bzw. globale Variable/Referenzparameter sind Aliase; eventuell<br />

kann Typinformation helfen)<br />

• Arraykomponenten<br />

– Lösungsansatz: Löse Gleichung 2*i = j<br />

– Ansonsten: Modelliere Zuweisungen an Teilkomponente als<br />

Zuweisung an gesamtes Array: Partial Update/Read m.a.W.<br />

konservative Annahme, dass jeder Index sich auf alle<br />

Array-Elemente beziehen kann.


Aliasing-Varianten<br />

stack-gerichtete Zeiger<br />

p := &x;<br />

heap-gerichtete Zeiger<br />

p := new T; q := new T; q.next = p;<br />

131 / 437<br />

• stack-gerichtete Zeiger<br />

– Lösungsansatz: Points-To-Analyse<br />

– Konservative Annahme: *p könnte jede statische Variable<br />

betreffen, deren Adresse genommen wird.<br />

• heap-gerichtete Zeiger<br />

– Lösungsansatz: ”<br />

Shape-Analyse“<br />

– Konservative Annahme: *p kann jedes Element des Heaps<br />

betreffen.


Probleme beim Aufbau von SSA<br />

Aufrufe von Unterprogrammen (Staiger u. a. 2007b, a)<br />

1 x := 1 ;<br />

2 f ( a , b ) ;<br />

3 −− hat x noch den Wert 1?<br />

132 / 437<br />

Welchen äußeren Effekt haben Unterprogrammaufrufe?<br />

• Lösung: Globale Analysen auf Seiteneffekte (aber: Problem von<br />

Bibliotheksroutinen, deren Quell-Code nicht vorhanden ist)<br />

• Ansonsten konservative Annahme: Aufruf eines Unterprogramms U<br />

verändert alle globalen Variablen (nicht nur jene im<br />

Sichtbarkeitsbereich von U) sowie jeden Referenzparameter<br />

• optimistische Annahme: . . . verändert nur seine Referenzparameter<br />

und über sie erreichbare Variable


Phasenspezifische Analysen (Abstraktionsebenen) I<br />

Programmtext mit Makros<br />

Suche nach Zeichenketten (einschließlich Makros)<br />

Programmtext nach Makro-Expansion<br />

Suche nach Zeichenketten (ohne Makros)<br />

Token-Strom<br />

Zeichenketten sind klassifiziert<br />

Suche nach Tokens<br />

abstrakter Syntaxbaum<br />

gibt syntaktische Dekomposition wieder<br />

Suche nach syntaktischen Mustern; evtl. auch spezialisierte<br />

Attributberechnungen<br />

133 / 437<br />

Phasenspezifische Analysen (Abstraktionsebenen) II<br />

attributierter abstrakter Syntaxbaum<br />

Namensbindung und Typinformation verfügbar<br />

Cross-Reference-Information<br />

Suche nach spezifischen Variablen möglich<br />

Kontroll- und Datenflussinformation<br />

Kontrollflussgraph<br />

Aufrufgraph<br />

explizit<br />

Aufrufe über Funktionszeiger<br />

Explizite Darstellung von Datenabhängigkeiten<br />

Alias-Information<br />

Points-To-Information<br />

134 / 437


Wiederholungs- und Vertiefungsfragen I<br />

Warum normalisiert gcc viele Anweisungen des Quellcodes?<br />

Warum sind diese Art Modifikationen für RE nicht<br />

wünschenswert?<br />

Wie kann man Quellennähe erreichen?<br />

Was ist ein abstrakter Syntaxbaum?<br />

Gegeben ist die folgende Grammatik. . . Geben Sie hierfür eine<br />

abstrakte Syntax an.<br />

Wie erreicht man sowohl Quellennähe als auch<br />

Vereinheitlichung?<br />

Selbe Frage mit konkretem Beispiel für Case-Anweisung in der<br />

Programmiersprachen Ada mittels if-Anweisung.<br />

Funktioniert die Umsetzung analog für C-Switch?<br />

Abstrakter Syntaxbaum gibt nur syntaktische Dekomposition<br />

wieder. Wie kann Kontrollfluss explizit dargestellt werden?<br />

135 / 437<br />

Wiederholungs- und Vertiefungsfragen II<br />

Wie ergibt sich Kontrollabhängigkeit aus dem Flussgraphen<br />

(grob skizziert)?<br />

Lässt sich Kontrollflussabhängigkeit auch direkt aus der<br />

Syntax ermitteln?<br />

Wie können Datenabhängigkeiten explizit repräsentiert<br />

werden? Datenabhängigkeitskanten (SSA)<br />

Welche Eigenschaft hat die SSA?<br />

Wie wird diese Eigenschaft erreicht?<br />

Wo werden φ-Knoten eingefügt?<br />

Welche weiteren Datenabhängigkeiten gibt es?<br />

Warum sind diese Abhängigkeiten für das RE überhaupt<br />

interessant?<br />

Wo werden Set / Use und Kontrollabhängigkeiten im RE noch<br />

benutzt?<br />

136 / 437


Literatur<br />

Muchnick (1997): gutes Lehrbuch zu Compilerbau<br />

Morgan (1998): Zwischendarstellungen sowie Kontroll- und<br />

Datenflussanalysen; enthält eine bessere Beschreibung der<br />

SSA als Muchnick (1997)<br />

Koschke u. a. (1998): Zwischendarstellung für das Reverse<br />

Engineering und dessen spezielle Anforderungen<br />

Plödereder (2008): Folien, Skript und Übungen zur <strong>Vorlesung</strong><br />

Programmanalysen und Compilerbau an der Universität<br />

Stuttgart<br />

Staiger u. a. (2007b, a): interprozedurale SSA-Form<br />

137 / 437<br />

Dynamische Analyse I<br />

Dynamische Analyse<br />

Probleme statischer Analysen<br />

Information durch dynamische Analyse<br />

Testfälle<br />

Instrumentierung<br />

Instrumentierung mit Aspect/J<br />

Probleme der Instrumentierung<br />

Weiterverarbeitung dynamischer Information<br />

Anwendungsbeispiele<br />

Vergleich mit statischer Analyse<br />

Wiederholungsfragen<br />

138 / 437


Probleme statischer Analysen<br />

Bisher: statische Analyse<br />

Aliasing<br />

Polymorphismus und dynamisches Binden<br />

Reflection<br />

verteilte Systeme<br />

Laufzeitinstanzen versus statischer Einheiten (z.B. Klassen)<br />

→ viele Entscheidungen erst zur Laufzeit<br />

→ statische Analyse problematisch<br />

139 / 437<br />

statisch = ruhend, unbeweglich – bezieht sich hier auf den Quellcode.<br />

Dieser ändert sich nicht während der Analyse.<br />

dynamisch = ändert sich – hier ändert sich der Zustand des Programms<br />

(d.h. des Arbeitsspeichers etc.) mit jeder ausgeführten Anweisung.<br />

Aliasing: mehrere Zeiger auf das gleiche Objekt. Problem: Erst zur<br />

Laufzeit ist wirklich bekannt, auf welches Objekt der Zeiger konkret zeigt.<br />

Polymorphismus: auch hier entscheidet sich erst zur Laufzeit durch den<br />

Typ des Objekts, welche Methode konkret aufgerufen wird.<br />

Reflection is the ability of a running program to examine itself and its<br />

software environment, and to change what it does depending on what it<br />

finds.<br />

Verteiltes System: Menge interagierender Prozesse ohne gemeinsamen<br />

Speicher, d.h. mit Kommunikation untereinander.


Dynamische Analyse<br />

Definition<br />

Dynamische Analyse: Analyse der Eigenschaften eines laufenden<br />

Programms.<br />

Ablauf:<br />

1 Ausführen des Programms<br />

2 Beobachtung der Ausführung<br />

3 Analyse der Ergebnisse<br />

Notwendig:<br />

1 Wahl der Testfälle<br />

2 Instrumentierung oder Interpretation<br />

3 Analysemethoden/-werkzeuge<br />

140 / 437<br />

Eine dynamische Analyse hat jeder schon verwendet: Testen. Auch dabei<br />

wird das Programm ausgeführt, das Verhalten des Programms während<br />

der Ausführung beobachtet und aus den Beobachtungen Rückschlüsse<br />

gezogen (ist die Spezifikation erfüllt oder nicht?).<br />

Auch beim Testen ist die Wahl der Testfälle entscheidend: Möglichst alle<br />

Teile des Programms sollen in allen Varianten mindestens einmal<br />

ausgeführt worden sein. Eine hohe Testabdeckung ist also<br />

wünschenswert. Genauso ist es bei den meisten dynamischen Analysen.<br />

Für weitergehende Analysen ist in der Regel ein Blick in den inneren<br />

Zustand des Programms notwendig. Daher müssen Vorkehrungen<br />

getroffen werden, um diesen Zustand zugreifbar zu machen. Zum Beispiel<br />

müssen evtl. Variablenwerte ausgegeben oder das Passieren bestimmter<br />

Punkte erfasst werden. Dazu ist eine Instrumentierung notwendig.


Was messen/beobachten?<br />

Beobachtung der Ausführung / Messen<br />

Codeabdeckung oder Häufigkeit<br />

Anweisungen, Zweige, Pfade, Aufrufe, . . .<br />

berechnete Werte<br />

Laufzeit, Speicherverbrauch<br />

Reihenfolge von Operationen<br />

. . .<br />

141 / 437<br />

Heisenbergsche Unschärfe bei der Beobachtung<br />

Problem<br />

Messung ändert Verhalten<br />

142 / 437


Wahl der Testfälle<br />

Testsuite bestimmt<br />

Kosten (Zeit und Raum)<br />

Genauigkeit (was wird nie ausgeführt)<br />

Problem<br />

Vollständige Testabdeckung im Allgemeinen nicht zu erreichen.<br />

143 / 437<br />

Vollständige Testabdeckung ist im Allgemeinen nicht erreichbar, sobald<br />

ein Programm Schleifen enthält, die beliebig oft durchlaufen werden<br />

können. Dann müsste es unendlich viele Testfälle geben.


Instrumentierung<br />

Source code<br />

Parser<br />

Analyzer<br />

Intermediate<br />

representation<br />

Preprocessor<br />

cpp<br />

Transformation<br />

Source code<br />

Unparser<br />

Intermediate<br />

representation<br />

Compiler<br />

gprof<br />

Object code<br />

Byte code<br />

BCEL<br />

Soot<br />

Libs<br />

Linker<br />

VM Execution<br />

JDI<br />

Executable<br />

Instrumented artefact<br />

VM Execution<br />

Valgrind<br />

gdb/mi<br />

Instrumenting instance<br />

144 / 437<br />

gprof Gnu Profiler der GNU Compiler Collection (gcc)<br />

BCEL Byte Code Engineering Library zur Analyse und<br />

Transformation von Java-Byte-Code<br />

Soot Framework zur Analyse und Transformation von<br />

Java-Byte-Code<br />

JDI Java Debugger Interface<br />

Valgrind : Werkzeugkasten für Debugging und Profiling von<br />

Linux-Programmen<br />

gdb/mi zeilenbasierte, maschinenorientierte und textuelle<br />

Schnittstelle zu gdb, dem Debugger der GNU Compiler<br />

Collection (gcc)


Instrumentierung<br />

Performanz?<br />

Debug-Schnittstelle i.d.R. langsam<br />

Profiler schnell<br />

Aufwand?<br />

Profile-, Coverage-Information umsonst<br />

automatisierbar?<br />

Bezug zum Quellcode?<br />

schwierig bei Maschinencode<br />

unabhängig von Compiler/Prozessor?<br />

Unabhängig von Programmiersprache?<br />

145 / 437<br />

Instrumentierung mit Aspect/J<br />

1 p u b l i c a s p e c t A c c e s s L o g g e r {<br />

2<br />

3 p r o t e c t e d s t a t i c P r i n t S t r e a m stream = System . out ;<br />

4<br />

5 /∗ ∗<br />

6 ∗ P r i n t s a ” r e a d ” message .<br />

7 ∗/<br />

8 p u b l i c s t a t i c v o i d t r a c e R e a d ( f i n a l S t r i n g s t r ) {<br />

9 stream . p r i n t l n ( ”R ” + s t r ) ;<br />

10 }<br />

11<br />

12 /∗ ∗<br />

13 ∗ P r i n t s a ” w r i t e ” message .<br />

14 ∗/<br />

15 p u b l i c s t a t i c v o i d t r a c e W r i t e ( S t r i n g s t r ) {<br />

16 stream . p r i n t l n ( ”W ” + s t r ) ;<br />

17 }<br />

146 / 437


Instrumentierung mit Aspect/J<br />

1 /∗ ∗ ∗ A p p l i c a t i o n c l a s s e s − E v e v e r t h i n g w i t h i n package s o o t .<br />

∗/<br />

2 p o i n t c u t myClass ( ) : w i t h i n ( s o o t . ∗ ∗ ) ;<br />

3<br />

4 /∗ ∗<br />

5 ∗ Every r e a d<br />

6 ∗/<br />

7 p o i n t c u t myGetter ( ) : myClass ( ) && g e t (∗ ∗ ) ;<br />

8<br />

9 /∗ ∗<br />

10 ∗ Every w r i t e<br />

11 ∗/<br />

12 p o i n t c u t mySetter ( ) : myClass ( ) && s e t (∗ ∗ ) ;<br />

147 / 437<br />

Instrumentierung mit Aspect/J<br />

1 /∗ ∗<br />

2 ∗ P r i n t s t r a c e messages b e f o r e r e a d i n g a f i e l d .<br />

3 ∗/<br />

4 b e f o r e ( ) : myGetter ( ) {<br />

5 t r a c e R e a d ( ” ” + t h i s J o i n P o i n t S t a t i c P a r t . g e t S i g n a t u r e ( ) ) ;<br />

6 }<br />

7<br />

8 /∗ ∗<br />

9 ∗ P r i n t s t r a c e messages b e f o r e w r i t i n g a f i e l d .<br />

10 ∗/<br />

11 b e f o r e ( ) : mySetter ( ) {<br />

12 t r a c e W r i t e ( ” ” + t h i s J o i n P o i n t S t a t i c P a r t . g e t S i g n a t u r e ( ) ) ;<br />

13 }<br />

14 }<br />

148 / 437


Datenmenge<br />

Problem<br />

Häufig zu viele Daten.<br />

Lösungen:<br />

selektive Instrumentierung<br />

sofortige Verarbeitung (online)<br />

Umkodierung<br />

verlustfreie Kompression<br />

Filterung<br />

Vorverarbeitung (Trace Summarization)<br />

149 / 437<br />

Auswertung<br />

Aggregation<br />

Statistiken<br />

Inferenz: Ableitung neuen Wissens<br />

maschinelles Lernen<br />

Visualisierung<br />

je online/offline möglich<br />

150 / 437


Beispiel: Profiling<br />

Ziel: Performance-Engpässe finden<br />

Techniken:<br />

Messen der Zeiten<br />

Zeit vor/nach Ausführung<br />

Mitzählen, Summieren<br />

PC Sampling<br />

% cumulative self self total<br />

time seconds seconds calls ms/call ms/call name<br />

33.34 0.02 0.02 7208 0.00 0.00 open<br />

16.67 0.03 0.01 244 0.04 0.12 offtime<br />

16.67 0.04 0.01 8 1.25 1.25 memccpy<br />

16.67 0.05 0.01 7 1.43 1.43 write<br />

...<br />

151 / 437<br />

PC sampling: in festen Zeitintervallen (z.B. alle 10ms) wird ermittelt,<br />

worauf gerade der Programmzähler (PC) zeigt. Der Zähler für die<br />

entsprechende Anweisung (oder die Funktion, zu der die Anweisung<br />

gehört) wird um eins erhöht. Dies wird über den gesamten Programmlauf<br />

wiederholt. Funktionen, in denen sehr viel Zeit verbracht wurde, haben<br />

damit am Ende einen hohen Zählerstand und können somit als<br />

Performance-kritisch identifiziert werden.<br />

Vorteil: wesentlich geringerer Overhead als beim Merken der Zeiten bei<br />

jedem Funktionseintritt/-austritt.


Beispiel: Dynamisches Slicing (Agrawal und Horgan 1990)<br />

a = 5;<br />

if (x) {<br />

a++;<br />

}<br />

print(a);<br />

Statisches Slicing: Erreichbarkeit im<br />

System-Dependency-Graph<br />

Dynamisches Slicing: Verfolgen von Werten zur Laufzeit<br />

152 / 437<br />

Zur Laufzeit ist immer bekannt, ob x true oder false war, d.h. ob bei<br />

print a=5 oder a=6 ist.<br />

Jeder Wert wird annotiert:<br />

• wo wurde er berechnet?<br />

• aus welchen Werten?<br />

Dann: Rückwärtsverfolgung der Verweise


Beispiel: Collaborations (Richner und Ducasse 2002)<br />

Aufzeichnen von Methodenaufrufen<br />

Aufrufer, Aufgerufener (Klasse,<br />

Instanz), Methode<br />

Pattern-Matching<br />

Suchen ähnlicher Muster<br />

Klassen, Instanzen, Methoden,<br />

Struktur<br />

Auswahl über Klassen/Methoden<br />

Collaboration Browser<br />

Object A<br />

methodCall()<br />

return<br />

Object B<br />

Visualisierung als<br />

Sequenzdiagramm<br />

153 / 437<br />

Beispiel: Invariantenerkennung (Ernst 2000)<br />

public class StackAr {<br />

Object[] theArray;<br />

int topOfStack;<br />

...<br />

}<br />

Invarianten:<br />

theArray != null<br />

theArray.getClass() == java.lang.Object[].class<br />

topOfStack >= -1<br />

topOfStack


Beispiel: Invariantenerkennung (Ernst 2000)<br />

Betrachtung aller Variablen<br />

auch abgeleitete Variablen<br />

sum, min, max<br />

first, second, last<br />

s[i], s[i-1], Teilsequenzen s[0..i], s[0..i-1]<br />

Anzahl Aufrufe einer Funktion<br />

Instanziierung und Testen aller potentiellen Invarianten<br />

Filterung der übrigen Invarianten<br />

hinreichend signifikant?<br />

155 / 437<br />

Object Process Graph (Quante und Koschke 2006)<br />

void main () {<br />

int i = 0;<br />

Stack s1 = new Stack();<br />

Stack s2 = Stack.read();<br />

reverse(s2, s1);<br />

do {<br />

s1.pop();<br />

i = i + 1;<br />

} while (!s1.empty());<br />

}<br />

void reverse(Stack from,<br />

Stack to) {<br />

while (!from.empty())<br />

to.push(from.pop());<br />

}<br />

main<br />

i=0<br />

s1 = new S()<br />

s2 = S.read()<br />

call<br />

s1.pop()<br />

i = i+1<br />

s1.empty()<br />

F<br />

T<br />

reverse<br />

from.empty()<br />

T<br />

F<br />

from.pop()<br />

to.push()<br />

156 / 437


• Rekonstruktion des Kontrollflussgraphen<br />

• Projektion auf ein Objekt<br />

• Instrumentierung von<br />

– Verzweigungen<br />

– Routinenaufrufen<br />

– Operationen auf Objekt<br />

Beispiel: Dynamic Object Process Graph<br />

select()<br />

Call irc_io<br />

Call connect_to_server<br />

read()<br />

main loop<br />

select()<br />

Call<br />

do_server<br />

Call irc_do_a_screen<br />

Call<br />

connect<br />

Call<br />

dgets<br />

fcntl()<br />

Call<br />

login_to_server<br />

Call<br />

p_channel<br />

Call<br />

send_line<br />

setsockopt()<br />

socket()<br />

Start<br />

send()<br />

Call<br />

parse_command<br />

Final<br />

close()<br />

fcntl()<br />

Call<br />

irc_exit<br />

send_to_server<br />

and its call sites<br />

command dispatching<br />

through function pointers<br />

157 / 437


Statische Analyse von Programmverhalten<br />

Analyse des Codes<br />

Modell des Programmzustands erstellen<br />

Mögliches Verhalten folgern<br />

abstrakte Ausführung<br />

Abstrakte Ausführung:<br />

meist Datenflussanalyse<br />

Transferfunktion für jede Anweisung<br />

Änderung des abstrakten Zustands?<br />

Beispiel: y = x++;<br />

158 / 437<br />

Wahl der abstrakten Domäne<br />

Domäne: {even, odd, either}<br />

y = x++; <br />

y = x++; <br />

Domäne: {prime, nonprime, anything}<br />

y = x++; <br />

Domäne: Menge von möglichen nat. Zahlen<br />

y = x++; <br />

Domäne: symbolische Auswertung<br />

y = x++; <br />

159 / 437


Wahl der abstrakten Domäne<br />

Abstraktion bestimmt<br />

Kosten (Zeit und Raum)<br />

Genauigkeit (Informationsverlust)<br />

160 / 437<br />

Statisch vs. Dynamisch<br />

Statisch<br />

abstrakte Domäne<br />

aufwändig wenn genau<br />

konservativ<br />

wegen Abstraktion<br />

vollständig (plus mehr)<br />

da konservativ<br />

begrenzte Sichtweite<br />

eher lokal<br />

Abstraktion bestimmt<br />

Kosten, Genauigkeit<br />

Dynamisch<br />

konkrete Ausführung<br />

kann aufwändig sein<br />

präzise<br />

keine Abstraktion<br />

unvollständig<br />

nicht verallgemeinerbar<br />

kann entfernte Beziehungen<br />

aufdecken<br />

Testsuite bestimmt<br />

Kosten, Genauigkeit<br />

161 / 437


Statisch vs. Dynamisch<br />

x = input();<br />

y = f(x);<br />

if (y < 0)<br />

y = -y;<br />

statisch<br />

dynamisch<br />

wirklich<br />

dynamisch: zu konkret, nicht<br />

verallgemeinerbar → Teilmenge<br />

statisch: zu abstrakt, daher<br />

ungenau → Obermenge<br />

y ∈ {0, 2, 7, 79}<br />

0 ≤ y ≤ MAX INT<br />

Wahrheit liegt (meist) dazwischen 0 ≤ y ≤ 100<br />

162 / 437<br />

Kombination<br />

dynamisch → statisch: Übergeneralisierung erkennen<br />

statisch → dynamisch: gezielte Instrumentierung<br />

163 / 437


Beispiele – Übersicht<br />

Anwendung dynamisch statisch<br />

Typprüfung × ×<br />

Speicherzugriffsprüfung × ×<br />

Slicing × ×<br />

Merkmalsuche × ×<br />

Protokollerkennung × ×<br />

Protokollverifikation × ×<br />

Metriken Verhalten Struktur<br />

- Profiling × —<br />

- Testabdeckung × —<br />

Finden toten Codes — ×<br />

Klonerkennung — ×<br />

Zeigeranalyse leicht schwierig<br />

164 / 437<br />

Wiederholungs- und Vertiefungsfragen I<br />

Nennen Sie Vor- und Nachteile dynamischer Analyse<br />

gegenüber statischer Analyse<br />

Was kann durch dynamische Analyse alles erfasst werden?<br />

Wie kann dynamische Analyse durchgeführt werden?<br />

Welche Probleme hat die Instrumentierung?<br />

Nennen Sie Beispiele für Anwendungen dynamischer Analyse.<br />

165 / 437


Program Slicing I<br />

Program Slicing<br />

Das tägliche Brot des Wartungsprogrammierers<br />

Was ist ein Slice?<br />

Slicing-Technik<br />

Intraprozedurales Slicing<br />

Interprozedurales Slicing<br />

Summary-Edges im SDG<br />

Slicing-Varianten<br />

Anwendungen von Slicing<br />

Wiederholungsfragen<br />

166 / 437<br />

Program Slicing<br />

Lernziele<br />

Verständnis von Slicing-Varianten einschließlich ihrer<br />

Berechnung und Anwendbarkeit<br />

Kontext<br />

Erste unmittelbare Anwendung von Compilerbau-Technologie<br />

zur Unterstützung des Programmverstehens<br />

Program Slicing ist Basis-Technologie für viele weitere<br />

<strong>Reengineering</strong>-Techniken<br />

168 / 437


Das tägliche Brot des Wartungsprogrammierers<br />

1 r e a d ( n ) ;<br />

2 i := 1 ;<br />

3 sum := 0 ; −− Was b e e i n f l u s s t d i e s e Anweisung ?<br />

4 p r o d u c t := 1 ;<br />

5 w h i l e i


Wie kommt es zu diesem Wert?<br />

Backward Slice (write (product)):<br />

1 r e a d ( n ) ;<br />

2 i := 1 ;<br />

3 sum := 0;<br />

4 p r o d u c t := 1 ;<br />

5 w h i l e i


Was wird durch diese Answeisung beeinflust?<br />

Forward Slice (sum := 0):<br />

1 read (n);<br />

2 i:= 1;<br />

3 sum := 0; −− Was b e e i n f l u s s t d i e s e Anweisung ?<br />

4 product := 1;<br />

5 w h i l e i


Slicing-Technik<br />

Idee und erste Technik des Slicings stammt von Weiser (1984).<br />

Moderne Slicing-Techniken basieren auf Abhängigkeitsgraphen<br />

Program Dependency Graph (PDG) (Ottenstein und<br />

Ottenstein 1984) für intraprozedurales Slicing<br />

System Dependency Graph (SDG) für interprozedurales Slicing<br />

(Horwitz u. a. 1990)<br />

PDG-basiertes Slicing:<br />

Kanten A → B im PDG: B ist daten- bzw. kontrollabhängig<br />

von A<br />

Forward Slicing: Graph-Traversierung in Richtung der<br />

Kontroll- und Datenabhängigkeitskanten<br />

Backward Slicing: Graph-Traversierung in umgekehrter<br />

Richtung der Kontroll- und Datenabhängigkeitskanten<br />

175 / 437<br />

Program Dependency Graph (PDG) nach Ottenstein und<br />

Ottenstein (1984)<br />

PDG = gerichteter Multigraph für Daten- und<br />

Kontrollflussabhängigkeiten.<br />

Knoten repräsentieren Zuweisungen und Prädikate<br />

zusätzlich einen speziellen Entry-Knoten<br />

zusätzlich φ-Knoten zur Reduktion der<br />

Datenabhängigkeitskanten<br />

(sowie einen Initial-Definition-Knoten für jede Variable, die<br />

benutzt werden kann, bevor sie gesetzt wird)<br />

Control-Kanten repräsentieren Kontrollabhängigkeiten<br />

Startknoten jeder Kontrollabhängigkeitskante ist entweder der<br />

Entry-Knoten oder ein Prädikatsknoten<br />

Flow-Kanten repräsentieren Set-Use-Abhängigkeiten<br />

177 / 437


Program Slicing mit PDG<br />

Kontrolleinfluss<br />

Set−Use<br />

entry<br />

read(n )<br />

i := 1<br />

sum := 0<br />

product := 1<br />

i


Program Slicing mit PDG/SSA<br />

entry<br />

Kontrolleinfluss<br />

read(n )<br />

1<br />

i := 1<br />

1<br />

sum<br />

1:= 0<br />

product := 1<br />

1<br />

i<br />

3


System Dependency Graph (SDG) nach Horwitz u. a.<br />

(1990)<br />

PDG stellt Abhängigkeiten innerhalb einer Funktion dar<br />

System Dependency Graph (SDG) stellt globale<br />

Abhängigkeiten dar:<br />

PDGs für verschiedene Unterprogramme werden vernetzt über<br />

interprozedurale Kontroll- und Datenflusskanten<br />

Aufruf: Call-Knoten<br />

aktuelle Parameter: actual-in / actual-out-Knoten<br />

(copy-in/copy-out-Parameterübergabe vorausgesetzt)<br />

formale Parameter: formal-in / formal-out-Knoten<br />

transitive Abhängigkeiten via PDG: Summary-Edges<br />

183 / 437<br />

Modellierung von Prozeduraufrufen<br />

1 Add ( x , y ) ⇔<br />

2<br />

3 a in := x 0 ;<br />

4 b in := y 0 ;<br />

5 Add ;<br />

6 x 1 := a out ;<br />

7 y 1 := b out ;<br />

a in := x 0<br />

call Add<br />

1 p r o c e d u r e Add<br />

2 ( x : i n out I n t e g e r ;<br />

3 y : i n out I n t e g e r ) i s ⇔<br />

4<br />

x 1 := a out<br />

5 p r o c e d u r e Add i s<br />

6 b e g i n<br />

7 a 0 := a in ; b 0 := b in ;<br />

8 . . .<br />

9 a out := a 1 ; b out := b 1 ;<br />

10 end A ;<br />

y 1 := b out<br />

b in := y 0<br />

184 / 437<br />

Entry Add<br />

a 0 := a in<br />

b 0 := b in<br />

a out := a 1bout<br />

:= b 0


Naives Slicing: Folge allen Flow- und Control-Kanten<br />

sum 0 := initialState(sum)<br />

i 0 := initialState(i)<br />

sum 1 := 0<br />

i 1 := 1<br />

Entry Main<br />

while i 3 < 11<br />

call A<br />

FinalUse(sum 3 )<br />

FinalUse(i 3 )<br />

i 3 := φ(i 1 , i 2 )<br />

sum 3 := φ(sum 1 , sum 2 )<br />

x in := sum 3 y in := i 3 sum 2 := x out i 2 := y out<br />

Entry A<br />

x 0 := x in y out := y 2<br />

y 0 := y in<br />

x out := x 1<br />

call Add<br />

call inc<br />

y<br />

a in := x 1 := b out<br />

0<br />

z in := y 1<br />

y 2 := z out<br />

x 1 := a out<br />

Entry Add<br />

Entry inc<br />

one 0 := 1<br />

z 0 := z in<br />

a 0 := a in a z out := z 1<br />

out := a 1bout call Add<br />

b 0 := b in := b 0<br />

one 1 := b out<br />

a 1 := a 0 + b 0 a in := z 0 b in := one 0 z 1 := a out<br />

b in := y 0<br />

185 / 437<br />

Problem: Formale in-Parameter können mehrere Vorgänger haben,<br />

formale out-Parameter mehrere Nachfolger.


Summary-Edges<br />

Summary-Edges sind spezielle Flow-Kanten und beschreiben die<br />

Abhängigkeit der aktuellen out-Parameter von den aktuellen<br />

in-Parametern im Aufrufkontext.<br />

a in := x 0<br />

call Add<br />

x 1 := a out<br />

y 1 := b out<br />

b in := y 0<br />

186 / 437<br />

Entry Add<br />

a 0 := a in a out := a 1bout<br />

b 0 := b in := b 0<br />

a 1 := a 0 + b 0<br />

sum 0 := initialState(sum)<br />

i 0 := initialState(i)<br />

sum 1 := 0<br />

i 1 := 1<br />

Entry Main<br />

while i 3 < 11<br />

call A<br />

FinalUse(sum 3 )<br />

FinalUse(i 3 )<br />

i 3 := φ(i 1 , i 2 )<br />

sum 3 := φ(sum 1 , sum 2 )<br />

x in := sum 3 y in := i 3 sum 2 := x out i 2 := y out<br />

Entry A<br />

x 0 := x in y out := y 2<br />

y 0 := y in<br />

x out := x 1<br />

call Add<br />

call inc<br />

y<br />

a in := x 1 := b out<br />

0<br />

z in := y 1<br />

y 2 := z out<br />

x 1 := a out<br />

Entry Add<br />

Entry inc<br />

one 0 := 1<br />

z 0 := z in<br />

a 0 := a in a z out := z 1<br />

out := a 1bout call Add<br />

b 0 := b in := b 0<br />

one 1 := b out<br />

a 1 := a 0 + b 0 a in := z 0 b in := one 0 z 1 := a out<br />

b in := y 0<br />

187 / 437


Interprozedurales Slicing (Traversierung)<br />

Backward-Slicing (Prozedur P, Statement S):<br />

Phase 1: Folge rückwärts Parameter-In-Kanten, Control- und<br />

Flow-Kanten (inklusive Summary-Kanten) ausgehend von S.<br />

Identifiziert Knoten in Prozeduren, die P (transitiv) aufrufen<br />

und von denen S abhängt.<br />

Da Parameter-Out-Edges ausgenommen sind, werden nur<br />

aufrufende Prozeduren besucht.<br />

Die Effekte nicht-besuchter Prozeduren werden aber nicht<br />

ignoriert, da Summary-Edges traversiert werden.<br />

188 / 437<br />

Interprozedurales Slicing (Traversierung)<br />

Backward-Slicing (Prozedur P, Statement S):<br />

Phase 2: Folge rückwärts Parameter-Out-Kanten, Control- und<br />

Flow-Kanten (inklusive Summary-Kanten) ausgehend von allen<br />

Knoten, die in Phase 1 identifiziert wurden.<br />

Identifiziert Knoten in Prozeduren, die von P (transitiv)<br />

aufgerufen werden und von denen S abhängt.<br />

Da Parameter-In-Edges ausgenommen sind, werden nur<br />

aufgerufene Prozeduren besucht.<br />

Es gilt wieder: Die Effekte nicht-besuchter Prozeduren werden<br />

nicht ignoriert, da Summary-Edges traversiert werden.<br />

190 / 437


Forward-Slicing ist analog zum Backward-Slicing mit dem folgenden<br />

Unterschied:<br />

• Phase 1 folgt vorwärts Control-und Flow-Kanten einschließlich von<br />

Parameter-Out-Kanten, aber nicht Parameter-In-Kanten<br />

• Phase 2 folgt vorwärts Control-und Flow-Kanten einschließlich von<br />

Parameter-In-Kanten, aber nicht Parameter-Out-Kanten<br />

Summary-Edges<br />

repräsentieren (transitive) Abhängigkeiten der aktuellen Inund<br />

Out-Parameter an einer gegebenen Aufrufstelle<br />

helfen während des Slicings, unnötige Abstiege in aufgerufene<br />

Prozeduren zu vermeiden<br />

werden in zwei Schritten ermittelt:<br />

direkte Abhängigkeiten zwischen den formalen Parametern<br />

innerhalb der aufrufenden Prozedur<br />

indirekte Abhängigkeiten, die sich transitiv durch Aufruf<br />

anderer Prozeduren ergeben<br />

werden hergeleitet mit bekannten Techniken zu<br />

Abhängigkeiten in Attributgrammatiken.<br />

191 / 437


Herleitung von Summary-Edges (1)<br />

(1) Für jedes Unterprogramm UP:<br />

Ermittle alle transitiven Abhängigkeiten zwischen formalen<br />

Parameter-Knoten innerhalb des PDGs von UP (liefert<br />

intraprozedural induzierte Abhängigkeiten).<br />

Für jede eingefügte Kante (a, b) zwischen Parametern von UP<br />

füge eine unmarkierte Summary-Edge zwischen a und b ein.<br />

192 / 437<br />

Herleitung von Summary-Edges (2)<br />

(2) Solange eine unmarkierte Summary-Edge (a, b) eines<br />

Unterprogramms UP existiert:<br />

Markiere (a, b)<br />

Für jeden Aufruf von UP innerhalb eines Unterprogramms<br />

UP’:<br />

Füge eine Kante zwischen den zu a und b korrespondierenden<br />

aktuellen Parametern in UP’ ein.<br />

Bilde die transitive Hülle in UP’ (liefert interprozedural<br />

induzierte Abhängigkeiten).<br />

Für jede eingefügte Kante (a’, b’) zwischen aktuellen<br />

Parametern in UP’ füge eine unmarkierte Summary Edge<br />

zwischen a’ und b’ ein, sofern sie noch nicht existiert.<br />

194 / 437


Slicing-Variante: Chopping (Jackson und Rollins 1994)<br />

Abhängigkeiten zwischen zwei Punkten<br />

1 read (n);<br />

2 i:= 1;<br />

3 sum := 0;<br />

4 product := 1;<br />

5 w h i l e i


Slicing-Variante: Amorphes Slicing (Harmann u. a. 2003)<br />

−− O r i g i n a l −− g e s t a l t e r h a l t e n d e s S l i c e<br />

i f p = q then i f p = q then<br />

x := 1 8 ; x := 18;<br />

e l s e<br />

e l s e<br />

x := 1 7 ; x := 1 7 ;<br />

end i f ; end i f ;<br />

i f p /= q then i f p /= q then<br />

y := x ; y := x ;<br />

e l s e<br />

e l s e<br />

y:= 2 ; y:= 2 ;<br />

end i f ; end i f ;<br />

put (y);<br />

put (y);<br />

196 / 437<br />

In diesem Beispiel wird ein besonders intelligentes Slicing durchgeführt,<br />

das auch noch die Prädikate auswertet. Die Zuweisung y := x im<br />

Originalprogramm findet nur statt, wenn p=q gilt. Wenn aber p=q gilt,<br />

dann findet die Zuweisung x := 18 nicht statt und kann deshalb<br />

vernachlässigt werden.<br />

Die Berücksichtigung von Prädikaten für Slicing ist nicht spezifisch für<br />

amorphes Slicing. Zumindest für einfache Fälle könnte man das auch<br />

beim Slicing, wie wir es bisher kennen gelernt haben, einbauen.


Slicing-Variante: Amorphes Slicing (Harmann u. a. 2003)<br />

−− O r i g i n a l −− T r a n s f o r m a t i o n −− amorphes S l i c e<br />

i f p = q then i f p = q then i f p = q then<br />

x := 1 8 ; x := 1 8 ; y := 2 ; x := 18; y := 2 ;<br />

e l s e e l s e e l s e<br />

x := 1 7 ; x := 1 7 ; x := 17;<br />

end i f ; y := x ; y := 17 y := x ;<br />

i f p /= q then end i f ; end i f ;<br />

y := x ;<br />

e l s e<br />

y:= 2 ;<br />

end i f ;<br />

put (y); put (y); put (y);<br />

197 / 437<br />

Amorphes Slicing ist die Kombination von Slicing mit automatischen<br />

Code-Transformationen, die zum Ziel haben, das resultierende Slice zu<br />

vereinfachen. Die Transformationen können vor dem Slicing durchgeführt<br />

werden oder hinterher. In diesem Beispiel findet die Transformation<br />

vorher statt.<br />

Eine Analyse hat erkannt, dass man das Originalprogramm, wie in der<br />

zweiten Spalte gezeigt, vereinfachen kann (wieder auf Grund der Analyse<br />

der Prädikate der beiden aufeinander folgenden Bedingungen).<br />

Anschließend wird das transformierte Programm ”<br />

gesliced“. Die<br />

Zuweisung x := 18 fällt wie vorher heraus. Die Zuweisung x := 17 ist<br />

relevant und gehört eigentlich zum Slice. Allerdings kann man in diesem<br />

Falle eine weitere Code-Transformation durchführen. Die so genannte<br />

Konstantenpropagierung ersetzt alle Variablen, die als konstant erkannt<br />

wurden, mit ihrem konstanten Wert. Die Zuweisung der Konstante kann<br />

dann entfallen.<br />

In diesem Beispiel finden also Code-Transformation vor und nach dem<br />

Slicing statt.


Slicing-Varianten<br />

Backward-/Forward-Slicing, Chopping<br />

ausführbare Slices/nicht-ausführbare Slices<br />

statisches/dynamisches Slicing<br />

gestalterhaltendes (syntax-preserving)/amorphes Slicing<br />

amorph: Slicing mit vereinfachender Transformation<br />

198 / 437<br />

Anwendungen von Slicing<br />

Programmverstehen<br />

Reduzierung des Codes auf das für das Verständnis notwendige<br />

Maß<br />

Änderungsanalyse<br />

alle von einer Änderung betroffenen Stellen<br />

Regressionstesten<br />

Reduktion des zu testenden Codes<br />

Restrukturierung<br />

z.B. Aufteilung von Unterprogrammen, die mehrere logisch<br />

verschiedene Funktionen implementieren Bewertung der<br />

Änderbarkeit (und somit der Wartbarkeit) eines Systems<br />

Messung von Kohäsion<br />

200 / 437


<strong>Software</strong>-Metriken I<br />

Metriken<br />

<strong>Software</strong>metriken<br />

Größenmetriken<br />

Komplexitätsmetriken<br />

Kopplung und Kohäsion<br />

Objektorientierte Metriken<br />

Einsatz von Metriken<br />

Grenzen von Metriken<br />

Zielorientiertes Messen<br />

Wiederholungsfragen<br />

202 / 437<br />

Fragen<br />

Wie lassen sich Bad Smells mit Metriken finden?<br />

Wie lassen sich Wartbarkeitsaspekte<br />

quantifizieren?<br />

203 / 437


<strong>Software</strong>-Metrik<br />

Definition<br />

Metric: A quantitative measure of the degree to which a system,<br />

component, or process possesses a given variable.<br />

– IEEE Standard Glossary<br />

204 / 437<br />

Klassifikation nach Fenton und Pfleeger (1996)<br />

<strong>Software</strong>−Metriken<br />

Prozess−Metriken<br />

Produkt−Metriken<br />

Ressourcen−Metriken<br />

intern<br />

extern<br />

Größe<br />

Länge<br />

Funktionalität<br />

Komplexität<br />

Struktur<br />

Kontrollfluss<br />

Datenfluss, Modularität<br />

OO−Metriken<br />

Datenstrukturen<br />

205 / 437


Prozessmetriken - intern: Zeit, Aufwand, Anzahl gef. Fehler, . . .<br />

Prozessmetriken - extern: Qualität, Kosten, Stabilität, . . .<br />

Ressourcenmetriken - intern: Personal (Alter, Lohn),<br />

Teamgröße/-struktur, Hardwareausstattung, Büroeinrichtung, . . .<br />

Ressourcenmetriken - extern: Produktivität, Erfahrung, . . .<br />

Produktmetriken - extern: Verläßlichkeit, Verständlichkeit,<br />

Benutzerfreundlichkeit, Wartbarkeit, . . .<br />

Metriken<br />

Halstead<br />

NOC DIT<br />

?<br />

LOC NOP<br />

WMC<br />

SLOC<br />

LCOM<br />

CBO<br />

NOM<br />

RFC<br />

NCO<br />

Fan−In<br />

Fan−Out<br />

McCabe<br />

Extended<br />

McCabe<br />

. . . und alle möglichen Kombinationen, z.B. NOC/Package als<br />

High-level Structuring<br />

Messen kann man viel. Aber wie findet man das Angemessene?<br />

206 / 437


Größenmetriken:<br />

• LOC - Lines of Code<br />

• SLOC - Source Lines of Code (ohne Leerzeilen/Kommentare)<br />

• Halstead - Größe unabhängig von Layout<br />

• McCabe - Anzahl der Bedingungen + 1<br />

• Extended - Wie McCabe aber mit Zählung von Operanden von and<br />

und or<br />

• NOP - Number of Packages<br />

• NOC - Number of Classes<br />

• NOM - Number of Methods<br />

Kopplungsmetriken<br />

• Fan-In: Anzahl eingehender Abhängigkeiten<br />

• Fan-Out: Anzahl ausgehender Abhängigkeiten<br />

OO-Metriken (Chidamber 1994; Chidamber und Kemerer 1994):<br />

• WMC - weighted methods per class<br />

Größenmetriken – LOC<br />

• DIT - depth of inheritance tree<br />

• NOC - number of children<br />

• CBO - coupling between objects (uses, used-by)<br />

• RFC - response for a class (#own + #called methods)<br />

Lines • LCOM of code - lack (LOC) of cohesion in methods<br />

+ relativ einfach messbar<br />

+ starke Korrelation mit anderen Maßen<br />

– ignoriert Komplexität von Anweisungen und Strukturen<br />

– schlecht vergleichbar<br />

207 / 437


Größenmetriken – LOC<br />

int main(int argc, char **argv) {<br />

printf("Hello World."); }<br />

208 / 437<br />

Größenmetriken – LOC<br />

/*<br />

* This program prints the message<br />

* "Hello World." to stdout.<br />

*/<br />

int main(int argc,<br />

char **argv)<br />

{<br />

}<br />

printf("Hello World.");<br />

209 / 437


Wie wird gezählt?<br />

• Leerzeilen<br />

• Kommentare<br />

• Daten<br />

• mehrere Anweisungen pro Zeile<br />

• generierter Code<br />

• lange Header usw.<br />

⇒ Leerzeilen und Kommentarzeilen bei LOC nicht mitzählen, dafür<br />

Kommentarzeilen CLOC einzeln zählen; dann kann z.B. die<br />

Kommentardichte ermittelt werden als CLOC/LOC.<br />

Nützlich zum Vergleichen von Projektgrößen, Produktivität, Entwicklung<br />

der Projektgröße, Zusammenhang mit Anzahl Fehler<br />

Zählen per Modul, per Funktion, . . .<br />

Größenmetriken – LOC<br />

/*<br />

* This function should be documented.<br />

*<br />

* Author:<br />

* Date created:<br />

* Date modified:<br />

* Version:<br />

*<br />

*/<br />

int main(int argc, char **argv)<br />

{<br />

printf("Hello World.");<br />

}<br />

210 / 437


Größenmetriken – Halstead<br />

Halstead (1977)<br />

Länge N = N 1 + N 2<br />

Vokabular µ = µ 1 + µ 2<br />

Volumen V = N · log 2 µ<br />

Program Level L est = (2/µ 1 ) · (µ 2 /N 2 )<br />

Programmieraufwand E est = V /L est<br />

mit µ 1 , µ 2 = Anzahl unterschiedlicher Operatoren, Operanden<br />

N 1 , N 2 = Gesamtzahl verwendeter Operatoren, Operanden<br />

+ komplexe Ausdrücke und viele Variablen berücksichtigt<br />

– Ablaufstrukturen unberücksichtigt<br />

211 / 437<br />

Operanden = Variablen, Konstanten, Literale<br />

Operatoren = Aktionen, bzw. alles andere außer Daten (+, *, while, for,<br />

. . . )<br />

Program Level = Größe der minimalen Implementierung / Größe der<br />

tatsächlichen Implementierung<br />

abgeleitete Halstead-Metriken umstritten.<br />

Halstead: Zeitaufwand T = E / 18 Sekunden


i n t i , j , t ;<br />

i f ( n < 2 ) r e t u r n ;<br />

f o r ( i = 0 ; i < n−1; i ++ ) {<br />

f o r ( j = i + 1 ; j < n ; j ++ ) {<br />

i f ( a [ i ] > a [ j ] ) {<br />

t = a [ i ] ;<br />

a [ i ] = a [ j ] ;<br />

a [ j ] = t ;<br />

}<br />

}<br />

}<br />

< = > - , ; ( ) [ ] { } + ++ for if int return<br />

3 5 1 1 2 9 4 4 6 6 3 3 1 2 2 2 1 1<br />

0 1 2 a i j n t<br />

1 2 1 6 8 7 3 3<br />

µ 1 = 18, µ 2 = 8, N 1 = 56, N 2 = 31<br />

⇒ V = N · log 2 (µ) = 87 · log 2 (26)<br />

212 / 437<br />

Wie werden Operanden + Operatoren definiert?<br />

z.B. Operator = Token, Operand = Literal und Bezeichner


Größenmetriken – weitere<br />

weitere:<br />

Anzahl Module<br />

Anzahl Operatoren, Operanden, Schlüsselworte<br />

Anzahl Parameter<br />

Anzahl/Umfang von Klonen<br />

durchschnittliche Länge von Bezeichnern<br />

. . .<br />

213 / 437<br />

Strukturmetriken<br />

Eigenschaften des Kontrollflussgraphen<br />

Eigenschaften des Aufrufgraphen (Größe, Tiefe, Breite)<br />

Modulkohäsion, Modulkopplung (Abhängigkeiten)<br />

OO-Metriken<br />

Daten, Datenstrukturen<br />

214 / 437


Strukturmetriken – Kontrollflussgraph<br />

Eigenschaften des Kontrollflussgraphen<br />

Anzahl Knoten<br />

Anzahl Kanten<br />

maximale Tiefe<br />

abgeleitete Maße<br />

215 / 437<br />

Komplexitätsmetriken – McCabe<br />

Zyklomatische Komplexität nach McCabe<br />

(1976): maximale Anzahl unabhängiger<br />

zyklischer Pfade in stark verbundenen<br />

Graphen.<br />

V (G) = #Kanten − #Knoten +1 a<br />

oder einfacher:<br />

V (g) = #Binärverzweigungen +1<br />

+ einfach zu berechnen<br />

– Komplexität von Anweisungen<br />

unberücksichtigt<br />

a Kontrollflussgraphen werden erst zu stark verbundenen Graphen durch eine<br />

künstliche Kante von Exit zu Entry → #Kanten = tatsächliche Kanten + 1<br />

216 / 437


Zyklomatische Komplexität: maximale Anzahl unabhängiger zyklischer<br />

Pfade in stark verbundenen Graphen (strongly connected graphs).<br />

Stark verbundener Graph: Jeder Knoten ist von jedem anderen Knoten<br />

erreichbar.<br />

Wir nehmen an, jede Kante hat eine eindeutige Nummer. Jeder Pfad in<br />

einem Graph mit e Kanten kann durch ein e-Tupel (i 1 , i 2 , . . . , i e )<br />

repräsentiert werden, bei dem der Index i j angibt, wie oft die j-te Kante<br />

im Pfad vorkommt.<br />

Ein Pfad p ist eine Linearkombination von Pfaden p 1 , . . . , p n , wenn es<br />

ganze Zahlen a 1 , . . . , a n gibt, so dass p = ∑ a i p i ist, wobei die Pfade wie<br />

oben kodiert sind.<br />

Eine Menge von Pfaden ist linear unabhängig, wenn kein Pfad eine<br />

lineare Kombination der anderen Pfade in der Menge ist.<br />

Die Basismenge von Zyklen ist die maximal große Menge von linear<br />

unabhängigen Zyklen. Jeder Pfade eines zyklischen Graphen lässt sich als<br />

Linearkombination von Pfaden der Basismenge beschreiben.<br />

Die Basismenge ist nicht notwendigerweise eindeutig. Allerdings ist die<br />

Kardinalität der Menge eindeutig. Sie wird zyklomatische Komplexität<br />

genannt und beträgt: e − n + 1.<br />

Komplexitätsmetriken – McCabe<br />

Kontrollflussgraphen sind keine stark verbundenen Graphen, können<br />

jedoch leicht in einen solchen umgewandelt werden, indem der<br />

Exit-Knoten mit dem Entry-Knoten verbunden wird. Damit erhöht sich<br />

dieiAnzahl n t i , der j , Kanten t ; um eins, so dass die zyklomatische Komplexität<br />

e −i n f + ( 2n beträgt. < 2 ) r e t u r n ;<br />

f o r ( i = 0 ; i < n−1; i ++ ) {<br />

f o r ( j = i + 1 ; j < n ; j ++ ) {<br />

i f ( a [ i ] > a [ j ] ) {<br />

t = a [ i ] ;<br />

a [ i ] = a [ j ] ;<br />

a [ j ] = t ;<br />

}<br />

}<br />

}<br />

217 / 437


V (g) = 4 + 1 = 5<br />

McCabe-Beispiele<br />

c a s e A i s<br />

when ’A ’ => i := 1 ;<br />

when ’B ’ => i := 2 ;<br />

when ’C ’ => i := 3 ;<br />

when ’D ’ => i := 4 ;<br />

when ’E ’ => i := 5 ;<br />

end c a s e ;<br />

S : a r r a y ( 1 . . 5 ) o f C h a r a c t e r := ( ’A ’ , ’B ’ , ’C ’ , ’D ’ , ’E ’ ) ;<br />

i := 1 ;<br />

l o o p<br />

e x i t when S ( i ) = A ;<br />

i := i + 1 ;<br />

end l o o p ;<br />

o = new . . . ;<br />

. . .<br />

o . mymethod ( ) ; // Verzweigung<br />

218 / 437


Strukturmetriken – Kopplung und Kohäsion<br />

starke Kopplung<br />

schwache Kohäsion<br />

schwache Kopplung<br />

starke Kohäsion<br />

219 / 437<br />

Strukturmetriken – OO<br />

OO-Metriken (Chidamber 1994; Chidamber und Kemerer 1994):<br />

WMC - weighted methods per class<br />

DIT - depth of inheritance tree<br />

NOC - number of children<br />

CBO - coupling between objects (uses, used-by)<br />

RFC - response for a class (#own + #called methods)<br />

LCOM - lack of cohesion in methods<br />

Metriken pro Klasse<br />

220 / 437


WMC: Anzahl Klassenmethoden, optional gewichtet nach Größe oder<br />

Komplexität<br />

DIT: Länge des Weges von der Wurzel bis zur Klasse (tiefe Hierarchie ist<br />

fehleranfällig)<br />

NOC: Anzahl direkter Unterklassen, hohe Zahl ist Indikator für gute<br />

Wiederverwendung<br />

CBO: Anzahl Klassen, mit denen eine Klasse gekoppelt ist (per uses,<br />

used-by); hoher Kopplungsgrad ist fehleranfällig, niedriger Kopplungsgrad<br />

fördert die Wiederverwendbarkeit<br />

RFC: Anzahl Methoden, die potentiell ausgeführt werden können, wenn<br />

das Objekt auf eine eingehende Nachricht reagiert; RFC = #methods in<br />

the class + #remote methods directly called by methods in the class;<br />

bevorzugt: rekursiv<br />

hoher RFC führt zu mehr Fehlern, deutet auf hohe Komplexität und<br />

schlechte Verständlichkeit hin<br />

LCOM: hoher Wert heißt, Klasse führt mehrere Funktionen aus; deutet<br />

auf schlechtes Design, hohe Komplexität und hohe<br />

Fehlerwahrscheinlichkeit hin; Klasse sollte möglicherweise überarbeitet<br />

werden; niedriger Wert deutet auf gute Kapselung hin<br />

Sei C eine Klasse. Der Zusammenhalt a(m 1 , m 2 ) zweier Methoden, m 1<br />

und m 2 von C sei definiert als die Anzahl der Attribute ihrer Klasse, die<br />

sie zusammen benutzen (lesend oder schreibend). LCOM1 ist dann wie<br />

folgt definiert: LCOM1 = max(|P| − |Q|, 0), wobei P die Menge von<br />

Paaren von Methoden aus C ist, die keine Attribute gemeinsam<br />

benutzen: {(m 1 , m 2 )|m 1 , m 2 ∈ C ∧ a(m 1 , m 2 ) = 0} und Q die Menge von<br />

Paaren von Methoden aus C ist, die mindestens ein Attribut gemeinsam<br />

benutzen: {(m 1 , m 2 )|m 1 , m 2 ∈ C ∧ a(m 1 , m 2 ) > 0}.<br />

LCOM2 = 1 - sum(mA)/(m*a) mit m=#Methoden, a=#Attribute,<br />

mA=#Methoden die Bein Attribut ansprechen A D<br />

LCOM3 = (m-sum(mA)/a)/(m-1)<br />

0 = hohe Kohäsion, 1 = keine Kohäsion, +m1() > 1 = Attribute werden nicht<br />

benutzt<br />

+m2()<br />

Strukturmetriken – OO<br />

C<br />

E<br />

+m3()<br />

+m4()<br />

F<br />

G<br />

H I J<br />

221 / 437


CBO(A) = 4<br />

RFC(A) = 4, RFC(B) = 0, RFC(C) = 1<br />

LCOM<br />

M2<br />

M1<br />

M3<br />

A2<br />

A3<br />

M9<br />

A1<br />

A4<br />

M4<br />

M8<br />

A6<br />

A5<br />

M7<br />

M6<br />

M5<br />

A<br />

Attribut<br />

M<br />

Methode<br />

Referenz<br />

222 / 437


Einsatz von Metriken<br />

Regelbasiertes Qualitätsmodell<br />

Benchmarking<br />

Visualisierung<br />

einer Version<br />

zeitlicher Verlauf<br />

Vorhersage von Qualitätseigenschaften<br />

223 / 437<br />

Regelbasierte Qualitätsmodelle<br />

Beispiel<br />

Gottklasse<br />

benutzt viele Attribute anderer Klassen<br />

hohe funktionale Komplexität<br />

geringe Kohärenz (innerer Zusammenhalt)<br />

Metriken (Lanza und Marinescu 2006):<br />

Access to Foreign Data (ATFD): Anzahl von Attributen<br />

anderer Klassen, die direkt oder mittels Zugriffsmethoden<br />

verwendet werden<br />

Weighted Methods per Class (WMC): gewichtete Anzahl von<br />

Methoden<br />

Tight Class Cohesion (TCC): relative Anzahl von<br />

Methodenpaaren einer Klasse, die gemeinsam auf mindestens<br />

ein Attribut derselben Klasse zugreifen<br />

224 / 437


Regelbasierte Qualitätsmodelle<br />

Feste Schwellwerte:<br />

ATFD > 5 ∧ WMC ≥ 800 ∧ 0 ≤ TCC ≤ 8<br />

Relative und feste Schwellwerte:<br />

ATFD > few ∧ WMC ≥ very high ∧ TCC ∈ P 30 (TCC)<br />

very high = (AVG + STDEV ) × 1.5<br />

few = 5<br />

Perzentil P n (f ) = Menge der ersten n % Prozent der<br />

Elemente, die nach Werten von f geordnet sind<br />

225 / 437<br />

30-Perzentil P 30<br />

Anzahl<br />

20<br />

25<br />

5<br />

10<br />

30 %<br />

15<br />

10<br />

5 5 5<br />

f<br />

226 / 437


Benchmarking: Code Quality Index (Simon u. a. 2006)<br />

Aufwand<br />

Bauchgefühl<br />

Code−Quality−<br />

Index<br />

manuelles<br />

Review<br />

Objektivität, Genauigkeit<br />

Quality-Benchmark-Level auf Basis eines statisch ermittelten,<br />

objektiven Code-Quality-Index:<br />

52 Qualitätsindikatoren (Typen von Bad Smells)<br />

Häufigkeitsverteilung für mehr als 120 industrielle Systeme<br />

geschrieben in C++ und Java → ”<br />

Industriestandard“<br />

227 / 437<br />

Visualisierung von Metriken (Lanza 2003)<br />

Kombination von Metriken und<br />

<strong>Software</strong>-Visualisierung<br />

Graph-Repräsentation<br />

Bis zu fünf Metriken<br />

bestimmen die Visualisierung<br />

der Knoten:<br />

Größe (1+2)<br />

Farbe/Farbton (3)<br />

Position (4+5)<br />

Element<br />

X-Koordinate Relation<br />

Y-Koordinate<br />

Farbton Höhe<br />

Breite<br />

228 / 437


Polymetrische Sichten<br />

229 / 437<br />

Polymetrische Sichten über die Zeit<br />

Definition<br />

Pulsar: wiederholte Änderungen, die Element größer und kleiner<br />

werden lassen.<br />

→ System-Hotspot: Jede neue Version verlangt Anpassungen.<br />

230 / 437


Polymetrische Sichten über die Zeit<br />

Definition<br />

Supernova: Plötzlicher Anstieg. Mögliche Gründe:<br />

massive Restrukturierung<br />

Datenspeicher für Daten, die plötzlich hinzugekommen sind<br />

Schläfer: Stumpf, der mit Funktionalität gefüllt wird<br />

231 / 437<br />

Trend-Analyse: Kommentierung<br />

232 / 437


Visualisierung: Cockpits<br />

233 / 437<br />

Qualitätseigenschaften: 1. Lernen<br />

...<br />

McCabe<br />

LOC<br />

Halstead<br />

...<br />

20<br />

100<br />

40<br />

...<br />

25<br />

200<br />

86<br />

...<br />

45<br />

180<br />

22<br />

...<br />

29<br />

100<br />

33<br />

...<br />

22<br />

77<br />

11<br />

...<br />

13<br />

222<br />

87<br />

...<br />

...<br />

...<br />

...<br />

...<br />

19<br />

85<br />

22<br />

...<br />

Fehler ja ja nein nein ja nein ... ja<br />

Lernverfahren<br />

20 ≤ McCabe ≤ 25<br />

Modell<br />

LOC ≥ 100<br />

McCabe > 25<br />

∨ McCabe < 20<br />

LOC < 100<br />

50 % 86 % 32 % 20 %<br />

25 % 50 %<br />

40 % 50 %<br />

234 / 437


Qualitätseigenschaften: 2. Vorhersage<br />

A B C D E F G H I<br />

McCabe<br />

LOC<br />

Halstead<br />

...<br />

20<br />

130<br />

44<br />

...<br />

30<br />

210<br />

55<br />

...<br />

24<br />

230<br />

15<br />

...<br />

44<br />

110<br />

45<br />

...<br />

12<br />

88<br />

13<br />

...<br />

67<br />

95<br />

44<br />

...<br />

23<br />

35<br />

56<br />

...<br />

87<br />

35<br />

44<br />

...<br />

33<br />

95<br />

22<br />

...<br />

20 ≤ McCabe ≤ 25<br />

Modell<br />

LOC ≥ 100<br />

McCabe > 25<br />

∨ McCabe < 20<br />

Fehlerwahr−<br />

scheinlichkeit<br />

LOC < 100<br />

50 % 20 % 40 % 80 % 23 % 57 % 60 % 90 % 45 %<br />

90 % 80 % 60 % 57 % 50 % 45 % 40 % 23 % 20 %<br />

H D G F A I C E B<br />

50 % 86 % 32 % 20 %<br />

25 % 50 %<br />

40 % 50 %<br />

235 / 437<br />

Vorhersage von Qualitätseigenschaften: Beispiele<br />

Studie von Ostrand u. a. (2005):<br />

Metriken bewerten Module<br />

die ersten 20 % der Dateien der Bewertungsordnung enthalten<br />

80 % der Fehler<br />

die ersten 20 % der Dateien der Bewertungsordnung enthalten<br />

70 % des Codes<br />

Studie von Mende und Koschke (2009):<br />

simple Ordnung nach Größe liefert vergleichbar gute<br />

Ergebnisse<br />

Studie von El Emam u. a. (2001):<br />

OO-Metriken können Fehler vorhersagen<br />

OO-Metriken, die auf LOC normalisiert werden, können Fehler<br />

nicht vorhersagen<br />

→ Bewertung muss Test- und Fehlerfolgekosten einbeziehen<br />

(Mende und Koschke 2010)<br />

236 / 437


Komplexitätsmetriken – McCabe<br />

Empirische Untersuchungen über Zusammenhang zyklomatische<br />

Komplexität (ZK) und Wartungsaufwand:<br />

(Fenton und Ohlsson 2000): Korrelation von Fehlern und ZK<br />

vor Release (nicht jedoch nach Release)<br />

(Grady 1994): Korrelation von Änderungshäufigkeit und ZK;<br />

schlägt ZK ≤ 15 als Qualitätsziel vor<br />

237 / 437<br />

SEI-Kategorisierung<br />

Cyclomatic Complexity Risk Evaluation<br />

1-10 a simple program, without much risk<br />

11-20 more complex, moderate risk<br />

21-50 complex, high risk program<br />

> 50 untestable program (very high risk)<br />

http:<br />

//www.sei.cmu.edu/str/descriptions/cyclomatic_body.html<br />

Einsatz z.B. bei Qualitätssicherung für <strong>Software</strong> des Zugtunnels<br />

England-Frankreich schreibt für Prozeduren ZK ≤ 20 und LOC ≤<br />

50 vor (Bennett 1994).<br />

238 / 437


Empirische Studien<br />

Maintainability Index (Coleman/Oman, 1994):<br />

MI 1 = 171 − 5.2 · ln(V ) − 0.23 · V (g ′ ) − 16.2 · ln(LOC)<br />

MI 2 = MI 1 + 50 · sin √ 2.46 · perCM<br />

V = average Halstead Volume per module<br />

V (g ′ ) = average extended cyclomatic complexity per module<br />

LOC = average LOC per module<br />

perCM = average percent of lines of comment per module<br />

MI 2 nur bei sinnvoller Kommentierung<br />

MI < 65 ⇒ schlechte / MI ≥ 85 ⇒ gute Wartbarkeit<br />

239 / 437<br />

nicht sinnvolle Kommentierung: wenn Kommentare nicht zum Code<br />

passen oder viel Code auskommentiert ist oder große Kommentarblöcke<br />

ohne relevante Informationen vorhanden sind.<br />

Liso (2001): statt konstantem Faktor 2.46 abhängig von<br />

Programmiersprache wählen<br />

Extended Cyclomatic Complexity: zusammengesetzte logische Ausdrücke<br />

werden berücksichtigt, AND und OR erhöhen die ECC jeweils um 1


Empirische Studien<br />

Maintainability model (Muthanna u. a. 2000):<br />

SMI = 125 − 3.989 · FAN − 0.954 · DF − 1.123 · MC<br />

FAN: average number of external calls from the module<br />

DF : total number of outgoing and incoming data flow for the<br />

module<br />

MC: average McCabe for the module<br />

240 / 437<br />

Empirische Studien – OO<br />

Wartbarkeit korreliert mit (Dagpinar und Jahnke 2003)<br />

TNOS - total number of statements<br />

NIM - number of instance methods<br />

FOUT - fan out, number of classes directly used<br />

nicht Vererbungshierarchie<br />

nicht Kohäsion<br />

nicht indirekte Kopplung<br />

nicht Kopplung über used-by Beziehungen<br />

241 / 437


Empirische Studien – OO<br />

Testbarkeit korreliert vor allem mit (Bruntink und van Deursen<br />

2004):<br />

LOCC - lines of code per class<br />

FOUT - fan out, number of classes directly used<br />

RFC - response for class<br />

242 / 437<br />

Vorhersage von Qualitätseigenschaften<br />

es gibt viele Studien über Zusammenhänge von Metriken<br />

(Code, Änderungen, Entwickler, Checkin-Zeitpunkt, . . . ) und<br />

Qualitätseigenschaften<br />

die Untersuchungen haben kein einheitliches Bild geboten<br />

Vorsicht ist bei den Details der Evaluation angebracht<br />

dennoch werden Metriken in der Praxis benutzt (z.B.<br />

Code-Quality-Index, Wartbarkeitssiegel von TÜV/Nord)<br />

243 / 437


Metriken: Grenzen<br />

Metriken sind Information, nicht Wissen<br />

Metriken müssen interpretiert werden<br />

Metriken sind starke Vereinfachungen, Details gehen verloren<br />

jede Metrik kann unterlaufen werden<br />

244 / 437<br />

Metriken richtig einsetzen: Zielorientiertes Messen<br />

GQM (Goal-Question-Metric) nach Basili und Weiss (1984):<br />

Nicht das messen, was einfach zu bekommen ist,<br />

sondern das, was benötigt wird<br />

1 Ziele erfassen.<br />

2 Zur Prüfung der Zielerreichung notwendige Fragen ableiten.<br />

3 Was muss gemessen werden, um diese Fragen zu beantworten?<br />

245 / 437


Zielorientiertes Messen<br />

G<br />

Effektivität der Codierrichtlinien bestimmen<br />

Q<br />

Wer benutzt<br />

den Standard?<br />

Wie ist die<br />

Produktivität<br />

der Programmierer?<br />

Wie ist die Qualität<br />

des Codes?<br />

M<br />

Anteil der<br />

Programmierer, die<br />

Standard benutzen<br />

Aufwand<br />

Code−Größe<br />

Fehler<br />

Erfahrung der Programmierer<br />

mit Standard/Sprache/Umgebung<br />

246 / 437<br />

Freie Tools:<br />

Tool Sprachen Metriken<br />

clc (Perl) C/C++ LOC, Comments, #Statements<br />

cccc C++, Java LOC, McCabe, OO, . . .<br />

Metrics C LOC, Comments, Halstead, McCabe<br />

MAS-C4 C LOC, Comments, Halstead, McCabe,<br />

Nesting, Fan-In/-Out, Data Flow, . . .<br />

JMT Java OO-Metriken<br />

Eclipse Plugins Java LOC, McCabe, OO, . . .<br />

Kommerzielle: z.B. Axivion Bauhaus Suite, McCabeQA, CMT,<br />

TAU/Logiscope, SDMetrics, CodeCheck, Krakatau Metrics, RSM,<br />

Together, . . .


Wiederholungs- und Vertiefungsfragen I<br />

Was ist eine <strong>Software</strong>-Metrik?<br />

Welche Produktmetriken kennen Sie (für die Größe,<br />

Komplexität und die Struktur)?<br />

247 / 437<br />

Techniken zur Klonerkennung I<br />

Klonerkennung<br />

Hintergrund<br />

Folgen<br />

Übersicht zu den Ansätzen zur Klonerkennung<br />

Lexikalische Verfahren<br />

Syntaxbasierte Verfahren<br />

Metrikbasierte Verfahren<br />

Vergleich von Techniken zur Klonerkennung<br />

Wiederholungsfragen<br />

248 / 437


No two parts are alike in software. . .<br />

<strong>Software</strong> entities are more complex for their size than<br />

perhaps any other human construct because no two<br />

parts are alike (at least above the statement level). If<br />

they are, we make the two similar parts into a<br />

subroutine — open or closed. In this respect, software<br />

systems differ profoundly from computers, buildings, or<br />

automobiles, where repeated elements abound.<br />

– by Frederick P. Brooks, Jr: No Silver Bullet: Essence and<br />

Accidents of <strong>Software</strong> Engineering<br />

251 / 437<br />

Klonerkennung<br />

Lernziele<br />

Varianten der Klonerkennung (Erkennung duplizierten Codes)<br />

Bezug zu Abstraktionsebenen von Programmdarstellungen<br />

Kontext<br />

Beseitigung von Redundanz auf Codierungsebene<br />

erleichtert nachfolgende <strong>Reengineering</strong>-Aktivitäten<br />

255 / 437


<strong>Software</strong>redundanz<br />

Duplikation von Quelltext durch Copy&Paste (Code Cloning) ist<br />

häufig:<br />

Nummer 1 auf Beck und Fowlers ”<br />

Stink Parade of Bad<br />

Smells“<br />

Forschung:<br />

% System Zeilen Referenz<br />

19 X Windows ≥ 30 Baker (1995)<br />

28 3 subs. of process-control sys. ? Baxter u. a. (1998)<br />

59 payroll system ≥ 10 Ducasse u. a. (1999)<br />

Klone länger als 25 Zeilen sind selten (Baxter u. a. 1998)<br />

256 / 437<br />

Klontypen: Typ 1<br />

1 PRIVATE UINT16 t y p l e n g t h ( a t n t y p e ∗ node )<br />

2 { i f ( node−>tag == REF )<br />

3 node = node−>t r e e . r e f t y p e ;<br />

4<br />

5 s w i t c h ( node−>tag )<br />

6 {<br />

7 c a s e INTEGER : r e t u r n 4 ;<br />

8 c a s e REAL : r e t u r n 8 ;<br />

9 c a s e BOOLEAN : r e t u r n 1 ;<br />

10 c a s e STRING : r e t u r n 4 ;<br />

11 c a s e ARRAY :<br />

12 r e t u r n t y p l e n g t h ( node−>t r e e . a r r a y . t y p e )<br />

13 ∗ ( node−>t r e e . a r r a y . upb<br />

14 − node−>t r e e . a r r a y . lwb +1);<br />

15 c a s e REF : r e t u r n 4 ;<br />

16 d e f a u l t :<br />

17 l o g e r r o r (ERR FATAL , SYSTEM ERROR,<br />

18 E ILLEGAL TAG , ” t y p e ” , 0 ) ;<br />

19 }<br />

20 r e t u r n 0 ;<br />

21 }<br />

1 PRIVATE UINT16 t y p l e n g t h ( a t n t y p e ∗ node )<br />

2 { i f ( node−>tag == REF )<br />

3 node = node−>t r e e . r e f t y p e ;<br />

4<br />

5 s w i t c h ( node−>tag )<br />

6 {<br />

7 c a s e INTEGER : r e t u r n 4 ;<br />

8 c a s e REAL : r e t u r n 8 ;<br />

9 c a s e BOOLEAN : r e t u r n 1 ;<br />

10 c a s e STRING : r e t u r n 4 ;<br />

11 c a s e ARRAY :<br />

12 r e t u r n t y p l e n g t h ( node−>t r e e . a r r a y . t y p e )<br />

13 ∗ ( node−>t r e e . a r r a y . upb<br />

14 − node−>t r e e . a r r a y . lwb +1);<br />

15 c a s e REF : r e t u r n 4 ;<br />

16 d e f a u l t :<br />

17 l o g e r r o r (ERR FATAL , SYSTEM ERROR,<br />

18 E ILLEGAL TAG , ” t y p e ” , 0 ) ;<br />

19 }<br />

20 r e t u r n 0 ;<br />

21 }<br />

257 / 437


Klontypen: Typ 2<br />

1 r e t u r n TRUE ;<br />

2 }<br />

3<br />

4 /∗ r e a d operand #0 ( a l w a y s p r e s e n t ) ∗/<br />

5<br />

6 thisOp −>op [ 0 ] . t y p e<br />

7 = v a a r g ( ap , a 3 a r g u m e n t t y p e ) ;<br />

8<br />

9 i f ( ( thisOp −>op [ 0 ] . t y p e == oCFLOAT ) | |<br />

10 ( thisOp −>op [ 0 ] . t y p e & 1 6 ) ) // i n d e x e d<br />

11 {<br />

12 thisOp −>op [ 0 ] . v a l . f [0]= v a a r g ( ap , INT32 ) 11 ; thisOp −>op [ 2 ] . v a l . f [0]= v a a r g ( ap , INT32 ) ;<br />

13 thisOp −>op [ 0 ] . v a l . f [1]= v a a r g ( ap , INT32 ) 12 ; thisOp −>op [ 2 ] . v a l . f [1]= v a a r g ( ap , INT32 ) ;<br />

14 }<br />

15 e l s e<br />

16 thisOp −>op [ 0 ] . v a l . l = v a a r g ( ap , INT32 ) 15 ; thisOp −>op [ 2 ] . v a l . l = v a a r g ( ap , INT32 ) ;<br />

17<br />

18 /∗ r e a d operand #1 ( sometimes p r e s e n t ) ∗/<br />

19<br />

20 i f ( ( s t a t t y p e != A3 GOTO ) &&<br />

1 /∗ r e a d operand #2 ( b i n a r y op o n l y ) ∗/<br />

2 i f ( ( s t a t t y p e == A3 BINARY OP ) | |<br />

3 ( s t a t t y p e == A3 COND ) )<br />

4 {<br />

5 thisOp −>op [ 2 ] . t y p e<br />

6 = v a a r g ( ap , a 3 a r g u m e n t t y p e ) ;<br />

7<br />

8 i f ( ( thisOp −>op [ 2 ] . t y p e == oCFLOAT ) | |<br />

9 ( thisOp −>op [ 2 ] . t y p e & 1 6 ) )<br />

10 {<br />

13 }<br />

14 e l s e<br />

16 }<br />

17 e l s e<br />

18 thisOp −>op [ 2 ] . t y p e = oNONE ;<br />

258 / 437<br />

Klontypen: Typ 3<br />

1 r e t u r n FALSE ;<br />

2 }<br />

3<br />

4 i f ( ! parse ( ) )<br />

5 {<br />

6 p r i n t e r r o r ( s t d o u t , 0 ) ;<br />

7 r e t u r n FALSE ;<br />

8 }<br />

9<br />

10 fclose( fp ) ;<br />

11<br />

12 i f ( d e b u g f l a g )<br />

13 {<br />

14 p r i n t f ( ” result of parser ” ) ;<br />

15 i f ( ! p r i n t t r e e ( FALSE ) )<br />

16 {<br />

17 p r i n t e r r o r ( s t d o u t , 0 ) ;<br />

18 r e t u r n FALSE ;<br />

19 }<br />

20 }<br />

21<br />

22 i f ( v e r b o s e f l a g | | d e b u g f l a g )<br />

23 p r i n t f ( ” s e m a n t i c a n a l y s i s \ n” ) ;<br />

1 i f ( v e r b o s e f l a g | | d e b u g f l a g )<br />

2 p r i n t f ( ” t y p e c h e c k i n g \ n” ) ;<br />

3<br />

4 i f ( ! type check ( ) )<br />

5 {<br />

6 p r i n t e r r o r ( s t d o u t , 0 ) ;<br />

7 r e t u r n FALSE ;<br />

8 }<br />

9<br />

10 i f ( d e b u g f l a g )<br />

11 {<br />

12 p r i n t f ( ” result of type check ” ) ;<br />

13 i f ( ! p r i n t t r e e ( TRUE ) )<br />

14 {<br />

15 p r i n t e r r o r ( s t d o u t , 0 ) ;<br />

16 r e t u r n FALSE ;<br />

17 }<br />

18 }<br />

19<br />

20 i f ( g e n c o d e f l a g )<br />

21 {<br />

22 i f ( ( f p = f o p e n ( o u t f i l e n a m e , ”wt”))==NULL)<br />

23 {<br />

259 / 437


Arten von Klonen Die Kopie eines Programmfragments wird Klon<br />

genannt.<br />

Typ 1 Exakte Kopie<br />

• Keinerlei Veränderung an der Kopie (bis auf White Space<br />

und Kommentaren).<br />

• Z.B. Inlining von Hand.<br />

Typ 2 Kopie mit Umbenennungen (parametrisierte Übereinstimmung)<br />

• Bezeichner werden in der Kopie umbenannt.<br />

• Z.B. ”<br />

Wiederverwendung“ einer Funktion, generische<br />

Funktion von Hand<br />

Typ 3 Kopie mit weiteren Modifikationen<br />

• Code der Kopie wird abgeändert, nicht nur Bezeichner.<br />

• Z.B. ”<br />

Erweiterung“ einer Funktion.<br />

Typ 4 Semantische Klone<br />

• Verschiedene Implementierungen desselben Konzepts.<br />

Warum gibt es Klone?<br />

Entwicklungsstrategie<br />

Klone als Vorlage: Templating<br />

Verzögerte Anpassung<br />

. . .<br />

Wartung<br />

Bewährter Quelltext<br />

Unabhängigkeit<br />

. . .<br />

Einschränkungen<br />

Architektur<br />

Zeit<br />

Mangelnde Kenntnisse<br />

Programmiersprache<br />

. . .<br />

260 / 437


Was sind die Auswirkungen von Klonen?<br />

Erhöhter Ressourcenbedarf<br />

Erhöhter Aufwand zum Verstehen<br />

Erhöhter Aufwand zum Ändern<br />

Duplizieren von Fehlern<br />

Unvollständige Anpassung<br />

Gefahr von inkonsistenten<br />

Änderungen<br />

Erhöhter Testaufwand<br />

. . .<br />

261 / 437<br />

Studien zu Auswirkungen von Klonen<br />

Wissenschaftliche Studien lassen negative Auswirkungen von<br />

Klonen erkennen:<br />

höchste Fehlerdichte in Modulen mit besonders langen Klonen<br />

(Monden u. a. 2002):<br />

Fehler treten vermehrt auf, wenn Klone vorkommen (Chou<br />

u. a. 2001)<br />

Fehler wegen Klonen, deren Bezeichner inkonsistent<br />

umbenannt wurden (Li u. a. 2006)<br />

Fehler durch inkonsistent geänderte Klone (Jürgens u. a.<br />

2009)<br />

263 / 437


Nächster Abschnitt<br />

Wie können Klone gefunden werden?<br />

264 / 437<br />

Wie können Klone gefunden werden?<br />

Granularität<br />

Klassen, Dateien<br />

Funktionen<br />

Anweisungen<br />

Vergleich von . . .<br />

Text<br />

Bezeichnern<br />

Token<br />

Syntaxbäume<br />

Kontroll- und<br />

Datenabhängigkeiten<br />

Verwendete<br />

Techniken:<br />

textual diff<br />

dotplot<br />

data mining<br />

suffix tree<br />

tree matching<br />

graph matching<br />

latent semantic<br />

indexing<br />

metric vector<br />

comparison<br />

hashing<br />

265 / 437


Probleme bei der Erkennung<br />

Jede Zeile/Funktion/Datei muss mit jeder anderen<br />

Zeile/Funktion/Datei verglichen werden:<br />

Wie kann quadratischer Aufwand vermieden werden?<br />

Wie kann von Bezeichnern geeignet abstrahiert werden?<br />

Soll die Umbenennung konsistent sein?<br />

Typ-3-Klone: Geklonte Codefragmente von Typ 1 und Typ 2<br />

können zu größeren Klonen zusammengefasst werden.<br />

Codefragmente müssen nicht direkt zusammenhängend sein.<br />

Code muss nicht identisch, nur ähnlich sein: Ähnlichkeitsmaß?<br />

266 / 437<br />

Textuelle Verfahren<br />

Vergleich von. . .<br />

Bezeichner und Kommentare<br />

(Information-Retrieval-Techniken)<br />

Latent Semantic Indexing (Marcus und Maletic 2001)<br />

Text<br />

Zeichenkettenvergleich (Johnson 1993, 1994) mittels<br />

Hashwerten<br />

zeilenweiser Vergleich über Dot-Plots (Ducasse u. a. 1999;<br />

Rieger 2005)<br />

42<br />

95 ... ...<br />

27 124 67 12 42<br />

a a b x y a a b z ...<br />

267 / 437


Dot-Plot:<br />

file2.c file1.c<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

1<br />

2<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

file1.c<br />

file2.c<br />

1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8<br />

Wie können Klone gefunden werden?<br />

Vergleich von . . .<br />

Tokens<br />

Type-1/-2-Klone: Suffixbaum für parametrisierte Zeichenketten<br />

pro Zeile (Baker 1995)<br />

Type-3: Zusammenfassung von Typ-1/2-Klonen mit Lücken<br />

(Baker 1995).<br />

pro Token mit Normalisierung des Tokenstroms (Kamiya u. a.<br />

2002)<br />

lexikalische Klone, die vollständig in syntaktischen Einheiten<br />

enthalten sind:<br />

lexikalisches Postprocessing (Higo u. a. 2002)<br />

lexikalisches Preprocessing (Synytskyy u. a. 2003; Cordy u. a.<br />

2004)<br />

268 / 437


Suffixbaum:<br />

a<br />

a b<br />

b<br />

x y a a b $<br />

$<br />

x y a a b $<br />

$<br />

b<br />

x y a a b $<br />

$<br />

x y a a b $<br />

y a a b $<br />

$<br />

Wie können Klone gefunden werden?<br />

Vergleich von . . .<br />

Metriken (Mayrand u. a. 1996; Kontogiannis 1997)<br />

Anweisungen durch Data-Mining-Techniken (Wahler u. a.<br />

2004; Li u. a. 2004)<br />

269 / 437


Wie können Klone gefunden werden?<br />

Vergleich von . . .<br />

Syntaxbäumen<br />

Hashing mit Baum-Matching (Baxter u. a. 1998)<br />

Baum-Matching mit dynamischem Programmieren (für<br />

Dateivergleich) (Yang 1991)<br />

Suffixbaum für serialisierten Syntaxbaum (Koschke u. a. 2006)<br />

seq<br />

if<br />

if<br />

if<br />

+<br />

=<br />

=<br />

id =<br />

=<br />

id =<br />

=<br />

id id<br />

id id<br />

id call<br />

id id<br />

id literal<br />

id id<br />

id id<br />

id<br />

270 / 437<br />

Wie können Klone gefunden werden?<br />

Vergleich von Program-Dependency-Graphs (PDG) (Komondoor<br />

und Horwitz 2001; Krinke 2001)<br />

r e a d ( n ) ; r e a d (m) ;<br />

i = 1 ; s = 0 ;<br />

p r o d u c t = 1 ; p = 1 ;<br />

sum = 0 ; f o r ( j = 1 ; j


Verfahren nach Baker (1995)<br />

Verfahren vergleicht Token-Sequenzen<br />

Vermeidung des quadratischen Aufwands:<br />

quasi-parallele Suche in einer Programmrepräsentation, die<br />

jedes mögliche Anfangsstück des Programms enthält.<br />

Abstraktion von Bezeichnern:<br />

Bezeichner werden auf relative Positionen abgebildet.<br />

Typ-3-Klone:<br />

Separater Schritt am Ende<br />

272 / 437<br />

Parameter-String<br />

Für jede Codezeile wird eine Zeichenkette aus<br />

Parametersymbolen und Nichtparametersymbolen generiert<br />

(so genannter Parameter-String oder P-String):<br />

Struktur der Zeile (genauer: Token-Sequenz) wird auf<br />

eindeutiges Nichtparametersymbol abgebildet (Funktor)<br />

Bezeichner werden in Argumentliste erfasst<br />

Ergebnis ist: (Funktor Argumentliste)<br />

Beispiel: x = x + y → (P = P + P; x,x,y) → α x x y<br />

Die Konkatenation der P-Strings aller Codezeilen repräsentiert<br />

das Programm.<br />

j = l e n g t h ( l ) ;<br />

i f ( j < 3) { x = x + y ; }<br />

→ α j length l β j 3 x x y<br />

273 / 437


Prev-String<br />

Kodierung prev(s) jedes P-Strings s:<br />

Erstes Vorkommen eines Bezeichners erhält Nummer 0.<br />

Jedes weitere Vorkommen erhält den relativen Abstand zum<br />

vorherigen Vorkommen (Funktoren mitgezählt).<br />

Beispiel:<br />

α j length l β j 3 x x y<br />

→ α 0 0 0 β 4 0 0 1 0<br />

Abstraktion der Bezeichner, jedoch nicht ihrer Reihenfolge.<br />

S ist ein P-Match von T genau dann, wenn<br />

prev(S) = prev(T )<br />

Beispiel: x = x + y und a = a + b sind ein P-Match wegen<br />

γ010 = γ010<br />

274 / 437<br />

P-Suffix-Baum<br />

Sei S i = s i s i+1 . . . s n $ das i’te Suffix von S ($ ist das<br />

Endezeichen).<br />

Der P-Suffix-Baum von S enthält alle prev(S i ) zu den<br />

Suffixen des P-Strings S.<br />

Beispiel: S = αyβyαxαx<br />

prev(S 1 ) = α0β2α0α2$<br />

prev(S 2 ) = 0β2α0α2$<br />

prev(S 3 ) = β0α0α2$<br />

prev(S 4 ) = 0α0α2$<br />

prev(S 5 ) = α0α2$<br />

prev(S 6 ) = 0α2$<br />

prev(S 7 ) = α0$<br />

prev(S 8 ) = 0$<br />

prev(S 9 ) = $<br />

α0<br />

β0α0α2$<br />

$<br />

β2α0α2$<br />

$<br />

α2$<br />

275 / 437


P-Suffix-Baum<br />

Klone: Verzweigungen im Suffix-Baum<br />

Länge: Anzahl der Funktoren<br />

→ Benutzer kann Mindestlänge festlegen<br />

Ort: Anzahl Funktoren zum Blatt gibt die Position relativ zum<br />

Programmende wieder<br />

276 / 437<br />

Typ-3-Erkennung<br />

Gefundene P-Matches werden, wenn möglich,<br />

zusammengefasst:<br />

A<br />

A<br />

d<br />

1<br />

B<br />

d 2<br />

B<br />

Modi:<br />

nur wenn d 1 = d 2<br />

wenn max(d 1 , d 2 ) ≤ Θ<br />

277 / 437


Bewertung des Ansatzes von Baker<br />

Bakers Klonerkennung ist lexem-basiert und damit sehr<br />

schnell:<br />

lineare Zeit- und Speicherkomplexität<br />

Plattform: SGI IRIX 4.1, 40 Mhz R3000, 256 MB<br />

Hauptspeicher<br />

System: 1,1 Mio LOC, mindestens zusammenhängende 30<br />

LOC/Klon<br />

nur 7 Minuten Analysezeit<br />

Der Ansatz ist invariant gegen Einfügung von Leerzeichen,<br />

Leerzeilen und Kommentaren.<br />

Der Ansatz ist weitgehend programmiersprachen-unabhängig<br />

(nur Regeln für Bezeichner/Tokens notwendig).<br />

Erweiterung von Kamiya u. a. (2002): Normalisierung des<br />

Tokenstroms (z.B. Entfernen von Qualifier wie static)<br />

278 / 437<br />

Bewertung des Ansatzes von Baker<br />

Allerdings entgehen dem Ansatz:<br />

äquivalente Ausdrücke mit kommutativen Operatoren:<br />

x = x + y x = y + x<br />

gleiche Anweisungsfolgen, die verschieden umbrochen wurden:<br />

if (a > 1) {x = 1;} if (a > 1)<br />

{x = 1;}<br />

gleiche Teilausdrücke:<br />

if (sp > 0) if (sp > 0 && s[sp] != a)<br />

279 / 437


AST-Matching<br />

Ansatz basierend auf abstrakten Syntaxbäumen (ASTs):<br />

vergleiche jeden Teilbaum mit jedem anderen Teilbaum auf<br />

Gleichheit<br />

if<br />

if<br />

cond else cond else<br />

then<br />

then<br />

= call :=<br />

=<br />

call :=<br />

left<br />

right<br />

lhs<br />

rhs<br />

left<br />

right<br />

lhs<br />

rhs<br />

a b x y z q t s<br />

280 / 437<br />

Verfahren nach Baxter u. a. (1998)<br />

Verfahren basiert auf ASTs<br />

Vermeidung des quadratischen Aufwands:<br />

Partitionierung der zu vergleichenden Bäumen.<br />

Abstraktion von Bezeichnern:<br />

Partitionierung und AST-Vergleich ignoriert Bezeichner.<br />

Typ-3-Klone:<br />

Vergleich auf Ähnlichkeit statt Gleichheit.<br />

Separater Schritt am Ende.<br />

281 / 437


Skalierungsproblem und Erkennung der Klontypen<br />

Bäume werden durch Hash-Funktion partitioniert.<br />

Nur Bäume innerhalb einer gemeinsamen Partition werden<br />

verglichen.<br />

Hash-Funktion gleich wie bei Erkennung von gemeinsamen<br />

Teilausdrücken durch optimierende Compiler:<br />

h : nodetype × arg 1 × arg 2 × . . . × arg n → integer<br />

Typ-1-Klonerkennung: h liefert genaue Partitionierung<br />

Typ-2-Klonerkennung: h ignoriert Parameter<br />

Nur Bäume T mit mass(T ) ≥ Θ m werden betrachtet<br />

(mass = Anzahl von Knoten, Θ m = benutzerdefinierter<br />

Schwellwert).<br />

282 / 437<br />

Basis-AST-Matching: Typ-1- und Typ-2-Klone<br />

C l o n e s := ∅ ;<br />

f o r each s u b t r e e t l o o p<br />

i f mass ( t ) ≥ Θ m then<br />

hash t to bucket ;<br />

end i f ;<br />

end l o o p ;<br />

f o r each s u b t r e e s and t i n the same bucket l o o p<br />

i f I s E q u a l ( s , t ) then<br />

f o r each s u b t r e e s ’ o f s and f o r each s u b t r e e t ’ o f t<br />

and Is Member ( s ’ , t ’ , C l o n e s )<br />

l o o p<br />

Remove Clone Pair ( s ’ , t ’ , C l o n e s ) ;<br />

end l o o p ;<br />

A d d C l o n e P a i r ( s , t , C l o n e s ) ;<br />

end i f ;<br />

end l o o p ;<br />

283 / 437


AST-Matching von Sequenzen (Schritt 2)<br />

Sequenzen von Bäumen (z.B. Anweisungsfolgen):<br />

v o i d f ( v o i d )<br />

{<br />

x = 0 ;<br />

a = a + 1 ;<br />

b = b + 2 ;<br />

c = c + 3 ;<br />

w = g ( ) ;<br />

}<br />

v o i d g ( v o i d )<br />

{<br />

y = 2 ∗ x ;<br />

a = a + 1 ;<br />

b = b + 2 ;<br />

c = c + 3 ;<br />

i = h ( ) ∗ 3 ;<br />

}<br />

Basis-Matching-Algorithmus identifiziert nur die gleichen<br />

Zuweisungen, ignoriert aber Gleichheit der beiden<br />

Anweisungsteilfolgen als Ganzes.<br />

Nächster Schritt identifiziert maximal lange gemeinsame<br />

Teilfolgen zweier Sequenzen mit einer benutzerdefinierten<br />

Mindestlänge.<br />

284 / 437<br />

Erkennung von Klonsequenzen (Schritt 2)<br />

f o r k i n Min Length . . Max Length l o o p<br />

p l a c e a l l s u b s e q u e n c e s o f l e n g t h k i n t o b u c k e t s ;<br />

f o r each s u b s e q u e n c e i and j i n same bucket l o o p<br />

i f Compare Sequence ( i , j , k ) ≥ Θ S then<br />

Remove Sequence Subclones Of ( i , j , k , C l o n e s ) ;<br />

A d d S e q u e n c e C l o n e P a i r ( i , j , k , C l o n e s ) ;<br />

end i f ;<br />

end l o o p ;<br />

end l o o p ;<br />

285 / 437


Erkennung von Typ-3-Klonen (Schritt 3)<br />

Schritt 1 erkennt nur Typ-1- und Typ-2-Klone<br />

s = 0 ; x = y ;<br />

w h i l e ( x > 0) {<br />

s = s + x ;<br />

x = x − 1 ;<br />

}<br />

p = 0 ; a = b ;<br />

w h i l e ( a > 0) {<br />

p = p ∗ x ;<br />

a = a − 1 ;<br />

}<br />

Idee: Väterknoten der als Klone erkannten Teilbäume auf<br />

Ähnlichkeit prüfen<br />

286 / 437<br />

Erkennung von Typ-3-Klonen (Schritt 3)<br />

C l o n e s T o G e n e r a l i z e := C l o n e s ;<br />

w h i l e C l o n e s T o G e n e r a l i z e ∅ l o o p<br />

Remove Clone Pair ( s , t , C l o n e s T o G e n e r a l i z e ) ;<br />

i f S i m i l a r i t y ( Parent ( s ) , Parent ( t ) ) ≥ Θ S then<br />

Remove Clone Pair ( s , t , C l o n e s ) ;<br />

A d d C l o n e P a i r ( Parent ( s ) , Parent ( t ) , C l o n e s ) ;<br />

A d d C l o n e P a i r ( Parent ( s ) , Parent ( t ) ,<br />

C l o n e s T o G e n e r a l i z e ) ;<br />

end i f ;<br />

end l o o p ;<br />

287 / 437


Ähnlichkeit von Teilbäumen<br />

Similarity(T 1 , T 2 ) =<br />

2 · Same(T 1 , T 2 )<br />

2 · Same(T 1 , T 2 ) + Difference(T 1 , T 2 )<br />

Same(T 1 , T 2 ) = Anzahl der korrespondierenden Knoten in T 1<br />

und T 2 , die gleich sind<br />

Difference(T 1 , T 2 ) = Anzahl der Knoten, in denen sich die<br />

Bäume unterscheiden.<br />

288 / 437<br />

Bewertung des Ansatzes von Baxter<br />

AST-Matching ist syntax-orientiert und deshalb aufwändiger<br />

als lexem-basierter Ansatz, da Parsing notwendig ist<br />

beim lexem-basierten Ansatz müssen nur Schlüsselworte und<br />

Trennzeichen erkannt werden<br />

was aber bei manchen Programmiersprachen eben doch<br />

Parsing voraussetzt, z.B. PL/1:<br />

IF IF = ELSE THEN ELSE := THEN ELSE IF := ELSE<br />

dafür aber genauer:<br />

syntaktische Einheiten werden verglichen, statt einzelner<br />

Code-Zeilen<br />

übereinstimmende Teilausdrücke werden erkannt<br />

289 / 437


Verfahren nach Mayrand u. a. (1996)<br />

Verfahren basiert auf Metriken des Codes.<br />

Vermeidung des quadratischen Aufwands:<br />

im Prinzip immer noch quadratisch, Vergleich ist aber relativ<br />

billig.<br />

Abstraktion von Bezeichnern:<br />

Bezeichner werden von Metriken ignoriert.<br />

Typ-3-Klone:<br />

durch Toleranz der Metriken bzw. ihrer Zusammenfassung.<br />

290 / 437<br />

Vergleich auf Basis von Kennzahlen<br />

Hoffnung:<br />

Code 1 = Code 2 ⇔ Kennzahlen(Code 1 ) =<br />

Kennzahlen(Code 2 )<br />

Granularität üblicherweise Funktionsebene, da hierfür viele<br />

Metriken existieren<br />

Aspekte nach Mayrand u. a. (1996):<br />

Namen<br />

Layout<br />

Anweisungen<br />

Kontrollfluss<br />

291 / 437


Vergleichsmetriken (Mayrand u. a. 1996)<br />

Name<br />

relative Anzahl gemeinsamer Zeichen<br />

Layout<br />

Anzahl Zeichen von Kommentaren (Deklarationsteil,<br />

Implementierungsteil)<br />

Anzahl mehrzeiliger Kommentare<br />

Anzahl nicht-leerer Zeilen (inklusive Kommentare)<br />

durchschnittliche Länge der Bezeichner<br />

292 / 437<br />

Vergleichsmetriken (Mayrand u. a. 1996)<br />

Anweisungen<br />

gesamte Anzahl von Funktionsaufrufen<br />

Anzahl verschiedener Aufrufe<br />

durchschnittliche Komplexität der Entscheidungen in der<br />

Funktion<br />

Anzahl der Deklarationen<br />

Anzahl ausführbarer Anweisungen<br />

Kontrollfluss<br />

Anzahl der Kanten im Kontrollflussgraphen (KFG)<br />

Anzahl der Knoten im KFG<br />

Anzahl der Bedingungen im KFG<br />

Anzahl der Pfade im KFG<br />

Komplexitätsmetrik über dem KFG<br />

293 / 437


Zusammenfassung der Metriken<br />

Zwei Funktionen f 1 und f 2 sind in Bezug auf einen Aspekt:<br />

gleich: gleiche Metrikwerte<br />

ähnlich: alle Metrikwerte liegen in einer gewissen<br />

Bandbreite (spezifisch für jede individuelle<br />

Kennzahl definiert), sind aber nicht gleich<br />

verschieden: mindestens ein Metrikwert liegt außerhalb einer<br />

Bandbreite<br />

294 / 437<br />

Klassifikation<br />

Exakte Kopie: Funktionen sind in jedem Aspekt gleich<br />

(Typ-1-Klon)<br />

Ähnliches Layout: Ähnliches Layout und ähnliche Namen, gleiche<br />

Anweisungen und Kontrollfluss (≈<br />

Typ-2-Klon)<br />

Ähnliche Ausdrücke: Name und Layout sind verschieden,<br />

Anweisungen und Kontrollfluss sind gleich (≈<br />

Typ-2-Klon)<br />

Verschieden: Alle Aspekte sind verschieden.<br />

295 / 437


Bewertung des Ansatzes von Mayrand u. a. (1996)<br />

Aspekte sind nicht unabhängig.<br />

Definition der Bandbreite ist notwendig.<br />

(Klassifikation ist unvollständig.)<br />

Präzision:<br />

Code 1 = Code 2 ⇒<br />

Kennzahlen(Code 2 ) √ Kennzahlen(Code 1 ) =<br />

Code 1 ≈ Code 2 ⇒<br />

Kennzahlen(Code 2 ) √ Kennzahlen(Code 1 ) ≈<br />

Kennzahlen(Code 1 ) = Kennzahlen(Code 2 ) ⇒ Code 1 =<br />

Code 2 ?<br />

Kennzahlen(Code 1 ) ≈ Kennzahlen(Code 2 ) ⇒ Code 1 ≈<br />

Code 2 ???<br />

296 / 437<br />

Vergleich von Klonerkennungstechniken (Bellon 2003;<br />

Bellon u. a. 2007)<br />

Teilnehmer<br />

Matthias Rieger<br />

Brenda S. Baker<br />

Toshihiro Kamiya<br />

Ira D. Baxter<br />

Ettore Merlo<br />

Jens Krinke<br />

Ansatz<br />

Pattern Matching auf Dotplots (textuell)<br />

Suffix-Tree, tokenbasiert (zeilenweise)<br />

Suffix-Tree, tokenbasiert (Einzeltoken)<br />

+ Eingabetransformation<br />

Subtree-Matching im AST<br />

Funktions-Metriken + Token-Vergleich<br />

Program Dependency Graph<br />

297 / 437


Zusammenfassung<br />

Rieger Baker Kamiya Baxter Krinke Merlo<br />

Ansatz Text Token Token AST PDG Metrik<br />

Erkennt Typ 1–3 1, 2 1–3 1, 2 3 1–3<br />

Klassifiziert – 1, 2 – 1, 2 3 1–3<br />

Geschwind. ? + + + - - - + +<br />

Speicher ? + + - + + +<br />

Recall + + + - - -<br />

Precision - - - + - +<br />

Versteckte 31 % 42 % 46 % 28 % 4 % 24 %<br />

298 / 437<br />

Weiterführende Literatur<br />

McCreight (1976): Konstruktion von Suffix-Bäumen für<br />

Zeichenketten in linearer Zeit und mit linearem<br />

Speicheraufwand (relativ zur Länge der Zeichenkette)<br />

Ukkonen (1995): wie McCreight, jedoch wird Eingabe von<br />

links nach rechts verarbeitet (on-line).<br />

Baker (1997) wie McCreight, jedoch für parametrisierte<br />

Zeichenketten<br />

Kodierung kann sich bei Entfernung des ersten Elements<br />

ändern<br />

→ Distinct Right Context Property ist verletzt<br />

www.csse.monash.edu.au/˜lloyd/tildeAlgDS/Tree/<br />

Suffix/ und http://www.dogma.net/markn/articles/<br />

suffixt/suffixt.htm: Erklärungen, Beispiele, Referenzen<br />

und Algorithmen<br />

Koschke u. a. (2006) verwenden Suffixbäume für AST-basierte<br />

Erkennung<br />

299 / 437


• McCreight (1976) stellt einen Algorithmus zur Konstruktion von<br />

Suffix-Bäumen für Zeichenketten vor (als Verbesserung von<br />

Weimers Algorithmus), der lineare Zeit und linearen<br />

Speicheraufwand (relativ zur Länge der Zeichenkette) benötigt.<br />

• Ukkonen (1995) beschreibt einen Algorithmus zur Konstruktion von<br />

Suffix-Bäumen für Zeichenketten, der die gleiche Zeit- und<br />

Speicherkomplexität wie der von McCreight aufweist, dafür aber<br />

algorithmisch einfacher ist und die Eingabe von links nach rechts<br />

verarbeitet (deshalb als on-line bezeichnet wird).<br />

• Baker (1997) erweitert den Algorithmus von McCreight für<br />

parametrisierte Zeichenketten (deren Kodierung sich bei Entfernung<br />

des ersten Elements nämlich ändern kann, was eine Verletzung der<br />

Distinct Right Context Property darstellt, die für das korrekte<br />

Funktionieren von McCreights Algorithmus Voraussetzung ist).<br />

• http://www.csse.monash.edu.au/˜lloyd/tildeAlgDS/Tree/<br />

Suffix/ und http:<br />

//www.dogma.net/markn/articles/suffixt/suffixt.htm<br />

geben Erklärungen, Beispiele, Referenzen und Algorithmen an.<br />

Wiederholungs- und Vertiefungsfragen I<br />

Was versteht man unter Klonen? Welche Typen gibt es? Mit<br />

Beispiel.<br />

Wie entstehen Klone?<br />

Warum sind Klone problematisch?<br />

Welche Klassen von Ansätzen zur Erkennung von Klonen gibt<br />

es und wie sind diese charakterisiert?<br />

Welche Probleme müssen bei der Erkennung bewältigt<br />

werden?<br />

Wie funktioniert der Ansatz von Baker?<br />

Wie ist der Ansatz von Baker zu bewerten?<br />

Wie funktioniert der Ansatz von Baxter?<br />

Wie ist der Ansatz von Baxter zu bewerten?<br />

Wie funktioniert der Ansatz von Mayrand?<br />

Wie ist der Ansatz von Mayrand zu bewerten?<br />

Welche Grundannahme liegt dem Ansatz von Mayrand zu<br />

Grunde? Diskutiere die Annahme.<br />

300 / 437


Refactorings I<br />

Refactoring<br />

Einführung<br />

Kataloge von Bad Smells<br />

Auswirkungen<br />

Integration in den Entwicklungsprozess<br />

Refactoring<br />

Refactorings von Fowler<br />

Pull-Up Field<br />

Extract Method<br />

Wann durchführen?<br />

Evaluation von Refactorings<br />

Kostenschätzung<br />

Portfolio-Analyse<br />

Bewertung Refactorings<br />

Wiederholungsfragen<br />

301 / 437<br />

Übersicht<br />

was ein Bad Smell ist<br />

wie Bad Smells wahrgenommen werden<br />

welche Kategorien von Bad Smells es gibt<br />

welche Auswirkungen Bad Smells haben<br />

wie Bad Smells automatisiert identifiziert werden können<br />

wie man mit Bad Smells umgehen kann<br />

Kompensation<br />

Refactoring<br />

wie man Bad Smells im Entwicklungsprozess integriert managt<br />

302 / 437


Vertrautheit mit Bad Smells<br />

— Yamashita und Moonen (2013)<br />

303 / 437<br />

Umfrage von Yamashita und Moonen (2013):<br />

73 Teilnehmer aus 29 Ländern (davon 12 aus Indien, 10 aus USA, 8 aus<br />

Pakistan, 8 aus Rumänien, sonst 1-3 aus allen anderen Ländern).<br />

Rollenverteilung:<br />

Rolle<br />

Prozent<br />

Angestellter Entwickler 66<br />

Gruppenleiter 18<br />

Architekt 7<br />

Projektmanager 5<br />

Selbstständiger Entwickler 4<br />

Tester 0<br />

Qualitätsmanager 0


Bad Smells<br />

Definition<br />

Bad Smell (Antipattern): wiederkehrender schlechter<br />

Lösungsansatz für ein bestimmtes Problem.<br />

Bad Smells<br />

Code Bad Smells Architecture Bad Smells Process Bad Smells<br />

− duplizierter Code<br />

− Gottklasse<br />

− Architekturverletzungen<br />

− falsch implementierte<br />

oder unpassende<br />

Entwurfsmuster<br />

− hohe Kopplung<br />

− ...<br />

− Analyse−Paralyse<br />

− Design by Committee<br />

− ...<br />

304 / 437<br />

Besorgnis durch Bad Smells<br />

— Yamashita und Moonen (2013)<br />

305 / 437


Besorgnis durch Bad Smells<br />

— Yamashita und Moonen (2013)<br />

306 / 437<br />

Nützlichkeit des Wissens über Bad Smells<br />

— Yamashita und Moonen (2013)<br />

307 / 437


Kataloge von Bad Smells<br />

Aufbläher, z.B. lange Parameterlisten, lange Methoden, lange<br />

Klassen<br />

Verkapsler, z.B. Nachrichtenkette, Mittelsmann<br />

Entbehrliche, z.B. toter Code, faule Klasse, Datenklasse,<br />

duplizierter Code<br />

OO-Schänder, z.B. temporäres Feld, ausgeschlagenes Erbe,<br />

Switch-Anweisungen, parallele Vererbungshierarchien<br />

Koppler, z.B. Feature-Neid, unangebrachte Vertrautheit<br />

andere, z.B. Kommentare<br />

Brown u. a. (1998); Brown und Malveau (2007); Fowler (2000); Mäntylä u. a.<br />

(2004)<br />

308 / 437<br />

Stink Parade of Bad Smells by Fowler (2000)<br />

duplizierter Code<br />

lange Methoden<br />

große Klassen<br />

lange Parameterlisten<br />

divergente Änderung<br />

eine Klasse wird stets geändert in verschiedener Weise und für<br />

unterschiedliche Gründe<br />

Schrotflinten-Chirurgie (Shotgun Surgery)<br />

kleine Änderungen überall<br />

Feature-Neid<br />

sehr viele Attribute einer anderen Klasse werden für eine<br />

Berechnung benutzt<br />

309 / 437


Stink Parade of Bad Smells by Fowler (2000)<br />

Datenklumpen (Data Clumps)<br />

eine Menge von Datenelementen, die häufig gemeinsam<br />

benutzt werden<br />

z.B. Attribute einer Klasse, Parameter in Methodensignaturen<br />

Fixierung aufs Primitive (Primitive Obsession)<br />

einfache Typen werden nicht als Klasse sondern als primitive<br />

Datentypen deklariert<br />

Switch-Anweisungen<br />

händisches dynamisches Binden<br />

Parallele Vererbungshierarchien<br />

Faule Klassen<br />

Klassen, die nichts Nützliches (mehr) tun<br />

. . .<br />

310 / 437<br />

Relevanz nach Entwickleransicht<br />

Rang Bad Smell Punkte<br />

1. Duplizierter Code 19.53<br />

2. Lange Methode 9.78<br />

3. Unnötige Komplexität 8.32<br />

4. Lange Klasse 7.09<br />

5. Exzessive Verwendung von Literalen 3.04<br />

6. Suboptimales Information-Hiding 2.70<br />

7. Faule Klasse 2.33<br />

8. Feature-Neid 2.33<br />

9. Lange Parameterliste 2.31<br />

10. Toter Code 2.25<br />

11. Schlechte (oder Mangel von guten) Kommentaren 1.50<br />

12. Verwendung veralteter Komponenten 1.50<br />

13. Multiple Verantwortlichkeit 1.20<br />

14. Komplexe Bedingungen 1.12<br />

15. Schlechte Benennung 1.12<br />

— Yamashita und Moonen (2013)<br />

311 / 437


Auswirkung von dupliziertem Code (Klone)<br />

13–27 % aller Klone, deren Bezeichner inkonsistent<br />

umbenannt wurden, sind fehlerhaft<br />

11 % aller Klone mit weitergehenden Inkonsistenzen sind<br />

fehlerhaft<br />

7 % aller Änderungen an identischen Klonen im Laufe der<br />

Evolution sind fehlerhaft<br />

Beseitigung von Fehlern in Klonen oft unvollständig<br />

— Li und Shatnawi (2007); Jürgens u. a. (2009); Göde und Koschke (2011);<br />

Harder und Tiarks (2012)<br />

312 / 437<br />

Auswirkungen von Gottklasse, Schrotflinten-Chirurgie und<br />

anderer<br />

höhere Änderungshäufigkeit<br />

höheres Änderungsvolumen<br />

je größer ein System, desto überproportional häufiger<br />

fehleranfälliger<br />

— Olbrich u. a. (2009); Li und Shatnawi (2007); Khomh u. a. (2012, 2009)<br />

313 / 437


Experimente mit Gottklassen<br />

verschlechtert Folge-Design, insbesondere Einsatz von<br />

Vererbung<br />

erschwert Verständnis<br />

erschwert es, Klassenattributen Domänenkonzepte zuzuordnen<br />

— Deligiannis u. a. (2003, 2004); Bois u. a. (2006)<br />

314 / 437<br />

Auswirkungen von Interaktionen von Bad Smells<br />

Interaktion von Blob und Spaghetti Code verursacht Probleme<br />

(Abbes u. a. 2011)<br />

In Klassen, die Wartungsprobleme bereiten, treten mehrere<br />

Bad Smells zugleich auf (Yamashita und Moonen 2013):<br />

Gottmethode, Gottklasse, Mehrzweckvariable, duplizierter<br />

Code, Feature-Neid<br />

Schrotflinten-Chirurgie, ISP-Verletzung<br />

Datenklasse, Datenklumpen<br />

ausgeschlagenes Erbe, falsch platzierte Klasse<br />

315 / 437


Smell versus Bad Smell<br />

Studie von Jbara u. a. (2012):<br />

Einige Quelltextdateien des Linux-Kernels haben hohe<br />

zyklomatische Komplexität<br />

Aber: Entwickler betrachten sie als unproblematisch<br />

Studie von Ratiu u. a. (2004):<br />

Gottklassen und Datenklassen über die Zeit verfolgt<br />

persistent: von Bad Smell zu mind. 95 % ihrer Lebensdauer<br />

betroffen<br />

stabil: wenig Änderungen<br />

circa 20 % der betroffenen Klassen sind stabil und persistent<br />

circa 30 % der betroffenen Klassen sind instabil und nicht<br />

persistent<br />

Studie von Olbrich u. a. (2010):<br />

Junior-Entwickler kommen mit Gottklassen besser zurecht<br />

→ Nicht alle Smells sind wirklich bad, manche Smells sind<br />

tolerierbar.<br />

Wie unterscheidet man Smells und Bad Smells?<br />

316 / 437<br />

Refactorings<br />

Definition<br />

Refactorings: semantikerhaltende, restrukturierende<br />

Code-Transformationen für objekt-orientierte Programme (zur<br />

Verbesserung der Wartbarkeit).<br />

Beschreibung nach Fowler (2000):<br />

Name<br />

Anwendbarkeit<br />

Motivation<br />

mechanische Schritte (die eigentliche Transformation), die von<br />

Hand ausgeführt werden<br />

Beispiel<br />

Sehr viele dieser Refactorings sind genauso auf prozedurale<br />

Programme anwendbar.<br />

317 / 437


Eine Übersicht über den Stand der Wissenschaft zum Thema Refactoring<br />

wurde von Mens und Tourwé (2004) veröffentlicht.<br />

Refactoring-Prozess<br />

Angestoßen von Änderungswunsch.<br />

Prozess (inkrementell, iterativ):<br />

1 Identifikation eines schlechten Geruchs“ (bad smell)<br />

”<br />

2 Refactoring<br />

3 Compile & Test<br />

4 Eigentliche Änderung<br />

5 Compile & Test<br />

318 / 437


70 Refactorings von Fowler (2000)<br />

Methodenzusammensetzung<br />

z.B. Extraktion von Methoden<br />

Eigenschaften zwischen Klassen bewegen<br />

z.B. Verschiebung von Attributen oder Methoden<br />

Organisation von Daten<br />

z.B. Verbergung von Attributen<br />

Vereinfachung bedingter Ausdrücke<br />

z.B. Zerlegung komplexer Bedingungen<br />

Vereinfachung von Methodenaufrufen<br />

z.B. Separierung von bloßem Zugriff von Manipulation<br />

Generalisierungen<br />

z.B. Attribute oder Methoden in der Hierarchie auf- oder<br />

abwärts bewegen<br />

319 / 437<br />

Beispiel: Pull-Up Field<br />

Gleiches Attribut in Unterklassen wird nach Oberklasse verlegt.<br />

Employee<br />

Employee<br />

− Name<br />

Salesman<br />

Engineer<br />

Salesman<br />

Engineer<br />

− Name<br />

− Name<br />

320 / 437


Motivation für Pull-Up Field nach Fowler (2000)<br />

If subclasses are developed independently, or combined through<br />

”<br />

refactoring, you often find that they duplicate features. In<br />

particular, certain fields can be duplicates. Such fields sometimes<br />

have similar names but not always. The only way to determine<br />

what is going on is to look at the fields and see how they are used<br />

by other methods. If they are being used in a similar way, you can<br />

generalize them.“<br />

Doing this reduces duplication in two ways. It removes the<br />

”<br />

duplicate data declaration and allows you to move from the<br />

subclassses to the superclass behavior that uses the field.“<br />

321 / 437<br />

Mechanics für Pull-Up Fields<br />

1 Inspect all uses of the candidate fields to ensure they are used<br />

in the same way.<br />

2 If the fields do not have the same name, rename the fields so<br />

that they have the name you want to use for the superclass<br />

field.<br />

3 Compile and test.<br />

4 Create a new field in the superclass.<br />

5 If the fields are private, you will need to protect the superclass<br />

field so that the subclasses can refer to it.<br />

6 Delete the subclass fields.<br />

7 Compile and test.<br />

8 Consider using Self Encapsulate Field on the new field.<br />

322 / 437


Extract Method<br />

s t a t i c Order o r d e r ;<br />

s t a t i c c h a r name [ ] ;<br />

v o i d p r i n t O w i n g ( ) {<br />

Enumeration e = e l e m e n t s ( o r d e r ) ;<br />

d o u b l e o u t s t a n d i n g = 0 . 0 ;<br />

}<br />

// p r i n t banner<br />

p r i n t f ( ” ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ \ n” ) ;<br />

p r i n t f ( ” ∗ Customer Owes ∗\ n” ) ;<br />

p r i n t f ( ” ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ \ n” ) ;<br />

// c a l c u l a t e o u t s t a n d i n g<br />

w h i l e ( hasMoreElements ( e ) ) {<br />

Order each = nextElement ( e ) ;<br />

o u t s t a n d i n g += getAmount ( each ) ;<br />

}<br />

// p r i n t d e t a i l s<br />

p r i n t f ( ”name %s \ n” , name ) ;<br />

p r i n t f ( ” amount %s \ n” , o u t s t a n d i n g ) ;<br />

323 / 437<br />

Extract Method: parameterlos<br />

// p r i n t banner<br />

v o i d printBanner() {<br />

p r i n t f ( ” ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ \ n” ) ;<br />

p r i n t f ( ” ∗ Customer Owes ∗\ n” ) ;<br />

p r i n t f ( ” ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ \ n” ) ;<br />

}<br />

v o i d p r i n t O w i n g ( ) {<br />

Enumeration e = e l e m e n t s ( o r d e r ) ;<br />

d o u b l e o u t s t a n d i n g = 0 . 0 ;<br />

}<br />

printBanner ( ) ;<br />

// c a l c u l a t e o u t s t a n d i n g<br />

w h i l e ( hasMoreElements ( e ) ) {<br />

Order each = nextElement ( e ) ;<br />

o u t s t a n d i n g += getAmount ( each ) ;<br />

}<br />

// p r i n t d e t a i l s<br />

p r i n t f ( ”name %s \ n” , name ) ;<br />

p r i n t f ( ” amount %s \ n” , o u t s t a n d i n g ) ;<br />

324 / 437


Extract Method: nur Eingabeparameter<br />

// p r i n t d e t a i l s<br />

v o i d printDetails ( d o u b l e o u t s t a n d i n g ) {<br />

p r i n t f ( ”name %s \ n” , name ) ;<br />

p r i n t f ( ” amount %s \ n” , o u t s t a n d i n g ) ;<br />

}<br />

v o i d p r i n t O w i n g ( ) {<br />

Enumeration e = e l e m e n t s ( o r d e r ) ;<br />

d o u b l e o u t s t a n d i n g = 0 . 0 ;<br />

}<br />

p r i n t B a n n e r ( ) ;<br />

// c a l c u l a t e o u t s t a n d i n g<br />

w h i l e ( hasMoreElements ( e ) ) {<br />

Order each = nextElement ( e ) ;<br />

o u t s t a n d i n g += getAmount ( each ) ;<br />

}<br />

printDetails( o u t s t a n d i n g ) ;<br />

325 / 437<br />

Extract Method: mit Ausgabeparameter<br />

// c a l c u l a t e o u t s t a n d i n g<br />

d o u b l e getOutstanding( Enumeration e , d o u b l e o u t s t a n d i n g ) {<br />

}<br />

w h i l e ( hasMoreElements ( e ) ) {<br />

Order each = nextElement ( e ) ;<br />

o u t s t a n d i n g += getAmount ( each ) ;<br />

}<br />

r e t u r n o u t s t a n d i n g ;<br />

v o i d p r i n t O w i n g ( ) {<br />

Enumeration e = e l e m e n t s ( o r d e r ) ;<br />

d o u b l e o u t s t a n d i n g = 0 . 0 ;<br />

}<br />

p r i n t B a n n e r ( ) ;<br />

o u t s t a n d i n g = getOutstanding( e , o u t s t a n d i n g ) ;<br />

p r i n t D e t a i l s ( o u t s t a n d i n g ) ;<br />

326 / 437


Extract Method: weitere Vereinfachung<br />

// c a l c u l a t e o u t s t a n d i n g<br />

d o u b l e g e t O u t s t a n d i n g ( ) {<br />

Enumeration e = e l e m e n t s ( o r d e r ) ;<br />

d o u b l e o u t s t a n d i n g = 0 . 0 ;<br />

}<br />

w h i l e ( hasMoreElements ( e ) ) {<br />

Order each = nextElement ( e ) ;<br />

o u t s t a n d i n g += getAmount ( each ) ;<br />

}<br />

r e t u r n o u t s t a n d i n g ;<br />

v o i d p r i n t O w i n g ( ) {<br />

p r i n t B a n n e r ( ) ;<br />

p r i n t D e t a i l s ( g e t O u t s t a n d i n g ( ) ) ;<br />

}<br />

327 / 437<br />

Wann sollte man Refactorings durchführen?<br />

Refactorings im Kleinen (lokale Änderungen innerhalb weniger<br />

Module)<br />

Wann immer eine Änderung der betreffenden Code-Stellen<br />

ansteht, die ohne Refactoring schwierig ist.<br />

Wenn sich eine Code-Stelle als fehlerträchtig oder unnötig<br />

kompliziert herausstellt.<br />

Wenn eine Code-Stelle permanent auch für verschiedenartige<br />

Kundenwünsche angepasst werden muss.<br />

Refactorings im Großen/<strong>Reengineering</strong> (massive Änderungen<br />

innerhalb eines Subsystems oder Änderungen in der Architektur)<br />

Wenn eine substantielle Erweiterung geplant ist.<br />

Wenn das Subsystem mit neuen Subsystemen integriert<br />

werden soll.<br />

Wenn die Wartbarkeit stark gesunken ist.<br />

328 / 437


Wie kann Erfolg von Refactorings gemessen werden?<br />

Eigentlich ganz einfach (sei P das Programm vor dem Refactoring<br />

und P ′ das danach):<br />

1 Man wähle geeigneten Satz von Metriken M.<br />

2 Erfolg = M(P ′ ) > M(P)<br />

Aber:<br />

Was sind geeignete Metriken?<br />

Und wie lässt sich M(P ′ ) schätzen, bevor das Refactoring<br />

durchgeführt wird?<br />

Was wird das Refactoring kosten und wie lange wird es<br />

dauern?<br />

329 / 437<br />

Kostenschätzung<br />

Faktoren<br />

Produkt, z.B.<br />

relative Wartbarkeit<br />

Metriken der Größe (LOC, Function Points)<br />

Aufwand des Testens bzw. bisherige Testüberdeckung (für<br />

Regressionstest)<br />

Anzahl gefundener Fehler (im Test/Feld) und deren Kosten<br />

Ressourcen, z.B.<br />

verfügbare Werkzeugunterstützung<br />

inklusive Aufwand für Anpassung bzw. Entwicklung von<br />

Werkzeugen<br />

Erfahrung des Wartungspersonals<br />

Prozess, z.B.<br />

bisheriger Aufwand für Wartung, idealerweise bezogen auf<br />

einzelne Aktivitäten<br />

330 / 437


Kostenschätzung<br />

Ideal: Aus den oben genannten Faktoren und den früheren Kosten<br />

wird mittels statistischer Analysen ein Kostenmodell entwickelt.<br />

331 / 437<br />

Alternative Kostenschätzung nach Sneed (1995)<br />

Gesamtaufwand = <strong>Reengineering</strong>-Aufwand + Testaufwand<br />

Komplexität×Größe [loc]<br />

durchschn. Produktivität [loc/pm]<br />

RE-Aufwand [pm] =<br />

über alle<br />

Komponenten<br />

Testaufwand = #Testfälle×Durchschnittsaufwand-pro-Testfall<br />

#Testfälle kann aus zyklomatischer Zahl (McCabe) im Falle<br />

der Zweigüberdeckung abgeleitet werden<br />

Gewichtung bezüglich Testbarkeit, Testunterstützung,<br />

Testumgebung<br />

Erfahrungswert: Testaufwand = n × RE-Aufwand für<br />

1 ≤ n ≤ 3<br />

Annahme: <strong>Reengineering</strong> wird großteils automatisiert.<br />

332 / 437


Wo beginnen? Portfolio-Analyse<br />

Zwei Aspekte:<br />

relative Wartbarkeit<br />

Geschäftswert = Wert der <strong>Software</strong><br />

Bestimmung des Geschäftswerts. . .<br />

durch Marktanalytiker<br />

Marktwert<br />

Beitrag zum Profit<br />

Werbewirksamkeit<br />

. . .<br />

und <strong>Software</strong>architekt<br />

hängen andere wichtige Systeme davon ab?<br />

333 / 437<br />

Portfolio-Analyse<br />

Vergebe 100 Prozentpunkte pro Wertaspekt. Summiere (eventuell<br />

mit Gewichtung) die Zeilen für jedes System.<br />

System Markwert Profit Werbewert Bewertung<br />

A 10 10 10 30<br />

B 40 30 20 90<br />

C 10 30 20 60<br />

D 10 10 30 50<br />

E 30 20 20 70<br />

Summe 100 100 100 300<br />

334 / 437


Zu sanierende Systeme<br />

relative<br />

Wart−<br />

barkeit<br />

gut −<br />

ausreichend<br />

D<br />

C<br />

schlecht<br />

sekundäre<br />

<strong>Reengineering</strong>−<br />

kandidaten<br />

A<br />

primäre<br />

<strong>Reengineering</strong>−<br />

kandidaten<br />

E<br />

B<br />

miserabel<br />

Aufgabe oder<br />

Wrapping<br />

F<br />

Wrapping oder<br />

Neuentwicklung<br />

niedrig<br />

Geschäftswert<br />

hoch<br />

335 / 437<br />

Bewertung Refactorings<br />

Teilweise gut durch Werkzeuge unterstützt:<br />

Refactoring Browser für Smalltalk<br />

Eclipse/JDT für Java<br />

Eclipse/CDT für C/C++<br />

Ansonsten werden die ”<br />

Mechanics“ von Hand durchgeführt<br />

Die Beurteilung, wann und welches Refactoring angebracht<br />

ist, obliegt dem Menschen.<br />

Hinter ”<br />

Compile & Test“ kann sich viel Arbeit verbergen.<br />

Zum Buch von Fowler (2000):<br />

Beschreibung ist zu informell für eine automatisierte<br />

Transformation, aber zumindest eine gute Checkliste.<br />

Die genauen Vorbedingungen sind nicht ausreichend<br />

angegeben.<br />

336 / 437


Wiederholungs- und Vertiefungsfragen I<br />

Welche Schritte sind beim Refactoring durchzuführen?<br />

Was ist ein Bad Smell? Beispiele?<br />

Wie lassen sich Bad Smells erkennen?<br />

Wie kann man die Reihenfolge der zu sanierenden Systeme<br />

festlegen?<br />

337 / 437<br />

Code-Transformationen I<br />

Codetransformation<br />

Vorteile (halb-)automatischer Transformationen<br />

Definition<br />

Transformationssystem<br />

Schritte<br />

Bestandteile<br />

Implementierung<br />

Eigenschaften<br />

Beispiele konkreter Transformationssysteme<br />

Wiederholungsfragen<br />

338 / 437


Wegweiser<br />

Wie kann man Refactorings automatisieren?<br />

339 / 437<br />

(Halb-)Automatische Transformationen<br />

(halb-)automatisch und beliebig wiederholbar<br />

dokumentarisch<br />

Transformation ist Spezifikation der Änderung<br />

kontrollierbar<br />

Transformationen repräsentieren Implementierungswissen<br />

Vor- und Nachbedingungen der Transformationen sind<br />

explizit; sollten automatisch prüfbar sein<br />

Erhalt der Semantik bei einer Transformation lässt sich u. U.<br />

zeigen<br />

340 / 437


Transformation<br />

Eine Transformation ist eine partielle Funktion t:<br />

t: Spezifikation/Programm ↦→ Spezifikation/Programm<br />

Beispiele: Compiler, YACC, Programmrestrukturierer<br />

Transformationen werden oft als Rewrite-Regeln mit<br />

Pattern-Variablen repräsentiert:<br />

r u l e e l i m i n a t e a d d i t i v e i d e n t i t y<br />

r e p l a c e [ e x p r e s s i o n ]<br />

T [ e x p r e s s i o n ] + 0<br />

by<br />

T<br />

end r u l e<br />

Syntax von TXL; siehe www.txl.ca<br />

341 / 437<br />

Transformationssystem<br />

Ein Transformationssystem ist ein System, das semantisch<br />

wohl-definierte (teil-)mechanisierte Programmmodifikationen<br />

ermöglicht.<br />

Source<br />

Code<br />

Parser<br />

Internal<br />

Representation<br />

Unparser<br />

Source<br />

Code<br />

Viewer<br />

focus<br />

reengineering<br />

specialist<br />

Analyzers<br />

Rewrite<br />

Rules<br />

Tranformation<br />

Engine<br />

analyze<br />

/ transform<br />

/ undo request<br />

end user<br />

342 / 437


Schritte einer Transformation<br />

1 Lokation: Identifikation der Stelle, an der Transformation<br />

angewandt werden soll<br />

1 Mustersuche<br />

2 Lokation durch Benutzer<br />

2 Prüfung der Vorbedingungen der Transformation<br />

3 Anwendung, falls Vorbedingungen erfüllt sind<br />

343 / 437<br />

Vorbedingungen von Transformationen<br />

Die Anwendbarkeit von Transformationen ist oft an Bedingungen<br />

geknüpft:<br />

r u l e e l i m i n a t e a d d i t i v e i d e n t i t y<br />

r e p l a c e [ e x p r e s s i o n ]<br />

T1 [ e x p r e s s i o n ] + T2 [ e x p r e s s i o n ]<br />

where<br />

v a l u e (T2) = 0<br />

by<br />

T1<br />

end r u l e<br />

344 / 437


Umsetzung von Transformationen<br />

Ersetzung wird als Prozedur auf dem abstrakten Syntaxbaum<br />

implementiert:<br />

p r o c e d u r e e l i m i n a t e a d d i t i v e i d e n t i t y ( n : i n out node ) i s<br />

b e g i n<br />

i f t y p e ( n ) = p l u s then −− Mustersuche<br />

i f v a l u e ( n . r i g h t ) = 0 −− Bedingung<br />

then<br />

−− E r s e t z u n g<br />

r e p l a c e c h i l d ( p a r e n t ( n ) , from => n , to => n . l e f t ) ;<br />

n . l e f t . p a r e n t := n . p a r e n t ;<br />

d e l e t e t r e e ( n . r i g h t ) ; d e l e t e ( n ) ;<br />

end i f ;<br />

end i f ;<br />

end ;<br />

345 / 437<br />

Eigenschaften von Transformationen<br />

Bestimmte Eigenschaften müssen bei der Transformation<br />

erhalten bleiben, andere Eigenschaften sollen sich ändern:<br />

Performanz<br />

Strukturiertheit<br />

Änderbarkeit<br />

. . .<br />

In vielen (aber nicht allen) Fällen soll die Semantik erhalten<br />

bleiben.<br />

346 / 437


Semantikerhaltende Transformationen<br />

Formale Betrachtung:<br />

Objekt o hat Attribute und ist definiert in einem Kalkül<br />

grundsätzliche Wahrheiten (Axiome) A<br />

Menge von Inferenzregeln i<br />

die Eigenschaften von Objekt o sind dessen Attribute sowie<br />

alle Fakten, die sich aus den Axiomen und Attributen mittels<br />

Inferenzregeln herleiten lassen<br />

Transformation t ist semantikerhaltend genau dann, wenn<br />

für alle Objekte o gilt:<br />

die Eigenschaften von o bleiben durch die Transformation<br />

erhalten: P(o) ⊆ P(t(o))<br />

P(t(o)) ist konsistent (enthält keine Widersprüche)<br />

(alte Eigenschaften bleiben erhalten, neue dürfen<br />

hinzukommen, es ergeben sich keine Widersprüche)<br />

347 / 437<br />

Semantikerhaltende Transformationen<br />

Die Erhaltung der Semantik ist in der Praxis nur sehr schwer<br />

nachweisbar:<br />

Beweis muss nicht nur die Semantik der Programmiersprache,<br />

sondern auch die aller aufgerufenen Betriebssystemfunktionen<br />

u.Ä. einbeziehen, und ist im Allgemeinen schwierig.<br />

Nach Transformationen stets Regressionstests durchführen!<br />

348 / 437


Beispiel eines Transformationssystems<br />

TXL 1 (Queens University, Canada): generisches<br />

Transformationssystem; ausgeprägt für C, C++, Java, Javascript,<br />

Modula, Object Pascal, XML:<br />

konkreter Syntaxbaum wird generiert<br />

funktionale Programmiersprache mit Transformationsregeln<br />

mit ”<br />

native Patterns“:<br />

automatische Traversierung des konkreten Syntaxbaums und<br />

Anwendung der Transformationen, solange dies möglich ist<br />

1 http://www.txl.ca<br />

349 / 437<br />

TXL<br />

r u l e e l i m i n a t e r e d u n d a n t d e c l a r a t i o n s<br />

r e p l a c e [ r e p e a t s t a t e m e n t ]<br />

v a r X [ i d ] : T [ t y p e s p e c ]<br />

R e s t o f S c o p e [ r e p e a t s t a t e m e n t ]<br />

where not<br />

R e s t o f S c o p e [ r e f e r e n c e s X]<br />

by<br />

R e s t o f S c o p e<br />

end r u l e<br />

f u n c t i o n r e f e r e n c e s X [ i d ]<br />

match ∗ [ i d ] X<br />

end f u n c t i o n<br />

350 / 437


Domain Maintenance System (DMS)<br />

Hersteller: Semantic Designs, Austin, Texas, USA<br />

generisches Transformationssystem<br />

parametrisierbar durch so genannte Domains (Grammatiken)<br />

konkrete Instanzen für C, C++, Java, Cobol, Jovial, . . .<br />

abstrakte Regeln für Parse-Baum<br />

Clone-Doctor 2 erkennt Klone (mit dem Verfahren von Baxter<br />

et al.) und beseitigt sie<br />

2 Eingetragenes Warenzeichen von Semantic Designs<br />

http://www.semdesigns.com<br />

351 / 437<br />

DMS-Beispiel<br />

d e f a u l t base domain Java ;<br />

r u l e merge− i f s ( \ c o n d i t i o n 1 ,<br />

\ c o n d i t i o n 2 ,<br />

\ then−s t a t e m e n t s )<br />

=<br />

” i f ( \ c o n d i t i o n 1 )<br />

i f ( \ c o n d i t i o n 2 )<br />

{ \ then−s t a t e m e n t s } ”<br />

r e w r i t e s to<br />

” i f ( \ c o n d i t i o n 1 && \ c o n d i t i o n 2 )<br />

{ \ then−s t a t e m e n t s } ”<br />

;<br />

352 / 437


Raincode<br />

Hersteller: Raincode 3 , Brüssel, Belgien<br />

unterstützte Sprachen: Ada, APS, C, C++, COBOL, CSP,<br />

Delphi, Ideal, Informix 4GL, Java, Natural, PL/1<br />

Skript-Sprache, um Transformationen zu programmieren<br />

Transformation findet auf dem Text selbst statt<br />

3 http://www.raincode.com<br />

353 / 437<br />

Raincode-Beispiel I<br />

PROCEDURE TERMINATE<br />

VAR v a l u e ;<br />

BEGIN<br />

−− scan a l l nodes<br />

FOR exp IN ROOT. SubNodes DO<br />

−− i f i t i s an e x p r e s s i o n t h a t i s not a s i m p l e l i t e r a l<br />

−− and i t has not been m o d i f i e d y e t<br />

IF exp I S NonCommaExpression<br />

AND exp I S NOT L i t e r a l<br />

AND exp [ ” Patched ”]TRUE THEN<br />

−− do e v a l u a t i o n<br />

v a l u e := E v a l ( exp ) ;<br />

−− i f e v a l u a t i o n s u c c e e d s<br />

IF v a l u e VOID THEN<br />

−− r e p l a c e the e x p r e s s i o n by i t s v a l u e<br />

PATCH. ReplaceNt ( exp , v a l u e ) ;<br />

−− r e p o r t something on c o n s o l e<br />

exp . W r i t e E r r o r ( LIST . T o S t r i n g (PATCH. NtImage ( exp )<br />

| | STR . Trim (X) , ” ” ) , ” = ”&v a l u e ) ;<br />

−− add a n n o t a t i o n so we do not p r o c e s s subnodes<br />

354 / 437


Raincode-Beispiel II<br />

FOR IN exp . SubNodes DO<br />

X . Patched := TRUE;<br />

END;<br />

END;<br />

END;<br />

END;<br />

−− s a v e the p r o c e s s e d s o u r c e<br />

PATCH. Save (ROOT. SourceName&” . patched ” ) ;<br />

END;<br />

355 / 437<br />

Wiederholungs- und Vertiefungsfragen<br />

Welche Vorteile haben semi-automatische Transformationen?<br />

Welche wichtige Eigenschaften sollten Transformationen im<br />

RE haben? Warum?<br />

Was sollte man nach der Durchführung einer Transformation<br />

realistischerweise stets tun?<br />

Aus welchen Bestandteilen besteht eine Transformation?<br />

Wie wird ein Rewrite implementiert, wenn als<br />

Zwischendarstellung ein AST benutzt wird?<br />

356 / 437


<strong>Software</strong>-Visualisierung I<br />

<strong>Software</strong>-Visualisierung<br />

Über diese Folien<br />

Lernziele<br />

<strong>Reengineering</strong>-Kontext<br />

Programm-Visualisierung<br />

Statische Programm-Visualisierung<br />

Graphen mit Metriken<br />

Klassenblaupause<br />

Dynamische Programm-Visualisierung<br />

Evolutionäre Aspekte<br />

Zusammenfassung<br />

Wiederholungsfragen<br />

357 / 437<br />

Über diese Folien<br />

Diese Folien entstammen einer Präsentation von Michele Lanza<br />

(Universität Lugano), mit dessen freundlicher Genehmigung.<br />

Die Unterschiede zum Original sind:<br />

Übersetzung ins Deutsche<br />

Kleinere Restrukturierungen und Verkürzungen<br />

Weitere Visualisierungen hinzugefügt<br />

358 / 437


Lernziele und Kontext<br />

Lernziele<br />

<strong>Software</strong>-Visualisierung in einem <strong>Reengineering</strong>-Kontext<br />

Statische Code-Visualisierung<br />

Dynamische Code-Visualisierung<br />

Visualisierung von Metriken<br />

Visualisierung der Evolution<br />

Leichtgewichtige Ansätze<br />

Kontext<br />

<strong>Reengineering</strong> ist meist interaktiv<br />

Große Datenmengen müssen verstanden werden<br />

359 / 437<br />

Definitionen zu <strong>Software</strong>-Visualisierung<br />

<strong>Software</strong> Visualization is the use of the crafts of<br />

typography, graphic design, animation, and<br />

cinematography with modern human-computer<br />

interaction and computer graphics technology to<br />

facilitate both the human understanding and effective use<br />

of computer software.<br />

– Price, Baecker and Small, Introduction to <strong>Software</strong> Visualization<br />

<strong>Software</strong> is intangible, having no physical shape or<br />

size. <strong>Software</strong> visualisation tools use graphical techniques<br />

to make software visible by displaying programs, program<br />

artifacts and program behaviour.<br />

– Thomas Ball<br />

360 / 437


SV im <strong>Reengineering</strong>-Kontext<br />

Ziele: Reduktion der Komplexität<br />

Herausforderungen:<br />

Skalierbarkeit<br />

Aufgabenabhängigkeit der Visualisierung<br />

Art der Visualisierung<br />

Begrenzte Ressourcen<br />

361 / 437<br />

Programm-Visualisierung<br />

Program visualization is the visualization of the<br />

actual program code or data structures in either static or<br />

dynamic form.<br />

– [Price, Baecker und Small]<br />

Gebiete<br />

Statische Programm-Visualisierung<br />

Dynamische Programm-Visualisierung<br />

Aufgaben<br />

Verschiedene Sichten generieren<br />

Inferenzen ermöglichen<br />

Spezifische Probleme (aktives Forschungsgebiet)<br />

Effiziente Ausnutzung des Platzes, Kantenüberschneidungen,<br />

Layout-Probleme, Fokus, Human-Computer-Interaction, . . .<br />

Keine Konventionen (Farben, Symbole, Interpretation, . . . )<br />

Granularität?<br />

Ganze System, Subsysteme, Module, Klassen, Hierarchien,...<br />

Wofür, wie und wann anzuwenden?<br />

362 / 437


Statische Programm-Visualisierung<br />

Visualisierung von Information, die statisch abgeleitet ist.<br />

Hängt von Sprache und Sprachparadigma ab:<br />

objektorientierte Sprachen: Klassen, Methoden, Attribute,<br />

Vererbung, . . .<br />

prozedurale Sprachen: Prozeduren, Aufrufe, . . .<br />

. . .<br />

Mittel:<br />

Quellcode<br />

Bäume<br />

Graphen<br />

Metaphern<br />

363 / 437<br />

Visualisierung von Code: SeeSoft (Eick u. a. 1992)<br />

364 / 437


Bäume: Klassen-Hierarchien<br />

Jun/OpenGL<br />

Smalltalk<br />

Klassenhierarchie<br />

Probleme:<br />

Keine weitere<br />

Bedeutung der Farbe<br />

Überladen<br />

365 / 437<br />

Bäume<br />

Euklidische Kegel<br />

Vorteile:<br />

Nutzen dritte<br />

Dimension<br />

Nachteile:<br />

Mangelnde Tiefe<br />

Navigation<br />

Hyperbolische Bäume<br />

Vorteile:<br />

Fokus ist wählbar<br />

Dynamisch<br />

Nachteile: Copyright<br />

366 / 437


Bäume mit Metriken: Tree Maps<br />

Vorteile:<br />

hierarchisch (m.E.)<br />

100% Raumnutzung<br />

skaliert<br />

Nachteile:<br />

Grenzen<br />

Wirres Bild<br />

Interpretation<br />

Nur Blätter<br />

367 / 437<br />

Treemap für Klone<br />

368 / 437


Bäume mit Metriken: Tree Maps mit Texturen<br />

– Quelle: Danny Holten http://www.win.tue.nl/˜dholten/<br />

369 / 437<br />

Klassendiagramme<br />

370 / 437


Klassendiagramme<br />

(wie von herkömmlichen CASE-Werkzeugen dargestellt. . . )<br />

Vorteile:<br />

Stellen objektorientierte Konzepte dar<br />

Geeignet für kleine Ausschnitte<br />

Nachteile:<br />

Skalieren nicht<br />

Benötigen Filter für relevante Informationen<br />

Nur voreingestellte Sichten<br />

371 / 437<br />

Klassendiagramme mit visuellen Attributen<br />

– Quelle: Alexandru Telea<br />

http://www.win.tue.nl/˜alext/ARCHIVIEW/<br />

372 / 437


Klassendiagramme mit Soft Shapes<br />

– Quelle: Alexandru Telea<br />

http://www.win.tue.nl/˜alext/ARCHIVIEW/<br />

373 / 437<br />

Allgemeine Graphen: Rigi<br />

Entity-Relationship-<br />

Visualisierung<br />

generische Präsentation<br />

durch typisierte Graphen<br />

Probleme:<br />

Filterung<br />

Navigation<br />

Skalierbarkeit<br />

374 / 437


Allgemeine Graphen: Rigi<br />

Entities können gruppiert<br />

werden<br />

Vorteile:<br />

Skaliert besser<br />

generisch<br />

Nachteile:<br />

Wenig Programmiersprachensemantik<br />

375 / 437<br />

Gebündelte Kanten<br />

– Quelle: Danny Holten http://www.win.tue.nl/˜dholten/<br />

376 / 437


Gebündelte Kanten<br />

– Quelle: Danny Holten http://www.win.tue.nl/˜dholten/<br />

377 / 437<br />

Gebündelte Kanten mit Metriken<br />

378 / 437


Graphen mit Metriken (Lanza 2003; Lanza und Ducasse<br />

2003)<br />

Kombination von Metriken und<br />

<strong>Software</strong>-Visualisierung<br />

Graph-Repräsentation<br />

Bis zu fünf Metriken<br />

bestimmen die Visualisierung<br />

der Knoten:<br />

Größe (1+2)<br />

Farbe/Farbton (3)<br />

Position (4+5)<br />

Element<br />

X-Koordinate Relation<br />

Y-Koordinate<br />

Farbton Höhe<br />

Breite<br />

379 / 437<br />

System Complexity View<br />

380 / 437


Method Efficiency Correlation View<br />

381 / 437<br />

Inheritance Classification View<br />

382 / 437


Added: Ganz neue Methoden.<br />

Overridden: Ererbt und redefinierte Methoden.<br />

Extended: Overridden + Aufruf der gleichen Methode via super<br />

(Smalltalk), d.h. Aufruf zur entsprechenden Methode, die überschrieben<br />

wurde.<br />

Data Storage Class Detection View<br />

383 / 437


Von links nach rechts nach Breite (Number of Methods) geordnet.<br />

Dieselbe Metrik (Number of Methods) wird hier auf dreifache Weise<br />

dargestellt: Breite, Position innerhalb der Ordnung und Farbe. Dadurch<br />

kann ein verstärkender Effekt erzeugt werden.<br />

Innereien und Abhängigkeiten<br />

384 / 437


Klassenblaupause (Ducasse und Lanza 2005)<br />

385 / 437<br />

Die Einteilung hängt etwas von der Ausdruckskraft der<br />

Programmiersprache ab, auch von dem, was der Benutzer tatsächlich<br />

sehen möchte: aus Sicht der Spezifikation bzw. aus Sicht der<br />

tatsächlichen Verhältnisse.<br />

• Initialization: Kontruktoren (soweit die Programmiersprache sie<br />

explizit darstellt, auch: Methoden, die Init und ähnliche<br />

Zeichenketten im Namen enthalten oder solche, die einen<br />

new-Operator für die Klasse enthalten<br />

• Interface: öffentliche Methoden; auch: solche, die tatsächlich von<br />

außerhalb aufgerufen werden (Accessors sind in einer anderen<br />

Kategorie; siehe unten)<br />

• Implementation: versteckte Methoden bzw. solche, die von<br />

außerhalb nicht benutzt werden<br />

• Accessors: Öffentliche Methoden, die nichts weiter tun als ein<br />

Attribut zu setzen bzw. zu lesen<br />

• Attribute: versteckte Attribute


Semantische Information<br />

Kategorisierung von Klassen<br />

Basiert auf<br />

Klassenblaupausen<br />

Zwei Perspektiven:<br />

Einzelne Klasse<br />

Vererbungskontext<br />

Klassenblaupause<br />

für jede einzelne<br />

Klasse<br />

Sind als Baum<br />

angeordnet<br />

386 / 437


Klassenblaupause: Data Storage<br />

Viele Attribute<br />

Kann viele<br />

Zugriffsoperationen haben<br />

(Accessors)<br />

Harmloses Verhalten<br />

387 / 437<br />

Klassenblaupause: Wide Interface<br />

Viele Methoden in der<br />

Schnittstelle<br />

388 / 437


Klassenblaupause: Large Implementation<br />

Geschachtelte<br />

Aufrufstruktur<br />

Viele Methoden<br />

Hohe Komplexität<br />

Breite Schnittstelle<br />

389 / 437<br />

Metaphorische Visualisierung<br />

– Quelle: Richard Wettel<br />

http://www.inf.usi.ch/phd/wettel/codecity-wof.html<br />

390 / 437


Dynamische Programm-Visualisierung<br />

Visualisierung dynamischen Verhaltens<br />

Ausführungs-Trace<br />

Ressourcenverbrauch (Speicher/Laufzeit)<br />

Objekt-Interaktion<br />

. . .<br />

Schritte:<br />

1 Code-Instrumentierung<br />

2 Gewinnung der Laufzeitinformation<br />

3 Auswertung der Laufzeitinformation<br />

4 Visualisierung der Auswertung<br />

391 / 437<br />

Dynamische SV: Probleme<br />

Code-Instrumentierungsproblem<br />

Logging, erweiterte Virtual Machines, Method-Wrapping<br />

Skalierbarkeitsproblem<br />

Fülle der Details<br />

Traces werden sehr schnell enorm groß<br />

Vollständigkeitsproblem<br />

Information gilt nur für betrachtete Szenarien<br />

392 / 437


Beispiel 1: JInsight<br />

(http://www.research.ibm.com/jinsight)<br />

Visualisierung von Ausführungs-Traces<br />

393 / 437<br />

Visualisiert Sequenzdiagramme; jeder vertikale Streifen repräsentiert ein<br />

Objekt; die Farbe des Streifens kodiert die Klasse. Die Zeit schreitet von<br />

oben nach unten fort. Aufrufe sind durch Kanten zwischen Objekten<br />

dargestellt und haben die aufgerufene Methode als Label. Die zeitlichen<br />

Bereiche, in denen ein Objekt tatsächlich aktiv sind (d.h. nicht nur eine<br />

andere Methode aufrufen, sondern tatsächlich selbst rechnen), sind im<br />

Streifen etwas heller dargestellt.<br />

JInsight erlaubt es auch, wiederkehrende Aufrufmuster automatisch zu<br />

erkennen und darzustellen. Ähnliche, aber leicht unterschiedliche Muster<br />

können dabei übereinandergelegt werden, um so die Unterschiede<br />

darzustellen.


Heatmap<br />

394 / 437<br />

Beispiel 2: Aufrufmatrix für Klassen (Heatmap)<br />

einfach<br />

skaliert<br />

reproduzierbar<br />

395 / 437


Inter-Class Call Matrix: This view provides an overview of the<br />

communication between classes. The matrix plots which classes call<br />

which other classes. Classes are arranged along both axes in the order<br />

they are instantiated. The color of the dots indicate the frequency of that<br />

communication.<br />

Evolutionäre Aspekte<br />

Die Gegenwart ist oft verständlicher, wenn man die<br />

Vergangenheit kennt.<br />

Betrachtung von Aspekten des Systems über die Zeit.<br />

Metrikwerte können besser eingeschätzt werden.<br />

Erlaubt, Trends auszumachen.<br />

396 / 437


Evolutionsmatrix<br />

397 / 437<br />

Kategorisierung von Klassen anhand der Evolutionsmatrix<br />

Dargestellte Metriken für Klassen:<br />

NOM (number of methods)<br />

NOA (number of attributes)<br />

Kategorisierung anhand der ”<br />

individuellen Evolution“ und der<br />

” System-Evolution“:<br />

Pulsar<br />

Supernova<br />

Weißer Zwerg<br />

Roter Riese<br />

Dornröschen<br />

Eintagsfliege<br />

Methusalem<br />

398 / 437


Pulsar<br />

Definition<br />

Pulsar: wiederholte Änderungen, die Element größer und kleiner<br />

werden lassen.<br />

→ System-Hotspot: Jede neue Version verlangt Anpassungen.<br />

399 / 437<br />

Supernova<br />

Definition<br />

Supernova: Plötzlicher Anstieg. Mögliche Gründe:<br />

massive Restrukturierung<br />

Datenspeicher für Daten, die plötzlich hinzugekommen sind<br />

Schläfer: Stumpf, der mit Funktionalität gefüllt wird<br />

400 / 437


Weißer Zwerg, Roter Riese, Dornröschen<br />

401 / 437<br />

Eintagsfliege & Methusalem<br />

402 / 437


Fallstudie MooseFinder (38 Versionen)<br />

403 / 437<br />

Spektrograph<br />

x-Achse: Zeit; y-Achse: <strong>Software</strong>einheit; Farbe: #commits<br />

404 / 437


Courtesy Jingwei Wu, Richard C. Holt, Ahmed Hassan, University of<br />

Waterloo, Canada<br />

Färbung im Spektrograph<br />

• linearer Gradient<br />

• exponentieller Gradient<br />

• Stufen


Evostreets (Steinbrückner 2013)<br />

405 / 437<br />

Quelle der Bilder: Dr. Frank Steinbrückner


<strong>Software</strong>-Visualisierung (SV) I<br />

SV ist unabdingbar im <strong>Reengineering</strong>-Kontext<br />

Match-Mismatch-Hypothese:<br />

problem-solving performance depends on<br />

whether the structure of a problem is matched by<br />

the structure of a notation<br />

– Gilmore und Green<br />

Jede SV betont bestimmte Information und vernachlässigt<br />

andere Information.<br />

Geeignete SV ist abhängig von der zu lösenden Aufgabe.<br />

408 / 437<br />

<strong>Software</strong>-Visualisierung (SV) II<br />

Alphabetismus“ der SV<br />

”<br />

Wie drücke ich es aus?<br />

Wie interpretiere ich es?<br />

Vieles noch in der Forschung, wenig in kommerziellen<br />

Werkzeugen<br />

<strong>Software</strong>-Wahrnehmung: Andere menschliche Sinne werden<br />

genutzt<br />

409 / 437


Wiederholungs- und Vertiefungsfragen I<br />

Wofür eignen sich Tree Maps und wie sind sie zu<br />

interpretieren?<br />

Welche Probleme gibt es bei der Visualisierung dynamischer<br />

Daten?<br />

Wie lassen sich Metriken visualisieren?<br />

Was ist eine Klassenblaupause?<br />

Wie lassen sich damit Klassen charakterisieren?<br />

Welche zusätzlichen Aspekte lassen sich durch die<br />

Visualisierung evolutionärer Daten gewinnen?<br />

Was sagt die Match-Mismatch-Hypothese (Gilmore und<br />

Green) aus?<br />

410 / 437<br />

1 Abbes u. a. 2011 Abbes, Marwen ; Khomh, Foutse ;<br />

Guéhéneuc, Yann-Gaël ; Antoniol, Giuliano: An Empirical<br />

Study of the Impact of Two Antipatterns, Blob and Spaghetti<br />

Code, on Program Comprehension. In: Proceedings of the<br />

European Conference on <strong>Software</strong> Maintenance and<br />

<strong>Reengineering</strong>, IEEE Computer Society Press, 2011, S. 181–190<br />

2 Agrawal und Horgan 1990 Agrawal, Hiralal ; Horgan,<br />

Joseph R.: Dynamic Program Slicing. In: Proceedings of the<br />

Conference on Programming Language Design and<br />

Implementation, ACM Press, Juni 1990, S. 246–256<br />

3 Arthur 1988 Arthur, L.J.: <strong>Software</strong> Evolution: The <strong>Software</strong><br />

Maintenance Challenge. New York, NY : John Wiley & Sons,<br />

1988<br />

4 Baker 1997 Baker, Brenda: Parameterized duplication in<br />

strings: Algorithms and an application to software maintenance.<br />

26 (1997), Oktober, Nr. 5, S. 1343–1362<br />

411 / 437


5 Baker 1995 Baker, Brenda S.: On Finding Duplication and<br />

Near-Duplication in Large <strong>Software</strong> Systems. In: Wills, L.<br />

(Hrsg.) ; Newcomb, P. (Hrsg.) ; Chikofsky, E. (Hrsg.):<br />

Proceedings of the Working Conference on Reverse Engineering.<br />

Los Alamitos, California : IEEE Computer Society Press, Juli<br />

1995, S. 86–95. – URL<br />

http://citeseer.nj.nec.com/baker95finding.html<br />

6 Basili und Weiss 1984 Basili, R. ; Weiss, D. M.: A<br />

Methodology for Collecting Valid <strong>Software</strong> Engineering Data. In:<br />

IEEE Computer Society Transactions on <strong>Software</strong> Engineering<br />

10 (1984), November, Nr. 6, S. 728–738<br />

7 Baumöl u. a. 1996 Baumöl, Ulrike ; Borchers, Jens ;<br />

Eicker, Stefan ; Hildebrand, Knut ; Jung, Reinhard ;<br />

Lehner, Franz: Einordnung und Terminologie des <strong>Software</strong><br />

<strong>Reengineering</strong>. In: <strong>Informatik</strong> Spektrum 19 (1996), S. 191–195<br />

412 / 437<br />

8 Baxter u. a. 1998 Baxter, Ira D. ; Yahin, Andrew ; Moura,<br />

Leonardo ; Sant’Anna, Marcelo ; Bier, Lorraine: Clone<br />

Detection Using Abstract Syntax Trees. In: Koshgoftaar,<br />

T. M. (Hrsg.) ; Bennett, K. (Hrsg.): Proceedings of the<br />

International Conference on <strong>Software</strong> Maintenance, IEEE<br />

Computer Society Press, 1998, S. 368–378. – ISBN<br />

0-7803-5255-6, 0-8186-8779-7, 0-8186-8795-9<br />

9 Bellon 2003 Bellon, Stefan:<br />

Vergleich von Klonerkennungstechniken. Fakultät <strong>Informatik</strong>,<br />

Universität Stuttgart, Deutschland, Diplomarbeit, 2003<br />

10 Bellon u. a. 2007 Bellon, Stefan ; Koschke, Rainer ;<br />

Antoniol, Giulio ; Krinke, Jens ; Merlo, Ettore:<br />

Comparison and Evaluation of Clone Detection Tools. In: IEEE<br />

Computer Society Transactions on <strong>Software</strong> Engineering 33<br />

(2007), September, Nr. 9, S. 577–591<br />

11 Bennett 1994 Bennett, P.A.: <strong>Software</strong> Development for the<br />

Channel Tunnel: a Summary. In: High Integrity Systems 1<br />

(1994), Nr. 2, S. 213–220<br />

413 / 437


12 Boehm 1981 Boehm, Barry: <strong>Software</strong> Engineering Economics.<br />

Englewood Cliffs, NJ : Prentice Hall, 1981<br />

13 Bois u. a. 2006 Bois, B. D. ; Demeyer, S. ; Verelst, J. ;<br />

Mens, T. ; Temmerman, M.: Does god class decomposition<br />

affect comprehensibility? In: Proceedings of the IASTED<br />

Conference on <strong>Software</strong> Engineering, IASTED/ACTA Press,<br />

2006, S. 346–355<br />

14 Brown u. a. 1998 Brown, W. J. ; Malveau, R. C. ; Brown,<br />

W. H. ; III, H. W. M. ; Mowbray, T. J.: Anti Patterns:<br />

Refactoring <strong>Software</strong>, Architectures, and Projects in Crisis. 1st<br />

edition. John Wiley and Sons, März 1998<br />

15 Brown und Malveau 2007 Brown, William J. ; Malveau,<br />

Raphael C.: Anti Patterns: Entwurfsfehler erkennen und<br />

vermeiden. zweite überarbeitete Auflage. Mitp-Verlag, 2007<br />

414 / 437<br />

16 Bruntink und van Deursen 2004 Bruntink, M. ; Deursen,<br />

A. van: Predicting Class Testability Using Object-Oriented<br />

Metrics. In: Proceedings of the IEEE International Workshop on<br />

Source Code Analysis and Manipulation, IEEE Computer Society<br />

Press, September 2004<br />

17 Chidamber 1994 Chidamber, S.: Metrics Suite For Object<br />

Oriented Design, M.I.T, Cambridge, Dissertation, 1994<br />

18 Chidamber und Kemerer 1994 Chidamber, S.R. ;<br />

Kemerer, C.F.: A Metrics Suite for Object-Oriented Design.<br />

In: IEEE Computer Society Transactions on <strong>Software</strong><br />

Engineering 20 (1994), Juni, Nr. 6, S. 476–493<br />

19 Chikofsky und Cross II. 1990 Chikofsky, Elliot J. ; Cross<br />

II., James H.: Reverse Engineering and Design Recovery: A<br />

Taxonomy. In: IEEE <strong>Software</strong> 7 (1990), Januar, Nr. 1, S. 13–17<br />

415 / 437


20 Chou u. a. 2001 Chou, Andy ; Yang, Junfeng ; Chelf,<br />

Benjamin ; Hallem, Seth ; Engler, Dawson R.: An Empirical<br />

Study of Operating System Errors. In: Symposium on Operating<br />

Systems Principles, ACM Press, 2001, S. 73–88. – URL<br />

citeseer.ist.psu.edu/chou01empirical.html<br />

21 Cordy u. a. 2004 Cordy, James R. ; Dean, Thomas R. ;<br />

Synytskyy, Nikita: Practical language-independent detection<br />

of near-miss clones. In: Proceedings of the Conference of the<br />

Centre for Advanced Studies on Collaborative research, IBM<br />

Press, 2004, S. 1–12<br />

22 Cytron u. a. 1991 Cytron, Ron ; Ferrante, Jeanne ;<br />

Rosen, Barry K. ; Wegman, Mark N. ; Zadeck, F. K.:<br />

Efficiently Computing Static Single Assignment Form and the<br />

Control Dependence Graph. In: ACM Transactions on<br />

Programming Languages and Systems 13 (1991), Oktober,<br />

Nr. 4, S. 451–490<br />

416 / 437<br />

23 Dagpinar und Jahnke 2003 Dagpinar, M. ; Jahnke, J.:<br />

Predicting Maintainability with Object-Oriented Metrics – An<br />

Empirical Comparison. In: Proceedings of the Working<br />

Conference on Reverse Engineering, IEEE Computer Society<br />

Press, 2003<br />

24 Deligiannis u. a. 2004 Deligiannis, I. ; Shepperd, M. ;<br />

Roumeliotis, M. ; Stamelos, I.: An empirical investigation<br />

of an object-oriented design heuristic for maintainability. In:<br />

Journal of Systems and <strong>Software</strong> 72 (2004), Nr. 2, S. 129–143<br />

25 Deligiannis u. a. 2003 Deligiannis, Ignatios ; Stamelos,<br />

Ioannis ; Angelis, Lefteris ; Roumeliotis, Manos ;<br />

Shepperd, Martin: Controlled Experiment Investigation of an<br />

Object Oriented Design Heuristic for Maintainability. In: Journal<br />

of Systems and <strong>Software</strong> 65 (2003), Februar, Nr. 2, S. 127–139<br />

26 Demeyer u. a. 2002 Demeyer, Serge ; Ducasse, Stephane ;<br />

Nierstrasz, Oscar: Object Oriented <strong>Reengineering</strong> Patterns.<br />

Morgan Kaufmann, 2002<br />

417 / 437


27 Ducasse und Lanza 2005 Ducasse, Stephane ; Lanza,<br />

Michele: The Class Blueprint: Visually Supporting the<br />

Understanding of Classes. In: IEEE Computer Society<br />

Transactions on <strong>Software</strong> Engineering 31 (2005), Januar, Nr. 1,<br />

S. 75–90<br />

28 Ducasse u. a. 1999 Ducasse, Stéphane ; Rieger, Matthias ;<br />

Demeyer, Serge: A Language Independent Approach for<br />

Detecting Duplicated Code. In: Proceedings of the International<br />

Conference on <strong>Software</strong> Maintenance (ICSM99), 1999,<br />

S. 109–118<br />

29 Eick u. a. 1992 Eick, Stephen G. ; Steffen, Joseph L. ;<br />

Sumner, Eric E.: Seesoft—A Tool for Visualizing Line Oriented<br />

<strong>Software</strong> Statistics. In: IEEE Computer Society Transactions on<br />

<strong>Software</strong> Engineering 18 (1992), November, S. 957–968<br />

418 / 437<br />

30 El Emam u. a. 2001 El Emam, Kalhed ; Benlarbi, Saïda ;<br />

Goel, Nishith ; Rai, Shesh N.: The Confounding Effect of<br />

Class Size on the Validity of Object-Oriented Metrics. In: IEEE<br />

Computer Society Transactions on <strong>Software</strong> Engineering 27<br />

(2001), Nr. 7, S. 630–650. – ISSN 0098-5589<br />

31 Ernst 2000 Ernst, Michael: Dynamically Discovering Likely<br />

Program Invariants. Seattle, Washington, USA, University of<br />

Washington, Department of Computer Science and Engineering,<br />

Dissertation, August 2000<br />

32 Fenton und Pfleeger 1996 Fenton, N. ; Pfleeger, S.:<br />

<strong>Software</strong> Metrics: A Rigorous and Practical Approach. 2nd.<br />

London : International Thomson Computer Press, 1996<br />

33 Fenton und Ohlsson 2000 Fenton, Norman E. ; Ohlsson,<br />

Niclas: Quantitative Analysis of Faults and Failures in a Complex<br />

<strong>Software</strong> System. In: IEEE Computer Society Transactions on<br />

<strong>Software</strong> Engineering 26 (2000), August, Nr. 8, S. 797–814<br />

419 / 437


34 Fjeldstad und Hamlen 1979 Fjeldstad, R.K. ; Hamlen,<br />

W.T.: Application Program Maintenance Study: Report to our<br />

Respondents. In: Proceedings of the GUIDE 48. Philadelphia,<br />

PA : The Guide Corporation, 1979<br />

35 Fowler 2000 Fowler, Martin: Refactoring: Improving the<br />

Design of Existing Code. Addison-Wesley, 2000<br />

36 Göde und Koschke 2011 Göde, Nils ; Koschke, Rainer:<br />

Frequency and Risks of Changes to Clones. In: Proceedings of<br />

the International Conference on <strong>Software</strong> Engineering, ACM<br />

Press, 2011, S. 311–320<br />

37 Grady 1994 Grady, R.B.: Successfully Applying <strong>Software</strong><br />

Metrics. In: IEEE Computer 27 (1994), September, Nr. 9,<br />

S. 18–25<br />

38 Halstead 1977 Halstead, Maurice: Elements of <strong>Software</strong><br />

Science. Elsevier, 1977<br />

420 / 437<br />

39 Harder und Tiarks 2012 Harder, Jan ; Tiarks, Rebecca: A<br />

Controlled Experiment on <strong>Software</strong> Clones. In: Proceedings of<br />

the International Conference on Program Comprehension, IEEE<br />

Computer Society Press, 2012, S. 219–228<br />

40 Harmann u. a. 2003 Harmann, Mark ; Binkley, David ;<br />

Danicic, Sebastian: Amorphous Program Slicing. In: Journal of<br />

Systems and <strong>Software</strong> 68 (2003), Nr. 1, S. 45–63<br />

41 Higo u. a. 2002 Higo, Yoshiki ; Ueda, Yasushi ; Kamiya,<br />

Toshihro ; Kusumoto, Shinji ; Inoue, Katsuro: On <strong>Software</strong><br />

Maintenance Process Improvement Based on Code Clone<br />

Analysis. In: International Conference on Product Focused<br />

<strong>Software</strong> Process Improvement Bd. 2559, Springer, 2002,<br />

S. 185–197. – ISBN ISBN:3-540-00234-0<br />

42 Horwitz u. a. 1990 Horwitz, Susan ; Reps, Thomas ;<br />

Binkley, David: Interprocedural Slicing Using Dependence<br />

Graphs. In: ACM Transactions on Programming Languages and<br />

Systems 12 (1990), Januar, Nr. 1, S. 26–60<br />

421 / 437


43 Jackson und Rollins 1994 Jackson, D. ; Rollins, E.: A new<br />

model of program dependences for reverse engineering. In: Proc.<br />

Symposium on the Foundations of <strong>Software</strong> Engineering, 1994<br />

44 Jbara u. a. 2012 Jbara, A. ; Matan, A. ; Feitelson, D. G.:<br />

High-MCC functions in the linux kernel. In: Proceedings of the<br />

International Conference on Program Comprehension, IEEE<br />

Computer Society Press, 2012, S. 83–92<br />

45 Johnson 1993 Johnson, J. H.: Identifying redundancy in<br />

source code using fingerprints. In: Proceedings of the<br />

Conference of the Centre for Advanced Studies on Collaborative<br />

research, IBM Press, 1993, S. 171–183<br />

46 Johnson 1994 Johnson, J. H.: Substring matching for clone<br />

detection and change tracking. In: Proceedings of the<br />

International Conference on <strong>Software</strong> Maintenance, IEEE<br />

Computer Society Press, 1994, S. 120–126<br />

422 / 437<br />

47 Jürgens u. a. 2009 Jürgens, E. ; Deissenböck, F. ;<br />

Hummel, B. ; Wagner, S.: Do Code Clones Matter? In:<br />

Proceedings of the International Conference on <strong>Software</strong><br />

Engineering, ACM Press, 2009, S. 485–495<br />

48 Kamiya u. a. 2002 Kamiya, Toshihiro ; Kusumoto, Shinji ;<br />

Inoue, Katsuro: CCFinder: A Multi-Linguistic Token-based<br />

Code Clone Detection System for Large Scale Source Code. In:<br />

IEEE Computer Society Transactions on <strong>Software</strong> Engineering<br />

28 (2002), Nr. 7, S. 654–670<br />

49 Khomh u. a. 2009 Khomh, Foutse ; Di Penta,<br />

Massimiliano ; Guéhéneuc, Yann-Gaël: An exploratory study<br />

of the impact of code smells on software change-proneness. In:<br />

Proceedings of the Working Conference on Reverse Engineering,<br />

IEEE Computer Society Press, 2009, S. 75–84<br />

50 Khomh u. a. 2012 Khomh, Foutse ; Di Penta, Massimiliano ;<br />

Guéhéneuc, Yann-Gaël: An exploratory study of the impact of<br />

antipatterns on class change- and fault-proneness. In: Journal<br />

Empirical <strong>Software</strong> Engineering 17 (2012), Nr. 3, S. 243–275<br />

423 / 437


51 Komondoor und Horwitz 2001 Komondoor, R. ; Horwitz,<br />

S.: Using slicing to identify duplication in source code. In: Proc.<br />

Int. Symposium on Static Analysis, Juli 2001, S. 40–56<br />

52 Kontogiannis 1997 Kontogiannis, K.: Evaluation<br />

Experiments on the Detection of Programming Patterns Using<br />

<strong>Software</strong> Metrics. In: Proceedings of the Working Conference on<br />

Reverse Engineering, 1997, S. 44–53<br />

53 Koschke u. a. 2006 Koschke, Rainer ; Falke, Raimar ;<br />

Frenzel, Pierre: Clone Detection Using Abstract Syntax Suffix<br />

Trees. In: Proceedings of the Working Conference on Reverse<br />

Engineering, IEEE Computer Society Press, 2006, S. 253–262<br />

54 Koschke u. a. 1998 Koschke, Rainer ; Girard,<br />

Jean-François ; Würthner, Martin: An Intermediate<br />

Representation for Reverse Engineering Analyses. In:<br />

Proceedings of the 5th Proceedings of the Working Conference<br />

on Reverse Engineering. Honolulu, HI, USA : IEEE Computer<br />

Society Press, Oktober 1998, S. 241–250<br />

424 / 437<br />

55 Krinke 2001 Krinke, Jens: Identifying Similar Code with<br />

Program Dependence Graphs. In: Proceedings of the Working<br />

Conference on Reverse Engineering, 2001, S. 301–309<br />

56 Krinke 2004 Krinke, Jens: Slicing, Chopping, and Path<br />

Conditions with Barriers. In: <strong>Software</strong> Quality Journal 12<br />

(2004), Nr. 4, S. 339–360<br />

57 Lanza 2003 Lanza, Michele: Object-Oriented Reverse<br />

Engineering - Coarse-grained, Fine-grained, and Evolutionary<br />

<strong>Software</strong> Visualization. http://www.inf.unisi.ch/faculty/<br />

lanza/Downloads/Lanz03b.pdf, University of Bern,<br />

Dissertation, 2003<br />

58 Lanza und Ducasse 2003 Lanza, Michele ; Ducasse,<br />

Stephane: Polymetric Views—A Lightweight Visual Approach to<br />

Reverse Engineering. In: IEEE Computer Society Transactions<br />

on <strong>Software</strong> Engineering 29 (2003), Nr. 9, S. 782–795<br />

425 / 437


59 Lanza und Marinescu 2006 Lanza, Michele ; Marinescu,<br />

Radu: Object-Oriented Metrics in Practice: Using <strong>Software</strong><br />

Metrics to Characterize, Evaluate, and Improve the Design of<br />

Object-Oriented Systems. Berlin : Springer, August 2006. –<br />

ISBN ISBN-10: 3540244298, ISBN-13: 978-3540244295<br />

60 Lehman 1980 Lehman, Meir M.: Programs, Life Cycles and<br />

Laws of Program Evolution. In: Proceedings of the IEEE,<br />

Special Issue on <strong>Software</strong> Evolution 68 (1980), September,<br />

Nr. 9, S. 1060–1076<br />

61 Lehner 1989 Lehner, Franz: Nutzung und Wartung von<br />

<strong>Software</strong>. Carl Hanser Verlag, 1989<br />

62 Li und Shatnawi 2007 Li, Wei ; Shatnawi, Raed: An<br />

empirical study of the bad smells and class error probability in<br />

the post-release object-oriented system evolution. In: Journal of<br />

Systems and <strong>Software</strong> 80 (2007), Nr. 7, S. 1120 – 1128. –<br />

Dynamic Resource Management in Distributed Real-Time<br />

Systems. – ISSN 0164-1212<br />

426 / 437<br />

63 Li u. a. 2004 Li, Z. ; Lu, S. ; Myagmar, S. ; Zhou, Y.:<br />

CP-Miner: A tool for Finding copy-paste and related bugs in<br />

operating system code. In: Operating System Design and<br />

Implementation, 2004, S. 289–302<br />

64 Li u. a. 2006 Li, Z ; Lu, S ; Myagmar, S. ; Zhou, Y.:<br />

Copy-Paste and Related Bugs in Large-Scale <strong>Software</strong> Code. In:<br />

IEEE Computer Society Transactions on <strong>Software</strong> Engineering<br />

32 (2006), März, Nr. 3, S. 176–192<br />

65 Lientz und Swanson 1980 Lientz, B.P. ; Swanson, E.B.:<br />

<strong>Software</strong> Maintenance Management. Reading, MA :<br />

Addison-Wesley, 1980<br />

66 Mäntylä u. a. 2004 Mäntylä, M.V. ; Vanhanen, J. ;<br />

Lassenius, C.: Bad smells - humans as code critics. In:<br />

Proceedings of the International Conference on <strong>Software</strong><br />

Maintenance, Sept. 2004, S. 399–408. – ISSN 1063-6773<br />

427 / 437


67 Marcus und Maletic 2001 Marcus, A. ; Maletic, J.I.:<br />

Identification of high-level concept clones in source code. In:<br />

Proceedings of the International Conference on Automated<br />

<strong>Software</strong> Engineering, 2001, S. 107–114<br />

68 Masak 2006 Masak, Dieter: Legacysoftware: Das lange Leben<br />

der Altsysteme. Springer, 2006<br />

69 Mayrand u. a. 1996 Mayrand, Jean ; Leblanc, Claude ;<br />

Merlo, Ettore M.: Experiment on the Automatic Detection of<br />

Function Clones in a <strong>Software</strong> System using Metrics. In:<br />

Proceedings of the International Conference on <strong>Software</strong><br />

Maintenance. Washington : IEEE Computer Society Press,<br />

November 4–8 1996, S. 244–254. – ISBN 0-8186-7678-7<br />

70 McCabe 1976 McCabe, Thomas J.: A Complexity Measure.<br />

In: IEEE Computer Society Transactions on <strong>Software</strong><br />

Engineering 2 (1976), Nr. 4, S. 308–320<br />

71 McCreight 1976 McCreight, E. M.: A space-economical<br />

suffix-tree construction algorithm. In: Journal of the ACM 23<br />

(1976), Nr. 2, S. 262–272<br />

428 / 437<br />

72 Mende und Koschke 2009 Mende, Thilo ; Koschke, Rainer:<br />

Revisiting the Evaluation of Defect Prediction Models. In:<br />

PROMISE ’09: Proceedings of the 5th International Conference<br />

on Predictor Models in <strong>Software</strong> Engineering. New York, NY,<br />

USA : ACM, 2009, S. 1–10. – ISBN 978-1-60558-634-2<br />

73 Mende und Koschke 2010 Mende, Thilo ; Koschke, Rainer:<br />

Effort-Aware Defect Prediction Models. In: Proceedings of the<br />

European Conference on <strong>Software</strong> Maintenance and<br />

<strong>Reengineering</strong>, 2010. – submitted for publication<br />

74 Mens und Tourwé 2004 Mens, Tom ; Tourwé, Tom: A<br />

Survey of <strong>Software</strong> Refactoring. In: IEEE Computer Society<br />

Transactions on <strong>Software</strong> Engineering 30 (2004), Februar, Nr. 2,<br />

S. 126–139<br />

75 Moad 1990 Moad, J.: Maintaining the Competitive Edge. In:<br />

DATAMATION, 1990, S. 61–66<br />

429 / 437


76 Monden u. a. 2002 Monden, A. ; Nakae, D. ; Kamiya, T. ;<br />

Sato, S. ; Matsumoto, K.: <strong>Software</strong> quality analysis by code<br />

clones in industrial legacy software. In: Proceedings of the IEEE<br />

Symposium on <strong>Software</strong> Metrics, 2002, S. 87–94<br />

77 Morgan 1998 Morgan, Robert: Building an Optimizing<br />

Compiler. Digital Press, 1998<br />

78 Muchnick 1997 Muchnick, Steven S.: Advanced Compiler<br />

Design and Implementation. Morgan Kaufmann, 1997<br />

79 Muthanna u. a. 2000 Muthanna, S. ; Kontogiannis, K. ;<br />

Ponnambalam, K. ; Stacey, B.: A Maintainability Model for<br />

Industrial <strong>Software</strong> Systems Using Design Level Metrics. In:<br />

Proceedings of the Working Conference on Reverse Engineering,<br />

IEEE Computer Society Press, 2000<br />

80 Müller 1997 Müller, Bernd: <strong>Reengineering</strong> – Eine<br />

Einführung. B.G. Teubner, 1997<br />

430 / 437<br />

81 Olbrich u. a. 2010 Olbrich, S. M. ; Cruzes, D. S. ;<br />

Sjøberg, D. I. K.: Are all Code Smells Harmful? A Study of<br />

God Classes and Brain Classes in the Evolution of Three Open<br />

Source Systems. In: Proceedings of the International Conference<br />

on <strong>Software</strong> Maintenance, IEEE Computer Society Press, 2010,<br />

S. 1–10<br />

82 Olbrich u. a. 2009 Olbrich, Steffen ; Cruzes, Daniela S. ;<br />

Basili, Victor ; Zazworka, Nico: The evolution and impact<br />

of code smells: A case study of two open source systems. In:<br />

Proceedings of the Empirical <strong>Software</strong> Engineering and<br />

Measurement, IEEE Computer Society Press, 2009, S. 390–400<br />

83 Ostrand u. a. 2005 Ostrand, T.J. ; Weyuker, E.J. ; Bell,<br />

R.M.: Predicting the location and number of faults in large<br />

software systems. In: IEEE Computer Society Transactions on<br />

<strong>Software</strong> Engineering 31 (2005), Nr. 4, S. 340–355. – ISSN<br />

0098-5589<br />

431 / 437


84 Ottenstein und Ottenstein 1984 Ottenstein, Karl J. ;<br />

Ottenstein, Linda M.: The program dependence graph in a<br />

software development environment. In: Proceedings of the ACM<br />

SIGSOFT/SIGPLAN <strong>Software</strong> Engineering Symposion on<br />

Practical Sofware Development Environments, 1984, S. 177–184<br />

85 Pigoski 1996 Pigoski, Thomas M.: Practical <strong>Software</strong><br />

Maintenance: Best Practices for Managing Your <strong>Software</strong><br />

Investment. John Wiley & Sons, Inc., 1996<br />

86 Plödereder 2008 Plödereder, Erhard: <strong>Vorlesung</strong><br />

Programmanalysen und Compilerbau. <strong>Vorlesung</strong>sskriptum.<br />

Oktober 2008. – URL<br />

http://www.iste.uni-stuttgart.de/ps/Lehre/WS0809/V_<br />

Programmanalysen/skript-CB+ProgAn-08.pdf<br />

87 Quante und Koschke 2006 Quante, Jochen ; Koschke,<br />

Rainer: Dynamic Object Process Graphs. In: Proceedings of the<br />

Proceedings of the European Conference on <strong>Software</strong><br />

Maintenance and <strong>Reengineering</strong>, IEEE Computer Society Press,<br />

März 2006<br />

432 / 437<br />

88 Rajlich und Bennett 2000 Rajlich, Vaclav T. ; Bennett,<br />

Keith H.: A Staged Model for the <strong>Software</strong> Life Cycle. In: IEEE<br />

Computer 33 (2000), Nr. 7, S. 66–71<br />

89 Ratiu u. a. 2004 Ratiu, Daniel ; Ducasse, Stéphane ;<br />

Gîrba, Tudor ; Marinescu, Radu: Using History Information<br />

to Improve Design Flaws Detection. In: Proceedings of the<br />

European Conference on <strong>Software</strong> Maintenance and<br />

<strong>Reengineering</strong>, IEEE Computer Society Press, 2004, S. 223–232<br />

90 Reps und Rosay 1995 Reps, T. ; Rosay, G.: Precise<br />

Interprocedural Chopping. In: Proc. Symposium on the<br />

Foundations of <strong>Software</strong> Engineering, 1995<br />

91 Richner und Ducasse 2002 Richner, Tamar ; Ducasse,<br />

Stéphane: Using Dynamic Information for the Iterative Recovery<br />

of Collaborations and Roles. In: Proceedings of the Proceedings<br />

of the International Conference on <strong>Software</strong> Maintenance.<br />

Montreal, Canada : IEEE Computer Society Press, Oktober<br />

2002, S. 34–43<br />

433 / 437


92 Rieger 2005 Rieger, Matthias: Effective Clone Detection<br />

Without Language Barriers, University of Bern, Switzerland,<br />

Dissertation, 2005<br />

93 Seacord u. a. 2003 Seacord, Robert C. ; Plakosh, Daniel ;<br />

Lewis, Grace A.: Modernizing Legacy Systems.<br />

Addison-Wesley, 2003<br />

94 Simon u. a. 2006 Simon, Frank ; Seng, Olaf ; Mohnhaupt,<br />

Thomas: Code-Quality-Management – Technische Qualität<br />

industrieller <strong>Software</strong>systeme transparent und vergleichbar<br />

gemacht. dpunkt.verlag, 2006<br />

95 Sneed 1995 Sneed, Harry: Planning the <strong>Reengineering</strong> of<br />

Legacy Systems. In: IEEE <strong>Software</strong> (1995), January. –<br />

beschreibt die Planung von <strong>Reengineering</strong>-Projekten<br />

96 Sneed u. a. 2005 Sneed, Harry M. ; Hasitschka, Martin ;<br />

Teichmann, Maria-Therese: <strong>Software</strong>-Produktmanagement –<br />

Wartung und Weiterentwicklung bestehender<br />

Anwendungssysteme. dpunkt.verlag, 2005<br />

434 / 437<br />

97 Staiger u. a. 2007a Staiger, Stefan ; Vogel, Gunther ;<br />

Keul, Steffen ; Wiebe, Eduard: Interprocedural Static Single<br />

Assignment Form. In: Proceedings of the Working Conference<br />

on Reverse Engineering, IEEE Computer Society Press, Oktober<br />

2007, S. 1–10. – URL http://ieeexplore.ieee.org/xpl/<br />

freeabs_all.jsp?arnumber=4400146<br />

98 Staiger u. a. 2007b Staiger, Stefan ; Vogel, Gunther ;<br />

Keul, Steffen ; Wiebe, Eduard: Interprocedural Static Single<br />

Assignment Form in Bauhaus / Institut für <strong>Software</strong>technologie,<br />

Abteilung Programmiersprachen. Universität Stuttgart,<br />

November 2007. – Forschungsbericht. – URL http:<br />

//elib.uni-stuttgart.de/opus/volltexte/2007/3338/<br />

99 Steinbrückner 2013 Steinbrückner, Frank: Consistent<br />

<strong>Software</strong> Cities Supporting Comprehension of Evolving <strong>Software</strong><br />

Systems, Brandenburgische Technische Universität Cottbus,<br />

Dissertation, 2013<br />

435 / 437


100 Synytskyy u. a. 2003 Synytskyy, Nikita ; Cordy,<br />

James R. ; Dean, Thomas: Resolution of static clones in<br />

dynamic Web pages. In: Proceedings of the Workshop on<br />

Website Evolution, 2003, S. 49–56<br />

101 Ukkonen 1995 Ukkonen, E.: On-line construction of suffix<br />

trees. In: Algorithmica 14 (1995), S. 249–260<br />

102 Wahler u. a. 2004 Wahler, V. ; Seipel, D. ; Gudenberg,<br />

Jürgen W. von ; Fischer, G.: Clone detection in source code<br />

by frequent itemset techniques. In: Proceedings of the IEEE<br />

International Workshop on Source Code Analysis and<br />

Manipulation, 2004, S. 128–135<br />

103 Weiser 1984 Weiser, M.: Program slicing. In: IEEE<br />

Transactions on <strong>Software</strong> Engineering 10 (1984), Nr. 4,<br />

S. 352–357. – In this paper some properties of slices are<br />

presented. It is shown that the use of data-flow analysis is<br />

sufficient to find approximate slices of the generally unsolvable<br />

problem of finding statement-minimal slices<br />

436 / 437<br />

104 Yamashita und Moonen 2013 Yamashita, A. ; Moonen,<br />

L.: Exploring the impact of inter-smell relations on software<br />

maintainability: An empirical study. In: Proceedings of the<br />

International Conference on <strong>Software</strong> Engineering, ACM Press,<br />

2013, S. 682–691<br />

105 Yang 1991 Yang, Wuu: Identifying Syntactic Differences<br />

Between Two Programs. In: <strong>Software</strong>–Practice and Experience<br />

21 (1991), Juli, Nr. 7, S. 739–755<br />

437 / 437

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!