![]() Universität Bayreuth Mathematisches Institut Einleitung Erste Schritte Mail und News Drucken KDE LaTeX/TeX![]() Linksammlung Linuxtools Netzwerk![]() Programmieren![]() Windows X Window Anträge Kontakt |
Fehlersuche mit dem Debugger Fehlersuche mit dem gdbObwohl Sie später sicherlich mehr mit dem ddd arbeiten werden, ist es ratsam, diese Seite zu lesen, um mit dem gdb umgehen zu können. Damit werden Sie sich mit dem ddd besser auskennen. Inhaltsverzeichnis
Start des gdbDer Befehl zum Start des gdb ist "gdb", gefolgt vom Namen der ausführbaren Datei.Beispielsweise (wie immer unter Linux):
gdb a.out
Es funktioniert aber nicht, wenn Sie nach dem Befehl gdb den richtigen Namen der Sourcedatei (zum Beispiel: "aufgabe.cpp") eingeben. Der Debugger benötigt die ausführbare Datei. Wichtig: Es ist auch sehr wichtig, dass Sie Ihr Programm mit der Option "-g" compilieren, weil diese Option zum Executable Debug-Informationen hinzufügt. Im normalen Ablauf verhält sich das Programm kaum anders als ohne diese Option. Nachdem Sie den gdb gestarten haben erhalten Sie am Bildshirm den gdb-Prompt:
(gbd)
Jetzt kann man gdb-Befehle eingeben.
Laden einer Core-DateiWenn Ihr Programm beim Absturz einen Abzug des Speichers (core-Datei) erzeugt, können Sie diesen als zweites Argument beim Aufruf des Debuggers angeben. Der Debugger kann diesen Speicherauszug laden und damit sofort die Stelle anzeigen, an der der Absturz passierte.Der Befehl lautet in seiner allgemeinen Form:
(gdb) <Name des executables> <Name der core-Datei>
Start eines Programms im DebuggerDer Debugger läuft. Jetzt können wir unser Programm innerhalb des Debuggers starten. Dies erfolgt mit dem Befehl "run". Weil der gdb eine Reihe von Kürzel für besonders häufig benutzte Kommandos kennt, können Sie anstelle von "run" auch nur "r" eingeben.
Das kann ganz anders passieren.
Um diese Frage beantworten zu können, werden wir den Ablauf des Programm Schritt für Schritt verfolgen.
Ausgabe von ProgrammcodeUm genau zu wissen, was in unserem Programm passiert, sollten wir den Programmcode vor Augen haben. Die Ausgabe des Programmcodes erfolgt durch das Kommando " list" oder "l" in Kurzform. Damit erhalten Sie ein paar Zeilen vom Programmcode. Die Anzahl der ausgegebenen Zeilen können Sie ändern, indem Sie "set listsize <Anzahl>" eingeben. Der Befehl "list" versteht auch als Argument eine Zeilennummer oder einen Funktionsnamen, ab denen Sie den Code anschauen wollen. Sie können auch als Argument "+" oder "-" eingeben, um die Zeilen vor bzw. nach dem letzten Codeausschnitt anzuzeigen.
Für Beispiel 2 (bsp2.cpp):
(gdb) set listsize 3 // Größe des Ausschnitt =3 Zeilen
(gdb) list
6 int main()
7 {
8 const int N=25;
(gdb) l - // 3 vorherige Zeilen anschauen
2 #include <iostream>
3 #include <fstream>
4 using namespace std;
(gdb) l 10 // Programmcode ab Zeile 10 anschauen
9 double n,erg;
10
11 ifstream fin;
HaltepunktDas Kommando zum Setzen eines Haltepunkts ist break oder einfach b. Der gbd bietet dann zwei verschiedene Argumente. Die Nummer der Zeilen, bei der das Programm stehen bleiben soll oder den Namen einer Funktion bzw. Methode, zu deren Beginn gestoppt werden soll.Mit dem Argument Zeilennummer bezieht sich der Haltepunkt auf die Datei, in der das Programm sich gerade befindet. Wenn Sie einen Funktionsnamen als Argument angeben, fügen Sie den Funktionnamen nach break an. Das Programm hält genau bei der ersten Anweisung dieser Funktion an. Für Beispiel 2(bsp2.cpp):
(gdb) b 9
Breakpoint 1 at 0x804882e: file bsp2.cpp, line 9.
(gdb) b Berechnung
Breakpoint 2 at 0x8048976: file bsp2.cpp, line 26.
(gdb)
Wenn Sie viele Haltepunkte verwenden und schon den Überblick verloren haben,
gibt es den Befehl info breakpoints. Mit diesem Befehl können
Sie eine Liste mit allen gesetzten Haltepunkten sehen.
Für unser Beispiel:
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x08048820 in main at bsp2.cpp:9
2 breakpoint keep y 0x08048976 in Berechnung(double,double,int) at bsp2.cpp:26
(gdb)
Wenn Sie einen Haltepunkt nicht mehr brauchen, können Sie diesen löschen. Dafür
gibt es zwei Kommandos clear und delete
Das Kommando clear ist praktisch das Gegenstück zu break und versteht dieselben Argumente. Das Kommando delete erwartet die Nummer des zu löschenden Haltepunkt. Für unser Beispiel (bsp2.cpp):
(gdb) clear Berechnung
Deleted breakpoint 2
(gdb) delete 1
(gdb) info breakpoints
No breakpoints or watchpoints.
Wenn Sie nicht ganz auf einen Haltepunkt verzichten wollen, können Sie ihn deaktivieren
durch das Kommando disable und nachher wieder einschalten mit dem Kommando
enable. Den aktuellen Zustand des Breakpoints können Sie
aus der mit info breakpoints
erhältlichen Liste entnehmen. Diese zwei Kommandos erwarten als Argument
die Nummer des
Haltepunktes.
Hier:
(gdb) b 11
Breakpoint 3 at 0x8048820: file bsp2.cpp, line 11.
(gdb) b 13
Breakpoint 4 at 0x804884f: file bsp2.cpp, line 13.
(gdb) disable 3
(gdb) info breakpoints
Num Type Disp Enb Address What
3 breakpoint keep n 0x08048820 in main at bsp2.cpp:11
4 breakpoint keep y 0x0804884f in main at bsp2.cpp:13
Nachdem Sie die Haltepunkte gesetzt haben, können Sie dann wieder das Programm mit
run laufen lassen.
(gdb) r
Starting program: /home/hiwicip2/a.out
Breakpoint 4, main () at bsp2.cpp:14
14 if (fin.fail())
(gdb) enable 3
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/hiwicip2/a.out
Breakpoint 3, main () at bsp2.cpp:11
11 ifstream fin;
(gdb)
Einzelschritte und FortsetzungZur Ausführung in Einzelschritten gibt es im wesentlichen vier Kommandos:
Verfolgung der AufrufketteWenn wir uns in einer Unterfunktion unseres Programmes befinden, ist es immer sehr hilfreich zu wissen, wo genau (in welcher Zeile) diese Funktion angerufen ist. Dafür gibt es das Kommando backtrace oder kurz bt.Für unser Beispiel 3 (bsp3.cpp)
(gdb) r
Starting program: /home/hiwicip2/cvs-rep/cip_www/download/debugger/a.out
Geben Sie 'n' Anzahl von Studenten ein (n>0) : 1
Geben Sie 'm' Anzahl von Fächern ein (m>0): 2
Breakpoint 1, main () at bsp3.cpp:33
33 eingabe( tabelle,n,m);
(gdb) s
eingabe (tabelle=0x804a1a0, n=1, m=2) at bsp3.cpp:57
57 for (int i=0;i<n;i++)
(gdb) s
59 cout<<"Geben Sie Punkten des "<<i+1<<".ten Studentes ein";
(gdb) bt // Befehl backtrace
#0 eingabe (tabelle=0x804a1a0, n=1, m=2) at bsp3.cpp:59 // Wir befinden uns in der Zeile 59
der Funkt.eingabe()
#1 0x8048a5b in main () at bsp3.cpp:33 // Diese Funktion wurde in der Zeile 33
der main-Funkt. aufgerufen.
(gdb)
Dieses Kommando ist auch sehr geeignet, wenn das Programm
wie in unserem Fall (bsp3.cpp)
abstürzt.
Wenn wir mit run das Programm läufen lassen, erhalten wir: Program received signal SIGSEGV, Segmentation fault.
0x400d822a in chunk_free (ar_ptr=0x401632c0, p=0x804a1a8) at malloc.c:3049
3049 malloc.c: Datei oder Verzeichnis nicht gefunden.
Current language: auto; currently c
(gdb)
Das Programm stürzt ab. Aber wo passiert es?
Wenn wir das Kommando bt eingeben, erhalten wir:
(gdb) bt
#0 0x400d822a in chunk_free (ar_ptr=0x401632c0, p=0x804a1a8) at malloc.c:3049
#1 0x400d81cf in free () at malloc.c:2952
#2 0x400483b4 in __builtin_vec_delete () at ./cp/new2.cc:63
#3 0x8048b6c in main () at bsp3.cpp:48
(gdb)
Jetzt wissen wir, wo die Funktion, die zum Absturz führt, aufgerufen
wurde. Es bleibt jetzt genauer zu schauen, was in
der Zeile 48 passiert.
Untersuchung von VariablenwertenBei der Fehlersuche kann man auch den Inhalt von Variable ausgeben lassen und schauen, wie sie sich verhalten. Die Ausgabe von Variablen erfolgt durch den Befehl print oder kurz p. Man kann also den Inhalt von Intergraldatentypen (Datentyp für ganzzahlige Werte) und Gleitpunktdatentypen ganz einfach anschauen, indem man print, gefolgt vom Namen der Variable, eingibt.Beipiel (Bsp3):
Geben Sie 'n' Anzahl von Studenten ein (n>0) : 1
Geben Sie 'm' Anzahl von Fächern ein (m>0): 2
(gdb) p n
$1 = 1
(gdb) p m
$2 = 2
Man kann auch den Inhalt von Arrays anschauen. Dazu gibt man die Arrayeinträge als normale Variablen aus.
Beispielsweise:
(gdb) p tabelle[0][1]
$4 = 1
Gerade bei Arrays will man meist nicht nur einzelne Werte anschauen, sondern den Inhalt des ganzen Arrays.
Das kann man zeilenweise machen. Man ergänzt das Zeichen " @" nach dem Name des
Arrayelements, ab dem man die Elemet anschauen will und gibt danach
eine Zahl an. Diese Zahl ist die Anzahl von Elementen, die man ab diesem
Arrayelement anschauen will.
Beispielsweise:
(gdb) p tabelle[0][1]@2 // Wir wollen uns die 2 Elemente tabelle[0][1] und tabelle[0][2] anschauen.
(gdb)
$5 = {1, 1}
Eine andere Art der Ausgabe erfolgt durch das Kommando display. Damit können Sie erreichen, dass der gdb
die angegebene Variable jedes Mal ausgibt, wenn das Programm stoppt.
Beispiel 3 (bsp3):
(gdb) display i
1: i = 0
(gdb) n
38 for(j=0;j<m;j++)
1: i = 0
(gdb) n
40 cout<<tabelle[i][j]<<" | " ;
1: i = 0
(gdb) n
42 }
1: i = 0
(gdb) n
40 cout<<tabelle[i][j]<<" | " ;
1: i = 0
(gdb) n
42 }
1: i = 0
(gdb) n
43 cout<<endl;
1: i = 0
(gdb) n
1.Student | 1 | 1 |
44 }
1: i = 0
(gdb) n
37 cout<<i+1<<".Student | ";
1: i = 1
(gdb) n
38 for(j=0;j<m;j++)
1: i = 1
(gdb) n
40 cout<<tabelle[i][j]<<" | " ;
1: i = 1
(gdb) n
42 }
1: i = 1
(gdb) n
40 cout<<tabelle[i][j]<<" | " ;
1: i = 1
(gdb) n
42 }
1: i = 1
(gdb) n
43 cout<<endl;
1: i = 1
(gdb) n
2.Student | 1 | 1 |
44 }
1: i = 1
(gdb) n
Interressant sind die Schleifenvariablen. Bei dem Beispiel 3
hatten wir einen Absturz bei der
for-Schleife in der Zeile 48 gehabt. Dieses Kommando ist also sehr
geeignet, um nachzuschauen, wie oft die Schleife durchlaufen wird.
Starting program: /home/hiwicip2/cvs-rep/cip_www/download/debugger/a.out
Geben Sie 'n' Anzahl von Studenten ein (n>0) : 2
Geben Sie 'm' Anzahl von Fächern ein (m>0): 4
Geben Sie Punkten des 1.ten Studentes ein
Fürs 1.te Fach : 1
Fürs 2.te Fach : 2
Fürs 3.te Fach : 3
Fürs 4.te Fach : 4
Geben Sie Punkten des 2.ten Studentes ein
Fürs 1.te Fach : 2
Fürs 2.te Fach : 1
Fürs 3.te Fach : 4
Fürs 4.te Fach : 3
1.Student | 1 | 2 | 3 | 4 |
2.Student | 2 | 1 | 4 | 3 |
Breakpoint 1, main () at bsp3.cpp:47
47 for( i=0;i<m;i++)
Current language: auto; currently c++
(gdb) display i
1: i = 2 // i wurde schon in der vorherigen for-Schleife benutzt.
(gdb) n
48 delete []tabelle[i];
1: i = 0 // Die neue Iteration fängt an.
(gdb) n
48 delete []tabelle[i];
1: i = 1
(gdb) n
48 delete []tabelle[i];
1: i = 2 // Die Iteration sollte schon bei i=1 aufhören, weil
//wir nur 2 Elemente allokiert haben (n=2)
(gdb) n
48 delete []tabelle[i];
1: i = 3
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x400d8178 in free () at malloc.c:2952
2952 malloc.c: Datei oder Verzeichnis nicht gefunden.
Current language: auto; currently c
(gdb)
Jetzt wissen wir, wo der Fehler steht. Wir haben Speicherplätze gelöscht, die wir nicht reserviert
haben.
Nach der Korrektur zu Beispiel 3 in File "bsp3_korr.cpp" erhalten wir: (gdb) r
Starting program: /home/hiwicip2/a.out
Geben Sie 'n' Anzahl von Studenten ein (n>0) : 2
Geben Sie 'm' Anzahl von Fächern ein (m>0): 4
Geben Sie Punkten des 1.ten Studentes ein
Fürs 1.te Fach : 1
Fürs 2.te Fach : 1
Fürs 3.te Fach : 1
Fürs 4.te Fach : 1
Geben Sie Punkten des 2.ten Studentes ein
Fürs 1.te Fach : 1
Fürs 2.te Fach : 1
Fürs 3.te Fach : 1
Fürs 4.te Fach : 1
1.Student | 1 | 1 | 1 | 1 |
2.Student | 1 | 1 | 1 | 1 |
Program exited normally. es ist jetzt OK !
Current language: auto; currently c
(gdb)
Beenden des gdbNachdem wir das Problem gefunden haben können wir der Debug-Sitzung beenden. Dies erfolgt mit dem Kommando quit, kurz q.Wenn das Programm noch beim Laufen ist, erhalten Sie:
(gdb) q
The program is running. Exit anyway? (y or n)
Sie können dann mit dem Kommando continue das
Programm bis zum regulären Ende laufen lassen oder aber das
Programm an dieser Stelle abbrechen.
|