Debuggen mit GDB (Gnu DeBugger) unter Eclipsebbdw58/anleit/debuggen.pdf · Debuggen mit GDB (Gnu...

12
Debuggen mit GDB (Gnu DeBugger) unter Eclipse Boris Budweg, 16.11.2009 Version 0.4 Abstract: Kleine Einführung in die Bedienung des GDB über Eclipse und die Möglichkeiten eines Debuggers. Viele Screenshots und Hinweise auf weiterführende Informationen sollen die Neugier wecken. Erste Schritte .............................................................................................................................. 2 Debug-Konfiguration anlegen und Debugger starten ............................................................ 2 Orientierung in der Debug-Perspektive.................................................................................. 5 Erster Lauf .............................................................................................................................. 6 Fortgeschrittene .......................................................................................................................... 8 View: Breakpoints .................................................................................................................. 8 View: Disassembly................................................................................................................. 9 View: Registers .................................................................................................................... 11 Callstack, Springen in/aus Funktionen ................................................................................. 12

Transcript of Debuggen mit GDB (Gnu DeBugger) unter Eclipsebbdw58/anleit/debuggen.pdf · Debuggen mit GDB (Gnu...

Debuggen mit GDB (Gnu DeBugger) unter Eclipse

Boris Budweg, 16.11.2009

Version 0.4 Abstract: Kleine Einführung in die Bedienung des GDB über Eclipse und die Möglichkeiten eines Debuggers. Viele Screenshots und Hinweise auf weiterführende Informationen sollen die Neugier wecken. Erste Schritte .............................................................................................................................. 2

Debug-Konfiguration anlegen und Debugger starten ............................................................ 2 Orientierung in der Debug-Perspektive.................................................................................. 5 Erster Lauf.............................................................................................................................. 6

Fortgeschrittene .......................................................................................................................... 8 View: Breakpoints.................................................................................................................. 8 View: Disassembly................................................................................................................. 9 View: Registers .................................................................................................................... 11 Callstack, Springen in/aus Funktionen................................................................................. 12

2

Erste Schritte

Debug-Konfiguration anlegen und Debugger starten

Nachdem ein Projekt erstellt wurde, dieses vollständig (ohne Fehler) gebaut wurde, ist es möglich, mit Hilfe eines Debuggers dem Ablauf des Programms zuzusehen und einzugreifen. Für dieses Beispiel wird folgendes Programm verwendet:

#include "stdio.h"

#include"math.h"

void main()

{

float zahl;

printf("zahl eingeben:");

scanf("%f", &zahl);

if (zahl < 0)

{

printf("berechnung nicht möglich\n");

} else

{

printf("quadratwurzel ist %f\n", sqrt(zahl) );

}

}

Klicken Sie auf den kleinen Pfeil neben dem Käfer und selektieren Sie „Debug Configurations…“:

In dem neuen Fester selektieren Sie zunächst „C/C++ Application“, dann „Neu“ (das leere Blatt mit dem + links darüber):

3

Es wird eine neue Debug-Konfiguration erstellt, die üblicherweise schon die korrekten Voreinstellungen hat:

Nach einem Klick auf „Debug“ geht’s los! Diese Konfiguration müssen Sie 1x pro Projekt anlegen, dannach können Sie das Projekt immer debuggen, indem Sie direkt nach dem Käfer-Pfeil auf die gewünschte Konfiguration klicken:

Ähnlich der C-Perspektive von Eclipse für C-Programme, gibt es eine Debug-Perspektive zum Debuggen. Selektieren Sie daher im folgenden Fenster „Remember“ und „Yes“.

4

5

Orientierung in der Debug-Perspektive

Bekannt in der Debug-Perspektive ist das Source-View aus der normalen C-Perspektive. Dort sehen Sie immer noch die geöffneten C-Dateien (1). In diesem Fenster ist stets eine Zeile markiert (4). Diese gibt die aktuelle Position der Programmausführung an. Gesteuert wird die Programmausführung über das Debug-View (2). Alle aktuellen Variablen und deren Werte sind unter dem Variablen-View (3) einsehbar. (Sollten Sie das Variablen-View nicht sehen, können Sie es einschalten unter „Window“ – „Show View“ – „Variables“)

Parallel wird ein weiteres Konsolenfester extra für das Programm geöffnet, das vorerst leer ist (da das Programm noch nichts gemacht hat). Steuern können Sie den Programmablauf mit den Kontroll-Knöpfen des Debug-Views:

Von links nach rechts:

• Neustart des Debug-Laufes • Normale Programmfortführung (bis zum nächsten „Breakpoint“) ([F8]) • Pause (Programmablauf ist hier schon pausiert) • Beenden des Debug-Laufes

• Disconnect (für Debuggen eines Programms auf einem anderen Rechner, wird bei uns nicht verwendet)

• Einzelschritt (in eine Funktion hinein) ([F5]) • Einzelschritt (über eine Funktion hinweg) ([F6])

• Sprung aus einer Funktion heraus (findet erst Verwendung, wenn Sie selbst Funktionen schreiben) ([F7])

6

Erster Lauf

Führen Sie nun Einzelschritte in Ihrem Programm aus (mit dem Knopf oder [F6]) Dabei können Sie die Position im Programm sowie die Änderung der Variablen verfolgen. Sobald Sie an einem „printf“ stehen und einen weiteren Einzelschritt ausführen, können Sie danach im Konsolenfenster die Ausgaben des Programms beobachten:

Schreiten Sie dagegen über ein „scanf“, verschwindet die Positionsanzeige im Debugger so lange, bis Sie im Konsolenfenster etwas eingegeben haben. Erst dann können Sie weiter debuggen. Sehen Sie, wie sich die Zahl geändert hat?

Je nachdem, ob Sie eine negative Zahle eingegeben haben, können Sie einen unterschiedlichen Verlauf des Programms feststellen, wenn Sie weiter schrittweise debuggen

( oder [F6]). Sie können übrigens jederzeit hin- und herschalten zwischen Debug- und C-Perspektive, indem Sie die Knöpfe oben rechts verwenden. Falls „C/C++“ nicht sichtbar ist, finden Sie es hinter dem Pfeil:

7

Nicht vergessen: Unabhängig davon, ob Sie die Perspektive wechseln, laufen Debugger und Programm weiterhin. Wenn Sie fertig gedebuggt haben, (d.h. wieder Programmcode ändern oder compilieren wollen) sollten Sie das Programm auf jeden Fall beenden (mit dem roten Stopp-Knopf, s.o.)!

8

Fortgeschrittene

View: Breakpoints

Breakpoints werden verwendet, um den Debugger an bestimmten Stellen im normalen Programmablauf zu aktivieren (nicht schon/nur am Programmanfang). Diese Funktion ist nützlich, wenn das Programm länger wird oder längere Schleifen durchlaufen werden, deren Verlauf man sich nicht ansehen will. Breakpoints können an jede Zeile, die eine Anweisung enthält, gesetzt werden. Wird diese Zeile nicht erreicht (z.B. weil der Zweig des IFs nicht durchlaufen wird), hält der Debugger das Programm nicht an. Sie sollten also immer genau darauf achten, dass Sie den Breakpoint an der richtigen Stelle setzen, damit Sie den kritischen Punkt nicht verpassen – „zurückspulen“ kann der Debugger das Programm nämlich nicht. Setzen Sie den Breakpoint, indem Sie am Linken Rand der Zeile (links von der Zeilennummer, falls Sie diese aktiviert haben), doppelt klicken. Es erscheint dann ein kleiner Punkt an dieser Stelle und im Breakpoints-View wird die Zeile eingetragen:

Wenn Sie nun „Resume“ ([F8]) wählen, läuft Ihr Programm normal weiter, bis es auf einen Breakpoint stößt. Erst dort können Sie den Debugger wieder steuern. Dies ist nützlich, wenn Sie nur an einer bestimmten Stelle im Programm debuggen wollen, ohne jeden einzelnen Schritt von Anfang an zu sehen. Sie können einen Breakpoint auch vorübergehend deaktivieren (ohne ihn zu löschen), indem Sie den Haken davor im Breakpoints-View entfernen. Eine Programmcodezeile ist übrigens nur eine von von vielen Möglichkeiten, einen Breakpoint zu setzen. Im Internet gibt es mit Sicherheit noch einiges zu entdecken.

9

View: Disassembly

Hier sehen Sie deutlich den Zusamenhang zwischen Ihrem Source-Code und dem vom Compiler generierten Assembler-Code (rechts ist blau der Original-Code angezeigt. Dies ist eine Zusatzfunktion von Eclipse; er gehört natürlich nicht zum Assembler-Code):

Übringens: Wenn Sie unter den Projekteinstellungen die Optimierung beim Compilieren einstellen, wird der Assembler-Code merklich kürzer:

Die Optimierungsstufe des Compilers benutzt verschiedene Strategien, um Ihren Code zu verkürzen. Kürzerer Code braucht weniger Speicher und wird schneller ausgeführt, braucht aber auch länger zum Compilieren und erschwert das Debuggen, da teilweise Befehle umgestellt werden (im Assembler-Code), und das Programm (im Source-Coode) dann nicht so abläuft, wie Sie es erwarten. Wenn ein Programm „fertig“ ist, d.h. ausgeliefert wird, schaltet man üblicherweise den Optimierungsvorgang an und deaktiviert die Debugging-Informationen – dann ist ein Debuggen mit Sourcecode allerdings nicht mehr möglich.

10

11

View: Registers

Die Hardware selbst kennt keine Variablen. Für den Prozessor hat eine Variable keinen Namen, sondern nur noch eine Speicheradresse. Um mit den Speicherinhalten zu arbeiten, muss eine Kopie in einen bestimmten Bereich des Prozessors (sog. „Register“) angefertigt werden. Bei x86-Prozessoren heißen die allgemen nutzbaren Register: eax, ebx, ecx, edx Außerdem gibt es ein paar weitere für spezielle Aufgaben: esp = Stack Pointer für die Stackverwaltung esi, edi = Quell-und Ziel-Index eip = Instruktions-Zeiger, zeigt auf den als nächstes auszuführenden Befehl … Die genannten sind alle Ganzzahlig. Der Prozessor bietet auch Register für Fließkommawerte (st0, st1, st2,…, st7). Moderne Prozessoren enthalten noch weitere Registersätze speziell für Massendatenverarbeitung (XMM, MMX oder Altivec, etc) von Multimedia-Anwendungen. Übrigens: Der Instruktionszeiger ist gewissermaßen eine Schwachstelle und Ziel von bestimmten Angriffsmethoden. Schafft es ein Angreifer hier eine Adresse einzuschleusen, an der der Code eines Computer-Virus liegt, führt der Prozessor unbemerkt ein schädliches Programm aus. Diese Attacke nennt man "Buffer Overflow".

12

Callstack, Springen in/aus Funktionen

Findet ein Aufruf einer Funktion statt, für die der Debugger auch Sourcecode zur Verfügung hat, können Sie: In diese Funktion hieneinspringen („Step into“; [F5]), über Sie hinweg („Step over“; [F6]), oder aus ihr heraus („Step return“; [F7]) Wenn Ihr Programm eine Funktion aufruft, deren Ausführung Sie verfolgen wollen, springen Sie hienein. Wenn Sie dann genug gesehen haben und erst wieder in der rufenden Funktion weiter debuggen wollen, springen Sie heraus. Wenn Ihr Programm eine Funktion aufruft, deren Sourcecode nicht vorliegt, oder Sie erst nach ihrer Rückkehr weiter debuggen wollen, springen sie darüber hinweg. Sie können selbstverständlich auch in Funktionen ohne Sourcecode hineinspringen, sehen dann aber nur den Assembler-Code. Im Debug-View von Eclipse sehen Sie den sog. „Callstack“. Dort ist, wie auf einem Stapel, gelistet, in welcher Funktion sich das Programm gerade befindet. Hier ein Beispiel mit einer rekursiven Funktion: