\documentclass{lxarticle}
\usepackage{german}
\usepackage[utf8]{inputenc}
\usepackage{lxheaders}
\usepackage{lxextras}

\begin{document}

\section*{Debugging}

Beim Debugging geht es darum, Fehler in einem System zu finden.

Es gibt zwei grundlegend verschiedene Fehlerarten:

\begin{enumerate}
\item die Anwendung beendet sich unerwartet
	\begin{description}
	\item[auf Grund von Fehlern bei der Speicherveraltung] z.B. durch freigeben
	eines Speicherbereichs, welcher zuvor nicht alloziert wurde:
		\begin{lstlisting}
mem = malloc(5);
free(((char*)mem)+6000);
		\end{lstlisting}
		Die Programmausf\"uhrung wird abgebrochen, die glibc liefert alle
		Informationen, welche n\"otig sind, um den Fehler zu decodieren:
		\begin{lstlisting}
bash-4.0$ ./test
*** glibc detected *** ./test: free(): invalid pointer: 0x083c9778 ***
======= Backtrace: =========
/lib/libc.so.6[0x57c231]
./test[0x804841e]
/lib/libc.so.6(__libc_start_main+0xe6)[0x522a66]
./test[0x8048361]
======= Memory map: ========
0048e000-0048f000 r-xp 0048e000 00:00 0          [vdso]
004e8000-00508000 r-xp 00000000 fd:00 66065      /lib/ld-2.10.1.so
00508000-00509000 r--p 0001f000 fd:00 66065      /lib/ld-2.10.1.so
...
		\end{lstlisting}
		Mit folgendem Befehl kann festgestellt werden, von wo aus der Fehler
		getriggered wurde:
		
		\cmd{addr2line -e test 0x804841e}

		/home/linutronix/test.c:16

	\item[Speicherzugriffsfehler] z.B. durch Zugriff auf nicht allozierten
	Speicher. Die Programmausf\"uhrung wird abgebrochen:
	\begin{lstlisting}
bash-4.0$ ./test
Speicherzugriffsfehler
	\end{lstlisting}
	Solche Fehler lassen sich am Besten mit einer gdb Backtrace Ausgabe (siehe
	unten) finden.
	\end{description}
\item die Anwendung verh\"alt sich anders als erwartet.

	In einem solchen Fall sollte man sich zu erst Gedanken dar\"uber machen,
	durch was das Fehlverhalten getriggered werden k\"onnte. Sofort mit einem
	Debugger durch den Code zu steppen ist kein guter Ansatz, da so evt. ein
	Fehler gefunden und behoben wird der aber nicht zwangsl\"aufig die Ursache
	f\"ur das Problem war. Dies kann dazu f\"uhren, dass die Fehlersuche eines
	sp\"ater entdeckten Bugs um so komplexer und schwieriger wird.

	Hat man sich Gedanken \"uber die Ursachen gemacht, kann man gezielt mit den
	nachfolgend beschriebenen Techniken dem Fehler auf den Grund gehen.
\end{enumerate}

\subsection*{Einfach aber wirkungsvoll: \cmd{printf}, \cmd{fprintf}}

Da Ausgaben auf die \cmd{stdout} gecached werden, ist es zuverl\"assiger f\"ur
Debugging \cmd{stderr} zu verwenden und nach jeder Ausgabe noch den cash flush
zu triggern. Ein Debug konstrukt k\"onnte z.B. folgenderma\ss en aussehen:

\begin{lstlisting}
fprintf(stderr, "debug test\n");
fflush(stderr);
\end{lstlisting}

F\"ur gew\"ohnlich wird \cmd{(f)printf} beim Debuggen f\"ur drei Zwecke
verwendet:

\begin{enumerate}
\item Tracing:
\begin{lstlisting}
fprintf(stderr, "%s, called\n", __FUNCTION__);
fflush(stderr);
\end{lstlisting}
\item Ausgabe von Variablenwerten:
\begin{lstlisting}
fprintf(stderr, "Wert: %d, Wert (hex): %x\n", wert, wert);
fflush(stderr);
\end{lstlisting}
\item Ausgabe von Adressen:
\begin{lstlisting}
fprintf(stderr, "Adresse: %p, Wert: %x\n", wert, wert);
fflush(stderr);
\end{lstlisting}
\end{enumerate}

Nat\"urlich k\"onnen die Methoden kombiniert werden.

Soll das Debugging zur Compilezeit zu- und abschaltbar sein, kann z.B. folgendes
Pr\"aprozessor konstrukt hilfreich sein:

\begin{lstlisting}
#ifdef DEBUG
#define debug(...)  fprintf(stderr, "%s:%d - ", __FILE__, __LINE__); \
                    fprintf(stderr, __VA_ARGS__); fflush(stderr)
#else
#define debug(...)  ((void)0)
#endif

int main(int argc, char **argv)
{
	int val = 3;
	debug("addr: %p - val: %d\n", &val, val);
	return 0;
}
\end{lstlisting}

Resultat:
\begin{lstlisting}
bash-4.0$ gcc -o test test.c
bash-4.0$ ./test
bash-4.0$
bash-4.0$ gcc -o test -DDEBUG test.c
bash-4.0$ ./test
test.c:22 - test: 0xbfef8694 - val: 3
bash-4.0$
\end{lstlisting}

\subsubsection*{deutlich flexibler: Logging Frameworks}

Wird f\"ur Debugging oder Logging mehr Flexibilit\"at ben\"otigt, sollte ein
Logging Framework verwendet werden. Prominente Vertretter f\"ur verschiedene
Programmiersprachen sind zum Beispiel:

\begin{itemize}
\item Log for C++ (http://log4cpp.sourceforge.net)
\item log4c (http://log4c.sourceforge.net)
\item log4j (http://logging.apache.org/log4j) - f\"ur JAVA
\end{itemize}

Diese Frameworks bieten die M\"oglichkeit das Loggingverhalten zum Startpunkt
der Anwendung, oder teilweise sogar, w\"ahrend der Laufzeit zu ver\"andern.

Es wird beim Loggen mindestens zwischen den Kategorien Debug, Error und Info
unteschieden, welche jeweils an verschiedene Ziele geleitet werden k\"onnen:
Datei, syslog, \dots

\subsubsection*{Backtraces ausgeben}

Die glibc bietet auch die M\"oglichkeit an einer beliebigen Stelle in der
Applikation die Ausgabe eines Function Call Traces (Backtrace) aus zu geben.

Dies ist hilfreich, wenn man herausfinden will, \"uber welche Funktionsaufrufe
die aktuelle Codezeile aufgerufen wurde.

Beispiel:
\begin{lstlisting}
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_BT_DEPTH 16

void btrace(int depth) {
  const void *trace[depth];
  char **messages = (char **) NULL;
  unsigned int i = 0;
  int trace_size = backtrace(trace, depth);
  messages = backtrace_symbols(trace, trace_size);
  fprintf(stderr, "***BACKTRACE***\n");
  for(; i < trace_size; i++)
      fprintf(stderr, "%s\n", messages[i]);
  fprintf(stderr, "***************\n");
  fflush(stderr);
}
void pong() {
#ifdef DEBUG
  btrace(DEFAULT_BT_DEPTH);
#endif
}
void ping() {
  pong();
}
int main(int argc, char **argv) {
  ping();
  return 0;
}
\end{lstlisting}

Anwendung:
\begin{lstlisting}
bash-4.0$ gcc -g -o test -DDEBUG test.c
bash-4.0$ ./test
***BACKTRACE***
./test [0x804859b]
./test [0x8048663]
./test [0x8048670]
./test [0x804867d]
/lib/libc.so.6(__libc_start_main+0xe6) [0x522a66]
./test [0x8048441]
***************
bash-4.0$ addr2line -e test 0x804867d
/home/linutronix/test.c:27
bash-4.0$ addr2line -e test 0x8048670
/home/linutronix/test.c:24
\end{lstlisting}

\subsection*{Tracing mit strace}

Mit \cmd{strace} k\"onnen Systemaufrufe getraced werden. Es kann ermittelt
werden welche Systemaufrufe von der Applikation zu welchem Zeitpunkt aufgerufen
wurden und wieviel Zeit der Kernel ben\"otigt um den Aufruf ab zu arbeiten.

Ein \cmd{strace} ist folgenderma\ss en zu interpretieren:

\begin{lstlisting}
bash-4.0$ strace -ftttT ./test
1245687387.873813 execve("./test", ["./test"], [/* 18 vars */]) = 0 <0.000233>
^                 ^          ^                                  ^    ^
|                 |          |                                  |    |
|                 |          +- Argumente                       |    +- Dauer
+- Zeitstempel    +- syscall                                    +- Rueckgabewert
\end{lstlisting}

Die Option
\begin{itemize}
\item \cmd{-f} sorgt daf\"ur, dass auch Kindprozesse mit getraced werden,
\item \cmd{-ttt} blendet den pr\"azisen Zeitstempel ein,
\item \cmd{-T} gibt die Dauer des Systemaufrufs mit an.
\end{itemize}

\subsection*{Debugging mit gdb}

gdb ist ein textbasierter Debugger. Eine Applikation kann z.B mit dem Befehl
\cmd{gdb ./test} in gdb geladen werden und anschliessend mit dem Befehl
\cmd{run} ausgef\"uhrt werden.

Die 'gdb Reference Card' listet viele n\"utzliche Befehle, z.B. zum Setzen von
Breakpoints und zum Steppen durch die Applikation.

Es gibt auch grafische Frontends f\"ur gdb, wie z.B. \cmd{ddd}
(http://www.gnu.org/software/ddd).

\subsubsection*{Core Dumps analysieren}

\cmd{gdb} eignet sich au\ss erdem hervorragend zur Analyse von so genannten
'core dumps'. Ein 'core dump' entsteht, wenn eine Applikation unerwartet beendet
wird. Gegebenenfalls mu\ss  mit \cmd{ulimit -c unlimited} vor dem
Applikationsstart das Limit f\"ur maximal zul\"assige core dumps erh\"oht
werden.

Beispiel:
\begin{lstlisting}
bash-4.0$ ulimit -c unlimited
bash-4.0$ ./test
Speicherzugriffsfehler (Speicherabzug geschrieben)
bash-4.0$ gdb ./test core.12371
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu"...
Reading symbols from /lib/libc.so.6...done.
Loaded symbols for /lib/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
[New process 12371]
#0  0x08048661 in pong () at test.c:22
22              *null_ptr = 3;
(gdb)
(gdb) bt
#0  0x08048661 in pong () at test.c:22
#1  0x08048671 in ping () at test.c:26
#2  0x0804867b in main (argc=1, argv=0xbf9921f4) at test.c:29
(gdb)
\end{lstlisting}

Der Befehl \cmd{bt} steht f\"ur Backtrace. Die zuletzt aufgerufene Funktion
steht an Position \#0. Zeigt \#0 nicht auf die eigene Applikationen sondern z.B.
in die glibc ist es eher warscheinlich, dass der Fehler durch eine nicht
spezifiezierte Verwendung einer glibc Funktion getriggered wurde. Man sollte in
diesem Fall zuerst untersuchen, ob die erste Funktion in der eigenen Software,
einen korrekten glibc call absetzt, bevor die glibc gedebugged wird.

%\subsubsection*{Remote-Debuggging mit gdbserver}

%Text

\end{document}
