Einer der Gründe für den hohen Zeitaufwand beim Debugging: Vielen Entwicklern ist gar nicht klar, wie leistungsfähig Debugger-Tools sind. Kommen nur Code-Breakpoints (Bild 1) und printf-Anweisungen zum Einsatz, wird es mitunter sehr schwierig, schwer nachvollziehbare Probleme wie verschachtelte Interrupts oder Exceptions aufzuspüren, insbesondere wenn diese printf-Anweisungen das Timing des Codes ändern und Fehler nur schwer reproduzierbar machen. Der Beitrag untersucht, wie Entwickler mit ausgefeilteren Debugging-Methoden ihre Embedded-Systeme untersuchen und damit den Zeitaufwand für das Debugging minimieren können.

Log-Breakpoints

Bild 1: Code-Breakpoints gehören zu den meistverwendeten Debugging-Techniken.

Bild 1: Code-Breakpoints gehören zu den meistverwendeten Debugging-Techniken. IAR

Ein Beispiel für einen Haltepunkttyp ist der „Log Breakpoint“ Er entspricht einer printf-Anweisung, bis auf die Tatsache, dass er den Code nicht mit Anweisungen überfrachtet, um Debug-Informationen aus dem Kern herauszuholen. Ein Log-Haltepunkt verwendet einen normalen Hardware-Haltepunkt, und jedes Mal, wenn die Entwicklungstoolchain erkennt, dass der Breakpoint erreicht wird, lässt der Debugger eine Nachricht im Protokollfenster erscheinen. Log-Breakpoints können auch Werte vom Mikrocontroller erfassen, sodass ersichtlich wird, wie sich dieser Wert im Laufe der Zeit verändert. Sie können auch Statusmeldungen oder Signale auslösen, damit der Nutzer den Fortschritt seines Codes verfolgen kann. Entscheidend ist aber, dass Log-Breakpoints weniger störend sind als eine printf-Anweisung und das Timing des Codes weniger negativ beeinflussen.

Bedingte / komplexe Haltepunkte

Eine weitere Herausforderung beim Einsatz von Standard-Code-Haltepunkten besteht darin, dass die Anwendung jedes Mal stoppt, wenn die betreffende Codezeile durchlaufen wird, auch wenn der Nutzer während dieser Iteration in der Codezeile gar nicht stoppen möchte. Mit professionellen Entwicklungstools lässt sich ein Code-Haltepunkt in einen bedingten oder komplexen Haltepunkt umwandeln. Es kann vorkommen, dass der Nutzer möchte, dass der Debugger den Kern nur jedes X.Mal stoppt, wenn er durch eine bestimmte Codezeile geht (wobei X eine individuell gewählte ganze Zahl ist). In diesem Fall lässt sich ein Skip-Zähler für den Haltepunkt festlegen, um dem Debugger mitzuteilen, wie oft er den Breakpoint in dieser Codezeile überspringen soll, bevor der Kern tatsächlich angehalten wird. Im Breakpoint-Fenster der Entwicklungstools ist nicht nur der bedingte Ausdruck, den der Nutzer setzt, sichtbar, sondern auch, wie viele Male noch übrig sind, bis der Breakpoint tatsächlich einen Stopp des Kerns auslöst (Bild 2).

Der Nutzer kann auch einen bedingten Haltepunkt setzen, der es ihm ermöglicht, eine Reihe von Bedingungen aufzubauen, die erfüllt sein müssen, bevor der Debugger den Kern stoppt. Das entspricht der Art und Weise, wie bedingte Anweisungen aufgebaut sind, die in einer if- oder while-Anweisung in C ausgewertet werden. Dabei lassen sich mehrere Klauseln der Anweisung zusammen mit logischen OR- und AND-Operationen zusammenfassen, um genau die richtige Reihenfolge von Ereignissen aufzubauen, die den Haltepunkt nur dann auslöst, wenn der Nutzer untersuchen möchte, was im Code vor sich geht. Dies spart Zeit, da der Nutzer die spezifischen Fälle im Code untersuchen kann, die dabei helfen, Probleme schnell zu erkennen.

Mehr Breakpoints per Debug-Probe

Bild 2: Mit professionellen Entwicklungstools lassen sich Code-Breakpoints in bedingte oder komplexe Haltepunkte umwandeln.

Bild 2: Mit professionellen Entwicklungstools lassen sich Code-Breakpoints in bedingte oder komplexe Haltepunkte umwandeln. IAR

Bild 3: Mit Data-Breakpoints lässt sich der Wert von Variablen überwachen und der Kern anhalten, wenn eine Variable aus dem Code gelesen oder in sie geschrieben wird.

Bild 3: Mit Data-Breakpoints lässt sich der Wert von Variablen überwachen und der Kern anhalten, wenn eine Variable aus dem Code gelesen oder in sie geschrieben wird. IAR

Alle diese Methoden verwenden jedoch Hardware-Haltepunkte – und dem Nutzer steht nur eine begrenzte Anzahl davon über die Debug-Schnittstelle seines Mikrocontrollers zur Verfügung. Was also, wenn mehr davon notwendig sind? Glücklicherweise gibt es darauf eine Antwort: Mit dem Einsatz einer hochentwickelten Hardware-Debug-Probe erhält der Nutzer eine unbegrenzte Zahl an Flash-Breakpoints zu seiner Verfügung. Hier fügt die Hardware-Probe einen Software-Breakpoint-Befehl genau dort ein, wo ein Stopp des Codes gewünscht ist. Bei Erreichen dieses Breakpoints und fortgesetzter Programmausführung wird die ursprüngliche Instruktion aus dem RAM ausgeführt, bevor die weitere Programmausführung wieder aus dem Flash läuft. Eine weitere praktische Möglichkeit ist die Platzierung der zu debuggenden Funktion im RAM. Daraus ergibt sich eine unbegrenzte Anzahl an Software-Haltepunkten. Professionelle Entwicklungstools erlauben es dem Entwickler, Code schnell in den RAM zu schreiben, indem er im Code Schlüsselwörter wie __ramfunc einbaut. Diese veranlassen den Startcode des Mikrocontrollers dazu, die Funktion beim Systemstart vom Flash in den RAM zu kopieren. Jedes Mal, wenn eine Codezeile diese Funktion aufruft, wird auf die RAM-basierte Funktion verwiesen. Auf diese Weise lassen sich so viele Haltepunkte setzen wie benötigt. Nach dem erfolgreichen Debuggen muss das Schlüsselwort in der Funktionsdeklaration nur entfernt werden und der Nutzer erhält wieder eine Flash-basierte Funktion.

Auch praktisch: Data-Breakpoints

Ein weiterer Haltepunkttyp ist der Data-Breakpoint (Bild 3). Dieser ermöglicht es dem Nutzer, den Wert von Variablen zu überwachen, wenn sie sich im Laufe der Zeit ändern, und gibt ihm die Möglichkeit, den Kern anzuhalten, wenn diese Variable aus dem Code gelesen oder in sie geschrieben wird. Das ist hilfreich, denn es kommt vor, dass Daten im Code überschrieben werden ohne dass ersichtlich wird, welcher Teil des Codes sie überschreibt. Mit dieser Funktionalität lässt sich das Problem isolieren und der Verursacher im Code einfach feststellen. Darüber hinaus ermöglichen Datenhaltepunkte eine Protokollierung und Darstellung von Variablenwerten bei einer Änderung – und das direkt innerhalb der Toolchain. Diese Transparenz im System ist der Schlüssel zu einem effektiven Debugging, und diese Leistungsfähigkeit hilft dabei, Fehler schnell zu isolieren und zu beseitigen.

Wie profitiert der Code vom Einsatz ausgefallener Debugging-Tricks? Für den Anfang bedeutet es schon mal weniger Fehler im Code zum Release-Zeitpunkt. Außerdem sparen die hier erläuterten Ratschläge Debugging-Zeit: Denn wenn der Entwickler 40 Prozent seiner Zeit mit dem Debuggen verbringt, sind das über 3,5 Stunden am Tag. Wenn er sich nur 30 Minuten pro Tag an Zeitaufwand fürs Debugging sparen kann, bedeutet das eine Ersparnis von einem ganzen Tag pro Monat für den Rest seines Entwicklerlebens. Diesen Tag kann er nutzen, um für sein Unternehmen schneller neue Produkte auf den Markt zu bringen oder um zusätzliche Funktionen in sein Produkt einzubauen und damit den Wettbewerb auszustechen. Und: Welcher Software-Entwickler könnte nicht ein paar zusätzliche Tage in seinem Zeitplan gebrauchen?