This chapter describes some tips and tricks for debugging numerical programs which use GSL.

Using gdb
=========

Any errors reported by the library are routed through the function 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

     break gsl_error

into your .gdbinit file in the directory where your program is started. If the breakpoint catches an error then you can use a backtrace (bt) to see the call-tree, and the arguments which possibly caused the error. By moving into the caller (up, up) you can investigate the values of variable at that point.

Here is an example from the program fft/test_trap, which contains the following line,

     status = gsl_fft_complex_wavetable_alloc (0, &complex_wavetable);

The function 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 gdb, using the file .gdbinit to define a breakpoint in gsl_error,

     bash$ 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.

When we run the program this breakpoint catches the error and shows the reason for it.

     (gdb) run
     Starting program: /home/bjg/gsl/fft/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)

The first argument of gsl_error is always a string describing the error. Now we can look at the backtrace to see what caused the problem,

     (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__ ()

We can see that the error was generated in the function gsl_fft_complex_wavetable_alloc when it was called with an argument of N=0. The original call came from line 94 in the file test_trap.c.

By moving up to the level of the original call we can find the line that caused the error,

     (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);

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 complex_wavetable.

GCC warning options for numerical programs
==========================================

Writing reliable numerical programs in C requires great care. The following GCC warning options are recommended when compiling numerical programs:

     gcc -ansi -pedantic -Werror -Wall -W -Wmissing-prototypes
       -Wstrict-prototypes -Wtraditional -Wconversion -Wshadow
       -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings
       -Waggregate-return -fshort-enums -fno-common -Wnested-externs
       -Dinline= -g -O4

For details of each option consult the manual Using and porting GCC. The following table gives a brief explanation of what types of errors these warnings catch.

-ansi -pedantic
     Use ANSI C, and reject any non-ANSI extensions. These flags help in writing portable programs that will compile on other systems.

-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.

-Wall
     This turns on a set of warnings for common programming problems. You need -Wall, but it is not enough on its own, as explained above.

-O4
     Turn on optimization. The warnings for uninitialized variables in -Wall rely on the optimizer to analyze the code. If there is no optimization then the warnings aren't generated.

-W
     This turns on some extra warnings not included in -Wall, such as missing return values and comparisons between signed and unsigned integers.

-Wmissing-prototypes -Wstrict-prototypes
     Warn if there are any missing or inconsistent prototypes. If your prototypes are missing then you will never detect problems with incorrect arguments. If your prototypes are inconsistent then you already have a problem.

-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.

-Wconversion
     The main use of this option is to warn about conversions from signed to unsigned integers. For example, unsigned int x = -1. If you need to perform such a conversion you can use an explicit cast.

-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.

-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 void, if you remove a const cast from a pointer, or if you cast a pointer to a type which has a different size, causing an invalid alignment.

-Wwrite-strings
     This option gives string constants a const qualifier so that it will be a compile-time error to attempt to overwrite them.

-Waggregate-return
     Warn if any functions that return structures or unions are defined or called. Some older compilers might have problems with such a construct.

-fshort-enums
     This option makes the type of enum as short as possible. Normally this makes an enum different from an int. Consequently any attempts to assign a pointer-to-int to a pointer-to-enum will generate a cast-alignment warning.

-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 extern declaration.

-Wnested-externs
     This warns if an extern declaration is encountered within an function.

-Dinline=
     The inline keyword is not part of ANSI C. Thus if you want to use -ansi with a program which uses inline functions you can use this preprocessor definition to remove the inline keywords.

-g
     It always makes sense to put debugging symbols in the executable so that you can debug it using gdb. The only effect of debugging symbols is to increase the size of the file, and you can use the strip command to remove them later if necessary.