Das Entwanzen von OpenGL-Anwendungen bereitet Kopfschmerzen. Der Allzweckreiniger GDB liefert meist zu wenig hilfreiche Informationen, ganz zu schweigen von dem fehlenden Einblick in Shader und Texturen. Zeit also für einen Debugger, der OpenGL bei den Hörnern packt.
Seit nunmehr über zwei Jahren bastelt Bruce Merry an einem kostenlosen und vollständig auf OpenGL-Anwendungen spezialisierten Debugger. Das Bugle (englisch für Jagdhorn) getaufte Werkzeug setzt sich als Wrapper-Bibliothek zwischen das zu untersuchende Programm und die OpenGL-Bibliothek, um die anfallenden Funktionsaufrufe zu überwachen. Auf Grund dieses Tricks arbeitet es sogar mit bereits fertigen Anwendungen ohne eingearbeitete Debug-Informationen.
Je nach Bedarf protokolliert Bugle die gesammelten Informationen, erzeugt Statistiken (beispielsweise über die Framerate), aktiviert nach jedem Funktionsaufruf »glGetError()«, um den Fehlerkanal auszulesen, erzwingt einen Wireframe-Modus, schießt Screenshots oder zeichnet Videos auf. Als Bonus gibt es zusätzlich noch ein GDB-ähnliches Hilfsprogramm, und zwar einschließlich einer grafischen Oberfläche, die auch Shader und Texturen inspiziert.
Allerdings sollten Anwender nicht zu früh jubeln: Trotz der langen Entwicklungszeit befindet sich Bugle noch immer in einem frühen Alphastadium. Auch wenn vieles schon stabil läuft, tauchen immer wieder vereinzelt Probleme auf. Das gilt insbesondere bei neuen Grafikkarten oder herstellerspezifischen OpenGL-Erweiterungen.
Funktionsweise
Bugle gibt es derzeit nur als Quellcode-Paket über die offizielle OpenGL-Seite unter [1]. Die Voraussetzungen für die Übersetzung nennt der Kasten “Abhängigkeiten”. Die anschließende Übersetzung und Installation erfolgt mit dem bekannten Dreisatz »./configure; make; make install« und einem sicherheitshalber nachgeschobenen »ldconfig«.
|
Abhängigkeiten |
|---|
|
Bugle selbst ist recht genügsam. Für die Übersetzung des Quellcode und den Betrieb setzt es lediglich folgende Pakete voraus:
|
Bugle hängt sich zwischen die Anwendung und die OpenGL-Bibliothek »libGL«. Damit dies auch bei jeder fertigen Anwendung klappt, verbündet sich Bugle mit dem dynamischen Linker. Dieser erlaubt es über die Umgebungsvariable »LD_PRELOAD«, weitere, dynamische Bibliotheken in das Programm zu laden. Bei diesem als Library Interposition bezeichneten Verfahren biegt der Linker alle passenden Funktionsaufrufe auf die eingeschmuggelte Bibliothek um.
Wie am Fließband
Sobald Bugle auf diese Weise aktiviert ist, fängt es jeden OpenGL-Funktionsaufruf ab. Er durchläuft dann eine oder mehrere von Bugles Bearbeitungsstationen, bevor er schließlich wieder an OpenGL durchgereicht wird.
Bildlich kann man sich diesen Vorgang wie ein Fließband vorstellen, auf dem der Funktionsaufruf an mehreren Arbeitern vorbeiläuft. Jeder dieser Arbeiter erledigt eine bestimmte Aufgabe, beispielsweise “Schieße Screenshot” oder “Notiere den Aufruf in einem Log”. Anschließend reicht er den Funktionsaufruf an seinen Nachfolger weiter. Einen solchen Arbeiter bezeichnet Bugle als Filter-Set (kurz Filter), das zusammen am Fließband eine Chain (Kette) bildet.
Kettenbildung
Um Bugle in der Praxis einzusetzen, muss als erste Maßnahme eine solche Kette her. Sie definiert die Konfigurationsdatei »filter«, die man im ersten Schritt in dem versteckten Verzeichnis »$HOME/.bugle/« mit einem herkömmlichen Texteditor manuell anlegt. In diese neu erzeugte Datei ist dann am Anfang eine leere Chain zu platzieren:
chain meinekette
{
}
Der Name, in diesem Fall »meinekette«, ist frei wählbar. Wie das Beispiel zeigt, ist die Notation leicht von C-angehaucht, die Kommentare beginnen ebenfalls stets mit einer Raute »#«.
Fließbandarbeit
Damit steht das Fließband, an das sich nun die Arbeiter – pardon, die Filter – setzen. Sie landen in der Reihenfolge ihrer Bearbeitung in den geschweiften Klammern:
chain meinekette
{
filterset trace
filterset showerror
filterset log
}
Dem Schlüsselwort »filterset« folgt hierbei der Name des Filters. Welche Filter insgesamt der Debugger überhaupt kennt, listet der Befehl
BUGLE_CHAIN=help LD_PRELOAD=libbugle.so Programm
auf der Kommandozeile auf (Abbildung 1). Genauere Informationen über einen Filter liefert die zugehörige Manpage. Sie trägt den Namen des Filters mit dem Präfix »bugle-«. Die Dokumentation zum »trace«-Filter steckt somit hinter »man bugle-trace«. Alternativ gibt es alle Informationen online unter [2].

Abbildung 1: Die bei Redaktionsschluss aktuelle Bugle-Version kannte diese Filter, die Auswahl reicht vom Screenshot über die Wireframe-Darstellung bis zum Logging.
Ausgabe aufzeichnen
In dem Beispiel zeichnet das Fließband »meinekette« zunächst den durchlaufenden OpenGL-Funktionsaufruf auf (»filterset trace«), prüft dann, ob ein Fehler vorliegt (»filterset showerror«), der über »stderr« ausgegeben würde, und schließlich schiebt die letzte Stufe der Kette (»filterset log«) die Informationen in eine Datei. Besser wäre festzulegen, um welche es sich dabei genau handelt, sonst landet alles wieder auf »stderr«:
filterset log
{
filename "/tmp/bugle.log"
flush "yes"
}
Die Parameter eines Filters wandern als Wertepaare der Form »Parametername “Wert”« wieder in geschweifte Klammern. Im Beispiel lautet der Name der Logdatei »/tmp/bugle.log«. Das eingeschaltete »flush« sorgt dafür, dass Bugle nach jedem Funktionsaufruf spült, also die Datei aktualisiert. Damit ist garantiert, dass das Log auch nach einem möglichen Absturz keine Lücken aufweist. Die vollständige Konfigurationsdatei zeigt noch einmal Listing 1.
|
Listing 1: |
|---|
01 chain meinekette
02 {
03 filterset trace
04 filterset showerror
05 filterset log
06 {
07 filename "/tmp/bugle.log"
08 flush "yes"
09 }
10 }
|
Theoretisch könnte man nun noch weitere Chains nach dem gleichen Muster definieren. Wie so etwas aussieht, zeigt die gut dokumentierte Beispielkonfiguration im Unterverzeichnis »doc/examples/filters« des entpackten Quellcode-Archivs. Sie bietet auch einen idealen Ausgangspunkt für eine eigene Fassung.
Schichtbeginn
Für das Beispiel reicht die kleine Chain aus. Jetzt bleibt nur noch das OpenGL-Programm unter Angabe der Bugle-Bibliothek zu starten:
$ BUGLE_CHAIN=meinekette LD_PRELOAD=/usr/local/lib/libbugle.so Programm
Hinter »BUGLE_CHAIN« steht der Name der Chain, die bei der Programmausführung zum Einsatz kommen soll. Fehlt diese Angabe, dann verwendet Bugle die erste Chain in der Konfigurationsdatei. Alternativ zu »LD_PRELOAD« lässt sich die Bugle-Bibliothek auch statt der GL-Bibliothek direkt an das eigene Programm linken. Dazu tauscht der Programmierer im Compiler-Aufruf »-lGL« gegen »-lbugle« aus, wobei eventuell noch Pfade anzupassen sind.
Bugle führt nun die OpenGL-Anwendung aus und arbeitet im Hintergrund die ihm zugewiesenen Aufgaben ab, ein Beispiel zeigen die Abbildungen 2a und 2b. Je nachdem, welche und wie viele Filter im Spiel sind, wird die OpenGL-Anwendung dabei spürbar langsamer. Sie kann durchaus auf ein Drittel oder noch weniger ihrer Originalgeschwindigkeit absacken.
Ebenfalls zur Bugle-Familie gehört der mitgelieferte Debugger Gldb, der wiederum auf Bugle aufsetzt. Nach seinem Start per »gldb Programm« erscheint eine Kommandozeile, die analog zum bekannten Debugger GDB funktioniert (Abbildung 3).
Nachwuchs-Debugger
Im Gegensatz zu seinem Vorbild beschränkt Gldb seine Tätigkeit auf das Setzen von Breakpoints, die Analyse von Backtraces und des OpenGL-Status sowie die Aktivierung und Deaktivierung der Bugle-Filter. So setzt »break glfunction« einen Breakpoint bei der OpenGL-Funktion »glfunction()«. Nach einem »run« hält das Programm folglich immer dann an, wenn diese Funktion aufgerufen wurde. Ein »step« wandert im Gegensatz zu »run« bis zur nächsten OpenGL-Anweisung. Eine Chain aktiviert der Befehl »chain Name«.
Gldb konzentriert sich ausschließlich auf den OpenGL-Teil. Da dies in vielen Situationen nicht ausreicht, kann der Programmierer über den Befehl »gdb« zusätzlich den gleichnamigen herkömmlichen Debugger zu Hilfe holen. Dabei sollte er allerdings beachten, dass die zu entwanzende Anwendung weiterhin unter der Kontrolle von Gldb steht. Aus diesem Grund darf er auch nicht der Versuchung erliegen und das Programm innerhalb von GDB killen. Dieser Vorgang würde unter Umständen von Gldb unbemerkt bleiben. Alle weiteren Befehle nennt die Manpage von Gldb oder die Internetseite unter [2].
|
Arbeitsverweigerung |
|---|
|
Einige ältere Anwendungen müssen eventuell zur Zusammenarbeit mit Bugle überredet werden. Dazu zählen beispielsweise alle Programme, die die SDL nutzen. Hier hilft ein Aufruf wie: LD_PRELOAD=/Pfad/zu/libbugle.so SDL_VIDEO_GL_DRIVER=/Pfad/zu/libbugle.so SDL-Anwendung Auch Quake 3 und Doom verlangen nach einem angepassten Befehl: LD_PRELOAD=/pfad/zu/libbugle.so quake3 +set r_glDriver /Pfad/zu/libbugle.so Mit dem Preloading-Trick sollten sich die meisten zu untersuchenden Programme in den Griff bekommen lassen. |
Live und in Farbe
Wer es etwas bunter mag, greift auf die nagelneue GUI-Fassung des Gldb zurück. Im Gegensatz zu ihrem Kommandozeilenkollegen ist sie gerade frisch ins Bugle-Paket gekommen. Den Mut, sie zu starten, belohnt sie mit einem zusätzlichen Betrachter für Texturen und Shader (Abbildung 4).

Abbildung 4: Die grafische Variante »gldb-gui« des Debuggers Gldb kennt zwar ein paar zusätzliche Funktionen, steht allerdings noch am Anfang ihrer Entwicklung.
Bugle bietet eine enorme Erleichterung beim Debuggen von OpenGL-Anwendungen. Allerdings merkt der Benutzer an vielen Stellen, dass Bugle noch eine kleine Baustelle ist. So verschluckt sich beispielsweise Gldb gerne noch an Threads. Daher ist dringend die Lektüre, der im Bugle-Archiv mitgelieferten Troubleshooting-Datei zu empfehlen.
Bald schon bekommt Bugle Konkurrenz: Mit einem Programm namens GDE Bugger steht ein weiterer, grafischer Debugger für OpenGL in den Startlöchern, der den Funktionsumfang von Bugle noch einmal übertreffen möchte [3]. (ofr)
|
Infos |
|---|
|
[1] Bugle: [http://www.opengl.org/sdk/tools/BuGLe] [2] Bugles Online-Dokumentation: [http://www.opengl.org/sdk/tools/BuGLe/documentation/index.php] [3] GDE Bugger: [http://www.opengl.org/sdk/tools/gDEBugger] |








