summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-05-28 13:22:05 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-05-28 13:22:05 -0400
commit5ed8df22cd59680a685c4ada7daa5555bf59d4fe (patch)
treeaffa9679e891608f9689c780cdd00710e5ea2c95
parent71d777ea126e7f02cb46c11bdb606094ca06f75c (diff)
Fix up llexception.h's cross-platform SEH wrapper.
Introduce AlwaysReturn<void> specialization, which always discards any result of calling the specified callable with specified args. Derive new Windows_SEH_exception from LLException, not std::runtime_error. Put the various SEH functions in LL::seh nested namespace, e.g. LL::seh::catcher() as the primary API. Break out more levels of Windows SEH handler to work around the restrictions on functions containing __try/__except. The triadic catcher() overload now does little save declare a std::string stacktrace before forwarding the call to catcher_inner(), passing a reference to stacktrace along with the trycode, filter and handler functions. catcher_inner() accepts the stacktrace and the three function template arguments. It contains the __try/__except logic. It calls a new filter_() wrapper template, which calls fill_stacktrace() before forwarding the call to the caller's filter function. fill_stacktrace(), in the .cpp file, contains the logic to populate the stacktrace string -- unless the Structured Exception is stack overflow, in which case it puts an explanatory string instead. catcher_inner()'s __except clause passes not only the code, but also the stacktrace string, to the caller's handler function. It wraps the caller's handler function in always_return<rtype>(), where rtype is the type returned by the trycode function. This allows a handler to return a value, while also supporting the void handler case, e.g. one that throws a C++ exception. (This is why we need AlwaysReturn<void>: some trycode() functions are themselves void.) For the dyadic catcher() overload, introduce common_filter() containing the logic to distinguish a C++ exception from any other kind of Structured Exception. The fact that the stacktrace is captured before the filter function is called should permit capturing a stacktrace for a C++ exception as well as for most other Structured Exceptions. As before, the monadic catcher() overload supplies the rethrow() handler, in the .cpp file. Change existing calls from seh_catcher() to LL::seh::catcher().
-rw-r--r--indra/llcommon/always_return.h16
-rw-r--r--indra/llcommon/llcoros.cpp2
-rw-r--r--indra/llcommon/llexception.cpp24
-rw-r--r--indra/llcommon/llexception.h104
-rw-r--r--indra/llwindow/llwindowwin32.cpp2
-rw-r--r--indra/newview/llfeaturemanager.cpp2
-rw-r--r--indra/test/test.cpp2
7 files changed, 91 insertions, 61 deletions
diff --git a/indra/llcommon/always_return.h b/indra/llcommon/always_return.h
index a206471da5..b99eb49096 100644
--- a/indra/llcommon/always_return.h
+++ b/indra/llcommon/always_return.h
@@ -79,6 +79,22 @@ namespace LL
DESIRED mDefault;
};
+ // specialize for AlwaysReturn<void>
+ template <>
+ struct AlwaysReturn<void>
+ {
+ public:
+ AlwaysReturn() {}
+
+ // callable returns a type not convertible to DESIRED, return default
+ template <typename CALLABLE, typename... ARGS>
+ void operator()(CALLABLE&& callable, ARGS&&... args)
+ {
+ // discard whatever callable(args) returns
+ std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...);
+ }
+ };
+
/**
* always_return<T>(some_function, some_args...) calls
* some_function(some_args...). It is guaranteed to return a value of type
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index f3943b7138..7512bac88d 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -299,7 +299,7 @@ void LLCoros::toplevel(std::string name, callable_t callable)
// run the code the caller actually wants in the coroutine
try
{
- seh_catcher(callable);
+ LL::seh::catcher(callable);
}
catch (const Stop& exc)
{
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp
index 74b33f1e3b..107fdc2b2d 100644
--- a/indra/llcommon/llexception.cpp
+++ b/indra/llcommon/llexception.cpp
@@ -101,44 +101,36 @@ void annotate_exception_(boost::exception& exc)
static constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
static constexpr U32 STATUS_STACK_FULL = 0xC00000FD;
-U32 ll_seh_filter(
- std::string& stacktrace,
- std::function<U32(U32, struct _EXCEPTION_POINTERS*)> filter,
- U32 code,
- struct _EXCEPTION_POINTERS* exception_infop)
+void LL::seh::fill_stacktrace(std::string& stacktrace, U32 code)
{
- // 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.
+ // Sadly, despite its diagnostic importance, trying to capture a
+ // stacktrace 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();
+ stacktrace = to_string(boost::stacktrace::stacktrace());
}
-
- return filter(code, exception_infop);
}
-U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS*)
+U32 LL::seh::common_filter(U32 code, struct _EXCEPTION_POINTERS*)
{
if (code == STATUS_MSC_EXCEPTION)
{
- // C++ exception, go on
+ // C++ exception, don't stop at this handler
return EXCEPTION_CONTINUE_SEARCH;
}
else
{
// This is a non-C++ exception, e.g. hardware check.
+ // Pass control into the handler block.
return EXCEPTION_EXECUTE_HANDLER;
}
}
-void seh_rethrow(U32 code, const std::string& stacktrace)
+void LL::seh::rethrow(U32 code, const std::string& stacktrace)
{
std::ostringstream out;
out << "Windows exception 0x" << std::hex << code;
diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h
index 3e50678b44..f58a553eb3 100644
--- a/indra/llcommon/llexception.h
+++ b/indra/llcommon/llexception.h
@@ -12,6 +12,7 @@
#if ! defined(LL_LLEXCEPTION_H)
#define LL_LLEXCEPTION_H
+#include "always_return.h"
#include <stdexcept>
#include <boost/exception/exception.hpp>
#include <boost/throw_exception.hpp>
@@ -106,90 +107,111 @@ 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
+struct Windows_SEH_exception: public LLException
{
- Windows_SEH_exception(const std::string& what): std::runtime_error(what) {}
+ Windows_SEH_exception(const std::string& what): LLException(what) {}
};
+namespace LL
+{
+namespace seh
+{
+
#if LL_WINDOWS //-------------------------------------------------------------
-#include <functional>
+void fill_stacktrace(std::string& stacktrace, U32 code);
+
+// wrapper around caller's U32 filter(U32 code, struct _EXCEPTION_POINTERS*)
+// filter function: capture a stacktrace, if possible, before forwarding the
+// call to the caller's filter() function
+template <typename FILTER>
+U32 filter_(std::string& stacktrace, FILTER&& filter,
+ U32 code, struct _EXCEPTION_POINTERS* exptrs)
+{
+ // By the time the handler gets control, the stack has been unwound,
+ // so report the stack trace now at filter() time.
+ fill_stacktrace(stacktrace, code);
+ return std::forward<FILTER>(filter)(code, exptrs);
+}
-// 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)
+auto catcher_inner(std::string& stacktrace,
+ 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()))
+ __except (filter_(stacktrace,
+ std::forward<FILTER>(filter),
+ GetExceptionCode(), GetExceptionInformation()))
{
- return std::forward<HANDLER>(handler)(GetExceptionCode(), stacktrace);
+ return always_return<decltype(trycode())>(
+ std::forward<HANDLER>(handler), GetExceptionCode(), stacktrace);
}
}
-// dyadic variant specifies try(), handler(U32, stacktrace), assumes default filter
+// 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 catcher(TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler)
+{
+ // Construct and destroy this stacktrace string in the outer function
+ // because we can't do either in the function with __try/__except.
+ std::string stacktrace;
+ return catcher_inner(stacktrace,
+ std::forward<TRYCODE>(trycode),
+ std::forward<FILTER>(filter),
+ std::forward<HANDLER>(handler));
+}
+
+// common_filter() handles the typical case in which we want our handler
+// clause to handle only Structured Exceptions rather than explicitly-thrown
+// C++ exceptions
+U32 common_filter(U32 code, struct _EXCEPTION_POINTERS*);
+
+// dyadic variant specifies try(), handler(U32, stacktrace), assumes common_filter()
template <typename TRYCODE, typename HANDLER>
-auto seh_catcher(TRYCODE&& trycode, HANDLER&& handler)
+auto catcher(TRYCODE&& trycode, HANDLER&& handler)
{
- return seh_catcher(
- std::forward<TRYCODE>(trycode),
- seh_filter,
- std::forward<HANDLER>(handler));
+ return catcher(std::forward<TRYCODE>(trycode),
+ common_filter,
+ std::forward<HANDLER>(handler));
}
// monadic variant specifies try(), assumes default filter and handler
template <typename TRYCODE>
-auto seh_catcher(TRYCODE&& trycode)
+auto catcher(TRYCODE&& trycode)
{
- return seh_catcher(
- std::forward<TRYCODE>(trycode),
- seh_filter,
- seh_rethrow);
+ return catcher(std::forward<TRYCODE>(trycode), 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 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);
+[[noreturn]] void 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&&)
+auto catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&)
{
return std::forward<TRYCODE>(trycode)();
}
template <typename TRYCODE, typename HANDLER>
-auto seh_catcher(TRYCODE&& trycode, HANDLER&&)
+auto catcher(TRYCODE&& trycode, HANDLER&&)
{
return std::forward<TRYCODE>(trycode)();
}
template <typename TRYCODE>
-auto seh_catcher(TRYCODE&& trycode)
+auto catcher(TRYCODE&& trycode)
{
return std::forward<TRYCODE>(trycode)();
}
#endif // not LL_WINDOWS -----------------------------------------------------
+} // namespace LL::seh
+} // namespace LL
+
#endif /* ! defined(LL_LLEXCEPTION_H) */
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 1535d00d50..12cd5320b8 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -163,7 +163,7 @@ HGLRC SafeCreateContext(HDC &hdc)
GLuint SafeChoosePixelFormat(HDC &hdc, const PIXELFORMATDESCRIPTOR *ppfd)
{
- return seh_catcher(ChoosePixelFormat(hdc, ppfd));
+ return LL::seh::catcher([hdc, ppfd]{ return ChoosePixelFormat(hdc, ppfd); });
}
//static
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index 1f2ebd5092..765599bb82 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -387,7 +387,7 @@ bool LLFeatureManager::loadGPUClass()
F32 gbps;
try
{
- gbps = seh_catcher(gpu_benchmark);
+ gbps = LL::seh::catcher(gpu_benchmark);
}
catch (const std::exception& e)
{
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index d1c65d6aa7..64ee702124 100644
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -656,7 +656,7 @@ int main(int argc, char **argv)
// a chained_callback subclass must be linked with previous
mycallback->link();
- seh_catcher(
+ LL::seh::catcher(
// __try
[test_group]
{