exception.cpp

00001
00002 /***************************************************************************
00003  *  exception.cpp - basic exception
00004  *
00005  *  Generated: Thu Feb 09 13:04:45 2006 (from FireVision)
00006  *  Copyright  2005-2006  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023
00024 #include <core/exception.h>
00025 #include <core/threading/mutex.h>
00026
00027 #ifndef _GNU_SOURCE
00028 #define _GNU_SOURCE
00029 #endif
00030 
00031 #include <cstring>
00032 #include <cstdlib>
00033 #include <cstdio>
00034 #ifdef HAVE_EXECINFO
00035 #  include <execinfo.h>
00036 #endif
00037 
00038 namespace fawkes {
00039 
00040 /** @class Exception core/exception.h
00041  * Base class for exceptions in Fawkes.
00042  * Exceptions are a good way to handle errors. If you choose to use
00043  * exceptions use derivates of this class so that there is a unified way of
00044  * handling errors in Fawkes. Do <i>not</i> throw an arbitrary class such as
00045  * a string or integer as this is hard to handle.
00046  *
00047  * For your exceptions in general you only need to override the constructor
00048  * and call the Exception constructor with the appropriate message. In
00049  * cases where more information is needed about the error add appropriate
00050  * handlers.
00051  *
00052  * In most cases it is bad to just throw an Exception like this:
00053  *
00054  * @code
00055  * if ( error_condition ) {
00056  *   throw Exception("Out of memory");
00057  * }
00058  * @endcode
00059  *
00060  * Rather you should explicitly subclass Exception appropriately. For the
00061  * above example you could have something like this as exception class:
00062  *
00063  * @code
00064  * class OutOfMemException : public Exception
00065  * {
00066  *  public:
00067  *   OutOfMemoryException() : Exception("Out of memory") {}
00068  * }
00069  * @endcode
00070  *
00071  * And in your handling code you throw a OutOfMemoryException. This is
00072  * especially useful if it is possible to throw several different exceptions.
00073  * If the message was different you would have to parse the string for
00074  * the exact error. This can be avoided if you just catch different
00075  * exceptions. This is also useful if the Exception is not catched explicitly
00076  * as this will printout the name of the exception class thrown just before
00077  * exiting the program. And reading something like
00078  * "terminate called after throwing an instance of 'OutOfMemoryException'"
00079  * makes it a lot easier to spot the problem.
00080  *
00081  * Exceptions should be catched by reference like this:
00082  * @code
00083  * try {
00084  *   some_operation();
00085  * } catch (OutOfMemoryException &e) {
00086  *   std::cout << e.c_str() << std::endl;
00087  *   error_handling();
00088  * }
00089  * @endcode
00090  *
00091  * Messages are stored as list. The first message however is called the
00092  * primary message and it should contain as much information as available.
00093  * This message is printed on the screen if the application crashes with an
00094  * unhandled exception. So having meaningful content here means that the
00095  * error can be traced more easily.
00096  *
00097  * You can utilize the list feature by adding appropriate information
00098  * through appropriate try/catch statements. This way you can
00099  * build information path ways that will help to debug your software. Use
00100  * block like this to append information:
00101  * @code
00102  * try {
00103  *   potentially_failing();
00104  * } catch {MyException &e) {
00105  *   e.append("info where exception happened");
00106  *   throw; // re-throw exception
00107  * }
00108  * @endcode
00109  * This is especially useful if the exception may occur at several different
00110  * places and it cannot be fixed where it happens.
00111  *
00112  *
00113  * @see example_exception.cpp
00114  * @ingroup FCL
00115  * @ingroup Exceptions
00116  *
00117  * @author Tim Niemueller
00118  */
00119 /** @var Exception::messages
00120  * List of messages. Should not be NULL. Messages are append with append().
00121  * Using a custom list to avoid including STL stuff in this core file.
00122  * @see append()
00123  */
00124 /** @var Exception::messages_iterator
00125  * Iterator to iterate over messages
00126  */
00127 /** @var Exception::messages_end
00128  * Pointer that points to the very last message. Used for fast appending.
00129  */
00130 /** @var Exception::messages_mutex
00131  * Mutex to protect operations on messages list.
00132  */
00133 /** @var Exception::_errno
00134  * Error number, should be used if the error was caused by a method that supplies
00135  * errno.
00136  */
00137
00138 
00139 /** Constructor.
00140  * Constructs a new exception with the given message.
00141  * @param format The format of the primary message. Supports the same
00142  * arguments as append(). The message is copied and not just referenced.
00143  * Thus the memory has to be freed if it is a dynamic  string on the heap.
00144  */
00145 Exception::Exception(const char *format, ...) throw()
00146 {
00147   messages_mutex = new Mutex();
00148
00149   _errno = 0;
00150
00151   messages = NULL;
00152   messages_end = NULL;
00153   messages_iterator = NULL;
00154
00155   if ( format != NULL ) {
00156     va_list arg;
00157     va_start(arg, format);
00158     append_nolock_va(format, arg);
00159     va_end(arg);
00160   } else {
00161     append_nolock("Unnkown Exception");
00162   }
00163 }
00164
00165 
00166 /** Constructor.
00167  * Constructs a new exception with the given message and errno value. This
00168  * is particularly handy when throwing the exception after a function failed
00169  * that returns an error code in errno. 
00170  * @param errno error number
00171  * @param format The format of the primary message. Supports the same
00172  * arguments as append(). The message is copied and not just referenced.
00173  * Thus the memory has to be freed if it is a dynamic  string on the heap.
00174  */
00175 Exception::Exception(int errno, const char *format, ...) throw()
00176 {
00177   messages_mutex = new Mutex();
00178
00179   _errno = errno;
00180
00181   messages = NULL;
00182   messages_end = NULL;
00183   messages_iterator = NULL;
00184
00185   if ( format != NULL ) {
00186     va_list arg;
00187     va_start(arg, format);
00188     char *ext_format;
00189     if ( asprintf(&ext_format, "%s (errno: %i, %s)", format, errno, strerror(errno)) == -1 ) {
00190       append_nolock_va(format, arg);
00191     } else {
00192       append_nolock_va(ext_format, arg);
00193       free(ext_format);
00194     }
00195     va_end(arg);
00196   } else {
00197     append_nolock("Exception with errno=%i (%s)", errno, strerror(errno));
00198   }
00199 }
00200
00201 
00202 /** Copy constructor.
00203  * The copy constructor is worth some extra discussion. If you do an exception
00204  * by value (which you shouldn't in the first place since this will generate a
00205  * copy, only do this if you can't avoid it for some reason. Not if you only
00206  * THINK that you can't avoid it) the copy constructor is called. If your catch
00207  * statements reads like
00208  * @code
00209  *   try {
00210  *     ...
00211  *   } catch (Exception e) {
00212  *     ...
00213  *   }
00214  * @endcode
00215  * then a copy will be created for the catch block. You throw the exception with
00216  * something like
00217  * @code
00218  *   throw Exception("Boom");
00219  * @endcode
00220  * This will create an Exception which is valid in the block where you throw the
00221  * exception. Now for the catch block a copy is created. Since the exception
00222  * holds a pointer on the heap the implicit copy constructor would just copy
00223  * the pointer, not the data. So both exceptions point to the same data (to the
00224  * message for the base exception). If now both destructors for the exception
00225  * are called they both try to free the very same memory. Of course the second
00226  * destructor will cause a disaster. If you are lucky your glibc detectes the
00227  * problem an kills the application. If you are not that fortunate you will
00228  * cause very strange behaviour of your application.
00229  *
00230  * In general you should not have to worry about this. But if you choose to have
00231  * own storage on the heap using either new, malloc or a method that returns
00232  * memory on the heap (like strdup()) you have to write your own copy contructor
00233  * and copy the memory area or take care that only one exception frees the memory.
00234  * @param exc Exception to copy
00235  */
00236 Exception::Exception(const Exception &exc) throw()
00237 {
00238   messages_mutex = new Mutex();
00239
00240   messages = NULL;
00241   messages_end = NULL;
00242   messages_iterator = NULL;
00243
00244   _errno = exc._errno;
00245   copy_messages(exc);
00246 }
00247
00248 
00249 /** Constructor for subclasses.
00250  * This constructor can be used in subclasses is some processing code is
00251  * needed (like sprintf) to assign the message. At least assign the empty
00252  * string to the message.
00253  */
00254 Exception::Exception() throw()
00255 {
00256   messages_mutex = new Mutex();
00257   _errno = 0;
00258   messages = NULL;
00259   messages_end = NULL;
00260   messages_iterator = NULL;
00261 }
00262
00263 
00264 /** Destructor. */
00265 Exception::~Exception() throw()
00266 {
00267   message_list_t *msg_this;
00268   messages_iterator = messages;
00269   while ( messages_iterator ) {
00270     free(messages_iterator->msg);
00271     msg_this = messages_iterator;
00272     messages_iterator = messages_iterator->next;
00273     free(msg_this);
00274   }
00275   messages = NULL;
00276   messages_end = NULL;
00277   delete messages_mutex;
00278 }
00279
00280 
00281 /** Prepend messages to the message list.
00282  * @param format format of the message to prepend, see printf(3) for details about formatting
00283  * options.
00284  */
00285 void
00286 Exception::prepend(const char *format, ...) throw()
00287 {
00288   // do not append empty messages
00289   if (format == NULL)  return;
00290
00291   va_list arg;
00292   va_start(arg, format);
00293   messages_mutex->lock();
00294   prepend_nolock_va(format, arg);
00295   messages_mutex->unlock();
00296   va_end(arg);
00297 }
00298
00299 
00300 /** Append messages to the message list.
00301  * @param format format of the message to append, see printf(3) for details about formatting
00302  * options.
00303  */
00304 void
00305 Exception::append(const char *format, ...) throw()
00306 {
00307   // do not append empty messages
00308   if (format == NULL)  return;
00309
00310   va_list arg;
00311   va_start(arg, format);
00312   messages_mutex->lock();
00313   append_nolock_va(format, arg);
00314   messages_mutex->unlock();
00315   va_end(arg);
00316 }
00317
00318 
00319 /** Append messages to the message list.
00320  * @param format format of the message to append, see printf(3) for details about formatting
00321  * options.
00322  * @param va va_list with arguments matching the format
00323  */
00324 void
00325 Exception::append_va(const char *format, va_list va) throw()
00326 {
00327   // do not append empty messages
00328   if (format == NULL)  return;
00329
00330   messages_mutex->lock();
00331   append_nolock_va(format, va);
00332   messages_mutex->unlock();
00333 }
00334
00335 
00336 /** Append message that are from another Exception.
00337  * @param e Exception to copy messages from
00338  */
00339 void
00340 Exception::append(const Exception &e) throw()
00341 {
00342   copy_messages(e);
00343 }
00344
00345 
00346 /** Append messages without lock.
00347  * this can be used to append messages without locking the mutex if the mutex
00348  * has been locked already to append many messages and keep their order intact
00349  * and thus to prevent messages to be appended inbetween.
00350  * Used for example in copy constructor.
00351  * @param format The format of the primary message. Supports the same
00352  * arguments as append(). The message is copied and not just referenced.
00353  * Thus the memory has to be freed if it is a dynamic  string on the heap.
00354  */
00355 void
00356 Exception::append_nolock(const char *format, ...) throw()
00357 {
00358   va_list arg;
00359   va_start(arg, format);
00360
00361   char *msg;
00362   if ( vasprintf(&msg, format, arg) == -1 ) {
00363     msg = strdup(format);
00364   }
00365
00366   va_end(arg);
00367
00368   if ( messages == NULL ) {
00369     // This is our first message
00370     messages = (message_list_t *)malloc(sizeof(message_list_t));
00371     messages->next = NULL;
00372     messages->msg  = msg;
00373     messages_end = messages;
00374   } else {
00375     message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t));
00376     ml->next = NULL;
00377     ml->msg = msg;
00378     messages_end->next = ml;
00379     messages_end = ml;
00380   }
00381 }
00382
00383 
00384 /** Prepend messages without lock by formatted string.
00385  * This can be used to append messages without locking the mutex if the mutex
00386  * has been locked already to append many messages and keep their order intact
00387  * and thus to prevent messages to be appended inbetween.
00388  * Used for example in copy constructor.
00389  * @param format format of the message to be appended
00390  * @param ap argument va_list for format
00391  */
00392 void
00393 Exception::prepend_nolock_va(const char *format, va_list ap) throw()
00394 {
00395   char *msg;
00396   if ( vasprintf(&msg, format, ap) == -1 ) {
00397     msg = strdup(format);
00398   }
00399
00400   if ( messages == NULL ) {
00401     // This is our first message
00402     messages = (message_list_t *)malloc(sizeof(message_list_t));
00403     messages->next = NULL;
00404     messages->msg  = msg;
00405     messages_end = messages;
00406   } else {
00407     message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t));
00408     ml->next = messages;
00409     ml->msg = msg;
00410     messages = ml;
00411   }
00412 }
00413
00414 
00415 /** Append messages without lock by formatted string.
00416  * this can be used to append messages without locking the mutex if the mutex
00417  * has been locked already to append many messages and keep their order intact
00418  * and thus to prevent messages to be appended inbetween.
00419  * Used for example in copy constructor.
00420  * @param format format of the message to be appended
00421  * @param ap argument va_list for format
00422  */
00423 void
00424 Exception::append_nolock_va(const char *format, va_list ap) throw()
00425 {
00426   char *msg;
00427   if ( vasprintf(&msg, format, ap) == -1 ) {
00428     msg = strdup(format);
00429   }
00430
00431   if ( messages == NULL ) {
00432     // This is our first message
00433     messages = (message_list_t *)malloc(sizeof(message_list_t));
00434     messages->next = NULL;
00435     messages->msg  = msg;
00436     messages_end = messages;
00437   } else {
00438     message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t));
00439     ml->next = NULL;
00440     ml->msg = msg;
00441     messages_end->next = ml;
00442     messages_end = ml;
00443   }
00444 }
00445
00446 
00447 /** Append message without copying.
00448  * Can be used in subclasses to append messages that have been allocated
00449  * on the heap. Use with extreme care. Do not add constant strings! This would
00450  * cause your application to crash since the destructor will try to free all
00451  * messages. The message list is not locked.
00452  * @param msg Message to append.
00453  */
00454 void
00455 Exception::append_nolock_nocopy(char *msg) throw()
00456 {
00457   if ( messages == NULL ) {
00458     // This is our first message
00459     messages = (message_list_t *)malloc(sizeof(message_list_t));
00460     messages->next = NULL;
00461     messages->msg  = msg;
00462     messages_end = messages;
00463   } else {
00464     message_list_t *ml = (message_list_t *)malloc(sizeof(message_list_t));
00465     ml->next = NULL;
00466     ml->msg = msg;
00467     messages_end->next = ml;
00468     messages_end = ml;
00469   }
00470 }
00471
00472 
00473 /** Assign an Exception.
00474  * As this is one of the Big Three (see C++ FAQ at
00475  * http://www.parashift.com/c++-faq-lite/coding-standards.html#faq-27.10) this
00476  * is needed because we already need a copy constructor. Read about the
00477  * copy constructor why this is the case.
00478  * @see Exception(const Exception &exc)
00479  * @param exc The exception with the values to assign to this exception.
00480  * @return reference to this object. Allows assignment chaining.
00481  */
00482 Exception &
00483 Exception::operator=(const Exception &exc) throw()
00484 {
00485   messages_mutex = new Mutex();
00486   copy_messages(exc);
00487
00488   return *this;
00489 }
00490
00491 
00492 /** Copy messages from given exception.
00493  * Copies the messages from exc to this exception.
00494  * @param exc Exception to copy messages from.
00495  */
00496 void
00497 Exception::copy_messages(const Exception &exc) throw()
00498 {
00499   messages_mutex->lock();
00500   exc.messages_mutex->lock();
00501
00502   // copy messages
00503   messages_iterator = exc.messages;
00504   while ( messages_iterator ) {
00505     append_nolock(messages_iterator->msg);
00506     messages_iterator = messages_iterator->next;
00507   }
00508
00509   exc.messages_mutex->unlock();
00510   messages_mutex->unlock();
00511 }
00512
00513 
00514 /** This can be used to throw this exception.
00515  * This can be used to throw this exception instance. This is a precaution if
00516  * it is needed. See C++ FAQ 17.10.
00517  */
00518 void
00519 Exception::raise()
00520 {
00521   throw *this;
00522 }
00523
00524 
00525 /** Prints a backtrace. */
00526 void
00527 Exception::print_backtrace() const throw()
00528 {
00529 #ifdef HAVE_EXECINFO
00530   void * array[25];
00531   int size = backtrace(array, 25);
00532   char ** symbols = backtrace_symbols(array, size);
00533
00534   printf("Backtrace:\n");
00535   for (int i = 0; i < size; ++i) {
00536     printf("  %s\n", symbols[i]);
00537   }
00538
00539   free(symbols);
00540 #else
00541   printf("Backtrace not available on current system\n");
00542 #endif
00543 }
00544
00545 
00546 /** Generate backtrace string.
00547  * @return freshly allocated string of backtrace. Free after you are done.
00548  */
00549 char *
00550 Exception::generate_backtrace() const throw()
00551 {
00552 #ifdef HAVE_BACKTRACE
00553   void * array[25];
00554   int size = backtrace(array, 25);
00555   char ** symbols = backtrace_symbols(array, size);
00556
00557   size_t total_size = 1; //null termination
00558   for (int i = 0; i < size; ++i) {
00559     total_size += strlen(symbols[i]) + 1;
00560   }
00561   char *rv = (char *)calloc(1, total_size);
00562   char *r = rv;
00563   for (int i = 0; i < size; ++i) {
00564     sprintf(r, "%s\n", symbols[i]);
00565     r += strlen(symbols[i]);
00566   }
00567
00568   free(symbols);
00569 #else
00570   char *rv = strdup("Backtrace not available on current system\n");
00571 #endif
00572 
00573   return rv;
00574 }
00575
00576 
00577 /** Prints trace to stderr.
00578  * This prints out a message trace of all messages appended to the exception
00579  * in chronological order starting with the oldest (first message appended
00580  * via constructor or append(). Output will be sent to stderr.
00581  */
00582 void
00583 Exception::print_trace() throw()
00584 {
00585   messages_mutex->lock();
00586   fprintf(stderr,
00587           "=================================================== BEGIN OF EXCEPTION =====\n");
00588   if ( messages == NULL ) {
00589     fprintf(stderr, "No messages recorded.\n");
00590   } else {
00591     messages_iterator = messages;
00592     while ( messages_iterator ) {
00593       fprintf(stderr, "%s\n", messages_iterator->msg);
00594       messages_iterator = messages_iterator->next;
00595     }
00596   }
00597   fprintf(stderr,
00598           "=================================================== END OF EXCEPTION =======\n");
00599   messages_mutex->unlock();
00600 }
00601
00602 
00603 /** Get errno.
00604  * @return error number, may be 0 if not set
00605  */
00606 int
00607 Exception::errno() throw()
00608 {
00609   return _errno;
00610 }
00611
00612 
00613 /** Get primary string.
00614  * Messages are stored in a list. The first entry in this list is called primary
00615  * message. This is why it is important to have a meaningful first message!
00616  * @return Returns a constant char pointer with the message. The message is
00617  * private to the exception and may not be modified or freed (hence const)
00618  * If no message has been set "Unknown error" is returned. This method may be
00619  * overidden by other exceptions.
00620  * This method is also called by the runtime system if the exception was not
00621  * caught and resulted in a program termination.
00622  * @return string describing the general cause of the current error
00623  */
00624 const char *
00625 Exception::what() const throw()
00626 {
00627 #ifdef HAVE_EXECINFO
00628   print_backtrace();
00629 #endif
00630   if ( messages != NULL ) {
00631     return messages->msg;
00632   } else {
00633     return "Unknown error";
00634   }
00635 }
00636
00637 
00638 /** Get iterator for messages.
00639  * @return iterator for messages
00640  */
00641 Exception::iterator
00642 Exception::begin() throw()
00643 {
00644   Exception::iterator i(messages);
00645   return i;
00646 }
00647
00648 
00649 /** @class Exception::iterator <core/exception.h>
00650  * Message iterator for exceptions.
00651  * This iterator allows for iterating over all messages carried by an Exception.
00652  * @author Tim Niemueller
00653  */
00654 
00655 /** Get end iterator for messages.
00656  * @return end iterator for messages.
00657  */
00658 Exception::iterator
00659 Exception::end() throw()
00660 {
00661   Exception::iterator i;
00662   return i;
00663 }
00664
00665 
00666 /** Constructor.
00667  * @param message_list list of messages, will be used unlocked so use
00668  * with care.
00669  */
00670 Exception::iterator::iterator(message_list_t *message_list)
00671 {
00672   mlist = message_list;
00673 }
00674
00675 
00676 /** Plain constructor.
00677  * Creates a new invalid iterator (same as Exception::end()).
00678  */
00679 Exception::iterator::iterator()
00680 {
00681   this->mlist = NULL;
00682 }
00683
00684 
00685 /** Copy constructor.
00686  * @param i iterator to copy
00687  */
00688 Exception::iterator::iterator(const Exception::iterator & i)
00689 {
00690   this->mlist = i.mlist;
00691 }
00692
00693 
00694 /** Prefix ++ operator.
00695  * @return reference to this iterator after advancing.
00696  */
00697 Exception::iterator &
00698 Exception::iterator::operator++()
00699 {
00700   if ( mlist != NULL ) {
00701     mlist = mlist->next;
00702   }
00703   return *this;
00704 }
00705
00706 
00707 /** Postfix ++ operator.
00708  * @param inc used to denote postfix operator
00709  * @return copy of iterator before advancing.
00710  */
00711 Exception::iterator
00712 Exception::iterator::operator++(int inc)
00713 {
00714   iterator i(mlist);
00715   if ( mlist != NULL ) {
00716     mlist = mlist->next;
00717   }
00718   return i;
00719 }
00720
00721 
00722 /** Check equality.
00723  * @param i iterator to compare to
00724  * @return true, if iterators point to the same message, false otherwise
00725  */
00726 bool
00727 Exception::iterator::operator==(const iterator & i) const
00728 {
00729   return (mlist == i.mlist);
00730 }
00731
00732 
00733 /** Check inequality.
00734  * @param i iterator to compare to
00735  * @return true, if iterators point to different messages, false otherwise
00736  */
00737 bool
00738 Exception::iterator::operator!=(const iterator & i) const
00739 {
00740   return (mlist != i.mlist);
00741 }
00742
00743 
00744 /** Get current message.
00745  * Get message at current position. Returns NULL for the invalid ieterator.
00746  * @return message or NULL if iterator is invalid
00747  */
00748 const char *
00749 Exception::iterator::operator* () const
00750 {
00751   if ( mlist != NULL ) {
00752     return mlist->msg;
00753   } else {
00754     return NULL;
00755   }
00756 }
00757
00758 
00759 /** Assignment operator.
00760  * @param i iterator to assign to this iterator.
00761  * @return reference to this iterator.
00762  */
00763 Exception::iterator &
00764 Exception::iterator::operator=(const iterator &i)
00765 {
00766   this->mlist = i.mlist;
00767   return *this;
00768 }
00769
00770
00771 } // end namespace fawkes