diff options
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 9 | ||||
-rw-r--r-- | indra/llcommon/llapp.cpp | 10 | ||||
-rw-r--r-- | indra/llcommon/llerror.cpp | 75 | ||||
-rw-r--r-- | indra/llcommon/llerror.h | 31 | ||||
-rw-r--r-- | indra/llcommon/llerrorcontrol.h | 47 | ||||
-rw-r--r-- | indra/llcommon/llleap.cpp | 31 | ||||
-rw-r--r-- | indra/llcommon/llsingleton.cpp | 10 | ||||
-rw-r--r-- | indra/llcommon/tests/llerror_test.cpp | 25 | ||||
-rw-r--r-- | indra/llcommon/tests/wrapllerrs.h | 28 |
9 files changed, 122 insertions, 144 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index eeb315ead6..79aa18eaec 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -4,6 +4,7 @@ project(llcommon) include(00-Common) include(LLCommon) +include(bugsplat) include(Linking) include(Boost) include(LLSharedLibs) @@ -258,10 +259,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/llapp.cpp b/indra/llcommon/llapp.cpp index 3dab632aef..252d08c830 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -528,7 +528,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() @@ -808,7 +813,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); @@ -845,7 +852,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); @@ -898,6 +907,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) { diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 411412c883..922c003443 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" @@ -499,7 +500,7 @@ namespace LLError LevelMap mTagLevelMap; std::map<std::string, unsigned int> mUniqueLogMessages; - LLError::FatalFunction mCrashFunction; + LLError::FatalHook mFatalHook; LLError::TimeFunction mTimeFunction; Recorders mRecorders; @@ -536,7 +537,7 @@ namespace LLError mFileLevelMap(), mTagLevelMap(), mUniqueLogMessages(), - mCrashFunction(NULL), + mFatalHook(NULL), mTimeFunction(NULL), mRecorders(), mShouldLogCallCounter(0) @@ -708,9 +709,9 @@ namespace LLError::Settings::getInstance()->reset(); LLError::setDefaultLevel(LLError::LEVEL_INFO); - LLError::setAlwaysFlush(true); - LLError::setEnabledLogTypesMask(0xFFFFFFFF); - LLError::setFatalFunction(LLError::crashAndLoop); + + LLError::setAlwaysFlush(true); + LLError::setEnabledLogTypesMask(0xFFFFFFFF); LLError::setTimeFunction(LLError::utcTime); // log_to_stderr is only false in the unit and integration tests to keep builds quieter @@ -745,16 +746,20 @@ namespace LLError commonInit(user_dir, app_dir, log_to_stderr); } - void setFatalFunction(const FatalFunction& f) + void setFatalHook(const FatalHook& fatal_hook) { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - s->mCrashFunction = f; - } - - FatalFunction getFatalFunction() + LL_DEBUGS("FatalHook") << "set fatal hook to " << (fatal_hook ? "non-null" : "null") + << " was " << (s->mFatalHook ? "non-null" : "null") + << LL_ENDL; + s->mFatalHook = fatal_hook; + } + + FatalHook getFatalHook() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - return s->mCrashFunction; + LL_DEBUGS("FatalHook") << "read fatal hook was " << (s->mFatalHook ? "non-null" : "null") << LL_ENDL; + return s->mFatalHook; } std::string getFatalMessage() @@ -1410,12 +1415,12 @@ namespace LLError return ; } - void Log::flush(std::ostringstream* out, const CallSite& site) + ErrFatalHookResult Log::flush(std::ostringstream* out, const CallSite& site) { LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5); if (!lock.isLocked()) { - return; + 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, @@ -1423,7 +1428,7 @@ namespace LLError // DO NOT resurrect them. if (Settings::wasDeleted() || Globals::wasDeleted()) { - return; + return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal } Globals* g = Globals::getInstance(); @@ -1457,7 +1462,7 @@ namespace LLError } else { - return; + return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal } } else @@ -1473,12 +1478,19 @@ namespace LLError if (site.mLevel == LEVEL_ERROR) { - g->mFatalMessage = message; - if (s->mCrashFunction) - { - s->mCrashFunction(message); - } + if (s->mFatalHook) + { + return s->mFatalHook(message); + } + else + { + return ERR_CRASH; // calling macro should crash + } } + else + { + return ERR_DO_NOT_CRASH; // not ERROR, so do not crash + } } } @@ -1541,29 +1553,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..18d11cfa9d 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -193,14 +193,20 @@ namespace LLError */ struct CallSite; + + enum ErrFatalHookResult { ERR_DO_NOT_CRASH, ERR_CRASH }; class LL_COMMON_API Log { 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 the calling macro should crash + static ErrFatalHookResult flush(std::ostringstream*, const CallSite&); + static std::string demangle(const char* mangled); /// classname<TYPE>() template <typename T> @@ -271,6 +277,7 @@ namespace LLError class LL_COMMON_API NoClassInfo { }; // used to indicate no class info known for logging + //LLCallStacks keeps track of call stacks and output the call stacks to log file //when LLAppViewer::handleViewerCrash() is triggered. // @@ -382,10 +389,24 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define LL_NEWLINE '\n' -#define LL_ENDL \ - LLError::End(); \ - LLError::Log::flush(_out, _site); \ - } \ +#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 \ +{ \ + *gCauseCrash = 0; \ + exit(*gCauseCrash); \ +} + +#define LL_ENDL \ + LLError::End(); \ + if (LLError::ERR_CRASH == LLError::Log::flush(_out, _site)) \ + 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 bfa2269025..d001a3bd03 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -93,40 +93,27 @@ namespace LLError Control functions. */ - typedef boost::function<void(const std::string&)> 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 + // 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<LLError::ErrFatalHookResult(const std::string&)> 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. + // (by, for example, setting a class level to LEVEL_NONE), will also + // prevent the fatal_hook being called and the resulting deliberate crash - LL_COMMON_API FatalFunction getFatalFunction(); - // Retrieve the previously-set FatalFunction + /// Retrieve the previously-set FatalHook + LL_COMMON_API FatalHook getFatalHook(); 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..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), - 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 @@ -145,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::setFatalFunction(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(), @@ -162,8 +158,6 @@ public: virtual ~LLLeapImpl() { LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL; - // Restore original FatalFunction - LLError::setFatalFunction(mPrevFatalFunction); } // Listener for failed launch attempt @@ -377,30 +371,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); - } - - // forward the call to the previous FatalFunction - mPrevFatalFunction(error); - } - private: /// We always want to listen on mReplyPump with wstdin(); under some /// circumstances we'll also echo other LLEventPumps to the plugin. @@ -421,7 +391,6 @@ private: mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection; boost::scoped_ptr<LLEventPump::Blocker> mBlocker; LLProcess::ReadPipe::size_type mExpect; - LLError::FatalFunction mPrevFatalFunction; boost::scoped_ptr<LLLeapListener> mListener; }; diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index d3d25201b2..f410bd6a92 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -512,15 +512,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..cdc2bf8c87 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::ErrFatalHookResult fatalHook(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::setFatalFunction(fatalCall); + LLError::setFatalHook(fatalHook); LLError::addRecorder(mRecorder); } @@ -777,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<> diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index b07d5afbd8..a6c44d5fdd 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -46,25 +46,24 @@ // 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::ErrFatalHookResult 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()), - // Save shutdown function called by LL_ERRS - mPriorFatal(LLError::getFatalFunction()) + :mPriorErrorSettings(LLError::saveAndResetSettings()) + ,mPriorFatalHook(LLError::getFatalHook()) { // Make LL_ERRS call our own operator() method - LLError::setFatalFunction(boost::bind(&WrapLLErrs::operator(), this, _1)); + LLError::setFatalHook(boost::bind(&WrapLLErrs::operator(), this, _1)); } ~WrapLLErrs() { - LLError::setFatalFunction(mPriorFatal); + LLError::setFatalHook(mPriorFatalHook); LLError::restoreSettings(mPriorErrorSettings); } @@ -73,7 +72,7 @@ struct WrapLLErrs FatalException(const std::string& what): LLException(what) {} }; - void 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; @@ -109,7 +108,7 @@ struct WrapLLErrs std::string error; LLError::SettingsStoragePtr mPriorErrorSettings; - LLError::FatalFunction mPriorFatal; + LLError::FatalHook mPriorFatalHook; }; /** @@ -199,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::setFatalFunction(wouldHaveCrashed); + LLError::setFatalHook(wouldHaveCrashed); LLError::setDefaultLevel(level); LLError::addRecorder(mRecorder); } @@ -212,6 +212,7 @@ public: { LLError::removeRecorder(mRecorder); LLError::restoreSettings(mOldSettings); + LLError::setFatalHook(mPriorFatalHook); } /// Don't assume the message we want is necessarily the LAST log message @@ -229,6 +230,7 @@ public: private: LLError::SettingsStoragePtr mOldSettings; + LLError::FatalHook mPriorFatalHook; LLError::RecorderPtr mRecorder; }; |