Fault Injenction: Einfach automatisiert

Einfach automatisiert

Die Fehlerinjektion ist eine bewährte Methode, um insbesondere die Software von
sicherheitskritischen Anwendungen dokumentierten Tests zu unterziehen. Für eine nachweisliche 100-prozentige Anweisungsüberdeckung lassen sich jetzt im Test-Tool Tessy mithilfe der Fault-Injection-Funktion Fehlersituationen vollkommen ohne Quellcodeänderung automatisiert testen.

 (Bild: Senior Software Quality Consultant)

(Bild: Senior Software Quality Consultant)

In der Softwareentwicklung Safety-relevanter Anwendungen kommt es in der Code-Erstellung auf Programmiertechniken wie Diversität, Defensivität, Selbsttests und ständige Überprüfungen an. Für den Entwickler bedeutet das eine besondere Herausforderung hinsichtlich der Einhaltung der einschlägigen Normen, wie ISO26262, EN/IEC61508, EN50128 oder IEC62304. Denn die Konformität muss durch dokumentierte Test, wie z.B. eine 100-prozentige Anweisungsüberdeckung (Statement Coverage), nachgewiesen werden. Ob die Funktionalität der Software den Anforderungen entspricht, lässt sich in der Regel ganz einfach mit etablierten Testwerkzeugen nachweisen. Allerdings kann hier die Anweisungsüberdeckung nicht immer vollständig erfüllt werden. Das liegt u.a. daran, dass im Funktionstest nicht alle denkbaren Fehler auftreten und deshalb die Sicherheitsvorkehrungen in der Software nicht vollständig abgearbeitet werden können. Für eine 100-prozentige Statement Coverage muss der Testingenieur daher Fehlersituationen aktiv erzeugen.

Statement Coverage vollständig erfüllen

In einem System- oder HiL (Hardware in the Loop)-Test lassen sich Fehlersituationen relativ einfach herbeiführen, denn es können äußere Interfaces durch Stimulation des Testsystems gesteuert und dadurch Fehlersituationen reproduzierbar getestet werden. Auf diese Weise lässt sich die Anweisungsüberdeckung erhöhen, aber oft noch nicht zu 100 Prozent erfüllen. Das liegt daran, dass die internen Sicherheitsvorkehrungen in der Software, wie z.B. die Diversität, hier nicht beeinflusst werden können. Eine weitere Schwierigkeit stellt die Messung der Statement Coverage dar. Bei der hier eingesetzten Technik wird der Quellcode instrumentiert, was Einfluss auf das dynamische Verhalten des Systems haben kann. In der Praxis wird die Anweisungsüberdeckung hauptsächlich während des Unit Test ermittelt. Auf diese Weise lassen sich beim Test einzelner Funktionen auch die internen Sicherheitsvorkehrungen in der Software testen.

Durch Anwenden von normgerechten Messmethoden wird bereits ein hohes Maß an Testqualität und damit eine hohe Statement Coverage der getesteten Funktion erreicht. Bei hoch-sicherheitskritischem Code lässt sich damit aber immer noch keine 100-prozentige Anweisungsüberdeckung erzielen. Denn Sicherheitsfunktionen wie Diversität und Anwendung von defensiven Programmiertechniken können nur durch Herbeiführen einer entsprechenden Fehlersituation vollständig getestet werden. Ferner wird bei schweren Fehlersituationen der sichere Zustand des Systems nur dadurch erreicht, dass das Programm in eine Endlosschleife läuft und darauf wartet, dass ein Watchdog anschlägt und das System in einen sicheren Modus überführt. Wird der Test auf dem Mikrocontroller ausgeführt, kann die Hardware nicht so einfach in jeden beliebigen Fehlerzustand versetzt werden, denn die Register der Peripherieeinheiten können nur entsprechend ihrer Hardware-Implementierung eingestellt werden. Folglich lassen sich auch in der Software nicht alle Fehlerzustände herstellen. Beim Einsatz eines Simulators wird je nach Leistungsumfang die Peripherie ebenso simuliert und es ergibt sich dieselbe Schwierigkeit wie beim Mikrocontroller-Test. Die Simulation der Peripherie kann zwar zumeist konfiguriert oder abgestellt werden, allerdings geht dann das Verhalten der Hardware verloren. So wird z.B. nach einem Schreibbefehl an die Peripherie ein Registerbit durch die Hardware gesetzt. Ohne Simulation liegen die Register der Peripherie in einem RAM-Speicher und verhalten sich nur noch wie eine Variable.

Fault Injection ohne Quellcodeänderung

Abhilfe schaffen an dieser Stelle Techniken wie die Fehlerinjektion (Fault Injection): Dazu werden im Quellcode, z.B. mithilfe von Makros, Fehler in die Anwendung eingeschleust, um ihr Verhalten in diesem Fall zu testen. In der Entwicklung wird die Fault Injection mit Sicht auf das System und die Systemanforderungen durchgeführt und ohne Berücksichtigung der inneren Sicherheitsvorkehrungen der Software. Eine unvollständige Anweisungsüberdeckung tritt damit erst bei der Entwicklung der Unit Tests auf. Diesen Mangel kann der Testingenieur erkennen und dann entsprechend die notwendigen Fehlerinjektionen implementieren. Die manuelle Fault Injection, z.B. mit Makros, hat den entscheidenden Nachteil, dass sie von Hand durchgeführt und verwaltet wird – und anschließend im produktiven Quellcode verbleibt. Das stellt beim Test von hoch-sicherheitskritischen Anwendungen ein Problem dar, denn in einem normgerechten Entwicklungsprozess sollte eine Quellcodeveränderung wieder alle Instanzen durchlaufen. Das bedeutet, die Änderungen für die Fault Injection werden freigegeben, in das Versionskontrollsystem übertragen und durchlaufen einen Codereview. Dieser Umweg bedeutet einen häufig unerwünschten zusätzlichen Zeit- und Kostenaufwand. Aus diesem Grund kommt die Analyse der Anweisungsüberdeckung bei professionellen Unit-Test-Werkzeugen ohne Quellcodeänderung aus. Dabei wird die Rate der Anweisungsüberdeckung ebenfalls mithilfe des Quellcodes gemessen, aber die Implementierung ist dynamisch für die Testdurchführung und verbleibt nicht dauerhaft im produktiven Quellcode. Durch das Abschalten der Messung der Anweisungsüberdeckung ist darüber hinaus bei einem erneuten Testlauf durch das gleiche Ergebnis beider Testdurchführungen gewährleistet, dass diese Instrumentierung keinen Einfluss auf den Ablauf der Funktion nimmt.

Fault Injection: Ein Beispiel

Beleuchten wir eine oft genutzte Funktionalität eines sehr einfachen Handlers einer Zustandsmaschine. Hierbei verhindern Sicherheitsmaßnahmen, dass der default-Pfad eines switch-Statements erreicht werden kann. Nur mit einer Fault Injection, die z.B. einen undefinierten Zustand einstellt, ist es möglich, diesen default-Pfad zu erreichen. Im Systemtest ist eine derartige Fehlerinjektion nicht möglich, da keine Software-Fault-Injections mehr vorhanden sind. Im HiL-Test werden zumeist Fault Injections für spezifizierte Fehlersituationen eingesetzt, die in den seltensten Fällen derartige oder alle default-Pfade abdecken. Nur während Unit-Test-Phase können Fehlerinjektionen sinnvoll für die Abdeckungsanalyse eingesetzt werden, was zudem eine sehr gute Ausgangsbasis für folgende Testphasen wie Integrations-, HiL- und Systemtest ist. Betrachten wir das Beispiel genauer: Das einhüllende if-Statement in Zeile 7 fasst alle vorherigen Sicherheitsabfragen über den gültigen Zustand der Zustandsmaschine zusammen. Ist der Zustand undefiniert, erfolgt bereits nach dieser Sicherheitsabfrage der Aufruf des Error Handlers und die Funktion wird beendet. Durch solche oder ähnliche vorherige Überprüfungen des Zustandes können nicht alle default-Pfade in der Software erreicht werden, so wie im vorliegenden Beispiel in Zeile 20 der default-Pfad des switch-Statements nicht erreicht werden kann.

 

Eine Fehlerinjektion, gesteuert über ein Define FAULTINJECTION für den C-Präprozessor, kann z.B. ab Zeile 9 des obigen Beispiels wie folgt aussehen:

 

Wird das Define FAULTINJECTION gesetzt, so kann auch diese Fehlersituation getestet und die Anweisungsüberdeckung zu 100 Prozent erfüllt werden. Die zusätzliche Testvariable tstFaultInjection steuert, ob die Fehlersituation für einen Testfall eintritt oder nicht, und wird gleichzeitig als Wert für den undefinierten Zustand genutzt. Bei der Erzeugung des binären Codes für das Endprodukt ist das Define FAULTINJECTION nicht gesetzt und die Fault Injection sollte nicht vorhanden sein. Da nicht immer alle Fault Injections aktiv sein können, werden diese für das System weiter unterteilt und über verschiedene Defines und Testvariablen gesteuert. Je nach Umfang des Systems steigt die Anzahl der Fault Injections, was wiederum eine entsprechende Verwaltung notwendig macht.

Automatische Fault Injection in Tessy 4.1

Razorcat schließt diese Lücke mit einer neuen Funktion für die Verwaltung und Implementierung von automatisierten Fault Injections, integriert in der neuen Version 4.1 des zertifizierten Unit- und Integrationstestwerkzeugs Tessy. Die neue Fault-Injection-Funktion verwaltet komfortabel alle Fehlerinjektionen und instrumentiert den Quellcode automatisch. Der Testingenieur kann innerhalb von Tessy flexibel bestimmen, ob Testfälle mit einer Fault Injection durchgeführt werden oder nicht. Die Instrumentierung kann jederzeit abgeschaltet werden, somit ist die gleiche Sicherheit wie bei der Analyse der Anweisungsüberdeckung über die Testergebnisse gewährleistet. Die Positionen der Fault Injection werden von Tessy bei einer Quellcodeänderung berücksichtigt und in einer Analyse der Programmstruktur automatisch an die entsprechenden Stellen im Quellcode dynamisch gesetzt. Die Tessy-Coverage-Viewer-Perspektive zeigt das Flow Chart der Funktion und markiert die Programmablaufpfade in den Farben rot und grün. Noch nicht durchlaufene Codebereiche erhalten eine rote Markierung und sind so einfach zu identifizieren. Parallel dazu wird für eine schnelle Analyse der korrespondierende Quellcode angezeigt. Wurde ein Programmpfad nicht ausgeführt, kann der Testingenieur am entsprechenden Pfad des Flow Charts einen Code von Tessy automatisch instrumentieren und so für den Test eine Fehlersituation herbeiführen. Bild 1 zeigt die Analyse der Anweisungsüberdeckung in der Coverage-Viewer-Perspektive von Tessy für das obige Beispiel. Bei diesem Handler einer Zustandsmaschine hat der Testingenieur die Möglichkeit im Flow Chart, am default case des switch-Statements einen benutzerdefinierten Code von Tessy automatisch instrumentieren zu lassen, z.B. um den Wert der Zustandsvariable eState zu verändern. Der zu instrumentierende Code wird in einem Dialogfeld eingegeben. Die Fault Injection schreibt im vorliegenden Beispiel den ungültigen Wert 0xFF in die Zustandsvariable eState, sodass der default-Pfad in Zeile 20 ausgeführt und der Aufruf des Error Handlers in Zeile 21 erfolgt.

Im Dialogfenster Edit Fault Injection (Bild 2) wird der zu instrumentierende Code eingegeben. Tessy analysiert, welche vorhandenen und ausgeführten Testfälle das betroffene switch-Statement erreichen und schlägt diese Testfälle für die Anwendung der Fehlerinjektion vor. Im vorliegenden Beispiel wurde Testfall 2 ausgewählt. Tessy erstellt nun mit der eingegeben Fault Injection eine Kopie des Testfalls 2 und erzeugt Testfall 5. Der neue Testfall 5 bekommt die Eigenschaft Fault Injection und wird hervorgehoben. Nachdem der Name des Testfalls 5 angepsst und der Test erneut durchgeführt wurde, wird die Anweisungsüberdeckung zu 100 Prozent erreicht. Da nach der erneuten Testdurchführung alle Pfade erreicht wurden, sind ebenso alle Pfade im Flow Chart der Coverage-Viewer-Perspektive grün dargestellt; der für die Fault Injection ausgehende default-Pfad erhält als Kennzeichen ein blaues Dreieck. In der Test-Items-Ansicht erhalten die Icons der Testfälle mit der Eigenschaft Fault Injection zusätzlich das gleiche Dreieck, in unserem Beispiel ist dies der Testfall 5.

Weitere Fehlersituationen, wie Sicherheitsüberprüfungen z.B. Read after Write oder das Beenden von Endlosschleifen, sind mit der Fault-Injection-Funktionalität nun einfach zu realisieren. Tessy verwaltet alle Fehlerinjektionen und trägt diese bei einer Testdurchführung automatisiert ein. Liegt eine neue Version des Quellcodes vor, analysiert Tessy diese neue Version und setzt die Fault Injection automatisch an die richtige Stelle. Dabei verbleibt die Instrumentierung, wie bei der Anweisungsüberdeckung, nicht im Quellcode und kann bei der Testdurchführung eingestellt werden. Die automatisch generierten Testergebnisberichte enthalten zusätzliche Informationen über die Verwendung von Fault Injections. Eine weitere Iteration durch den Entwicklungsprozess ist somit nicht mehr notwendig und die Unit Tests lassen sich in einem Arbeitsprozess vollständig erstellen, um – ohne weiteren Zeit- und Kostenaufwand – eine 100-prozentige Anweisungsüberdeckung zu erreichen.

 Tessy Coverage-Viewer-Perspektive (Bild: Razorcat Development GmbH)

Bild 1: Tessy Coverage-Viewer-Perspektive (Bild: Razorcat Development GmbH)

 Dialog Edit Fault Injection (Bild: Razorcat Development GmbH)

Bild 2: Dialog Edit Fault Injection (Bild: Razorcat Development GmbH)

 Mit der Fault Injection ist 100% Anweisungsüberdeckung erfüllt (Bild: Razorcat Development GmbH)

Bild 3: Mit der Fault Injection ist 100% Anweisungsüberdeckung erfüllt (Bild: Razorcat Development GmbH)

Autor: Thomas Dirsch,
Razorcat Development GmbH
www.razorcat.com

Ausgabe:
Razorcat Development GmbH
www.razorcat.com

Das könnte Sie auch Interessieren