Wie man Software durch Prüfung von Invarianten zuverlässiger macht

25. August 2009, 10:37 Uhr | Stephan Grünfelder
Diesen Artikel anhören

Fortsetzung des Artikels von Teil 1

Testbarkeit und Robustheit

Oft soll Software für eingebettete Systeme „robust“ sein. Das heißt, dass etwa ein kleiner Datenfehler das Programm weder zum Absturz bringen noch zu einer fehlerhaften Berechnung führen soll, sondern stattdessen die Software mit plausiblen Werten weiterarbeiten soll. Ein Beispiel ist in zu sehen. Ein unerkannter Kommandotyp führt zu der dafür am plausibelsten erscheinenden Reaktion: einem Neustart, genauso, als ob das Kommando „Restart“ empfangen worden wäre.

passend zum Thema

Robustheit steht aber im Widerspruch zur zuvor definierten Testbarkeit. Ohne Untersuchung der in Listing 1 verwendeten Zwischenergebnisse lässt sich innerhalb des Software-Systems nicht feststellen, ob die Software soeben einen potentiellen Systemfehler maskiert hat, weil Daten für ein nicht definiertes Kommando empfangen wurden, oder ob die Software ein reguläres Kommando „Restart“ empfangen hat. Die Reaktion ist in beiden Fällen dieselbe. Im Feld ist das vielleicht so gewünscht. Während der Testphase wäre es aber doch gut zu wissen, ob es undefinierte Kommandos gab!

Wenn man während der Testphase Testbarkeit und während des Feldeinsatzes Robustheit wünscht, dann muss man sich offenbar etwas einfallen lassen. Eine Lösung ist, den default-Zweig der switch-Anweisung genauso allgemein beizubehalten, wie ihn die robuste Implementierung in Listing 1 zeigt, jedoch dort während der Testphase sicherzustellen, dass auch der erwartete Wert Null für den Kommandotyp „Restart“ empfangen wurde. Ist der Wert nicht Null, so wird das Programm angehalten.

So eine bedingte Überprüfung liefert das Makro assert(). Dieses Makro wird vom Compiler nur dann übersetzt, wenn auch Debug-Information im Code gewünscht ist, also typischerweise nur während der Entwicklungsphase, nicht aber im endgültigen Software-Produkt. Ein schwerer Fehler ist daher, wenn man im Argument des Makros eine Zuweisung macht und die Prüfung des Zuweisungsergebnisses dem Makro überlässt. So könnte folgende Code-Zeile beim Debuggen des Programms richtige Ergebnisse liefern, obwohl das Programm im Feld falsche Werte liefert, weil für das Produktivsystem ohne Debug-Information übersetzt wurde:

assert(x = y + 42);
/* fatale Zusicherung */

Listing 2 zeigt eine mögliche Implementierung von assert(). Es ist zu sehen, dass nur Code erzeugt wird, wenn der Code mit Debug-Information übersetzt wird.

In Listing 3 ist gezeigt, wie man diese bedingte Zusicherung einsetzen kann, um den default-Zweig der switch-Anweisung in der Produktiv-Software genauso robust zu halten, wie in Listing 1. Diesmal werden unerkannte Kommandos beim Test sofort erkannt, wenn mit Debug-Information übersetzt wird. Durch die Zusicherung wurde die Testbarkeit dieses Software-Moduls erhöht, ohne ihre Robustheit im Produktivsystem zu unterlaufen.

Robustheit und Zuverlässigkeit

Bei Programmen mit hohen Anforderungen an die Integrität der Software ist manchmal die Zuverlässigkeit des Ergebnisses viel wichtiger als die Verfügbarkeit oder die Robustheit des Systems. Man denke etwa an ein System mit Sicherheitsrelevanz und einem sicheren Abschaltzustand. Besser ist es, das System auszuschalten und niemanden zu gefährden, als es beim Auftreten von Fehlern operativ zu halten.


  1. Wie man Software durch Prüfung von Invarianten zuverlässiger macht
  2. Testbarkeit und Robustheit
  3. Wie man Software durch Prüfung von Invarianten zuverlässiger macht
  4. Wie man Software durch Prüfung von Invarianten zuverlässiger macht
  5. Wie man Software durch Prüfung von Invarianten zuverlässiger macht

Jetzt kostenfreie Newsletter bestellen!