HomeMapIndexSearchNewsärchives"Linksäbout
[Top Bar]
[Bottom Bar]
[Photo of the Author]
Jose M. Laveda

M@ail an den Autor

Inhalt
Prolog
Was ist ein Debugger?
Was ist DDD?
Die grafische Oberfläche
Unten angefangen
Allgemeine Aufgaben
Schlußwort
Literaturverweise

Code debuggen mit ddd


Zusammenfassung:
Der Autor beschreibt die grundlegenden Funktionen von ddd, einem sehr guten grafischen Debugger.


Prolog

Ziel dieses Artikels ist es, den Lesern grundlegende Konzepte zu vermitteln. Er richtet sich sowohl an die Neulinge in Sachen Debugging, als auch an diejenigen, die auf der Suche nach einem Werkzeug mit ansprechender grafischer Oberfläche sind. Es gibt viel über Fähigkeiten und Robustheit des beschriebenen Debuggers (gdb) zu sagen, wie üblich wird aber versucht, sich nicht in komplexen Erklärungen zu verlieren, was dem Lerneffekt zugute kommen dürfte :)

Was ist ein Debugger?

" Es war einmal ein Programmierer, dessen einzige Aufgabe es war, einen Fehler in seinem Quelltext zu finden:

/*Code*/
(...)
loop
change_a_variable;
show_value_of_variable;
end_loop
(...)

Er sah sich gezwungen, diese Zeilen immer und immer wieder in seinen Kode einzufügen, wollte er doch die Werte der Variablen im Programm zur Laufzeit kennen. Es war eine undankbare Aufgabe, welche im das Leben schwer machte und die Kosten des Code Debugging war doppelt so hoch, wie die des Programmierens selbst (...)".

Wer sah sich nicht schon einmal in dieser Situation ? Oftmals versteckt sich irgendwo im Programm ein böser Fehler, alles wurde versucht und man ist an dem Punkt angelangt, an dem man sich sicher ist: "Es muß ein Fehler im Compiler sein!", ist doch so ziemlich alles ausprobiert worden...Und hier kommt der Debugger ins Spiel.

Ein Debugger erlaubt die schrittweise Ausfühtung des zu testenden Programmes, wodurch es einfacher wird, den jeweiligen Zustand der Variablen und ihre Definitionen zu erfahren, zu prüfen, was unter bestimmten Bedingungen geschieht, usw. Dies alles kann gemacht werden, es sei nochmals erwähnt, während das zu testende Programm Schritt für Schritt ausgeführt wird. Sollte einem diese Definition nicht ganz transparent sein, so sollte sich dies während des Artikels ändern.

Angenommen, der Programmierer in unserer Geschichte hätte einen Debugger, dessen Name sei "spy", welcher ihm folgendes erlauben würde:

jose# spy my_program

Und was wäre, wenn unser Programmierer nach dem Start von "spy" dies tun könnte:

spy > execute my_program "up to line number 50"
spy > show me the value of the variable <X>
spy > what is the type of variable <X>

Es ist sehr wahrscheinlich, daß unser imaginärer Programmierer in diesem Augenblick vor Freude herumspringen würde, da er so schließlich den Fehler gefunden hätte.

Offensichtlich war "spy" ausgesprochen hilfreich, da es dem Programmierer erlaubte, das Programm beliebig auszuführen, während er die Werte und Definitionen der Programmvariablen beobachten konnte. Prinzipiell ist das, was einen DEBUGGER ausmacht, auch wenn er jetzt nur grob beschrieben worden ist.

Achtung !!: Debugger können nur mit Programmen arbeiten, welcher mit der Compileroption "-g" (im Falle des GNU gcc Compilers) übersetzt worden sind.

Es gibt ein Werzeug für das Debuggen von Programmen, welches für LINUX Benutzer (aber auch für viele andere Plattformen) zugänglich ist, der GDB The GNU Source-Level Debugger". Er ist unter der gleichen Lizenz zu haben, unter der auch das Betriebssystem zu haben ist, welches wohl der Leser momentan verwendet: die GNU General Public License. Er gestattet das Debuggen von Kode,der in C, C++, Modula-2 und Assembler geschrieben ist.

Höchstwahrscheinlich wird der Leser bei seiner Linux Distribution den Debugger vorfinden, ansonsten sollte er die Distribution wechseln oder die Quelltexte im Netz suchen, wo sie an Myriaden von Orten herumliegen ;).

Angenommen, die Quelltexte wurden im Verzeichnis /usr/src untergebracht, dann geht man in "/usr/src/gdb-4.xxxx/gdb", gibt dort "./configure" ein und wechselt dann nach "doc". Dort kann die Dokumentation für gdb erzeugt werden, und zwar durch "make gdb.dvi;make refcard.dvi". Beide Dateien können recht einfach auf jedem Linnuxrechner angeschaut oder gedruckt werden.

Was ist DDD?

Statt mit einer detailierten Betrachtung der Funktionen von gdb und all seinen Befehlen fortzufahren, ist es für den Neuling besser, wenn er die wesentlich benutzerfreundlichere Umgebung "ddd" kennenlernt. ddd steht für Display Data Debugger.

Ganz allgemein kann man sagen, daß ddd einen einfacheren und benutzerfreundlicheren Weg für die Konfiguration einer Debug Session bietet. Dennoch sollte nicht verschwiegen werden, daß ddd nur eine grafische Oberfläche für gdb darstellt, weswegen ohne den Debugger, ddd recht sinnlos wird. ddd erlaubt dem Benutzer, gdb direkt zu manipulieren, wenn es notwendg wird. Neben gdb können auch andere Debugger, wie dbx oder xdb, mit ddd benutzt werden.

Informationen über ddd sind unter http://www.cs.tu-bs.de/softech/ddd/ zu finden, wer RedHat verwendet, kann die Quellen auch im RPM Format benutzen. Es gibt zwei Versionen von ddd, eine, die dynamisch gegen Motif gelinkt ist und eine statische Version. Letztere ist für Benutzer gedacht, die nicht im Besitz der Motif Bibliothek sind (welche nur käuflich zu erwerben ist).

Die Konstellation von ddd und LESSTIF (http://www.lesstif.org) wird nicht näher betrachtet, da ich mit dem gegenwärtigen Status von lesstif nicht vertraut bin. Lesstif stelle eine freie Implementierung der Motif Bibliothek dar. Vor nicht allzu lange Zeit war es mir möglich, ddd mit lesstif zu kompilieren, dank eines Patches. Das System war Kernel 1.2.13 mit lesstif, Version 0.75...glaube ich ;). Näheres über dieses Projekt und dessen Status kann der interessierte Leser auf den Heimatseiten von lesstif erfahren.

Startet man nun endlich ddd, so sieht das so aus:


Abbildung 1. Hauptfenster von ddd

Es gibt drei Wege, ddd zu starten; der eine wurde schon erwähnt, die anderen beiden sind:

ddd <program> core
ddd <program> <process_id>

Die Datei "core" wird immer dann erzeugt, wenn ein Programm abstuerzt und enthält brauchbare Informationen über den Status des Programmes zum Zeitpunkt des Fehlers, der den Absturz verursachte. Sollte ein System keine Coredumps durchführen, so sollte man einen Blick auf die zuständigen Umgebungsvariablen werfen ('ulimit -a' zeigt alles an, 'ulimit -c <value>' setzt gewünschte Werte).

Die Prozeß ID macht es möglich, das Programm während der Auführung zu observieren.

Die grafische Oberfläche von ddd bietet immer mehrere Möglichkeiten, eine Aufgabe durchzuführen; hier werden nicht alle beschrieben, es wird nur auf die einfachsten bzw. direktesten eingegangen. Das unterste Fenster der Hauptschaltfläche von ddd zeigt Informationen aller von ddd ausgeführten Transaktionen. Das Log Fenster kann recht hilfreich sein, wenn man den Gebrauch von gdb von der Kommandozeile aus erlernen will.

Die grafische Oberfläche

Abbildung 1 zeigt, daß das Hauptfenster in drei Teilfenster aufgeteilt ist. Das unterste dient als direkte Schnittstelle zum Debugger, (gdb in unserem Fall). Es können gdb Kommandos direkt eingegeben werden, ganz als ob man gar nicht mit der ddd Oberfläche arbeiten würde. Das mittlere Fenster zeigt den Quelltext des Programmes und das obere Fenster dient als grafische Schnittstelle zu den Variablen und Objekten des Programmes. Zum Schluß wäre da noch die Werkzeugleiste, welche die Kontrolle über ddd Kommandos bietet.

Zusätzlich zum Hauptfenster existiert noch ein Fenster für die Ausführung des laufenden Prozesses und eins, welches des Quelltext des zu untersuchenden Programmes anzeigt. Beide Fenster sind optional.

Mit ddd kommt eine Vielzahl von Dokumentation mit, welche den Benutzer mit notwendigen Informationen zu jedem Zeitpunkt der Debug Session versorgt. Zum Beispiel erscheint immer dann, wenn der Cursor über eine Variable oder einen Button der Oberfläche bewegt wird, ein Dialogfenster. Diese bietet dann relevante Informationen über das jeweilige Objekt. Der untere Teil des Hauptfensters enthält da noch eine Statuszeile, welche das gerade ausgeführte Kommando und den Status anzeigt. Rechts findet man ein Hilfe-Menü. Mittels F1 kann man themenbezogene Informationen erhalten. Schließlich kann man im gdb Fenster (ganz unten) das Kommando "help" eingeben und allgemein Hilfe über den Debugger oder spezielle Informationen über eines der Kommandos zu erhalten.

Standardmäßig stellt die ddd Oberfläche drei Fenster , in einem Hauptfenster zusammengefaßt, dar. Jedoch kann man im Menü "Preferences" einstellen, daß stattdessen einzelne separate Fenster dargestellt werden.


Abbildung 2. Hilfe für das "File" Menü

Unten angefangen

Die "DDD:Debugger Console" dient für unsere ersten Schritte, die Verwendung des Debugger zu erlernen. Erfahrene Benutzer, die schon mit gdb vertraut sind, können einfach von hier aus ddd benutzen. Meine Erfahrungen zeigen, daß es hilft, die Ausgabe auf der Debugger Konsole zu betrachten, wenn Kommandos über die grafische Oberfläche ausgeführt werden. Die Option "Commands->Command History" zeigt in einem separaten Fenster eine Liste aller bereits ausgeführten Kommandos an.

Mehr über die Fähigkeiten und speziellen Aspekte von DDD kann man direkt aus der Originaldokumentation lernen. Auf jeden Fall wird im weiteren beschrieben, wie einige einfache Aktionen vom Debugger direkt ausgeführt werden können.

Allgemeine Aufgaben

Quelltext, der überprüft werden soll, kann von der Kommandozeile in DDD aus oder über den Menüpunkt "File" geladen werden. Der Inhalt der Quelltextdatei wird im entsprechenden Fenster angezeigt. Von nun an kann der Benutzer sich durch den Quelltext bewegen, den Wert und Typ einer Variable anschauen, das Programm kontrolliert ausführen, uvm.

Die Ausgabe des Programmes kann im Fenster für die Programmausführung (Options -> Run in Execution Window) oder in der Debugger Konsole betrachtet werden. Letzteres funktioniert allerdings nicht, falls das Program GUI orientiert ist, d.h. Motif oder ein anderes grafisches Toolkit benutzt.

Wird der Cursor über irgendeine Variable des Quelltextes bewegt, so wird ihr gegenwärtiger Wert in der Statuszeile von ddd angezeigt. Wird die rechte Maustaste gedrückt, so erscheint folgendes Menü:


Dieses Menü erlaubt es, den Wert der Variable "fname" im unteren Fenster anzuzeigen, während im oberen Fenster ("drawing area") angezeigt wird, ob es eine richtige Variable oder ein Zeiger (eine Variable, die die Speicheradresse einer anderen Variable und nicht ihren Wert enthält) ist. Desweiteren stellt "What is" die Struktur oder den Typ der Variable dar. Lookup sucht das Auftreten der Variable im weiteren Text. Und Break at und Clear at kontrollieren die sogenannten Breakpoints, die in Kürze erläutert werden.

Einige Optionen sind in der Buttonleiste unterhalb des Quelltextfensters verfügbar, der gewünschte Parameter wird einfach in dem linken Textfeld eingegeben und die entsprechende Aktion gewählt.

Ein Breakpoint unterbricht die Programmausführung in der Zeile im Programm, an der er gesetzt worden ist. Der Benuter kann Wert der Variablen bis zu diesem Punkt untersuchen, die Ausführung des Programmes Schritt für Schritt weiterführen, Threads überprüfen, uvm. Wichtig ist, daß ohne einen einzigen Breakpoint das Programm bis zu seiner Beendigung durchläuft bzw. halt wegen eines Fehlers abstürzt. Es ist dann zu spät, irgendwie das Programm zu überprüfen, Debugging kann nur während der Ausführung betrieben werden.

Ein Breakpoint wird wie folgt im Quelltext gesetzt:

In der Abbildung sind zwei Breakpoints in Zeile 70 und 71 des Quelltextes zu sehen, das Symbol für einen Breakpoint sollte selbsterklärend sein.

Das folgende Menü dient der Verwaltung von Breakpoints:

Durch die Option Condition können bedingte Breakpoints gesetzt werden. In diesem Falle hält das Programm an dem Breakpoint nur an, wenn die entsprechende Bedingung erfüllt ist. Ein bestimmte Bedingung ist Ignore Count. Diese ist nur erfüllt, nachdem die Zeile mit dem Breakpoint n-mal erreicht worden ist. Zum Beispiel kann das Programm so nach dem 15. Durchlauf einer Schleife angehalten werden.
Nachdem das Programm an einem Breakpoint gehalten hat, können die Werte der Variablen im Programm angeschaut werden, über das entsprechende Menü. Man sollte sich merken, daß diese Funktionen im Hauptmenü (z.B. Menü Data) zu finden sind.
Die Kontrolle über die Programmausführung wird durch die Buttonleiste ermöglicht, die extra für diesen Zweck da ist. Sie befindet sich im Hauptfenste oben-rechts.


Parallelen zwischen der Buttonleiste und der Menüleiste sind klar zu erkennen.

Das Programm kann gestartet und angehalten werden, wird die Menüleiste benutzt, können Parameter an das Programm über ein Dialogfenster übergeben werden. Step führt eine weitere Programmzeile aus (Schritt für Schritt), ist dort zum Beispiel ein Funktionsaufruf sprigt der Debugger an den Anfang der Funktion und wartet auf das nächste Step Kommando. Das Next Kommando hingegen führt einen Funktionsaufruf als atomare Programmzeile aus (also, als eine einzige Zeile), die komplette Funktion wird durchlaufen.

Continue erlaubt die Weiterführung der Ausführung des Programmes, nachdem ein Breakpoint dieses gestoppt hatte. Kill, Interrupt und Abort werden für die Unterbrechung der Programmausführung benutzt.

Das möglicherweise interessanteste Feature des grafischen Werkzeuges ist das Fenster für die Darstellung der Daten, im oberen Teil des Fensters. Es werden die Struktur und der Inhalt von Daten, sowie deren Abhängigkeiten untereinander, grafisch dargestellt. Im folgenden Beispiel sind ein Array und vier seiner Elemente dargestellt.

Dieses Fenster kann eine große Zahl von Daten anzeigen, man werfe ein Blick auf das Menü Data->More Status Displays , wo alle gewünschten Einstellungen für die Anzeige im Fenster vorgenommen werden können. Im nächsten Beispiel werden die Werte von Prozessorregistern, die benötigten dynamischen Bibliotheken und den Ausführungszustand des Programmes visualisiert:


Schlußwort

Die ddd Umgebung kann aus dem Programm heraus konfiguriert werden und zwar über das Menü Options->Preferences, sowie über das klassische Ressourcenmodell der Motif Anwendungen (hier die Datei $HOME/.dddinit). Es sprengt den Rahmen dieses Artikels, zu beschreiben, welche Werte modifiziert werden können und wie dies geschieht.

Es ist ratsam, das Handbuch, welches mit ddd mitkommt (ddd.ps) und das für den Debugger selbst ("Debugging with GDB"), zu lesen. Trotzdem kann der Benutzer mit ein wenig Neugier sich selbst das Notwendige beibringen, man muß einen gut verstandenen Quelltext debuggen, um alle Möglichkeiten herauszufinden.

Zum Schluß möchte ich mich für alle potentielle Fehler in diesem Artikel entschuldigen :)

Literaturverweise


Übersetzung: Harald Radke

This website is maintained by Miguel Angel Sepulveda
© 1998 Jose M. Laveda

LinuxFocus 1998