Sie sind auf Seite 1von 15

Werkzeuge zur Codeanalyse

Thomas Gängler

Technische Universität Dresden, Fakultät Informatik,


Institut Software- und Multimediatechnik, Lehrstuhl Softwaretechnologie,
01062 Dresden, Deutschland
s9736463@mail.inf.tu-dresden.de

Zusammenfassung. Der Bereich der Software-Qualitätssicherung be-


kommt in letzter Zeit eine immer größere Bedeutung. Nachdem fast
alltäglich Nachrichten von Programmierfehlern in großen Projekten die
Schlagzeilen machen, sind die Hersteller nun gefordert geeignete Lösun-
gen zu entwickeln und einzusetzen um diesen negativen Trend entge-
genzuwirken. Durch die enorm gestiegene Komplexität von modernen
Software-Systemen ist es wirtschaftlich und technisch fast nicht mehr
möglich diese Aufgaben manuell zu erledigen. Werkzeuge zur Codeana-
lyse automatisieren einen großen Teil dieser Arbeit und sollen in die-
sen Aufsatz umfassend vorgestellt, unterschieden und klassifiziert werden
und somit dem Entwickler helfen den richtigen Einstieg in diesen Bereich
zu finden.

1 Einleitung
Software-Qualität und das damit verbundene Code-Quality-Management spie-
len eine große Rolle bei modernen Software-Entwicklern. Dass die Wahrung von
Qualitätskriterien mit wachsender Codezeilenanzahl eines Projektes immer kom-
plexer und schwieriger wird, ist ein einfache logische und allseits bekannte Folge.
Die immer umfangreicheren Entwicklungsumgebungen oder separate Werkzeuge
sollen dem Entwickler dabei einen großen Teil der manuelle Test- und Analy-
searbeit abnehmen. Trotzdem herrscht bei den beteiligten Rollen im Software-
entwicklungsprozess noch oft Unklarheit bei folgenden Fragen. Welche Möglich-
keiten zum Test- und Analysieren gibt es berhaupt? Welches Werkzeug erledigt
welche Aufgabe im Qualitätssicherungszyklus? Deshalb soll dieser Aufsatz einen
Einblick und Überblick in die Vielzahl vorhandener Test- und Analysewerkzeuge
und deren Einsatzgebiete beschrieben. Als Erstes soll eine Typisierung für Werk-
zeuge zur Sicherung von Software-Qualität vorgestellt werden und insbesondere
auf die Kategorien zum Testen und zur Codeanalyse eingegangen werden (s.
Kap. 2). Dabei werden immer wieder Beispielwerkzeuge genannt, wobei der Fo-
kus i.A. auf frei erhältlichen Vertretern liegt und diese oft auch als Plug-in für
die Eclipse Entwicklungsumgebung erhältlich sind.
Danach wird auf die, erweiterten statischen Analysewerkzeuge eingegangen, wel-
che durch kurze Beschreibungen von zwei Beispielen abgerundet wird (s. Kap.
3). Anschließend folgt ein Kapitel über Portale (s. Kap. 4). Diese Form ver-
eint mehrere Werkzeugtypen in auf sinnvolle Art und Weise, und erleichtert die
2

Zusammenarbeit zwischen den verschiedenen Rollen in der Softwareentwicklung.


Letztendlich gibt das Fazit zum Schluss noch einmal ein kurze Zusammenfassung
über die Vorteile und den Nutzen des Einsatzes von Werkzeugen zur Codeana-
lyse.

2 Werkzeugtypen

Die Unterteilung der Werkzeugtypen hält sich grob an die Vorlage von Ligges-
meyer [6]. Dabei wird insbesondere auf die Typen dynamische Testwerkzeuge
(s. Kap. 2.1) und statische Analysewerkzeuge (s. Kap. 2.2) eingegangen. Diese
kooperieren oft miteinander bzw. sind ineinander integriert und bilden gene-
rell die zahlenmäßig stärkste Kategorie. Deshalb werden sie in diesem Aufsatz
besonders analysiert. Des weiteren werden anschließend noch die beiden Werk-
zeugtypen formale Verifikationswerkzeuge (s. Kap. 2.3), und modellierende und
analysierende Werkzeuge (s. Kap. 2.4) vorgestellt, welche zwar Grundlagen für
die erweiterten statischen Analysewerkzeuge (s. Kap. 3) bereitstellen, generell
aber gesondert und ausführlicher betrachtet werden müssen.

2.1 Dynamische Testwerkzeuge

Die Kategorie der dynamischen Testwerkzeuge kann in verschiedene Unterka-


tegorien unterteilt werden, wobei die einzelnen Werkzeugtypen eng miteinan-
der in Verbindung stehen. Generell wird durch Vertreter dieser Kategorie das
Programm während des Tests mit ausgeführt und somit Daten über die Lei-
stungsfähigkeit und Funktionsweise der Anwendung ermittelt. Im Folgenden wer-
den nun die vier Typisierungen der dynamischen Testwerkzeuge erläutert.

Strukturorientierte Testwerkzeuge. Vertreter dieser Unterkategorie stellen dem


Entwickler Informationen über den Stand der Prüfung bereit. Zum Nachweis der
ordungsgemäßen Durchführung von Tests werden diverse Testüberdeckungspro-
tokolle angewandt, d.h. es wird i.A. immer eine vollständige Abdeckung des je-
weiligen Überdeckungstests gefordert1 . Der Bekannteste ist dabei der Zweigüber-
deckungstest, welcher die Ausführung aller Zweige bzw. die Überdeckung aller
Kanten fordert [6]. Das Testwerkzeug visualisiert dann die bereits ausgeführten
Zweige mittels eines automatisch generierten Kontrollflussgraphens. Als Über-
deckungsmaß dient hierbei die Zweigüberdeckungsrate (absolut oder relativ). Ein
weiteres Beispiel ist der Statement-Überdeckungstest, welcher die Ausführung al-
ler Codezeilen eines Programms fordert.
Generell gibt es zwei Arbeitsweisen bei strukturorientierten Testwerkzeugen. Die
Mehrheit arbeitet instrumentierend, d.h. es werden dem Quellcode zusätzliche
Anweisungen hinzugefügt (Annotationen). Dies hat den Vorteil, dass das Test-
werkzeug beim eigentlichen Test nicht mehr aktiv sein muss; gleichzeitig aber
1
Bei komplexen Programmen kann dies zu einem Zeit intensiven und teuren Prozess
werden, wenn alle Testfälle von Hand abgewickelt werden müssen [2]
3

den Nachteil, dass sich durch die Anweisungsanreicherung die Ausführungszeit


verlängert. Durch diese Form kann aber eine Merkmalsauswertung bei der Syn-
taxanalyse mit durchgeführt werden (z.B. Ermittleln der zyklomatischen Zahl ; s.
Kap. 2.2), d.h. statische Analysefunktionalitäten sind mit in diesem dynamischen
Testwerkzeug integriert. Dieser Arbeitsweise gegenüber steht die nicht Instru-
mentierende. Hierbei werden die von der Testausführung genutzten Adressen di-
rekt über eine Hardware-Schnittstelle abgegriffen, was oft mittels eines separaten
Computers erledigt wird. Somit können Überdeckungen auf einem niedrigeren
Niveau registriert werden. Durch Caching-Strategien gelangen aber nicht immer
alle Adressen die sich auf dem Adressbus befinden zur Ausführung, was sich
wiederum nachteilig auf die Analyse auswirken kann.
Die strukturorientierten Testwerkzeuge bieten unterschiedliche Formen der Dar-
stellung für die jeweiligen Testtypen an. Zum einen sind die grafischen Formen
mit den bereits erwähnten Kontrollflussgraphen, welche bei Modultests genutzt
werden, und Strukturdiagramme für Integrationstests. Zum anderen ist die tex-
tuelle Form, welche eine Aufbereitung des Quelltextes wiedergibt. Oft werden
diese Darstellungsformen miteinander kombiniert und es ist z.B. möglich von
einer Stelle im Kontrollflussgraphen zum zugehörigen Quelltext zu gelangen.
Diese Form der Testwerkzeuge ist generell programmiersprachen-abhängig durch
die speziellen programmiersprachlichen Konstrukte der jeweiligen Programmier-
sprache. Es gibt umfassende Unterstützung für C, C++ und Java. Des weite-
ren gibt es jedoch auch Werkzeuge für spezifischere Programmiersprachen wie
Ada, Pascal und Fortran für den naturwissenschaftlichen Bereich, oder Cobol für
den kaufmännischen Bereich (s. Übersicht auf [1]). Ein Beispiel einer Werkzeu-
gumgebung, welche strukturorientierte Tests mit durchführen kann, ist die frei
erhältliche Test and Performance Tools Platform 2 (TPTP) von Eclipse. Dieses
Plug-in kann insbesondere einfache und verteilte Java-Anwendungen analysieren
und testen. Eine weiteres Beispiel ist das kommerzielle Prevent von der Firma
Coverity3 , welches die Programmiersprachen C, C++ und Java unterstützt.

Funktionsorientierte Testwerkzeuge. Damit eine nahezu vollständige Testab-


deckung gewährleistet werden kann, müssen Tests strukturiert geplant und Test-
fälle erzeugt werden. Funktionsorientierte Testwerkzeuge sollen dem Entwickler
helfen Testfälle nach diversen Testtechniken planen zu können und somit den
Komfort zu erhöhen und einen Überblick über die Testplanung bereitzustellen.
Deshalb ist diese Kategorie durch ihre Abstraktion i.d.R. programmiersprachen-
unabhängig. Ein Bespiel für eine Testtechnik ist die funktionale Äquivalenzklas-
senbildung, wobei Testfälle aus der Spezifikation des Programmes durch Äqui-
valenzklassenbildung abgeleitet werden.
Darüber hinaus bieten diese Testwerkzeuge die Möglichkeit zur Definition von
Testdaten bzw. erwarteten Testergebnissen. Diese Beispieldaten können dadurch

2
http://www.eclipse.org/tptp/
3
http://www.coverity.com/
4

immer wieder verwendet werden4 und erleichtern somit die Testautomatisierung


bzw. Wiederverwendbarkeit. Ein Beispieltyp für funktionsorientierte Testwerk-
zeuge sind Zusicherungswerkzeuge, wo formale Aussagennotationen nach einer
festen Syntax in den Quellcode eingefügt werden (engl. assertions). Als Beispiel
für diese Unterkategorie kann wieder das TPTP -Plug-in für die Eclipse Interface
Development Environment (IDE) genannt werden, welches eine Testplanungs-
und erzeugungsansicht besitzt (inklusive Anlegen und Verwalten von Testdaten-
pools). Hierbei wird das bekannte Framework zur Testfallgenerierung für Java-
Anwendungen, JUnit, mit integriert.

Regressionstestwerkzeuge. Nachdem nun mittels funktionsorientierter Testwerk-


zeuge Tests geplant und Testfälle erzeugt, und mittels strukturorientierter Test-
werkzeuge während der Durchführung visualisiert werden können; benötigt der
Entwickler noch einen Werkzeugtyp zur Automatisierung der Testdurchführung.
Regressionstestwerkzeuge ermöglichen das Einspielen von Testdaten und das
Aufzeichnen von Testergebnissen. Somit ist ein Vergleich zwischen unterschiedli-
chen Testdurchläufen gewährleistet und demzufolge eine automatisierte Meldung
von Abweichungen realisierbar.
Generell sollten Regressionstests nach jeder Fehlerkorrektur und Funktionserwei-
terung5 des Programmes durchgeführt werden. Dies ist wiederum nur durch eine
automatische Testdurchführung technisch und wirtschaftlich sinnvoll. Im Allge-
meinen können diese Werkzeugtypen programmiersprachen-unabhängig arbei-
ten. Sie sind aber manchmal programmiersprachen-spezifisch, wie im Fall von
JUnit für Java. Deshalb kann als konkretes Beispiel wieder das TPTP -Plug-
in für die Eclipse IDE genannt werden, was u.a. Komponenten zur Historien-
Analyse und zur Berichterstattung enthält.

Leistungs- und Stresstestwerkzeuge. Oft mit interegriert in Regressionstestwerk-


zeuge sind die Unterkategorien für Leistungs- und Stresstests. Die erste Form
generiert Lasten im Normalbereich an der (vorher festgelegten) Grenze zur Über-
last. Die zweite Art simuliert den Betrieb bei Überlast.
Eine, nach festen Vorgaben definierte, Lastenerzeugung kann i.d.R. nur noch
werkzeugunterstützt vorgenommen werden, da i.A. Lasten für moderne Mehrbe-
nutzer-Anwendungen nicht mehr von Hand erzeugt werden können. Die Lasten-
erzeugung kann z.B. durch Vervielfältigung und Modifikation von Regressions-
test-Mitschnitten erstellt werden. Diese Werkzeugtypen enthalten zusätzlich di-
verse Messfunktionen zum Registrieren von Antwortzeiten und Ressourcenaus-
lastungen. Als Beispiel kommt abermals das TPTP -Plug-in für die Eclipse IDE
in Frage, welches ein Performanz-Testwerkzeug für Webanwendungen enthält.

2.2 Statische Analysewerkzeuge


Im Gegensatz zu den Testfällen wird bei statischen Analysen nur der Quell-
code oder dessen Kompilat analysiert und ausgewertet, d.h. es sind keine kon-
4
Weil sie i.d.R. persistent abgespeichert werden, z.B. in einer relationalen Datenbank
5
Wobei hier ggf. die Testfälle mit erweitert werden müssen
5

kreten Testfällen und Testdaten nötig. Durch den engen Bezug zum Quellcode
sind Werkzeuge dieser Kategorie oft programmiersprachen-spezifisch. Wie schon
beschrieben, bilden die statischen Analysewerkzeuge oft eine Einheit mit den
dynamischen Testwerkzeugen. Analog zu Diesen gibt es hier eine weitere Unter-
kategorisierung, welche nun vorgestellt wird.

Messwerkzeuge. Als erste, sehr verbreitete und recht alte Unterkategorie stehen
die Messwerkzeuge. Sie dienen zur Informationsgewinnung und -darstellung mit-
tels statischer Analyse. Hierbei wird der gesamte Quellcode eines Programms
analysiert und bestimmte Merkmale registriert. Verbreitete Maße hierfür sind:
zyklomatische Zahl, Halstead -Maße, Maße zur Datenkomplexität (z.B. Anzahl
der Klassen) oder Anzahl der Codezeilen (engl. Lines of Code) durch den Nut-
zer. Diese Maße müssen präzise definiert sein, d.h. es darf kein manueller Eingriff
während der Analyse vom Nutzer stattfinden.
Die Ergebnisse lassen sich entweder in textueller (Tabellen, Bäume) oder in gra-
fischer Form (Graphen, Diagramme) darstellen. Die Messwerkzeuge ermöglichen
es dem Entwickler oft Grenzewerte für diverse Maße zu definieren und in der Er-
gebnisdarstellung Grenzüberschreitungen aufzeigen zu lassen. Die abstrakte De-
finition der Maßberechnungen ist zwar i.A. programmiersprachen-unabhängig
aber zur Ermittlung dieser sind die Werkzeuge i.d.R. programmiersprachen-
spezifisch.
Beispiele sind das bekannte Open-Source Metrics-Plug-in für die Eclipse IDE
von Frank Sauer6 und SemmleCode von der Firma Semmle7 , welches ebenfalls
ein frei erhältliches Plug-in für die Eclipse Entwicklungsumgebung ist und u.a.
Metriken berechnen kann.

Stilanalysatoren. Diese Unterkategorie analysiert den Quellcode nach bestimm-


ten vordefinierten oder einstellbaren Verletzungen von Programmierregeln. Dies
sind z.B. Einschränkungen von zu verwendenden programmiersprachlichen Kon-
strukten oder die Definition von zusätzlichen Forderungen um strengere Code-
Konventionen festzulegen. Stilanalysatoren tragen damit gut zur Vereinheitli-
chung von Programmcode in großen Projekten bei. Des weiteren sind sie teilwei-
se Voraussetzung für bestimmte Programmiersprachen in bestimmten Anwen-
dungsgebieten (z.B. sicherheitskritische Anwendungen).
Als ältester Vertreter dieses Werkzeugtyps tritt Lint für die Programmiersprache
C auf, welche von Natur aus eher schwache Stildefinitionen hat. In der Eclipse
IDE ist ein Code Style Dialog schon durch das Java Development Toolkit mit
eingebettet, welcher diverse Funktionen zur Code-Formatierung und Stilanalyse
bereitstellt. Darüber hinaus ist das Open-Source-Plug-in Checkstyle 8 für diese
Entwicklerumgebung erhältlich. Dieses Werkzeug besitzt eine anpassbare Kon-
figuration, welche Standards wie die Sun Code Conventions für Java umsetzen
6
http://metrics.sourceforge.net/; nicht zu verwechseln mit dem gleichnamigen Plug-
in von State Of Flow (http://eclipse-metrics.sourceforge.net/)
7
http://semmle.com/
8
http://checkstyle.sourceforge.net/
6

kann. Zusätzlich kann es noch diverse andere Probleme überprüfen (z.B. Erken-
nung von dupliziertem Code).

Werkzeuge zur Erzeugung von Tabellen und Grafiken. Werkzeuge dieses Typs
stellen im eigentlichen Sinn keine separate Unterkategorie von statischen Analy-
sewerkzeugen dar. Sie sind aber i.A. Bestandteil von vielen Werkzeugumgebun-
gen. Wie schon aus der Bezeichnung abgeleitet werden kann, dient dieser Werk-
zeugtyp zur Erzeugung verschiedenster Ergebnisdarstellungen, z.B. Kontroll-
fluss- und Aufrufgraphen, oder Variablen-Quer-Verweis-Tabellen.
Das Metrics-Plug-in von Frank Sauer kann solche Abhängigkeitsgraphen gene-
rieren oder die Analyseergebnisse in Tabellen mit Bäumen darstellen. Weitere
Beispiele für Werkzeugumbebungen, die eine solche Visualisierung ermöglichen
sind das kommerzielle CodeSonar von Grammatech9 oder SemmleCode, welches
Anfragen als Tabelle, Baum, Graph oder Diagramm ausgeben kann10 .

Slicing-Werkzeuge. Generell dient Slicing zum Vereinfachen von Programmen,


indem nur ein bestimmter semantischer Aspekt betrachtet wird (engl. point of
interest) [4]. Dieses Prinzip blendet einfach nicht relevante Programmteile aus.
Dabei gibt es verschiedene Typen und Formen von Slicing. Die einfachste Form
ist das statische Slicing, wo die irrelevanten Teile einfach bei der Darstellung
gelöscht werden. Darauf aufbauend befindet sich das dynamische Slicing, wo
zuätzlich konkrete Eingabewerte mit ausgewertet werden. Als Vermischung von
statischen und dynamischen Slicing steht der Typ bedingtes Slicing. Hierbei
werden keine konkreten Eingabewerte mehr ausgewertet sondern abstrakt defi-
nierte Bedingungen. Somit wird schnell ein großer Bereich abgedeckt. Als letzter
Typ, ist das formlose Slicing zu nennen, was unabhängig von den anderen For-
men, jede Programmtransformation nutzt um das Programm zu vereinfachen.
Der Programminhalt/ -zweck wird dabei aber beibehalten.
Im Allgemeinen bietet das Slicing vielfältige Anwendungsgebiete, z.B. zum Te-
sten, Fehlersuchen, Umgestaltungen, Verstehen oder Vermessen von Program-
men. Im Speziellen, für den Anwendungsfall der Codeanalyse, unterstützt Sli-
cing den Entwickler bei der Fehlersuche nach Erkennung eines Fehlverhaltens
(engl. debugging). Hierbei werden dann nur die Bereiche betrachtet, die poten-
tiell den Fehler enthalten können, d.h. z.B. Variablen die mit dem Problem in
Zusammenhang stehen.
Die Debug-Perspektive in der Eclipse Entwicklungsumgebung bietet verschiede-
ne Funktionalitäten zum Eingrenzen und Fokussieren von Programmteilen. Des
weiteren kann das kommerzielle Slicing-Werkzeug Wisconsin Program Slicing
System 11 von GrammaTech C-Programme in einer annehmbaren Zeit zerteilen.

Datenflussanomalieanalysatoren. Dieser Werkzeugtyp ist eigentlich selbst be-


schreibend - er dient zum Auffinden von Datenflussanomalien, d.h. fehlerhafte
9
http://www.grammatech.com/index.html
10
Wobei nicht immer jede Ausgabeform für jede Anfrage sinnvoll ist und deshalb der
Ergebnistyp festgelegt werden kann
11
http://www.cs.wisc.edu/wpis/html/
7

Zugriffssequenzen auf eine Variable (z.B. lesender Zugriff auf eine nicht initia-
lisierte Variable). Durch ihre essenzielle Funktionalität sind sie oftmals schon
in Compiler integriert. Die Analyse ist auf statischem Wege mit wenig Aufwand
realisierbar und stets automatisierbar. Dadurch bietet sie sichere und zuverlässli-
che Ergebnisse.
In der Eclipse Entwicklungsumgebung werden solche Fehler teilweise schon ad-
hoc bei der Programmierung ausgewertet und angezeigt (mittels Online-Fehler-
Überprüfung). Generell sind solche Werkzeuge in allen gängigen statischen Ana-
lysewerkzeugen mit enthalten (z.B. die Überprüfung auf Null-Pointer-Derefe-
renzierung).

2.3 Formale Verifikationswerkzeuge

Diese Werkzeugkategorie dient zur Überprüfung der Konsistenz zwischen Spe-


zifikation und Realisierung (d.h. des Programmcodes) mittels mathematischer
Mittel [6]. Sie hat dabei ihre speziellen Anwendungsbereiche größtenteils in der
Automatisierungs- und Steuerungstechnik. Dort muss die eingebettete Software
im sicherheitskritischen Umfeld unbedingt verifiziert werden (z.B. in Geldauto-
maten oder militärischen Software-Entwicklungen).
Dabei gibt es verschiedene Verfahren und Techniken zur formalen Verifikation
von Programmen. Für diesen Aufsatz von Bedeutung sind die automatenbasier-
ten Techniken und das symbolische Testen. Unter den ersten Ansatz fällt das
Symbolic Model Checking, welches eine formale Nachweistechnik für Eigenschaf-
ten zustandsendlich beschriebener Systeme ist. Beim zweiten Ansatz werden
Tests mit allgemeinen symbolischen Werten durchgeführt.
Ein Beispiel Werkzeug für diese Kategorie ist UPPAAL12 , welches von der Upp-
sala Universität in Schweden und der Alborg Universität in Dänemark entwickelt
wurde. Es dient zum Modellieren, Simulieren und Verifizieren von Echtzeitsyste-
men.

2.4 Modellierende und analysierende Werkzeuge

Diese Kategorie von Werkzeugen soll nur kurz erwähnt werden, weil sie nicht dem
Hauptthema des Aufsatzes entspricht13 . Werkzeuge diesen Typs haben ihre An-
wendungsbereiche in der Risiko-, Sicherheits-, Zuverlässigkeits- und Verfügbar-
keitsanalyse. Sie sind generell eher programmiersprachen-unabhängig. Diese Art
beinhaltet effiziente Spezialisierungen bzw. Unterkategorien, z.B. FMECA14 -,
Fehlerbaumanalyse- oder Markov -Werzeuge.
Die Werkzeugumgebungen sind dabei häufig im kommerziellen Bereich angesie-
delt und beinhalten oft mehrere Werkzeugtypen dieser Kategorie. Des weiteren
besitzen die Software-Firmen oft eine langjährige Erfahrung in der Entwicklung.
12
http://www.uppaal.com/
13
für einen detaillierteren Einstieg bitte das zugehörigen Kapitel in [6] lesen
14
engl. Failure, Mode, Effects and Criticality Analysis
8

Abb. 1. Aufbau erweiterter statischer Analysewerkzeuge [8]

Beispielhafte Vertreter sind der Isograph Reliability Workbench 15 und das Relex
Reliability Studio 16 .

3 Erweiterte statische Analysewerkzeuge


Diese Kategorie der statischen Analysewerkzeuge hat sich aus den vorhandenen
Werkzeugumgebungen in den letzten Jahren stark hervorgetan. Sie sind ähn-
lich strukturiert und nutzen i.A. die gleichen grundlegenden Techniken, d.h. es
können eigene Abfragen zur Erkennung von Problemmustern formuliert wer-
den. Dazu soll im folgenden der Aufbau (s. Kap. 3.1) und danach die genutzten
Techniken (s. Kap. 3.2) dieser Werkzeuge erklärt werden. Darüber hinaus wer-
den verwendete Begriffe erläutert (s. Kap. 3.3) und die derzeitigen Grenzen der
erweiterten statischen Analysewerkzeuge aufgezeigt (s. Kap. 3.4). Abschlieend
werden bekannte Vertreter vorgestellt (s. Kap. 3.5).

3.1 Grundstruktur
Der Aufbau dieser Analysewerkzeuge ist grafisch in der Abb. 1 visualisiert. Hier
sind klar die vier Grundkomponenten: Faktenextraktor / Dekorierer, Datenbank-
system (DBS), Analyse- und Reportkomponente zu erkennen. Dabei ist die zwei-
te Komponente nicht zwingend erforderlich, wenn die entstandenen Daten nicht
in einer Datenbank abgespeichert werden sollen. Wie aus der Grafik gut zu
erkennen ist, lässt sich der gesamte Knowledge-Discovery-Prozess leicht auto-
matisieren und z.B. das Werkzeug im Batch-Betrieb als Cron-Job ausführen. Im
Folgenden werden nun die einzelnen Bestandteile der Grundstruktur erklärt.

Faktenextraktor/ Dekorierer. Als Ausgangsdaten nehmen diese Analysewerk-


zeuge, wie bei der statischen Codeanalyse üblich, den Quellcode oder die Kom-
pilation des Programmcodes. Hierbei extrahiert zunächst der Faktenextraktor
15
http://www.isograph-software.com/index.htm
16
http://www.relex.com/
9

ein Systemmodell aus den Daten. Dessen Struktur und Inhalt wird durch ein
Systemmetamodell vorgegeben. Dabei werden Artefakte wie z.B. Verzeichnisse,
Quellcode-Dateien, Pakete, Klassen, Methoden oder Attribute erfasst. Des weite-
ren werden noch die Referenzen zwischen bestimmten Artefakttypen ermittelt.
Das sind Eigenschaften wie z.B. Enthaltenssein- und Vererbungsbeziehungen
oder Methodenaufrufe und Attributbenutzungen.
Diese Artefakttypen und Referenzarten variieren je nach Programmiersprache
(z.B. friend in C++ und implements in Java). Daraus ergibt sich das Problem
der Definition des Systemmetamodells. Entweder es werden alle vorhandenen
Unterschiede in einem universellen bzw. generischen Systemmetamodell verei-
nigt, oder es wird ein separates Modell für jede Programmiersprache erstellt.
Optional hingegen ist die Systenmodellanreicherung durch Dekorierer. Sie be-
stimmen noch nicht ermittelte Merkmale aus dem Quellcode oder Log-Dateien
eines Konfigurations-Management-Systems (z.B. CVS oder Subversion). Bei-
spiele für solche Merkmale sind u.a. Änderungshäufigkeit oder Angaben über
die Vollständigkeit der Javadoc- oder Doxygen-Kommentare. Zusätzlich können
mit Dekorierern Vorberechnungen für wiederholt benötigte Daten für die Ana-
lysephase vorgenommen werden (z.B. Berechnung der transitiven Hülle der Ver-
erbungsbeziehungen). Generell führen diese Vorberechnungen zu einem späteren
Performance-Gewinn.
Die eigentliche Datenmenge wird durch den Faktenextraktor und Dekorierer re-
duziert und es ist damit keine vollständige Rückwärtsgenerierung möglich.

Datenbanksystem. Das nun extrahierte Systemmodell des Programmes wird


häufig in einer relationalen Datenbank abgespeichert. Dies hat mehrere Vor-
teile. Als Erstes können DBSe i.A. gut mit großen Datenmengen umgehen. Als
Zweites können damit Relationen effizient berechnet werden. Als Drittes werden
die Daten persistent abgespeichert und es kann immer wieder auf sie zugegriffen
werden.
Die Werkzeuge besitzen teilweise ein internes DBS. Diese sind i.A. nicht so lei-
stungsstark und effizient wie externe DBSe, welche groe Datenmengen schnell
verarbeiten können. Darüber hinaus besteht dann aber oft die Möglichkeit mit
externen DBSen zu kooperieren (z.B. PostgreSQL oder Oracle).

Analysekomponente. Nachdem nun die Daten persistent in einer Datenbank ab-


gespeichert wurden, können nun vordefinierte oder neu erstellte Anfragen auf
das Systemmodell ausgeübt werden. Die Analysekomponente ist durch Nutzung
einer Datenbank zeitlich unabhängig von der Faktenextraktion und der Deko-
rierung17 . Zusätzlich ermöglicht dies eine Wiederholung oder Modifikation der
Analyse auf einfachen Wege.
Eine Qualitätsanalyse setzt sich generell aus vielen Teilanalysen zusammen. Die-
se Anfragen werden durch eine Anfragesprache, wie z.B. SQL, oder durch werk-
zeugspezifische Erweiterungen beschränkt. Der dabei wichtigste Faktor ist die
17
Werkzeuge die diese Anforderung nicht erfüllen verarbeiten die anfallenden Daten
sofort weiter
10

Laufzeit der Berechnungen, welche durch Wahl geeigneter Hardware, Wahl eines
geeigneten DBSs und Anfrageoptimierung verbessert werden kann.

Reportkomponente. Die Ergebnisse der Analysekomponente werden i.A. in Ta-


bellen und Diagrammen dargestellt. Des weiteren kann die Reportkomponente
die Resultate aber als Berichte aufarbeiten und somit in verschiedenen Abstrak-
tionsstufen wiedergeben (z.B. für das Management oder den Entwickler).

3.2 Technik

Die erweiterte statische Analyse hat ihre Wurzeln, in den schon erwähnten,
Model-Checking-Technologien und in der abstrakten Interpretation [2]. Sie nutzt
symbolischen Eingaben, d.h. abstrakte Werte, um viele konkrete Werte gleichzei-
tig abzudecken und somit effizient zu arbeiten. Folglich arbeiten diese Werkzeu-
ge im Kontrast zu dynamischen Testwerkzeugen, wo konkrete Werte verwendet
werden. Dies hat den Vorteil, das i.A. einen höhere Abdeckung erzielt wird ohne
spezielle Testfallgenerierungen. Dies geschieht durch die abstrakte Betrachtung
und Auswertung der Pfade und Bedingungen.

3.3 Fachterminologie

Die Technologie der erweiterten statischen Analyse führte zur Bildung einer
Reihe von neuen Fachbegriffen. Die Grundlage bilden die Fehlermuster, wel-
che Problemmuster genannt werden. Dies ist die spezielle Beschreibung eines
wiederkehrenden und i.d.R. erkennbaren Fehlverhaltens von Programmcode. Sie
entstehen z.B. durch die unterschiedlichen Eigenschaften der Programmierspra-
chen, falsch verstandene Schnittstellen-Methoden oder einfache kleine Versehen
(z.B. Nutzung des falschen Boolean-Operators). Diese Fehlermuster werden dann
nach ihrer Art in Fehlerklassen zusammengefasst. Dabei gibt es verschiedene
Ansätze und Kategorisierungen. Beispiele für Fehlerklassen sind unvorhersehba-
re/ kritische Fehler (z.B. Pufferüberlauf/-unterlauf), Speicher-Allokations-Fehler
(z.B. doppeltes Freigeben) oder Konkurrenz-Fehler (z.B. doppeltes Sperren von
kritischen Abschnitten). Manche Kategorien beziehen sich nur auf bestimmte
Programmiersprachen. Demzufolge spielen Speicher-Allokierungs-Fehler in Java,
welches eine automatische Speicherverwaltung besitzt, keine Rolle. Letztendlich
werden die Problemmuster in Qualitätsindikatorenkataloge [8] oder Fehlermu-
sterkatalogen zusammengefasst. Diese variieren dann von Werkzeug zu Werk-
zeug und können teilweise noch selbst von den Entwicklern definiert werden.
Ein wichtiger Vergleichswert zwischen der Leistungsfähigkeit der verschiedenen
Werkzeuge ist die sogenannte False-Positive-Rate [9]. Diese fasst die Warnun-
gen zusammen, welche gar keine richtigen Fehler sind. Dem gegenüber steht
die False-Negative-Rate, welche die Anzahl der nicht gefunden Fehler widerspie-
gelt. Generell sind die Entwickler von erweiterten statischen Analysewerkzeugen
bestrebt ein geringe False-Positive-Rate bei ihren Analysen zu erzielen. Diese
variiert aber von Fehlermuster zu Fehlermuster.
11

3.4 Probleme und Grenzen

1 void f 0 ( ) { i f ( ∗ ) { A; } e l s e { B ; } } // 2 Pfade
2 void f 1 ( ) { f 0 ( ) ; f 0 ( ) ; } // 4 Pfade
3 void f 2 ( ) { f 1 ( ) ; f 1 ( ) ; } // 16 Pfade
4 ...
5 void f i ( ) { f i − 1 ( ) ; f i − 1 ( ) ; } // 2ˆ(2ˆ i ) Pfade
Listing 1.1. Beispiel für die Entwicklung der Pfadanzahl [9]

Nach dem derzeitigen Stand der Technik können erweiterte statische Analyse-
werkzeuge trotzdem keine 100%-ige Pfadabdeckung18 ermöglichen [9]. Dies liegt
in der exponentiellen Pfadanzahlerhöhung bei Schleifen und Rekursionen.
Wie man im Listing 1.1 sieht, steigt die Anzahl der Pfade recht schnell an. In der
ersten Methode befindet sich eine Bedingung woraus sich zwei Pfade ergeben.
Danach werden in den nachfolgenden Methoden die Vorherigen jeweils zweimal
aufgerufen. Somit ergibt sich durch die azyklischen, interprozeduralen Metho-
denaufrufe ein doppelt exponentieller Anstieg der Anzahl der Pfade.
Des weiteren gewährleisten die Zeiger-Analyse-Algorithmen noch keine exak-
te Analyse [9]. Die Folge sind falsche Verweise (nicht durchführbare Verweise)
die in den Aufrufgraphen auftauchen oder fehlende Verweise, welche z.B. durch
redundante Bedingungen entstehen. Generell ignorieren die Werkzeuge solche
Ausnahmen die zu unvorhersehbaren Ausführungspfaden führen. Auf der ande-
ren Seite fordern aber diverse Sicherheitsstandards (z.B. DO-178B Standard für
die Luftfahrt) 100%-ige Abdeckung in verschiedenen Risikoklassen.
Eine andere Grenze der erweiterten statischen Codeanalyse ist die eingeschränkte
Modellierung von konkurrienden Thread -Zugriffen (engl. mutlithreading). Hier-
bei werden oft nur Annäherungen vorgenommen.
Dem allgemeinen Problem, der Reduzierung der False-Positive-Rate, wird ver-
sucht sich mit verschiedenen Techniken anzunähern. Auf der einen Seite werden
die Werkzeuge mit automatischen Lernfunktionen ausgestattet. Diese versuchen
gebräuchlichen Programmierausdrücke/ -muster und deren Absichten zu verste-
hen. Auf der anderen Seite kann der Nutzer selbst bestimmte Verhaltensweisen
definieren um somit die False-Positive-Rate zu verringern.
Letztendlich können diese Werkzeugtypen aber keine logischen Fehler im Pro-
grammablauf erkennen. Diese lassen sich aber gut durch dymamische Tests her-
ausfinden.
3.5 Beispiele
Hierbei sollen nun kurz zwei frei erhältliche Beispielwerkzeuge für die erweiter-
te statische Analyse betrachtet werden und weitere Anwendungen nur genannt
werden.

FindBugs. Dies ist ein Open-Source-Projekt von der Universität von Maryland
für die Programmiersprache Java und als Eclipse Plug-in erhältlich19 . Es analy-
18
Obwohl es andere Hersteller behaupten; s. [7]
19
http://findbugs.sourceforge.net/
12

siert mittels Detektoren, die auf den Visitor -Entwurfsmuster beruhen, den Java-
Bytecode. Dabei behaupten die Entwickler immer eine False-Positive-Rate ge-
ringer als 50% zu erzielen [5]. Durch seine Struktur ist es beliebig mit neuen
Fehler-Detektoren erweiterbar. Bei der Analyse werden die anfallenden Daten
direkt weiter verarbeitet und ausgewertet.
Mittlerweile besitzt das Werkzeug schon einen ziemlich umfangreichen Fehlermu-
sterkatalog der sich in verschiedene Kategorien unterteilt. Des weiteren ermög-
licht es durch diverse Filter die Analyse einzuschränken und diese z.B. nur mit
gewissen Detektoren durchzuführen oder nur auf bestimmte Klassen anzuwen-
den. Letztendlich kann man die Ergebnisse als XML-Report exportieren und
immer wieder in das Programm zur Fehlerauswertung und -beseitigung laden.

SemmleCode. Das schon bereits erwähnte Werkzeug SemmleCode ist ein ziemlich
umfangreiches und flexibles Analysewerkzeug. Es ist als frei erhältiches Eclipse
Plug-in verfügbar und basiert auf der objekt-orientierten, Allzweck-Anfragespra-
che .QL, welche mehrere Ansätze in sich vereint. Somit hat sie eine starke Ähn-
lichkeit zu SQL, nutzt die Fixpunkt-Semantik von Datalog 20 , und gebraucht
die Eindhoven Quantifier Notation zur simplen Konstruktion von Aggregat-
Funktionen21 .
Durch diese erweiterte Anfragesprache ist es möglich eine Vielzahl der Aufgaben
der statischen Analyse abzudecken und mit einer guten Performance zu berech-
nen und ausgeben zu lassen, z.B. Fehler finden, Metriken berechnen, Abhängig-
keiten zu verstehen oder Anfragen anzupassen und neu zu definieren. Die anfal-
lenden Daten der Analyse werden in einer relationalen Datenbank abgespeichert.

Weitere Beispiele. Andere Vertreter erweiterte statische Analysewerkzeugen die


ein DBS nutzen sind die kommerzielle CAST Application Intelligence Platform 22
und das schon erwähnte CodeSonar. Darüber hinaus erzielt Prevent sehr gute
Ergebnisse und TPTP besitzt eine Komponente zur erweiterten statischen Ana-
lyse.

4 Portale

Wie der aufmerksame Leser vielleicht schon während des Lesen dieses Aufsatzes
festgestellt hat, reicht ein einzelnes Werkzeug heutzutage nicht mehr aus um alle
Bereiche der Codeanalyse zur Qualitätssicherung abzudecken. Deshalb werden
die einzelnen Werkzeugtypen oft in einer Werkzeugumgebung zusammengeführt
(z.B. TPTP ) oder ein Werkzeug ist so flexibel um mehrere Kategorien gleich-
zeitig abzudecken (z.B. SemmleCode).
Darauf aufbauend erstreckt sich das Gebiet der Portale zur Codeanalyse, welche
eine zentralisierte, komplexe Datenerhebung systemweit ermöglichen und diese
20
Welches eine einfache Form von logischer Programmierung ist
21
Für eine detailierte Beschreibung von .QL bitte [3] lesen
22
http://www.castsoftware.com/Default.aspx
13

Abb. 2. Rollenstruktur in der Software-Entwicklung [8]

natürlich umfangreich analysieren können um daraus variable Berichte zu gene-


rieren. Die verschiedenen Rollen in der Software-Entwicklung (Entwickler, Pro-
jektleiter und Manager) haben unterschiedliche Sichten auf die zu entwickelnden
Systeme (s. Abb. 2). Durch die zentralisierte Speicherung der Daten ist es nun
möglich Reporte für jeden dieser Bereiche zu erstellen und von der Granularität
anzupassen, d.h. Analysen auf Codeebene für den Entwickler23 , Analysen auf
Architekturebene für den Projektleiter und eine mehrere Systeme übergreifende
Sicht für den Manager (engl. Application Portfolio Management). Des weiteren
können dadurch zusätzliche Metriken ermittelt werden.
Oft werden Portale über Webserver verwaltet. Dies ermöglicht es den beteilig-
ten Personen mehrfach und ortsunabhängig auf die Berichte zuzugreifen und
eine Personalisierung der Daten vorzunehmen (damit ist i.A. die Explorations-
tiefe der Berichte gemeint). Dies hat den Vorteil einer einheitlichen Datenba-
sis, welche konsistente Sichten der Ergebnisse und somit eine Vergleichbarkeit
zulässt (z.B. Trendbeobachtungen auf Tages- oder Versionsbasis). Um Daten der
zentralen Datenbank aktuell zu halten ist ein hoher Automatisierungsgrad er-
forderlich, welcher einen relativ großen Aktualisierungsintervall zur Folge hat.
Die Vernachlässigung der lokalen Gültigkeit der Analyse steht dabei deutlich im
Kontrast zu den Vorteilen die ein Unternehmen aus einer Portal -Nutzung ziehen
kann. Diese sind Maximierung des Nutzen des ermittelten Daten, Minimierung
des Ressourcenbedarfts für die Analyse und geringe Wartungskosten.
Beispielhaft zeigt Abb. 3 den Aufbau von CodeSonar, welches eine zentralen
Hub hat. Dieser stellt den Zugang über ein Web-Interface zu den automatisch
generierten Berichten bereit und ermöglicht es den beteiligten Entwicklern ihre
Daten zur Analyse von Hand oder automatisch einzuspeisen. Weitere Beispiele

23
Welche auch nur auf die zu bearbeitenden Module beschränkt werden können
14

Abb. 3. Aufbau der Portal -Struktur von CodeSonar [9]

für Portale sind das schon erwähnte Prevent 24 , Insight von Klocwork25 und die
Client/ Server-Werkzeuge von PolySpace26 .

5 Fazit
Nachdem nun eine Vielzahl von Werkzeugtypen, deren Einsatzgebiete mit Bei-
spielen vorgestellt wurde, sollte klar sein, dass die Nutzung solcher Hilfsmittel
in einem guten Software-Entwicklungs-Prozess unerlässlich aber auch teilwei-
se recht einfach ist. Die modernen Werkzeugumgebungen zum Testen und zur
Codeanalyse ergänzen oft die komplexen Entwicklungsumgebungen oder binden
sich gut in diese als Plug-in ein. Die Vertreter aus dem Open-Source-Bereich
brauchen sich nicht vor den kommerziellen Konkurrenten zu verstecken, da die
kostenlosen Projekte oft ein breites Industriekonsortium hinter sich haben (s.
TPTP ).
Eine logische Folge, ist auch die Vereinigung mehrere Kategorien in einer Werk-
zeugumgebung. Generell bilden die statischen Typen eine gute Ergänzung zu
den dynamischen Testwerkzeugen, ersetzen diese aber nicht. Sie helfen dem Ent-
wickler trotzdem effizient echte Fehler zu finden und somit die Software-Qualität
zu steigern.
Durch die breite Automatisierung reduzieren statische Analysewerkzeuge die Ko-
sten im Entwicklungsprozess und sparen natürlich eine Menge Zeit ein. Mittels
24
Welches sehr gut mit dem ThreadAnalyzer von Coverity zusammenarbeitet und auch
als Eclipse Plug-in in der Java-Version erhältlich ist
25
http://www.klocwork.com/default.asp
26
http://www.mathworks.com/
15

variabler Berichterstellung optimieren Portale zusätzlich die Wege zur Zusam-


menarbeit und den Kommunikationsfluss zwischen den beteiligten Rollen in der
Software-Entwicklung.
Deshalb sollte gerade die recht leicht zu erlernenden, frei erhältlichen Vertreter
zum Einsatz in allen (möglichen) Software-Projekt kommen, wobei diese sich
oft nur auf gängigen Programmiersprachen wie C, C++ und Java beschränken
damit aber eine große Anzahl an Systemen abdecken.

Literatur
1. Liggesmeyer Home [online]. Available from: http://www.liggesmeyer.de.
2. P. Anderson. Detecting Bugs in Saftey-Critical Code. Dr. Dobb’s Journal, March,
2008.
3. O. de Moor, M. Verbaere, E. Hajiyev, P. Avgustinov, T. Ekman, N. Ongkingco,
D. Sereni, and J. Tibble. Keynote Address: .QL for Source Code Analysis. Technical
report, Semmle Limited, 2007.
4. M. Harman and R. M. Hierons. An Overview of Program Slicing [online]. Available
from: http://www.dcs.kcl.ac.uk/staff/mark/sf.html.
5. D. Hovemeyer and W. Pugh. Finding Bugs is Easy. Technical report, Dept. of
Computer Science, University of Maryland, 2004.
6. P. Liggesmeyer. Software-Qualität: Testen, Analysieren und Verifizieren von Soft-
ware. Spektrum, Akademischer Verlag, 2002.
7. Misc. Prevent SQS C/C++. Technical report, Coverity, Inc., 2008.
8. F. Simon, O. Seng, and T. Mohaupt. Code-Quality-Management. dpunkt.verlag,
2006.
9. M. Zarins. Overview of GrammaTech Static-Analysis Technology. Technical report,
GrammaTech, Inc., 2008.