Debug workshop
Written by Chad & Mark

  1. Changes

  2. Display Status

  3. Report Errors and Die

  4. Handle Errors

  5. Miscellaneous Notes and Links



There is a nice opportunity for code reuse with error handling and debuging. The downside is that according to U of C Calendar, that is plagiarism. You should always consult your professor before using code that was written previously, or in a separate course.

  1. Changes

    The original version had a major bug in the example code that uses:

    #define PRINTF(fmt, args...) printf (fmt, ## args)
    The original code _may_ have compiled but certainly would not work.
    (Rubini's code was fine, there was an error on this page.)

    Also this document will now call superfrink.net home. CSUS should continue to get updates if there are any.


  2. Display Status

    Most debuging is done because of a logic error. The code isn't doing what you think it should be. In order to find and fix a logic error, you normally need to display information about what is happening and in what order. Once that is displayed, it is relatively easy to figure out WHY things aren't going the way you think they should. Debug information could consist of markers like this (pure C):

    printf("Stage 1 entered\n");
    
    (more code)
    
    printf("End of stage 1.\n");
    
    Or, if more information is required, also dump the value of variables. Please note that all integers and characters should always be displayed in hexadecimal. If a character somehow gets a value of 1, in hex it's displayed as 1, as a character it will either look like a smily face, or else be unprintable.

    Now, you probably won't want to display debug statements all of the time, that's where this next example comes in. We found this strategy being used by Alessandro Rubini.

    #include <stdio.h>
    
    #define DEBUG_ON
    /* remove the previous line to turn off debuging */
    
    #undef PRINTF
    
    #ifdef DEBUG_ON
    #define PRINTF(fmt, args...)    printf (fmt, ## args)
    
    #else
    #define PRINTF(fmt, args...)    /* do nothing */
    
    #endif      // #define DEBUG_ON
    
    
    int main() {
    
            int i = 2, j = 8;
    
            PRINTF("\ni = %d", i);
            /*   The previous line will only be displayed if we put
             *          #define DEBUG_ON
             *    at the top of this file
             */
    
            printf("\n\nj = %d", j);
            /*    The previous line will always be displayed.
             */
    
            printf("\n");
            return(0);
    }
    

    In this case, by changing only one line you can tell the compiler to include or remove all of the debug statements.

  3. Report Errors and Die

    Richard W. Stevens was the first programmer I've read that used function wrappers in order to debug. Below is an example with the imaginary command "blob". blob returns a positive value on success, and zero on failure. If you wanted to assume that the function was always successful, and die with an error message otherwise, you could use this code:

    int Blob(int parameters)
    {
      int value=0;
      value=blob(parameters);
      if (value==0)
        {
        printf("The blob command has failed.  Ending program.\n");
        exit (0);
        }
      else
        return value;
    }
    

    Now in place of using "blob", you can use "Blob" and it will trap all of the errors. If we were calling "blob" alot, in order to do error handling, it would require 7 lines of additional code. With the "Blob" function, we can stay nice and lazy. "Blob" also allows for cleaner looking code, especially if the function and prototype are kept in a separate file than our main code. This is one of the best reasons to use a Makefile.

  4. Handling errors

    Let's say that instead of dying when you hit an error, you needed to recover. It may be possible (depending on the requirments) to use a system similar to Stevens' style. If the parameter was based on data obtained from the user, in Blob we could add a loop to ask for a different parameter and try again until successful. Sometimes it is simply not possible to have elegant error trapping, or else it is not worth the time investment. In a choice between handling errors, reporting errors and dying, or doing nothing, you should always at the very least report errors and die. It is foolish to think that errors simply won't happen, and it may be arrogant to ignore them. Anyway it goes, errors will normally have to be handled differently based on the context in which they are created. There is no single fool-proof error handling code. Sorry.

  5. Miscellaneous Notes and Links

    Notes:

    Links:

    http://www.csd.uwo.ca/~jamie/.Refs/misc_debug.html
    http://www.amp.york.ac.uk/external/visual/jar11/teaching/tbugs.html