Testnachweis mit knappen Ressourcen Code Coverage bei Embedded Systems
Der Nachweis der Testabdeckung – auch Code Coverage genannt – wird von zahlreichen Normen und Standards gefordert. Welche Hürden sich damit bei Embedded-Systemen ergeben, erläutert Klaus Lambertz von Verifysoft Technology im Interview.
Anbieter zum Thema

Im Embedded-Umfeld sehen sich Tester angesichts der knappen Hardware-Ressourcen mit besonderen Herausforderungen konfrontiert. Klaus Lambertz, Geschäftsführer der auf Testing im Embedded-Umfeld spezialisierten Verifysoft Technology GmbH in Offenburg, weiß um Fallstricke bei der Code Coverage und wie man sie umgehen kann.
David Göhler: Herr Lambertz, was genau versteht man unter Code Coverage und welche Rolle spielt sie bei der Entwicklung von Embedded-Systemen?
Klaus Lambertz: Code-Coverage – auf Deutsch auch Testabdeckung genannt – ist eine Metrik bei kontrollflussorientierten Testtechniken, die aussagt, wie umfassend eine Software oder ein Teil davon getestet wurde. Sehr vereinfacht ausgedrückt: Werden bei einem Test etwa drei von vier möglichen Optionen durchlaufen, beträgt die Testabdeckung 75 Prozent.
Es bleibt also eine Unsicherheit. Für die nicht durchlaufene Option wurde entweder noch kein passender Testfall geschrieben oder es handelt sich um toten Code, der gar nicht erreichbar ist. Bei der Testabdeckung unterscheidet man verschiedene Stufen, die unterschiedlich aufwändig und unterschiedlich aussagekräftig sind.
Bei der Embedded-Entwicklung dient die Code Coverage unter anderem dazu, die in den unterschiedlichen Normen und Standards definierten Anforderungen an das Testing nachzuweisen. Das ist vor allem in sicherheitskritischen Bereichen der Fall. Etwa in der Luftfahrt mit DO-178C, in der Automobilbranche mit ISO 26262 oder in der allgemeinen Norm IEC 61508. Hier werden bestimmte Testüberdeckungsstufen und deren Nachweis gefordert, um die notwendige Sicherheit zu gewährleisten.
Göhler: Welche Code-Coverage-Stufen sind dabei für die Entwickler und Tester relevant?
Lambertz: Meist setzen die einschlägigen Standards Anweisungs- und Zweigüberdeckungstests voraus. Auch modifizierte Bedienungs- und Entscheidungsüberdeckungstests – häufig als MC/DC, Modified Condition/Decision Coverage bezeichnet – sind oft üblich.
Grundsätzlich gilt: Je höher die Sicherheitsanforderungen an eine Software, desto höher muss die geforderte Testabdeckung sein. MC/DC ist dabei die höchste Stufe und extrem komplex, da hier zumindest theoretisch alle Bedingungskombinationen überprüft werden müssen. Bei der Anweisungs- oder der Zweigüberdeckung muss nur ermittelt werden, ob alle Programmzweige oder Statements durchlaufen werden.
Der Vollständigkeit halber möchte ich noch die Aufrufüberdeckung als einfachste Stufe erwähnen. Da dabei aber die inneren Abläufe einer Software komplett ignoriert werden, ist ihr Nutzen doch sehr überschaubar.
Göhler: Wie wird die Code Coverage im Rahmen des Testings denn erfasst?
Lambertz: Dazu wird der zu testende Code vor der Übergabe an den Compiler durch Zähler ergänzt, die je nach gewünschter Code-Coverage-Stufe unterschiedlich komplex und umfangreich sind. Man nennt diesen Vorgang Instrumentieren. Zudem muss auf dem zu testenden Target eine Bibliothek implementiert werden, die unter anderem die Datenübertragung an einen Host übernimmt. Dort werden die Ergebnisse der Zähler erfasst.
Für die Instrumentierung nutzt man Code Coverage Analyzer. Ein recht einfaches Werkzeug, um das mal auszuprobieren, ist zum Beispiel das GNU Coverage Testing Tool gcov. Damit kann man schon einige Informationen zusammentragen. Für große oder sicherheitskritische Projekte fehlen diesem Open-Source-Tool aber die anspruchsvollen Testabdeckungsstufen. Auch die Aufbereitung und Interpretation der gewonnenen Daten ist natürlich nicht auf dem Niveau, das man für professionelles Testing benötigt.
Göhler: Nachdem im Zuge der Instrumentierung der vorhandene Code erweitert? Beeinflusst das nicht die Performance der Software während des Testings?
Lambertz: Durchaus. Der Code wird umfangreicher, der Speicherbedarf wächst und die Ausführungsgeschwindigkeit nimmt ab. Die Zähler werden in der Regel als globale Arrays im Datenspeicher abgelegt. Wie stark diese Effekte zum Tragen kommen, hängt natürlich nicht zuletzt davon ab, welche Testabdeckungsstufe benötigt wird. Auf PCs oder Servern spielt das aber keine Rolle, dort stehen heute ja mehr als genug Ressourcen zur Verfügung. Spannend wird es, wenn man Code auf Embedded-Targets testet. Hier sind die verfügbaren Hardware-Ressourcen aus Kostengründen oft sehr knapp kalkuliert.
Göhler: Welche Möglichkeiten gibt es, auch auf schwachen Targets zu testen? Könnte man den Test nicht einfach in einer Emulation oder dergleichen durchführen?
Lambertz: Innerhalb der Entwicklung kann man sicher den einen oder anderen Code-Bestandteil auch mit Hilfe von Emulatoren testen. Allerdings sind diese Ergebnisse mit sehr viel Vorsicht zu genießen und auch für einen standardkonformen Nachweis ungeeignet. Denn letztlich testet man auf einer anderen Architektur, die das Testergebnis ja auch signifikant beeinflussen kann. Man kommt also um das Testing und die Code Coverage auf dem echten Target nicht herum.
Göhler: Welche Möglichkeiten hat man dann, die Testüberdeckung sinnvoll zu erfassen?
Lambertz: Zum einen gibt es Code Coverage Analyzer, die speziell für Embedded-Systeme optimiert sind. Unser Tool Testwell CTC++ beispielsweise erweitert den Code beim Instrumentieren so wenig wie möglich. Zum anderen gibt es Möglichkeiten, den Einfluss der Instrumentierung zu reduzieren. Naheliegend ist zum Beispiel, den Code nur abschnittsweise zu instrumentieren und die Code Coverage für jeden Abschnitt separat zu erfassen. Trägt man diese Ergebnisse dann zusammen, entsteht wieder ein vollständiges Bild. Ein anderer Ansatz ist, die Größe der Zähler zu beschränken. Das sollte man aber mit sehr viel Vorsicht umsetzen.
Göhler: Welche Probleme können auftreten, wenn die Zählergröße verkleinert wird?
Lambertz: Letztlich besteht die Gefahr, dass ein Zähler überläuft. Üblicherweise arbeiten Code-Coverage-Tools mit 32-Bit-Zählern. Entscheidet man sich für 16 oder acht Bit, spart man natürlich erheblich an benötigtem Speicherplatz. Laufen die Zähler jedoch über, verfälscht das das Ergebnis bis zur Unbrauchbarkeit. Da sollte man also die Daten mit größter Sorgfalt interpretieren.
Als Extremfall kann man da die so genannte Bit-Coverage nehmen: Wenn mir die Information reicht, dass ein bestimmtes Statement oder ein bestimmter Zweig durchlaufen wurde, kann man auf einen 1-Bit-Zähler gehen. Dann sieht man zwar nicht, wie oft ein Bereich beim Testing durchlaufen wurde, reduziert aber den Instrumentierungs-Overhead auf das absolute Minimum.
Göhler: Abgesehen vom Speicher: Was kann bei kleinen Targets sonst noch schieflaufen?
Lambertz: Die Instrumentierung belastet natürlich nicht nur RAM und ROM, sondern auch den Prozessor. Die Zähler müssen ja auch verarbeitet werden. Dabei kann es passieren, dass ein definiertes Timing nicht mehr eingehalten wird. Vor allem, wenn der Prozessor im Target bereits eng an der Leistungsgrenze arbeitet, sind fehlerhafte Programmabläufe möglich.
Insbesondere die Bus-Kommunikation ist hier anfällig. Der Tester sollte also den Ablauf überwachen und die Ergebnisse sehr genau prüfen. Mit einem leistungsfähigen, auf die Erfordernisse der Embedded-Entwicklung angepassten Code-Coverage-Tool ist es aber möglich, sowohl den zusätzlichen Speicherbedarf als auch die Änderungen des Laufzeitverhaltens relativ gering zu halten.
Göhler: Das klingt unterm Strich nach einem komplexen und aufwändigen Verfahren. Warum sollte man, abgesehen von vorgeschriebenen Normen, diesen Aufwand treiben?
Lambertz: Ohne ausreichendes und dokumentiertes Testing wissen Sie nicht, ob Ihr Produkt auch wirklich sicher und funktional ist. Gerade Embedded-Geräte sind heute aber in vielen Bereichen kritisch. Dabei muss man gar nicht an die Gefahren denken, die von fehlerhaften medizinischen Geräten oder Devices im Auto ausgehen können.
Nehmen Sie einfach das aktuelle Trendthema IoT: Basiert das Geschäftsmodell eines Unternehmens auf IoT, müssen diese Devices zuverlässig funktionieren. Ansonsten steht das Unternehmen. Und Sie als Hersteller oder Entwickler dieser Geräte müssen sofort reagieren und die Fehler beseitigen. Der Aufwand dafür übersteigt den Aufwand für strukturiertes und angemessenes Testing in der Regel erheblich – vom beschädigten Ruf ganz abgesehen. Je früher Sie Fehler entdecken, desto einfacher und günstiger ist die Beseitigung.
(ID:45447350)