\documentstyle[german,12pt]{article}
\parindent0cm
\parskip\baselineskip
%\addtolength{\topmargin}{-3.5cm}
%\addtolength{\textheight}{5cm}

%% Vorsich hier, die Druckertreiber machen noch zus"atzliche R"ander
\oddsidemargin-0.1cm
\evensidemargin-0.1cm
%% 21 cm - 2 x 2.54 cm
\textwidth15.7cm
\topmargin-1.1cm
%% 29 cm - 2 x 2.54
\textheight28.92cm  
\input epsf

\begin{document}

\Huge

\vspace{-2.5cm}

RUS Minikurs 

\huge

Symbolisches Debuggen mit gdb und dbx 

\vspace{0.5cm}
\vspace{0.5cm}

J. Scheurich ICA 2 \\
scheurich@rus.uni-stuttgart.de

\newpage

\huge
\begin{enumerate}

\vspace{6.0cm}

\item Anwendungsgebiet eines Symbolischen Debuggers

\vspace{1.5cm}

\item Compilierung 

\vspace{1.5cm}

\item  Aufruf

\vspace{1.5cm}

\item Die grundlegenden Befehle

\vspace{1.5cm}

\item Fehler, die zu einem Programmabsturz f\"uhren

\vspace{1.5cm}

\item Beispiel und kleinere \"Ubungen

\end{enumerate}

\newpage

\huge
Anwendungsgebiet eines Symbolischen Debuggers
\Large

\vspace{0.5cm}

Debugger erlauben das Betrachten von Programm-abl\"aufen und -zwischen\-ergebnissen 
ohne den Quellcode zu ver\"andern.

Am n\"utzlichsten sind Debugger bei Programm\-abst\"urzen.

Unter Unix wir bei einem Programmabsturz ein 'core'-files angelegt.

Dieses 'core'-file enth\"alt den Speicherinhalt des abgest\"urzten Pro\-gramms.

Der Debugger kann aus dem 'core'-file die Absturz-Stelle und 
Variablen\-inhalte ermitteln. 

Debugger sind hier der konventionellen Methode der Fehlersuche durch 
verschiedene Programmausgaben \"uberlegen, da unter Unix die Ausgabe wegen 
der Pufferung nicht immer wie gedacht arbeitet (Ein Beispiel dazu folgt 
sp"ater).

Debugger sind besonders n"utzlich, wenn die problematische Stelle 
ungef"ahr klar ist. Zum Beispiel wenn man aus dem 'core'-file 
die Absturz-Stelle ermitteln kann oder man sich sowieso nur f"ur den
Inhalt einer Variablen an einer bestimmten Stelle interessiert. 
Ansonsten ist der Rechenzeit- oder Tippaufwand unvern"unftig hoch.

\newpage

\huge
Compilierung
\Large

Der Debugger ben\"otigt Informationen \"uber Namen und Adresse der 
verwendeten Symbole (Variablen- und Funktionen-\-namen) sowie Referenzen 
zum Quellcode.

Diese Informationen wird w\"ahrend der Compilierung und Bindung des Programms 
erzeugt und im ausf\"uhrbaren File abgespeichert.

Die Standartoption unter Unix zum {\bf g}enerieren symbolischer Inform\-ationen
ist \huge -g \Large 

\large
cc   {\bf -g} file.c  ( bzw. gcc {\bf -g} file.c ) \\
f77  {\bf -g} file.f \\
pas  {\bf -g} file.p \\
g++  {\bf -g} file.cc \\
gcc  {\bf -g} file.adb \\
usw.
\Large 

Es wird dringend empfohlen, {\bf nicht} die Option \huge -O \Large fuer 
{\bf O}ptimier\-ung zu benutzen. Nur wenige Compiler unterst\"utzen die gleichzeitige
Benutzung von {\bf -g} und {\bf -O} (u.U. gcc/g++), diese geben dies aber in 
der Manpage ausdr\"ucklich bekannt.

{\bf Achtung:}  Nicht bei allen Implementierungen unterst\"utzt der gcc/g++
Compiler die {\bf -g} Option.

Bei der Verwendung von Translatoren, die von einer Programmier\-sprache in eine 
andere wandeln, kann in einigen F\"allen die korrekte Generierung
symbolischer Information erzwungen werden (bekannt ist das fuer f2c).
\newpage

\huge
Aufruf
\Large

Bevor der Debugger aufgerufen wird, ist unter Umst\"anden zuerst die 
Erzeugung eines 'core'-files von Interesse.

Die Erzeugung eines 'core'-files wird mit den shell-internen limit Befehlen
in der ksh {\bf ulimit -c} bzw. in der csh {\bf limit coredumpsize} 
(siehe manpages der shells) gesteuert.

{\bf ulimit -c} zeigt die maximal m\"ogliche L\"ange eines 'core'-files.
Mit {\bf ulimit -c neue\_l\"ange} kann diese L\"ange gesetzt werden.
Bei der L\"ange 0 wird das Schreiben eines 'core'-files unterdr\"uckt.

Beim Aufruf selbst muss man zwischen den Debuggern {\bf dbx} und {\bf gdb}
unterscheiden.

F\"ur {\bf dbx}: \\
\large 
{\bf dbx ausf\"uhrbares\_programm} 
\Large 
Das 'core'-file wird n"otigenfalls automatisch dazugeladen. \\

\Large 
F\"ur {\bf gdb}: \\
\large 
{\bf gdb ausf\"uhrbares\_programm core} \\
\Large
oder \\
\large
{\bf gdb ausf\"uhrbares\_programm} \\
\Large
wenn kein 'core'-file vorhanden ist.\\

{\bf dbx} hat in der Regel zus"atzlich eine Option, mit der auf einen laufenden 
Prozess mit einer bestimmen Prozessnummer (PID) zugegriffen werden kann.
Neuere Versionen von gdb erlauben die Angabe der PID anstatt des corefiles.
Damit ist man in der Lage, auch parallelisierte Programme zu debuggen.


Es gibt f\"ur diese Debugger auch graphische Implementationen \\
{\bf dbx} : xdbx, xde (AIX), cvd/edge (IRIX), ddd \\
{\bf gdb} : xxgdb, ddd

\newpage

{\bf gdb} wurde f"ur den gcc/g++ -Compiler entwickelt, {\bf dbx} f"ur die
Compiler des jeweiligen Computerherstellers.
Es ist derher nicht gesagt, da\3 die Debugger etwas mit
dem Output des anderen Compilers anfangen k"onnen.

{\bf gdb} bietet derzeit keine echte Sprachunterst"utzung f"ur andere
Sprachen als {\bf C} oder {\bf C++}. \\
Manche {\bf dbx} Versionen bietet immer noch keine echte 
Sprachunterst"utzung f"ur {\bf C++}. 

 
\newpage
\huge
Die grundlegenden Befehle
\Large

Bis auf ein paar Ausnahmen heissen die einfachsten Befehle in {\bf dbx} und
{\bf gdb} gleich. 

Mit dem Kommando {\bf help} kann interaktiv die genaue Syntax eines
Befehls ermittelt werden. Im Rahmen dieses Minikurses wird nur die 
wichtigste Anwendung eines Befehls besprochen. 

Alle Befehle k\"onnen eindeutig abgek\"urzt werden.

In {\bf dbx} kann man das Sourcefile mit dem {\bf file sourcefile} Befehl 
wechseln. Zeilennummern k"onnen mit {\bf gdb} auch in der Form
{\bf sourcefile:zeilennummer} verwendet werden. 

{\bf list argument} dient zur Orientierung und listet ein St\"uck des 
Quellcodes. M"ogliche Argumente sind eine Zeilennummer oder der Name eines
Unterprogramms. Ohne Argument wird das n\"achste St\"uck gelistet. 

Um das Programm im Debugger zu starten wird \\
{\bf run kommandozeilenargumente} verwendet.

Der Befehl {\bf where} dient zur Ausgabe der Stelle, an dem das Programm
gestoppt wurde (z.B. bei einem Programmabsturz). Bei {\bf gdb} wird
immer die gesamte Unter\-programms\-vor\-geschichte einschlie\3lich der
der Argumente ausgegeben. Bei {\bf dbx} muss der Befehl u. U. in der Form \\
{\bf where zahl\_unterprogramme} angeben werden, um den gleichen Effekt
zu erreichen.

\newpage

Mit {\bf print argument} werden Adressen, Variablen, Zeiger, Speicher\-inhalte 
oder sogar gr"o\3ere Ausdr"ucke (z.B. arrays/structs) ausgegeben.
Zus"atzlich dazu ergibt der Operator {\bf \&} die Adresse einer Variable, der 
Operator {\bf *} den Inhalt einer Adresse (z.B. print * 0x00b4). Die Debugger 
{\bf dbx} und {\bf gdb} verstehen in der Regel immer die Syntax der 
Sprache {\bf C}.

Der {\bf dbx}-Befehl {\bf trace} bewirkt, da\3 alle ausgef\"uhrten Befehle
(z.B. nach {\bf run}) ausgegeben werden. F\"ur diesen Befehl gibt es in
dieser Form keine Entsprechung in {\bf gdb}. Neuere Versionen von {\bf dbx}
akzeptieren diesen Befehl nicht mehr.

Der {\bf dbx}-Befehl in der Form {\bf trace variablenname} gibt jede \"Anderung 
der Variablen gemeldet. Die {\bf gdb} Entsprechung dazu hei\3t 
{\bf watch variablenname}. H"aufig wird dabei ein sogenannter 
Hardware-Watchpoint unterst"utzt. Dabei wird jede "Anderung der entsprechenden
Speicheradresse gemeldet, auch wenn die dazu\-geh"orende Programmzeile 
scheinbar nichts mit dieser Adresse zu tun haben scheint (beispielsweise
wenn man "uber die Grenzen eines Arrays hinausliest, und dabei den
Speicherbereich der n"achsten Variablen erwischt). Wenn ein System
keinen Hardwarewatchpoint zur Verf"ugung stellt, wird diese Art des 
Debuggens sehr zeitaufwendig, und sollte wenn m"oglich nur
lokal eingesetzt werden.

Um einen Breakpoint zu setzen, bei dem ein Programmlauf ge\-stoppt werden kann,
wird mit {\bf gdb} der Befehl {\bf break} und mit {\bf dbx} der Befehl 
{\bf stop} benutzt. {\bf dbx} unterscheidet zusaetzlich zwischen 
{\bf stop at zeilennummer} und \\
{\bf stop in routine}. 

\newpage

Um nach Erreichen eines Breakpoints den Programmlauf fortzusetzen, wird
{\bf cont} benutzt.

Um nach Erreichen eines Breakpoints die n\"achste Zeile auszuf\"uhren gibt 
es den Befehl {\bf next}.

Um nach Erreichen eines Breakpoints den n\"achsten Programmbefehl (der auch
in ein Unterprogramm f\"uhren kann) auszuf\"uhren gibt es den 
Befehl {\bf step}.

Der Befehl f"ur die Ausgabe einer Liste der Break- und Watchpoints
in {\bf dbx} heisst {\bf status}, w"ahrend daf"ur in {\bf gdb} daf"ur 
der Befehl {\bf info breakpoints} benutzt wird. In dieser Liste 
sind Break- und Watchpoints durchnummeriert.

Mit dem Befehl {\bf delete nummer\_der\_liste} kann der entsprechende Break- bzw.
Watchpoint aus der Liste gel"oscht werden.

Um einen Breakpoint oder Watchpoint nur unter einer bestimmten Bedingung
aktiv werden zu lassen, wird in {\bf dbx} der Ausdruck \\ 
{\bf if logischer\_ausdruck} an den {\bf stop} oder {\bf trace} Befehl
angeh"angt (z.B. stop at 19 if x == 3). \\
Bei {\bf gdb} wird ein extra Befehl \\ {\bf condtion nummer\_der\_liste 
logischer\_ausdruck} ben"otigt (z.B. condition 1 i==3). 

Mit {\bf quit} wird der Debugger verlassen.

\newpage
\Large
\begin{tabular}{l|l|l}
                                      & dbx      & gdb \\ 
\hline
Interaktive Hilfe                     & help     & help \\
\hline
Quellcode anschauen                   & list     & list \\
\hline
Variablen ausgeben                    & print    & print \\
\hline
Programm neu starten                  & run      & run  \\
\hline
Programmablauf beobachten             & trace    &  \\
\hline
Aufrufbaum betrachten                 & where    & where \\
\hline
Breakpoint setzen                     & stop at & break \\
\hline
                                      & stop in & break \\
\hline
Programmlauf fortsetzen               & cont     & cont \\
\hline
N\"achste Programmzeile               & next     & next     \\
\hline
N\"achster Programmbefehl             & step     & step     \\
\hline
Break/Watchpointliste                 & status   & info breakpoints \\
\hline
Bedingter Break/Watchpoint            & befehl if  & befehl \\
                                      &            & condition \\
\hline
Debugger verlassen                    & quit     & quit     \\
\end{tabular}

\newpage

\huge
Fehler, die zu einem Programmabsturz f\"uhren
\Large

{\bf Floating point exception} \\
Tritt auf bei mathematischen Operationen, bei denen ein Ergebnis nicht 
errechnet werden kann: negative Wurzel, negativer Logarithmus usw.

{\bf Segmentation violation} \\
Das Programm versucht auf Speicherbereiche zuzugreifen, die nicht ihm 
geh\"oren. Das kann passieren, wenn \"uber inkorrekte Zeiger zugegriffen wird,
wenn Arraygrenzen stark \"uberschritten werden, versucht wird eine Konstante 
zu \"uberschreiben, oder wenn die Zahl der Aufrufargumente beim 
Unterprogrammaufruf nicht stimmt.

{\bf Memory fault} \\
Es ist ein Fehler bei der Speicherorganisation aufgetreten .
Dies wird zum Beispiel durch einen Aufruf der free()-funktion mit einem
Pointer, der nicht auf einen Bereich zeigt, der mit alloc() angefordert 
wurde, erreicht. 

{\bf Bus error} \\
Eine unm\"ogliche Anforderung an den Speicherbus trat auf. \\
Wichtig ist in diesem Zusammenhang der sogenannte Alignfehler. \\
Einige Prozessoren (vor allem RISC-Prozessoren) k\"onnen wegen ihrer 
Bus\-organisation beim Speicher\-zugriff auf mehrere Bytes gleich\-zeitig 
(typischerweise beim Zugriff auf einen 4 Byte langen Integer/Float-wert) nur 
zugreifen, wenn die Adresse ein mehrfaches der Anzahl der Bytes ist. 

\newpage

{\bf Illegal instruction} \\
Das Programm wurde an einer Stelle abgebrochen, an der ein unbekannter
Maschinenbefehl aufgetreten ist. Das kann durch den falschen Aufruf von  
longjmp() oder einem Compilerfehler passiert sein.

{\bf Programmabst\"urze sind nicht portabel.} 

Zum Beispiel f\"uhren nicht alle Architekturen per default eine 
{\bf floating point exception} bei einer Division durch Null durch. \\
Eine unterschiedliche Speicherorganisation bei verschiedenen Systemen kann zu 
unterschiedlichen Verhalten bei {\bf Segmentation violation} und 
{\bf Memory fault} f\"uhren. \\
Verschiedenes Alignverhalten kann ebenfalls zu Problemen bei der Portierung 
von Programmen f\"uhren. \\

\newpage

\Huge
Beispiele
\normalsize

\begin{verbatim}

$ulimit -c
unlimited
$f77 -g test.f
$a.out
%1     493 Segmentation violation - core dumped a.out
$gdb a.out core

GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.10.pl1 (i486-unknown-linux), 
Copyright 1993 Free Software Foundation, Inc...
(gdb) list
1		program nix
2		character *10 i,j
3	        i='1'
4	        j='2'
5	        call swap(i,j)
6	        call swap(j,'3')
7	        end
8	        subroutine swap(i,j)
9	        character*(*) i,j
10	        character*(100) temp
(gdb) list
11	        temp=i
12	        i=j
13	        j=temp
14	        end
#0  0x2ba in s_copy ()
(gdb) where
#0  0x2ba in s_copy ()
#1  0x10a in swap_ (i=0x118ac "1         ", j=0x54 "3", i_len=10, j_len=1)
    at test.f:13
#2  0xbc in MAIN__ () at test.f:6
#3  0x293 in main ()
(gdb) break 2
Breakpoint 1 at 0x64: file test.f, line 2.
\end{verbatim}
\newpage
\begin{verbatim}

(gdb) run
Starting program: /home/mufti/f/a.out 

Breakpoint 1, MAIN__ () at test.f:2
2		character *10 i,j
(gdb) print i
$1 = "\000\000\000\000\000\000\000\000\000"
(gdb) break test.f:5
Breakpoint 2 at 0x93: file test.f, line 5.
(gdb) continue
Continuing.

Breakpoint 2, MAIN__ () at test.f:5
5	        call swap(i,j)
(gdb) print i
$2 = "1         "
(gdb) print j
$3 = "2         "
(gdb) next
6	        call swap(j,'3')
(gdb) print i
$4 = "2         "
(gdb) print j
$5 = "1         "
(gdb) list test.f:1
1		program nix
2		character *10 i,j
3	        i='1'
4	        j='2'
5	        call swap(i,j)
6	        call swap(j,'3')
7	        end
8	        subroutine swap(i,j)
9	        character*(*) i,j
10	        character*(100) temp
(gdb) step
swap_ (i=0x118ac "1         ", j=0x54 "3", i_len=10, j_len=1) at test.f:11
11	        temp=i
(gdb) print i
$6 = 0x118ac "1         "
(gdb) next
12	        i=j
(gdb) next
13	        j=temp
(gdb) next

\end{verbatim}
\newpage
\Large 
Beispiel zur Pufferung (Linux 0.99, bash, gcc, gdb)
\normalsize
\begin{verbatim}
/home/mufti/c$ bash
/home/mufti/c$ ulimit -c
unlimited
/home/mufti/c$ cat pufferfehler.c
#include <stdio.h>

main()
{
int i;
for (i=1;i<=ZAHL;i++)
   printf("%d ",i);
i=i/0;
printf("%d\n",i);
}
/home/mufti/c$ gcc -g -DZAHL=283 pufferfehler.c
/home/mufti/c$ a.out
Floating point exception (core dumped)
/home/mufti/c$ gcc -g -DZAHL=284 pufferfehler.c
/home/mufti/c$ a.out
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 5
7 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 Floating point e
xception (core dumped)
/home/mufti/c$ gdb a.out core
GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.10.pl1 (i486-unknown-linux), 
Copyright 1993 Free Software Foundation, Inc...
#0  0xac in main () at pufferfehler.c:8
8	i=i/0;
(gdb) print i
$1 = 285
(gdb) 
\end{verbatim}
\newpage

\end{document}



