Blog 3 Min. Lesezeit

Statische C++-Analyse: Bugs vs. fehleranfällige Situationen

Diesen Artikel teilen
C++ Static Analysis: Bugs vs. Bug-Prone Situations

Bei statischer Analyse geht es nicht nur darum, Bugs direkt zu finden, sondern auch fehleranfällige Situationen zu erkennen, die Lesbarkeit und Wartbarkeit des Codes beeinträchtigen können. Darüber hinaus kann statische Analyse viele weitere Eigenschaften des Codes untersuchen:

  • Code-Metriken: Methoden mit zu vielen Schleifen sowie if-, else-, switch- und case-Anweisungen werden beispielsweise schwer verständlich und damit schwer wartbar. Die Erfassung dieser Strukturen über die Metrik der zyklomatischen Komplexität ist eine gute Möglichkeit festzustellen, wann eine Methode zu komplex wird.
  • Abhängigkeiten: Sind die Klassen eines Programms stark miteinander verflochten, werden die Auswirkungen von Codeänderungen unvorhersehbar. Statische Analyse hilft dabei, solche Verflechtungen zwischen Klassen und Komponenten zu erkennen.
  • Unveränderlichkeit: Typen, die gleichzeitig von mehreren Threads verwendet werden, sollten unveränderlich sein. Andernfalls müssen Lese- und Schreibzugriffe auf ihren Zustand durch komplexe Locking-Strategien geschützt werden, die schnell schwer wartbar werden. Statische Analyse kann sicherstellen, dass bestimmte Klassen unveränderlich bleiben.
  • Toter Code : Toter Code kann sicher entfernt werden, weil er zur Laufzeit nicht mehr aufgerufen wird. Er kann nicht nur entfernt werden, sondern muss entfernt werden, da zusätzlicher ungenutzter Code unnötige Komplexität verursacht. Statische Analyse kann den Großteil des toten Codes eines Programms erkennen, wenn auch nicht vollständig.
  • Breaking Changes an APIs: Wenn Sie Ihren Kunden eine API bereitstellen, kann versehentlich ein öffentliches Member entfernt und dadurch deren Code beschädigt werden. Statische Analyse kann zwei Zustände eines Programms vergleichen und vor diesem Problem warnen.
  • API-Nutzung: Manche APIs müssen mit besonderer Sorgfalt verwendet werden. Eine Klasse mit zu entsorgenden Feldern sollte beispielsweise in der Regel selbst entsorgbar sein – außer die Lebensdauer des Feldes stimmt nicht mit der Lebensdauer der Klasseninstanz überein, was wiederum auf ein Designproblem hindeuten kann.

Es gibt viele nützliche Werkzeuge, um Bugs in einer C++-Codebasis zu erkennen, darunter Cppcheck, Clang und der Visual-Studio-Analyzer. Doch wie lassen sich fehleranfällige Situationen identifizieren?

Die Entwickler eines statischen Analysewerkzeugs können festlegen, welche Situationen als Bugs gelten. Bei fehleranfälligen Situationen ist das anders, denn deren Bewertung hängt von den Entscheidungen des jeweiligen Entwicklungsteams ab. Ein Team kann etwa eine Methode mit mehr als 20 Zeilen als komplex einstufen, während ein anderes die Grenze bei 30 Zeilen setzt. Ein Werkzeug zur Erkennung solcher Situationen muss daher eine Anpassung der entsprechenden Regeln ermöglichen.

Code als Daten: der beste Weg, fehleranfällige Situationen zu erkennen

Statische Analyse bedeutet, Quellcode auf verschiedene Eigenschaften hin zu untersuchen und darüber zu berichten. Philosophisch betrachtet bedeutet sie aber auch, Code als Daten zu behandeln. Für uns Anwendungsentwickler wirkt das zunächst ungewohnt, weil wir Quellcode vor allem als Anweisungen, Abläufe und Algorithmen betrachten. Gleichzeitig ist genau dieser Ansatz außerordentlich leistungsfähig.

Nach der Analyse einer Quelldatei lässt sich ihr AST extrahieren und daraus ein Modell mit zahlreichen nützlichen Informationen über den Code erzeugen. Dieses Modell kann anschließend mit einer SQL-ähnlichen Code-Abfragesprache abgefragt werden.

CppDepend stellt mit CQLinq eine leistungsfähige Code-Abfragesprache bereit, mit der sich die Codebasis wie eine Datenbank abfragen lässt. Entwickler, Designer und Architekten können eigene Abfragen definieren, um fehleranfällige Situationen gezielt zu erkennen.

Mit CQLinq lassen sich Daten aus Code-Metriken, Abhängigkeiten, API-Nutzung und weiteren Modellinformationen kombinieren, um sehr fortgeschrittene Abfragen für konkrete fehleranfällige Situationen zu definieren.

Here’s an example  of a CQLinq query that matches the most complex methods:

bugs

Zusammenfassung

Um Probleme in einer C++-Codebasis zu erkennen, ist es sinnvoll, mehrere Werkzeuge zu kombinieren: Einige finden konkrete Bugs, andere zusätzlich fehleranfällige Situationen. Mit CppDepend möchten wir die Stärken verschiedener Werkzeuge verbinden. Eigene Abfragen lassen sich einfach definieren; außerdem können Ergebnisse anderer statischer Analysewerkzeuge importiert und anschließend mit CQLinq abgefragt werden.

Diesen Artikel teilen