diff options
| author | Andrey Lihatskiy <alihatskiy@productengine.com> | 2021-05-21 20:31:53 +0300 | 
|---|---|---|
| committer | Andrey Lihatskiy <alihatskiy@productengine.com> | 2021-05-21 20:31:53 +0300 | 
| commit | 2922c593160f81d19f39d80fc84a25c2a0e0d8aa (patch) | |
| tree | 7b5c5aa2889cb2e7d746d54a6ec9778cc7c77480 /indra/llcommon | |
| parent | 646cde231d72fb217c6d34cd95d941a24544ca3a (diff) | |
| parent | 24501dfa0ee3fd6f5755deb1bc5261cd297a2bc7 (diff) | |
Merge branch 'sl-10297' into DRTVWR-516-maint
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 | 188 | ||||
| -rw-r--r-- | indra/llcommon/llerror.h | 60 | ||||
| -rw-r--r-- | indra/llcommon/llerrorcontrol.h | 57 | ||||
| -rw-r--r-- | indra/llcommon/llleap.cpp | 48 | ||||
| -rw-r--r-- | indra/llcommon/llsingleton.cpp | 76 | ||||
| -rw-r--r-- | indra/llcommon/llsingleton.h | 86 | ||||
| -rw-r--r-- | indra/llcommon/tests/llerror_test.cpp | 58 | ||||
| -rw-r--r-- | indra/llcommon/tests/wrapllerrs.h | 22 | 
10 files changed, 268 insertions, 346 deletions
| diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index cecfadcd91..dd266630ea 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) @@ -260,10 +261,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 a90b294550..6064a843ae 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 f876b8ee4a..8355df9045 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()      { @@ -549,7 +542,7 @@ namespace LLError  		mFileLevelMap(),  		mTagLevelMap(),  		mUniqueLogMessages(), -		mCrashFunction(NULL), +		mCrashFunction([](const std::string&){}),  		mTimeFunction(NULL),  		mRecorders(),  		mShouldLogCallCounter(0) @@ -728,7 +721,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 @@ -1360,57 +1352,7 @@ namespace LLError  	} -	std::ostringstream* Log::out() -	{ -		LLMutexTrylock lock(getMutex<LOG_MUTEX>(),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) -	{ -		LLMutexTrylock lock(getMutex<LOG_MUTEX>(),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' ; -		} - -		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<LOG_MUTEX>(),5);  		if (!lock.isLocked()) @@ -1421,22 +1363,11 @@ 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)  		{ -            std::ostringstream message_stream; +			std::ostringstream message_stream;  			std::map<std::string, unsigned int>::iterator messageIter = s->mUniqueLogMessages.find(message);  			if (messageIter != s->mUniqueLogMessages.end()) @@ -1457,8 +1388,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 +1397,7 @@ namespace LLError  		if (site.mLevel == LEVEL_ERROR)  		{  			g->mFatalMessage = message; -			if (s->mCrashFunction) -			{ -				s->mCrashFunction(message); -			} +			s->mCrashFunction(message);  		}  	}  } @@ -1533,29 +1461,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); @@ -1572,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) @@ -1609,33 +1488,24 @@ 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 -    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<STACKS_MUTEX>(), 5);          if (!lock.isLocked()) @@ -1643,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 @@ -1665,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 ffaa464d77..d439136ca8 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -29,7 +29,9 @@  #define LL_LLERROR_H  #include <sstream> +#include <string>  #include <typeinfo> +#include <vector>  #include "stdtypes.h" @@ -198,9 +200,7 @@ 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&, const CallSite&);  		static std::string demangle(const char* mangled);  		/// classname<TYPE>()  		template <typename T> @@ -281,18 +281,15 @@ namespace LLError      class LL_COMMON_API LLCallStacks      {      private: -        static char**  sBuffer ; -        static S32     sIndex ; - -        static void allocateStackBuffer(); -        static void freeStackBuffer(); +        typedef std::vector<std::string> StringVector; +        static StringVector sBuffer ;      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 +303,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 +353,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,15 +376,27 @@ 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' -#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<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 +		// 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<Recorder> RecorderPtr; +    /** +     * Instantiate GenericRecorder with a callable(level, message) to get +     * control on every log message without having to code an explicit +     * Recorder subclass. +     */ +    template <typename CALLABLE> +    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 <typename CALLABLE> +	RecorderPtr addGenericRecorder(const CALLABLE& callable) +	{ +		RecorderPtr ptr{ new GenericRecorder<CALLABLE>(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<LLEventPump::Blocker> mBlocker;      LLProcess::ReadPipe::size_type mExpect; -    LLError::FatalFunction mPrevFatalFunction; +    LLError::RecorderPtr mRecorder;      boost::scoped_ptr<LLLeapListener> mListener;  }; diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index ad933154c2..6b1986d0e9 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -38,11 +38,6 @@  #include <sstream>  #include <stdexcept> -namespace { -void log(LLError::ELevel level, -         const char* p1, const char* p2, const char* p3, const char* p4); -} // 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 @@ -218,8 +213,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 +235,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 +326,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 +347,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 +396,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 +422,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,49 +446,40 @@ void LLSingletonBase::deleteAll()  /*---------------------------- Logging helpers -----------------------------*/  namespace { -void log(LLError::ELevel level, -         const char* p1, const char* p2, const char* p3, const char* p4) +std::ostream& operator<<(std::ostream& out, const LLSingletonBase::string_params& args)  { -    LL_VLOGS(level, "LLSingleton") << p1 << p2 << p3 << p4 << 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(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logwarns(const string_params& args)  { -    log(LLError::LEVEL_WARN, p1, p2, p3, p4); +    LL_WARNS("LLSingleton") << args << LL_ENDL;  }  //static -void LLSingletonBase::loginfos(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::loginfos(const string_params& args)  { -    log(LLError::LEVEL_INFO, p1, p2, p3, p4); +    LL_INFOS("LLSingleton") << args << LL_ENDL;  }  //static -void LLSingletonBase::logdebugs(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logdebugs(const string_params& args)  { -    log(LLError::LEVEL_DEBUG, p1, p2, p3, p4); +    LL_DEBUGS("LLSingleton") << args << LL_ENDL;  }  //static -void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4) +void LLSingletonBase::logerrs(const string_params& args)  { -    log(LLError::LEVEL_ERROR, p1, p2, p3, p4); -    // 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; -    auto crash = LLError::getFatalFunction(); -    if (crash) -    { -        crash(out.str()); -    } -    else -    { -        LLError::crashAndLoop(out.str()); -    } +    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 30a5b21cf8..7c81d65a8b 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -27,9 +27,10 @@  #include <boost/noncopyable.hpp>  #include <boost/unordered_set.hpp> +#include <initializer_list>  #include <list> -#include <vector>  #include <typeinfo> +#include <vector>  #include "mutex.h"  #include "lockstatic.h"  #include "llthread.h"               // on_main_thread() @@ -111,14 +112,13 @@ 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=""); +public: +    typedef std::initializer_list<const std::string> 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 @@ -327,8 +327,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<DERIVED_TYPE>().reset_initializing(prev_size); -            logwarns("Error constructing ", classname<DERIVED_TYPE>().c_str(), -                     ": ", err.what()); +            logwarns({"Error constructing ", classname<DERIVED_TYPE>(), +                     ": ", 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 +356,8 @@ private:              // BEFORE logging, so log-machinery LLSingletons don't record a              // dependency on DERIVED_TYPE!              pop_initializing(lk->mInstance); -            logwarns("Error in ", classname<DERIVED_TYPE>().c_str(), -                     "::initSingleton(): ", err.what()); +            logwarns({"Error in ", classname<DERIVED_TYPE>(), +                     "::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 +506,9 @@ public:              case CONSTRUCTING:                  // here if DERIVED_TYPE's constructor (directly or indirectly)                  // calls DERIVED_TYPE::getInstance() -                logerrs("Tried to access singleton ", -                        classname<DERIVED_TYPE>().c_str(), -                        " from singleton constructor!"); +                logerrs({"Tried to access singleton ", +                        classname<DERIVED_TYPE>(), +                        " from singleton constructor!"});                  return nullptr;              case INITIALIZING: @@ -523,9 +523,9 @@ public:              case DELETED:                  // called after deleteSingleton() -                logwarns("Trying to access deleted singleton ", -                         classname<DERIVED_TYPE>().c_str(), -                         " -- creating new instance"); +                logwarns({"Trying to access deleted singleton ", +                         classname<DERIVED_TYPE>(), +                         " -- creating new instance"});                  // fall through              case UNINITIALIZED:              case QUEUED: @@ -552,8 +552,8 @@ public:          } // unlock 'lk'          // Per the comment block above, dispatch to the main thread. -        loginfos(classname<DERIVED_TYPE>().c_str(), -                 "::getInstance() dispatching to main thread"); +        loginfos({classname<DERIVED_TYPE>(), +                 "::getInstance() dispatching to main thread"});          auto instance = LLMainThreadTask::dispatch(              [](){                  // VERY IMPORTANT to call getInstance() on the main thread, @@ -563,16 +563,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<DERIVED_TYPE>().c_str(), -                         "::getInstance() on main thread"); +                loginfos({classname<DERIVED_TYPE>(), +                         "::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<DERIVED_TYPE>().c_str(), -                 "::getInstance() returning on requesting thread"); +        loginfos({classname<DERIVED_TYPE>(), +                 "::getInstance() returning on requesting thread"});          return instance;      } @@ -641,16 +641,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<DERIVED_TYPE>().c_str(), -                           " twice!"); +            super::logerrs({"Tried to initialize singleton ", +                           super::template classname<DERIVED_TYPE>(), +                           " twice!"});              return nullptr;          }          else if (on_main_thread())          {              // on the main thread, simply construct instance while holding lock -            super::logdebugs(super::template classname<DERIVED_TYPE>().c_str(), -                             "::initParamSingleton()"); +            super::logdebugs({super::template classname<DERIVED_TYPE>(), +                             "::initParamSingleton()"});              super::constructSingleton(lk, std::forward<Args>(args)...);              return lk->mInstance;          } @@ -662,8 +662,8 @@ private:              lk->mInitState = super::QUEUED;              // very important to unlock here so main thread can actually process              lk.unlock(); -            super::loginfos(super::template classname<DERIVED_TYPE>().c_str(), -                            "::initParamSingleton() dispatching to main thread"); +            super::loginfos({super::template classname<DERIVED_TYPE>(), +                            "::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 +674,12 @@ private:              // references.              auto instance = LLMainThreadTask::dispatch(                  [&](){ -                    super::loginfos(super::template classname<DERIVED_TYPE>().c_str(), -                                    "::initParamSingleton() on main thread"); +                    super::loginfos({super::template classname<DERIVED_TYPE>(), +                                    "::initParamSingleton() on main thread"});                      return initParamSingleton_(std::forward<Args>(args)...);                  }); -            super::loginfos(super::template classname<DERIVED_TYPE>().c_str(), -                            "::initParamSingleton() returning on requesting thread"); +            super::loginfos({super::template classname<DERIVED_TYPE>(), +                            "::initParamSingleton() returning on requesting thread"});              return instance;          }      } @@ -707,14 +707,14 @@ public:          {          case super::UNINITIALIZED:          case super::QUEUED: -            super::logerrs("Uninitialized param singleton ", -                           super::template classname<DERIVED_TYPE>().c_str()); +            super::logerrs({"Uninitialized param singleton ", +                           super::template classname<DERIVED_TYPE>()});              break;          case super::CONSTRUCTING: -            super::logerrs("Tried to access param singleton ", -                           super::template classname<DERIVED_TYPE>().c_str(), -                           " from singleton constructor!"); +            super::logerrs({"Tried to access param singleton ", +                           super::template classname<DERIVED_TYPE>(), +                           " from singleton constructor!"});              break;          case super::INITIALIZING: @@ -726,8 +726,8 @@ public:              return lk->mInstance;          case super::DELETED: -            super::logerrs("Trying to access deleted param singleton ", -                           super::template classname<DERIVED_TYPE>().c_str()); +            super::logerrs({"Trying to access deleted param singleton ", +                           super::template classname<DERIVED_TYPE>()});              break;          } 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 <vector> +#include <stdexcept>  #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 <list>  #include <string> -// 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<CaptureLogRecorder>(mRecorder)->messageWith(search, required); +        return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->messageWith(search, required);      }      std::ostream& streamto(std::ostream& out) const      { -		return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out); +        return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);      }  private: +    LLError::FatalFunction mFatalFunction;      LLError::SettingsStoragePtr mOldSettings; -	LLError::RecorderPtr mRecorder; +    LLError::RecorderPtr mRecorder;  };  #endif /* ! defined(LL_WRAPLLERRS_H) */ | 
