Blame doc/debug.rst

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