Wie man C-Programme in Linux mit gdb debuggt

Unabhängig davon, wie erfahren Sie als Programmierer sind, kann jede Software, die Sie entwickeln, nicht völlig fehlerfrei sein. Daher ist die Identifizierung von Fehlern und deren Behebung eine der wichtigsten Aufgaben im Software-Entwicklungszyklus. Während es viele Möglichkeiten gibt, Fehler zu identifizieren (Testen, Selbstüberprüfung des Codes und mehr), gibt es spezielle Software – sogenannte Debugger – die Ihnen helfen, genau zu verstehen, wo das Problem liegt, so dass Sie es leicht beheben können.

Wenn Sie ein C/C++-Programmierer sind oder Software mit den Programmiersprachen Fortran und Modula-2 entwickeln, werden Sie sich freuen zu erfahren, dass es einen ausgezeichneten Debugger – GDB genannt – gibt, mit dem Sie Ihren Code leicht auf Fehler und andere Probleme hin untersuchen können. In diesem Artikel werden wir die Grundlagen von GDB besprechen, einschließlich einiger der nützlichen Funktionen/Optionen, die es bietet.

Aber bevor wir weitermachen, ist es erwähnenswert, dass alle Anweisungen sowie die in diesem Artikel vorgestellten Beispiele auf Ubuntu 14.04LTS getestet wurden. Der im Tutorial verwendete Beispielcode ist in der Sprache C geschrieben; die von uns verwendete Kommandozeilen-Shell ist bash (Version 4.3.11); und die von uns verwendete GDB-Version ist 7.7.1.

GDB-Debugger-Grundlagen

Laienhaft ausgedrückt, erlaubt GDB Ihnen einen Blick in ein Programm zu werfen, während das Programm ausgeführt wird, etwas, das Ihnen hilft, das genaue Problem zu identifizieren. Wir werden die Verwendung des GDB-Debuggers anhand eines Arbeitsbeispiels im nächsten Abschnitt besprechen, aber vorher werden wir hier einige grundlegende Punkte besprechen, die Ihnen später helfen werden.

Erstens, um erfolgreich Debugger wie GDB zu verwenden, müssen Sie Ihr Programm so kompilieren, dass der Compiler auch Debugging-Informationen erzeugt, die von den Debuggern benötigt werden. Zum Beispiel müssen Sie im Falle des gcc-Compilers, den wir später in diesem Tutorial zum Kompilieren des C-Beispielprogramms verwenden werden, während der Kompilierung Ihres Codes die Befehlszeilenoption -g verwenden.

Um zu wissen, was die Handbuchseite des gcc-Compilers über diese Kommandozeilenoption sagt, gehen Sie hier weiter.

Der nächste Schritt ist, sicherzustellen, dass Sie GDB auf Ihrem System installiert haben. Wenn das nicht der Fall ist und Sie sich auf einem Debian-basierten System wie Ubuntu befinden, können Sie das Werkzeug einfach mit dem folgenden Befehl installieren:

sudo apt-get install gdb

Für die Installation auf einer anderen Distro, gehen Sie hierhin.

Sobald Sie Ihr Programm so kompiliert haben, dass es debugging-fähig ist, und GDB auf Ihrem System vorhanden ist, können Sie Ihr Programm im Debugging-Modus mit dem folgenden Befehl ausführen:

gdb [prog-executable-name]

Während dies den GDB-Debugger einleitet, wird Ihr ausführbares Programm an diesem Punkt nicht gestartet. Dies ist der Zeitpunkt, an dem Sie Ihre debugging-bezogenen Einstellungen definieren können. Zum Beispiel können Sie einen Haltepunkt definieren, der GDB anweist, die Programmausführung bei einer bestimmten Zeilennummer oder Funktion zu pausieren.

Um Ihr Programm tatsächlich zu starten, müssen Sie den folgenden gdb-Befehl ausführen:

run

Es ist erwähnenswert, dass Sie hier angeben können, wenn Ihr Programm einige Befehlszeilenargumente benötigt, die ihm übergeben werden müssen. Zum Beispiel können Sie

run [arguments]

Die GDB bietet viele nützliche Befehle, die beim Debuggen nützlich sind. Wir werden einige davon im Beispiel im nächsten Abschnitt besprechen.

Beispiel für die Verwendung von GDB

Jetzt haben wir eine grundlegende Idee über GDB sowie deren Verwendung. Nehmen wir also ein Beispiel und wenden wir das Wissen dort an. Hier ist ein Beispielcode:

#include <stdio.h>

int main()
{
int out = 0, tot = 0, cnt = 0;
int val[] = {5, 54, 76, 91, 35, 27, 45, 15, 99, 0};

while(cnt < 10)
{
out = val[cnt];
tot = tot + 0xffffffff/out;
cnt++;
}

printf(„\n Total = [%d]\n“, tot);
return 0;
}

Im Grunde genommen nimmt dieser Code also jeden im ‚val‘-Array enthaltenen Wert, weist ihn der ‚out‘-Ganzzahl zu und berechnet dann ‚tot‘, indem er den vorherigen Wert der Variablen und das Ergebnis von ‚0xffffffff/out‘ aufsummiert.

Das Problem hierbei ist, dass der Code beim Ausführen des Codes den folgenden Fehler erzeugt:

$ ./gdb-test 
Floating point exception (core dumped)

Um den Code zu debuggen, wäre der erste Schritt also, das Programm mit -g zu kompilieren. Hier ist der Befehl:

gcc -g -Wall gdb-test.c -o gdb-test

Als Nächstes lassen wir GDB laufen und lassen es wissen, welche ausführbare Datei wir debuggen wollen. Hier ist der Befehl dafür:

gdb ./gdb-test

Nun, der Fehler, den ich bekomme, ist „Floating Point Exception“, und wie die meisten von Ihnen vielleicht schon wissen, wird er durch n % x verursacht, wenn x 0 ist. Mit diesem Gedanken im Hinterkopf habe ich also einen Haltepunkt in Zeile Nummer 11 gesetzt, wo die Division stattfindet. Dies wurde auf folgende Weise durchgeführt:

(gdb) break 11

Beachten Sie, dass „(gdb)“ die Eingabeaufforderung des Debuggers ist, ich habe gerade den ‚break‘-Befehl geschrieben.

Nun, ich habe GDB gebeten, die Ausführung des Programms zu starten:

run

Als der Haltepunkt also zum ersten Mal erreicht wurde, zeigte die GDB in der Ausgabe Folgendes:

Breakpoint 1, main () at gdb-test.c:11
11 tot = tot + 0xffffffff/out;
(gdb)

Wie Sie in der obigen Ausgabe sehen können, zeigte der Debugger die Zeile, in der der Haltepunkt gesetzt wurde. Lassen Sie uns nun den aktuellen Wert von ‚out‘ ausdrucken. Dies kann auf folgende Weise geschehen:

(gdb) print out
$1 = 5
(gdb)

Wie Sie sehen können, wurde der Wert ‚5‘ gedruckt, es ist also im Moment alles in Ordnung. Ich bat den Debugger, die Ausführung des Programms bis zum nächsten Haltepunkt fortzusetzen, was mit dem Befehl ‚c‘ möglich ist.

c

Ich machte so lange weiter, bis ich sah, dass der Wert von ‚out‘ gleich Null war.

...
...
...
Breakpoint 1, main () at gdb-test.c:11
11 tot = tot + 0xffffffff/out;
(gdb) print out
$2 = 99
(gdb) c
Continuing.

Breakpoint 1, main () at gdb-test.c:11
11 tot = tot + 0xffffffff/out;
(gdb) print out
$3 = 0
(gdb)

Nun, um zu bestätigen, dass dies das genaue Problem ist, habe ich diesmal GDBs ’s‘ (oder ’step‘) Befehl anstelle von ‚c‘ verwendet. Der Grund dafür ist, dass ich nur Zeile 11, wo die Programmausführung derzeit pausiert, ausführen wollte, um zu sehen, ob an dieser Stelle ein Absturz auftritt.

Folgendes ist passiert:

(gdb) s

Program received signal SIGFPE, Arithmetic exception.
0x080484aa in main () at gdb-test.c:11
11 tot = tot + 0xffffffff/out;

Ja, wie durch die oben hervorgehobene Ausgabe bestätigt wird, wurde hier die Ausnahme geworfen. Die endgültige Bestätigung kam, als ich den Befehl ’s‘ noch einmal versuchte:

(gdb) s

Program terminated with signal SIGFPE, Arithmetic exception.
The program no longer exists.

Auf diese Weise können Sie also Ihre Programme mit GDB debuggen.

Schlussfolgerung

Wir haben hier nur an der Oberfläche gekratzt, denn die GDB bietet eine Menge Funktionen, die von den Benutzern erkundet und genutzt werden können. Gehen Sie durch die Manpage der GDB, um mehr über das Tool zu erfahren, und versuchen Sie es zu benutzen, wann immer Sie etwas in Ihrem Code debuggen wollen. Der Debugger ist mit einer gewissen Lernkurve verbunden, aber es ist die harte Arbeit wert.

Das könnte dich auch interessieren …