Hand in Hand für das Multicore-Debugging und -Tracing

Autor / Redakteur: Andrea Martin * / Franz Graser

Die Anzahl der Cores in Multicore-Prozessoren steigt stetig. Prozessor- und Debuggerhersteller müssen eng zusammenarbeiten, um ein effizientes Debugging zu ermöglichen.

Anbieter zum Thema

Beim Debugging von SMP-Systemen wird für alle Cores nur eine einzige Debugger-Instanz gestartet.
Beim Debugging von SMP-Systemen wird für alle Cores nur eine einzige Debugger-Instanz gestartet.
(Grafik: Lauterbach)

Viele moderne Geräte werden heute von einem Multicore-Prozessor gesteuert. Ingenieure, die solche Geräte entwickeln, müssen sich deshalb mit dem Thema Multicore-Debugging auseinandersetzen. Die optimale Aufgabenverteilung auf die Cores, der Umgang mit gemeinsam genutzten Ressourcen, die Gesamtperformance des Systems – all das muss getestet und verifiziert werden. Dieser Artikel stellt die wichtigsten Konzepte für das Multicore-Debugging vor und fasst gleichzeitig die Voraussetzungen zusammen, die ein Prozessor mitbringen muss, um ein effizientes Multicore-Debugging zu ermöglichen.

Die Basis für das Multicore-Debugging ist zunächst ein Debugger, der alle eingesetzten Cores unterstützt. Da die möglichen Core-Kombinationen vielfältig sind, muss der Tool-Hersteller breit aufgestellt sein. Seine Produktpalette sollte Standard-Cores, DSPs sowie eine Vielzahl von anwendungsspezifischen Cores umfassen.

Die vom Debugger angebotenen Testmöglichkeiten ergeben sich aus der im Prozessor implementierten Debug-Logik. Deshalb wollen wir uns zuerst diesem Thema zuwenden.

Für das Debugging stellen Prozessoren in der Regel eine JTAG-Schnittstelle zu Verfügung. Besitzt der Debugger vollständige Informationen über den Aufbau des Multicore-Prozessors, dann kann er über diese Schnittstelle auf die einzelnen Cores zugreifen. Dadurch kann er den Zustand des Gesamtsystems aus der Perspektive eines Cores auslesen und verändern sowie die Programausführung auf den Cores starten und stoppen. Damit man mit Hilfe eines Debuggers die Interaktion zwischen den einzelnen Cores zeitgenau testen kann, muss der Prozessor eine Synchronisationslogik bereitstellen, die es erlaubt, alle Cores zeitgleich zu starten und zu stoppen.

Features zur Analyse komplexer Systemfehler sowie zur Performanceoptimierung kann ein Debugger dann anbieten, wenn der Prozessor Logik enthält, die es erlaubt, prozessor-interne Abläufe für den Debugger sichtbar zu machen. Damit sind wir beim Thema Nachverfolgung (Tracen) angekommen. Für das Multicore-Debugging haben sich zwei Trace-Arten bewährt:

  • Core-Traces, die Informationen über die Programmausführung der einzelnen Cores generieren. Idealerweise ist im Prozessor für jeden Core ein solches Tracemodul implementiert.
  • Der so genannte System-Trace, der Diagnosedaten über prozessor-interne Abläufe aufsammelt und ausgibt. Dabei können die Diagnosedaten durch Code-Instrumentierung erzeugt werden oder von Hardware-Modulen wie etwa dem Power Management kommen.

Asymmetrisches versus symmetrisches Multiprocessing

Welches Debug-Konzept beim Multicore-Debugging zu Tragen kommt, hängt im Wesentlichen davon ab, ob die Embedded-Applikation als SMP- oder AMP-System organisiert ist.

Hat man ein SMP-System (SMP steht für Symmetric Multiprocessing), dann steht die Performancesteigerung durch Parallelisierung im Vordergrund. Statt von einem Core werden die Applikationsaufgaben von mehreren Cores bearbeitet. Die Verteilung der gleichzeitig anstehenden Applikationsaufgaben auf die Cores übernimmt das SMP-Betriebssystem. Die Zuweisung erfolgt dabei dynamisch zur Programmlaufzeit. Vereinfacht ausgedrückt bedeutet dies, dass eine zur Bearbeitung anstehende Aufgabe einem gerade freien Core zugewiesen wird.

Für SMP-Systeme muss man einen Prozessor verwenden, der für dieses Einsatzszenario entwickelt wurde. Ein solcher Prozessor enthält zwei oder mehr identische oder zumindest befehlssatz-kompatible Cores, die den Speicher und die Peripheriemodule gemeinsam nutzen können, sowie Mechanismen zur Cache Kohärenz.

Ganz anders verhält es sich bei AMP-Systemen (AMP steht für Asymmetric Multiprocessing). Hier erhofft man sich eine Performance-Steigerung, indem man die allgemeinen Applikationsaufgaben von einem Standard-Core bearbeiten lässt. Spezialaufgaben werden an anwendungsspezifische Cores vergeben. Die Aufteilung der Applikationsaufgaben auf die unterschiedlichen Cores ist hier statisch und wurde bereits in der Designphase des AMP-Systems vorgenommen.

In diesem Multicore-System werden die allgemeinen Applikationsaufgaben von einem SMP-System übernommen, das aus zwei Cortex-A9 Cores besteht, während ein DSP die Spezialaufgaben bearbeitet.
In diesem Multicore-System werden die allgemeinen Applikationsaufgaben von einem SMP-System übernommen, das aus zwei Cortex-A9 Cores besteht, während ein DSP die Spezialaufgaben bearbeitet.
( Lauterbach)

Zwar kommen AMP- und SMP-Systeme noch heute in ihrer Reinform vor. Weit häufiger sind aber AMP-Systeme, in denen die allgemeinen Applikationsaufgaben auf einem SMP-System laufen, während Spezialaufgaben wie etwa die Bildverarbeitung oder die Getrieberegelung von anwendungsspezifischen Spezialkernen ausgeführt werden.

Debug-Konzepte für SMP-und AMP- Systeme

Das gesamte SMP-System wird über eine einzige Instanz des Debuggers kontrolliert. Über diese Instanz werden alle Cores synchron gestartet und gestoppt. In einer GUI werden sowohl die gemeinsam genutzten Daten und Peripheriemodule als auch die core-spezifischen Eigenschaften des Systems wie Core Register, Stack Frame oder Caches dargestellt (siehe Bild am Anfang des Artikels)

Beim Debugging von AMP-Systemen wird für jeden Core eine eigene Debugger-Instanz gestartet.
Beim Debugging von AMP-Systemen wird für jeden Core eine eigene Debugger-Instanz gestartet.
(Grafik: Lauterbach)

Da erst zur Programmlaufzeit feststeht, welcher Core welche Aufgabe – im Folgenden sprechen wir von Tasks - bearbeitet, müssen die Breakpoints vom Debugger so gesetzt werden, dass sie für jeden Core gelten. Beim Stopp an einem Breakpoint blendet der Debugger den Zustand des Gesamtsystems immer aus der Perspektive des Cores ein, auf dem der Breakpoint zugeschlagen hat. Damit dies möglich ist, muss die Debug-Logik des Prozessors bei jedem Stopp eindeutig anzeigen, auf welchem Core welcher Breakpoint zum Stopp geführt hat.

Für das Debugging eines Tasks genügt es in der Regel, den Zustand des Gesamtsystems aus der Perspektive des Cores zu untersuchen, der diesen Task gerade bearbeitet. Dennoch muss der Debugger selbstverständlich die Möglichkeit bieten, jederzeit den Zustand anderer Tasks bzw. Cores zu inspizieren.

Beim Debugging von AMP-Systemen wird für jeden Core eine eigene Instanz des Debuggers gestartet, da jeder Core die ihm zugeteilte Aufgabe mit einem separaten Programm bearbeitet. Jede Debugger-Instanz visualisiert und modifiziert die Systeminformationen ihres Cores und setzt auch die Breakpoints so, dass sie nur für diesen Core gelten. Ob es ausreicht, dass jede Instanz nur ihren Core startet und stoppt, oder ob alle Cores synchron gestartet und gestoppt werden, sollte für den Anwender je nach Testszenario konfigurierbar sein.

Die Informationen zur Programmausführung, die von den einzelnen Core-Trace-Modulen erzeugt werden, müssen zur Programmlaufzeit an den Debugger übertragen werden. Dazu muss der Multicore-Prozessor über einen Off-Chip-Traceport mit angemessener Bandbreite verfügen. Die von parallelen Traceports erreichten Übertragungsraten reichen für moderne Prozessoren oft nicht mehr aus. Deshalb sind die meisten Multicore-Prozessoren heute mit seriellen Traceports ausgestattet.

Die Anzahl der Cores, deren Trace-Daten verlustfrei übertragen werden können, wird sowohl durch die Bandbreite des Traceports als auch durch die Menge an Trace-Daten bestimmt, die ein Core bei einer bestimmten Frequenz erzeugt. Erzeugt ein Core bei einer Frequenz von 1 GHz beispielsweise bis zu 2 GBit Trace-Daten pro Sekunde, kann ein Traceport mit einer Bandbreite von 12 GBit/s die Trace-Daten von maximal 6 Cores übertragen.

Diese Limitierung kann unter bestimmten Umständen umgangen werden:

  • Einzelne Cores sind weitgehend inaktiv (idle) und erzeugen kaum Trace-Daten.
  • Dem Traceport sind auf dem Prozessor große FIFOs vorgelagert, die Lastspitzen abpuffern können.
  • Die Core-Trace-Logik lässt sich so programmieren, dass nur für einen speziellen Programmabschnitt bzw. einen einzelnen Task Trace-Daten erzeugt werden.

Auf Debuggerseite werden alle Tracedaten gemeinsam in einem Trace-Speicher abgelegt. Dieser kann eigentlich nicht groß genug sein. Üblich sind heute meist 4 GByte. Auch bei der Analyse und Darstellung von Core-Trace-Daten unterscheidet der Debugger zwischen SMP- und AMP-Systemen.

Die Debug-Instanz, die für ein SMP-System gestartet wurde, hat Zugriff auf die Trace-Daten aller Cores. Somit ist es grundsätzlich möglich, das Laufzeitverhalten eines einzelnen Cores, aller Cores sowie des Gesamtsystems zu untersuchen.

Trace-Konzepte für SMP-und AMP-Systeme

Die Funktionslaufzeiten innerhalb des Tasks TASKRC1 werden analysiert. Auf welchem Core die einzelne Funktion gelaufen ist, spielt für die Auswertung keine Rolle.
Die Funktionslaufzeiten innerhalb des Tasks TASKRC1 werden analysiert. Auf welchem Core die einzelne Funktion gelaufen ist, spielt für die Auswertung keine Rolle.
(Bild: Lauterbach)

Für die Fehlersuche in einem Task bzw. für task-spezifische Laufzeitmessungen lässt sich die Trace-Information gezielt für einen einzelnen Task analysieren und darstellen. Stehen Fragestellungen wie „Von welchen Cores wurde mein Task bearbeitet?“ oder „Wie sieht die Laufzeit-Auslastung meiner Cores aus?“ im Vordergrund, dann ist es zweckdienlich, die Trace-Information für alle Cores gemeinsam darzustellen.

Für ein SMP-System wird angezeigt, wann welcher Task von welchem Core bearbeitet wurde.
Für ein SMP-System wird angezeigt, wann welcher Task von welchem Core bearbeitet wurde.
(Bild: Lauterbach)

Auch in einem AMP-System überträgt der Prozessor die Trace-Daten aller Cores über einen gemeinsamen Traceport. Die Trace-Hardware des Debuggers sorgt dafür, dass diese in der Empfangsreihenfolge im Trace-Speicher abgelegt werden. Dadurch bleibt der zeitliche Bezug der einzelnen Programmabläufe erhalten.

Um Laufzeitmessungen vornehmen zu können und um die Programmabläufe auf den einzelnen Cores zeitlich zueinander in Beziehung setzen zu können, erhalten die Trace-Daten beim Speichern in den Trace-Speicher einen Zeitstempel. Da durch das Aufsammeln der Trace-Daten im Prozessor und durch die Übertragungstechnik variable Verzögerungen entstehen können, stimmt dieser Zeitstempel nicht exakt mit dem ursprünglichen Programmablauf überein. Um exakte Zeitmessungen zu ermöglichen, bieten einige Multicore-Prozessoren heute schon die Möglichkeit einen prozessor-globalen Zeitstempel in die einzelnen Core-Traces zu integrieren.

Die zeitliche Synchronisation von Trace-Informationen erlaubt die Überprüfung, welche Programmausschnitte die parallel arbeitenden Cores zu einem bestimmten Zeitpunkt bearbeitet haben.
Die zeitliche Synchronisation von Trace-Informationen erlaubt die Überprüfung, welche Programmausschnitte die parallel arbeitenden Cores zu einem bestimmten Zeitpunkt bearbeitet haben.
(Grafik: Lauterbach)

Für die Auswertung der Trace-Daten bezieht jede Debug-Instanz aus dem Trace-Speicher die Informationen, die für den von ihr kontrollierten Core relevant sind. Um das Zusammenspiel der einzelnen Cores zu testen und möglichst schnell komplexe Systemfehler zu lokalisieren, bietet der Debugger die Möglichkeit die einzelnen Aufzeichnungen zeitlich korreliert darzustellen.

Neben den Core-Aktivtäten gibt es in einem Multicore-Prozessor weitere prozessor-interne Abläufe, die für das Multicore-Debugging von Interesse sein können. Informationen darüber sammelt ein so genannter System-Trace im Prozessor für die Ausgabe auf.

Diagnosedaten über die Zustandsänderungen von Mutexes werden durch Codeinstrumentierung erzeugt.
Diagnosedaten über die Zustandsänderungen von Mutexes werden durch Codeinstrumentierung erzeugt.
(Bild: Lauterbach)

Der System-Trace unterstützt hauptsächlich zwei Arten von Informationen:

  • Diagnosedaten im Printf-Stil, die durch Codeinstrumentierung erzeugt werden (siehe Bild links).
  • Diagnosedaten, die von der prozessor-internen Hardware-Überwachung erzeugt werden. Überwacht werden können beispielsweise Zugriffe auf einzelne RAM-Adressen oder die Aktivitäten in einer Clock Domain.

Ein Clock Management Monitor generiert Informationen zur Aktivität in einer Clock-Domain.
Ein Clock Management Monitor generiert Informationen zur Aktivität in einer Clock-Domain.
(Bild: Lauterbach)

* Andrea Martin ist Diplom-Informatikerin. Seit 1994 ist sie bei Lauterbach für das Training verantwortlich.

(ID:42675722)