Vorlesung Software-Reengineering ¨Uberblick I - Informatik - FB3 ...
Vorlesung Software-Reengineering ¨Uberblick I - Informatik - FB3 ...
Vorlesung Software-Reengineering ¨Uberblick I - Informatik - FB3 ...
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