Parasoft-Logo

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

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

Jetzt registrieren

WEBINAR

Alles, was Sie über die strukturelle Codeabdeckung für C und C++ wissen müssen

Strukturelle Abdeckung ist die Identifizierung von Code, der ausgeführt und protokolliert wurde. Es gibt mehrere Gründe, warum es wichtig ist, diese Aktivität für eingebettete sicherheitskritische Systeme durchzuführen. Zum einen muss festgestellt werden, ob die Software ausreichend getestet wurde. Eine weitere besteht darin, Compliance- und Zertifizierungsanforderungen zu erfüllen. Möglicherweise möchten Sie auch sicherstellen, dass Ihre Software keinen toten Code enthält.

Diese Präsentation befasst sich eingehend mit Strukturelle Codeabdeckung für C- und C++-Entwicklung, wobei der Schwerpunkt auf der Bedeutung für sicherheitskritische Systeme liegt und verschiedene Messkriterien wie Anweisungs-, Zweig- und MC/DC-Abdeckung untersucht werden. Wir behandeln auch praktische Methoden zum Erreichen und Automatisieren der Codeabdeckung.

Wir zeigen Ihnen die strukturelle Codeabdeckung für Anweisung, Zweig und MC/DC sowie automatisierte Berichte und Metriken für Codeabdeckung und Codekomplexität.

Codeabdeckung verstehen

Bei der Codeabdeckung geht es darum, zu ermitteln, welche Teile Ihres Codes während des Tests tatsächlich ausgeführt wurden. Sie ist eine wichtige Kennzahl, um festzustellen, ob Ihre Software gründlich genug getestet wurde. Bei sicherheitskritischen eingebetteten Systemen ist dies für die Einhaltung von Vorschriften und die Zertifizierung von entscheidender Bedeutung. Außerdem hilft sie Ihnen, toten Code zu finden und zu eliminieren – Code, der nie ausgeführt wird.

Im Wesentlichen beantwortet die Codeabdeckung die Frage: „Habe ich genug getestet?“ Sie hilft auch, Fehler in ungetesteten Abschnitten aufzudecken. Die große Frage ist: Wollen Sie diese ungetesteten Bereiche riskieren? In vielen regulierten Branchen haben Sie möglicherweise keine Wahl – Sie müssen möglicherweise eine 100-prozentige Abdeckung erreichen.

Key Take Away

  • Zweck: Identifizieren Sie ausgeführten Code, stellen Sie angemessene Tests sicher, erfüllen Sie die Compliance und finden Sie toten Code.
  • Mechanismus: Dies wird normalerweise durch Code-Instrumentierung erreicht, bei der der Protokollausführung zusätzlicher Code hinzugefügt wird.
  • Metriken: Zu den gängigen Typen gehören Anweisung, Verzweigung (Entscheidung) und Modified Condition/Decision Coverage (MC/DC).
  • Standards: Branchen wie die Automobilindustrie (ISO 26262), die Luft- und Raumfahrtindustrie (DO-178C) und die Medizintechnik (IEC 62304) haben spezifische Abdeckungsanforderungen basierend auf den Sicherheitsintegritätsstufen.
  • Automation: Tools können die Testfallgenerierung und Abdeckungsanalyse automatisieren und in IDEs und CI/CD-Pipelines integrieren.
  • Challenges: Das Erreichen einer 100-prozentigen Abdeckung kann aufgrund von defensivem oder nicht erreichbarem Code schwierig sein und erfordert häufig eine visuelle Inspektion (Debugger) oder die Entwicklung spezifischer Testfälle.

Höhepunkte der Demonstration

Während der Demonstration werden die wichtigsten Funktionen vorgestellt:

  • IDE-Integration: Visualisierung von Abdeckungsmetriken direkt in IDEs wie Eclipse und VS Code, Anzeige der Zeilen-, Anweisungs-, Zweig- und MC/DC-Abdeckung.
  • Automatisierte Testgenerierung: Generieren von Unit-Tests, die zu den Abdeckungszielen beitragen.
  • Anwendungsabdeckung (Befehlszeile): Verwenden Sie Tools wie C/C++test, um Tests für Anwendungen außerhalb der IDE zu instrumentieren, zu erstellen und auszuführen und Abdeckungsprotokolle zu generieren, die dann zur Analyse wieder in eine IDE importiert werden können.
  • Testen der Zielhardware: Unterstützung für das Sammeln von Abdeckungsdaten auf eingebetteten Zielen mit Optionen zur Optimierung hinsichtlich Größe oder Geschwindigkeit.
  • Abdeckungs-Dashboard: Zentralisierte Berichterstattung und Analyse von Abdeckungsdaten mithilfe von Plattformen wie DTP.

Funktionsweise der Codeabdeckung

Der gebräuchlichste Weg nach Codeabdeckung abrufen erfolgt durch Code-Instrumentierung. Dabei werden Ihrem ursprünglichen Quellcode kleine Codeteile hinzugefügt. Diese Ergänzungen verfolgen, ob eine Anweisung, Entscheidung oder ein Zweig ausgeführt wurde. Die Instrumentierung protokolliert diese Informationen anschließend, sodass Tools einen Abdeckungsprozentsatz berechnen und visualisieren können, welche Codeteile betroffen sind.

Möglicherweise sehen Sie Ihren Code grün hervorgehoben (getestet) mit einigen roten Teilen (ungetestet). Einige Tools zeigen teilweise abgedeckten Code an, oft in Gelb. Dies kann bei einem if Anweisung mit mehreren Bedingungen, bei der nicht alle möglichen Ergebnisse dieser Bedingungen getestet wurden.

Arten der strukturellen Abdeckung

Wenn wir über strukturelle Abdeckung sprechen, betrachten wir verschiedene Möglichkeiten, um zu messen, wie gründlich die Struktur des Codes getestet wurde. Obwohl Begriffe manchmal überladen sind oder branchenübergreifend unterschiedlich interpretiert werden, sind die wichtigsten Typen für sicherheitskritische C- und C++-Anwendungen:

  • Erklärungsabdeckung: Stellt sicher, dass jede ausführbare Anweisung im Code mindestens einmal ausgeführt wurde.
  • Zweigabdeckung (oder Entscheidungsabdeckung): Garantiert, dass jeder mögliche Zweig (wie die true und false Ergebnisse einer if Anweisung) wurde ausgeführt.
  • Modifizierte Bedingungs-/Entscheidungsabdeckung (MC/DC): Dies ist ein strengerer Standard, der oft für Systeme mit hoher Sicherheit erforderlich ist. Er stellt sicher, dass jede Bedingung innerhalb einer Entscheidung unabhängig voneinander nachgewiesen wurde und sich auf das Ergebnis der Entscheidung auswirkt. Für eine Entscheidung wie (A && B), MC/DC erfordert Tests, die zeigen A das Ergebnis zu ändern, während B ist festgelegt und B das Ergebnis zu ändern, während A ist behoben, zusätzlich zum Testen aller Zweige.

Es gibt noch weitere Typen, wie etwa Bedingungsabdeckung (Testen einzelner Bedingungen) und Zeilenabdeckung (Sicherstellen der Ausführung jeder Zeile, was sich von der Anweisungsabdeckung unterscheiden kann, wenn mehrere Anweisungen in einer Zeile stehen). Anweisung, Verzweigung und MC/DC werden in Sicherheitsstandards jedoch am häufigsten genannt.

Automatisieren der Codeabdeckung

Das manuelle Ermitteln der genauen Testfälle, die zum Erreichen bestimmter Abdeckungsziele erforderlich sind, kann unglaublich zeitaufwändig sein. Glücklicherweise können Tools erheblich helfen.

Abdeckungsberater Funktionen können Ihren Code analysieren und spezifische Parameterwerte oder Vorbedingungen für Ihre Unit-Tests vorschlagen, um bestimmte Zeilen oder Zweige abzudecken. Dies kann die Erstellung von Testfällen drastisch beschleunigen.

Automatisierte Generierung von Unit-Testfällen ist eine weitere leistungsstarke Funktion. Tools können automatisch eine Reihe von Unit-Tests generieren, die nicht nur die Funktionalität prüfen, sondern auch Ihre strukturellen Abdeckungsanforderungen erfüllen. Diese generierten Tests umfassen häufig verschiedene Arten von Prüfungen, wie Nullzeigertests, Grenzwerttests und Mid-Max-Tests, um potenzielle Fehler aufzudecken und die Abdeckung zu verbessern.

Kombinieren der Abdeckung verschiedener Methoden

Es ist selten, dass eine einzelne Testmethode eine 100%ige Abdeckung erreicht, insbesondere wenn Ihr Ziel hoch ist. So können verschiedene Methoden kombiniert werden:

  • Systemtest: Durch Ausführen Ihrer vorhandenen Systemtests mit instrumentiertem Code können Sie mit minimalem Zusatzaufwand einen großen Teil Ihrer Anwendung abdecken. Systemtests übersehen jedoch häufig defensive Codepfade, die nur unter Fehlerbedingungen (wie Speicherbeschädigung oder Hardwarefehlern) ausgelöst werden. Dies führt in der Regel zu einer Abdeckung von etwa 60 %.
  • Unit-Tests: Die Erstellung gezielter Unit-Tests ist entscheidend, um diese defensiven Codepfade oder andere spezifische Szenarien zu testen, die von Systemtests nicht erkannt werden. Durch die Entwicklung von Unit-Tests, die auf bestimmte Bedingungen zugeschnitten sind, können Sie Ihre Gesamtabdeckung deutlich erhöhen.
  • Manuelles Testen: Sogar manuelle Tests können zu Abdeckungsdaten beitragen, wenn sie mit instrumentiertem Code ausgeführt werden.

Moderne Tools ermöglichen die Zusammenführung der Abdeckungsergebnisse aus diesen verschiedenen Testmethoden. Dadurch erhalten Sie eine konsolidierte Ansicht Ihrer gesamten Codeabdeckung und erfüllen Compliance-Anforderungen, die eine Kombination von Metriken zulassen.

Herausforderungen bewältigen

  • Nicht erreichbarer Code: Manchmal ist Code so strukturiert, dass er durch normale Tests nicht erreichbar ist (z. B. eine Endlosschleife gefolgt von einer return-Anweisung). In solchen Fällen ermöglichen Standards oft eine Abdeckung durch Inspektion. Das bedeutet, dass Sie visuell bestätigen können, dass der Code nicht erreichbar ist, oder ihn mithilfe von Debugging-Tools schrittweise durchgehen und dokumentieren können, warum er als abgedeckt gilt.
  • Code-Aufblähung: Durch die Instrumentierung kann sich die Größe Ihrer ausführbaren Datei erhöhen, sodass sie möglicherweise nicht auf ressourcenbeschränkte Zielhardware passt. Eine gängige Problemumgehung besteht darin, die Hälfte des Codes zu instrumentieren, Tests auszuführen, die Abdeckung zu erfassen, dann die erste Hälfte zu deinstrumentieren, die zweite Hälfte zu instrumentieren, Tests auszuführen und die Abdeckungsergebnisse zusammenzuführen.
  • Assembly-Code-Abdeckung: Für höchste Sicherheitsstufen (wie DO-178C DAL A) kann eine Abdeckungsanalyse auf Assembler- oder Objektcodeebene erforderlich sein. Dies liegt daran, dass Compiler und Linker Code generieren können, der nicht direkt auf die Quelle zurückgeführt werden kann. Spezialisierte Tools können den Prozess der Erfassung der strukturellen Abdeckung aus ausführbarem Objektcode automatisieren.

Integration der Abdeckung in CI/CD

Für die moderne Entwicklung ist die Integration der Codeabdeckung in Ihre Continuous Integration/Continuous Delivery (CI/CD)-Pipeline entscheidend. Tools können Ihre Anwendung im Rahmen des Build-Prozesses instrumentieren, Rohdaten zur Codeabdeckung während automatisierter Tests erfassen und diese Daten anschließend in Ihrer IDE oder einem zentralen Dashboard (wie der DTP – Development Testing Platform von Parasoft) protokollieren.

Dies ermöglicht kontinuierliches Feedback zu Codequalität und -risiken und ermöglicht so eine bessere Entscheidungsfindung während des gesamten Entwicklungslebenszyklus. Integrationen mit gängigen CI/CD-Tools wie Jenkins, GitLab und Azure DevOps sind üblich.

Fazit

Strukturelle Codeabdeckung ist ein wichtiges Verfahren zur Gewährleistung der Qualität, Sicherheit und Zuverlässigkeit von Software, insbesondere in regulierten Branchen. Durch das Verständnis der verschiedenen Abdeckungsarten, die Nutzung von Automatisierung und die Integration der Abdeckungsanalyse in Ihren Entwicklungsworkflow können Sie Compliance-Anforderungen effektiv erfüllen und robustere Software erstellen.