Open Source im professionellen Einsatz
Linux-Magazin 04/2005

Workshop: Debugging mit GDB

Auf der Pirsch

Wer Programme schreibt, baut ahnungslos oft auch gut versteckte Bugs ein. Die richtigen Tools und etwas Geduld vereinfachen das Debugging. Dieser Workshop erklärt die Grundlagen der Fehlersuche und vermittelt den Umgang mit dem prominentesten Vertreter der Gattung, dem GNU-Debugger GDB.

779

Die Existenz aller Bugs geht auf ein grundlegendes Missverständnis zurück: Etwas, was Sie für richtig gehalten haben, ist in Wirklichkeit falsch - egal ob sich der Fehler auf den Wert einer Variablen oder die Gültigkeit einer Speicheradresse oder eines Zeigers bezieht. Wie sich die Bugs auswirken, können Sie nicht vorhersagen. Vielleicht stürzt das Programm aufgrund eines Speicherzugriff-Fehlers ab, vielleicht bleibt es in einer Endlosschleife hängen oder es liefert falsche Ergebnisse. Aber eines steht fest: Alle Bugs sind wichtig und als Programmierer kommen Sie nicht darum herum, sie zu beseitigen.

Gründlich informieren

Als konventioneller Lösungsansatz dienen so genannte Trace-Meldungen. Sie geben aktuelle Zwischenergebnisse des Programms aus. In der Programmiersprache C fügt man üblicherweise Code hinzu, der auf dem Kanal zur Fehlerausgabe ausgiebig Bericht erstattet:

fprintf(stderr, "Entering CalcAverage now (num=%d, total=%d)",num,total);
....
fprintf(stderr, "Leaving CalcAverage now (return av=%d)", average);


Solche Befehle dokumentieren Funktionsaufrufe und Variablenwerte. Diese Informationen helfen bei der weiteren Suche nach der Fehlerursache.

Für Faultiere

Obwohl diese Methode für kleinere Programme angebracht sein mag, wird sie mit zunehmendem Code-Umfang sehr mühsam. Zusätzlich schleichen sich dabei schnell neue Fehler ein - schließlich kann auch der hinzugefügte Debugging-Code wieder Fehler enthalten.

Faulheit gilt bei Programmierern als eine Tugend, ebenso wie das Verschieben auf morgen. Bevor Sie sich also kopfüber ins Debugging stürzen, lehnen Sie sich zurück, um in Ruhe über prophylaktische Maßnahmen nachzudenken.

Erstens: Wird das Programm sauber kompiliert? Gibt der Compiler Warnungen aus und können diese Warnungen mit dem fehlerhaften Verhalten des Programms zusammenhängen? Kompilieren Sie mit den Optionen »-Wall« und »-O3«; Letztere führt zusätzliche Checks durch, die bei der nicht optimierten Kompilierung fehlen, »-Wall« gibt sämtliche Warnungen aus. Dann sehen Sie sich die Ausgabe erneut an.

Zweitens: Überprüfen Sie den Code mit anderen Tools wie Splint[2]; es meldet Fehler, die GCC ignoriert. Auf diese Art stoßen Sie auf Programmabschnitte, die unter bestimmten Umständen den Dienst verweigern. Typische Problemfälle sind zum Beispiel:

  • Eine Zahl des Typs »unsigned« nimmt einen negativen
    Wert an.
  • Aus dem Gleichheitssymbol »==« wird durch einen
    Tippfehler eine Zuweisung (»=«).
  • Beim Type-Casting (explizit oder implizit) gehen Informationen
    verloren.
  • Schleifenanfänge oder -enden liegen um eins neben dem
    gewünschten Wert (Off-by-One-Fehler).

Drittens: Melden andere Tools Probleme mit dem Programm? Zum Beispiel ein Memory-Debugger, der »malloc«-Probleme oder den Zugriff auf Daten außerhalb des gültigen Bereichs abfängt.

Dieses Problem tritt am häufigsten bei Zeichenketten auf, weil ein Test auf Grenzen (Bounds-Checking) hier typischerweise vernachlässigt wird. Allerdings kann das Problem jedes Array, jeden Zeiger sowie dynamisch allozierten Speicher betreffen.

Tabelle 1:
Allgemeine Befehle

 

Kürzel

Befehl

Beschreibung

r

run [ Args]

Programm starten

c

continue

Ablauf (vom aktuellen Breakpoint aus) fortsetzen

n

next

Nächste Anweisung, schrittweise Funktionsaufrufe
abarbeiten

s

step

Zur nächsten Zeile oder Funktion gehen

p

print

Variable anzeigen

pt

ptype

Gibt den Typ einer Variablen sowie Details der Struktur
zurück

bt

backtrace

Listet einen Stack aller Funktionen auf, die bis zu diesem
Punkt aufgerufen wurden (auch unter dem Namen Where bekannt)

l

list

Listet einen Programmabschnitt auf

h

help

Spezifische Hilfe zum Beispiel mit »help
delete«

q

quit

GDB beenden

Bekannte Memory-Debugger (siehe[3]) sind unter anderem Valgrind, CCMalloc, und Electric Fence von Bruce Perens, dem ehemaligen Leiter des Debian-Projekts. Nicht alle diese Tools funktionieren auf die gleiche Weise. So setzt Valgrind auf Just-in-Time-Debugging und interpretiert dazu den Quellcode selbst. Im Gegensatz dazu verlinkt der Entwickler bei CCMalloc die kompilierte Datei mit der CCMalloc-Bibliothek, um Speicherlecks aufzuspüren.

Verweigert Ihr Programm nach allen Bemühungen weiterhin den Dienst, müssen Sie den Code zeilenweise durchforsten, um festzustellen, wo und warum etwas schief läuft. Das Tool der Wahl für diesen Zweck heißt GDB, auf Wunsch mit Hilfe eines der grafischen Frontends wie DDD (siehe Artikel in diesem Heft) oder XXGDB.

Auch grafische Entwicklungsumgebungen wie KDevelop (siehe Abbildung 1), Anjuta und sogar Eclipse suchen Fehler mit Hilfe des GDB-Debuggers. Mit ihm steuern Sie den Programmablauf interaktiv, halten ein Programm an beliebiger Stelle an, untersuchen die Variablen und beeinflussen sogar die Ablaufreihenfolge des Code im laufenden Programm.

Der GNU Debugger ist einer der ältesten Bestandteile der GNU-Entwicklungsumgebung und in praktische jeder Linux-Distribution enthalten. Bereits 1988 rief Richard Stallman das Projekt ins Leben. Trotz oder gerade wegen dieser langen Geschichte ist GDB weiterhin eine reine Befehlszeilenanwendung, ausgestattet mit einer großen Vielfalt an mächtigen Befehlen.

Für den Linux-Kernel gibt es eine spezielle Version des Debuggers: KGDB. Sie unterstützt unter anderem das Remote-Kernel-Debugging über eine serielle Leitung. Mit Hilfe von GDB Server lässt sich GDB außerdem über eine TCP/IP-Verbindung steuern.

Linux-Magazin kaufen

Einzelne Ausgabe
 
Abonnements
 
TABLET & SMARTPHONE APPS
Bald erhältlich
Get it on Google Play

Deutschland

Ähnliche Artikel

  • Humpeln zur Diagnose

    Viele verdammen Debugger als Teufelszeug. Oft erweisen sie sich aber als letzte Rettung. Perl hat einen sogar eingebaut. Mit ihm beschäftigt sich dieser Snapshot - passend zum Schwerpunkt des Hefts.

  • Jagdstimmung

    Bei der Jagd nach Bugs im eigenen Programmcode holen sich Entwickler gern Hilfe. Etwa beim DDD: Er stöbert das virtuelle Ungeziefer auf und gibt den Blick frei auf alle Daten, die das Programm produziert.

  • Getriebeschaden

    Dreht sich das Perl-Rad nur noch knirschend, sind vielleicht Fehler im Perl-Interpreter oder in Erweiterungsmodulen schuld. Der gute alte GDB inspiziert das Getriebe und kommt Problemstellen auf die Schliche.

  • AMD stellt Code XL 2.0 unter Open-Source-Lizenz

    Drastische Änderungen am Entwicklungsmodell kündigt AMD für die neue Version 2.0 von Code XL an, einem GPU-Debugger und -Profiler.

  • Verwandlungskünstler

    Anpassungsfähigkeit ist die herausragende Eigenschaft der IDE Eclipse. Das CDT-Plugin macht sich dies zu Nutze und adaptiert die Workbench für die Sprachen C und C++. Heraus kommt eine ergonomische Entwicklungsumgebung mit nützlichen Zusatzfunktionen.

comments powered by Disqus

Stellenmarkt

Artikelserien und interessante Workshops aus dem Magazin können Sie hier als Bundle erwerben.