Universität Bayreuth Mathematisches Institut Erste Schritte Mail und News 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:13Nachdem 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 = 2Man kann auch den Inhalt von Arrays anschauen. Dazu gibt man die Arrayeinträge als normale Variablen aus. Beispielsweise: (gdb) p tabelle[0][1] $4 = 1Gerade 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) nInterressant 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.
|