diff options
| author | Manuel Traut <manut@linutronix.de> | 2009-06-22 20:21:10 +0100 |
|---|---|---|
| committer | Manuel Traut <manut@linutronix.de> | 2009-06-22 20:21:10 +0100 |
| commit | e472102af365fe62bfff720b36d3b5f2b5128179 (patch) | |
| tree | 9b46183e782dbf53ec29a171c43271d512366f79 /application-devel | |
| parent | 46027380cf6cd71408a70e985430328acaa2dc2e (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.tex | 290 |
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} |
