14.4. Error Handling

Up: Contents Next: Incremental Debugging Previous: Debugging

Errors are handled through the routine PetscError(). This routine checks a stack of error handlers and calls the one on the top. If the stack is empty, it selects PetscTraceBackErrorHandler(), which tries to print a traceback. A new error handler can be put on the stack with

   ierr = PetscPushErrorHandler(int (*HandlerFunction)(int line,char *dir,char *file, 
                                char *message,int number,void*),void *HandlerContext) 
The arguments to HandlerFunction() are the line number where the error occurred, the file in which the error was detected, the corresponding directory, the error message, the error integer, and the HandlerContext. The routine
   ierr = PetscPopErrorHandler() 
removes the last error handler and discards it.

PETSc provides two additional error handlers besides PetscTraceBackErrorHandler():

   PetscAbortErrorHandler() 
   PetscAttachErrorHandler() 
PetscAbortErrorHandler() calls abort on encountering an error, while PetscAttachErrorHandler() attaches a debugger to the running process if an error is detected. At runtime, these error handlers can be set with the options -on_error_abort or -on_error_attach_debugger [noxterm, dbx, xxgdb, xldb] [-display DISPLAY].

All PETSc calls can be traced (useful for determining where a program is hanging without running in the debugger) with the option

  -log_trace [filename] 
where filename is optional. By default the traces are printed to the screen. This can also be set with the command PLogTraceBegin(FILE*).

It is also possible to trap signals by using the command

   ierr = PetscPushSignalHandler( int (*Handler)(int,void *),void *ctx); 
The default handler PetscDefaultSignalHandler() calls PetscError() and then terminates. In general, a signal in PETSc indicates a catastrophic failure. Any error hander that the user provides should try to clean up only before exiting. By default all PETSc programs use the default signal handler, although the user can turn this off at runtime with the option -no_signal_handler .

There is a separate signal handler for floating-point exceptions. The option -fp_trap turns on the floating-point trap at runtime, and the routine

   ierr = PetscSetFPTrap(int flag); 
can be used in-line. A flag of PETSC_FP_TRAP_ON indicates that floating-point exceptions should be trapped, while a value of PETSC_FP_TRAP_OFF (the default) indicates that they should be ignored. Note that on certain machines, in particular the IBM RS/6000, trapping is very expensive.

A small set of macros is used to make the error handling lightweight. These macros are used throughout the PETSc libraries and can be employed by the application programmer as well. When an error is first detected, one should set it by calling

   SETERRQ(int flag,int pflag,char *message); 
The user should check the return codes for all PETSc routines (and possibly user-defined routines as well) with
   ierr = PetscRoutine(...); CHKERRQ(int ierr); 
Likewise, all memory allocations should be checked with
   ptr = (double *) PetscMalloc(n*sizeof(double)); CHKPTRQ(void *ptr); 
If this procedure is followed throughout all of the user's libraries and codes, any error will by default generate a clean traceback of the location of the error. In any main programs, however, the variants SETERRA(), CHKERRA(), and CHKPTRA() should be used instead to cause all processes of program to abort when an error has been detected. Use of the abort variant of the error checking commands is critical in the main program, since they ensure that MPI_Abort() is called before the process ends; otherwise, other MPI processes that did not generate errors may remain unterminated.

Note that the macro __FUNC__ is used to keep track of routine names during error tracebacks. Users need not worry about this macro in their application codes; however, users can take advantage of this feature if desired by setting this macro before each user-defined routine that may call SETERRQ(), SETERRA(), CHKERRQ(), or CHKERRA(). A simple example of usage is given below.

    #undef __FUNC__   
    #define __FUNC__ "MyRoutine1" 
    int MyRoutine1() {  
        /* code here */ 
        return 0; 
    } 


Up: Contents Next: Incremental Debugging Previous: Debugging