Jump to content
xisto Community
Sign in to follow this  
mitchellmckain

Handling Errors To Help You In Debugging finding the source of your errors

Recommended Posts

One of the most important programming tasks is learning how to handle errors in a way that will be helpful to your debugging efforts. In today's programming we make extensive use of libraries and other code written by other people. When this code generates errors they can be rather hard to track down, since they usually generate exceptions which are caught out of your typical code sequence. In order to deal with this, I want to suggest three error handling ideas, that you can use without resorting to the disassembler.

 

The first is the use of checkpoints. These are regularly spaced points in your code where you store a unique code number in a static variable to keep track of where you are in the execution of your own code.

 

The second idea is to channel your response to all the errors you catch by various methods through a single code point. The reason is that with a single break point you can stop your program in time to trace the execution back to where the error was generated.

 

The third idea is to pass an origin code to subroutines which you make a large number of calls from numerous locations in your code. This is to help you immediately identify the location from which the subroutine was called. If you make a large enough number of calls to this routine, tracing back to exactly which call to it led to the error can be difficult.

 

It is a good idea to make this error code a seperate module, since it is something you can use in all of your projects. In this module you can make the header file something to include by other modules. In addition to your static checkpoint variable, you can define a macro which stores the checkpoint code and tests for errors. When an error is found you call the subroutine (error_break) which handles all of your errors according to the second idea above. Another useful item is a flag which tells your program whether to terminate on an error (user mode) or continue execution (debugging mode) for the purpose of tracing and error back to its source.

 

error.h

//Use this macro to check for errors while recording a checkpoint location//test represents any additional local conditions under which to signal an error#define checkpoint(test,j); {checkpointcode=j;if(test||errno==EDOM)error_break(j);}extern bool exit_on_error;extern int checkpoint;void error_break(int j);//you will also want to define any special exception types you might need here//When you throw an exception you can throw anything at all: any integer, array, class, etc. in your program.//So defining a seperate structure like this for an exception is optional.  However using a different structure// for each type of exception makes your programming more readable and managable.  struct specialException {   int i,j,k;};struct error_break_Exception {   char buf[100];};

Now you can put the actual variables and subroutine code in the c++ file. You can also put in a _matherr routine to overrule the default handler.

 

error.cpp

// exit_on_error set to false causes the program to continue execution after an error// use with breaks on the line below to trace the errors back to the source   bool exit_on_error=false; //change this to true when you are finished debugging your program   int checkpointcode=0;//Since this file does not call error.h you need define here any exception types you use in this filestruct error_break_Exception {   int j;};//---------------------------------------------------------------------------void error_break(int j){        errbrkException e;	e.j=j;//###################################################################################//******************put a break on the next line to cause program to halt when called//###################################################################################        errno=0;	//here you can display error codes	//         store a whole list of error codes	//         throw an error_break_Exception under whatever conditions, for example	if(j>27){e.j=j;throw e;}return;}//---------------------------------------------------------------------------int _matherr (struct _exception *a){//  In some case you may want to put code here to modifiy the return value without generating an error//  For example acos and asin generate errors outside the range of -1 to 1.  Since it is easy to fall //  outside of this range due to round off error you may simply want to return an appropriate value//  rather than generate any kind of error.  This is accomplished by the following code.    if(!strcmp(a->name,"acos")){      if(a->arg1>1.0&&a->arg1<1.01){a->retval = 0.0;return 1;}      if(a->arg1<-1.0&&a->arg1>-1.01){a->retval = M_PI;return 1;}}    if(!strcmp(a->name,"asin")){      if(a->arg1>1.0&&a->arg1<1.01){a->retval = M_PI_2;return 1;}      if(a->arg1<-1.0&&a->arg1>-1.01){a->retval = - M_PI_2;return 1;}}  error_break(9999);//when you return from error_break you can examine the type, name and arguments of the error.//...........................................................................//a->type == DOMAIN	Argument was not in domain of function, such as log(-1).//a->type == SING	Argument would result in a singularity, such as pow(0, -2).//a->type == OVERFLOW	Argument would produce a function result greater than DBL_MAX (or LDBL_MAX), such as exp(1000).//a->type == UNDERFLOW  Argument would produce a function result less than DBL_MIN (or LDBL_MIN), such as exp(-1000).//a->type == TLOSS	Argument would produce function result with total loss of significant digits, such as sin(10e70).//  You can check which math function generated the error by looking at a->name//  You can check the arguments of the math function by looking at a->arg1 and a->arg2 (if 2 arguments)//...........................................................................  if(exit_on_error)return 0;  return 1;  }

In your main program you will use one or more try catch statement sets to catch error exceptions.

 

#include "error.h"     ...      //Using more than one try - catch set will also help you to identify what part of your code generated the error       try {//your code, usually calls to subroutines and functions in your other modules 	}             catch (error_break_Exception &e) {  <code to display error message or information contained in e>  //since this exception is generated by error_break itself, you do not call error break here.         if(exit_on_error)exit_program(0);}       catch (Exception &ex) {  <code to display error message or information contained in ex>         if(exit_on_error)exit_program(0);         else errbrk(0);}       catch (...) {         //here is where you catch unknown exceptions types that may be thrown in libraries you use  //You have no idea what informations was sent in the exception so you cannot display that   // But you can display the checkpoint code that was saved at your last checkpoint.    <code to display error message with last checkpoint code>           if(exit_on_error)exit_program(0);           else errbrk(0);}

Here is another method for catching errors defined in signal.h, which I have put in my error handling code.

 

Of course you need this at the top of your program.

 

 #include <signal.h>

Then you need to signal to install the signal handler at the beginning of you main program

 

signal(SIGFPE, (fptr)SignalSIGFPECatcher);

Add your signal handler prototype to error.h

 

  void SignalSIGFPECatcher(int *reglist);

Then you need to add your signal handler to error.cpp

 

//---------------------------------------------------------------------------typedef void (*fptr)(int);void SignalSIGFPECatcher(int *reglist){   signal(SIGFPE, (fptr)SignalSIGFPECatcher);  //  ******reinstall signal handler  error_break(9998);   if(exit_on_error)_exit(1);   else *(reglist + 8) = 3; /* make return AX = 3 */}

The signal types that you can catch this way are as follows:

...........................................................................

SIGABRT Abnormal termination (default handler calls _exit(3))

SIGFPE Bad floating-point operation (default handler calls _exit(1))

Arithmetic error caused by

division by 0, invalid operation, etc.

 

SIGILL Illegal operation (default handler calls _exit(1))

 

SIGINT Control-C interrupt (default handler calls _exit(3))

SIGSEGV Invalid access to storage (default handler calls _exit(1))

SIGTERM Request for program termination (default handler calls _exit(1))

...........................................................................

Share this post


Link to post
Share on other sites

On reveiwing this tutorial I noticed that one small explantion seems to have vanished. You are to use

#include "error.h"
in all of your other modules, so that you can use the checkpoint macro in them. Notice that you should not have this include statement in error.cpp itself.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×
×
  • Create New...

Important Information

Terms of Use | Privacy Policy | Guidelines | We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.