| This chapter describes some tips and tricks for debugging numerical |
| programs which use GSL. |
| |
| @menu |
| * Using gdb:: |
| * Examining floating point registers:: |
| * Handling floating point exceptions:: |
| * GCC warning options for numerical programs:: |
| * Debugging References:: |
| @end menu |
| |
| @node Using gdb |
| @section Using gdb |
| @cindex gdb |
| @cindex debugging numerical programs |
| @cindex breakpoints |
| Any errors reported by the library are passed to the function |
| @code{gsl_error}. By running your programs under gdb and setting a |
| breakpoint in this function you can automatically catch any library |
| errors. You can add a breakpoint for every session by putting |
| |
| @example |
| break gsl_error |
| @end example |
| @comment |
| |
| @noindent |
| into your @file{.gdbinit} file in the directory where your program is |
| started. |
| |
| If the breakpoint catches an error then you can use a backtrace |
| (@code{bt}) to see the call-tree, and the arguments which possibly |
| caused the error. By moving up into the calling function you can |
| investigate the values of variables at that point. Here is an example |
| from the program @code{fft/test_trap}, which contains the following |
| line, |
| |
| @smallexample |
| status = gsl_fft_complex_wavetable_alloc (0, &complex_wavetable); |
| @end smallexample |
| |
| @noindent |
| The function @code{gsl_fft_complex_wavetable_alloc} takes the length of |
| an FFT as its first argument. When this line is executed an error will |
| be generated because the length of an FFT is not allowed to be zero. |
| |
| To debug this problem we start @code{gdb}, using the file |
| @file{.gdbinit} to define a breakpoint in @code{gsl_error}, |
| |
| @smallexample |
| $ gdb test_trap |
| |
| 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.16 (i586-debian-linux), |
| Copyright 1996 Free Software Foundation, Inc. |
| |
| Breakpoint 1 at 0x8050b1e: file error.c, line 14. |
| @end smallexample |
| |
| @noindent |
| When we run the program this breakpoint catches the error and shows the |
| reason for it. |
| |
| @smallexample |
| (gdb) run |
| Starting program: test_trap |
| |
| Breakpoint 1, gsl_error (reason=0x8052b0d |
| "length n must be positive integer", |
| file=0x8052b04 "c_init.c", line=108, gsl_errno=1) |
| at error.c:14 |
| 14 if (gsl_error_handler) |
| @end smallexample |
| @comment |
| |
| @noindent |
| The first argument of @code{gsl_error} is always a string describing the |
| error. Now we can look at the backtrace to see what caused the problem, |
| |
| @smallexample |
| (gdb) bt |
| #0 gsl_error (reason=0x8052b0d |
| "length n must be positive integer", |
| file=0x8052b04 "c_init.c", line=108, gsl_errno=1) |
| at error.c:14 |
| #1 0x8049376 in gsl_fft_complex_wavetable_alloc (n=0, |
| wavetable=0xbffff778) at c_init.c:108 |
| #2 0x8048a00 in main (argc=1, argv=0xbffff9bc) |
| at test_trap.c:94 |
| #3 0x80488be in ___crt_dummy__ () |
| @end smallexample |
| @comment |
| |
| @noindent |
| We can see that the error was generated in the function |
| @code{gsl_fft_complex_wavetable_alloc} when it was called with an |
| argument of @var{n=0}. The original call came from line 94 in the |
| file @file{test_trap.c}. |
| |
| By moving up to the level of the original call we can find the line that |
| caused the error, |
| |
| @smallexample |
| (gdb) up |
| #1 0x8049376 in gsl_fft_complex_wavetable_alloc (n=0, |
| wavetable=0xbffff778) at c_init.c:108 |
| 108 GSL_ERROR ("length n must be positive integer", GSL_EDOM); |
| (gdb) up |
| #2 0x8048a00 in main (argc=1, argv=0xbffff9bc) |
| at test_trap.c:94 |
| 94 status = gsl_fft_complex_wavetable_alloc (0, |
| &complex_wavetable); |
| @end smallexample |
| @comment |
| |
| @noindent |
| Thus we have found the line that caused the problem. From this point we |
| could also print out the values of other variables such as |
| @code{complex_wavetable}. |
| |
| @node Examining floating point registers |
| @section Examining floating point registers |
| |
| The contents of floating point registers can be examined using the |
| command @code{info float} (on supported platforms). |
| |
| @smallexample |
| (gdb) info float |
| st0: 0xc4018b895aa17a945000 Valid Normal -7.838871e+308 |
| st1: 0x3ff9ea3f50e4d7275000 Valid Normal 0.0285946 |
| st2: 0x3fe790c64ce27dad4800 Valid Normal 6.7415931e-08 |
| st3: 0x3ffaa3ef0df6607d7800 Spec Normal 0.0400229 |
| st4: 0x3c028000000000000000 Valid Normal 4.4501477e-308 |
| st5: 0x3ffef5412c22219d9000 Zero Normal 0.9580257 |
| st6: 0x3fff8000000000000000 Valid Normal 1 |
| st7: 0xc4028b65a1f6d243c800 Valid Normal -1.566206e+309 |
| fctrl: 0x0272 53 bit; NEAR; mask DENOR UNDER LOS; |
| fstat: 0xb9ba flags 0001; top 7; excep DENOR OVERF UNDER LOS |
| ftag: 0x3fff |
| fip: 0x08048b5c |
| fcs: 0x051a0023 |
| fopoff: 0x08086820 |
| fopsel: 0x002b |
| @end smallexample |
| |
| @noindent |
| Individual registers can be examined using the variables @var{$reg}, |
| where @var{reg} is the register name. |
| |
| @smallexample |
| (gdb) p $st1 |
| $1 = 0.02859464454261210347719 |
| @end smallexample |
| |
| @node Handling floating point exceptions |
| @section Handling floating point exceptions |
| |
| It is possible to stop the program whenever a @code{SIGFPE} floating |
| point exception occurs. This can be useful for finding the cause of an |
| unexpected infinity or @code{NaN}. The current handler settings can be |
| shown with the command @code{info signal SIGFPE}. |
| |
| @smallexample |
| (gdb) info signal SIGFPE |
| Signal Stop Print Pass to program Description |
| SIGFPE Yes Yes Yes Arithmetic exception |
| @end smallexample |
| |
| @noindent |
| Unless the program uses a signal handler the default setting should be |
| changed so that SIGFPE is not passed to the program, as this would cause |
| it to exit. The command @code{handle SIGFPE stop nopass} prevents this. |
| |
| @smallexample |
| (gdb) handle SIGFPE stop nopass |
| Signal Stop Print Pass to program Description |
| SIGFPE Yes Yes No Arithmetic exception |
| @end smallexample |
| |
| @noindent |
| Depending on the platform it may be necessary to instruct the kernel to |
| generate signals for floating point exceptions. For programs using GSL |
| this can be achieved using the @code{GSL_IEEE_MODE} environment variable |
| in conjunction with the function @code{gsl_ieee_env_setup} as described |
| in @pxref{IEEE floating-point arithmetic}. |
| |
| @example |
| (gdb) set env GSL_IEEE_MODE=double-precision |
| @end example |
| |
| |
| @node GCC warning options for numerical programs |
| @section GCC warning options for numerical programs |
| @cindex warning options |
| @cindex gcc warning options |
| |
| Writing reliable numerical programs in C requires great care. The |
| following GCC warning options are recommended when compiling numerical |
| programs: |
| |
| @comment Uninitialized variables, conversions to and from integers or from |
| @comment signed to unsigned integers can all cause hard-to-find problems. For |
| @comment many non-numerical programs compiling with @code{gcc}'s warning option |
| @comment @code{-Wall} provides a good check against common errors. However, for |
| @comment numerical programs @code{-Wall} is not enough. |
| |
| @comment If you are unconvinced take a look at this program which contains an |
| @comment error that can occur in numerical code, |
| |
| @comment @example |
| @comment #include <math.h> |
| @comment #include <stdio.h> |
| |
| @comment double f (int x); |
| |
| @comment int main () |
| @comment @{ |
| @comment double a = 1.5; |
| @comment double y = f(a); |
| @comment printf("a = %g, sqrt(a) = %g\n", a, y); |
| @comment return 0; |
| @comment @} |
| |
| @comment double f(x) @{ |
| @comment return sqrt(x); |
| @comment @} |
| @comment @end example |
| |
| @comment @noindent |
| @comment This code compiles cleanly with @code{-Wall} but produces some strange |
| @comment output, |
| |
| @comment @example |
| @comment bash$ gcc -Wall tmp.c -lm |
| @comment bash$ ./a.out |
| @comment a = 1.5, sqrt(a) = 1 |
| @comment @end example |
| |
| @comment @noindent |
| @comment Note that adding @code{-ansi} does not help here, since the program does |
| @comment not contain any invalid constructs. What is happening is that the |
| @comment prototype for the function @code{f(int x)} is not consistent with the |
| @comment function call @code{f(y)}, where @code{y} is a floating point |
| @comment number. This results in the argument being silently converted to an |
| @comment integer. This is valid C, but in a numerical program it also likely to |
| @comment be a programming error so we would like to be warned about it. (If we |
| @comment genuinely wanted to convert @code{y} to an integer then we could use an |
| @comment explicit cast, @code{(int)y}). |
| |
| @comment Fortunately GCC provides many additional warnings which can alert you to |
| @comment problems such as this. You just have to remember to use them. Here is a |
| @comment set of recommended warning options for numerical programs. |
| |
| @example |
| gcc -ansi -pedantic -Werror -Wall -W |
| -Wmissing-prototypes -Wstrict-prototypes |
| -Wtraditional -Wconversion -Wshadow |
| -Wpointer-arith -Wcast-qual -Wcast-align |
| -Wwrite-strings -Wnested-externs |
| -fshort-enums -fno-common -Dinline= -g -O2 |
| @end example |
| |
| @noindent |
| For details of each option consult the manual @cite{Using and Porting |
| GCC}. The following table gives a brief explanation of what types of |
| errors these options catch. |
| |
| @table @code |
| @item -ansi -pedantic |
| Use ANSI C, and reject any non-ANSI extensions. These flags help in |
| writing portable programs that will compile on other systems. |
| @item -Werror |
| Consider warnings to be errors, so that compilation stops. This prevents |
| warnings from scrolling off the top of the screen and being lost. You |
| won't be able to compile the program until it is completely |
| warning-free. |
| @item -Wall |
| This turns on a set of warnings for common programming problems. You |
| need @code{-Wall}, but it is not enough on its own. |
| @item -O2 |
| Turn on optimization. The warnings for uninitialized variables in |
| @code{-Wall} rely on the optimizer to analyze the code. If there is no |
| optimization then these warnings aren't generated. |
| @item -W |
| This turns on some extra warnings not included in @code{-Wall}, such as |
| missing return values and comparisons between signed and unsigned |
| integers. |
| @item -Wmissing-prototypes -Wstrict-prototypes |
| Warn if there are any missing or inconsistent prototypes. Without |
| prototypes it is harder to detect problems with incorrect arguments. |
| @item -Wtraditional |
| This warns about certain constructs that behave differently in |
| traditional and ANSI C. Whether the traditional or ANSI interpretation |
| is used might be unpredictable on other compilers. |
| @item -Wconversion |
| The main use of this option is to warn about conversions from signed to |
| unsigned integers. For example, @code{unsigned int x = -1}. If you need |
| to perform such a conversion you can use an explicit cast. |
| @item -Wshadow |
| This warns whenever a local variable shadows another local variable. If |
| two variables have the same name then it is a potential source of |
| confusion. |
| @item -Wpointer-arith -Wcast-qual -Wcast-align |
| These options warn if you try to do pointer arithmetic for types which |
| don't have a size, such as @code{void}, if you remove a @code{const} |
| cast from a pointer, or if you cast a pointer to a type which has a |
| different size, causing an invalid alignment. |
| @item -Wwrite-strings |
| This option gives string constants a @code{const} qualifier so that it |
| will be a compile-time error to attempt to overwrite them. |
| @item -fshort-enums |
| This option makes the type of @code{enum} as short as possible. Normally |
| this makes an @code{enum} different from an @code{int}. Consequently any |
| attempts to assign a pointer-to-int to a pointer-to-enum will generate a |
| cast-alignment warning. |
| @item -fno-common |
| This option prevents global variables being simultaneously defined in |
| different object files (you get an error at link time). Such a variable |
| should be defined in one file and referred to in other files with an |
| @code{extern} declaration. |
| @item -Wnested-externs |
| This warns if an @code{extern} declaration is encountered within a |
| function. |
| @item -Dinline= |
| The @code{inline} keyword is not part of ANSI C. Thus if you want to use |
| @code{-ansi} with a program which uses inline functions you can use this |
| preprocessor definition to remove the @code{inline} keywords. |
| @item -g |
| It always makes sense to put debugging symbols in the executable so that |
| you can debug it using @code{gdb}. The only effect of debugging symbols |
| is to increase the size of the file, and you can use the @code{strip} |
| command to remove them later if necessary. |
| @end table |
| |
| @comment For comparison, this is what happens when the test program above is |
| @comment compiled with these options. |
| |
| @comment @example |
| @comment bash$ gcc -ansi -pedantic -Werror -W -Wall -Wtraditional |
| @comment -Wconversion -Wshadow -Wpointer-arith -Wcast-qual -Wcast-align |
| @comment -Wwrite-strings -Waggregate-return -Wstrict-prototypes -fshort-enums |
| @comment -fno-common -Wmissing-prototypes -Wnested-externs -Dinline= |
| @comment -g -O4 tmp.c |
| @comment cc1: warnings being treated as errors |
| @comment tmp.c:7: warning: function declaration isn't a prototype |
| @comment tmp.c: In function `main': |
| @comment tmp.c:9: warning: passing arg 1 of `f' as integer rather than floating |
| @comment due to prototype |
| @comment tmp.c: In function `f': |
| @comment tmp.c:14: warning: type of `x' defaults to `int' |
| @comment tmp.c:15: warning: passing arg 1 of `sqrt' as floating rather than integer |
| @comment due to prototype |
| @comment make: *** [tmp] Error 1 |
| @comment @end example |
| |
| @comment @noindent |
| @comment The error in the prototype is flagged, plus the fact that we should have |
| @comment defined main as @code{int main (void)} in ANSI C. Clearly there is some |
| @comment work to do before this program is ready to run. |
| |
| @node Debugging References |
| @section References and Further Reading |
| |
| The following books are essential reading for anyone writing and |
| debugging numerical programs with @sc{gcc} and @sc{gdb}. |
| |
| @itemize @asis |
| @item |
| R.M. Stallman, @cite{Using and Porting GNU CC}, Free Software |
| Foundation, ISBN 1882114388 |
| |
| @item |
| R.M. Stallman, R.H. Pesch, @cite{Debugging with GDB: The GNU |
| Source-Level Debugger}, Free Software Foundation, ISBN 1882114779 |
| @end itemize |
| |
| @noindent |
| For a tutorial introduction to the GNU C Compiler and related programs, |
| see |
| |
| @itemize @asis |
| @item |
| B.J. Gough, @cite{An Introduction to GCC}, Network Theory Ltd, ISBN |
| 0954161793 |
| @end itemize |
| |
| |