From f648758c2a3da2dd03c8f57e98598c085b2030a6 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Wed, 16 Jan 2019 11:05:55 -0500 Subject: SL-10297: Modify LL_ERRS and other deliberate crashes to avoid a common stack frame --- indra/llcommon/llerror.cpp | 51 +++++++++++------------------------ indra/llcommon/llerror.h | 24 +++++++++++++---- indra/llcommon/llerrorcontrol.h | 36 +++---------------------- indra/llcommon/llleap.cpp | 12 ++++----- indra/llcommon/llsingleton.cpp | 10 +------ indra/llcommon/tests/llerror_test.cpp | 2 +- indra/llcommon/tests/wrapllerrs.h | 10 +++---- 7 files changed, 49 insertions(+), 96 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 335a0995fe..0ddce353f1 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -683,7 +683,6 @@ namespace LLError::setDefaultLevel(LLError::LEVEL_INFO); LLError::setAlwaysFlush(true); LLError::setEnabledLogTypesMask(0xFFFFFFFF); - LLError::setFatalFunction(LLError::crashAndLoop); LLError::setTimeFunction(LLError::utcTime); // log_to_stderr is only false in the unit and integration tests to keep builds quieter @@ -719,16 +718,16 @@ namespace LLError commonInit(user_dir, app_dir, log_to_stderr); } - void setFatalFunction(const FatalFunction& f) + void overrideCrashOnError(const FatalFunction& fatal_function) { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - s->mCrashFunction = f; + s->mCrashFunction = fatal_function; } - FatalFunction getFatalFunction() + void restoreCrashOnError() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - return s->mCrashFunction; + s->mCrashFunction = NULL; } std::string getFatalMessage() @@ -1306,12 +1305,12 @@ namespace LLError return ; } - void Log::flush(std::ostringstream* out, const CallSite& site) + bool Log::flush(std::ostringstream* out, const CallSite& site) { LLMutexTrylock lock(&gLogMutex,5); if (!lock.isLocked()) { - return; + return true; } // If we hit a logging request very late during shutdown processing, @@ -1319,7 +1318,7 @@ namespace LLError // DO NOT resurrect them. if (Settings::wasDeleted() || Globals::wasDeleted()) { - return; + return true; } Globals* g = Globals::getInstance(); @@ -1353,7 +1352,7 @@ namespace LLError } else { - return; + return true; } } else @@ -1370,11 +1369,14 @@ namespace LLError if (site.mLevel == LEVEL_ERROR) { g->mFatalMessage = message; - if (s->mCrashFunction) - { - s->mCrashFunction(message); - } + if (s->mCrashFunction) + { + s->mCrashFunction(message); + return false; + } } + + return true; } } @@ -1437,29 +1439,6 @@ namespace LLError return s->mShouldLogCallCounter; } -#if LL_WINDOWS - // VC80 was optimizing the error away. - #pragma optimize("", off) -#endif - void crashAndLoop(const std::string& message) - { - // Now, we go kaboom! - int* make_me_crash = NULL; - - *make_me_crash = 0; - - while(true) - { - // Loop forever, in case the crash didn't work? - } - - // this is an attempt to let Coverity and other semantic scanners know that this function won't be returning ever. - exit(EXIT_FAILURE); - } -#if LL_WINDOWS - #pragma optimize("", on) -#endif - std::string utcTime() { time_t now = time(NULL); diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 0a78229555..cbade88f61 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -199,8 +199,12 @@ namespace LLError public: static bool shouldLog(CallSite&); static std::ostringstream* out(); + static void flush(std::ostringstream* out, char* message); - static void flush(std::ostringstream*, const CallSite&); + + // returns false iff there is a fatal crash override in effect + static bool flush(std::ostringstream*, const CallSite&); + static std::string demangle(const char* mangled); }; @@ -367,10 +371,20 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_NEWLINE '\n' -#define LL_ENDL \ - LLError::End(); \ - LLError::Log::flush(_out, _site); \ - } \ +// Use this only in LL_ERRS or in a place that LL_ERRS may not be used +#define LLERROR_CRASH \ +{ \ + int* make_me_crash = NULL;\ + *make_me_crash = 0; \ + exit(*make_me_crash); \ +} + +#define LL_ENDL \ + LLError::End(); \ + if (LLError::Log::flush(_out, _site) \ + && _site.mLevel == LLError::LEVEL_ERROR) \ + LLERROR_CRASH \ + } \ } while(0) // NEW Macros for debugging, allow the passing of a string tag diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 276d22fc36..7ca6ddb737 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -92,41 +92,13 @@ namespace LLError /* Control functions. */ - typedef boost::function FatalFunction; - LL_COMMON_API void crashAndLoop(const std::string& message); - // Default fatal function: access null pointer and loops forever - - LL_COMMON_API void setFatalFunction(const FatalFunction&); - // The fatal function will be called when an message of LEVEL_ERROR - // is logged. Note: supressing a LEVEL_ERROR message from being logged - // (by, for example, setting a class level to LEVEL_NONE), will keep - // the that message from causing the fatal funciton to be invoked. - - LL_COMMON_API FatalFunction getFatalFunction(); - // Retrieve the previously-set FatalFunction + LL_COMMON_API void overrideCrashOnError(const FatalFunction&); + LL_COMMON_API void restoreCrashOnError(); + LL_COMMON_API std::string getFatalMessage(); - // Retrieve the message last passed to FatalFunction, if any - - /// temporarily override the FatalFunction for the duration of a - /// particular scope, e.g. for unit tests - class LL_COMMON_API OverrideFatalFunction - { - public: - OverrideFatalFunction(const FatalFunction& func): - mPrev(getFatalFunction()) - { - setFatalFunction(func); - } - ~OverrideFatalFunction() - { - setFatalFunction(mPrev); - } - - private: - FatalFunction mPrev; - }; + // Retrieve the message last passed to LL_ERRS, if any typedef std::string (*TimeFunction)(); LL_COMMON_API std::string utcTime(); diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index cf8f8cc6a5..f7bfa36bb5 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -59,7 +59,6 @@ public: // pump name -- so it should NOT need tweaking for uniqueness. mReplyPump(LLUUID::generateNewID().asString()), mExpect(0), - mPrevFatalFunction(LLError::getFatalFunction()), // Instantiate a distinct LLLeapListener for this plugin. (Every // plugin will want its own collection of managed listeners, etc.) // Pass it a callback to our connect() method, so it can send events @@ -146,7 +145,7 @@ public: .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1)); // For our lifespan, intercept any LL_ERRS so we can notify plugin - LLError::setFatalFunction(boost::bind(&LLLeapImpl::fatalFunction, this, _1)); + LLError::overrideCrashOnError(boost::bind(&LLLeapImpl::fatalFunction, this, _1)); // Send child a preliminary event reporting our own reply-pump name -- // which would otherwise be pretty tricky to guess! @@ -162,8 +161,8 @@ public: virtual ~LLLeapImpl() { LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL; - // Restore original FatalFunction - LLError::setFatalFunction(mPrevFatalFunction); + // Restore original fatal crash behavior for LL_ERRS + LLError::restoreCrashOnError(); } // Listener for failed launch attempt @@ -397,8 +396,8 @@ public: mainloop.post(nop); } - // forward the call to the previous FatalFunction - mPrevFatalFunction(error); + // go ahead and do the crash that LLError would have done + LLERROR_CRASH } private: @@ -421,7 +420,6 @@ private: mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection; boost::scoped_ptr mBlocker; LLProcess::ReadPipe::size_type mExpect; - LLError::FatalFunction mPrevFatalFunction; boost::scoped_ptr mListener; }; diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index c45c144570..6f254ef670 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -461,15 +461,7 @@ void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, co // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG) std::ostringstream out; out << p1 << p2 << p3 << p4; - auto crash = LLError::getFatalFunction(); - if (crash) - { - crash(out.str()); - } - else - { - LLError::crashAndLoop(out.str()); - } + LLERROR_CRASH; } std::string LLSingletonBase::demangle(const char* mangled) diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index 8e1f4c14ac..2f8923d2de 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -120,7 +120,7 @@ namespace tut mPriorErrorSettings = LLError::saveAndResetSettings(); LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - LLError::setFatalFunction(fatalCall); + LLError::overrideCrashOnError(fatalCall); LLError::addRecorder(mRecorder); } diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index b07d5afbd8..b280271476 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -54,17 +54,15 @@ struct WrapLLErrs // Resetting Settings discards the default Recorder that writes to // stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the // console output of successful tests, potentially confusing things. - mPriorErrorSettings(LLError::saveAndResetSettings()), - // Save shutdown function called by LL_ERRS - mPriorFatal(LLError::getFatalFunction()) + mPriorErrorSettings(LLError::saveAndResetSettings()) { // Make LL_ERRS call our own operator() method - LLError::setFatalFunction(boost::bind(&WrapLLErrs::operator(), this, _1)); + LLError::overrideCrashOnError(boost::bind(&WrapLLErrs::operator(), this, _1)); } ~WrapLLErrs() { - LLError::setFatalFunction(mPriorFatal); + LLError::restoreCrashOnError(); LLError::restoreSettings(mPriorErrorSettings); } @@ -203,7 +201,7 @@ public: mOldSettings(LLError::saveAndResetSettings()), mRecorder(new CaptureLogRecorder()) { - LLError::setFatalFunction(wouldHaveCrashed); + LLError::overrideCrashOnError(wouldHaveCrashed); LLError::setDefaultLevel(level); LLError::addRecorder(mRecorder); } -- cgit v1.2.3 From e409c0492f1b1ce63606c0b693c92cdb36dcc28b Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Sat, 2 Mar 2019 11:58:11 -0500 Subject: convert to an explicit USE_BUGSPLAT switch in cmake, revise LL_ERRS approach --- indra/llcommon/CMakeLists.txt | 9 +++++---- indra/llcommon/llerror.cpp | 21 ++++++++++++++------- indra/llcommon/llerror.h | 20 ++++++++++++-------- indra/llcommon/llerrorcontrol.h | 14 +++++++++++--- indra/llcommon/llleap.cpp | 30 +----------------------------- indra/llcommon/tests/wrapllerrs.h | 5 +++-- 6 files changed, 46 insertions(+), 53 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index af41b9e460..e3afa5eca4 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -5,6 +5,7 @@ project(llcommon) include(00-Common) include(LLCommon) +include(bugsplat) include(Linking) include(Boost) include(LLSharedLibs) @@ -257,10 +258,10 @@ set(llcommon_HEADER_FILES set_source_files_properties(${llcommon_HEADER_FILES} PROPERTIES HEADER_FILE_ONLY TRUE) -if (BUGSPLAT_DB) - set_source_files_properties(llapp.cpp - PROPERTIES COMPILE_DEFINITIONS "LL_BUGSPLAT") -endif (BUGSPLAT_DB) +if (USE_BUGSPLAT) + set_source_files_properties(${llcommon_SOURCE_FILES} + PROPERTIES COMPILE_DEFINITIONS "${BUGSPLAT_DEFINE}") +endif (USE_BUGSPLAT) list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 0ddce353f1..77d7fe1b24 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -25,6 +25,7 @@ * $/LicenseInfo$ */ +#define _LLERROR_CPP_ #include "linden_common.h" #include "llerror.h" @@ -724,7 +725,7 @@ namespace LLError s->mCrashFunction = fatal_function; } - void restoreCrashOnError() + void restoreCrashOnError() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); s->mCrashFunction = NULL; @@ -1310,7 +1311,7 @@ namespace LLError LLMutexTrylock lock(&gLogMutex,5); if (!lock.isLocked()) { - return true; + return false; // because this wasn't logged, it cannot be fatal } // If we hit a logging request very late during shutdown processing, @@ -1318,7 +1319,7 @@ namespace LLError // DO NOT resurrect them. if (Settings::wasDeleted() || Globals::wasDeleted()) { - return true; + return false; // because this wasn't logged, it cannot be fatal } Globals* g = Globals::getInstance(); @@ -1352,7 +1353,7 @@ namespace LLError } else { - return true; + return false; // because this wasn't logged, it cannot be fatal } } else @@ -1372,11 +1373,17 @@ namespace LLError if (s->mCrashFunction) { s->mCrashFunction(message); - return false; + return false; // because an override is in effect + } + else + { + return true; // calling macro should crash } } - - return true; + else + { + return false; // not ERROR, so do not crash + } } } diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index cbade88f61..07aa5c6f8b 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -202,7 +202,7 @@ namespace LLError static void flush(std::ostringstream* out, char* message); - // returns false iff there is a fatal crash override in effect + // returns false iff the calling macro should crash static bool flush(std::ostringstream*, const CallSite&); static std::string demangle(const char* mangled); @@ -371,18 +371,22 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_NEWLINE '\n' +#ifdef _LLERROR_CPP_ +volatile int* gCauseCrash = NULL; +#else +volatile extern int* gCauseCrash; +#endif // _LLERROR_CPP_ + // Use this only in LL_ERRS or in a place that LL_ERRS may not be used -#define LLERROR_CRASH \ -{ \ - int* make_me_crash = NULL;\ - *make_me_crash = 0; \ - exit(*make_me_crash); \ +#define LLERROR_CRASH \ +{ \ + *gCauseCrash = 0; \ + exit(*gCauseCrash); \ } #define LL_ENDL \ LLError::End(); \ - if (LLError::Log::flush(_out, _site) \ - && _site.mLevel == LLError::LEVEL_ERROR) \ + if (LLError::Log::flush(_out, _site)) \ LLERROR_CRASH \ } \ } while(0) diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 7ca6ddb737..af46430f74 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -92,11 +92,19 @@ namespace LLError /* Control functions. */ + typedef boost::function FatalFunction; - LL_COMMON_API void overrideCrashOnError(const FatalFunction&); - LL_COMMON_API void restoreCrashOnError(); - + /// Override the default behavior of crashing on LL_ERRS; this should NEVER be used except in test code + LL_COMMON_API void overrideCrashOnError(const FatalFunction&); + // The fatal function will be called when an message of LEVEL_ERROR + // is logged. Note: supressing a LEVEL_ERROR message from being logged + // (by, for example, setting a class level to LEVEL_NONE), will keep + // the that message from causing the fatal funciton to be invoked. + + /// Undo the effect of the overrideCrashOnError above + LL_COMMON_API void restoreCrashOnError(); + LL_COMMON_API std::string getFatalMessage(); // Retrieve the message last passed to LL_ERRS, if any diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index f7bfa36bb5..bf20c87c89 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -59,6 +59,7 @@ public: // pump name -- so it should NOT need tweaking for uniqueness. mReplyPump(LLUUID::generateNewID().asString()), mExpect(0), + // Instantiate a distinct LLLeapListener for this plugin. (Every // plugin will want its own collection of managed listeners, etc.) // Pass it a callback to our connect() method, so it can send events @@ -144,9 +145,6 @@ public: mStderrConnection = childerr.getPump() .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1)); - // For our lifespan, intercept any LL_ERRS so we can notify plugin - LLError::overrideCrashOnError(boost::bind(&LLLeapImpl::fatalFunction, this, _1)); - // Send child a preliminary event reporting our own reply-pump name -- // which would otherwise be pretty tricky to guess! wstdin(mReplyPump.getName(), @@ -161,8 +159,6 @@ public: virtual ~LLLeapImpl() { LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL; - // Restore original fatal crash behavior for LL_ERRS - LLError::restoreCrashOnError(); } // Listener for failed launch attempt @@ -376,30 +372,6 @@ public: return false; } - void fatalFunction(const std::string& error) - { - // Notify plugin - LLSD event; - event["type"] = "error"; - event["error"] = error; - mReplyPump.post(event); - - // All the above really accomplished was to buffer the serialized - // event in our WritePipe. Have to pump mainloop a couple times to - // really write it out there... but time out in case we can't write. - LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN)); - LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); - LLSD nop; - F64 until = (LLTimer::getElapsedSeconds() + 2).value(); - while (childin.size() && LLTimer::getElapsedSeconds() < until) - { - mainloop.post(nop); - } - - // go ahead and do the crash that LLError would have done - LLERROR_CRASH - } - private: /// We always want to listen on mReplyPump with wstdin(); under some /// circumstances we'll also echo other LLEventPumps to the plugin. diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index b280271476..fedc17dbe9 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -50,11 +50,11 @@ extern void wouldHaveCrashed(const std::string& message); struct WrapLLErrs { - WrapLLErrs(): + WrapLLErrs() // Resetting Settings discards the default Recorder that writes to // stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the // console output of successful tests, potentially confusing things. - mPriorErrorSettings(LLError::saveAndResetSettings()) + :mPriorErrorSettings(LLError::saveAndResetSettings()) { // Make LL_ERRS call our own operator() method LLError::overrideCrashOnError(boost::bind(&WrapLLErrs::operator(), this, _1)); @@ -210,6 +210,7 @@ public: { LLError::removeRecorder(mRecorder); LLError::restoreSettings(mOldSettings); + LLError::restoreCrashOnError(); } /// Don't assume the message we want is necessarily the LAST log message -- cgit v1.2.3 From e86c0b3d0fbc5f91090241be959ef19bfffd8636 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 5 Mar 2019 04:44:29 -0500 Subject: fix error message reporting? --- indra/llcommon/llerror.cpp | 18 ++++++++---------- indra/llcommon/llerror.h | 6 ++++-- indra/llcommon/llerrorcontrol.h | 11 ++++++++--- indra/llcommon/tests/llerror_test.cpp | 8 ++++++-- indra/llcommon/tests/wrapllerrs.h | 8 ++++---- 5 files changed, 30 insertions(+), 21 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 77d7fe1b24..6fa9e590cb 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -719,7 +719,7 @@ namespace LLError commonInit(user_dir, app_dir, log_to_stderr); } - void overrideCrashOnError(const FatalFunction& fatal_function) + void setFatalHandler(const FatalFunction& fatal_function) { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); s->mCrashFunction = fatal_function; @@ -1306,12 +1306,12 @@ namespace LLError return ; } - bool Log::flush(std::ostringstream* out, const CallSite& site) + ErrCrashHandlerResult Log::flush(std::ostringstream* out, const CallSite& site) { LLMutexTrylock lock(&gLogMutex,5); if (!lock.isLocked()) { - return false; // because this wasn't logged, it cannot be fatal + return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal } // If we hit a logging request very late during shutdown processing, @@ -1319,7 +1319,7 @@ namespace LLError // DO NOT resurrect them. if (Settings::wasDeleted() || Globals::wasDeleted()) { - return false; // because this wasn't logged, it cannot be fatal + return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal } Globals* g = Globals::getInstance(); @@ -1353,7 +1353,7 @@ namespace LLError } else { - return false; // because this wasn't logged, it cannot be fatal + return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal } } else @@ -1369,20 +1369,18 @@ namespace LLError if (site.mLevel == LEVEL_ERROR) { - g->mFatalMessage = message; if (s->mCrashFunction) { - s->mCrashFunction(message); - return false; // because an override is in effect + return s->mCrashFunction(message); } else { - return true; // calling macro should crash + return ERR_CRASH; // calling macro should crash } } else { - return false; // not ERROR, so do not crash + return ERR_DO_NOT_CRASH; // not ERROR, so do not crash } } } diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 07aa5c6f8b..2a73ccb36a 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -194,6 +194,8 @@ namespace LLError struct CallSite; + enum ErrCrashHandlerResult { ERR_DO_NOT_CRASH, ERR_CRASH }; + class LL_COMMON_API Log { public: @@ -203,7 +205,7 @@ namespace LLError static void flush(std::ostringstream* out, char* message); // returns false iff the calling macro should crash - static bool flush(std::ostringstream*, const CallSite&); + static ErrCrashHandlerResult flush(std::ostringstream*, const CallSite&); static std::string demangle(const char* mangled); }; @@ -386,7 +388,7 @@ volatile extern int* gCauseCrash; #define LL_ENDL \ LLError::End(); \ - if (LLError::Log::flush(_out, _site)) \ + if (LLError::ERR_CRASH == LLError::Log::flush(_out, _site)) \ LLERROR_CRASH \ } \ } while(0) diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index af46430f74..2fa220ba5a 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -93,16 +93,21 @@ namespace LLError Control functions. */ - typedef boost::function FatalFunction; + // A FatalFunction is called if set using setFatalHandler; its return controls + // whether or not the calling error logging code should crash. + // ERR_DO_NOT_CRASH should be used only in test code. + typedef boost::function FatalFunction; + /// Override the default behavior of crashing on LL_ERRS; this should NEVER be used except in test code - LL_COMMON_API void overrideCrashOnError(const FatalFunction&); + LL_COMMON_API void setFatalHandler(const FatalFunction&); // The fatal function will be called when an message of LEVEL_ERROR // is logged. Note: supressing a LEVEL_ERROR message from being logged // (by, for example, setting a class level to LEVEL_NONE), will keep // the that message from causing the fatal funciton to be invoked. + // The - /// Undo the effect of the overrideCrashOnError above + /// Undo the effect of the setFatalHandler above LL_COMMON_API void restoreCrashOnError(); LL_COMMON_API std::string getFatalMessage(); diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index 2f8923d2de..9dcb5c145f 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -70,7 +70,11 @@ namespace namespace { static bool fatalWasCalled; - void fatalCall(const std::string&) { fatalWasCalled = true; } + LLError::ErrCrashHandlerResult fatalCall(const std::string&) + { + fatalWasCalled = true; + return LLError::ERR_DO_NOT_CRASH; + } } namespace tut @@ -120,7 +124,7 @@ namespace tut mPriorErrorSettings = LLError::saveAndResetSettings(); LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - LLError::overrideCrashOnError(fatalCall); + LLError::setFatalHandler(fatalCall); LLError::addRecorder(mRecorder); } diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index fedc17dbe9..a412e12a04 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -46,7 +46,7 @@ // statically reference the function in test.cpp... it's short, we could // replicate, but better to reuse -extern void wouldHaveCrashed(const std::string& message); +extern LLError::ErrCrashHandlerResult wouldHaveCrashed(const std::string& message); struct WrapLLErrs { @@ -57,7 +57,7 @@ struct WrapLLErrs :mPriorErrorSettings(LLError::saveAndResetSettings()) { // Make LL_ERRS call our own operator() method - LLError::overrideCrashOnError(boost::bind(&WrapLLErrs::operator(), this, _1)); + LLError::setFatalHandler(boost::bind(&WrapLLErrs::operator(), this, _1)); } ~WrapLLErrs() @@ -71,7 +71,7 @@ struct WrapLLErrs FatalException(const std::string& what): LLException(what) {} }; - void operator()(const std::string& message) + LLError::ErrCrashHandlerResult operator()(const std::string& message) { // Save message for later in case consumer wants to sense the result directly error = message; @@ -201,7 +201,7 @@ public: mOldSettings(LLError::saveAndResetSettings()), mRecorder(new CaptureLogRecorder()) { - LLError::overrideCrashOnError(wouldHaveCrashed); + LLError::setFatalHandler(wouldHaveCrashed); LLError::setDefaultLevel(level); LLError::addRecorder(mRecorder); } -- cgit v1.2.3 From 07870a9ebb17e7a31df276f4ac2d8f52fec2a3b0 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 5 Mar 2019 13:57:02 -0500 Subject: rename crash handler to hook, and make them chainable by putting back the get and documenting it --- indra/llcommon/llerror.cpp | 18 +++++++++--------- indra/llcommon/llerror.h | 6 +++--- indra/llcommon/llerrorcontrol.h | 26 ++++++++++++++------------ indra/llcommon/llleap.cpp | 32 +++++++++++++++++++++++++++++++- indra/llcommon/tests/llerror_test.cpp | 4 ++-- indra/llcommon/tests/wrapllerrs.h | 23 +++++++++++++---------- 6 files changed, 72 insertions(+), 37 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 6fa9e590cb..5f3edf7f84 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -483,7 +483,7 @@ namespace LLError LevelMap mTagLevelMap; std::map mUniqueLogMessages; - LLError::FatalFunction mCrashFunction; + LLError::FatalHook mFatalHook; LLError::TimeFunction mTimeFunction; Recorders mRecorders; @@ -523,7 +523,7 @@ namespace LLError mFileLevelMap(), mTagLevelMap(), mUniqueLogMessages(), - mCrashFunction(NULL), + mFatalHook(NULL), mTimeFunction(NULL), mRecorders(), mFileRecorder(), @@ -719,16 +719,16 @@ namespace LLError commonInit(user_dir, app_dir, log_to_stderr); } - void setFatalHandler(const FatalFunction& fatal_function) + void setFatalHook(const FatalHook& fatal_hook) { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - s->mCrashFunction = fatal_function; + s->mFatalHook = fatal_hook; } - void restoreCrashOnError() + FatalHook getFatalHook() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - s->mCrashFunction = NULL; + return s->mFatalHook; } std::string getFatalMessage() @@ -1306,7 +1306,7 @@ namespace LLError return ; } - ErrCrashHandlerResult Log::flush(std::ostringstream* out, const CallSite& site) + ErrFatalHookResult Log::flush(std::ostringstream* out, const CallSite& site) { LLMutexTrylock lock(&gLogMutex,5); if (!lock.isLocked()) @@ -1369,9 +1369,9 @@ namespace LLError if (site.mLevel == LEVEL_ERROR) { - if (s->mCrashFunction) + if (s->mFatalHook) { - return s->mCrashFunction(message); + return s->mFatalHook(message); } else { diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 2a73ccb36a..6bdb2e852f 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -194,7 +194,7 @@ namespace LLError struct CallSite; - enum ErrCrashHandlerResult { ERR_DO_NOT_CRASH, ERR_CRASH }; + enum ErrFatalHookResult { ERR_DO_NOT_CRASH, ERR_CRASH }; class LL_COMMON_API Log { @@ -205,7 +205,7 @@ namespace LLError static void flush(std::ostringstream* out, char* message); // returns false iff the calling macro should crash - static ErrCrashHandlerResult flush(std::ostringstream*, const CallSite&); + static ErrFatalHookResult flush(std::ostringstream*, const CallSite&); static std::string demangle(const char* mangled); }; @@ -272,7 +272,7 @@ namespace LLError //when LLAppViewer::handleViewerCrash() is triggered. // //Note: to be simple, efficient and necessary to keep track of correct call stacks, - //LLCallStacks is designed not to be thread-safe. + //LLCallStacks is designed not to be thread-safe. //so try not to use it in multiple parallel threads at same time. //Used in a single thread at a time is fine. class LL_COMMON_API LLCallStacks diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 2fa220ba5a..e7c5241cc9 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -93,22 +93,24 @@ namespace LLError Control functions. */ - // A FatalFunction is called if set using setFatalHandler; its return controls + // A FatalHook is called if set using setFatalHook; its return controls // whether or not the calling error logging code should crash. // ERR_DO_NOT_CRASH should be used only in test code. - typedef boost::function FatalFunction; - - - /// Override the default behavior of crashing on LL_ERRS; this should NEVER be used except in test code - LL_COMMON_API void setFatalHandler(const FatalFunction&); - // The fatal function will be called when an message of LEVEL_ERROR + typedef boost::function FatalHook; + + /// Supplement and control the default behavior of crashing on LL_ERRS + /// This may be used to suppress crashes only in test code; + /// otherwise, the FatalHook should always either return + /// the result from the previous hook (see getFatalHook below), + /// or return LLError::ERR_CRASH + LL_COMMON_API void setFatalHook(const FatalHook& fatal_hook); + // The fatal_hook function will be called when an message of LEVEL_ERROR // is logged. Note: supressing a LEVEL_ERROR message from being logged - // (by, for example, setting a class level to LEVEL_NONE), will keep - // the that message from causing the fatal funciton to be invoked. - // The + // (by, for example, setting a class level to LEVEL_NONE), will also + // prevent the fatal_hook being called and the resulting deliberate crash - /// Undo the effect of the setFatalHandler above - LL_COMMON_API void restoreCrashOnError(); + /// Retrieve the previously-set FatalHook + LL_COMMON_API FatalHook getFatalHook(); LL_COMMON_API std::string getFatalMessage(); // Retrieve the message last passed to LL_ERRS, if any diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index bf20c87c89..161d25bc34 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -59,7 +59,7 @@ public: // pump name -- so it should NOT need tweaking for uniqueness. mReplyPump(LLUUID::generateNewID().asString()), mExpect(0), - + mPrevFatalHook(LLError::getFatalHook()), // Instantiate a distinct LLLeapListener for this plugin. (Every // plugin will want its own collection of managed listeners, etc.) // Pass it a callback to our connect() method, so it can send events @@ -145,6 +145,9 @@ public: mStderrConnection = childerr.getPump() .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1)); + // For our lifespan, intercept any LL_ERRS so we can notify plugin + LLError::setFatalHook(boost::bind(&LLLeapImpl::fatalHook, this, _1)); + // Send child a preliminary event reporting our own reply-pump name -- // which would otherwise be pretty tricky to guess! wstdin(mReplyPump.getName(), @@ -159,6 +162,8 @@ public: virtual ~LLLeapImpl() { LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL; + // Restore original FatalHook + LLError::setFatalHook(mPrevFatalHook); } // Listener for failed launch attempt @@ -372,6 +377,30 @@ public: return false; } + LLError::ErrFatalHookResult fatalHook(const std::string& error) + { + // Notify plugin + LLSD event; + event["type"] = "error"; + event["error"] = error; + mReplyPump.post(event); + + // All the above really accomplished was to buffer the serialized + // event in our WritePipe. Have to pump mainloop a couple times to + // really write it out there... but time out in case we can't write. + LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN)); + LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); + LLSD nop; + F64 until = (LLTimer::getElapsedSeconds() + 2).value(); + while (childin.size() && LLTimer::getElapsedSeconds() < until) + { + mainloop.post(nop); + } + + // forward the call to the previous FatalHook, default to crashing if there isn't one + return mPrevFatalHook ? mPrevFatalHook(error) : LLError::ERR_CRASH; + } + private: /// We always want to listen on mReplyPump with wstdin(); under some /// circumstances we'll also echo other LLEventPumps to the plugin. @@ -392,6 +421,7 @@ private: mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection; boost::scoped_ptr mBlocker; LLProcess::ReadPipe::size_type mExpect; + LLError::FatalHook mPrevFatalHook; boost::scoped_ptr mListener; }; diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index 9dcb5c145f..9895abc1f3 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -70,7 +70,7 @@ namespace namespace { static bool fatalWasCalled; - LLError::ErrCrashHandlerResult fatalCall(const std::string&) + LLError::ErrFatalHookResult fatalHook(const std::string&) { fatalWasCalled = true; return LLError::ERR_DO_NOT_CRASH; @@ -124,7 +124,7 @@ namespace tut mPriorErrorSettings = LLError::saveAndResetSettings(); LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - LLError::setFatalHandler(fatalCall); + LLError::setFatalHook(fatalHook); LLError::addRecorder(mRecorder); } diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index a412e12a04..a6c44d5fdd 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -46,7 +46,7 @@ // statically reference the function in test.cpp... it's short, we could // replicate, but better to reuse -extern LLError::ErrCrashHandlerResult wouldHaveCrashed(const std::string& message); +extern LLError::ErrFatalHookResult wouldHaveCrashed(const std::string& message); struct WrapLLErrs { @@ -55,14 +55,15 @@ struct WrapLLErrs // stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the // console output of successful tests, potentially confusing things. :mPriorErrorSettings(LLError::saveAndResetSettings()) + ,mPriorFatalHook(LLError::getFatalHook()) { // Make LL_ERRS call our own operator() method - LLError::setFatalHandler(boost::bind(&WrapLLErrs::operator(), this, _1)); + LLError::setFatalHook(boost::bind(&WrapLLErrs::operator(), this, _1)); } ~WrapLLErrs() { - LLError::restoreCrashOnError(); + LLError::setFatalHook(mPriorFatalHook); LLError::restoreSettings(mPriorErrorSettings); } @@ -71,7 +72,7 @@ struct WrapLLErrs FatalException(const std::string& what): LLException(what) {} }; - LLError::ErrCrashHandlerResult operator()(const std::string& message) + LLError::ErrFatalHookResult operator()(const std::string& message) { // Save message for later in case consumer wants to sense the result directly error = message; @@ -107,7 +108,7 @@ struct WrapLLErrs std::string error; LLError::SettingsStoragePtr mPriorErrorSettings; - LLError::FatalFunction mPriorFatal; + LLError::FatalHook mPriorFatalHook; }; /** @@ -197,11 +198,12 @@ public: // with that output. If it turns out that saveAndResetSettings() has // some bad effect, give up and just let the DEBUG level log messages // display. - : boost::noncopyable(), - mOldSettings(LLError::saveAndResetSettings()), - mRecorder(new CaptureLogRecorder()) + : boost::noncopyable() + , mOldSettings(LLError::saveAndResetSettings()) + , mPriorFatalHook(LLError::getFatalHook()) + , mRecorder(new CaptureLogRecorder()) { - LLError::setFatalHandler(wouldHaveCrashed); + LLError::setFatalHook(wouldHaveCrashed); LLError::setDefaultLevel(level); LLError::addRecorder(mRecorder); } @@ -210,7 +212,7 @@ public: { LLError::removeRecorder(mRecorder); LLError::restoreSettings(mOldSettings); - LLError::restoreCrashOnError(); + LLError::setFatalHook(mPriorFatalHook); } /// Don't assume the message we want is necessarily the LAST log message @@ -228,6 +230,7 @@ public: private: LLError::SettingsStoragePtr mOldSettings; + LLError::FatalHook mPriorFatalHook; LLError::RecorderPtr mRecorder; }; -- cgit v1.2.3 From e711376cc75a51973291d1730467c0b6e3a74226 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Wed, 5 Jun 2019 14:27:48 -0400 Subject: assorted cleanup --- indra/llcommon/llapp.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 421af3006e..34c969437b 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -526,7 +526,12 @@ void LLApp::setupErrorHandling(bool second_instance) #endif // LL_LINUX #endif // ! LL_WINDOWS + +#ifdef LL_BUGSPLAT + // do not start our own error thread +#else // ! LL_BUGSPLAT startErrorThread(); +#endif } void LLApp::startErrorThread() -- cgit v1.2.3 From 83ec9ebfbd602771dbc45c08fe203044b5ce3527 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 24 Sep 2019 10:15:20 -0400 Subject: log calls that modify the fatal error hook --- indra/llcommon/llerror.cpp | 2 ++ indra/llcommon/tests/llerror_test.cpp | 17 ++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 5f3edf7f84..da68117ff5 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -723,11 +723,13 @@ namespace LLError { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); s->mFatalHook = fatal_hook; + LL_DEBUGS("FatalHook") << "set fatal hook to " << fatal_hook << LL_ENDL; } FatalHook getFatalHook() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); + LL_DEBUGS("FatalHook") << "read fatal hook " << s->mFatalHook << LL_ENDL; return s->mFatalHook; } diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index 9895abc1f3..cdc2bf8c87 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -781,30 +781,33 @@ namespace tut // proper cached, efficient lookup of filtering void ErrorTestObject::test<15>() { - LLError::setDefaultLevel(LLError::LEVEL_NONE); + LLError::setDefaultLevel(LLError::LEVEL_NONE); + + // Note that the setFatalHook in the ErrorTestData constructor + // increments the shouldLogCallCount TestAlpha::doInfo(); ensure_message_count(0); - ensure_equals("first check", LLError::shouldLogCallCount(), 1); + ensure_equals("first check", LLError::shouldLogCallCount(), 2); TestAlpha::doInfo(); ensure_message_count(0); - ensure_equals("second check", LLError::shouldLogCallCount(), 1); + ensure_equals("second check", LLError::shouldLogCallCount(), 2); LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG); TestAlpha::doInfo(); ensure_message_count(1); - ensure_equals("third check", LLError::shouldLogCallCount(), 2); + ensure_equals("third check", LLError::shouldLogCallCount(), 3); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("fourth check", LLError::shouldLogCallCount(), 2); + ensure_equals("fourth check", LLError::shouldLogCallCount(), 3); LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("fifth check", LLError::shouldLogCallCount(), 3); + ensure_equals("fifth check", LLError::shouldLogCallCount(), 4); TestAlpha::doInfo(); ensure_message_count(2); - ensure_equals("sixth check", LLError::shouldLogCallCount(), 3); + ensure_equals("sixth check", LLError::shouldLogCallCount(), 4); } template<> template<> -- cgit v1.2.3 From d0619f8e476c1c5118ae926dfe1746f1dda986d8 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 24 Sep 2019 14:27:21 -0400 Subject: improve fatal hook logging --- indra/llcommon/llerror.cpp | 4 ++-- indra/llcommon/llleap.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index da68117ff5..df8443bd7a 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -723,13 +723,13 @@ namespace LLError { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); s->mFatalHook = fatal_hook; - LL_DEBUGS("FatalHook") << "set fatal hook to " << fatal_hook << LL_ENDL; + LL_DEBUGS("FatalHook") << "set fatal hook to " << reinterpret_cast(fatal_hook) << LL_ENDL; } FatalHook getFatalHook() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - LL_DEBUGS("FatalHook") << "read fatal hook " << s->mFatalHook << LL_ENDL; + LL_DEBUGS("FatalHook") << "read fatal hook " << reinterpret_cast(s->mFatalHook) << LL_ENDL; return s->mFatalHook; } diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index 161d25bc34..bb32c73d04 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -67,6 +67,7 @@ public: // this class or method name. mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2))) { + LL_DEBUGS("FatalHook") << "previous fatal hook was " <(mPrevFatalHook) << LL_ENDL; // Rule out unpopulated Params block if (! cparams.executable.isProvided()) { @@ -398,6 +399,7 @@ public: } // forward the call to the previous FatalHook, default to crashing if there isn't one + LL_DEBUGS("FatalHook") << "end" << LL_ENDL; return mPrevFatalHook ? mPrevFatalHook(error) : LLError::ERR_CRASH; } -- cgit v1.2.3 From 581b6fe5234110efa9c27751ed2c9e062ae8f248 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 24 Sep 2019 15:08:45 -0400 Subject: simplify function pointer logging --- indra/llcommon/llerror.cpp | 10 ++++++---- indra/llcommon/llleap.cpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index df8443bd7a..1f947a0a74 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -722,14 +722,16 @@ namespace LLError void setFatalHook(const FatalHook& fatal_hook) { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); + LL_DEBUGS("FatalHook") << "set fatal hook to "(fatal_hook ? "non-null" : "null") + << " was " << (s->mFatalHook ? "non-null" : "null") + << LL_ENDL; s->mFatalHook = fatal_hook; - LL_DEBUGS("FatalHook") << "set fatal hook to " << reinterpret_cast(fatal_hook) << LL_ENDL; - } - + } + FatalHook getFatalHook() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - LL_DEBUGS("FatalHook") << "read fatal hook " << reinterpret_cast(s->mFatalHook) << LL_ENDL; + LL_DEBUGS("FatalHook") << "read fatal hook was " << (s->mFatalHook ? "non-null" : "null") << LL_ENDL; return s->mFatalHook; } diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index bb32c73d04..1c0678e453 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -67,7 +67,7 @@ public: // this class or method name. mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2))) { - LL_DEBUGS("FatalHook") << "previous fatal hook was " <(mPrevFatalHook) << LL_ENDL; + LL_DEBUGS("FatalHook") << "previous fatal hook was " << (mPrevFatalHook ? "non-null" : "null") << LL_ENDL; // Rule out unpopulated Params block if (! cparams.executable.isProvided()) { -- cgit v1.2.3 From 092bf4220eeaea0ae7765ea3cb49f122fb3e3dff Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 24 Sep 2019 15:33:42 -0400 Subject: further simplify function pointer logging --- indra/llcommon/llerror.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 1f947a0a74..327c3ad537 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -722,7 +722,7 @@ namespace LLError void setFatalHook(const FatalHook& fatal_hook) { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - LL_DEBUGS("FatalHook") << "set fatal hook to "(fatal_hook ? "non-null" : "null") + LL_DEBUGS("FatalHook") << "set fatal hook to " << (fatal_hook ? "non-null" : "null") << " was " << (s->mFatalHook ? "non-null" : "null") << LL_ENDL; s->mFatalHook = fatal_hook; -- cgit v1.2.3 From bc8b55e1f2d772a657e4f7affc8a6c7d20f2c2e4 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Wed, 25 Sep 2019 12:56:05 -0400 Subject: remove fatal error hook usage from LLLeap (potential races for multiple processes) --- indra/llcommon/llleap.cpp | 33 --------------------------------- 1 file changed, 33 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index 1c0678e453..8293c35516 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -59,7 +59,6 @@ public: // pump name -- so it should NOT need tweaking for uniqueness. mReplyPump(LLUUID::generateNewID().asString()), mExpect(0), - mPrevFatalHook(LLError::getFatalHook()), // Instantiate a distinct LLLeapListener for this plugin. (Every // plugin will want its own collection of managed listeners, etc.) // Pass it a callback to our connect() method, so it can send events @@ -67,7 +66,6 @@ public: // this class or method name. mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2))) { - LL_DEBUGS("FatalHook") << "previous fatal hook was " << (mPrevFatalHook ? "non-null" : "null") << LL_ENDL; // Rule out unpopulated Params block if (! cparams.executable.isProvided()) { @@ -146,9 +144,6 @@ public: mStderrConnection = childerr.getPump() .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1)); - // For our lifespan, intercept any LL_ERRS so we can notify plugin - LLError::setFatalHook(boost::bind(&LLLeapImpl::fatalHook, this, _1)); - // Send child a preliminary event reporting our own reply-pump name -- // which would otherwise be pretty tricky to guess! wstdin(mReplyPump.getName(), @@ -163,8 +158,6 @@ public: virtual ~LLLeapImpl() { LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL; - // Restore original FatalHook - LLError::setFatalHook(mPrevFatalHook); } // Listener for failed launch attempt @@ -378,31 +371,6 @@ public: return false; } - LLError::ErrFatalHookResult fatalHook(const std::string& error) - { - // Notify plugin - LLSD event; - event["type"] = "error"; - event["error"] = error; - mReplyPump.post(event); - - // All the above really accomplished was to buffer the serialized - // event in our WritePipe. Have to pump mainloop a couple times to - // really write it out there... but time out in case we can't write. - LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN)); - LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); - LLSD nop; - F64 until = (LLTimer::getElapsedSeconds() + 2).value(); - while (childin.size() && LLTimer::getElapsedSeconds() < until) - { - mainloop.post(nop); - } - - // forward the call to the previous FatalHook, default to crashing if there isn't one - LL_DEBUGS("FatalHook") << "end" << LL_ENDL; - return mPrevFatalHook ? mPrevFatalHook(error) : LLError::ERR_CRASH; - } - private: /// We always want to listen on mReplyPump with wstdin(); under some /// circumstances we'll also echo other LLEventPumps to the plugin. @@ -423,7 +391,6 @@ private: mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection; boost::scoped_ptr mBlocker; LLProcess::ReadPipe::size_type mExpect; - LLError::FatalHook mPrevFatalHook; boost::scoped_ptr mListener; }; -- cgit v1.2.3 From a45ca18ff8160bc3edc584d6516ca019dd6c6275 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Wed, 25 Sep 2019 12:59:20 -0400 Subject: when using bugsplat, do not catch SIGABRT; also, fix signal setting in Mac (broken macro) --- indra/llcommon/llapp.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 34c969437b..27960371f1 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -779,7 +779,9 @@ void setup_signals() act.sa_flags = SA_SIGINFO; // Synchronous signals +# ifndef LL_BUGSPLAT sigaction(SIGABRT, &act, NULL); +# endif sigaction(SIGALRM, &act, NULL); sigaction(SIGBUS, &act, NULL); sigaction(SIGFPE, &act, NULL); @@ -816,7 +818,9 @@ void clear_signals() act.sa_flags = SA_SIGINFO; // Synchronous signals +# ifndef LL_BUGSPLAT sigaction(SIGABRT, &act, NULL); +# endif sigaction(SIGALRM, &act, NULL); sigaction(SIGBUS, &act, NULL); sigaction(SIGFPE, &act, NULL); @@ -869,6 +873,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *) return; case SIGABRT: + // Note that this handler is not set for SIGABRT when using Bugsplat // Abort just results in termination of the app, no funky error handling. if (LLApp::sLogInSignal) { -- cgit v1.2.3 From 68105d9f06aea69980cf0402f00da0e2be450dd2 Mon Sep 17 00:00:00 2001 From: Brad Kittenbrink Date: Thu, 2 Apr 2020 10:55:31 -0700 Subject: Fixed -Wstring-plus-int related errors for compatibility with Xcode-11.4 --- indra/llcommon/llsdutil.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index 9d00395c0a..859d2eb567 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -332,7 +332,7 @@ struct Data const char* name; } typedata[] = { -#define def(type) { LLSD::type, #type + 4 } +#define def(type) { LLSD::type, &#type[4] } def(TypeUndefined), def(TypeBoolean), def(TypeInteger), -- cgit v1.2.3 From 7897f909c4879d2f092f7310d59d78b5deebe634 Mon Sep 17 00:00:00 2001 From: Mnikolenko Productengine Date: Tue, 25 Aug 2020 15:18:40 +0300 Subject: SL-13843 FIXED URL gets changed when opened in the internal web browse --- indra/llcommon/lluri.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index 9942bc0cf8..22711a83d2 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -276,7 +276,7 @@ std::string LLURI::escapePathAndData(const std::string &str) std::string fragment; size_t fragment_pos = str.find('#'); - if (fragment_pos != std::string::npos) + if ((fragment_pos != std::string::npos) && (fragment_pos > delim_pos)) { query = str.substr(path_size, fragment_pos - path_size); fragment = str.substr(fragment_pos); -- cgit v1.2.3 From ed98ebb8115bffc5cf78ec00e33adf2243c55f0d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 12 Mar 2021 18:19:53 +0200 Subject: SL-14961 Coroutine crash was not reported to bugsplat --- indra/llcommon/llapp.h | 4 ++++ indra/llcommon/llcoros.cpp | 58 ++++++++++++++++++++++++++++++++-------------- indra/llcommon/llcoros.h | 7 +++--- 3 files changed, 49 insertions(+), 20 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index 245c73e3a2..5fa91b8bf5 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -259,6 +259,10 @@ public: */ LLRunner& getRunner() { return mRunner; } +#ifdef LL_WINDOWS + virtual void reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { } +#endif + public: typedef std::map string_map; string_map mOptionMap; // Contains all command-line options and arguments in a map diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 262929006d..111c50af93 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -255,8 +255,21 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific -U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) +U32 cpp_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop, const std::string& name) { + // C++ exceptions were logged in toplevelTryWrapper, but not SEH + // log SEH exceptions here, to make sure it gets into bugsplat's + // report and because __try won't allow std::string operations + if (code != STATUS_MSC_EXCEPTION) + { + LL_WARNS() << "SEH crash in " << name << ", code: " << code << LL_ENDL; + } + // Handle bugsplat here, since GetExceptionInformation() can only be + // called from within filter for __except(filter), not from __except's {} + // Bugsplat should get all exceptions, C++ and SEH + LLApp::instance()->reportCrashToBugsplat(exception_infop); + + // Only convert non C++ exceptions. if (code == STATUS_MSC_EXCEPTION) { // C++ exception, go on @@ -269,29 +282,29 @@ U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) } } -void LLCoros::winlevel(const callable_t& callable) +void LLCoros::winlevel(const std::string& name, const callable_t& callable) { __try { - callable(); + toplevelTryWrapper(name, callable); } - __except (exception_filter(GetExceptionCode(), GetExceptionInformation())) + __except (cpp_exception_filter(GetExceptionCode(), GetExceptionInformation(), name)) { - // convert to C++ styled exception + // convert to C++ styled exception for handlers other than bugsplat // Note: it might be better to use _se_set_translator // if you want exception to inherit full callstack - char integer_string[32]; - sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode()); + // + // in case of bugsplat this will get to exceptionTerminateHandler and + // looks like fiber will terminate application after that + char integer_string[512]; + sprintf(integer_string, "SEH crash in %s, code: %lu\n", name.c_str(), GetExceptionCode()); throw std::exception(integer_string); } } #endif -// 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! -void LLCoros::toplevel(std::string name, callable_t callable) +void LLCoros::toplevelTryWrapper(const std::string& name, const callable_t& callable) { // keep the CoroData on this top-level function's stack frame CoroData corodata(name); @@ -301,16 +314,12 @@ void LLCoros::toplevel(std::string name, callable_t callable) // run the code the caller actually wants in the coroutine try { -#if LL_WINDOWS && LL_RELEASE_FOR_DOWNLOAD - winlevel(callable); -#else callable(); -#endif } catch (const Stop& exc) { LL_INFOS("LLCoros") << "coroutine " << name << " terminating because " - << exc.what() << LL_ENDL; + << exc.what() << LL_ENDL; } catch (const LLContinueError&) { @@ -323,10 +332,25 @@ void LLCoros::toplevel(std::string name, callable_t callable) { // Any OTHER kind of uncaught exception will cause the viewer to // crash, hopefully informatively. - CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name)); + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << name)); + // to not modify callstack + throw; } } +// 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! +void LLCoros::toplevel(std::string name, callable_t callable) +{ +#if LL_WINDOWS + // Can not use __try in functions that require unwinding, so use one more wrapper + winlevel(name, callable); +#else + toplevelTryWrapper(name, callable); +#endif +} + //static void LLCoros::checkStop() { diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 38c2356c99..6c0bec3ef9 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -290,11 +290,12 @@ public: private: std::string generateDistinctName(const std::string& prefix) const; - void toplevel(std::string name, callable_t callable); - struct CoroData; #if LL_WINDOWS - static void winlevel(const callable_t& callable); + void winlevel(const std::string& name, const callable_t& callable); #endif + void toplevelTryWrapper(const std::string& name, const callable_t& callable); + void toplevel(std::string name, callable_t callable); + struct CoroData; static CoroData& get_CoroData(const std::string& caller); S32 mStackSize; -- cgit v1.2.3 From 95d8085fa42ca73773a06f2bd5622f69aef0e598 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 10 May 2021 15:21:51 -0400 Subject: SL-10297: Make LLSingletonBase::llerrs() et al. runtime variadic. Instead of accepting a fixed list of (const char* p1="", etc.), accept (std::initializer_list). Accepting a std::initializer_list in your parameter list allows coding (e.g.) func({T0, T1, T2, ... }); -- in other words, you can pass the initializer_list a brace-enclosed list of an arbitrary number of instances of T. Using std::string_view instead of const char* means we can pass *either* const char* or std::string. string_view is cheaply constructed from either, allowing uniform treatment within the function. Constructing string_view from std::string simply extracts the pointer and length from the std::string. Constructing string_view from const char* (e.g. a "string literal") requires scanning the string for its terminating nul byte -- but that would be necessary even if the scan were deferred until the function body. Since string_view stores the length, the scan still happens only once. --- indra/llcommon/llsingleton.cpp | 66 ++++++++++++++++++---------------- indra/llcommon/llsingleton.h | 81 ++++++++++++++++++++---------------------- 2 files changed, 75 insertions(+), 72 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index ad933154c2..d0dcd463ff 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -39,8 +39,7 @@ #include namespace { -void log(LLError::ELevel level, - const char* p1, const char* p2, const char* p3, const char* p4); +void log(LLError::ELevel level, std::initializer_list); } // anonymous namespace // Our master list of all LLSingletons is itself an LLSingleton. We used to @@ -218,8 +217,8 @@ void LLSingletonBase::pop_initializing() if (list.empty()) { - logerrs("Underflow in stack of currently-initializing LLSingletons at ", - classname(this).c_str(), "::getInstance()"); + logerrs({"Underflow in stack of currently-initializing LLSingletons at ", + classname(this), "::getInstance()"}); } // Now we know list.back() exists: capture it @@ -240,9 +239,9 @@ void LLSingletonBase::pop_initializing() // Now validate the newly-popped LLSingleton. if (back != this) { - logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ", - classname(this).c_str(), "::getInstance() trying to pop ", - classname(back).c_str()); + logerrs({"Push/pop mismatch in stack of currently-initializing LLSingletons: ", + classname(this), "::getInstance() trying to pop ", + classname(back)}); } // log AFTER popping so logging singletons don't cry circularity @@ -331,15 +330,15 @@ void LLSingletonBase::capture_dependency() // // Example: LLNotifications singleton initializes default channels. // Channels register themselves with singleton once done. - logdebugs("LLSingleton circularity: ", out.str().c_str(), - classname(this).c_str(), ""); + logdebugs({"LLSingleton circularity: ", out.str(), + classname(this)}); } else { // Actual circularity with other singleton (or single singleton is used extensively). // Dependency can be unclear. - logwarns("LLSingleton circularity: ", out.str().c_str(), - classname(this).c_str(), ""); + logwarns({"LLSingleton circularity: ", out.str(), + classname(this)}); } } else @@ -352,8 +351,8 @@ void LLSingletonBase::capture_dependency() if (current->mDepends.insert(this).second) { // only log the FIRST time we hit this dependency! - logdebugs(classname(current).c_str(), - " depends on ", classname(this).c_str()); + logdebugs({classname(current), + " depends on ", classname(this)}); } } } @@ -401,7 +400,7 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort() void LLSingletonBase::cleanup_() { - logdebugs("calling ", classname(this).c_str(), "::cleanupSingleton()"); + logdebugs({"calling ", classname(this), "::cleanupSingleton()"}); try { cleanupSingleton(); @@ -427,23 +426,23 @@ void LLSingletonBase::deleteAll() if (! sp->mDeleteSingleton) { // This Should Not Happen... but carry on. - logwarns(name.c_str(), "::mDeleteSingleton not initialized!"); + logwarns({name, "::mDeleteSingleton not initialized!"}); } else { // properly initialized: call it. - logdebugs("calling ", name.c_str(), "::deleteSingleton()"); + logdebugs({"calling ", name, "::deleteSingleton()"}); // From this point on, DO NOT DEREFERENCE sp! sp->mDeleteSingleton(); } } catch (const std::exception& e) { - logwarns("Exception in ", name.c_str(), "::deleteSingleton(): ", e.what()); + logwarns({"Exception in ", name, "::deleteSingleton(): ", e.what()}); } catch (...) { - logwarns("Unknown exception in ", name.c_str(), "::deleteSingleton()"); + logwarns({"Unknown exception in ", name, "::deleteSingleton()"}); } } } @@ -451,40 +450,47 @@ void LLSingletonBase::deleteAll() /*---------------------------- Logging helpers -----------------------------*/ namespace { -void log(LLError::ELevel level, - const char* p1, const char* p2, const char* p3, const char* p4) +void log(LLError::ELevel level, std::initializer_list args) { - LL_VLOGS(level, "LLSingleton") << p1 << p2 << p3 << p4 << LL_ENDL; + LL_VLOGS(level, "LLSingleton"); + for (auto arg : args) + { + LL_CONT << arg; + } + LL_ENDL; } } // anonymous namespace //static -void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logwarns(std::initializer_list args) { - log(LLError::LEVEL_WARN, p1, p2, p3, p4); + log(LLError::LEVEL_WARN, args); } //static -void LLSingletonBase::loginfos(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::loginfos(std::initializer_list args) { - log(LLError::LEVEL_INFO, p1, p2, p3, p4); + log(LLError::LEVEL_INFO, args); } //static -void LLSingletonBase::logdebugs(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logdebugs(std::initializer_list args) { - log(LLError::LEVEL_DEBUG, p1, p2, p3, p4); + log(LLError::LEVEL_DEBUG, args); } //static -void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logerrs(std::initializer_list args) { - log(LLError::LEVEL_ERROR, p1, p2, p3, p4); + log(LLError::LEVEL_ERROR, args); // The other important side effect of LL_ERRS() is // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG) std::ostringstream out; - out << p1 << p2 << p3 << p4; + for (auto arg : args) + { + out << arg; + } auto crash = LLError::getFatalFunction(); if (crash) { diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 30a5b21cf8..b9570d42db 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -34,6 +34,7 @@ #include "lockstatic.h" #include "llthread.h" // on_main_thread() #include "llmainthreadtask.h" +#include class LLSingletonBase: private boost::noncopyable { @@ -111,14 +112,10 @@ protected: void capture_dependency(); // delegate logging calls to llsingleton.cpp - static void logerrs(const char* p1, const char* p2="", - const char* p3="", const char* p4=""); - static void logwarns(const char* p1, const char* p2="", - const char* p3="", const char* p4=""); - static void loginfos(const char* p1, const char* p2="", - const char* p3="", const char* p4=""); - static void logdebugs(const char* p1, const char* p2="", - const char* p3="", const char* p4=""); + static void logerrs (std::initializer_list); + static void logwarns (std::initializer_list); + static void loginfos (std::initializer_list); + static void logdebugs(std::initializer_list); static std::string demangle(const char* mangled); // these classname() declarations restate template functions declared in // llerror.h because we avoid #including that here @@ -327,8 +324,8 @@ private: // init stack to its previous size BEFORE logging so log-machinery // LLSingletons don't record a dependency on DERIVED_TYPE! LLSingleton_manage_master().reset_initializing(prev_size); - logwarns("Error constructing ", classname().c_str(), - ": ", err.what()); + logwarns({"Error constructing ", classname(), + ": ", err.what()}); // There isn't a separate EInitState value meaning "we attempted // to construct this LLSingleton subclass but could not," so use // DELETED. That seems slightly more appropriate than UNINITIALIZED. @@ -356,8 +353,8 @@ private: // BEFORE logging, so log-machinery LLSingletons don't record a // dependency on DERIVED_TYPE! pop_initializing(lk->mInstance); - logwarns("Error in ", classname().c_str(), - "::initSingleton(): ", err.what()); + logwarns({"Error in ", classname(), + "::initSingleton(): ", err.what()}); // Get rid of the instance entirely. This call depends on our // recursive_mutex. We could have a deleteSingleton(LockStatic&) // overload and pass lk, but we don't strictly need it. @@ -506,9 +503,9 @@ public: case CONSTRUCTING: // here if DERIVED_TYPE's constructor (directly or indirectly) // calls DERIVED_TYPE::getInstance() - logerrs("Tried to access singleton ", - classname().c_str(), - " from singleton constructor!"); + logerrs({"Tried to access singleton ", + classname(), + " from singleton constructor!"}); return nullptr; case INITIALIZING: @@ -523,9 +520,9 @@ public: case DELETED: // called after deleteSingleton() - logwarns("Trying to access deleted singleton ", - classname().c_str(), - " -- creating new instance"); + logwarns({"Trying to access deleted singleton ", + classname(), + " -- creating new instance"}); // fall through case UNINITIALIZED: case QUEUED: @@ -552,8 +549,8 @@ public: } // unlock 'lk' // Per the comment block above, dispatch to the main thread. - loginfos(classname().c_str(), - "::getInstance() dispatching to main thread"); + loginfos({classname(), + "::getInstance() dispatching to main thread"}); auto instance = LLMainThreadTask::dispatch( [](){ // VERY IMPORTANT to call getInstance() on the main thread, @@ -563,16 +560,16 @@ public: // the main thread processes them, only the FIRST such request // actually constructs the instance -- every subsequent one // simply returns the existing instance. - loginfos(classname().c_str(), - "::getInstance() on main thread"); + loginfos({classname(), + "::getInstance() on main thread"}); return getInstance(); }); // record the dependency chain tracked on THIS thread, not the main // thread (consider a getInstance() overload with a tag param that // suppresses dep tracking when dispatched to the main thread) capture_dependency(instance); - loginfos(classname().c_str(), - "::getInstance() returning on requesting thread"); + loginfos({classname(), + "::getInstance() returning on requesting thread"}); return instance; } @@ -641,16 +638,16 @@ private: // For organizational purposes this function shouldn't be called twice if (lk->mInitState != super::UNINITIALIZED) { - super::logerrs("Tried to initialize singleton ", - super::template classname().c_str(), - " twice!"); + super::logerrs({"Tried to initialize singleton ", + super::template classname(), + " twice!"}); return nullptr; } else if (on_main_thread()) { // on the main thread, simply construct instance while holding lock - super::logdebugs(super::template classname().c_str(), - "::initParamSingleton()"); + super::logdebugs({super::template classname(), + "::initParamSingleton()"}); super::constructSingleton(lk, std::forward(args)...); return lk->mInstance; } @@ -662,8 +659,8 @@ private: lk->mInitState = super::QUEUED; // very important to unlock here so main thread can actually process lk.unlock(); - super::loginfos(super::template classname().c_str(), - "::initParamSingleton() dispatching to main thread"); + super::loginfos({super::template classname(), + "::initParamSingleton() dispatching to main thread"}); // Normally it would be the height of folly to reference-bind // 'args' into a lambda to be executed on some other thread! By // the time that thread executed the lambda, the references would @@ -674,12 +671,12 @@ private: // references. auto instance = LLMainThreadTask::dispatch( [&](){ - super::loginfos(super::template classname().c_str(), - "::initParamSingleton() on main thread"); + super::loginfos({super::template classname(), + "::initParamSingleton() on main thread"}); return initParamSingleton_(std::forward(args)...); }); - super::loginfos(super::template classname().c_str(), - "::initParamSingleton() returning on requesting thread"); + super::loginfos({super::template classname(), + "::initParamSingleton() returning on requesting thread"}); return instance; } } @@ -707,14 +704,14 @@ public: { case super::UNINITIALIZED: case super::QUEUED: - super::logerrs("Uninitialized param singleton ", - super::template classname().c_str()); + super::logerrs({"Uninitialized param singleton ", + super::template classname()}); break; case super::CONSTRUCTING: - super::logerrs("Tried to access param singleton ", - super::template classname().c_str(), - " from singleton constructor!"); + super::logerrs({"Tried to access param singleton ", + super::template classname(), + " from singleton constructor!"}); break; case super::INITIALIZING: @@ -726,8 +723,8 @@ public: return lk->mInstance; case super::DELETED: - super::logerrs("Trying to access deleted param singleton ", - super::template classname().c_str()); + super::logerrs({"Trying to access deleted param singleton ", + super::template classname()}); break; } -- cgit v1.2.3 From c9fc4349b7d4ab1f5a7bfc0125014a96a07e51a3 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 11 May 2021 21:42:14 -0400 Subject: SL-10297: Move LL_ERRS crash location into the LL_ERRS macro itself. Introduce Oz's LLERROR_CRASH macro analogous to the old LLError::crashAndLoop() function. Change LL_ENDL macro so that, after calling flush(), if the CallSite is for LEVEL_ERROR, we invoke LLERROR_CRASH right there. Change the meaning of LLError::FatalFunction. It used to be responsible for the actual crash (hence crashAndLoop()). Now, instead, its role is to disrupt control flow in some other way if you DON'T want to crash: throw an exception, or call exit() or some such. Any FatalFunction that returns normally will fall into the new crash in LL_ENDL. Accordingly, the new default FatalFunction is a no-op lambda. This eliminates the need to test for empty (not set) FatalFunction in Log::flush(). Remove LLError::crashAndLoop() because the official LL_ERRS crash is now in LL_ENDL. One of the two common use cases for setFatalFunction() used to be to intercept control in the last moments before crashing -- not to crash or to avoid crashing, but to capture the LL_ERRS message in some way. Especially when that's temporary, though (e.g. LLLeap), saving and restoring the previous FatalFunction only works when the lifespans of the relevant objects are strictly LIFO. Either way, that's a misuse of FatalFunction. Fortunately the Recorder mechanism exactly addresses that case. Introduce a GenericRecorder template subclass, with LLError::addGenericRecorder(callable) that accepts a callable with suitable (level, message) signature, instantiates a GenericRecorder, adds it to the logging machinery and returns the RecorderPtr for possible later use with removeRecorder(). Change llappviewer.cpp's errorCallback() to an addGenericRecorder() callable. Its role was simply to update gDebugInfo["FatalMessage"] with the LL_ERRS message, then call writeDebugInfo(), before calling crashAndLoop() to finish crashing. Remove the crashAndLoop() call, retaining the gDebugInfo logic. Pass errorCallback() to LLError::addGenericRecorder() instead of setFatalFunction(). Oddly, errorCallback()'s crashAndLoop() call was conditional on a compile-time SHADER_CRASH_NONFATAL symbol. The new mechanism provides no way to support SHADER_CRASH_NONFATAL -- it is a Bad Idea to return normally from any LL_ERRS invocation! Rename LLLeapImpl::fatalFunction() to onError(). Instead of passing it to LLError::setFatalFunction(), pass it to addGenericRecorder(). Capture the returned RecorderPtr in mRecorder, replacing mPrevFatalFunction. Then ~LLLeapImpl() calls removeRecorder(mRecorder) instead of restoring mPrevFatalFunction (which, as noted above, was order-sensitive). Of course, every enabled Recorder is called with every log message. onError() and errorCallback() must specifically test for calls with LEVEL_ERROR. LLSingletonBase::logerrs() used to call LLError::getFatalFunction(), check the return and call it if non-empty, else call LLError::crashAndLoop(). Replace all that with LLERROR_CRASH. Remove from llappviewer.cpp the watchdog_llerrs_callback() and watchdog_killer_callback() functions. watchdog_killer_callback(), passed to Watchdog::init(), used to setFatalFunction(watchdog_llerrs_callback) and then invoke LL_ERRS() -- which seems a bit roundabout. watchdog_llerrs_callback(), in turn, replicated much of the logic in the primary errorCallback() function before replicating the crash from llwatchdog.cpp's default_killer_callback(). Instead, pass LLWatchdog::init() a lambda that invokes the LL_ERRS() message formerly found in watchdog_killer_callback(). It no longer needs to override FatalFunction with watchdog_llerrs_callback() because errorCallback() will still be called as a Recorder, obviating watchdog_llerrs_callback()'s first half; and LL_ENDL will handle the crash, obviating the second half. Remove from llappviewer.cpp the static fast_exit() function, which was simply an alias for _exit() acceptable to boost::bind(). Use a lambda directly calling _exit() instead of using boost::bind() at all. In the CaptureLog class in llcommon/tests/wrapllerrs.h, instead of statically referencing the wouldHaveCrashed() function from test.cpp, simply save and restore the current FatalFunction across the LLError::saveAndResetSettings() call. llerror_test.cpp calls setFatalFunction(fatalCall), where fatalCall() was a function that simply set a fatalWasCalled bool rather than actually crashing in any way. Of course, that implementation would now lead to crashing the test program. Make fatalCall() throw a new FatalWasCalled exception. Introduce a CATCH(LL_ERRS("tag"), "message") macro that expands to: LL_ERRS("tag") << "message" << LL_ENDL; within a try/catch block that catches FatalWasCalled and sets the same bool. Change all existing LL_ERRS() in llerror_test.cpp to corresponding CATCH() calls. In fact there's also an LL_DEBUGS(bad tag) invocation that exercises an LL_ERRS internal to llerror.cpp; wrap that too. --- indra/llcommon/llerror.cpp | 37 +++------------------- indra/llcommon/llerror.h | 22 ++++++++++--- indra/llcommon/llerrorcontrol.h | 57 +++++++++++++++++++++++++++------- indra/llcommon/llleap.cpp | 48 ++++++++++++++--------------- indra/llcommon/llsingleton.cpp | 15 +-------- indra/llcommon/tests/llerror_test.cpp | 58 +++++++++++++++++++++++------------ indra/llcommon/tests/wrapllerrs.h | 22 ++++++------- 7 files changed, 142 insertions(+), 117 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index f876b8ee4a..9d775dcef3 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -549,7 +549,7 @@ namespace LLError mFileLevelMap(), mTagLevelMap(), mUniqueLogMessages(), - mCrashFunction(NULL), + mCrashFunction([](const std::string&){}), mTimeFunction(NULL), mRecorders(), mShouldLogCallCounter(0) @@ -728,7 +728,6 @@ namespace LLError::setDefaultLevel(LLError::LEVEL_INFO); LLError::setAlwaysFlush(true); LLError::setEnabledLogTypesMask(0xFFFFFFFF); - LLError::setFatalFunction(LLError::crashAndLoop); LLError::setTimeFunction(LLError::utcTime); // log_to_stderr is only false in the unit and integration tests to keep builds quieter @@ -1436,7 +1435,7 @@ namespace LLError if (site.mPrintOnce) { - std::ostringstream message_stream; + std::ostringstream message_stream; std::map::iterator messageIter = s->mUniqueLogMessages.find(message); if (messageIter != s->mUniqueLogMessages.end()) @@ -1457,8 +1456,8 @@ namespace LLError message_stream << "ONCE: "; s->mUniqueLogMessages[message] = 1; } - message_stream << message; - message = message_stream.str(); + message_stream << message; + message = message_stream.str(); } writeToRecorders(site, message); @@ -1466,10 +1465,7 @@ namespace LLError if (site.mLevel == LEVEL_ERROR) { g->mFatalMessage = message; - if (s->mCrashFunction) - { - s->mCrashFunction(message); - } + s->mCrashFunction(message); } } } @@ -1533,29 +1529,6 @@ namespace LLError return s->mShouldLogCallCounter; } -#if LL_WINDOWS - // VC80 was optimizing the error away. - #pragma optimize("", off) -#endif - void crashAndLoop(const std::string& message) - { - // Now, we go kaboom! - int* make_me_crash = NULL; - - *make_me_crash = 0; - - while(true) - { - // Loop forever, in case the crash didn't work? - } - - // this is an attempt to let Coverity and other semantic scanners know that this function won't be returning ever. - exit(EXIT_FAILURE); - } -#if LL_WINDOWS - #pragma optimize("", on) -#endif - std::string utcTime() { time_t now = time(NULL); diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index ffaa464d77..f8c0d03aea 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -382,11 +382,23 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_NEWLINE '\n' -#define LL_ENDL \ - LLError::End(); \ - LLError::Log::flush(_out, _site); \ - } \ - } while(0) +// Use this only in LL_ERRS or in a place that LL_ERRS may not be used +#define LLERROR_CRASH \ +{ \ + int* make_me_crash = NULL;\ + *make_me_crash = 0; \ + exit(*make_me_crash); \ +} + +#define LL_ENDL \ + LLError::End(); \ + LLError::Log::flush(_out, _site); \ + if (_site.mLevel == LLError::LEVEL_ERROR) \ + { \ + LLERROR_CRASH \ + } \ + } \ + } while(0) // NEW Macros for debugging, allow the passing of a string tag diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 25786d5457..e87bb7bf35 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -94,14 +94,16 @@ namespace LLError */ typedef boost::function FatalFunction; - LL_COMMON_API void crashAndLoop(const std::string& message); - // Default fatal function: access null pointer and loops forever LL_COMMON_API void setFatalFunction(const FatalFunction&); - // The fatal function will be called when an message of LEVEL_ERROR + // The fatal function will be called after an message of LEVEL_ERROR // is logged. Note: supressing a LEVEL_ERROR message from being logged // (by, for example, setting a class level to LEVEL_NONE), will keep - // the that message from causing the fatal funciton to be invoked. + // that message from causing the fatal function to be invoked. + // The passed FatalFunction will be the LAST log function called + // before LL_ERRS crashes its caller. A FatalFunction can throw an + // exception, or call exit(), to bypass the crash. It MUST disrupt the + // flow of control because no caller expects LL_ERRS to return. LL_COMMON_API FatalFunction getFatalFunction(); // Retrieve the previously-set FatalFunction @@ -147,14 +149,14 @@ namespace LLError virtual void recordMessage(LLError::ELevel, const std::string& message) = 0; // use the level for better display, not for filtering - virtual bool enabled() { return true; } + virtual bool enabled() { return true; } bool wantsTime(); bool wantsTags(); bool wantsLevel(); bool wantsLocation(); bool wantsFunctionName(); - bool wantsMultiline(); + bool wantsMultiline(); void showTime(bool show); void showTags(bool show); @@ -165,15 +167,35 @@ namespace LLError protected: bool mWantsTime; - bool mWantsTags; - bool mWantsLevel; - bool mWantsLocation; - bool mWantsFunctionName; - bool mWantsMultiline; + bool mWantsTags; + bool mWantsLevel; + bool mWantsLocation; + bool mWantsFunctionName; + bool mWantsMultiline; }; typedef boost::shared_ptr RecorderPtr; + /** + * Instantiate GenericRecorder with a callable(level, message) to get + * control on every log message without having to code an explicit + * Recorder subclass. + */ + template + class GenericRecorder: public Recorder + { + public: + GenericRecorder(const CALLABLE& callable): + mCallable(callable) + {} + void recordMessage(LLError::ELevel level, const std::string& message) override + { + mCallable(level, message); + } + private: + CALLABLE mCallable; + }; + /** * @NOTE: addRecorder() and removeRecorder() uses the boost::shared_ptr to allow for shared ownership * while still ensuring that the allocated memory is eventually freed @@ -181,6 +203,19 @@ namespace LLError LL_COMMON_API void addRecorder(RecorderPtr); LL_COMMON_API void removeRecorder(RecorderPtr); // each error message is passed to each recorder via recordMessage() + /** + * Call addGenericRecorder() with a callable(level, message) to get + * control on every log message without having to code an explicit + * Recorder subclass. Save the returned RecorderPtr if you later want to + * call removeRecorder(). + */ + template + RecorderPtr addGenericRecorder(const CALLABLE& callable) + { + RecorderPtr ptr{ new GenericRecorder(callable) }; + addRecorder(ptr); + return ptr; + } LL_COMMON_API void logToFile(const std::string& filename); LL_COMMON_API void logToStderr(); diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index cf8f8cc6a5..e8ea0ab398 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -59,7 +59,6 @@ public: // pump name -- so it should NOT need tweaking for uniqueness. mReplyPump(LLUUID::generateNewID().asString()), mExpect(0), - mPrevFatalFunction(LLError::getFatalFunction()), // Instantiate a distinct LLLeapListener for this plugin. (Every // plugin will want its own collection of managed listeners, etc.) // Pass it a callback to our connect() method, so it can send events @@ -146,7 +145,9 @@ public: .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1)); // For our lifespan, intercept any LL_ERRS so we can notify plugin - LLError::setFatalFunction(boost::bind(&LLLeapImpl::fatalFunction, this, _1)); + mRecorder = LLError::addGenericRecorder( + [this](LLError::ELevel level, const std::string& message) + { onError(level, message); }); // Send child a preliminary event reporting our own reply-pump name -- // which would otherwise be pretty tricky to guess! @@ -162,8 +163,7 @@ public: virtual ~LLLeapImpl() { LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL; - // Restore original FatalFunction - LLError::setFatalFunction(mPrevFatalFunction); + LLError::removeRecorder(mRecorder); } // Listener for failed launch attempt @@ -377,28 +377,28 @@ public: return false; } - void fatalFunction(const std::string& error) + void onError(LLError::ELevel level, const std::string& error) { - // Notify plugin - LLSD event; - event["type"] = "error"; - event["error"] = error; - mReplyPump.post(event); - - // All the above really accomplished was to buffer the serialized - // event in our WritePipe. Have to pump mainloop a couple times to - // really write it out there... but time out in case we can't write. - LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN)); - LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); - LLSD nop; - F64 until = (LLTimer::getElapsedSeconds() + 2).value(); - while (childin.size() && LLTimer::getElapsedSeconds() < until) + if (level == LLError::LEVEL_ERROR) { - mainloop.post(nop); + // Notify plugin + LLSD event; + event["type"] = "error"; + event["error"] = error; + mReplyPump.post(event); + + // All the above really accomplished was to buffer the serialized + // event in our WritePipe. Have to pump mainloop a couple times to + // really write it out there... but time out in case we can't write. + LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN)); + LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop")); + LLSD nop; + F64 until = (LLTimer::getElapsedSeconds() + 2).value(); + while (childin.size() && LLTimer::getElapsedSeconds() < until) + { + mainloop.post(nop); + } } - - // forward the call to the previous FatalFunction - mPrevFatalFunction(error); } private: @@ -421,7 +421,7 @@ private: mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection; boost::scoped_ptr mBlocker; LLProcess::ReadPipe::size_type mExpect; - LLError::FatalFunction mPrevFatalFunction; + LLError::RecorderPtr mRecorder; boost::scoped_ptr mListener; }; diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index d0dcd463ff..4b1666563e 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -486,20 +486,7 @@ void LLSingletonBase::logerrs(std::initializer_list args) log(LLError::LEVEL_ERROR, args); // The other important side effect of LL_ERRS() is // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG) - std::ostringstream out; - for (auto arg : args) - { - out << arg; - } - auto crash = LLError::getFatalFunction(); - if (crash) - { - crash(out.str()); - } - else - { - LLError::crashAndLoop(out.str()); - } + LLERROR_CRASH; } std::string LLSingletonBase::demangle(const char* mangled) diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index 8e1f4c14ac..148c18aabe 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -26,6 +26,7 @@ */ #include +#include #include "linden_common.h" @@ -69,21 +70,41 @@ namespace namespace { - static bool fatalWasCalled; - void fatalCall(const std::string&) { fatalWasCalled = true; } + static bool fatalWasCalled = false; + struct FatalWasCalled: public std::runtime_error + { + FatalWasCalled(const std::string& what): std::runtime_error(what) {} + }; + void fatalCall(const std::string& msg) { throw FatalWasCalled(msg); } } +// Because we use LLError::setFatalFunction(fatalCall), any LL_ERRS call we +// issue will throw FatalWasCalled. But we want the test program to continue. +// So instead of writing: +// LL_ERRS("tag") << "some message" << LL_ENDL; +// write: +// CATCH(LL_ERRS("tag"), "some message"); +#define CATCH(logcall, expr) \ + try \ + { \ + logcall << expr << LL_ENDL; \ + } \ + catch (const FatalWasCalled&) \ + { \ + fatalWasCalled = true; \ + } + namespace tut { class TestRecorder : public LLError::Recorder { public: TestRecorder() - { - showTime(false); - } + { + showTime(false); + } virtual ~TestRecorder() - {} + {} virtual void recordMessage(LLError::ELevel level, const std::string& message) @@ -252,7 +273,7 @@ namespace LL_DEBUGS("WriteTag","AnotherTag") << "one" << LL_ENDL; LL_INFOS("WriteTag") << "two" << LL_ENDL; LL_WARNS("WriteTag") << "three" << LL_ENDL; - LL_ERRS("WriteTag") << "four" << LL_ENDL; + CATCH(LL_ERRS("WriteTag"), "four"); } }; @@ -380,7 +401,7 @@ namespace std::string errorReturningLocation() { - LL_ERRS() << "die" << LL_ENDL; int this_line = __LINE__; + int this_line = __LINE__; CATCH(LL_ERRS(), "die"); return locationString(this_line); } } @@ -701,7 +722,7 @@ public: static void doDebug() { LL_DEBUGS() << "add dice" << LL_ENDL; } static void doInfo() { LL_INFOS() << "any idea" << LL_ENDL; } static void doWarn() { LL_WARNS() << "aim west" << LL_ENDL; } - static void doError() { LL_ERRS() << "ate eels" << LL_ENDL; } + static void doError() { CATCH(LL_ERRS(), "ate eels"); } static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } }; @@ -712,7 +733,7 @@ public: static void doDebug() { LL_DEBUGS() << "bed down" << LL_ENDL; } static void doInfo() { LL_INFOS() << "buy iron" << LL_ENDL; } static void doWarn() { LL_WARNS() << "bad word" << LL_ENDL; } - static void doError() { LL_ERRS() << "big easy" << LL_ENDL; } + static void doError() { CATCH(LL_ERRS(), "big easy"); } static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } }; @@ -874,13 +895,10 @@ namespace tut namespace { std::string writeTagWithSpaceReturningLocation() - { - LL_DEBUGS("Write Tag") << "not allowed" << LL_ENDL; int this_line = __LINE__; - - std::ostringstream location; - location << LLError::abbreviateFile(__FILE__).c_str() << "(" << this_line << ")"; - return location.str(); - } + { + int this_line = __LINE__; CATCH(LL_DEBUGS("Write Tag"), "not allowed"); + return locationString(this_line); + } }; namespace tut @@ -894,9 +912,9 @@ namespace tut std::string location = writeTagWithSpaceReturningLocation(); std::string expected = "Space is not allowed in a log tag at " + location; - ensure_message_field_equals(0, LEVEL_FIELD, "ERROR"); - ensure_message_field_equals(0, MSG_FIELD, expected); - ensure("fatal callback called", fatalWasCalled); + ensure_message_field_equals(0, LEVEL_FIELD, "ERROR"); + ensure_message_field_equals(0, MSG_FIELD, expected); + ensure("fatal callback called", fatalWasCalled); } } diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index b07d5afbd8..3779fb41bc 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -44,10 +44,6 @@ #include #include -// statically reference the function in test.cpp... it's short, we could -// replicate, but better to reuse -extern void wouldHaveCrashed(const std::string& message); - struct WrapLLErrs { WrapLLErrs(): @@ -59,7 +55,8 @@ struct WrapLLErrs mPriorFatal(LLError::getFatalFunction()) { // Make LL_ERRS call our own operator() method - LLError::setFatalFunction(boost::bind(&WrapLLErrs::operator(), this, _1)); + LLError::setFatalFunction( + [this](const std::string& message){ (*this)(message); }); } ~WrapLLErrs() @@ -199,11 +196,13 @@ public: // with that output. If it turns out that saveAndResetSettings() has // some bad effect, give up and just let the DEBUG level log messages // display. - : boost::noncopyable(), + : boost::noncopyable(), + mFatalFunction(LLError::getFatalFunction()), mOldSettings(LLError::saveAndResetSettings()), - mRecorder(new CaptureLogRecorder()) + mRecorder(new CaptureLogRecorder()) { - LLError::setFatalFunction(wouldHaveCrashed); + // reinstate the FatalFunction we just reset + LLError::setFatalFunction(mFatalFunction); LLError::setDefaultLevel(level); LLError::addRecorder(mRecorder); } @@ -219,17 +218,18 @@ public: /// for the sought string. std::string messageWith(const std::string& search, bool required=true) { - return boost::dynamic_pointer_cast(mRecorder)->messageWith(search, required); + return boost::dynamic_pointer_cast(mRecorder)->messageWith(search, required); } std::ostream& streamto(std::ostream& out) const { - return boost::dynamic_pointer_cast(mRecorder)->streamto(out); + return boost::dynamic_pointer_cast(mRecorder)->streamto(out); } private: + LLError::FatalFunction mFatalFunction; LLError::SettingsStoragePtr mOldSettings; - LLError::RecorderPtr mRecorder; + LLError::RecorderPtr mRecorder; }; #endif /* ! defined(LL_WRAPLLERRS_H) */ -- cgit v1.2.3 From 147c66d67ce83778fc3f102dd132ec095e6032fc Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 12 May 2021 09:46:49 -0400 Subject: SL-10297: Simplify implementation of LLSingletonBase::logwarns() etc. Introduce 'string_params' typedef for std::initialization_list, and make logwarns(), loginfos(), logdebugs() and logerrs() accept const string_params&. Eliminate the central log() function in llsingleton.cpp that used LL_VLOGS(). To cache the result of a (moderately expensive) Log::shouldLog() call, LL_VLOGS() wants its CallSite object to be static -- but of course the shouldLog() result will differ for different ELevel values, so LL_VLOGS() instantiates a static array of CallSite instances. It seems silly to funnel distinct logwarns(), etc., functions through a common log() function only to have LL_VLOGS() tease apart ELevel values again. Instead, make logwarns() directly invoke LL_WARNS(), and similarly for the rest. To reduce boilerplate in these distinct functions, teach std::ostream how to stream a string_params instance by looping over its elements. Then each logwarns(), etc., function can simply stream its string_params argument to LL_WARNS() or whichever. In particular, eliminate the LLERROR_CRASH macro in logerrs(). The fact that it invokes LL_ERRS() ensures that its LL_ENDL macro will crash the viewer. --- indra/llcommon/llsingleton.cpp | 37 +++++++++++++++---------------------- indra/llcommon/llsingleton.h | 11 +++++++---- 2 files changed, 22 insertions(+), 26 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index 4b1666563e..6b1986d0e9 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -38,10 +38,6 @@ #include #include -namespace { -void log(LLError::ELevel level, std::initializer_list); -} // anonymous namespace - // Our master list of all LLSingletons is itself an LLSingleton. We used to // store it in a function-local static, but that could get destroyed before // the last of the LLSingletons -- and ~LLSingletonBase() definitely wants to @@ -450,43 +446,40 @@ void LLSingletonBase::deleteAll() /*---------------------------- Logging helpers -----------------------------*/ namespace { -void log(LLError::ELevel level, std::initializer_list args) +std::ostream& operator<<(std::ostream& out, const LLSingletonBase::string_params& args) { - LL_VLOGS(level, "LLSingleton"); - for (auto arg : args) - { - LL_CONT << arg; - } - LL_ENDL; + // However many args there are in args, stream each of them to 'out'. + for (auto arg : args) + { + out << arg; + } + return out; } } // anonymous namespace //static -void LLSingletonBase::logwarns(std::initializer_list args) +void LLSingletonBase::logwarns(const string_params& args) { - log(LLError::LEVEL_WARN, args); + LL_WARNS("LLSingleton") << args << LL_ENDL; } //static -void LLSingletonBase::loginfos(std::initializer_list args) +void LLSingletonBase::loginfos(const string_params& args) { - log(LLError::LEVEL_INFO, args); + LL_INFOS("LLSingleton") << args << LL_ENDL; } //static -void LLSingletonBase::logdebugs(std::initializer_list args) +void LLSingletonBase::logdebugs(const string_params& args) { - log(LLError::LEVEL_DEBUG, args); + LL_DEBUGS("LLSingleton") << args << LL_ENDL; } //static -void LLSingletonBase::logerrs(std::initializer_list args) +void LLSingletonBase::logerrs(const string_params& args) { - log(LLError::LEVEL_ERROR, args); - // The other important side effect of LL_ERRS() is - // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG) - LLERROR_CRASH; + LL_ERRS("LLSingleton") << args << LL_ENDL; } std::string LLSingletonBase::demangle(const char* mangled) diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index b9570d42db..2eb39c6c8c 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -112,10 +112,13 @@ protected: void capture_dependency(); // delegate logging calls to llsingleton.cpp - static void logerrs (std::initializer_list); - static void logwarns (std::initializer_list); - static void loginfos (std::initializer_list); - static void logdebugs(std::initializer_list); +public: + typedef std::initializer_list string_params; +protected: + static void logerrs (const string_params&); + static void logwarns (const string_params&); + static void loginfos (const string_params&); + static void logdebugs(const string_params&); static std::string demangle(const char* mangled); // these classname() declarations restate template functions declared in // llerror.h because we avoid #including that here -- cgit v1.2.3 From 5b96ee0e10923a00ddb3836d4dc3c5f912ca4330 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 12 May 2021 12:02:57 -0400 Subject: SL-10297: Eliminate llerror.cpp's Globals::messageStream and bool. Instead of a single std::ostringstream instance shared by all callers, even those on different threads, make each of the relevant lllog_test_() and llcallstacks macros instantiate independent (stack) std::ostringstream objects. lllog_test_() is called by LL_DEBUGS(), LLINFOS(), LL_WARNS(), LL_ERRS(), LL_VLOGS() et al. Eliminate LLError::Log::out(), whose sole function was to arbitrate use of that shared std::ostringstream. Amusingly, if the lock couldn't be locked or if messageStreamInUse was set, out() would allocate a new (heap!) std::ostringstream anyway, which would then have to be freed by flush(). Make both LLError::Log::flush() overloads accept const std::ostringstream&. Make LL_ENDL pass the local _out instance. This eliminates the need to check whether the passed std::ostringstream* references the shared instance and (if so) reset it or (if not) delete it. Make LLError::LLCallStacks::insert() accept the local _out instance as non- const std::ostream&, rather than acquiring and returning std::ostringstream*. Make end() accept the local instance as const std::ostringstream&. --- indra/llcommon/llerror.cpp | 73 +++++++--------------------------------------- indra/llcommon/llerror.h | 30 +++++++++---------- 2 files changed, 26 insertions(+), 77 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 9d775dcef3..f7594ed815 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -442,8 +442,6 @@ namespace protected: Globals(); public: - std::ostringstream messageStream; - bool messageStreamInUse; std::string mFatalMessage; void addCallSite(LLError::CallSite&); @@ -453,12 +451,7 @@ namespace CallSiteVector callSites; }; - Globals::Globals() - : messageStream(), - messageStreamInUse(false), - callSites() - { - } + Globals::Globals() {} Globals* Globals::getInstance() { @@ -1359,25 +1352,7 @@ namespace LLError } - std::ostringstream* Log::out() - { - LLMutexTrylock lock(getMutex(),5); - - if (lock.isLocked()) - { - Globals* g = Globals::getInstance(); - - if (!g->messageStreamInUse) - { - g->messageStreamInUse = true; - return &g->messageStream; - } - } - - return new std::ostringstream; - } - - void Log::flush(std::ostringstream* out, char* message) + void Log::flush(const std::ostringstream& out, char* message) { LLMutexTrylock lock(getMutex(),5); if (!lock.isLocked()) @@ -1385,31 +1360,18 @@ namespace LLError return; } - if(strlen(out->str().c_str()) < 128) + if(strlen(out.str().c_str()) < 128) { - strcpy(message, out->str().c_str()); + strcpy(message, out.str().c_str()); } else { - strncpy(message, out->str().c_str(), 127); + strncpy(message, out.str().c_str(), 127); message[127] = '\0' ; } - - Globals* g = Globals::getInstance(); - if (out == &g->messageStream) - { - g->messageStream.clear(); - g->messageStream.str(""); - g->messageStreamInUse = false; - } - else - { - delete out; - } - return ; } - void Log::flush(std::ostringstream* out, const CallSite& site) + void Log::flush(const std::ostringstream& out, const CallSite& site) { LLMutexTrylock lock(getMutex(),5); if (!lock.isLocked()) @@ -1420,18 +1382,7 @@ namespace LLError Globals* g = Globals::getInstance(); SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - std::string message = out->str(); - if (out == &g->messageStream) - { - g->messageStream.clear(); - g->messageStream.str(""); - g->messageStreamInUse = false; - } - else - { - delete out; - } - + std::string message = out.str(); if (site.mPrintOnce) { @@ -1600,15 +1551,13 @@ namespace LLError } //static - std::ostringstream* LLCallStacks::insert(const char* function, const int line) + void LLCallStacks::insert(std::ostream& out, const char* function, const int line) { - std::ostringstream* _out = LLError::Log::out(); - *_out << function << " line " << line << " " ; - return _out ; + out << function << " line " << line << " " ; } //static - void LLCallStacks::end(std::ostringstream* _out) + void LLCallStacks::end(const std::ostringstream& out) { LLMutexTrylock lock(getMutex(), 5); if (!lock.isLocked()) @@ -1626,7 +1575,7 @@ namespace LLError clear() ; } - LLError::Log::flush(_out, sBuffer[sIndex++]) ; + LLError::Log::flush(out, sBuffer[sIndex++]) ; } //static diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index f8c0d03aea..51423350e6 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -198,9 +198,8 @@ namespace LLError { public: static bool shouldLog(CallSite&); - static std::ostringstream* out(); - static void flush(std::ostringstream* out, char* message); - static void flush(std::ostringstream*, const CallSite&); + static void flush(const std::ostringstream& out, char* message); + static void flush(const std::ostringstream&, const CallSite&); static std::string demangle(const char* mangled); /// classname() template @@ -289,10 +288,10 @@ namespace LLError public: static void push(const char* function, const int line) ; - static std::ostringstream* insert(const char* function, const int line) ; + static void insert(std::ostream& out, const char* function, const int line) ; static void print() ; static void clear() ; - static void end(std::ostringstream* _out) ; + static void end(const std::ostringstream& out) ; static void cleanup(); }; @@ -306,10 +305,11 @@ namespace LLError //this is cheaper than llcallstacks if no need to output other variables to call stacks. #define LL_PUSH_CALLSTACKS() LLError::LLCallStacks::push(__FUNCTION__, __LINE__) -#define llcallstacks \ - { \ - std::ostringstream* _out = LLError::LLCallStacks::insert(__FUNCTION__, __LINE__) ; \ - (*_out) +#define llcallstacks \ + { \ + std::ostringstream _out; \ + LLError::LLCallStacks::insert(_out, __FUNCTION__, __LINE__) ; \ + _out #define llcallstacksendl \ LLError::End(); \ @@ -355,11 +355,11 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \ lllog_test_() -#define lllog_test_() \ - if (LL_UNLIKELY(_site.shouldLog())) \ - { \ - std::ostringstream* _out = LLError::Log::out(); \ - (*_out) +#define lllog_test_() \ + if (LL_UNLIKELY(_site.shouldLog())) \ + { \ + std::ostringstream _out; \ + _out #define lllog_site_args_(level, once, tags) \ level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \ @@ -378,7 +378,7 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; // LL_CONT << " for " << t << " seconds" << LL_ENDL; // //Such computation is done iff the message will be logged. -#define LL_CONT (*_out) +#define LL_CONT _out #define LL_NEWLINE '\n' -- cgit v1.2.3 From 91c20363eee4e1e02435e0ee74867cdb3f6c7136 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 12 May 2021 13:37:24 -0400 Subject: SL-10297: Get rid of LLError::LLCallStacks::allocateStackBuffer(). Also freeStackBuffer() and all the funky classic-C string management of a big flat buffer divided into exactly 512 128-byte strings. Define StringVector as a std::vector, and use that instead. Retain the behavior of clearing the vector if it exceeds 512 entries. This eliminates the LLError::Log::flush(const std::ostringstream&, char*) overload as well, with its baffling mix of std::string and classic-C (e.g. strlen(out.str().c_str()). If we absolutely MUST use a big memory pool for performance reasons, let's use StringVector with allocators. --- indra/llcommon/llerror.cpp | 88 ++++++++-------------------------------------- indra/llcommon/llerror.h | 10 +++--- 2 files changed, 18 insertions(+), 80 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index f7594ed815..8355df9045 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -1352,25 +1352,6 @@ namespace LLError } - void Log::flush(const std::ostringstream& out, char* message) - { - LLMutexTrylock lock(getMutex(),5); - if (!lock.isLocked()) - { - return; - } - - if(strlen(out.str().c_str()) < 128) - { - strcpy(message, out.str().c_str()); - } - else - { - strncpy(message, out.str().c_str(), 127); - message[127] = '\0' ; - } - } - void Log::flush(const std::ostringstream& out, const CallSite& site) { LLMutexTrylock lock(getMutex(),5); @@ -1496,33 +1477,7 @@ namespace LLError namespace LLError { - char** LLCallStacks::sBuffer = NULL ; - S32 LLCallStacks::sIndex = 0 ; - - //static - void LLCallStacks::allocateStackBuffer() - { - if(sBuffer == NULL) - { - sBuffer = new char*[512] ; - sBuffer[0] = new char[512 * 128] ; - for(S32 i = 1 ; i < 512 ; i++) - { - sBuffer[i] = sBuffer[i-1] + 128 ; - } - sIndex = 0 ; - } - } - - void LLCallStacks::freeStackBuffer() - { - if(sBuffer != NULL) - { - delete [] sBuffer[0] ; - delete [] sBuffer ; - sBuffer = NULL ; - } - } + LLCallStacks::StringVector LLCallStacks::sBuffer ; //static void LLCallStacks::push(const char* function, const int line) @@ -1533,21 +1488,14 @@ namespace LLError return; } - if(sBuffer == NULL) - { - allocateStackBuffer(); - } - - if(sIndex > 511) + if(sBuffer.size() > 511) { clear() ; } - strcpy(sBuffer[sIndex], function) ; - sprintf(sBuffer[sIndex] + strlen(function), " line: %d ", line) ; - sIndex++ ; - - return ; + std::ostringstream out; + insert(out, function, line); + sBuffer.push_back(out.str()); } //static @@ -1565,17 +1513,12 @@ namespace LLError return; } - if(sBuffer == NULL) - { - allocateStackBuffer(); - } - - if(sIndex > 511) + if(sBuffer.size() > 511) { clear() ; } - LLError::Log::flush(out, sBuffer[sIndex++]) ; + sBuffer.push_back(out.str()); } //static @@ -1587,33 +1530,30 @@ namespace LLError return; } - if(sIndex > 0) + if(! sBuffer.empty()) { LL_INFOS() << " ************* PRINT OUT LL CALL STACKS ************* " << LL_ENDL; - while(sIndex > 0) + for (StringVector::const_reverse_iterator ri(sBuffer.rbegin()), re(sBuffer.rend()); + ri != re; ++ri) { - sIndex-- ; - LL_INFOS() << sBuffer[sIndex] << LL_ENDL; + LL_INFOS() << (*ri) << LL_ENDL; } LL_INFOS() << " *************** END OF LL CALL STACKS *************** " << LL_ENDL; } - if(sBuffer != NULL) - { - freeStackBuffer(); - } + cleanup(); } //static void LLCallStacks::clear() { - sIndex = 0 ; + sBuffer.clear(); } //static void LLCallStacks::cleanup() { - freeStackBuffer(); + clear(); } std::ostream& operator<<(std::ostream& out, const LLStacktrace&) diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 51423350e6..d439136ca8 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -29,7 +29,9 @@ #define LL_LLERROR_H #include +#include #include +#include #include "stdtypes.h" @@ -198,7 +200,6 @@ namespace LLError { public: static bool shouldLog(CallSite&); - static void flush(const std::ostringstream& out, char* message); static void flush(const std::ostringstream&, const CallSite&); static std::string demangle(const char* mangled); /// classname() @@ -280,11 +281,8 @@ namespace LLError class LL_COMMON_API LLCallStacks { private: - static char** sBuffer ; - static S32 sIndex ; - - static void allocateStackBuffer(); - static void freeStackBuffer(); + typedef std::vector StringVector; + static StringVector sBuffer ; public: static void push(const char* function, const int line) ; -- cgit v1.2.3 From d10badf0d23f48665239117838c5daf0fd667e01 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 12 May 2021 16:26:53 -0400 Subject: SL-10297: #include in llsingleton.h --- indra/llcommon/llsingleton.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 2eb39c6c8c..163c08099f 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -27,14 +27,15 @@ #include #include +#include #include -#include +#include #include +#include #include "mutex.h" #include "lockstatic.h" #include "llthread.h" // on_main_thread() #include "llmainthreadtask.h" -#include class LLSingletonBase: private boost::noncopyable { -- cgit v1.2.3 From 24501dfa0ee3fd6f5755deb1bc5261cd297a2bc7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 12 May 2021 22:35:09 -0400 Subject: SL-10297: Use initializer_list vs. . This is somewhat more expensive for string literals, but switching to std::string_view implies more extensive changes, to be considered separately. --- indra/llcommon/llsingleton.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 163c08099f..7c81d65a8b 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include "mutex.h" @@ -114,7 +113,7 @@ protected: // delegate logging calls to llsingleton.cpp public: - typedef std::initializer_list string_params; + typedef std::initializer_list string_params; protected: static void logerrs (const string_params&); static void logwarns (const string_params&); -- cgit v1.2.3 From 823f97ac59e4eb11746cc38d330c223313a5a8fa Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Fri, 21 May 2021 20:06:41 +0300 Subject: SL-15272 Bugsplat crashes at condition wait() Made sure all waits will be triggered, won't loop back and that in case of http queue it had some time to trigger --- indra/llcommon/llthread.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 0b9dec969c..e9e6b5e73c 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -354,8 +354,9 @@ void LLThread::setQuitting() { mStatus = QUITTING; } + // It's only safe to remove mRunCondition if all locked threads were notified + mRunCondition->broadcast(); mDataLock->unlock(); - wake(); } // static -- cgit v1.2.3