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.65 2012/01/02 17:02:13 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 #if defined(__GNUC__)
00058 #pragma implementation "w_error.h"
00059 #endif
00060 
00061 #include <cstring>
00062 
00063 #include <w_base.h>
00064 const
00065 #include <fc_einfo_gen.h>
00066 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00067 #include "block_alloc.h"
00068 #endif
00069 
00070 //
00071 // Static equivalent of insert(..., error_info, ...)
00072 //
00073 const 
00074 w_error_t::info_t*        w_error_t::_range_start[w_error_t::max_range] = {
00075     w_error_t::error_info, 0 
00076 };
00077 w_base_t::uint4_t        w_error_t::_range_cnt[w_error_t::max_range] = {
00078     fcERRMAX - fcERRMIN + 1, 0
00079 };
00080 
00081 const char *w_error_t::_range_name[w_error_t::max_range]= { 
00082     "Foundation Classes",
00083     0
00084 };
00085 w_base_t::uint4_t        w_error_t::_nreg = 1;
00086 
00087 const w_error_t w_error_t::no_error_instance(__FILE__, __LINE__, 0, 0, 0);
00088 
00089 w_error_t* const w_error_t::no_error = const_cast<w_error_t *>(&no_error_instance);
00090 
00091 static void w_error_t_no_error_code()
00092 {
00093 }
00094 w_error_t&
00095 w_error_t::add_trace_info(
00096         const char* const    filename,
00097         uint4_t        line_num)
00098 {
00099     if (_trace_cnt < max_trace)  {
00100         _trace_file[_trace_cnt] = filename;
00101         _trace_line[_trace_cnt] = line_num;
00102         ++_trace_cnt;
00103     }
00104 
00105     return *this;
00106 }
00107 
00108 #if W_DEBUG_LEVEL > 1
00109 #define CHECK_STRING(x) if((x) != NULL) w_assert2(*(x) != 0)
00110 #else
00111 #define CHECK_STRING(x) 
00112 #endif
00113 
00114 w_error_t&
00115 w_error_t::clear_more_info_msg()
00116 {
00117     delete[] more_info_msg;
00118     more_info_msg = NULL;
00119     return *this;
00120 }
00121 
00122 w_error_t&
00123 w_error_t::append_more_info_msg(const char* more_info)
00124 {
00125     CHECK_STRING(more_info);
00126     if (more_info)  
00127     {
00128         int more_info_len = strlen(more_info);
00129         if(more_info_len > 0)
00130         {
00131             if(more_info[more_info_len-1] == '\n') more_info_len--;
00132 
00133             int more_info_msg_len = more_info_msg?strlen(more_info_msg):0;
00134             char* new_more_info_msg = new 
00135                 char[more_info_len + more_info_msg_len + 2];
00136             if(more_info_msg) { 
00137                 strcpy(new_more_info_msg, more_info_msg);
00138             }
00139             strcpy(new_more_info_msg + more_info_msg_len, more_info);
00140             new_more_info_msg[more_info_msg_len + more_info_len] = '\n';
00141             new_more_info_msg[more_info_msg_len + more_info_len + 1] = '\0';
00142 
00143             if(more_info_msg) delete[] more_info_msg;
00144             more_info_msg = new_more_info_msg;
00145 
00146             CHECK_STRING(more_info_msg);
00147         }
00148     }
00149 
00150     return *this;
00151 }
00152 
00153 const char*
00154 w_error_t::get_more_info_msg() const
00155 { 
00156     CHECK_STRING(more_info_msg);
00157     return more_info_msg;
00158 }
00159 
00160 /* automagically generate a sys_err_num from an errcode */
00161 inline w_base_t::uint4_t w_error_t::classify(int er)
00162 {
00163     uint4_t    sys = 0;
00164     switch (er) {
00165     case fcOS:
00166         sys = errno;
00167         break;
00168     }
00169     return sys;
00170 }
00171 
00172 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00173 DEFINE_TLS_SCHWARZ(block_alloc<w_error_t>, w_error_alloc);
00174 // This operator delete doesn't do anything automagic.
00175 // It's just a little more convenient for the caller than
00176 // calling block_alloc<w_error_t>::destroy_object.
00177 // Only a little. 
00178 // It still has to be called iff USE_BLOCK_ALLOC_FOR_W_ERROR_T
00179 // because we have to avoid doing any ::delete, which will call
00180 // the destructor implicitly. Destroy_object calls the destructor
00181 // explicitly.
00182 void w_error_t::operator delete(void* p) {
00183     DEBUG_BLOCK_ALLOC_MARK_FOR_DELETION((w_error_t *)p)
00184     block_alloc<w_error_t>::destroy_object((w_error_t*) p);
00185 }
00186 #endif
00187 
00188 
00189 inline
00190 w_error_t::w_error_t(const char* const        fi,
00191                  uint4_t        li,
00192                  err_num_t        er,
00193                  w_error_t*        list,
00194                  const char*    more_info)
00195 : err_num(er),
00196   file(fi),
00197   line(li), 
00198   sys_err_num(classify(er)),
00199   more_info_msg(more_info),
00200   _trace_cnt(0),
00201   _next(list)
00202 {
00203     CHECK_STRING(more_info_msg);
00204     CHECKIT;
00205 }
00206 
00207 
00208 w_error_t*
00209 w_error_t::make(
00210         const char* const    filename,
00211         uint4_t              line_num,
00212         err_num_t            err_num,
00213         w_error_t*           list,
00214         const char*          more_info)
00215 {
00216 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00217     return new (*w_error_alloc) w_error_t(filename, line_num, err_num, list, more_info);
00218 #else
00219     return new  w_error_t(filename, line_num, err_num, list, more_info);
00220 #endif
00221 
00222 }
00223 
00224 inline NORET
00225 w_error_t::w_error_t(
00226         const char* const    fi,
00227         uint4_t        li,
00228         err_num_t      er,
00229         uint4_t        sys_er,
00230         w_error_t*        list,
00231         const char*        more_info)
00232         : err_num(er),
00233           file(fi), line(li), 
00234           sys_err_num(sys_er),
00235           more_info_msg(more_info),
00236           _trace_cnt(0),
00237           _next(list)
00238 {
00239     CHECK_STRING(more_info_msg);
00240     CHECKIT;
00241 }
00242 
00243 w_error_t*
00244 w_error_t::make(
00245         const char* const    filename,
00246         uint4_t              line_num,
00247         err_num_t            err_num,
00248         uint4_t              sys_err,
00249         w_error_t*           list,
00250         const char*          more_info)
00251 {
00252     CHECK_STRING(more_info);
00253 // Template version causes gcc to choke on strict-aliasing warning
00254 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00255   return  new (*w_error_alloc) w_error_t(filename, line_num, 
00256           err_num, sys_err, list, more_info);
00257   // void * ptr = (*w_error_alloc).alloc(); 
00258   // return  new (ptr)  w_error_t(filename, line_num, err_num, sys_err, list, more_info);
00259 #else
00260     return new  w_error_t(filename, line_num, err_num, sys_err, list, more_info);
00261 #endif
00262 }
00263 
00264 // Application has to be careful not to let these be called by 
00265 // multiple threads at once.
00266 // The SM protects calls to this (which are made via init_errorcodes()) 
00267 // with a mutex in the ss_m constructor and destructor.
00268 bool
00269 w_error_t::insert(
00270         const char *        modulename,
00271         const info_t    info[],
00272         uint4_t        count)
00273 {
00274     if (_nreg >= max_range)
00275         return false;
00276 
00277     err_num_t start = info[0].err_num;
00278 
00279     for (uint4_t i = 0; i < _nreg; i++)  {
00280         if (start >= _range_start[i]->err_num && start < _range_cnt[i])
00281             return false;
00282         uint4_t end = start + count;
00283         if (end >= _range_start[i]->err_num && end < _range_cnt[i])
00284             return false;
00285     }
00286     _range_start[_nreg] = info;
00287     _range_cnt[_nreg] = count;
00288     _range_name[_nreg] = modulename;
00289 
00290     ++_nreg;
00291     return true;
00292 }
00293 
00294 const char* 
00295 w_error_t::error_string(err_num_t err_num)
00296 {
00297     if(err_num ==  w_error_t::no_error->err_num ) {
00298         return "no error";
00299     }
00300     uint4_t i;
00301     for (i = 0; i < _nreg; i++)  {
00302         if (err_num >= _range_start[i]->err_num && 
00303             err_num <= _range_start[i]->err_num + _range_cnt[i]) {
00304             break;
00305         }
00306     }
00307         
00308     if (i == _nreg)  {
00309         w_error_t_no_error_code();
00310         return error_string( fcNOSUCHERROR );
00311         // return "unknown error code";
00312     }
00313 
00314     const uint4_t j = CAST(int, err_num - _range_start[i]->err_num);
00315     return _range_start[i][j].errstr;
00316 }
00317 
00318 const char*
00319 w_error_t::module_name(err_num_t err_num)
00320 {
00321     if(err_num ==  w_error_t::no_error->err_num ) {
00322             return "all modules";
00323     }
00324     uint4_t i;
00325     for (i = 0; i < _nreg; i++)  {
00326         if (err_num >= _range_start[i]->err_num && 
00327             err_num <= _range_start[i]->err_num + _range_cnt[i]) {
00328             break;
00329         }
00330     }
00331     
00332     if (i == _nreg)  {
00333         return "unknown module";
00334     }
00335     return _range_name[i];
00336 }
00337 
00338 void format_unix_error(int 
00339 #ifdef HAVE_STRERROR
00340  err
00341 #else
00342 #endif
00343   , char *buf, int bufsize)
00344 {
00345 #ifdef HAVE_STRERROR
00346     char    *s = strerror(err);
00347 #else
00348     const char    *s = "No strerror function. Cannot format unix error.";
00349 #endif
00350     strncpy(buf, s, bufsize);
00351     buf[bufsize-1] = '\0';
00352 }
00353 
00354 ostream& w_error_t::print_error(ostream &o) const
00355 {
00356     if (this == w_error_t::no_error) {
00357         return o << "no error";
00358     }
00359 
00360     int cnt = 1;
00361     for (const w_error_t* p = this; p; p = p->_next, ++cnt)  {
00362 
00363         const char* f = strrchr(p->file, '/');
00364         f ? ++f : f = p->file;
00365         o << cnt << ". error in " << f << ':' << p->line << " ";
00366         if(cnt > 1) {
00367             if(p == this) {
00368                 o << "Error recurses, stopping" << endl;
00369                 break;
00370             } 
00371             if(p->_next == p) {
00372                 o << "Error next is same, stopping" << endl;
00373             break;
00374             }
00375         }
00376         if(cnt > 20) {
00377             o << "Error chain >20, stopping" << endl;
00378             break;
00379         }
00380         o << p->error_string(p->err_num);
00381         o << " [0x" << hex << p->err_num << dec << "]";
00382 
00383         /* Eventually error subsystems will have their own interfaces
00384            here. */
00385         switch (p->err_num) {
00386         case fcOS: {
00387             char buf[1024];
00388             format_unix_error(p->sys_err_num, buf, sizeof(buf));
00389             o << " --- " << buf;
00390             break;
00391             } 
00392         }
00393 
00394         o << endl;
00395 
00396         if (more_info_msg)  {
00397             o << "\tadditional information: " << more_info_msg << endl;
00398         }
00399 
00400         if (p->_trace_cnt)  {
00401             o << "\tcalled from:" << endl;
00402             for (unsigned i = 0; i < p->_trace_cnt; i++)  {
00403                 f = strrchr(p->_trace_file[i], '/');
00404                 f ? ++f : f = p->_trace_file[i];
00405                 o << "\t" << i << ") " << f << ':' 
00406                   << p->_trace_line[i] << endl;
00407             }
00408         }
00409     }
00410 
00411     return o;
00412 }
00413 
00414 ostream &operator<<(ostream &o, const w_error_t &obj)
00415 {
00416         return obj.print_error(o);
00417 }
00418 
00419 ostream &
00420 w_error_t::print(ostream &out)
00421 {
00422     for (unsigned i = 0; i < _nreg; i++)  {
00423         err_num_t first    = _range_start[i]->err_num;
00424         unsigned int last    = first + _range_cnt[i] - 1;
00425 
00426         for (unsigned j = first; j <= last; j++)  {
00427             const char *c = module_name(j);
00428             const char *s = error_string(j);
00429 
00430             out <<  c << ":" << j << ":" << s << endl;
00431         }
00432     }
00433         
00434     return out;
00435 }
00436 
00437 /* Here we insert documentation for generation of error codes,
00438  * to be picked up by doxygen:
00439  */
00440 
00441 /**\page ERRNUM Error Codes
00442  * This page describes the error codes used in 
00443  * the various Shore Storage Manager modules.
00444  *
00445  * These numbers are generated by the Perl script 
00446  * \code
00447  * tools/errors.pl
00448  * \endcode
00449  *
00450  * This page is of interest to those who wish to use this tool to
00451  * generate their own sets of error codes.
00452  *
00453  * \section ERRNUM1 Error Codes and Modules
00454  * Error codes are unsigned integers.
00455  * Each error code has associated metadata, which consists of 
00456  * a descriptive string and a name 
00457  * (either by way of an enumeration, or by a C-preprocessor-defined name).
00458  *
00459  * The integer values associated with error code names,
00460  * the descriptive strings, the enumerations, and the
00461  * C Preprocessor macros are generated by the Perl script.
00462  * Error codes are grouped into modules, so that all the error codes
00463  * for a software module and their metadata are kept together.
00464  * Each module is given a mask, which is folded into the
00465  * values assigned to the errorcodes.
00466  * This keeps the error codes for different software modules distinct.  
00467  * The software that manages error codes keeps a (global) list
00468  * of all the modules of error codes.
00469  * Each software layer that uses the error codes must 
00470  * invoke a method to `install' its module in the global list, 
00471  * preferably at server start-up time, in the main thread, using w_error_t::insert,
00472  * which is called by a Perl-generated method <class>::init_errorcodes();-
00473  *
00474  * \section ERRNUM2 Generating Sets of Error Codes
00475  * Generating the codes is best described by way of an example.
00476  * The following example is taken from the Shore Storage Manager.
00477  *
00478  * The script takes one of two mutually exclusive options, and a file name.  
00479  * One or the other of the options (-d, -e) is required:
00480  * \code
00481  * $(SHORE_SOURCES)/tools/errors.pl -d <input-file>
00482  *    or
00483  * $(SHORE_SOURCES)/tools/errors.pl -e <input-file>
00484  * \endcode
00485  *
00486  * In the first case (-d) the named constants are generated as 
00487  * C preprocessor defined constants.
00488  * The prefix of the generated names is capitalized and 
00489  * separated from the rest of the name by an underscore character. 
00490  *
00491  * In the second case (-e) the named constants are generated as
00492  * members of an anonymous enumeration.  The prefix of the generated names is
00493  * taken, case unchanged, from the input file.
00494  * \code
00495  * e = 0x00080000 "Storage Manager" smlevel_0 {
00496  * 
00497  * ASSERT          Assertion failed
00498  * USERABORT       User initiated abort
00499  * ... and so on ...
00500  * }
00501  * \endcode
00502  *
00503  * The input is parsed as follows.
00504  * On the first line: 
00505  * - \b e 
00506     A prefix used to generate the names of the constants for the
00507     error codes for this module. 
00508     This prefix must not conflict with prefixes for other
00509     modules.
00510  * - \b = 
00511  *   Separates the name prefix from the mask.
00512  * - \b 0x00080000 
00513  *   This mask is added into each named constant generated for this module.
00514  *   This mask must not conflict with the masks used in other modules.
00515  * - \b "Storage Manager"
00516  *   The name of the module.
00517  * - \b smlevel_0
00518  *   The name of a C++ class. If a class name is present, certain generated
00519  *   data structures and methods will be members of the class, such as
00520  *   init_errorcodes(), which will be <class>::init_errorcodes().
00521  *   If no class name appears, these entities will have global namescope.
00522  * - \b {
00523  *   Begins the set of error codes and descriptions for this module.
00524  *
00525  * - The next two lines define error codes:
00526  *   - ASSERT
00527  *   - USERABORT
00528  *   These cause the named constants eASSERT and eUSERABORT to appear 
00529  *   in an anonymous enumeration type.
00530  *   The values associated with eASSERT and eUSERABORT will contain the mask:
00531  *   \code
00532  *   enum {
00533  *     eASSERT    = 0x80000,
00534  *     eUSERABORT = 0x80001,
00535  *     ...
00536  *   }
00537  *   \endcode
00538  *   The string \b "Assertion failed" is the descriptive string associated with
00539  *   eASSERT.  These descriptive strings will appear in arrays generated by 
00540  *   the Perl script.
00541  * - \b }
00542  *   Ends the set of error codes and descriptions for this module.
00543  *
00544  *   Blank lines may appear anywhere.
00545  *   Lines beginning with \b # are comments.
00546  *
00547  *
00548  * \section ERRNUM3 Generated Files
00549  * The names of the files generated contain the prefix given on the first line
00550  * of the module's input.  In the above example, that prefix is \b e.
00551  *
00552  * The set of files generated is determined by the arguments with which the 
00553  * script is invoked:
00554  * \subsection errorspld errors.pl -d <file> :
00555  *     - <prefix>_error_def_gen.h
00556  *        - defines C Preprocessor macros for the <prefix>_ERROR values, e.g.,
00557  *        \code
00558  *        #define E_ASSERT 0x80000
00559  *        \endcode
00560  *     - <prefix>_errmsg_gen.h (same as with -e option)
00561  *        - defines an array \code static char* <prefix>_errmsg[] \endcode
00562  *          containing the descriptive strings and
00563  *          \code const <prefix>_msg_size = <number of error codes in this module> \endcode.
00564  *     - <prefix>_einfo_gen.h (same as with -e option)
00565  *        - defines an array \code <class>::error_info[] \endcode
00566  *          of { integer,  descriptive-string } pairs.
00567  *        - defines  the method \code void <class>::init_errorcodes() \endcode
00568  *          to be called by the server at start-up to insert the 
00569  *          module information into the global list; this is generated only if 
00570  *          the module contains a class name.
00571  *     - <prefix>_einfo_bakw_gen.h
00572  *        - defines an array \code  <prefix>_error_info_backw[] \endcode 
00573  *          of { integer, string-name-of-macro } pairs.
00574  *
00575  * \subsection errorsple errors.pl -e <file> :
00576  *     - <prefix>_error_enum_gen.h
00577  *        - defines an enumeration for the <prefix>_ERROR values, e.g.,
00578  *        \code
00579  *        enum {
00580  *         eASSERT         = 0x80000,
00581  *         eUSERABORT      = 0x80001,
00582  *         ...
00583  *        }
00584  *        \endcode
00585  *     - <prefix>_errmsg_gen.h (same as with -d option)
00586  *        - defines an array \code static char* <prefix>_errmsg[] \endcode
00587  *          containing the descriptive strings and
00588  *          \code const <prefix>_msg_size = <number of error codes in this module> \endcode.
00589  *     - <prefix>_einfo_gen.h (same as with -d option)
00590  *        - defines an array \code <class>::error_info[] \endcode
00591  *          of { integer,  descriptive-string } pairs.
00592  *        - defines  the method \code void <class>::init_errorcodes() \endcode
00593  *          to be called by the server at start-up to insert the 
00594  *          module information into the global list. This is generated only
00595  *          if the module contains a class name.
00596  *
00597  */
00598 

Generated on Mon Jan 2 15:13:57 2012 for Shore Storage Manager by  doxygen 1.4.7