Effiziente Zielverfolgung

Auf dem PC sind Logging-Frameworks Standard. Doch wirklich hilfreich wäre solch ein konfigurierbarer Echtzeit-Tracer auf einem Embedded-System!

Probleme eines Embedded-Entwicklers
Immer wieder begegne ich beim Debuggen auf einem Embedded-Gerät Herausforderungen. Der Debugger ist nicht gerade zur Hand oder beeinträchtigt den Code so, dass das Problem nicht mehr auftritt. Da macht man gerne mal Gebrauch vom guten, alten printf (Traces). Bei ein paar wenigen Traces geht das ganz ordentlich, wird aber das Programm komplexer, sehe ich vor lauter Bäumen den Wald nicht mehr. Zudem nehmen die Traces viel Programmspeicher in Anspruch und beeinflussen die Laufzeit meines Programms erheblich.

Möglicher Lösungsansatz
Um Platz auf dem Gerät zu sparen, wird der statische Teil des Traces, also der Format-String, nicht in den Programmcode integriert, sondern in eine separate Datei geschrieben, welche nicht auf das Gerät geladen wird. Übertragen wird dann nur noch eine ID, womit der entsprechende String in der Datei wiedergefunden werden kann. Ein String, der solange ist wie dieser Blogbeitrag, würde auf wenige Bytes reduziert.

Das Ausführen von printf auf dem Gerät ist zeitintensiv, da die Argumente zur Laufzeit ausgewertet werden. Durch die Verwendung eines binären Protokolls für die Datenübertragung werden die Argumente ohne weitere Verarbeitung direkt übertragen. Dies reduziert den Rechen- und somit Zeitaufwand auf dem Gerät und man erreicht eine hohe Übertragungseffizienz. Die printf-Ausgabe wird schliesslich auf dem PC zusammengestellt.

Die Datenübertragung über eine serielle Schnittstelle benötigt immer noch relativ viel Zeit, was das Verhalten des Programms beeinflusst. Speziell in einem Interrupt-Kontext kann das das Verhalten des Programms verändern. Durch Verwendung einer Queue, die im Hintergrund abgearbeitet wird, ist die Erzeugung des Traces von der Datenübertragung entkoppelt. Damit der zeitliche Ablauf der Traces trotzdem nachvollzogen werden kann, wird beim Erzeugen des Traces ein Zeitstempel aus einem Hardwaretimer hinzugefügt.

Ein zeitlicher Vergleich zwischen einem regulären printf vom String «hello world» und einem Trace mittels entkoppelter Datenübertragung einer Trace-ID hat gezeigt, dass letzteres zirka 50-mal schneller ist.  Auf dem Embedded-Target sind das Prozessorzyklen, die wir in der eigentlichen Anwendung benötigen und nur ungern für Debug-Infos verschwenden.

Zur Bewältigung des Baum-Wald Problems können Trace-Module und Trace-Levels definiert und während der Laufzeit aktiviert/deaktiviert werden. Entsprechend werden nur die gewählten Traces generiert. So kann der Entwickler sehr gezielt den Teil des Programms debuggen, welcher gerade relevant ist und das ohne das Programm neu kompilieren zu müssen. Der Overhead, welcher durch das Analysieren der gewählten Module/Levels entsteht, beträgt zirka 10% gegenüber dem Erzeugen des Traces und ist somit sehr gering.

 

Tracing Tool in Action

Dieses Tracing-Framework ist eines der Hauptwerkzeuge, die ich während der Entwicklung auf Embedded-Geräten einsetze. Es steigert meine Arbeitseffizienz immens, da nebst den herkömmlichen Trace-Informationen noch zusätzliche Infos zur Verfügung stehen. Dank der Zeitinfo können Echtzeitprobleme analysiert werden. Da die Länge des Strings keinen Einfluss auf die Übertragung hat, kann die Datei und Codezeile des Trace-Aufrufs mitangezeigt werden.

Dank der Module und Levels, die farblich unterschiedlich dargestellt sind, behält man die Übersicht und kommt durch Anwenden von Filtern schnell zu den gewünschten Informationen. Probleme zu lösen ist unser Job. Dank diesem Framework kann ich mich auf die wirklichen Probleme fokussieren und erreiche so effizienter eine Lösung.

Kontaktiere uns