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.64 2010/12/08 17:37:37 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 err, char *buf, int bufsize)
00339 {
00340 #ifdef HAVE_STRERROR
00341     char    *s = strerror(err);
00342 #else
00343     char    *s = "No strerror function. Cannot format unix error.";
00344 #endif
00345     strncpy(buf, s, bufsize);
00346     buf[bufsize-1] = '\0';
00347 }
00348 
00349 ostream& w_error_t::print_error(ostream &o) const
00350 {
00351     if (this == w_error_t::no_error) {
00352         return o << "no error";
00353     }
00354 
00355     int cnt = 1;
00356     for (const w_error_t* p = this; p; p = p->_next, ++cnt)  {
00357 
00358         const char* f = strrchr(p->file, '/');
00359         f ? ++f : f = p->file;
00360         o << cnt << ". error in " << f << ':' << p->line << " ";
00361         if(cnt > 1) {
00362             if(p == this) {
00363                 o << "Error recurses, stopping" << endl;
00364                 break;
00365             } 
00366             if(p->_next == p) {
00367                 o << "Error next is same, stopping" << endl;
00368             break;
00369             }
00370         }
00371         if(cnt > 20) {
00372             o << "Error chain >20, stopping" << endl;
00373             break;
00374         }
00375         o << p->error_string(p->err_num);
00376         o << " [0x" << hex << p->err_num << dec << "]";
00377 
00378         /* Eventually error subsystems will have their own interfaces
00379            here. */
00380         switch (p->err_num) {
00381         case fcOS: {
00382             char buf[1024];
00383             format_unix_error(p->sys_err_num, buf, sizeof(buf));
00384             o << " --- " << buf;
00385             break;
00386             } 
00387         }
00388 
00389         o << endl;
00390 
00391         if (more_info_msg)  {
00392             o << "\tadditional information: " << more_info_msg << endl;
00393         }
00394 
00395         if (p->_trace_cnt)  {
00396             o << "\tcalled from:" << endl;
00397             for (unsigned i = 0; i < p->_trace_cnt; i++)  {
00398                 f = strrchr(p->_trace_file[i], '/');
00399                 f ? ++f : f = p->_trace_file[i];
00400                 o << "\t" << i << ") " << f << ':' 
00401                   << p->_trace_line[i] << endl;
00402             }
00403         }
00404     }
00405 
00406     return o;
00407 }
00408 
00409 ostream &operator<<(ostream &o, const w_error_t &obj)
00410 {
00411         return obj.print_error(o);
00412 }
00413 
00414 ostream &
00415 w_error_t::print(ostream &out)
00416 {
00417     for (unsigned i = 0; i < _nreg; i++)  {
00418         err_num_t first    = _range_start[i]->err_num;
00419         unsigned int last    = first + _range_cnt[i] - 1;
00420 
00421         for (unsigned j = first; j <= last; j++)  {
00422             const char *c = module_name(j);
00423             const char *s = error_string(j);
00424 
00425             out <<  c << ":" << j << ":" << s << endl;
00426         }
00427     }
00428         
00429     return out;
00430 }
00431 
00432 /* Here we insert documentation for generation of error codes,
00433  * to be picked up by doxygen:
00434  */
00435 
00436 /**\page ERRNUM Error Codes
00437  * This page describes the error codes used in 
00438  * the various Shore Storage Manager modules.
00439  *
00440  * These numbers are generated by the Perl script 
00441  * \code
00442  * tools/errors.pl
00443  * \endcode
00444  *
00445  * This page is of interest to those who wish to use this tool to
00446  * generate their own sets of error codes.
00447  *
00448  * \section ERRNUM1 Error Codes and Modules
00449  * Error codes are unsigned integers.
00450  * Each error code has associated metadata, which consists of 
00451  * a descriptive string and a name 
00452  * (either by way of an enumeration, or by a C-preprocessor-defined name).
00453  *
00454  * The integer values associated with error code names,
00455  * the descriptive strings, the enumerations, and the
00456  * C Preprocessor macros are generated by the Perl script.
00457  * Error codes are grouped into modules, so that all the error codes
00458  * for a software module and their metadata are kept together.
00459  * Each module is given a mask, which is folded into the
00460  * values assigned to the errorcodes.
00461  * This keeps the error codes for different software modules distinct.  
00462  * The software that manages error codes keeps a (global) list
00463  * of all the modules of error codes.
00464  * Each software layer that uses the error codes must 
00465  * invoke a method to `install' its module in the global list, 
00466  * preferably at server start-up time, in the main thread, using w_error_t::insert,
00467  * which is called by a Perl-generated method <class>::init_errorcodes();-
00468  *
00469  * \section ERRNUM2 Generating Sets of Error Codes
00470  * Generating the codes is best described by way of an example.
00471  * The following example is taken from the Shore Storage Manager.
00472  *
00473  * The script takes one of two mutually exclusive options, and a file name.  
00474  * One or the other of the options (-d, -e) is required:
00475  * \code
00476  * $(SHORE_SOURCES)/tools/errors.pl -d <input-file>
00477  *    or
00478  * $(SHORE_SOURCES)/tools/errors.pl -e <input-file>
00479  * \endcode
00480  *
00481  * In the first case (-d) the named constants are generated as 
00482  * C preprocessor defined constants.
00483  * The prefix of the generated names is capitalized and 
00484  * separated from the rest of the name by an underscore character. 
00485  *
00486  * In the second case (-e) the named constants are generated as
00487  * members of an anonymous enumeration.  The prefix of the generated names is
00488  * taken, case unchanged, from the input file.
00489  * \code
00490  * e = 0x00080000 "Storage Manager" smlevel_0 {
00491  * 
00492  * ASSERT          Assertion failed
00493  * USERABORT       User initiated abort
00494  * ... and so on ...
00495  * }
00496  * \endcode
00497  *
00498  * The input is parsed as follows.
00499  * On the first line: 
00500  * - \b e 
00501     A prefix used to generate the names of the constants for the
00502     error codes for this module. 
00503     This prefix must not conflict with prefixes for other
00504     modules.
00505  * - \b = 
00506  *   Separates the name prefix from the mask.
00507  * - \b 0x00080000 
00508  *   This mask is added into each named constant generated for this module.
00509  *   This mask must not conflict with the masks used in other modules.
00510  * - \b "Storage Manager"
00511  *   The name of the module.
00512  * - \b smlevel_0
00513  *   The name of a C++ class. If a class name is present, certain generated
00514  *   data structures and methods will be members of the class, such as
00515  *   init_errorcodes(), which will be <class>::init_errorcodes().
00516  *   If no class name appears, these entities will have global namescope.
00517  * - \b {
00518  *   Begins the set of error codes and descriptions for this module.
00519  *
00520  * - The next two lines define error codes:
00521  *   - ASSERT
00522  *   - USERABORT
00523  *   These cause the named constants eASSERT and eUSERABORT to appear 
00524  *   in an anonymous enumeration type.
00525  *   The values associated with eASSERT and eUSERABORT will contain the mask:
00526  *   \code
00527  *   enum {
00528  *     eASSERT    = 0x80000,
00529  *     eUSERABORT = 0x80001,
00530  *     ...
00531  *   }
00532  *   \endcode
00533  *   The string \b "Assertion failed" is the descriptive string associated with
00534  *   eASSERT.  These descriptive strings will appear in arrays generated by 
00535  *   the Perl script.
00536  * - \b }
00537  *   Ends the set of error codes and descriptions for this module.
00538  *
00539  *   Blank lines may appear anywhere.
00540  *   Lines beginning with \b # are comments.
00541  *
00542  *
00543  * \section ERRNUM3 Generated Files
00544  * The names of the files generated contain the prefix given on the first line
00545  * of the module's input.  In the above example, that prefix is \b e.
00546  *
00547  * The set of files generated is determined by the arguments with which the 
00548  * script is invoked:
00549  * \subsection errorspld errors.pl -d <file> :
00550  *     - <prefix>_error_def_gen.h
00551  *        - defines C Preprocessor macros for the <prefix>_ERROR values, e.g.,
00552  *        \code
00553  *        #define E_ASSERT 0x80000
00554  *        \endcode
00555  *     - <prefix>_errmsg_gen.h (same as with -e option)
00556  *        - defines an array \code static char* <prefix>_errmsg[] \endcode
00557  *          containing the descriptive strings and
00558  *          \code const <prefix>_msg_size = <number of error codes in this module> \endcode.
00559  *     - <prefix>_einfo_gen.h (same as with -e option)
00560  *        - defines an array \code <class>::error_info[] \endcode
00561  *          of { integer,  descriptive-string } pairs.
00562  *        - defines  the method \code void <class>::init_errorcodes() \endcode
00563  *          to be called by the server at start-up to insert the 
00564  *          module information into the global list; this is generated only if 
00565  *          the module contains a class name.
00566  *     - <prefix>_einfo_bakw_gen.h
00567  *        - defines an array \code  <prefix>_error_info_backw[] \endcode 
00568  *          of { integer, string-name-of-macro } pairs.
00569  *
00570  * \subsection errorsple errors.pl -e <file> :
00571  *     - <prefix>_error_enum_gen.h
00572  *        - defines an enumeration for the <prefix>_ERROR values, e.g.,
00573  *        \code
00574  *        enum {
00575  *         eASSERT         = 0x80000,
00576  *         eUSERABORT      = 0x80001,
00577  *         ...
00578  *        }
00579  *        \endcode
00580  *     - <prefix>_errmsg_gen.h (same as with -d option)
00581  *        - defines an array \code static char* <prefix>_errmsg[] \endcode
00582  *          containing the descriptive strings and
00583  *          \code const <prefix>_msg_size = <number of error codes in this module> \endcode.
00584  *     - <prefix>_einfo_gen.h (same as with -d option)
00585  *        - defines an array \code <class>::error_info[] \endcode
00586  *          of { integer,  descriptive-string } pairs.
00587  *        - defines  the method \code void <class>::init_errorcodes() \endcode
00588  *          to be called by the server at start-up to insert the 
00589  *          module information into the global list. This is generated only
00590  *          if the module contains a class name.
00591  *
00592  */
00593 

Generated on Thu Dec 9 08:42:27 2010 for Shore Storage Manager by  doxygen 1.4.7