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.63 2010/08/30 21:28:26 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 #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 const w_error_t w_error_t::no_error_instance(__FILE__, __LINE__, 0, 0, 0);
00089 
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 w_error_t&
00096 w_error_t::add_trace_info(
00097         const char* const    filename,
00098         uint4_t        line_num)
00099 {
00100     verify_owner();
00101     if (_trace_cnt < max_trace)  {
00102         _trace_file[_trace_cnt] = filename;
00103         _trace_line[_trace_cnt] = line_num;
00104         ++_trace_cnt;
00105     }
00106 
00107     return *this;
00108 }
00109 
00110 #if W_DEBUG_LEVEL > 1
00111 #define CHECK_STRING(x) if((x) != NULL) w_assert2(*(x) != 0)
00112 #else
00113 #define CHECK_STRING(x) 
00114 #endif
00115 
00116 w_error_t&
00117 w_error_t::clear_more_info_msg()
00118 {
00119     delete[] more_info_msg;
00120     more_info_msg = NULL;
00121     return *this;
00122 }
00123 
00124 w_error_t&
00125 w_error_t::append_more_info_msg(const char* more_info)
00126 {
00127     CHECK_STRING(more_info);
00128     verify_owner();
00129     if (more_info)  
00130     {
00131         int more_info_len = strlen(more_info);
00132         if(more_info_len > 0)
00133         {
00134             if(more_info[more_info_len-1] == '\n') more_info_len--;
00135 
00136             int more_info_msg_len = more_info_msg?strlen(more_info_msg):0;
00137             char* new_more_info_msg = new 
00138                 char[more_info_len + more_info_msg_len + 2];
00139             if(more_info_msg) { 
00140                 strcpy(new_more_info_msg, more_info_msg);
00141             }
00142             strcpy(new_more_info_msg + more_info_msg_len, more_info);
00143             new_more_info_msg[more_info_msg_len + more_info_len] = '\n';
00144             new_more_info_msg[more_info_msg_len + more_info_len + 1] = '\0';
00145 
00146             if(more_info_msg) delete[] more_info_msg;
00147             more_info_msg = new_more_info_msg;
00148 
00149             CHECK_STRING(more_info_msg);
00150         }
00151     }
00152 
00153     return *this;
00154 }
00155 
00156 const char*
00157 w_error_t::get_more_info_msg() const
00158 { 
00159     CHECK_STRING(more_info_msg);
00160     return more_info_msg;
00161 }
00162 
00163 /* automagically generate a sys_err_num from an errcode */
00164 inline w_base_t::uint4_t w_error_t::classify(int er)
00165 {
00166     uint4_t    sys = 0;
00167     switch (er) {
00168     case fcOS:
00169         sys = errno;
00170         break;
00171     }
00172     return sys;
00173 }
00174 
00175 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00176 DEFINE_TLS_SCHWARZ(block_alloc<w_error_t>, w_error_alloc);
00177 // This operator delete doesn't do anything automagic.
00178 // It's just a little more convenient for the caller than
00179 // calling block_alloc<w_error_t>::destroy_object.
00180 // Only a little. 
00181 // It still has to be called iff USE_BLOCK_ALLOC_FOR_W_ERROR_T
00182 // because we have to avoid doing any ::delete, which will call
00183 // the destructor implicitly. Destroy_object calls the destructor
00184 // explicitly.
00185 void w_error_t::operator delete(void* p) {
00186     DEBUG_BLOCK_ALLOC_MARK_FOR_DELETION((w_error_t *)p)
00187     block_alloc<w_error_t>::destroy_object((w_error_t*) p);
00188 }
00189 #endif
00190 
00191 
00192 inline
00193 w_error_t::w_error_t(const char* const        fi,
00194                  uint4_t        li,
00195                  err_num_t        er,
00196                  w_error_t*        list,
00197                  const char*    more_info)
00198 : err_num(er),
00199   file(fi),
00200   line(li), 
00201   sys_err_num(classify(er)),
00202   more_info_msg(more_info),
00203   _trace_cnt(0),
00204   _next(list)
00205 {
00206     CHECK_STRING(more_info_msg);
00207     claim();
00208     CHECKIT;
00209 }
00210 
00211 
00212 w_error_t*
00213 w_error_t::make(
00214         const char* const    filename,
00215         uint4_t              line_num,
00216         err_num_t            err_num,
00217         w_error_t*           list,
00218         const char*          more_info)
00219 {
00220 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00221     return new (*w_error_alloc) w_error_t(filename, line_num, err_num, list, more_info);
00222 #else
00223     return new  w_error_t(filename, line_num, err_num, list, more_info);
00224 #endif
00225 
00226 }
00227 
00228 inline NORET
00229 w_error_t::w_error_t(
00230         const char* const    fi,
00231         uint4_t        li,
00232         err_num_t      er,
00233         uint4_t        sys_er,
00234         w_error_t*        list,
00235         const char*        more_info)
00236         : err_num(er),
00237           file(fi), line(li), 
00238           sys_err_num(sys_er),
00239           more_info_msg(more_info),
00240           _trace_cnt(0),
00241           _next(list)
00242 {
00243     CHECK_STRING(more_info_msg);
00244     claim();
00245     CHECKIT;
00246 }
00247 
00248 w_error_t*
00249 w_error_t::make(
00250         const char* const    filename,
00251         uint4_t              line_num,
00252         err_num_t            err_num,
00253         uint4_t              sys_err,
00254         w_error_t*           list,
00255         const char*          more_info)
00256 {
00257     CHECK_STRING(more_info);
00258 // Template version causes gcc to choke on strict-aliasing warning
00259 #if USE_BLOCK_ALLOC_FOR_W_ERROR_T
00260   return  new (*w_error_alloc) w_error_t(filename, line_num, 
00261           err_num, sys_err, list, more_info);
00262   // void * ptr = (*w_error_alloc).alloc(); 
00263   // return  new (ptr)  w_error_t(filename, line_num, err_num, sys_err, list, more_info);
00264 #else
00265     return new  w_error_t(filename, line_num, err_num, sys_err, list, more_info);
00266 #endif
00267 }
00268 
00269 // Application has to be careful not to let these be called by 
00270 // multiple threads at once.
00271 // The SM protects calls to this (which are made via init_errorcodes()) 
00272 // with a mutex in the ss_m constructor and destructor.
00273 bool
00274 w_error_t::insert(
00275         const char *        modulename,
00276         const info_t    info[],
00277         uint4_t        count)
00278 {
00279     if (_nreg >= max_range)
00280         return false;
00281 
00282     err_num_t start = info[0].err_num;
00283 
00284     for (uint4_t i = 0; i < _nreg; i++)  {
00285         if (start >= _range_start[i]->err_num && start < _range_cnt[i])
00286             return false;
00287         uint4_t end = start + count;
00288         if (end >= _range_start[i]->err_num && end < _range_cnt[i])
00289             return false;
00290     }
00291     _range_start[_nreg] = info;
00292     _range_cnt[_nreg] = count;
00293     _range_name[_nreg] = modulename;
00294 
00295     ++_nreg;
00296     return true;
00297 }
00298 
00299 const char* 
00300 w_error_t::error_string(err_num_t err_num)
00301 {
00302     if(err_num ==  w_error_t::no_error->err_num ) {
00303         return "no error";
00304     }
00305     uint4_t i;
00306     for (i = 0; i < _nreg; i++)  {
00307         if (err_num >= _range_start[i]->err_num && 
00308             err_num <= _range_start[i]->err_num + _range_cnt[i]) {
00309             break;
00310         }
00311     }
00312         
00313     if (i == _nreg)  {
00314         w_error_t_no_error_code();
00315         return error_string( fcNOSUCHERROR );
00316         // return "unknown error code";
00317     }
00318 
00319     const uint4_t j = CAST(int, err_num - _range_start[i]->err_num);
00320     return _range_start[i][j].errstr;
00321 }
00322 
00323 const char*
00324 w_error_t::module_name(err_num_t err_num)
00325 {
00326     if(err_num ==  w_error_t::no_error->err_num ) {
00327             return "all modules";
00328     }
00329     uint4_t i;
00330     for (i = 0; i < _nreg; i++)  {
00331         if (err_num >= _range_start[i]->err_num && 
00332             err_num <= _range_start[i]->err_num + _range_cnt[i]) {
00333             break;
00334         }
00335     }
00336     
00337     if (i == _nreg)  {
00338         return "unknown module";
00339     }
00340     return _range_name[i];
00341 }
00342 
00343 void format_unix_error(int err, char *buf, int bufsize)
00344 {
00345 #ifdef HAVE_STRERROR
00346     char    *s = strerror(err);
00347 #else
00348     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 Nov 8 11:12:37 2010 for Shore Storage Manager by  doxygen 1.4.7