LCOV - code coverage report
Current view: top level - libexcept - exception.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 47 47 100.0 %
Date: 2021-08-28 17:55:24 Functions: 14 14 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2011-2021  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program; if not, write to the Free Software
      18             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : // self
      21             : //
      22             : #include    "./exception.h"
      23             : 
      24             : #include    "./demangle.h"
      25             : 
      26             : 
      27             : // C++ includes
      28             : //
      29             : #include    <iostream>
      30             : #include    <memory>
      31             : #include    <vector>
      32             : 
      33             : 
      34             : // C lib includes
      35             : //
      36             : #include    <execinfo.h>
      37             : #include    <link.h>
      38             : #include    <unistd.h>
      39             : 
      40             : 
      41             : /** \file
      42             :  * \brief Implementation of the libexcept classes.
      43             :  *
      44             :  * This file includes the library implementation. Especially, it has the
      45             :  * code that generates a stack trace and converts the results to a C++
      46             :  * vector of strings.
      47             :  */
      48             : 
      49             : 
      50             : /** \mainpage
      51             :  *
      52             :  * The libexcept library offers us a way to automatically get a stack trace
      53             :  * every time an exception occurs.
      54             :  *
      55             :  * \section introduction Introduction
      56             :  *
      57             :  * The Snap! C++ environment uses a lot of exceptions, but nearly only when
      58             :  * the exception can't be avoided (i.e. more or less a fatal error in the
      59             :  * current situation.) Therefore, having a way to immediately discover where
      60             :  * the exception occurred by using the libexcept exception classes gives you
      61             :  * a way to immediately find out which function raised the exception nearly
      62             :  * 99% of the time including in the runtime environment of Snap! C++ and
      63             :  * any other project using the libexcept library.
      64             :  *
      65             :  * \section classes Classes to Derive From
      66             :  *
      67             :  * This library gives you two exception classes to derive from:
      68             :  *
      69             :  * \subsection logic_exception libexcept::logic_exception_t
      70             :  *
      71             :  * Used to raise an exception about logic; although this is often an
      72             :  * "emergency" type of error (even worse than a fatal error), we have
      73             :  * a definitions for it because we raise many logic errors.
      74             :  *
      75             :  * Example of a logic error:
      76             :  *
      77             :  * A function is expected to receive two parameters, say both are enumerations.
      78             :  * When the first enumeration is set to `FOO` then the second is expected
      79             :  * to be one of `BAR` or `BAZ`. If the second is set to `NOT_SO_GOOD` instead,
      80             :  * then the function raises a logic error because the programmer made a
      81             :  * mistake and the problem can be fixed by fixing the code (i.e. once the
      82             :  * code is fixed, you should then never see the error again.)
      83             :  *
      84             :  * \subsection out_of_range_exception libexcept::out_of_range_t
      85             :  *
      86             :  * This is an extension of the std::logic_error which is expected to be used
      87             :  * whenever an out of range error occurs. This is mainly for when an index
      88             :  * is out of range when attempting to retrieve an item from an array or
      89             :  * similar concept.
      90             :  *
      91             :  * \subsection runtime_exception libexcept::exception_t
      92             :  *
      93             :  * Used for most of our exceptions. This is based on the
      94             :  * `std::runtime_error` base class.
      95             :  *
      96             :  * \section object_stack_trace Collect a Stack Trace Creating an Object
      97             :  *
      98             :  * You may also use the libexcept::exception_base_t class directly in your
      99             :  * class(es) in order to collect a stack trace at the time the class is
     100             :  * instantiated. The libexcept::exception_base_t::get_stack_trace()
     101             :  * gives you the results.
     102             :  *
     103             :  * \code
     104             :  *      libexcept::exception_base_t stack_info;
     105             :  *      libexcept::stack_trace_t stack_dump(stack_info.get_stack_trace());
     106             :  *      ...here `stack_dump` is a vector of strings, one string represents one frame...
     107             :  * \endcode
     108             :  *
     109             :  * By default we use STACK_TRACE_DEPTH as the number of stings to return
     110             :  * in the libexcept::stack_trace_t vector.
     111             :  *
     112             :  * \section in_place_stack_trace Collect a Stack Trace Anywhere
     113             :  *
     114             :  * Finally, you can directly call the libexcept::collect_stack_trace()
     115             :  * function since it is a global function. It gives you a vector of
     116             :  * strings representing the stack trace.
     117             :  *
     118             :  * We also offer the libexcept::collect_stack_trace_with_line_numbers()
     119             :  * for debug only. The exceptions do not make use of that function by
     120             :  * default because it is way too slow. It is useful to convert the
     121             :  * frame IP addresses to line numbers (assuming you still have debug
     122             :  * information in your text files and the software can find the text
     123             :  * file concerned by the problem.)
     124             :  *
     125             :  * \section thread_safety Thread Safety
     126             :  *
     127             :  * The library is thread safe. All the functions are reentrant except
     128             :  * the set_collect_stack(), which is still safe to use, only the
     129             :  * results may not always be exactly as expected.
     130             :  *
     131             :  * In terms of parallelism, the collect_stack_trace_with_line_numbers()
     132             :  * runs some console processes to collect the line number and demangle
     133             :  * the function names. This means that it could be really heavy if many
     134             :  * threads use that function often.
     135             :  */
     136             : 
     137             : 
     138             : 
     139             : namespace libexcept
     140             : {
     141             : 
     142             : 
     143             : 
     144             : 
     145             : namespace
     146             : {
     147             : 
     148             : 
     149             : /** \brief Global flag to eventually prevent stack trace collection.
     150             :  *
     151             :  * Whenever a libexcept exception is raised, the stack gets collected.
     152             :  * This is very slow if you run a test which is to generate exceptions
     153             :  * over and over again, like 1,000,000 times in a tight loop.
     154             :  *
     155             :  * To make your tests faster we added a general flag which one can use
     156             :  * to collect or not collect the stack trace.
     157             :  *
     158             :  * At some point we may add an option to our command lines/configuration
     159             :  * files to tweak this flag on load. That way any of our daemon can
     160             :  * benefit by not having a stack trace in a production environment unless
     161             :  * requested. Rmember, though, that we use exceptions wisely so they really
     162             :  * only happens when something really bad is detected so it is fairly
     163             :  * safe to keep the collection of the stack trace turned on.
     164             :  */
     165             : collect_stack_t     g_collect_stack = collect_stack_t::COLLECT_STACK_YES;
     166             : 
     167             : 
     168             : 
     169             : 
     170             : } // no name namespace
     171             : 
     172             : 
     173             : 
     174             : 
     175             : 
     176             : 
     177             : 
     178             : 
     179             : 
     180             : 
     181             : 
     182             : 
     183             : 
     184             : /** \brief Tells you whether the general flag is true or false.
     185             :  *
     186             :  * This function gives you the current status of the collect stack flag.
     187             :  * If true, when exceptions will collect the stack at the time they
     188             :  * are emitted. This is very practical in debug since it gives you
     189             :  * additional information of where and possibly why an exception
     190             :  * occurred.
     191             :  */
     192          31 : collect_stack_t get_collect_stack()
     193             : {
     194          31 :     return g_collect_stack;
     195             : }
     196             : 
     197             : 
     198             : /** \brief Set a general flag on whether to collect stack traces or not.
     199             :  *
     200             :  * Because collecting the stack trace can be time consuming and once in
     201             :  * a while you may need the highest possible speed including libexcept
     202             :  * exceptions, we offer a flag to avoid all stack collection processing.
     203             :  *
     204             :  * We especially use this feature when running tests because we generate
     205             :  * the exceptions on purpose and do not want to get the stack trace which
     206             :  * is rather useless in this case. We do not yet have any other situations
     207             :  * where we do not want a stack trace.
     208             :  *
     209             :  * By default \p collect_stack is already true so you do not need to change
     210             :  * it on startup.
     211             :  *
     212             :  * \warning
     213             :  * The function itself is not multithread safe. It is unlikely to cause
     214             :  * any serious problems, though. Some threads may have or may be missing
     215             :  * the stack trace, that's all. If you never call this function, all threads
     216             :  * will always include the stack trace. Calling this function before you
     217             :  * create threads will resolve all possible issues (if you do not have
     218             :  * to dynamically change the flag.)
     219             :  *
     220             :  * \param[in] collect_stack  Whether to collect the stack or not.
     221             :  */
     222          33 : void set_collect_stack(collect_stack_t collect_stack)
     223             : {
     224          33 :     g_collect_stack = collect_stack;
     225          33 : }
     226             : 
     227             : 
     228             : 
     229             : 
     230             : 
     231             : /** \var int libexcept::exception_base_t::STACK_TRACE_DEPTH
     232             :  * \brief Default depth of stack traces collected.
     233             :  *
     234             :  * This parameter defines the default number of lines returned by the
     235             :  * collect_stack_trace() function.
     236             :  *
     237             :  * All the functions that call the collect_stack_trace() have a
     238             :  * `stack_trace_depth` parameter you can use to change this value.
     239             :  *
     240             :  * Note that a value of 0 is valid as the stack trace depth. This
     241             :  * just means not even one line is going to be taken from the
     242             :  * stack.
     243             :  *
     244             :  * \attention
     245             :  * It is to be noted that since a few functions from the libexcept are
     246             :  * going to be included in your stack trace, using a very small depth
     247             :  * such as 1 or 2 is not going to be helpful at all. You would only
     248             :  * get data about the libexcept functions instead of the actual function
     249             :  * that generated the error.
     250             :  */
     251             : 
     252             : 
     253             : /** \var stack_trace_t libexcept::exception_base_t::f_stack_trace
     254             :  * \brief The variable where the exception stack trace gets saved.
     255             :  *
     256             :  * This parameter holds the vector of strings representing the stack
     257             :  * trace at the time an exception was raised and an instance of
     258             :  * the exception_base_t class was created.
     259             :  */
     260             : 
     261             : 
     262             : /** \var typedef std::vector<std::string> libexcept::exception_base_t::stack_trace_t
     263             :  * \brief The stack trace results.
     264             :  *
     265             :  * This typedef defines the type of the variables used to pass the stack
     266             :  * trace between functions. It is a simple vector a C++ strings.
     267             :  *
     268             :  * The first string (`trace[0]`) represents the current function. Note that
     269             :  * the collected functions will include all the functions, including the
     270             :  * exception_base_t::collect_stack_trace() and various calling functions
     271             :  * from the libexcept library. In most cases this means 2 or 3 lines at
     272             :  * the start of the stack trace vector are going to be about libexcept
     273             :  * functions and not the function where the exception was raised.
     274             :  */
     275             : 
     276             : 
     277             : /** \brief Initialize this Snap! exception.
     278             :  *
     279             :  * Initialize the base exception class by generating the output of
     280             :  * a stack trace to a list of strings.
     281             :  *
     282             :  * \warning
     283             :  * At this time every single exception derived from exception_t generates
     284             :  * a stack trace. Note that in most cases, our philosophy is to generate
     285             :  * exceptions only in very exceptional cases and not on every single error
     286             :  * so the event should be rare in a normal run of our daemons.
     287             :  *
     288             :  * \param[in] stack_trace_depth  The number of lines to grab in our
     289             :  *                               stack trace.
     290             :  *
     291             :  * \sa collect_stack_trace()
     292             :  */
     293          22 : exception_base_t::exception_base_t(int const stack_trace_depth)
     294             : {
     295          22 :     switch(get_collect_stack())
     296             :     {
     297           6 :     case collect_stack_t::COLLECT_STACK_NO:
     298           6 :         break;
     299             : 
     300           8 :     case collect_stack_t::COLLECT_STACK_YES:
     301           8 :         f_stack_trace = collect_stack_trace(stack_trace_depth);
     302           8 :         break;
     303             : 
     304           8 :     case collect_stack_t::COLLECT_STACK_COMPLETE:
     305           8 :         f_stack_trace = collect_stack_trace_with_line_numbers(stack_trace_depth);
     306           8 :         break;
     307             : 
     308             :     }
     309          22 : }
     310             : 
     311             : 
     312             : /** \fn exception_base_t::~exception_base_t()
     313             :  * \brief Destructor of the exception base class.
     314             :  *
     315             :  * This destructor is defined to ease derivation when some of the classes
     316             :  * have virtual functions.
     317             :  */
     318             : 
     319             : 
     320             : 
     321             : /** \fn exception_base_t::get_stack_trace()
     322             :  * \brief Retrieve the stack trace.
     323             :  *
     324             :  * This function retreives a reference to the vector of strings representing
     325             :  * the stack trace at the time the exception was raised.
     326             :  */
     327             : 
     328             : 
     329             : 
     330             : /** \brief Initialize an exception from a C++ string.
     331             :  *
     332             :  * This function initializes an exception settings its 'what' string to
     333             :  * the specified \p what parameter.
     334             :  *
     335             :  * \note
     336             :  * Logic exceptions are used for things that just should not ever happen.
     337             :  * More or less, a verification of your class contract that fails.
     338             :  *
     339             :  * \param[in] what  The string used to initialize the exception what parameter.
     340             :  * \param[in] stack_trace_depth  The number of lines to grab in our
     341             :  *                               stack trace.
     342             :  */
     343           3 : logic_exception_t::logic_exception_t(
     344             :           std::string const & what
     345           3 :         , int const stack_trace_depth)
     346             :     : std::logic_error(what.c_str())
     347           3 :     , exception_base_t(stack_trace_depth)
     348             : {
     349           3 : }
     350             : 
     351             : 
     352             : /** \fn logic_exception_t::~logic_exception_t()
     353             :  * \brief Destructor of the logic exception class.
     354             :  *
     355             :  * This destructor is defined to ease derivation when some of the classes
     356             :  * have virtual functions.
     357             :  */
     358             : 
     359             : 
     360             : /** \brief Initialize an exception from a C string.
     361             :  *
     362             :  * This function initializes an exception settings its 'what' string to
     363             :  * the specified \p what parameter.
     364             :  *
     365             :  * \note
     366             :  * Logic exceptions are used for things that just should not ever happen.
     367             :  * More or less, a verification of your class contract that fails.
     368             :  *
     369             :  * \param[in] what  The string used to initialize the exception what parameter.
     370             :  * \param[in] stack_trace_depth  The number of lines to grab in our
     371             :  *                               stack trace.
     372             :  */
     373           5 : logic_exception_t::logic_exception_t(
     374             :           char const * what
     375           5 :         , int const stack_trace_depth)
     376             :     : std::logic_error(what)
     377           5 :     , exception_base_t(stack_trace_depth)
     378             : {
     379           5 : }
     380             : 
     381             : 
     382             : /** \brief Retrieve the `what` parameter as passed to the constructor.
     383             :  *
     384             :  * This function returns the `what` description of the exception when the
     385             :  * exception was initialized.
     386             :  *
     387             :  * \note
     388             :  * We have an overload because of the dual derivation.
     389             :  *
     390             :  * \return A pointer to the what string. Must be used before the exception
     391             :  *         gets destructed.
     392             :  */
     393           8 : char const * logic_exception_t::what() const throw()
     394             : {
     395           8 :     return std::logic_error::what();
     396             : }
     397             : 
     398             : 
     399             : 
     400             : 
     401             : 
     402             : 
     403             : 
     404             : 
     405             : 
     406             : 
     407             : 
     408             : /** \brief Initialize an exception from a C++ string.
     409             :  *
     410             :  * This function initializes an exception settings its 'what' string to
     411             :  * the specified \p what parameter.
     412             :  *
     413             :  * \note
     414             :  * Out of Range exceptions are an extension of the Logic Exception used
     415             :  * whenever a container is being accessed with an index which is too large.
     416             :  * It may also be used whenever a number doesn't fit its destination variable
     417             :  * (i.e. trying to return 300 in an `int8_t`).
     418             :  *
     419             :  * \param[in] what  The string used to initialize the exception what parameter.
     420             :  * \param[in] stack_trace_depth  The number of lines to grab in our
     421             :  *                               stack trace.
     422             :  */
     423           3 : out_of_range_t::out_of_range_t(
     424             :           std::string const & what
     425           3 :         , int const stack_trace_depth)
     426             :     : std::out_of_range(what.c_str())
     427           3 :     , exception_base_t(stack_trace_depth)
     428             : {
     429           3 : }
     430             : 
     431             : 
     432             : /** \fn out_of_range_t::~out_of_range_t()
     433             :  * \brief Destructor of the out_of_range exception class.
     434             :  *
     435             :  * This destructor is defined to ease derivation when some of the classes
     436             :  * have virtual functions.
     437             :  */
     438             : 
     439             : 
     440             : /** \brief Initialize an exception from a C string.
     441             :  *
     442             :  * This function initializes an exception settings its 'what' string to
     443             :  * the specified \p what parameter.
     444             :  *
     445             :  * \note
     446             :  * Logic exceptions are used for things that just should not ever happen.
     447             :  * More or less, a verification of your class contract that fails.
     448             :  *
     449             :  * \param[in] what  The string used to initialize the exception what parameter.
     450             :  * \param[in] stack_trace_depth  The number of lines to grab in our
     451             :  *                               stack trace.
     452             :  */
     453           3 : out_of_range_t::out_of_range_t(
     454             :           char const * what
     455           3 :         , int const stack_trace_depth)
     456             :     : std::out_of_range(what)
     457           3 :     , exception_base_t(stack_trace_depth)
     458             : {
     459           3 : }
     460             : 
     461             : 
     462             : /** \brief Retrieve the `what` parameter as passed to the constructor.
     463             :  *
     464             :  * This function returns the `what` description of the exception when the
     465             :  * exception was initialized.
     466             :  *
     467             :  * \note
     468             :  * We have an overload because of the dual derivation.
     469             :  *
     470             :  * \return A pointer to the what string. Must be used before the exception
     471             :  *         gets destructed.
     472             :  */
     473           6 : char const * out_of_range_t::what() const throw()
     474             : {
     475           6 :     return std::out_of_range::what();
     476             : }
     477             : 
     478             : 
     479             : 
     480             : 
     481             : 
     482             : 
     483             : 
     484             : 
     485             : 
     486             : 
     487             : 
     488             : 
     489             : /** \brief Initialize an exception from a C++ string.
     490             :  *
     491             :  * This function initializes an exception settings its 'what' string to
     492             :  * the specified \p what parameter.
     493             :  *
     494             :  * \param[in] what  The string used to initialize the exception what parameter.
     495             :  * \param[in] stack_trace_depth  The number of lines to grab in our
     496             :  *                               stack trace.
     497             :  */
     498           3 : exception_t::exception_t(
     499             :           std::string const & what
     500           3 :         , int const stack_trace_depth)
     501             :     : std::runtime_error(what.c_str())
     502           3 :     , exception_base_t(stack_trace_depth)
     503             : {
     504           3 : }
     505             : 
     506             : 
     507             : /** \fn exception_t::~exception_t()
     508             :  * \brief Destructor of the exception class.
     509             :  *
     510             :  * This destructor is defined to ease derivation when some of the classes
     511             :  * have virtual functions.
     512             :  */
     513             : 
     514             : 
     515             : /** \brief Initialize an exception from a C string.
     516             :  *
     517             :  * This function initializes an exception settings its 'what' string to
     518             :  * the specified \p what parameter.
     519             :  *
     520             :  * \param[in] what  The string used to initialize the exception what parameter.
     521             :  * \param[in] stack_trace_depth  The number of lines to grab in our
     522             :  *                               stack trace.
     523             :  */
     524           5 : exception_t::exception_t(
     525             :           char const * what
     526           5 :         , int const stack_trace_depth)
     527             :     : std::runtime_error(what)
     528           5 :     , exception_base_t(stack_trace_depth)
     529             : {
     530           5 : }
     531             : 
     532             : 
     533             : /** \brief Retrieve the `what` parameter as passed to the constructor.
     534             :  *
     535             :  * This function returns the `what` description of the exception when the
     536             :  * exception was initialized.
     537             :  *
     538             :  * \note
     539             :  * We have an overload because of the dual derivation.
     540             :  *
     541             :  * \return A pointer to the what string. Must be used before the exception
     542             :  *         gets destructed.
     543             :  */
     544           8 : char const * exception_t::what() const throw()
     545             : {
     546           8 :     return std::runtime_error::what();
     547             : }
     548             : 
     549             : 
     550           6 : }
     551             : // namespace libexcept
     552             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13