summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/llcoros.cpp51
-rw-r--r--indra/llcommon/llexception.cpp50
-rw-r--r--indra/llcommon/llexception.h86
-rw-r--r--indra/llwindow/llwindowwin32.cpp13
-rw-r--r--indra/newview/llappviewerwin32.cpp36
-rw-r--r--indra/newview/llfeaturemanager.cpp36
-rw-r--r--indra/test/test.cpp68
7 files changed, 137 insertions, 203 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) */
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index d6b93b93d9..1535d00d50 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -163,18 +163,7 @@ HGLRC SafeCreateContext(HDC &hdc)
GLuint SafeChoosePixelFormat(HDC &hdc, const PIXELFORMATDESCRIPTOR *ppfd)
{
- __try
- {
- return ChoosePixelFormat(hdc, ppfd);
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- // convert to C++ styled exception
- // C exception don't allow classes, so it's a regular char array
- char integer_string[32];
- sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
- throw std::exception(integer_string);
- }
+ return seh_catcher(ChoosePixelFormat(hdc, ppfd));
}
//static
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 4c90a82fcb..a13e4de308 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -400,17 +400,10 @@ void ll_nvapi_init(NvDRSSessionHandle hSession)
}
}
-//#define DEBUGGING_SEH_FILTER 1
-#if DEBUGGING_SEH_FILTER
-# define WINMAIN DebuggingWinMain
-#else
-# define WINMAIN wWinMain
-#endif
-
-int APIENTRY WINMAIN(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- PWSTR pCmdLine,
- int nCmdShow)
+int APIENTRY wWinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ PWSTR pCmdLine,
+ int nCmdShow)
{
// Call Tracy first thing to have it allocate memory
// https://github.com/wolfpld/tracy/issues/196
@@ -559,27 +552,6 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
return 0;
}
-#if DEBUGGING_SEH_FILTER
-// The compiler doesn't like it when you use __try/__except blocks
-// in a method that uses object destructors. Go figure.
-// This winmain just calls the real winmain inside __try.
-// The __except calls our exception filter function. For debugging purposes.
-int APIENTRY wWinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- PWSTR lpCmdLine,
- int nCmdShow)
-{
- __try
- {
- WINMAIN(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
- }
- __except( viewer_windows_exception_handler( GetExceptionInformation() ) )
- {
- _tprintf( _T("Exception handled.\n") );
- }
-}
-#endif
-
void LLAppViewerWin32::disableWinErrorReporting()
{
std::string executable_name = gDirUtilp->getExecutableFilename();
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index 99139e6528..1f2ebd5092 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -40,6 +40,7 @@
#include "llappviewer.h"
#include "llbufferstream.h"
+#include "llexception.h"
#include "llnotificationsutil.h"
#include "llviewercontrol.h"
#include "llworld.h"
@@ -377,33 +378,6 @@ bool LLFeatureManager::parseFeatureTable(std::string filename)
F32 gpu_benchmark();
-#if LL_WINDOWS
-
-F32 logExceptionBenchmark()
-{
- // FIXME: gpu_benchmark uses many C++ classes on the stack to control state.
- // SEH exceptions with our current exception handling options do not call
- // destructors for these classes, resulting in an undefined state should
- // this handler be invoked.
- F32 gbps = -1;
- __try
- {
- gbps = gpu_benchmark();
- }
- __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation()))
- {
- // HACK - ensure that profiling is disabled
- LLGLSLShader::finishProfile(false);
-
- // convert to C++ styled exception
- char integer_string[32];
- sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
- throw std::exception(integer_string);
- }
- return gbps;
-}
-#endif
-
bool LLFeatureManager::loadGPUClass()
{
if (!gSavedSettings.getBOOL("SkipBenchmark"))
@@ -413,14 +387,12 @@ bool LLFeatureManager::loadGPUClass()
F32 gbps;
try
{
-#if LL_WINDOWS
- gbps = logExceptionBenchmark();
-#else
- gbps = gpu_benchmark();
-#endif
+ gbps = seh_catcher(gpu_benchmark);
}
catch (const std::exception& e)
{
+ // HACK - ensure that profiling is disabled
+ LLGLSLShader::finishProfile(false);
gbps = -1.f;
LL_WARNS("RenderInit") << "GPU benchmark failed: " << e.what() << LL_ENDL;
}
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index 1239b34d04..d1c65d6aa7 100644
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -36,6 +36,7 @@
#include "linden_common.h"
#include "llerrorcontrol.h"
+#include "llexception.h"
#include "lltut.h"
#include "chained_callback.h"
#include "stringize.h"
@@ -68,13 +69,6 @@
#pragma warning (pop)
#endif
-// 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>
-
#include <fstream>
void wouldHaveCrashed(const std::string& message);
@@ -524,64 +518,6 @@ void wouldHaveCrashed(const std::string& message)
static LLTrace::ThreadRecorder* sMasterThreadRecorder = NULL;
-// 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
-
-static constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
-static constexpr U32 STATUS_STACK_FULL = 0xC00000FD;
-
-U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS*)
-{
- if (code == STATUS_MSC_EXCEPTION)
- {
- // C++ exception, go on -- but TUT is supposed to have caught those already?!
- return EXCEPTION_CONTINUE_SEARCH;
- }
- else
- {
- // This is a non-C++ exception, e.g. hardware check.
- // By the time the handler gets control, the stack has been unwound,
- // so report the stack trace now at filter() time.
- // Sadly, even though, at the time of this writing, stack overflow is
- // the problem we would most like to diagnose, calling another
- // function when the stack is already blown only terminates us faster.
- if (code != STATUS_STACK_FULL)
- {
- std::cerr << boost::stacktrace::stacktrace() << std::endl;
- }
- // pass control into the handler block
- return EXCEPTION_EXECUTE_HANDLER;
- }
-}
-
-template <typename CALLABLE0, typename CALLABLE1>
-void seh_catcher(CALLABLE0&& trycode, CALLABLE1&& handler)
-{
- __try
- {
- trycode();
- }
- __except (seh_filter(GetExceptionCode(), GetExceptionInformation()))
- {
- handler(GetExceptionCode());
- }
-}
-
-#else // not LL_WINDOWS
-
-template <typename CALLABLE0, typename CALLABLE1>
-void seh_catcher(CALLABLE0&& trycode, CALLABLE1&&)
-{
- trycode();
-}
-
-#endif // not LL_WINDOWS
-
int main(int argc, char **argv)
{
// The following line must be executed to initialize Google Mock
@@ -734,7 +670,7 @@ int main(int argc, char **argv)
}
},
// __except
- [mycallback](U32 code)
+ [mycallback](U32 code, const std::string& /*stacktrace*/)
{
static std::map<U32, const char*> codes = {
{ 0xC0000005, "Access Violation" },