Der Objektcode stimmt möglicherweise aus der Perspektive eines Software-Entwicklers nicht mit dem Quellcode überein. Beispiel:
1 int i,x;
2 for (; x < 10; ++x)
3 {
4 ++i;
5 }
Zeile 2 wird typischerweise in zwei oder drei Blöcke aufgeteilt (Bild 1); einer zum Initialisieren der Schleife, ein weiterer zum Prüfen der Bedingung und Inkrementieren der Schleifenvariable sowie ein Block, der nach Verlassen der Schleife wieder für Ordnung sorgt.
Die Anweisungen werden nicht sequenziell durchlaufen, sondern in zwei oder drei Blöcken, die sich an nicht zusammenhängenden Speicherorten befinden können. Darüber hinaus gibt es zwei oder mehr Bedingungen (siehe unten, Assembler). Jede Bedingung teilt den Ausführungspfad in (mindestens) zwei weitere Ausführungspfade. Die Codeüberdeckungsdaten aller drei Blöcke müssen in einer Quellcodezeile zusammengeführt werden, damit der Anwender sie sehen kann.
Die iSystem-Tools zeigen die Codeüberdeckungsergebnisse für den auf dem Prozessor ausgeführten Code an. Im Disassembly-Fenster werden die oben beschriebenen Anweisungsblöcke dargestellt. Achten Sie auf den Verzweigungsbefehl „b“ ganz am Anfang und die bedingte Verzweigung „bc“ mit einem Sprung am Ende.
Komplexe Bedingungen
Beispiel:
1 if (a == 3 || b
==4)
2 {
3 somefunction(c);
4 }
Zeile 1 in diesem Beispiel lässt sich nur teilweise testen, wenn sich die erste Bedingung als wahr erweist und die Kurzschluss-Auswertungslogik gilt. In diesem Fall wird die zweite Bedingung nicht überprüft. Achten Sie bei der Codeüberdeckungsmessung darauf, dass alle Bedingungen bewertet werden.
Die iSystem-Tools stellen diese Situation mit sehr großer Detailtiefe dar (Bild 2). Wird die erste Bedingung erfüllt und es greift die Kurzschluss-Auswertungslogik, so zeigt die Überdeckungsinformation im Quellcode-Fenster an, dass die Zeile nicht ausgeführt wurde. Bei näherer Betrachtung der Assemblerdaten für diese Zeile ist zu sehen, dass der Code nur teilweise ausgeführt wurde.
In Bild 2 ist zu sehen, dass im Quellcode die „if“-Anweisung als ausgeführt markiert ist (grüner Pfeilschaft), jedoch kein Pfad als durchlaufen (rote Pfeilspitzen). Hier handelt es sich um ein Mischszenario, das auf Assembler-Ebene analysiert werden muss. Im Disassembly-Fenster ist zu sehen, dass die erste Bedingung wahr war, daher wurde die zweite Bedingung gar nicht evaluiert. Die Überdeckungslegende dieser Assembler-Zeilen weist daher rote Quadrate und Pfeile auf.
Bibliotheken
Durch den Einsatz von Bibliotheksfunktionen, z.B. Betriebssystemfunktionen, wird Objektcode zugefügt, für den kein Quellcode vorhanden ist. Diese Bibliotheksfunktionen müssen umfangreich getestet werden, da sie toten Code enthalten könnten - also Codepfade, die nie durchlaufen bzw. getestet wurden. Die iSystem-Tools erhalten die Überdeckungsdaten direkt aus der Hardware. Damit unterscheidet sich die Analyse von Bibliothekscode nicht von der Analyse von anwenderprogrammiertem Code. Ist kein Quellcode vorhanden (z.B. bei einer Betriebssystemfunktion), sind Informationen nur auf Objekt- und Assembler-Ebene verfügbar. Mit iSystem-Tools ist konfigurierbar, ob Bibliothekscode in den Daten und Reports zur Gesamtüberdeckung enthalten ist oder nicht.
Für Funktionen, für die kein Quellcode vorhanden ist, zeigt das Assembler-Fenster leere Spalten beim Aufruf der entsprechenden Funktion an. Trotzdem kann man für jede Funktion, jeden Objektcode und jede Assembler-Anweisung die vollständigen Codeüberdeckungsdaten sehen (Bild 3).
Compiler-generierter Code
Besonders bei kleineren Prozessoren (z.B. Freescale MPC5604B) muss der Compiler Code einfügen, um die im Quellcode möglichen Funktionen auch auf dem Prozessor umzusetzen. Dies gilt hauptsächlich für Befehle, die sich nicht direkt in Maschinencode übersetzen lassen. Die Umwandlung einer Floating-Point-Variable in Integer wird z.B. von inline eingefügtem Code ausgeführt oder der Compiler fügt den Aufruf einer Bibliotheksfunktion ein. Compiler-generierter Code sollte deshalb gründlich geprüft werden, da er toten Code oder nicht durchlaufene Codepfade beinhalten kann.
Da die iSystem-Tools die Überdeckungsinformation direkt von der Hardware beziehen, unterscheidet sich die Analyse des vom Compiler erzeugten Codes nicht von der Analyse von anwenderprogrammiertem Code. Ist kein Quellcode vorhanden, steht die Information nur auf Objekt- und Assembler-Ebene zur Verfügung. Entscheidend ist, dass auch compilergenerierter Code der Analyse unterzogen wird und Codesegmente, die nicht ausgeführt wurden, sofort sichtbar werden. Wenn diese Codesegmente nie getestet werden, fehlerhaft sind und in einem Seriengerät später doch einmal ausgeführt werden, kann dies zu katastrophalen Fehlern führen.
Compiler-Optimierung
Ein weiterer Aspekt ist die Compiler-Optimierung. Compiler können
Überprüfen Sie bei der Codeüberdeckungsmessung, welche Quellcodezeilen Objektcode erzeugen, bzw. ermitteln Sie die Ursache, falls kein Objektcode erzeugt wird. Jegliche Modifizierung oder Recompilierung des Codes kann die Compiler-Optimierung verändern und dazu führen, dass -Codepfade nicht getestet werden.
Die iSystem-Tools zeigen den aktuellen Ablauf präzise an. In der Quellcode-Ansicht auf iSystem winIDEA wird jeder Zeile, die Objektcode generiert, linkerhand eine Codeüberdeckungslegende zugeordnet. Hinweis: „{“ und „}” erzeugt manchmal keinen Objektcode und erhält somit keine Codeüberdeckungslegende (Bild 4).
Compiler-Fehler
Auch Compiler können Fehler aufweisen, wenn auch selten. Diese Bugs treten im erzeugten Code selten auf, da Compiler i.d.R. diesbezüglich getestet werden. Meistens finden sie sich in den Debugdaten, die vom Compiler erzeugt und von anderen Tools genutzt werden. Diese Bugs sind wie gesagt selten, lassen sich jedoch nicht gänzlich ausschließen. Nur mit On-Chip-Trace
möglich.
Die Anforderungen an die Codeüberdeckungsanalyse steigen in fast allen Anwendungsbereichen kontinuierlich. Mittels objektcode-basierender Codeüberdeckung lassen sich Tests einfach und sehr nah am Endprodukt durchführen. Um einen hohen Grad an Codeüberdeckung zu erreichen, überprüfen Sie den Objektcode und finden Sie Testfälle, um den (noch nicht) getesteten Code abzudecken.
iSystem bietet die Codeüberdeckungsanalyse in dem gleichen Tool, mit dem auch der Code entwickelt wird. So werden Entwicklungskosten und -zeiten optimal genutzt, um Software-Fehler möglichst frühzeitig im Entwicklungsprozess aufzuspüren und zu bereinigen. Gleich bei der Auswahl eines Prozessors oder Controllers für ein Produkt sollte man deshalb die Möglichkeit des On-Chip-Tracings berücksichtigen, d.h. der Controller muss die dafür notwendigen Schnittstellen zur Verfügung stellen.