diff options
| author | Nat Goodspeed <nat@lindenlab.com> | 2019-11-18 20:22:45 -0500 | 
|---|---|---|
| committer | Nat Goodspeed <nat@lindenlab.com> | 2020-03-25 19:21:16 -0400 | 
| commit | 9ef702db1149b6961a4c9b158bf203007c0d3a92 (patch) | |
| tree | 91334b328d3dd36e4ddf245db3bc96e4ee6ca053 | |
| parent | b41a2eff4545b361afbee5d32f5d0f679702b203 (diff) | |
DRTVWR-476: Enrich LLExceptions thrown by LLTHROW() with stack trace.
The LLTHROW() abstraction allows us to enrich the subject exception with a
boost::stacktrace -- without having to propagate the boost/stacktrace.hpp
header throughout the code base.
To my delight, our existing use of
boost::current_exception_diagnostic_information() already reports the newly
added boost::stacktrace information -- we don't have to query it specifically!
| -rw-r--r-- | indra/llcommon/llexception.cpp | 27 | ||||
| -rw-r--r-- | indra/llcommon/llexception.h | 26 | 
2 files changed, 50 insertions, 3 deletions
| diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index b32ec2c9c9..c0483e8927 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -18,10 +18,23 @@  #include <typeinfo>  // external library headers  #include <boost/exception/diagnostic_information.hpp> +#include <boost/exception/error_info.hpp> +// On Mac, got: +// #error "Boost.Stacktrace requires `_Unwind_Backtrace` function. Define +// `_GNU_SOURCE` macro or `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED` if +// _Unwind_Backtrace is available without `_GNU_SOURCE`." +#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED +#include <boost/stacktrace.hpp>  // other Linden headers  #include "llerror.h"  #include "llerrorcontrol.h" +// used to attach and extract stacktrace information to/from boost::exception, +// see https://www.boost.org/doc/libs/release/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.exceptions_with_stacktrace +// apparently the struct passed as the first template param needs no definition? +typedef boost::error_info<struct errinfo_stacktrace_, boost::stacktrace::stacktrace> +        errinfo_stacktrace; +  namespace {  // used by crash_on_unhandled_exception_() and log_unhandled_exception_()  void log_unhandled_exception_(LLError::ELevel level, @@ -53,3 +66,17 @@ void log_unhandled_exception_(const char* file, int line, const char* pretty_fun      // routinely, but we DO expect to return from this function.      log_unhandled_exception_(LLError::LEVEL_WARN, file, line, pretty_function, context);  } + +void annotate_exception_(boost::exception& exc) +{ +    // https://www.boost.org/doc/libs/release/libs/exception/doc/tutorial_transporting_data.html +    // "Adding of Arbitrary Data to Active Exception Objects" +    // Given a boost::exception&, we can add boost::error_info items to it +    // without knowing its leaf type. +    // The stacktrace constructor that lets us skip a level -- and why would +    // we always include annotate_exception_()? -- also requires a max depth. +    // For the nullary constructor, the stacktrace class declaration itself +    // passes static_cast<std::size_t>(-1), but that's kind of dubious. +    // Anyway, which of us is really going to examine more than 100 frames? +    exc << errinfo_stacktrace(boost::stacktrace::stacktrace(1, 100)); +} diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index dfcb7c192f..422dd8810a 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -67,9 +67,29 @@ struct LLContinueError: public LLException   * enriches the exception's diagnostic_information() with the source file,   * line and containing function of the LLTHROW() macro.   */ -// Currently we implement that using BOOST_THROW_EXCEPTION(). Wrap it in -// LLTHROW() in case we ever want to revisit that implementation decision. -#define LLTHROW(x) BOOST_THROW_EXCEPTION(x) +#define LLTHROW(x)                                                      \ +do {                                                                    \ +    /* Capture the exception object 'x' by value. (Exceptions must */   \ +    /* be copyable.) It might seem simpler to use                  */   \ +    /* BOOST_THROW_EXCEPTION(annotate_exception_(x)) instead of    */   \ +    /* three separate statements, but:                             */   \ +    /* - We want to throw 'x' with its original type, not just a   */   \ +    /*   reference to boost::exception.                            */   \ +    /* - To return x's original type, annotate_exception_() would  */   \ +    /*   have to be a template function.                           */   \ +    /* - We want annotate_exception_() to be opaque.               */   \ +    /* We also might consider embedding BOOST_THROW_EXCEPTION() in */   \ +    /* our helper function, but we want the filename and line info */   \ +    /* embedded by BOOST_THROW_EXCEPTION() to be the throw point   */   \ +    /* rather than always indicating the same line in              */   \ +    /* llexception.cpp.                                            */   \ +    auto exc{x};                                                        \ +    annotate_exception_(exc);                                           \ +    BOOST_THROW_EXCEPTION(exc);                                         \ +    /* Use the classic 'do { ... } while (0)' macro trick to wrap  */   \ +    /* our multiple statements.                                    */   \ +} while (0) +void annotate_exception_(boost::exception& exc);  /// Call this macro from a catch (...) clause  #define CRASH_ON_UNHANDLED_EXCEPTION(CONTEXT) \ | 
