Ein Assistenzsystem beschleunigt ein Fahrzeug unter bestimmten Parametern. Es wurde korrekt entwickelt und funktioniert fehlerfrei. Doch was, wenn der Entwickler das Fahrzeug in dieser Situation gar nicht beschleunigen, sondern abbremsen lassen wollte?
Automatisierte Testsysteme wie etwa die statische Code-Analyse leisten sehr gute Arbeit bei der Erkennung von objektiven Fehlern wie Syntaxfehlern, Buffer-Overflows oder Abweichungen von Standards. Schwieriger wird es hingegen bei »weichen« Kriterien wie der Intention des Entwicklers – in der Regel der Startpunkt eines umfassenden Funktions-Testings. Natürlich kann keine Software Gedanken lesen. Ein neuer Ansatz zielt nun jedoch darauf ab, die statische Code-Analyse zu unterstützen und Indizien zu erkennen, die auf Abweichungen von der eigentlichen Absicht des Entwicklers hindeuten können – ermöglicht durch ein schlankes Machine-Learning-Modell.
Ein falscher Klick und der Rechner stürzt ab. Was in vielen alltäglichen Fällen höchstens ärgerlich ist, kann im Embedded-Sektor im schlimmsten Fall zum Risiko für Menschenleben werden, sei es beim Software-gestützten Medizingerät im OP-Saal oder den vernetzten Assistenzsystemen im modernen Fahrzeug auf der Autobahn. Seit jeher steht daher in der Embedded-Software-Entwicklung die Sicherheit an oberster Stelle: Der ISO-Standard 26262 für funktionale Sicherheit lässt sich kaum ohne die strenge Einhaltung eines Kodierungsstandards erreichen. Die verbreitetsten, MISRA und Autosar, beinhalten zentrale Regeln für ein höchstmögliches Sicherheitsniveau des entwickelten Quellcodes. Doch mit steigender Komplexität von Embedded-Software erhöht sich somit auch der Prüfungs- und Testaufwand kontinuierlich: Nicht selten umfasst der Quellcode von Self-contained Systems in modernen Fahrzeugen mittlerweile 50 Millionen Zeilen und mehr. Code-Massen, die geprüft werden wollen – und für die eine umfassende Einhaltung der Compliance-Vorgaben sichergestellt sein muss.
Wie kritisch die Situation damit für viele Entwicklerteams geworden ist, verdeutlichen etwa die Erkenntnisse des diesjährigen Perforce-Reports »2024 State of Automotive Software Development«. In dessen Rahmen wurden knapp 600 Entwicklungsspezialisten weltweit zu ihrer täglichen Arbeit, wahrgenommenen Herausforderungen und zentralen Technologien befragt. 23 Prozent beklagen darin eine »zu komplexe« Code-Basis. Die Hälfte der Befragten (50 Prozent) bestätigte, dass die Sicherstellung aller ISO-26262-Anforderungen herausfordernd und zeitintensiv sei. Und jeder vierte Befragte (24 Prozent) stimmte sogar der Aussage zu: »Unsere Bemühungen im Bereich Testing sind nicht ausreichend, doch uns fehlt die Zeit, noch mehr zu testen.«
Viele Entwicklerteams stehen somit vor einem Dilemma: Die bestehenden Standards fordern (zurecht!), dass jede Code-Zeile auf Sicherheit geprüft und getestet wird. Gleichzeitig genügen die personellen Kapazitäten kaum, um dies für alle erstellten Softwarekomponenten erschöpfend zu bewerkstelligen. Die Mehrzahl der Entwicklerteams (67 Prozent der Umfrageteilnehmer) nutzt daher softwarebasierte Testing-Tools, etwa zur statischen Code-Analyse. Diese prüfen den Quellcode auf Coding-Richtlinien und -Vorgaben, idealerweise bereits während des Verfassens.
Statische Code-Analyse schöpft ihr volles Potenzial bei der Identifizierung von Fehlern aus, die durch objektive Kriterien erkannt werden können. Ebenso wichtig ist es jedoch, den Quellcode auf die Intention des Entwicklers hin zu prüfen, sprich: festzustellen, ob die entwickelte Komponente tatsächlich das tut, was sie tun soll. Hat ein Entwickler etwa ein Tool zum Komprimieren von Textdateien erstellt und funktioniert dieses vollkommen korrekt, wird die statische Code-Analyse keine Fehler feststellen – es liegen schließlich keine vor. Die eigentliche Intention des Entwicklers bestand jedoch darin, Textdateien zu verschlüsseln. Das Programm tut schlicht das Falsche – wenn auch auf korrekte Weise.
Ohne auf magische Art in die Köpfe der Entwickler zu blicken, schien hierfür bislang keine Lösung möglich. Mit maschinellem Lernen kristallisiert sich nun jedoch ein neuer Ansatz heraus, die Problematik solcher »weicher Fehler« anzugehen. Bei der sogenannten Sentiment-Analyse handelt es sich um einen ML-gestützten Algorithmus, der in der Lage ist, plötzliche Änderungen des Entwickler-Stils im Quellcode zu erkennen (Bild 1). Diese können Hinweise für Inkonsistenzen im mentalen Modell des Autors sein, die in der Folge Brüche in der Logik des Programmflusses und damit riskante Fehler zur Folge haben können.
Inkonsistenzen im mentalen Modell können unter anderem dadurch entstehen, dass ein Autor die jeweilige Funktion nicht vollständig verstanden hat oder erst nach einem zeitlichen Abstand daran weiterarbeitet. Auch können Inkonsistenzen durch verschiedene Beteiligte entstehen, die unterschiedliche Vorstellungen davon haben, was ein bestimmter Code-Abschnitt tut oder tun soll. Selbst zwischen menschlichen und künstlichen Beteiligten können Inkonsistenzen auftreten, etwa wenn Code-Teile modellgeneriert erstellt werden und menschliche Entwickler eine andere Vorstellung von der Funktion haben oder das Modell an sich missverstehen. Gerade im Automotive-Sektor ist dies ein relevantes Szenario, werden laut aktuellem Perforce-Report doch mittlerweile 47 Prozent des Codes in diesem Sektor modellgeneriert.
Die Sentiment-Analyse bedient sich eines Small-Language-Modells, das kein Training erfordert, keine Services von Drittherstellern und auf handelsüblichen Prozessoren ausgeführt werden kann, während die Ergebnisse zu jedem Zeitpunkt vollständig nachvollziehbar und erklärbar sind. Um plötzliche Stiländerungen innerhalb des Quellcodes zu erkennen, ermittelt sie den mathematischen Abstand zwischen dem mittleren Informationsgehalt (Entropie) eines Testfragments (z. B. eine bestimmte Code-Zeile) und eines Referenz-Samples (z. B. ein zusammenhängender Ausschnitt aus demselben Quellcode). Ist das Testfragment konsistent mit dem gewählten Referenzbereich im übrigen Quellcode, fällt der Abstand gering aus. Ein plötzlich ansteigender Abstand deutet hingegen auf eine unerwartete Änderung im Stil des Entwicklers hin, auf die es bei der Prüfung des Codes ein besonderes Augenmerk zu legen gilt.
Der Abstand der Entropien von Testfragment und Referenz-Sample wird mithilfe eines Small-Language-Modells ermittelt, das die jeweiligen Code-Bereiche nach zlib komprimiert. Das Small-Language-Modell lernt am Referenz-Sample die optimale Enkodierung für exakt diesen Inhalt. Mit genau dieser Enkodierung wird dann das Testfragment komprimiert. Je ähnlicher das Testfragment dabei dem Referenz-Sample ist, desto besser lässt es sich komprimieren. Illustrieren lässt sich dies an einem Referenz-Sample, das aus vielen Seiten englischem Text besteht: Gilt es auf dieser Basis nun, einen englischen Satz als Testfragment zu enkodieren, der in exakt derselben Weise bereits auf den englischen Textseiten vorkommt, ist eine optimale Kompression möglich. Etwas schlechter wird sich der Satz komprimieren lassen, wenn er zwar englisch ist, jedoch anders lautet. Doch er wird sich noch immer besser komprimieren lassen als ein gälischer Satz, und der gälische Satz wiederum besser als ein koreanischer (Bild 2).
Der Algorithmus stellt also für jedes Testfragment die Frage: »Wie unterschiedlich ist das hier von dem Vorangegangenen?« Im Rahmen der Analyse wird der Software-Code also nicht im Kontext seiner semantischen Bedeutung betrachtet, sondern wird (abhängig von der jeweiligen Meta-Konfiguration) in erster Linie als reine Zeichenfolge wahrgenommen – damit ist sie vollständig sprachagnostisch, funktioniert also mit allen Programmiersprachen. Konkret lassen sich damit Stiländerungen auf folgende Weise identifizieren: Beinhaltet ein Software-Code mehrere Schleifen, deren Header nach den Vorgaben von MISRA C:2023 Abschnitt 8.14 geschrieben sind, sowie eine weitere Schleife, die anders geschrieben ist, wird sich diese neue Schleife nicht so gut komprimieren lassen wie die vorherigen – der ermittelte Abstand wird höher ausfallen. So deutet die Analyse auf einen möglichen Bruch im mentalen Modell des Autors hin: Warum hat sich plötzlich der übliche Stil der Schleifen-Header geändert?
Entscheidend für die Qualität des Ergebnisses der Sentiment-Analyse ist die Wahl des richtigen Referenz-Samples. Auch wenn es zunächst naheliegend scheinen mag, den gesamten Quelltext als Referenz heranzuziehen, ist das in der Praxis nicht ratsam. Das gesamte Programm enthält auch das zu testende Fragment und macht daher die Änderungen in der Entropie zunichte. Auch ein inkrementell ansteigendes Fenster bestehend aus allen Zeilen vor dem Testfragment ist nicht sinnvoll, da es das Ergebnis verzerren würde: Eine Test-Zeile sehr früh im Programm hätte einen höheren Unterschied zum bisher Dagewesenen, aus dem einfachen Grund, dass es sich beim bisher Dagewesenen noch nicht um einen besonders repräsentativen Durchschnitt handelt.
Es empfiehlt sich daher, den Code beispielsweise in separate Blöcke zu teilen und auf dieser Basis mindestens zwei Sample-Fenster zu erstellen. So kann die Sentiment-Analyse stets dasjenige Fenster verwenden, von dem das Testfragment selbst nicht Teil ist. Alternativ lassen sich auch mehrere kleine Fenster zu einem Sample-Fenster verknüpfen, das sich dynamisch hinter dem zu testenden Fragment herschiebt. Auf diese Weise lässt sich eine höhere lokale Nähe zur jeweiligen Testzeile erzeugen, falls Stiländerungen noch granularer wahrgenommen werden müssen. Bei all diesen Überlegungen handelt es sich um Meta-Entscheidungen, die je nach Analyseart oder Programm modifiziert werden können, um optimale Ergebnisse zu erzielen.
Die statische Code-Analyse einer modernen Automotive-Software liefert nicht selten Massen an Fehlermeldungen und Warnungen zurück, die durchaus im zweistelligen Millionenbereich liegen können. Sie auf lineare Weise abzuarbeiten ist schlicht unmöglich. In Zeiten von Fachkräftemangel und schrumpfenden Entwicklerteams benötigen Reviewer daher Möglichkeiten, Fehler und Warnungen effizient zu priorisieren. Dabei kann die Sentiment-Analyse eine wertvolle Orientierung liefern.
Sie ermöglicht es, Warnungen nach Kriterien zu sortieren, die über reine Zeilennummern hinausgehen. Vielmehr können Warnungen beispielsweise mit einem Stilbruch-Score versehen werden, wodurch eine Sortierung nach »Interessantheit« möglich wird (Bild 3). Reviewer erhalten einen Überblick über die Hotspots, die besonders dringend oder sorgfältig geprüft werden sollten: etwa Warnungen zu besonders kritischen Fehlern oder zum Kern eines Problems. Wird dieser behoben, verschwindet eine Vielzahl anderer Meldungen vielleicht von ganz allein. Da darüber hinaus immer nur zu Beginn einer sich verändernden Entropie-Distanz gewarnt wird, erhält der Reviewer auch bei vielen aufeinanderfolgenden Zeilen mit ungewöhnlichen Abstandswerten nur eine einzige Warnung, sodass der Fokus auf das Wesentliche gewahrt bleibt.
Auch aus präventiver Sicht ergeben sich durch die Sentiment-Analyse Vorteile. Denn neben potenziellen Brüchen im mentalen Modell des Autors lassen sich auch weitere weiche Fehler, etwa die schlechte Wartbarkeit bestimmter Code-Teile, identifizieren, die mit klassischen Mitteln nur schwerlich erkannt werden können. Dabei kann es sich um Abschnitte handeln, die besonders aufgebläht und wortreich verfasst wurden oder viele Komponenten enthalten, die lediglich von anderer Stelle kopiert wurden und bei Bug-Fixes am ursprünglichen Ort übersehen werden könnten.
Ein Paradebeispiel für schwer wartbaren Code ist die übermäßige Verwendung von go-to-Statements in C++. Sie unterbrechen den normalen Programmfluss und erlauben es stattdessen, an eine beliebige Stelle im Code zu springen. Das kann die einfache Nachvollziehbarkeit des Programmablaufs stark erschweren und damit die Komplexität massiv erhöhen. Durch die Sentiment-Analyse lassen sich die Reviewer auf entsprechende kritische Stellen aufmerksam machen, sodass diese im Sinne der Einfachheit überarbeitet werden können. Das reduziert die Anhäufung von Technischen Schulden, aus denen sich in der Zukunft Probleme ergeben könnten.
Wie zentral dies gerade im Automotive-Bereich sein kann, zeigen Beispiele aus der Vergangenheit: So gingen bereits Fälle durch die Medien, in denen der Code in Fahrzeugsystemen derart komplex und daher schwer nachvollziehbar wurde, dass Testprozesse nicht jeden möglichen Fall in Gänze abdecken konnten. Dabei wurden auch Fehler übersehen, die zu unerwünschten und riskanten Fehlfunktionen von Fahrzeugen in der Praxis führten, bis hin zu unbeabsichtigten Beschleunigungen.
Die Sentiment-Analyse ist keine ML-basierte Wunderwaffe, die Fehlerquellen magisch verschwinden lässt. Sie kann und soll auch die klassische regelbasierte statische Analyse nicht ersetzen. Doch sie kann eine wertvolle Ergänzung sein. Sie ermöglicht es, kritische Stellen zu identifizieren, die auf klassischem Wege nicht erkennbar wären, ersetzt jedoch nie den Reviewer selbst. Ohne menschliches Zutun kommt sie nicht aus. Doch genau darin liegt ihre Stärke: Eine automatisierte Korrektur entsprechender Stellen durch KI oder ML würde de facto nur einen weiteren »Autor« Hand an den Quelltext legen lassen, mitsamt seinem individuellen »Verständnis« und mentalen Modell des Programms. In der Folge würde sich die Anzahl der potenziell kritischen Stilbrüche vervielfältigen, nicht verringern.
Anstatt einen endlosen Teufelskreis in Gang zu setzen, sollte die Lösung der Wahl daher immer darin bestehen, einen Menschen zu bitten, sich in den jeweiligen Code-Abschnitt einzudenken und ihn mit Verstand nachzuvollziehen, bevor Änderungen vorgenommen werden. Die Sentiment-Analyse ist ein hilfreiches Werkzeug, die Aufmerksamkeit der menschlichen Experten bestmöglich auf das zu lenken, was wirklich wichtig ist. So unterstützt sie überlastete Testing-Teams in Zeiten immer komplexer werdender Embedded-Systeme und hilft ihnen, die Sicherheit kritischer Software effizient zu erhöhen – ganz ohne hellseherische Fähigkeiten. (lb)
Kleiner ist besser |
---|
Den Text aus Quellcodedateien auf unerwartete Änderungen hin untersuchen – wäre das nicht auch eine Paradeanwendung für die derzeit viel beachteten Large-Language-Modelle? Können diese nicht ebenso gut auf Stilbrüche hinweisen? Tatsächlich sind die meisten Large-Language-Modelle zumindest nach aktuellem Stand der Technik genau für diesen Anwendungsfall weniger geeignet. Denn ihre Herangehensweise an Quellcode wäre hauptsächlich textlich: Je nach konkretem Modell wäre es kaum nachvollziehbar, zu welchen Teilen das Ergebnis eines Large-Language-Modells auf der Logik des Programms basiert, auf dem reinen Text des Quellcodes oder auf völlig irrelevanten Kriterien. Darüber hinaus wäre das Modell vortrainiert – und das auf Basis von Massen an Quellcode, die allesamt andere Zwecke erfüllen als das eigene Programm. Ein weiterer Aspekt, der Large-Language-Modelle für eine Sentiment-Analyse weniger geeignet macht, ist deren im Vergleich zum Small-Language-Modell derzeit verhältnismäßig noch recht kleines Kontextfenster. Moderne Automotive-Software umfasst Gigabytes an Daten, das Large-Language-Modell hätte jedoch stets nur mehrere tausend Zeilen in Erinnerung, die unmittelbar vor der Testzeile verarbeitet wurden. Wenn es im Quellcode daher auf etwas stößt, das interessant wäre, hätte es unter Umständen bereits lange vergessen, was es zuvor schon alles im selben Quellcode gesehen hat. Die kompressionsbasierten Small-Language-Modelle hingegen sind skalierbar: Es besteht keine Obergrenze dafür, wie viel inkludiert werden kann. Auch Gigabytes an Daten lassen sich verhältnismäßig leicht nutzen und bearbeiten. Darüber hinaus ist das Modell nicht durch ein vorangehendes Training an externen Quellcode-Dateien beeinflusst. Schließlich geht es nicht darum, etwas mit bestehendem Wissen zu vergleichen, sondern um das mathematische Verhältnis zwischen dem, was gerade analysiert wird, und dem, was zuvor in der Datei beinhaltet war. Das System lernt die optimale Enkodierung für jeden Input aufs Neue – und genau das ist gewünscht. (lb) |