Aus Linux-Magazin 04/2005

Workshop: Debugging mit GDB

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.

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.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Comments
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben