w_error.cpp

00001 /* -*- mode:C++; c-basic-offset:4 -*-
00002          Shore-MT -- Multi-threaded port of the SHORE storage manager
00003    
00004                            Copyright (c) 2007-2009
00005           Data Intensive Applications and Systems Labaratory (DIAS)
00006                    Ecole Polytechnique Federale de Lausanne
00007    
00008                              All Rights Reserved.
00009    
00010    Permission to use, copy, modify and distribute this software and
00011    its documentation is hereby granted, provided that both the
00012    copyright notice and this permission notice appear in all copies of
00013    the software, derivative works or modified versions, and any
00014    portions thereof, and that both notices appear in supporting
00015    documentation.
00016    
00017    This code is distributed in the hope that it will be useful, but
00018    WITHOUT ANY WARRANTY; without even the implied warranty of
00019    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS
00020    DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER
00021    RESULTING FROM THE USE OF THIS SOFTWARE.
00022 */
00023 
00024 /*<std-header orig-src='shore'>
00025 
00026  $Id: w_error.cpp,v 1.60 2010/06/08 22:27:22 nhall Exp $
00027 
00028 SHORE -- Scalable Heterogeneous Object REpository
00029 
00030 Copyright (c) 1994-99 Computer Sciences Department, University of
00031                           Wisconsin -- Madison
00032 All Rights Reserved.
00033 
00034 Permission to use, copy, modify and distribute this software and its
00035 documentation is hereby granted, provided that both the copyright
00036 notice and this permission notice appear in all copies of the
00037 software, derivative works or modified versions, and any portions
00038 thereof, and that both notices appear in supporting documentation.
00039 
00040 THE AUTHORS AND THE COMPUTER SCIENCES DEPARTMENT OF THE UNIVERSITY
00041 OF WISCONSIN - MADISON ALLOW FREE USE OF THIS SOFTWARE IN ITS
00042 "AS IS" CONDITION, AND THEY DISCLAIM ANY LIABILITY OF ANY KIND
00043 FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
00044 
00045 This software was developed with support by the Advanced Research
00046 Project Agency, ARPA order number 018 (formerly 8230), monitored by
00047 the U.S. Army Research Laboratory under contract DAAB07-91-C-Q518.
00048 Further funding for this work was provided by DARPA through
00049 Rome Research Laboratory Contract No. F30602-97-2-0247.
00050 
00051 */
00052 
00053 #include "w_defines.h"
00054 
00055 /*  -- do not edit anything above this line --   </std-header>*/
00056 
00057 #ifdef __GNUC__
00058 #pragma implementation "w_error.h"
00059 #endif
00060 
00061 #include <cstring>
00062 
00063 #define W_SOURCE
00064 #include <w_base.h>
00065 const
00066 #include <fc_einfo_gen.h>
00067 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00068 #include "block_alloc.h"
00069 #endif
00070 
00071 //
00072 // Static equivalent of insert(..., error_info, ...)
00073 //
00074 const 
00075 w_error_t::info_t*        w_error_t::_range_start[w_error_t::max_range] = {
00076     w_error_t::error_info, 0 
00077 };
00078 w_base_t::uint4_t        w_error_t::_range_cnt[w_error_t::max_range] = {
00079     fcERRMAX - fcERRMIN + 1, 0
00080 };
00081 
00082 const char *w_error_t::_range_name[w_error_t::max_range]= { 
00083     "Foundation Classes",
00084     0
00085 };
00086 w_base_t::uint4_t        w_error_t::_nreg = 1;
00087 
00088 
00089 const w_error_t w_error_t::no_error_instance(__FILE__, __LINE__, 0, 0, 0);
00090 w_error_t* const w_error_t::no_error = const_cast<w_error_t *>(&no_error_instance);
00091 
00092 static void w_error_t_no_error_code()
00093 {
00094 }
00095 
00096 #if W_DEBUG_LEVEL > 3
00097 #define CHECKIT do {\
00098         w_error_t*    my = _next; \
00099         w_error_t*    p = my; \
00100         while(p) { \
00101         if (p == p->_next || my == p->_next) { \
00102             cerr << "Recursive error detected:" << endl << *this << endl;\
00103             W_FATAL(fcINTERNAL); \
00104         } \
00105         p = p->_next; \
00106         } \
00107   } while(0)
00108 
00109 #else
00110 #define CHECKIT
00111 #endif
00112 
00113 w_error_t&
00114 w_error_t::add_trace_info(
00115         const char* const    filename,
00116         uint4_t        line_num)
00117 {
00118     verify_owner();
00119     if (_trace_cnt < max_trace)  {
00120         _trace_file[_trace_cnt] = filename;
00121         _trace_line[_trace_cnt] = line_num;
00122         ++_trace_cnt;
00123     }
00124 
00125     return *this;
00126 }
00127 
00128 #if W_DEBUG_LEVEL > 1
00129 #define CHECK_STRING(x) if((x) != NULL) w_assert2(*(x) != 0)
00130 #else
00131 #define CHECK_STRING(x) 
00132 #endif
00133 
00134 w_error_t&
00135 w_error_t::clear_more_info_msg()
00136 {
00137     delete[] more_info_msg;
00138     more_info_msg = NULL;
00139     return *this;
00140 }
00141 
00142 w_error_t&
00143 w_error_t::append_more_info_msg(const char* more_info)
00144 {
00145     CHECK_STRING(more_info);
00146     verify_owner();
00147     if (more_info)  
00148     {
00149         int more_info_len = strlen(more_info);
00150         if(more_info_len > 0)
00151         {
00152             if(more_info[more_info_len-1] == '\n') more_info_len--;
00153 
00154             int more_info_msg_len = more_info_msg?strlen(more_info_msg):0;
00155             char* new_more_info_msg = new 
00156                 char[more_info_len + more_info_msg_len + 2];
00157             if(more_info_msg) { 
00158                 strcpy(new_more_info_msg, more_info_msg);
00159             }
00160             strcpy(new_more_info_msg + more_info_msg_len, more_info);
00161             new_more_info_msg[more_info_msg_len + more_info_len] = '\n';
00162             new_more_info_msg[more_info_msg_len + more_info_len + 1] = '\0';
00163 
00164             if(more_info_msg) delete[] more_info_msg;
00165             more_info_msg = new_more_info_msg;
00166 
00167             CHECK_STRING(more_info_msg);
00168         }
00169     }
00170 
00171     return *this;
00172 }
00173 
00174 const char*
00175 w_error_t::get_more_info_msg() const
00176 { 
00177     CHECK_STRING(more_info_msg);
00178     return more_info_msg;
00179 }
00180 
00181 /* automagically generate a sys_err_num from an errcode */
00182 inline w_base_t::uint4_t w_error_t::classify(int er)
00183 {
00184     uint4_t    sys = 0;
00185     switch (er) {
00186     case fcOS:
00187         sys = errno;
00188         break;
00189     }
00190     return sys;
00191 }
00192 
00193 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00194 DEFINE_TLS_SWATCHZ(block_alloc<w_error_t>, w_error_alloc);
00195 void w_error_t::operator delete(void* p) {
00196   block_alloc<w_error_t>::destroy_object((w_error_t*) p);
00197 }
00198 #endif
00199 
00200 
00201 inline
00202 w_error_t::w_error_t(const char* const        fi,
00203                  uint4_t        li,
00204                  err_num_t        er,
00205                  w_error_t*        list,
00206                  const char*    more_info)
00207 : err_num(er),
00208   file(fi),
00209   line(li), 
00210   sys_err_num(classify(er)),
00211   more_info_msg(more_info),
00212   _trace_cnt(0),
00213   _next(list)
00214 {
00215     CHECK_STRING(more_info_msg);
00216     claim();
00217     CHECKIT;
00218 }
00219 
00220 
00221 w_error_t*
00222 w_error_t::make(
00223         const char* const    filename,
00224         uint4_t              line_num,
00225         err_num_t            err_num,
00226         w_error_t*           list,
00227         const char*          more_info)
00228 {
00229 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00230     return new (*w_error_alloc) w_error_t(filename, line_num, err_num, list, more_info);
00231 #else
00232     return new  w_error_t(filename, line_num, err_num, list, more_info);
00233 #endif
00234 
00235 }
00236 
00237 inline NORET
00238 w_error_t::w_error_t(
00239         const char* const    fi,
00240         uint4_t        li,
00241         err_num_t      er,
00242         uint4_t        sys_er,
00243         w_error_t*        list,
00244         const char*        more_info)
00245         : err_num(er),
00246           file(fi), line(li), 
00247           sys_err_num(sys_er),
00248           more_info_msg(more_info),
00249           _trace_cnt(0),
00250           _next(list)
00251 {
00252     CHECK_STRING(more_info_msg);
00253     claim();
00254     CHECKIT;
00255 }
00256 
00257 w_error_t*
00258 w_error_t::make(
00259         const char* const    filename,
00260         uint4_t              line_num,
00261         err_num_t            err_num,
00262         uint4_t              sys_err,
00263         w_error_t*           list,
00264         const char*          more_info)
00265 {
00266     CHECK_STRING(more_info);
00267 // Template version causes gcc to choke on strict-aliasing warning
00268 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00269   return  new (*w_error_alloc) w_error_t(filename, line_num, 
00270           err_num, sys_err, list, more_info);
00271   // void * ptr = (*w_error_alloc).alloc(); 
00272   // return  new (ptr)  w_error_t(filename, line_num, err_num, sys_err, list, more_info);
00273 #else
00274     return new  w_error_t(filename, line_num, err_num, sys_err, list, more_info);
00275 #endif
00276 }
00277 
00278 // Application has to be careful not to let these be called by 
00279 // multiple threads at once.
00280 // The SM protects calls to this (which are made via init_errorcodes()) 
00281 // with a mutex in the ss_m constructor and destructor.
00282 bool
00283 w_error_t::insert(
00284         const char *        modulename,
00285         const info_t    info[],
00286         uint4_t        count)
00287 {
00288     if (_nreg >= max_range)
00289         return false;
00290 
00291     err_num_t start = info[0].err_num;
00292 
00293     for (uint4_t i = 0; i < _nreg; i++)  {
00294         if (start >= _range_start[i]->err_num && start < _range_cnt[i])
00295             return false;
00296         uint4_t end = start + count;
00297         if (end >= _range_start[i]->err_num && end < _range_cnt[i])
00298             return false;
00299     }
00300     _range_start[_nreg] = info;
00301     _range_cnt[_nreg] = count;
00302     _range_name[_nreg] = modulename;
00303 
00304     ++_nreg;
00305     return true;
00306 }
00307 
00308 const char* 
00309 w_error_t::error_string(err_num_t err_num)
00310 {
00311     if(err_num ==  w_error_t::no_error->err_num ) {
00312         return "no error";
00313     }
00314     uint4_t i;
00315     for (i = 0; i < _nreg; i++)  {
00316         if (err_num >= _range_start[i]->err_num && 
00317             err_num <= _range_start[i]->err_num + _range_cnt[i]) {
00318             break;
00319         }
00320     }
00321         
00322     if (i == _nreg)  {
00323         w_error_t_no_error_code();
00324         return error_string( fcNOSUCHERROR );
00325         // return "unknown error code";
00326     }
00327 
00328     const uint4_t j = CAST(int, err_num - _range_start[i]->err_num);
00329     return _range_start[i][j].errstr;
00330 }
00331 
00332 const char*
00333 w_error_t::module_name(err_num_t err_num)
00334 {
00335     if(err_num ==  w_error_t::no_error->err_num ) {
00336             return "all modules";
00337     }
00338     uint4_t i;
00339     for (i = 0; i < _nreg; i++)  {
00340         if (err_num >= _range_start[i]->err_num && 
00341             err_num <= _range_start[i]->err_num + _range_cnt[i]) {
00342             break;
00343         }
00344     }
00345     
00346     if (i == _nreg)  {
00347         return "unknown module";
00348     }
00349     return _range_name[i];
00350 }
00351 
00352 void format_unix_error(int err, char *buf, int bufsize)
00353 {
00354 #ifdef HAVE_STRERROR
00355     char    *s = strerror(err);
00356 #else
00357     char    *s = "No strerror function. Cannot format unix error.";
00358 #endif
00359     strncpy(buf, s, bufsize);
00360     buf[bufsize-1] = '\0';
00361 }
00362 
00363 ostream& w_error_t::print_error(ostream &o) const
00364 {
00365     if (this == w_error_t::no_error) {
00366         return o << "no error";
00367     }
00368 
00369     int cnt = 1;
00370     for (const w_error_t* p = this; p; p = p->_next, ++cnt)  {
00371 
00372         const char* f = strrchr(p->file, '/');
00373         f ? ++f : f = p->file;
00374         o << cnt << ". error in " << f << ':' << p->line << " ";
00375         if(cnt > 1) {
00376             if(p == this) {
00377                 o << "Error recurses, stopping" << endl;
00378                 break;
00379             } 
00380             if(p->_next == p) {
00381                 o << "Error next is same, stopping" << endl;
00382             break;
00383             }
00384         }
00385         if(cnt > 20) {
00386             o << "Error chain >20, stopping" << endl;
00387             break;
00388         }
00389         o << p->error_string(p->err_num);
00390         o << " [0x" << hex << p->err_num << dec << "]";
00391 
00392         /* Eventually error subsystems will have their own interfaces
00393            here. */
00394         switch (p->err_num) {
00395         case fcOS: {
00396             char buf[1024];
00397             format_unix_error(p->sys_err_num, buf, sizeof(buf));
00398             o << " --- " << buf;
00399             break;
00400             } 
00401         }
00402 
00403         o << endl;
00404 
00405         if (more_info_msg)  {
00406             o << "\tadditional information: " << more_info_msg << endl;
00407         }
00408 
00409         if (p->_trace_cnt)  {
00410             o << "\tcalled from:" << endl;
00411             for (unsigned i = 0; i < p->_trace_cnt; i++)  {
00412                 f = strrchr(p->_trace_file[i], '/');
00413                 f ? ++f : f = p->_trace_file[i];
00414                 o << "\t" << i << ") " << f << ':' 
00415                   << p->_trace_line[i] << endl;
00416             }
00417         }
00418     }
00419 
00420     return o;
00421 }
00422 
00423 ostream &operator<<(ostream &o, const w_error_t &obj)
00424 {
00425         return obj.print_error(o);
00426 }
00427 
00428 ostream &
00429 w_error_t::print(ostream &out)
00430 {
00431     for (unsigned i = 0; i < _nreg; i++)  {
00432         err_num_t first    = _range_start[i]->err_num;
00433         unsigned int last    = first + _range_cnt[i] - 1;
00434 
00435         for (unsigned j = first; j <= last; j++)  {
00436             const char *c = module_name(j);
00437             const char *s = error_string(j);
00438 
00439             out <<  c << ":" << j << ":" << s << endl;
00440         }
00441     }
00442         
00443     return out;
00444 }
00445 
00446 /* Here we insert documentation for generation of error codes,
00447  * to be picked up by doxygen:
00448  */
00449 
00450 /**\page ERRNUM Error Codes
00451  * This page describes the error codes used in 
00452  * the various Shore Storage Manager modules.
00453  *
00454  * These numbers are generated by the Perl script 
00455  * \code
00456  * tools/errors.pl
00457  * \endcode
00458  *
00459  * This page is of interest to those who wish to use this tool to
00460  * generate their own sets of error codes.
00461  *
00462  * \section ERRNUM1 Error Codes and Modules
00463  * Error codes are unsigned integers.
00464  * Each error code has associated metadata, which consists of 
00465  * a descriptive string and a name 
00466  * (either by way of an enumeration, or by a C-preprocessor-defined name).
00467  *
00468  * The integer values associated with error code names,
00469  * the descriptive strings, the enumerations, and the
00470  * C Preprocessor macros are generated by the Perl script.
00471  * Error codes are grouped into modules, so that all the error codes
00472  * for a software module and their metadata are kept together.
00473  * Each module is given a mask, which is folded into the
00474  * values assigned to the errorcodes.
00475  * This keeps the error codes for different software modules distinct.  
00476  * The software that manages error codes keeps a (global) list
00477  * of all the modules of error codes.
00478  * Each software layer that uses the error codes must 
00479  * invoke a method to `install' its module in the global list, 
00480  * preferably at server start-up time, in the main thread, using w_error_t::insert,
00481  * which is called by a Perl-generated method <class>::init_errorcodes();-
00482  *
00483  * \section ERRNUM2 Generating Sets of Error Codes
00484  * Generating the codes is best described by way of an example.
00485  * The following example is taken from the Shore Storage Manager.
00486  *
00487  * The script takes one of two mutually exclusive options, and a file name.  
00488  * One or the other of the options (-d, -e) is required:
00489  * \code
00490  * $(SHORE_SOURCES)/tools/errors.pl -d <input-file>
00491  *    or
00492  * $(SHORE_SOURCES)/tools/errors.pl -e <input-file>
00493  * \endcode
00494  *
00495  * In the first case (-d) the named constants are generated as 
00496  * C preprocessor defined constants.
00497  * The prefix of the generated names is capitalized and 
00498  * separated from the rest of the name by an underscore character. 
00499  *
00500  * In the second case (-e) the named constants are generated as
00501  * members of an anonymous enumeration.  The prefix of the generated names is
00502  * taken, case unchanged, from the input file.
00503  * \code
00504  * e = 0x00080000 "Storage Manager" smlevel_0 {
00505  * 
00506  * ASSERT          Assertion failed
00507  * USERABORT       User initiated abort
00508  * ... and so on ...
00509  * }
00510  * \endcode
00511  *
00512  * The input is parsed as follows.
00513  * On the first line: 
00514  * - \b e 
00515     A prefix used to generate the names of the constants for the
00516     error codes for this module. 
00517     This prefix must not conflict with prefixes for other
00518     modules.
00519  * - \b = 
00520  *   Separates the name prefix from the mask.
00521  * - \b 0x00080000 
00522  *   This mask is added into each named constant generated for this module.
00523  *   This mask must not conflict with the masks used in other modules.
00524  * - \b "Storage Manager"
00525  *   The name of the module.
00526  * - \b smlevel_0
00527  *   The name of a C++ class. If a class name is present, certain generated
00528  *   data structures and methods will be members of the class, such as
00529  *   init_errorcodes(), which will be <class>::init_errorcodes().
00530  *   If no class name appears, these entities will have global namescope.
00531  * - \b {
00532  *   Begins the set of error codes and descriptions for this module.
00533  *
00534  * - The next two lines define error codes:
00535  *   - ASSERT
00536  *   - USERABORT
00537  *   These cause the named constants eASSERT and eUSERABORT to appear 
00538  *   in an anonymous enumeration type.
00539  *   The values associated with eASSERT and eUSERABORT will contain the mask:
00540  *   \code
00541  *   enum {
00542  *     eASSERT    = 0x80000,
00543  *     eUSERABORT = 0x80001,
00544  *     ...
00545  *   }
00546  *   \endcode
00547  *   The string \b "Assertion failed" is the descriptive string associated with
00548  *   eASSERT.  These descriptive strings will appear in arrays generated by 
00549  *   the Perl script.
00550  * - \b }
00551  *   Ends the set of error codes and descriptions for this module.
00552  *
00553  *   Blank lines may appear anywhere.
00554  *   Lines beginning with \b # are comments.
00555  *
00556  *
00557  * \section ERRNUM3 Generated Files
00558  * The names of the files generated contain the prefix given on the first line
00559  * of the module's input.  In the above example, that prefix is \b e.
00560  *
00561  * The set of files generated is determined by the arguments with which the 
00562  * script is invoked:
00563  * \subsection errorspld errors.pl -d <file> :
00564  *     - <prefix>_error_def_gen.h
00565  *        - defines C Preprocessor macros for the <prefix>_ERROR values, e.g.,
00566  *        \code
00567  *        #define E_ASSERT 0x80000
00568  *        \endcode
00569  *     - <prefix>_errmsg_gen.h (same as with -e option)
00570  *        - defines an array \code static char* <prefix>_errmsg[] \endcode
00571  *          containing the descriptive strings and
00572  *          \code const <prefix>_msg_size = <number of error codes in this module> \endcode.
00573  *     - <prefix>_einfo_gen.h (same as with -e option)
00574  *        - defines an array \code <class>::error_info[] \endcode
00575  *          of { integer,  descriptive-string } pairs.
00576  *        - defines  the method \code void <class>::init_errorcodes() \endcode
00577  *          to be called by the server at start-up to insert the 
00578  *          module information into the global list; this is generated only if 
00579  *          the module contains a class name.
00580  *     - <prefix>_einfo_bakw_gen.h
00581  *        - defines an array \code  <prefix>_error_info_backw[] \endcode 
00582  *          of { integer, string-name-of-macro } pairs.
00583  *
00584  * \subsection errorsple errors.pl -e <file> :
00585  *     - <prefix>_error_enum_gen.h
00586  *        - defines an enumeration for the <prefix>_ERROR values, e.g.,
00587  *        \code
00588  *        enum {
00589  *         eASSERT         = 0x80000,
00590  *         eUSERABORT      = 0x80001,
00591  *         ...
00592  *        }
00593  *        \endcode
00594  *     - <prefix>_errmsg_gen.h (same as with -d option)
00595  *        - defines an array \code static char* <prefix>_errmsg[] \endcode
00596  *          containing the descriptive strings and
00597  *          \code const <prefix>_msg_size = <number of error codes in this module> \endcode.
00598  *     - <prefix>_einfo_gen.h (same as with -d option)
00599  *        - defines an array \code <class>::error_info[] \endcode
00600  *          of { integer,  descriptive-string } pairs.
00601  *        - defines  the method \code void <class>::init_errorcodes() \endcode
00602  *          to be called by the server at start-up to insert the 
00603  *          module information into the global list. This is generated only
00604  *          if the module contains a class name.
00605  *
00606  */
00607 

Generated on Wed Jul 7 17:22:32 2010 for Shore Storage Manager by  doxygen 1.4.7