diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-05-24 17:34:04 -0400 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-05-24 17:34:04 -0400 |
commit | 71d777ea126e7f02cb46c11bdb606094ca06f75c (patch) | |
tree | 795d79c321adb65682a6f104f9955df3d93dd3c5 /indra/llcommon | |
parent | f06f84aed26fa7ed294a14ed12dae58c063b0aa3 (diff) |
Promote seh_catcher() et al. to llexception.{h,cpp} for general use.
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/llcoros.cpp | 51 | ||||
-rw-r--r-- | indra/llcommon/llexception.cpp | 50 | ||||
-rw-r--r-- | indra/llcommon/llexception.h | 86 |
3 files changed, 126 insertions, 61 deletions
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index a70e3d9ae7..f3943b7138 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -286,55 +286,6 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl return name; } -namespace -{ - -#if LL_WINDOWS - -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific - -U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) -{ - if (code == STATUS_MSC_EXCEPTION) - { - // C++ exception, go on - return EXCEPTION_CONTINUE_SEARCH; - } - else - { - // handle it - return EXCEPTION_EXECUTE_HANDLER; - } -} - -void sehandle(const LLCoros::callable_t& callable) -{ - __try - { - callable(); - } - __except (exception_filter(GetExceptionCode(), GetExceptionInformation())) - { - // convert to C++ styled exception - // Note: it might be better to use _se_set_translator - // if you want exception to inherit full callstack - char integer_string[512]; - sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); - throw std::exception(integer_string); - } -} - -#else // ! LL_WINDOWS - -inline void sehandle(const LLCoros::callable_t& callable) -{ - callable(); -} - -#endif // ! LL_WINDOWS - -} // anonymous namespace - // Top-level wrapper around caller's coroutine callable. // Normally we like to pass strings and such by const reference -- but in this // case, we WANT to copy both the name and the callable to our local stack! @@ -348,7 +299,7 @@ void LLCoros::toplevel(std::string name, callable_t callable) // run the code the caller actually wants in the coroutine try { - sehandle(callable); + seh_catcher(callable); } catch (const Stop& exc) { diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index c0154a569f..74b33f1e3b 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -15,7 +15,12 @@ #include "llexception.h" // STL headers // std headers +#include <iomanip> +#include <sstream> #include <typeinfo> +#if LL_WINDOWS +#include <excpt.h> +#endif // LL_WINDOWS // external library headers #include <boost/exception/diagnostic_information.hpp> #include <boost/exception/error_info.hpp> @@ -29,7 +34,6 @@ // On Windows, header-only implementation causes macro collisions -- use // prebuilt library #define BOOST_STACKTRACE_LINK -#include <excpt.h> #endif // LL_WINDOWS #include <boost/stacktrace.hpp> @@ -94,15 +98,34 @@ void annotate_exception_(boost::exception& exc) // For windows SEH exception handling we sometimes need a filter that will // separate C++ exceptions from C SEH exceptions -static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific +static constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific +static constexpr U32 STATUS_STACK_FULL = 0xC00000FD; -U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) +U32 ll_seh_filter( + std::string& stacktrace, + std::function<U32(U32, struct _EXCEPTION_POINTERS*)> filter, + U32 code, + struct _EXCEPTION_POINTERS* exception_infop) { - const auto stack = to_string(boost::stacktrace::stacktrace()); - LL_WARNS() << "SEH Exception handled (that probably shouldn't be): Code " << code - << "\n Stack trace: \n" - << stack << LL_ENDL; + // By the time the handler gets control, the stack has been unwound, + // so report the stack trace now at filter() time. + // Even though stack overflow is a problem we would very much like to + // diagnose, calling another function when the stack is already blown only + // terminates us faster. + if (code == STATUS_STACK_FULL) + { + stacktrace = "(stack overflow, no traceback)"; + } + else + { + stacktrace = boost::stacktrace::stacktrace().to_string(); + } + + return filter(code, exception_infop); +} +U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS*) +{ if (code == STATUS_MSC_EXCEPTION) { // C++ exception, go on @@ -110,9 +133,20 @@ U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) } else { - // handle it + // This is a non-C++ exception, e.g. hardware check. return EXCEPTION_EXECUTE_HANDLER; } } +void seh_rethrow(U32 code, const std::string& stacktrace) +{ + std::ostringstream out; + out << "Windows exception 0x" << std::hex << code; + if (! stacktrace.empty()) + { + out << '\n' << stacktrace; + } + LLTHROW(Windows_SEH_exception(out.str())); +} + #endif //LL_WINDOWS diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 68e609444e..3e50678b44 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -102,14 +102,94 @@ void crash_on_unhandled_exception_(const char*, int, const char*, const std::str log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT) void log_unhandled_exception_(const char*, int, const char*, const std::string&); +/***************************************************************************** +* Structured Exception Handling +*****************************************************************************/ +// this is used in platform-generic code -- define outside #if LL_WINDOWS +struct Windows_SEH_exception: public std::runtime_error +{ + Windows_SEH_exception(const std::string& what): std::runtime_error(what) {} +}; + +#if LL_WINDOWS //------------------------------------------------------------- + +#include <functional> + +// triadic variant specifies try(), filter(U32, struct _EXCEPTION_POINTERS*), +// handler(U32, const std::string& stacktrace) +// stacktrace may or may not be available +template <typename TRYCODE, typename FILTER, typename HANDLER> +auto seh_catcher(TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler) +{ + // don't try to construct a std::function at the moment of Structured Exception + std::function<U32(U32, struct _EXCEPTION_POINTERS*)> + filter_function(std::forward<FILTER>(filter)); + std::string stacktrace; + __try + { + return std::forward<TRYCODE>(trycode)(); + } + __except (ll_seh_filter( + stacktrace, + filter_function, + GetExceptionCode(), + GetExceptionInformation())) + { + return std::forward<HANDLER>(handler)(GetExceptionCode(), stacktrace); + } +} + +// dyadic variant specifies try(), handler(U32, stacktrace), assumes default filter +template <typename TRYCODE, typename HANDLER> +auto seh_catcher(TRYCODE&& trycode, HANDLER&& handler) +{ + return seh_catcher( + std::forward<TRYCODE>(trycode), + seh_filter, + std::forward<HANDLER>(handler)); +} -#if LL_WINDOWS +// monadic variant specifies try(), assumes default filter and handler +template <typename TRYCODE> +auto seh_catcher(TRYCODE&& trycode) +{ + return seh_catcher( + std::forward<TRYCODE>(trycode), + seh_filter, + seh_rethrow); +} // SEH exception filtering for use in __try __except // Separates C++ exceptions from C SEH exceptions // Todo: might be good idea to do some kind of seh_to_msc_wrapper(function, ARGS&&); -U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop); +U32 ll_seh_filter( + std::string& stacktrace, + std::function<U32(U32, struct _EXCEPTION_POINTERS*)> filter, + U32 code, + struct _EXCEPTION_POINTERS* exception_infop); +U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS* exception_infop); +void seh_rethrow(U32 code, const std::string& stacktrace); + +#else // not LL_WINDOWS ----------------------------------------------------- + +template <typename TRYCODE, typename FILTER, typename HANDLER> +auto seh_catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&) +{ + return std::forward<TRYCODE>(trycode)(); +} + +template <typename TRYCODE, typename HANDLER> +auto seh_catcher(TRYCODE&& trycode, HANDLER&&) +{ + return std::forward<TRYCODE>(trycode)(); +} + +template <typename TRYCODE> +auto seh_catcher(TRYCODE&& trycode) +{ + return std::forward<TRYCODE>(trycode)(); +} -#endif //LL_WINDOWS +#endif // not LL_WINDOWS ----------------------------------------------------- #endif /* ! defined(LL_LLEXCEPTION_H) */ |