irplib_error.c

00001 /* $Id: irplib_error.c,v 1.12 2006/10/11 12:02:31 llundin Exp $
00002  *
00003  * This file is part of the irplib package
00004  * Copyright (C) 2002,2003 European Southern Observatory
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA
00019  */
00020 
00021 /*
00022  * $Author: llundin $
00023  * $Date: 2006/10/11 12:02:31 $
00024  * $Revision: 1.12 $
00025  * $Name:  $
00026  */
00027 
00028 #ifdef HAVE_CONFIG_H
00029 #include <config.h>
00030 #endif
00031 
00032 /*-----------------------------------------------------------------------------
00033                                    Includes
00034  -----------------------------------------------------------------------------*/
00035 
00036 #include "irplib_error.h"
00037 
00038 #include <cpl.h>
00039 #include <cxutils.h>
00040 #include <stdarg.h>
00041 /*----------------------------------------------------------------------------*/
00045 /*----------------------------------------------------------------------------*/
00046 
00047 /*-----------------------------------------------------------------------------
00048                                    Define
00049  -----------------------------------------------------------------------------*/
00050 /*
00051  *  As the error handler itself should never fail, it cannot rely on
00052  *  allocating memory dynamically. Therefore, define max limits for
00053  *  string lengths, size of error queue.
00054  */
00055 
00056 #ifndef IRPLIB_ERROR_MAX_STRING_LENGTH
00057 #define IRPLIB_ERROR_MAX_STRING_LENGTH 256
00058 #endif
00059 #ifndef IRPLIB_ERROR_MAX_ERRORS
00060 #define IRPLIB_ERROR_MAX_ERRORS 20
00061 #endif
00062 /* IRPLIB_ERROR_MAX_ERRORS is the maximum call-tree depth
00063    for error tracking. If the call-tree exceeds this limit,
00064    the deepest errors are removed (as the user is probably more interested
00065    in knowing the position of the errors that occurred nearest the
00066    root of the call-tree).
00067 */
00068 /*-----------------------------------------------------------------------------
00069                                    Typedefs
00070  -----------------------------------------------------------------------------*/
00071 
00072 /* @cond */
00073 typedef struct {
00074     char             filename    [IRPLIB_ERROR_MAX_STRING_LENGTH];
00075     char             function    [IRPLIB_ERROR_MAX_STRING_LENGTH];
00076     unsigned int     linenumber                     ;
00077     cpl_error_code   errorcode                      ;
00078     char             errormessage[IRPLIB_ERROR_MAX_STRING_LENGTH];
00079     char             cplmessage  [IRPLIB_ERROR_MAX_STRING_LENGTH];
00080 } irplib_error;
00081 /* @endcond */
00082 
00083 /*-----------------------------------------------------------------------------
00084                                    Local variables
00085  -----------------------------------------------------------------------------*/
00086 
00087 /* @cond The error queue */
00088 static struct {
00089     irplib_error errors[IRPLIB_ERROR_MAX_ERRORS];
00090     cpl_boolean         is_empty;
00091     unsigned int        first;    /* Index of first error (inclusive) */
00092     unsigned int        last;     /* Index of last  error (inclusive) */
00093     
00094     /* Invariant: 
00095        The last error in the queue matches
00096        the current CPL error state.
00097     
00098        This will be violated if the user calls
00099        cpl_error_set() or cpl_error_reset() or friends
00100        (directly).
00101 
00102        To (re-)establish this invariant
00103            irplib_error_validate_state()
00104        is called at the entry of every function
00105     */
00106 
00107 } queue;
00108 /* @endcond */
00109 
00110 /* Used only to avoid variadic macros which might not be supported
00111    on some platforms */
00112 static char error_msg[IRPLIB_ERROR_MAX_STRING_LENGTH];
00113 
00114 /*
00115  *  This module uses static memory; therefore it must be initialized
00116  *  (by calling irplib_error_reset()) before use.
00117  *  Complain loudly if the caller forgot.
00118  */
00119 static cpl_boolean is_initialized = CPL_FALSE;
00120 
00121 /*-----------------------------------------------------------------------------
00122                                    Functions code
00123  -----------------------------------------------------------------------------*/
00126 /*----------------------------------------------------------------------------*/
00142 /*----------------------------------------------------------------------------*/
00143 static void
00144 irplib_error_validate_state(const char *func, const char *file, unsigned int line)
00145 {
00146     if (!is_initialized)
00147     {
00148         cpl_msg_error(cpl_func, "Error handling system was not initialized "
00149               "when called from %s:%s:%d!", file, func, line);
00150         return;
00151     }
00152 
00153     if (cpl_error_get_code() == CPL_ERROR_NONE)
00154     {
00155         if (queue.is_empty)
00156         {
00157             /* Fine */
00158         }
00159         else
00160         {
00161             /* The cpl_error_code has been reset without resetting
00162                the the queue.
00163             */
00164             irplib_error_reset();
00165         }
00166     }
00167     else
00168     {
00169         /* An error code is set. Verify that it matches the
00170            latest error in the queue (which might not be the
00171            case if the error was set without using irplib_error_push(). 
00172         */
00173         
00174         irplib_error er;
00175         
00176         if (!queue.is_empty) 
00177         {
00178             er = queue.errors[queue.last];
00179         }
00180         
00181         /* if mismatch... */
00182         if (queue.is_empty ||
00183         !(
00184             strcmp(er.filename  ,cpl_error_get_file())     == 0 &&
00185             strcmp(er.function  ,cpl_error_get_function()) == 0 &&
00186             strcmp(er.cplmessage,cpl_error_get_message())  == 0 &&
00187             /* Don't match er.errormessage */
00188             er.errorcode == cpl_error_get_code() &&
00189             er.linenumber== cpl_error_get_line()
00190             )
00191         )
00192         {
00193             /* Insert this error into the queue */
00194             
00195             /* But to avoid infinite recursion (as we might be
00196                called from irplib_error_push_macro):
00197                1. Store the current CPL error state in a safe place.
00198                2. Set/reset CPL's error state to match the queue.
00199                3. Insert the previously stored state.
00200                4. Also, be careful to leave 
00201                   error_msg[IRPLIB_ERROR_MAX_STRING_LENGTH] unchanged.
00202             */
00203             
00204             char file_cpl[IRPLIB_ERROR_MAX_STRING_LENGTH];
00205             char func_cpl[IRPLIB_ERROR_MAX_STRING_LENGTH];
00206             cpl_error_code ec_cpl;
00207             unsigned int line_cpl;
00208 
00209             char message_local[IRPLIB_ERROR_MAX_STRING_LENGTH];
00210             
00211             /* 1 */
00212             strncpy(file_cpl, cpl_error_get_file(), 
00213                 IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00214             file_cpl[IRPLIB_ERROR_MAX_STRING_LENGTH-1] = '\0';
00215 
00216             strncpy(func_cpl, cpl_error_get_function(),
00217                 IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00218             func_cpl[IRPLIB_ERROR_MAX_STRING_LENGTH-1] = '\0';
00219 
00220             ec_cpl = cpl_error_get_code();
00221             line_cpl = cpl_error_get_line();
00222             
00223             /* 2 */
00224             if (queue.is_empty)
00225             {
00226                 cpl_error_reset();
00227             }
00228             else
00229             {
00230                 cpl_error_set_macro(er.function,
00231                         er.errorcode,
00232                         er.filename,
00233                         er.linenumber);
00234             }
00235             
00236             /* 3 + 4 */
00237             strncpy(message_local, error_msg,
00238                 IRPLIB_ERROR_MAX_STRING_LENGTH-1);
00239             message_local[IRPLIB_ERROR_MAX_STRING_LENGTH-1] = '\0';
00240             
00241 
00242             irplib_error_set_msg(" ");
00243             irplib_error_push_macro(func_cpl,
00244                         ec_cpl,
00245                         file_cpl,
00246                         line_cpl);
00247             irplib_error_set_msg("%s", message_local);
00248 
00249         } /* mismatch with CPL's error state */
00250 
00251     } /* cpl_error_code is set */
00252 
00253     return;
00254 }
00255 
00256 /*----------------------------------------------------------------------------*/
00276 /*----------------------------------------------------------------------------*/
00277 
00278 void irplib_error_push_macro(const char *func,
00279                  cpl_error_code ec,
00280                  const char *file, 
00281                  unsigned int line)
00282 {
00283     irplib_error_validate_state(func, file, line);
00284 
00285     if (ec == CPL_ERROR_NONE)
00286     {
00287         cpl_msg_error(cpl_func, "The error code CPL_ERROR_NONE was set from "
00288               "%s:%s:%d! Code changed to CPL_ERROR_UNSPECIFIED",
00289               file, func, line);
00290 
00291         ec = CPL_ERROR_UNSPECIFIED;
00292     }
00293 
00294     /* Compute new first/last indices */
00295     if (queue.is_empty)
00296     {
00297         /* First error */
00298         queue.first = 0;
00299         queue.last  = 0;
00300     }
00301     else
00302     {
00303         /* If queue is full */
00304         if ((queue.last + 1) % IRPLIB_ERROR_MAX_ERRORS
00305                 == (queue.first % IRPLIB_ERROR_MAX_ERRORS))
00306         {
00307             /* Delete oldest error */
00308             queue.first = (queue.first + 1) % IRPLIB_ERROR_MAX_ERRORS;
00309         }
00310         queue.last = (queue.last + 1) % IRPLIB_ERROR_MAX_ERRORS;
00311     }
00312     queue.is_empty = CPL_FALSE;
00313 
00314     cpl_error_set_macro(func, ec, file, line);
00315     
00316     /* Insert current error into the queue
00317      *
00318      * Make sure that the target string is always 0-terminated.
00319      * This is not guaranteed by strncpy.
00320      */
00321     strncpy(queue.errors[queue.last].filename    , file,
00322         IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00323     strncpy(queue.errors[queue.last].function    , func,
00324         IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00325     strncpy(queue.errors[queue.last].cplmessage  , cpl_error_get_message(), 
00326         IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00327     strncpy(queue.errors[queue.last].errormessage, error_msg,
00328         IRPLIB_ERROR_MAX_STRING_LENGTH - 1);
00329     queue.errors[queue.last].filename    [IRPLIB_ERROR_MAX_STRING_LENGTH - 1]
00330         = '\0';
00331     queue.errors[queue.last].function    [IRPLIB_ERROR_MAX_STRING_LENGTH - 1]
00332         = '\0';
00333     queue.errors[queue.last].cplmessage  [IRPLIB_ERROR_MAX_STRING_LENGTH - 1]
00334         = '\0';
00335     queue.errors[queue.last].errormessage[IRPLIB_ERROR_MAX_STRING_LENGTH - 1]
00336         = '\0';
00337 
00338     queue.errors[queue.last].linenumber = line;
00339     queue.errors[queue.last].errorcode  = ec;
00340 
00341     return;
00342 }
00343 
00344 /*----------------------------------------------------------------------------*/
00355 /*----------------------------------------------------------------------------*/
00356 void irplib_error_set_msg(const char *format, ...)
00357 {
00358     va_list al;
00359 
00360     va_start(al, format);
00361     /* vsnprintf is C99, so use cx_vsnprintf
00362        vsnprintf(error_msg, IRPLIB_ERROR_MAX_STRING_LENGTH, format, al);
00363     */
00364     cx_vsnprintf(error_msg, IRPLIB_ERROR_MAX_STRING_LENGTH, format, al);
00365     va_end(al);
00366 }
00367 
00368 /*----------------------------------------------------------------------------*/
00376 /*----------------------------------------------------------------------------*/
00377 void irplib_error_reset(void)
00378 {
00379     cpl_error_reset();
00380     queue.is_empty = CPL_TRUE;
00381     error_msg[0] = '\0';
00382 
00383     is_initialized = CPL_TRUE;
00384 }
00385 
00386 /*----------------------------------------------------------------------------*/
00403 /*----------------------------------------------------------------------------*/
00404 void irplib_error_dump_macro(const char *func, 
00405                  const char *file,
00406                  unsigned int line,
00407                  cpl_msg_severity severity,
00408                  cpl_msg_severity trace_severity)
00409 {
00410     /* Pointing to the CPL messaging functions
00411        that correspond to the specified severities: 
00412     */
00413     void (*error_msg_func)(const char *, const char *, ...)
00414 #if defined __GNUC__ &&  __GNUC__ >= 3
00415     __attribute__((format (printf, 2, 3)))
00416 #endif
00417     ;
00418 
00419     void (*trace_msg_func)(const char *, const char *, ...)
00420 #if defined __GNUC__ &&  __GNUC__ >= 3
00421     __attribute__((format (printf, 2, 3)))
00422 #endif
00423     ;
00424 
00425     irplib_error_validate_state(func, file, line);
00426 
00427     switch (severity)
00428     {
00429     case CPL_MSG_DEBUG:
00430         error_msg_func = &cpl_msg_debug;
00431         break;
00432     case CPL_MSG_INFO:
00433         error_msg_func = &cpl_msg_info;
00434         break;
00435     case CPL_MSG_WARNING:
00436         error_msg_func = &cpl_msg_warning;
00437         break;
00438     case CPL_MSG_ERROR:
00439         error_msg_func = &cpl_msg_error;
00440         break;
00441     case CPL_MSG_OFF:
00442         error_msg_func = NULL;
00443         break;
00444     default:
00445         cpl_msg_error(func, "Unknown message level: %d !",
00446               severity);
00447         error_msg_func = &cpl_msg_error;
00448         break;
00449     }
00450 
00451     switch (trace_severity)
00452     {
00453     case CPL_MSG_DEBUG:
00454         trace_msg_func = &cpl_msg_debug;
00455         break;
00456     case CPL_MSG_INFO:
00457         trace_msg_func = &cpl_msg_info;
00458         break;
00459     case CPL_MSG_WARNING:
00460         trace_msg_func = &cpl_msg_warning;
00461         break;
00462     case CPL_MSG_ERROR:
00463         trace_msg_func = &cpl_msg_error;
00464         break;
00465     case CPL_MSG_OFF:
00466         trace_msg_func = NULL;
00467         break;
00468     default:
00469         cpl_msg_error(func, "Unknown message level: %d !",
00470               severity);
00471         trace_msg_func = &cpl_msg_error;
00472         break;
00473     }
00474 
00475     if (cpl_error_get_code() == CPL_ERROR_NONE) 
00476     {
00477         if (error_msg_func != NULL)
00478         {
00479             error_msg_func(func, "No error has occurred");
00480         }
00481     }
00482     else
00483     {
00484         int i;
00485         cpl_error_code current_ec;
00486         
00487         /* Don't reset indentation:  cpl_msg_indent(0); 
00488            This will interfere with error-recovery. It is anyway
00489            the responsibility of the client never to change the net
00490            indentation, also when an error occurs (so don't try to
00491            hide such bug).
00492         */
00493         
00494         if (trace_msg_func != NULL)
00495         {
00496             trace_msg_func(func, "An error occurred, "
00497                    "dumping error trace:");
00498             trace_msg_func(func, " ");
00499         }
00500         
00501         /* 
00502          * Print errors from first to last
00503          * (both inclusive, and maybe wrap around) 
00504          */
00505         current_ec = CPL_ERROR_NONE;
00506         i = queue.first - 1;
00507         do {
00508         const char *c;
00509         cpl_boolean empty_message;
00510         
00511         i = (i+1) % IRPLIB_ERROR_MAX_ERRORS;
00512         
00513         c = queue.errors[i].errormessage;
00514         empty_message = CPL_TRUE;
00515         while (*c != '\0')
00516             {
00517             empty_message = empty_message && (*c == ' ');
00518             c++;
00519             }
00520 
00521         /* There are 2x2 cases to consider
00522            Was a non-empty error message was provided?
00523            Did the cpl_error_code change from the previous
00524            (lower) level? */
00525         if (empty_message)
00526             {
00527             /* No error message provided, print the standard
00528                CPL error message */
00529             if (error_msg_func != NULL)
00530                 {
00531                 error_msg_func(func, "%s",
00532                            queue.errors[i].cplmessage);
00533                 }
00534             }
00535         else if (queue.errors[i].errorcode == current_ec)
00536             {
00537             /* Error message provided, don't repeat error code */
00538             if (error_msg_func != NULL)
00539                 {
00540                 error_msg_func(func, "%s",
00541                            queue.errors[i].errormessage);
00542                 }
00543             }
00544         else
00545             {
00546             /* Error message provided, error code different
00547                from previous (lower) level */
00548             if (error_msg_func != NULL)
00549                 {
00550                 error_msg_func(func, "%s (%s)",
00551                            queue.errors[i].errormessage,
00552                            queue.errors[i].cplmessage);
00553                 }
00554             }
00555         
00556         if (trace_msg_func != NULL)
00557             {
00558             trace_msg_func(func,
00559                        " in [%d]%s() at %s:%-3d",
00560                        /* Here errors are numbered
00561                       according to depth: from N to 1 */
00562                        ((queue.last - i
00563                                          + IRPLIB_ERROR_MAX_ERRORS) %
00564                     IRPLIB_ERROR_MAX_ERRORS) + 1,
00565                        queue.errors[i].function,
00566                        queue.errors[i].filename,
00567                        queue.errors[i].linenumber);
00568             trace_msg_func(func, " ");
00569             }
00570 
00571         current_ec = queue.errors[i].errorcode;
00572         
00573         } while ((unsigned int) i != queue.last);
00574         
00575     } /* If an error occurred */
00576 }
00577 

Generated on Wed Jan 17 08:33:41 2007 for SINFONI Pipeline Reference Manual by  doxygen 1.4.4