summaryrefslogtreecommitdiff
path: root/application-devel
diff options
context:
space:
mode:
authorManuel Traut <manut@linutronix.de>2009-06-22 20:21:10 +0100
committerManuel Traut <manut@linutronix.de>2009-06-22 20:21:10 +0100
commite472102af365fe62bfff720b36d3b5f2b5128179 (patch)
tree9b46183e782dbf53ec29a171c43271d512366f79 /application-devel
parent46027380cf6cd71408a70e985430328acaa2dc2e (diff)
application-devel: _REALLY_ added debugging section
Signed-off-by: Manuel Traut <manut@linutronix.de>
Diffstat (limited to 'application-devel')
-rw-r--r--application-devel/app-debugging/handout_app-debugging_de.tex290
1 files changed, 284 insertions, 6 deletions
diff --git a/application-devel/app-debugging/handout_app-debugging_de.tex b/application-devel/app-debugging/handout_app-debugging_de.tex
index 4c391b2..89c71c2 100644
--- a/application-devel/app-debugging/handout_app-debugging_de.tex
+++ b/application-devel/app-debugging/handout_app-debugging_de.tex
@@ -1,17 +1,295 @@
-\documentclass{article}
+\documentclass{lxarticle}
\usepackage{german}
\usepackage[utf8]{inputenc}
+\usepackage{lxheaders}
+\usepackage{lxextras}
\begin{document}
-\section*{Titel}
+\section*{Debugging}
-\subsection*{Abschnitt1}
+Beim Debugging geht es darum, Fehler in einem System zu finden.
-Text
+Es gibt zwei grundlegend verschiedene Fehlerarten:
-\subsection*{Abschnitt2}
+\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}
-Text
+ /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}