Das bislang betrachtete Stop-&-go-Debugging birgt aber auch Gefahren. Hier ein Beispiel: Zwei Tasks initiieren jeweils ein Ereignis, wobei die Reihenfolge dieser Ereignisse entscheidend ist.
Auf einem Single-Core-System stellt der Entwickler fest, dass die beiden Ereignisse offensichtlich in falscher Reihenfolge ausgelöst werden und bemüht den Debugger, die Ursachen dafür zu finden. Der Debugger führt einige Debug-Operationen wie das Auslesen von Speicher- oder Registerinhalten aus. Die dazu notwendige Kommunikation über die Debug-Schnittstelle konkurriert mit der normalen Programmabarbeitung und beeinflusst das Laufzeitverhalten. Am Fehlerbild ändert sich jedoch nichts. Auch während der Debug-Sitzung ist die Reihenfolge der Ereignisse immer noch falsch (Bild 4, oben).
Bei einem Multi-Core-System kann das wiederum ganz anders aussehen. Beide Tasks werden auf unterschiedlichen CPUs ausgeführt und produzieren das gleiche Fehlerbild. Der Debugger beeinflusst durch die Kommunikation mit dem Controller ebenfalls das Laufzeitverhalten und könnte aber bei unterschiedlichem Zeitverhalten der Debug-Operationen auf den einzelnen Prozessorkernen das Fehlverhalten der Anwendung „korrigieren“ (Bild 4, unten). Solche Probleme, die eng mit der Nebenläufigkeit zusammenhängen, sind oftmals nur schwer reproduzier- und beobachtbar. Nicht selten werden Fehler durch den Debugger einfach verdeckt oder aber bislang nicht beobachtbare Fehler kommen ans Licht. Es ist also wichtig, den Einfluss des Debuggers auf das Systemverhalten möglichst gering zu halten.
Am effizientesten lässt sich dies mit Trace realisieren. Wie auch für die synchrone Ablaufsteuerung ist für Trace jedoch spezielle Hardware auf dem Chip erforderlich, um die Befehlsausführung der einzelnen CPUs oder gar Datentransfers zwischen den Cores aufzuzeichnen. Über eine Trace-Schnittstelle werden die gesammelten Daten an den Debugger übertragen, wo sie anschließend analysiert werden. Neben der nicht vorhandenen Beeinflussung des Laufzeitverhaltens durch den Debugger ist der große Vorteil von Trace, dass man nicht nur einen bestimmten Zeitpunkt z.B. an einem Breakpoint betrachtet, sondern auch in die Vergangenheit zurückblicken kann. Fehler, die ihre Ursache nicht unmittelbar am Breakpoint selbst haben, können so wesentlich leichter gefunden werden.
Leider gibt es auch beim On-Chip-Trace eine Kehrseite der Medaille: Die aufgezeichneten Trace-Daten müssen nämlich noch an den Debugger übertragen werden. Selbst wenn spezielle Trace-Ports schon in naher Zukunft mit Transferraten von bis zu 10 Gbit/s aufwarten können – mit zunehmender Taktfrequenz und steigender Anzahl der zu beobachtenden Prozessorkerne könnte das Datenaufkommen schon bald Übertragungsraten von 100 Gbit/s erforderlich machen. Um dieser Diskrepanz Herr zu werden, braucht es also andere Lösungen, als Trace-Daten einfach direkt vom Chip herunter zu schieben.
Die meisten modernen On-Chip-Trace-Architekturen behelfen sich derzeit mit einem Trace-Speicher direkt auf dem Chip und entsprechend hohen internen Übertragungsraten. In diesem Fall reicht eine gewöhnliche Debug-Schnittstelle aus, um die zwischengespeicherten Daten zu übertragen. Allerdings ist die Größe dieses Speichers mit maximal ein bis zwei Mbyte sehr begrenzt und die mögliche Aufzeichnungszeit damit kurz.
Eine interessanter Ansatz, den limitierten Trace-Speicher trotzdem optimal zu nutzen, sind Filter- und Trigger-Mechanismen, wie sie beispielsweise in der Infineon Multi-Core Debug Solution (MCDS) realisiert sind. Der Grundgedanke hierbei ist, den Trace nur auf die wirklich interessanten Stellen, z.B. den Zugriff auf eine Variable oder das Betreten einer Reservierungs-Routine, zu beschränken. Letztlich hilft diese Vorverarbeitung auf dem Chip auch bei der Auswertung. Man muss die Fehlerstelle nicht mehr aufwändig suchen.