diff options
-rw-r--r-- | indra/llcommon/llexception.cpp | 50 | ||||
-rw-r--r-- | indra/llcommon/llexception.h | 86 | ||||
-rw-r--r-- | indra/llwindow/llwindowwin32.cpp | 13 | ||||
-rw-r--r-- | indra/newview/llappviewerwin32.cpp | 36 | ||||
-rw-r--r-- | indra/test/test.cpp | 68 |
5 files changed, 121 insertions, 132 deletions
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index 74b33f1e3b..c0154a569f 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -15,12 +15,7 @@ #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> @@ -34,6 +29,7 @@ // 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> @@ -98,34 +94,15 @@ 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 constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific -static constexpr U32 STATUS_STACK_FULL = 0xC00000FD; +static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific -U32 ll_seh_filter( - std::string& stacktrace, - std::function<U32(U32, struct _EXCEPTION_POINTERS*)> filter, - U32 code, - struct _EXCEPTION_POINTERS* exception_infop) +U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) { - // 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); -} + 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; -U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS*) -{ if (code == STATUS_MSC_EXCEPTION) { // C++ exception, go on @@ -133,20 +110,9 @@ U32 seh_filter(U32 code, struct _EXCEPTION_POINTERS*) } else { - // This is a non-C++ exception, e.g. hardware check. + // handle it 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 3e50678b44..68e609444e 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -102,94 +102,14 @@ 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)); -} -// 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); -} +#if LL_WINDOWS // 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); - -#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)(); -} +U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop); -#endif // not LL_WINDOWS ----------------------------------------------------- +#endif //LL_WINDOWS #endif /* ! defined(LL_LLEXCEPTION_H) */ diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index 97bd789134..4fca74497f 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -160,7 +160,18 @@ HGLRC SafeCreateContext(HDC &hdc) GLuint SafeChoosePixelFormat(HDC &hdc, const PIXELFORMATDESCRIPTOR *ppfd) { - return seh_catcher(ChoosePixelFormat(hdc, 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); + } } //static diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp index ef609026ad..4f5fa53312 100644 --- a/indra/newview/llappviewerwin32.cpp +++ b/indra/newview/llappviewerwin32.cpp @@ -396,10 +396,17 @@ void ll_nvapi_init(NvDRSSessionHandle hSession) } } -int APIENTRY wWinMain(HINSTANCE hInstance, - HINSTANCE hPrevInstance, - PWSTR pCmdLine, - int nCmdShow) +//#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) { // Call Tracy first thing to have it allocate memory // https://github.com/wolfpld/tracy/issues/196 @@ -548,6 +555,27 @@ int APIENTRY wWinMain(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/test/test.cpp b/indra/test/test.cpp index 536c252d97..09147a65a3 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -36,7 +36,6 @@ #include "linden_common.h" #include "llerrorcontrol.h" -#include "llexception.h" #include "lltut.h" #include "chained_callback.h" #include "stringize.h" @@ -57,6 +56,13 @@ #include <boost/iostreams/tee.hpp> #include <boost/iostreams/stream.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> + #include <fstream> void wouldHaveCrashed(const std::string& message); @@ -506,6 +512,64 @@ 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) { ll_init_apr(); @@ -652,7 +716,7 @@ int main(int argc, char **argv) } }, // __except - [mycallback](U32 code, const std::string& /*stacktrace*/) + [mycallback](U32 code) { static std::map<U32, const char*> codes = { { 0xC0000005, "Access Violation" }, |