3 Statische Verfahren
Der Begriff Software umfasst vieles. So gehören sowohl die Design- als auch die Programmdokumentation dazu und natürlich auch der Programmcode. Nur dieser wird im Folgenden betrachtet. Statische Verfahren analysieren den Programmcode, ohne ihn auszuführen – daher der Name. Zu den statischen Verfahren gehören sowohl Reviews zur Bewertung des Programmcodes und zur Aufdeckung von Fehlern als auch die automatisierte Analyse mit Werkzeugen. Es gibt verschiedene Arten von Reviews, auf die unten kurz eingegangen wird. Allen Reviews ist gemeinsam, dass sie Arbeitszeit kosten, nicht nur die des Autors, sondern auch die von Kollegen, die als Gutachter tätig werden. Uns geht es darum, die Arbeitszeit aller Beteiligten zu reduzieren, um mit demselben Aufwand bessere Qualität zu erzielen. Deshalb liegt der Schwerpunkt dieses Kapitels nicht auf Reviews, die nur der Vollständigkeit halber erwähnt werden, sondern auf Verfahren zur statischen Analyse, die automatisiert ablaufen und daher wenig Arbeitszeit kosten. Reviews werden dadurch nicht überflüssig, aber weniger aufwendig, weil ein Teil der Fehler oder Schwächen schon vorher durch die statische Analyse aufgedeckt und anschließend korrigiert werden kann. Je früher ein Fehler gefunden wird, desto leichter (und billiger) ist seine Korrektur.
Bevor es mit dem Testen unserer Software – also der Ausführung des Testobjekts mit Testdaten auf dem Rechner – losgeht, ist es sinnvoll, vorher so viele Fehler wie möglich zu entdecken, um den Aufwand für das Testen gering (»lean«) zu halten. Daher werden hier die statischen Analyseverfahren ausführlich vorgestellt.
Statische Verfahren werden typischerweise von Entwicklern eingesetzt. Diese Verfahren können natürlich nicht alle Fehler finden, insbesondere nicht diejenigen, die von externen Daten herrühren, die erst zur Laufzeit vom Programm eingelesen werden. Dafür gibt es die dynamischen Verfahren1, insbesondere die Unit Tests auf der Ebene des Entwicklers. Umgekehrt können Unit Tests verschiedene statische Eigenschaften nicht prüfen, wie etwa die Einhaltung von Programmierrichtlinien. Statische Verfahren sind besonders geeignet zur Prüfung der folgenden Elemente:
Syntax (Grammatik) des Programms. Bei einer nicht der Spezifikation der Programmiersprache entsprechenden Syntax gibt schon der Compiler eine Fehlermeldung aus. Voraussetzung ist natürlich, dass der Compiler die Sprachspezifikation richtig implementiert.
Einhaltung anerkannter Prinzipien zur Softwareentwicklung bzw. Programmierung. So kann ein Verfahren das (im Allgemeinen unerwünschte) Vorhandensein globaler Variablen entdecken oder unbeabsichtigte fehlerhafte Typumwandlungen.
Einhaltung der Programmierrichtlinien (sofern es welche gibt). Diese schreiben zum Beispiel vor, wie Namen von Klassen und Objekten zu bilden sind, wie Kommentare gestaltet werden sollen und ob Exceptions verwendet werden dürfen.
Kontrollfluss. So können nicht erreichbarer Code oder fehlerhafte Ablaufstrukturen entdeckt werden.
Datenfluss. Dabei wird zum Beispiel geprüft, ob eine Variable vor der Verwendung mit einem Wert initialisiert wurde oder ob ein zugewiesener Wert überhaupt verwendet wird. Wenn nicht, muss das kein Fehler sein, deutet aber daraufhin, dass der Programmcode nicht der eigentlichen Absicht entspricht.
Konkrete Beispiele finden Sie weiter unten.
Statische Verfahren können nicht nur auf den Quellcode angewendet werden, sondern auch auf Bytecode (wie etwa das Werkzeug FindBugs2 für die Programmiersprache Java) oder auf binäre ausführbare Programme. Wir beschränken uns hier auf den Quellcode.
Beschränkungen in der Praxis
Ein Werkzeug ist ein Werkzeug, nicht mehr und nicht weniger. Insbesondere kann es nicht die Gedanken des Programmierers lesen und daraufhin die korrekte Umsetzung in Programmcode überprüfen. Das bedeutet, dass die Entwickler eines Werkzeugs bestimmte Vorstellungen haben, wie Anweisungen und Programmstrukturen aussehen sollen, und diese Annahmen im Werkzeug implementieren. Diese Annahmen können sich an manchen Stellen als falsch erweisen. Aus diesem Grund kann es sein, dass ein Werkzeug
einen »Fehler« meldet, der tatsächlich keiner ist (falsch positive Meldung), oder
einen vorhandenen Fehler nicht meldet (falsch negative Meldung).
Ein besonders gründliches Werkzeug erzeugt möglicherweise eine Menge falsch positiver Meldungen, sodass die Gefahr besteht, das wichtige Meldungen in der schieren Menge untergehen. Die Entwickler solcher Werkzeuge bemühen sich, die Anzahl der falsch positiven Ergebnisse zu reduzieren.
Praktischer Einsatz
Um die Menge an Fehlermeldungen und Warnungen zu reduzieren und die Anzahl der Tests zu beschränken, empfiehlt sich die nachstehende Reihenfolge:
Das Testobjekt (Programmcode) compilieren und ggf. korrigieren, bis die Compilation fehlerlos durchläuft. Dabei die höchste Warnstufe einschalten und die Warnungen des Compilers berücksichtigen.
Statische Verfahren einsetzen und alle gefundenen Fehler korrigieren. Die Anzahl der jetzt noch notwendigen Tests wird durch jeden in dieser Phase gefundenen Fehler reduziert.
Erst dann die Unit Tests durchführen.
3.1 Codereview
Review ist der Oberbegriff für verschiedene statische Prüfverfahren, die von Personen durchgeführt werden. Das Prüfobjekt kann eine Designdokumentation sein, ein zu erstellendes Produkt oder ein Teil davon oder auch der Ablauf eines Prozesses. Ein Autor ist oft »betriebsblind« und sieht bestimmte Dinge nicht mehr, deshalb ist es wichtig, dass er den Code einem Kollegen zeigt. Ein großer Vorteil eines Reviews: Andere Personen sehen das Prüfobjekt unter einem ganz anderen Blickwinkel. In diesem Abschnitt geht es aber nur um Codereviews, die hauptsächlich in zwei Arten vorkommen:
Walkthrough
Das Vorgehen ist für kleine Teams von bis zu fünf Personen geeignet und verursacht relativ wenig Aufwand. Dabei stellt der Programmautor den Code einigen Experten vor, zum Beispiel fachlich versierten Kollegen – möglichst aus anderen Projekten – oder Testern. Mit ihnen zusammen werden verschiedene Benutzungsabläufe durchgespielt. Ziel ist das gegenseitige Lernen und Verständnis über das Prüfobjekt und natürlich, Fehler zu finden.
Inspektion
Eine Inspektion folgt einem formalisierten Ablauf, in dem es verschiedene Rollen gibt, wie etwa einen Moderator und verschiedenen Gutachter (ebenfalls fachlich versierte Kollegen). Einer der Gutachter trägt den Inhalt des Prüfobjekts vor, wobei die anderen entsprechend Fragen stellen. Die Gutachter bereiten sich auf die Sitzung vor, was beim Walkthrough entfallen kann. Während der Inspektion werden auch Daten gesammelt, die zur Qualitätsbeurteilung des Entwicklungs- und Inspektionsprozesses herangezogen werden. Ziel der Inspektion ist das Finden von Fehlern und ggf. deren Ursachen.
Codereviews tragen sehr zur Qualität einer Software bei. Deswegen werden sie hier als statisches Verfahren erwähnt, auch wenn sie nicht weiter behandelt werden.
Ausführliche Beschreibungen zu den unterschiedlichen Arten der Reviews sind in [Spillner & Linz 12, Kap. 4] und in [Rösler et al. 13] zu finden.
3.2 Compiler
Ein Compiler muss zwangsläufig ein Programm analysieren, wenn er lauffähigen Code daraus herstellen soll. Typischerweise baut er einen abstrakten Syntaxbaum auf, abgekürzt AST (abstract syntax tree) genannt. Im Prinzip hat ein Compiler alle im Programmcode liegenden Informationen zur Verfügung, sodass er einige Fehler leicht entdecken kann, wie etwa die fehlende Deklaration einer Variablen oder den Aufruf einer Funktion mit einem falschen Parametertyp.
Was ein Compiler aber nicht oder nur zum Teil kennt, sind Empfehlungen für einen defensiven Programmierstil oder die gewünschten Programmierrichtlinien.
Es gibt eine Menge verschiedener Compiler, sowohl Open Source als auch kommerziell. Die hier und im Folgenden beschriebene Problematik ist bei allen Compilern strukturell ähnlich, weswegen wir uns auf die Open-Source-Compiler GNU C++ (G++) und Clang (Aufruf clang++) beschränken.
G++
G++ gibt es für Mac OS und Linux [URL: gcc]. Die Distribution für Mac OS hinkt bezüglich der Versionsnummern gegenüber Linux etwas hinterher. Unsere Empfehlung: Auf dem Mac lieber Clang verwenden (siehe unten). In beiden Fällen wird die mehrere Gigabytes umfassende Entwicklungsumgebung Xcode benötigt.
G++ gibt es auch für Windows, wobei die MinGW- und die Cygwin-Distribution am weitesten verbreitet sind. Aber auch hier hinken die Versionsnummern denen für Linux hinterher.
Clang
Clang [URL: clang] ist ein Frontend für die C-Sprachfamilie (C/C++, Objective C/C++), das für LLVM3, eine modulare Compilerarchitektur, entwickelt wurde. Clang/LLVM ist Open Source. LLVM ist aus einem Projekt der Compilerforschung entstanden. Clang/LLVM ist besser als G++ für die statische Analyse eines...