Parasoft-Logo

Erfahren Sie mehr über Parasoft C/C++test.

Nehmen Sie an unserer monatlichen 30-minütigen Produktdemo teil.

Jetzt registrieren

WEBINAR

Was Sie über Codeabdeckung für eingebettete C/C++-Systeme wissen müssen

Die Codeabdeckung misst, wie viel vom Quellcode einer Anwendung mit verschiedenen Methoden wie Unit-Tests, manuellen Tests und automatisierten Funktionstests getestet wird. Prozentuale Codeabdeckungsziele können subjektiv sein. Die Vollständigkeit der Abdeckung in sicherheitskritischen Systemen hängt vom branchenüblichen SIL-Metrikwert (Application Safety Integrity Level) und dem in der Avionik üblichen Design Assurance Level (DAL) ab. Bei der Entwicklung sicherheitskritischer Anwendungen, bei denen ein Ausfall zum Tod führen kann, verlangen gesetzliche Vorschriften und Industriestandards eine 100-prozentige strukturelle Codeabdeckung.

Diese Präsentation befasst sich mit dem wichtigen Thema der Codeabdeckung für eingebettete C/C++-Systeme und erläutert deren Bedeutung, verschiedene Messarten und die praktische Anwendung. Wir untersuchen, wie gründliche Tests, insbesondere in sicherheitskritischen Umgebungen, durchgeführt werden können, und stellen Tools vor, die den Prozess vereinfachen.

Codeabdeckung verstehen

Die Codeabdeckung ist eine Kennzahl, die angibt, wie viel Quellcode Ihrer Anwendung während des Tests ausgeführt wurde. Sie beantwortet die Frage: „Haben wir genug getestet?“ Durch die Hervorhebung ungetesteten Codes können potenzielle Fehler aufgedeckt und toter Code identifiziert werden. Die gängigste Methode hierfür ist die Code-Instrumentierung. Dabei wird zusätzlicher Code hinzugefügt, um die Ausführung von Anweisungen, Entscheidungen oder Verzweigungen zu verfolgen. Diese Instrumentierung protokolliert anschließend die Ausführung, sodass ein Prozentsatz der Codeabdeckung berechnet und visualisiert werden kann. Code wird dabei häufig grün (ausgeführt) oder rot (nicht ausgeführt) hervorgehoben.

Wichtige Abdeckungskriterien

  • Erklärungsabdeckung: Stellt sicher, dass jede Anweisung in der Anwendung ausgeführt wurde.
  • Leitungsabdeckung: Überprüft, ob jede Codezeile ausgeführt wurde. Der Ergebniswert kann von der Anweisungsabdeckung abweichen, wenn mehrere Anweisungen in einer Zeile vorhanden sind.
  • Branchenabdeckung (Entscheidungsabdeckung): Überprüft, ob jeder mögliche Ausführungspfad nach einem Entscheidungspunkt (z. B. einer if-Anweisung) mindestens einmal durchlaufen wird. Dabei wird sichergestellt, dass sowohl wahre als auch falsche Ergebnisse für Bedingungen getestet werden.
  • Modifizierte Bedingungs-/Entscheidungsabdeckung (MC/DC): Ein strengerer Standard, insbesondere für sicherheitskritische Systeme. MC/DC erfordert, dass jede Bedingung innerhalb einer Entscheidung nachweislich das Ergebnis der Entscheidung unabhängig beeinflusst. Dies bedeutet, dass alle möglichen Ergebnisse für jede Bedingung getestet und sichergestellt werden müssen, dass jede Bedingung das Ergebnis der Entscheidung beeinflussen kann.
  • Mehrfacherkrankungsschutz (MCC): Testet alle möglichen Ergebniskombinationen für Bedingungen innerhalb von Entscheidungen. Obwohl gründlich, kann dies zu einer sehr hohen Anzahl von Testfällen führen.

Highlights der praktischen Demonstration

Die Präsentation umfasste eine Demonstration, in der Folgendes gezeigt wurde:

  • Automatisierte Generierung von Unit-Testfällen: Tools können automatisch zahlreiche Unit-Tests generieren, um das Erreichen der Abdeckungsziele zu unterstützen.
  • Abdeckungsanalyse: Visualisieren von Abdeckungsmetriken (Zeile, Anweisung, Zweig, MC/DC) in IDEs wie Eclipse, VS Code oder über eigenständige Befehlszeilentools.
  • Anwendungsabdeckung: Verwenden Sie Befehlszeilentools zum Instrumentieren und Sammeln von Abdeckungsdaten für eigenständige Anwendungen, die dann zur Analyse in eine IDE importiert werden können.
  • Testen der Zielhardware: Die Möglichkeit, Abdeckungsanalysen auf eingebetteten Zielen durchzuführen, mit Optimierungen für Größe und Geschwindigkeit sowie Unterstützung für Multithread-Anwendungen.
  • Reporting: Erstellen von Berichten und Veröffentlichen von Abdeckungsdaten auf Plattformen wie DTP (Development Testing Platform) zur zentralen Analyse und Dashboarderstellung.

Warum bestimmte Abdeckungsarten in eingebetteten Systemen wichtig sind

In der Embedded-Welt werden C und C++ häufig in sicherheitskritischen Systemen verwendet. Branchen wie die Automobilindustrie (ISO 26262), die Avionik (DO-178C) und die Medizintechnik (IEC 62304) unterliegen strengen gesetzlichen Anforderungen und Prozessstandards. Diese Standards weisen Softwarekomponenten häufig Sicherheitsintegritätsstufen (SIL) oder Design Assurance Level (DAL) zu. Höhere Stufen, die ein höheres Risiko bei einem Softwarefehler bedeuten, erfordern in der Regel strengere Tests. Beispielsweise empfiehlt SIL 4 in IEC 61508 dringend eine 100-prozentige Abdeckung für Anweisungen, Verzweigungen und MC/DC. Der rote Faden, der sich durch diese Standards zieht, ist der Fokus auf die Abdeckung von Anweisungen, Verzweigungen und MC/DC, da diese von Branchenexperten als Best Practices zur Gewährleistung von qualitativ hochwertigem, sicherem und zuverlässigem Code angesehen werden.

Methoden zum Ermitteln der Codeabdeckung

Erreichen der Codeabdeckung kann durch verschiedene Testmethoden erfolgen:

  • Systemtest: Durch Ausführen vorhandener Systemtestfälle mit instrumentiertem Code kann mit minimalem Zusatzaufwand ein erheblicher Teil der Abdeckung erreicht werden. Allerdings wird die 100-prozentige Abdeckung häufig nicht erreicht, insbesondere bei defensivem Code, der nur unter Fehlerbedingungen ausgeführt wird.
  • Unit-Tests: Durch die Erstellung spezifischer Komponententests können Sie auf nicht abgedeckten Code abzielen, einschließlich defensivem Code oder bestimmten Zweigen, um höhere Abdeckungsziele zu erreichen.
  • Manuelles Testen: Obwohl die manuelle Testausführung weniger automatisiert ist, kann sie auch zu Abdeckungsmetriken beitragen.

Viele Organisationen kombinieren die Ergebnisse dieser verschiedenen Testmethoden, um ihre Gesamtabdeckungsziele zu erreichen. Beispielsweise kann die Abdeckung durch Unit-Tests mit der Abdeckung durch Systemtests zusammengeführt werden.

Integration der Abdeckung in CI/CD

Die Codeabdeckung ist ein wichtiger Bestandteil einer Continuous Integration/Continuous Delivery (CI/CD)-Pipeline. Tools automatisieren die Instrumentierung, Ausführung und Berichterstattung von Abdeckungsdaten und lassen sich nahtlos in Build-Systeme und CI/CD-Plattformen wie Jenkins, GitLab und Azure DevOps integrieren. Dies liefert Echtzeit-Feedback zur Codequalität und hilft, Risiken effektiv zu managen.

Umgang mit Herausforderungen bei der Codeabdeckung

  • Nicht erreichbarer Code: Manchmal können aufgrund von Endlosschleifen oder bestimmten Codestrukturen bestimmte Anweisungen durch normale Tests nicht erreicht werden. In solchen Fällen Abdeckung durch Inspektion (visuelle Überprüfung der Ausführung, üblicherweise durch Debuggen) ist ein empfohlener Ansatz zur Erfüllung von Standards.
  • Code-Aufblähung: Durch die Instrumentierung kann die Größe der ausführbaren Datei zunehmen. Wenn die Anwendung dadurch nicht auf die Zielhardware passt, besteht eine gängige Vorgehensweise darin, die Hälfte des Codes zu instrumentieren, die Abdeckung zu erfassen, dann die andere Hälfte zu instrumentieren und die Ergebnisse zusammenzuführen.
  • Abdeckung auf Baugruppenebene: Für hochsichere Systeme (wie DO-178C Level A) ist eine Abdeckung auf Assembler- oder Objektcodeebene erforderlich, da Compiler Code generieren können, der nicht direkt auf die Quelle zurückgeführt werden kann. Es gibt Tools zur Automatisierung dieses Prozesses.

Letztendlich ist die Codeabdeckung eine leistungsstarke Methode, um die Qualität und Zuverlässigkeit eingebetteter C/C++-Systeme sicherzustellen, insbesondere in sicherheitskritischen Bereichen. Durch das Verständnis der verschiedenen Abdeckungskriterien und den Einsatz geeigneter Tools können Entwicklungsteams ihre Testziele effizienter erreichen.