diff options
| author | Mark Lentczner <markl@lindenlab.com> | 2007-02-06 00:57:33 +0000 | 
|---|---|---|
| committer | Mark Lentczner <markl@lindenlab.com> | 2007-02-06 00:57:33 +0000 | 
| commit | d0d4670f4941dcf7430fb1269c6613140ecf3ff7 (patch) | |
| tree | e3d6b59c19cac6bc172ec5fb0131ffc8f4923b75 /indra | |
| parent | 77f04c74eb1603bf2fadc30127d05378bfc7a48a (diff) | |
merge in of error-refactor-3
concludes (fixes) SL-31187
pair programmed and reviewed by markl and karen
Diffstat (limited to 'indra')
29 files changed, 1655 insertions, 316 deletions
| diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index 3e9ec14eb3..2ecda65d6f 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -30,6 +30,7 @@  #include "stdtypes.h"  #include "lldefs.h"  #include "llerror.h" +#include "llformat.h"  #include "llstring.h"  #include "lltimer.h"  #include "llfasttimer.h" diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 87e7016f39..fe5587365c 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -11,9 +11,11 @@  #include "llcommon.h"  #include "llapr.h" +#include "llerrorcontrol.h"  #include "llerrorthread.h"  #include "llframetimer.h"  #include "llmemory.h" +#include "lltimer.h"  //  // Signal handling @@ -174,10 +176,8 @@ LLSD LLApp::getOptionData(OptionPriority level)  void LLApp::stepFrame()  { -	// Update the static frame timer.  	LLFrameTimer::updateFrameTime(); - -	// Run ready runnables +	LLEventTimer::updateClass();  	mRunner.run();  } @@ -544,7 +544,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)  		else  		{  			// Don't log anything, even errors - this is because this signal could happen anywhere. -			gErrorStream.setLevel(LLErrorStream::NONE); +			LLError::setDefaultLevel(LLError::LEVEL_NONE);  		}  		// Change the signal that we reraise to SIGABRT, so we generate a core dump. diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 9f643fd9eb..52b37bb05c 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -1,40 +1,1041 @@  /**    * @file llerror.cpp - * @brief Function to crash. + * @date   December 2006 + * @brief error message system   * - * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.   * $License$   */ +  #include "linden_common.h"  #include "llerror.h" +#include "llerrorcontrol.h" + +#include "llapr.h" +extern apr_thread_mutex_t *gLogMutexp; +#include "llfile.h" +#include "llfixedbuffer.h" +#include "lllivefile.h" +#include "llsd.h" +#include "llsdserialize.h" + +#include <algorithm> +#include <cctype> +#include <map> +#include <sstream> +#if !LL_WINDOWS +#include <stdio.h> +#include <syslog.h> +#endif +#include <time.h> +#if LL_WINDOWS +#include <windows.h> +#endif +#include <vector> + + +#ifdef __GNUC__ +#include <cxxabi.h> +#endif + +namespace { +#if !LL_WINDOWS +	class RecordToSyslog : public LLError::Recorder +	{ +	public: +		RecordToSyslog(const std::string& identity) +			: mIdentity(identity) +		{ +			openlog(mIdentity.c_str(), LOG_CONS|LOG_PID, LOG_LOCAL0); +				// we need to set the string from a local copy of the string +				// since apparanetly openlog expects the const char* to remain +				// valid even after it returns (presumably until closelog) +		} +		 +		~RecordToSyslog() +		{ +			closelog(); +		} +		 +		virtual void recordMessage(LLError::ELevel level, +									const std::string& message) +		{ +			int syslogPriority = LOG_CRIT; +			switch (level) { +				case LLError::LEVEL_DEBUG:	syslogPriority = LOG_DEBUG;	break; +				case LLError::LEVEL_INFO:	syslogPriority = LOG_INFO;	break; +				case LLError::LEVEL_WARN:	syslogPriority = LOG_WARNING; break; +				case LLError::LEVEL_ERROR:	syslogPriority = LOG_CRIT;	break; +				default:					syslogPriority = LOG_CRIT; +			} +			 +			syslog(syslogPriority, "%s", message.c_str()); +		} +	private: +		std::string mIdentity; +	}; +#endif + +	class RecordToFile : public LLError::Recorder +	{ +	public: +		RecordToFile(const std::string& filename) +		{ +			mFile.open(filename.c_str(), llofstream::out | llofstream::app); +			if (!mFile) +			{ +				llinfos << "Error setting log file to " << filename << llendl; +			} +		} +		 +		~RecordToFile() +		{ +			mFile.close(); +		} +		 +		bool okay() { return mFile; } +		 +		virtual bool wantsTime() { return true; } +		 +		virtual void recordMessage(LLError::ELevel level, +									const std::string& message) +		{ +			mFile << message << std::endl; +			// mFile.flush(); +				// *FIX: should we do this?  +		} +	 +	private: +		llofstream mFile; +	}; +	 +	 +	class RecordToStderr : public LLError::Recorder +	{ +	public: +		RecordToStderr(bool timestamp) : mTimestamp(timestamp) { } + +		virtual bool wantsTime() { return mTimestamp; } +		 +		virtual void recordMessage(LLError::ELevel level, +									const std::string& message) +		{ +			fprintf(stderr, "%s\n", message.c_str()); +		} +	 +	private: +		bool mTimestamp; +	}; -LLErrorBuffer gErrorBuffer; -LLErrorStream gErrorStream(&gErrorBuffer); +	class RecordToFixedBuffer : public LLError::Recorder +	{ +	public: +		RecordToFixedBuffer(LLFixedBuffer& buffer) : mBuffer(buffer) { } +		 +		virtual void recordMessage(LLError::ELevel level, +									const std::string& message) +		{ +			mBuffer.addLine(message.c_str()); +		} +	 +	private: +		LLFixedBuffer& mBuffer; +	}; + +#if LL_WINDOWS +	class RecordToWinDebug: public LLError::Recorder +	{ +	public: +		virtual void recordMessage(LLError::ELevel level, +									const std::string& message) +		{ +			llutf16string utf16str = +				wstring_to_utf16str(utf8str_to_wstring(message)); +			utf16str += '\n'; +			OutputDebugString(utf16str.c_str()); +		} +	}; +#endif +} -void _llcrash_and_loop() +namespace  { -	// Now, we go kaboom! -	U32* crash = NULL; +	std::string className(const std::type_info& type) +	{ +#ifdef __GNUC__ +		// GCC: type_info::name() returns a mangled class name, must demangle + +		static size_t abi_name_len = 100; +		static char* abi_name_buf = (char*)malloc(abi_name_len); +			// warning: above is voodoo inferred from the GCC manual, +			// do NOT change + +		int status; +			// We don't use status, and shouldn't have to pass apointer to it +			// but gcc 3.3 libstc++'s implementation of demangling is broken +			// and fails without. +			 +		char* name = abi::__cxa_demangle(type.name(), +										abi_name_buf, &abi_name_len, &status); +			// this call can realloc the abi_name_buf pointer (!) + +		return name ? name : type.name(); + +#elif LL_WINDOWS +		// DevStudio: type_info::name() includes the text "class " at the start + +		static const std::string class_prefix = "class "; + +		std::string name = type.name(); +		std::string::size_type p = name.find(class_prefix); +		if (p == std::string::npos) +		{ +			return name; +		} + +		return name.substr(p + class_prefix.size()); + +#else		 +		return type.name(); +#endif +	} + +	std::string functionName(const std::string& preprocessor_name) +	{ +#if LL_WINDOWS +		// DevStudio: the __FUNCTION__ macro string includes +		// the type and/or namespace prefixes + +		std::string::size_type p = preprocessor_name.rfind(':'); +		if (p == std::string::npos) +		{ +			return preprocessor_name; +		} +		return preprocessor_name.substr(p + 1); + +#else +		return preprocessor_name; +#endif +	} + + +	class LogControlFile : public LLLiveFile +	{ +		LOG_CLASS(LogControlFile); +	 +	public: +		static LogControlFile& fromDirectory(const std::string& dir); +		 +		virtual void loadFile(); +		 +	private: +		LogControlFile(const std::string &filename) +			: LLLiveFile(filename) +			{ } +	}; + +	LogControlFile& LogControlFile::fromDirectory(const std::string& dir) +	{ +		std::string dirBase = dir + "/"; +			// NB: We have no abstraction in llcommon  for the "proper" +			// delimiter but it turns out that "/" works on all three platforms +			 +		std::string file = dirBase + "logcontrol-dev.xml"; +		 +		llstat stat_info; +		if (LLFile::stat(file.c_str(), &stat_info)) { +			// NB: stat returns non-zero if it can't read the file, for example +			// if it doesn't exist.  LLFile has no better abstraction for  +			// testing for file existence. +			 +			file = dirBase + "logcontrol.xml"; +		} +		return * new LogControlFile(file); +			// NB: This instance is never freed +	} +	 +	void LogControlFile::loadFile() +	{ +		LLSD configuration; + +		{ +			llifstream file(filename().c_str()); +			if (file.is_open()) +			{ +				LLSDSerialize::fromXML(configuration, file); +			} + +			if (configuration.isUndefined()) +			{ +				llwarns << filename() << " missing, ill-formed," +							" or simply undefined; not changing configuration" +						<< llendl; +				return; +			} +		} +		 +		LLError::configure(configuration); +		llwarns << "error logging reconfigured from " << filename() << llendl; +	} + + +	typedef std::map<std::string, LLError::ELevel> LevelMap; +	typedef std::vector<LLError::Recorder*> Recorders; +	typedef std::vector<LLError::CallSite*> CallSiteVector; + +	class Globals +	{ +	public: +		std::ostringstream messageStream; +		bool messageStreamInUse; + +		void addCallSite(LLError::CallSite&); +		void invalidateCallSites(); +		 +		static Globals& get(); +			// return the one instance of the globals + +	private: +		CallSiteVector callSites; + +		Globals() +			:	messageStreamInUse(false) +			{ } +		 +	}; + +	void Globals::addCallSite(LLError::CallSite& site) +	{ +		callSites.push_back(&site); +	} +	 +	void Globals::invalidateCallSites() +	{ +		for (CallSiteVector::const_iterator i = callSites.begin(); +			 i != callSites.end(); +			 ++i) +		{ +			(*i)->invalidate(); +		} +		 +		callSites.clear(); +	} -	*crash = 0; +	Globals& Globals::get() +	{ +		/* This pattern, of returning a reference to a static function +		   variable, is to ensure that this global is constructed before +		   it is used, no matter what the global initializeation sequence +		   is. +		   See C++ FAQ Lite, sections 10.12 through 10.14 +		*/ +		static Globals* globals = new Globals;		 +		return *globals; +	} +} + +namespace LLError +{ +	class Settings +	{ +	public: +		bool printLocation; -	while(TRUE) +		LLError::ELevel defaultLevel; +		 +		LevelMap functionLevelMap; +		LevelMap classLevelMap; +		LevelMap fileLevelMap; +		 +		LLError::FatalFunction crashFunction; +		LLError::TimeFunction timeFunction; +		 +		Recorders recorders; +		Recorder* fileRecorder; +		Recorder* fixedBufferRecorder; +		std::string fileRecorderFileName; +		 +		int shouldLogCallCounter; +		 +		static Settings& get(); +	 +		static void reset(); +		static Settings* saveAndReset(); +		static void restore(Settings*); +		 +	private: +		Settings() +			:	printLocation(false), +				defaultLevel(LLError::LEVEL_DEBUG), +				crashFunction(NULL), +				timeFunction(NULL), +				fileRecorder(NULL), +				fixedBufferRecorder(NULL), +				shouldLogCallCounter(0) +			{ } +		 +		static Settings*& getPtr(); +	}; +	 +	Settings& Settings::get() +	{ +		Settings* p = getPtr(); +		if (!p) +		{ +			reset(); +			p = getPtr(); +		} +		return *p; +	} +	 +	void Settings::reset() +	{ +		Globals::get().invalidateCallSites(); +		 +		Settings*& p = getPtr(); +		delete p; +		p = new Settings(); +	} +	 +	Settings* Settings::saveAndReset() +	{ +		Globals::get().invalidateCallSites(); +		 +		Settings*& p = getPtr(); +		Settings* originalSettings = p; +		p = new Settings(); +		return originalSettings; +	} +	 +	void Settings::restore(Settings* originalSettings)  	{ +		Globals::get().invalidateCallSites(); +		 +		Settings*& p = getPtr(); +		delete p; +		p = originalSettings; +	} -		// Loop forever, in case the crash didn't work? +	Settings*& Settings::getPtr() +	{ +		static Settings* currentSettings = NULL; +		return currentSettings;  	}  } -LLScopedErrorLevel::LLScopedErrorLevel(LLErrorBuffer::ELevel error_level) +namespace LLError  { -	mOrigErrorLevel = gErrorStream.getErrorLevel(); -	gErrorStream.setErrorLevel(error_level); +	CallSite::CallSite(ELevel level, +					const char* file, int line, +					const std::type_info& class_info, const char* function) +		: mLevel(level), mFile(file), mLine(line), +		  mClassInfo(class_info), mFunction(function), +		  mCached(false), mShouldLog(false) +		{ } + + +	void CallSite::invalidate() +		{ mCached = false; }  } +namespace +{ +	bool shouldLogToStderr() +	{ +#if LL_DARWIN +		// On Mac OS X, stderr from apps launched from the Finder goes to the +		// console log.  It's generally considered bad form to spam too much +		// there. +		 +		// If stdin is a tty, assume the user launched from the command line and +		// therefore wants to see stderr.  Otherwise, assume we've been launched +		// from the finder and shouldn't spam stderr. +		return isatty(0); +#else +		return true; +#endif +	} +	 +	bool stderrLogWantsTime() +	{ +#if LL_WINDOWS +		return false; +#else +		return true; +#endif +	} +	 +	 +	void commonInit(const std::string& dir) +	{ +		LLError::Settings::reset(); +		 +		LLError::setDefaultLevel(LLError::LEVEL_INFO); +		LLError::setFatalFunction(LLError::crashAndLoop); +		LLError::setTimeFunction(LLError::utcTime); + +		if (shouldLogToStderr()) +		{ +			LLError::addRecorder(new RecordToStderr(stderrLogWantsTime())); +		} +		 +#if LL_WINDOWS +		LLError::addRecorder(new RecordToWinDebug); +#endif + +		LogControlFile& e = LogControlFile::fromDirectory(dir); +		e.addToEventTimer(); +	} +} -LLScopedErrorLevel::~LLScopedErrorLevel() +namespace LLError  { -	gErrorStream.setErrorLevel(mOrigErrorLevel); +	void initForServer(const std::string& identity) +	{ +		std::string dir = LLApp::instance()->getOption("configdir"); +		commonInit(dir); +#if !LL_WINDOWS +		addRecorder(new RecordToSyslog(identity)); +#endif +	} + +	void initForApplication(const std::string& dir) +	{ +		commonInit(dir); +	} + +	void setPrintLocation(bool print) +	{ +		Settings& s = Settings::get(); +		s.printLocation = print; +	} + +	void setFatalFunction(FatalFunction f) +	{ +		Settings& s = Settings::get(); +		s.crashFunction = f; +	} + +	void setTimeFunction(TimeFunction f) +	{ +		Settings& s = Settings::get(); +		s.timeFunction = f; +	} + +	void setDefaultLevel(ELevel level) +	{ +		Globals& g = Globals::get(); +		Settings& s = Settings::get(); +		g.invalidateCallSites(); +		s.defaultLevel = level; +	} + +	void setFunctionLevel(const std::string& function_name, ELevel level) +	{ +		Globals& g = Globals::get(); +		Settings& s = Settings::get(); +		g.invalidateCallSites(); +		s.functionLevelMap[function_name] = level; +	} + +	void setClassLevel(const std::string& class_name, ELevel level) +	{ +		Globals& g = Globals::get(); +		Settings& s = Settings::get(); +		g.invalidateCallSites(); +		s.classLevelMap[class_name] = level; +	} + +	void setFileLevel(const std::string& file_name, ELevel level) +	{ +		Globals& g = Globals::get(); +		Settings& s = Settings::get(); +		g.invalidateCallSites(); +		s.fileLevelMap[file_name] = level; +	}  } + +namespace { +	LLError::ELevel decodeLevel(std::string name) +	{ +		static LevelMap level_names; +		if (level_names.empty()) +		{ +			level_names["ALL"]		= LLError::LEVEL_ALL; +			level_names["DEBUG"]	= LLError::LEVEL_DEBUG; +			level_names["INFO"]		= LLError::LEVEL_INFO; +			level_names["WARN"]		= LLError::LEVEL_WARN; +			level_names["ERROR"]	= LLError::LEVEL_ERROR; +			level_names["NONE"]		= LLError::LEVEL_NONE; +		} +		 +		std::transform(name.begin(), name.end(), name.begin(), toupper); +		 +		LevelMap::const_iterator i = level_names.find(name); +		if (i == level_names.end()) +		{ +			llwarns << "unrecognized logging level: '" << name << "'" << llendl; +			return LLError::LEVEL_INFO; +		} +		 +		return i->second; +	} +	 +	void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level) +	{ +		LLSD::array_const_iterator i, end; +		for (i = list.beginArray(), end = list.endArray(); i != end; ++i) +		{ +			map[*i] = level; +		} +	} +} + +namespace LLError +{ +	void configure(const LLSD& config) +	{ +		Globals& g = Globals::get(); +		Settings& s = Settings::get(); +		 +		g.invalidateCallSites(); +		s.functionLevelMap.clear(); +		s.classLevelMap.clear(); +		s.fileLevelMap.clear(); +		 +		setPrintLocation(config["print-location"]); +		setDefaultLevel(decodeLevel(config["default-level"])); +		 +		LLSD sets = config["settings"]; +		LLSD::array_const_iterator a, end; +		for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a) +		{ +			const LLSD& entry = *a; +			 +			ELevel level = decodeLevel(entry["level"]); +			 +			setLevels(s.functionLevelMap,	entry["functions"],	level); +			setLevels(s.classLevelMap,		entry["classes"],	level); +			setLevels(s.fileLevelMap,		entry["files"],		level); +		} +	} +} + + +namespace LLError +{ +	Recorder::~Recorder() +		{ } + +	// virtual +	bool Recorder::wantsTime() +		{ return false; } + + + +	void addRecorder(Recorder* recorder) +	{ +		if (recorder == NULL) +		{ +			return; +		} +		Settings& s = Settings::get(); +		s.recorders.push_back(recorder); +	} + +	void removeRecorder(Recorder* recorder) +	{ +		if (recorder == NULL) +		{ +			return; +		} +		Settings& s = Settings::get(); +		s.recorders.erase( +			std::remove(s.recorders.begin(), s.recorders.end(), recorder), +			s.recorders.end()); +	} +} + +namespace LLError +{ +	void logToFile(const std::string& file_name) +	{ +		LLError::Settings& s = LLError::Settings::get(); + +		removeRecorder(s.fileRecorder); +		delete s.fileRecorder; +		s.fileRecorder = NULL; +		s.fileRecorderFileName.clear(); +		 +		if (file_name.empty()) +		{ +			return; +		} +		 +		RecordToFile* f = new RecordToFile(file_name); +		if (!f->okay()) +		{ +			delete f; +			return; +		} + +		s.fileRecorderFileName = file_name; +		s.fileRecorder = f; +		addRecorder(f); +	} +	 +	void logToFixedBuffer(LLFixedBuffer* fixedBuffer) +	{ +		LLError::Settings& s = LLError::Settings::get(); + +		removeRecorder(s.fixedBufferRecorder); +		delete s.fixedBufferRecorder; +		s.fixedBufferRecorder = NULL; +		 +		if (!fixedBuffer) +		{ +			return; +		} +		 +		s.fixedBufferRecorder = new RecordToFixedBuffer(*fixedBuffer); +		addRecorder(s.fixedBufferRecorder); +	} + +	std::string logFileName() +	{ +		LLError::Settings& s = LLError::Settings::get(); +		return s.fileRecorderFileName; +	} +} + +namespace +{ +	void writeToRecorders(LLError::ELevel level, const std::string& message) +	{ +		LLError::Settings& s = LLError::Settings::get(); +	 +		std::string messageWithTime; +		 +		for (Recorders::const_iterator i = s.recorders.begin(); +			i != s.recorders.end(); +			++i) +		{ +			LLError::Recorder* r = *i; +			 +			if (r->wantsTime()  &&  s.timeFunction != NULL) +			{ +				if (messageWithTime.empty()) +				{ +					messageWithTime = s.timeFunction() + " " + message; +				} +				 +				r->recordMessage(level, messageWithTime); +			} +			else +			{ +				r->recordMessage(level, message); +			} +		} +	} +} + + +/* +Recorder formats: + +$type = "ERROR" | "WARNING" | "ALERT" | "INFO" | "DEBUG" +$loc = "$file($line)" +$msg = "$loc : " if FATAL or printing loc +		"" otherwise +$msg += "$type: " +$msg += contents of stringstream + +$time = "%Y-%m-%dT%H:%M:%SZ" if UTC +	 or "%Y-%m-%dT%H:%M:%S %Z" if local + +syslog:	"$msg" +file: "$time $msg\n" +stderr: "$time $msg\n" except on windows, "$msg\n" +fixedbuf: "$msg" +winddebug: "$msg\n" + +Note: if FATAL, an additional line gets logged first, with $msg set to +	"$loc : error" +	 +You get: +	llfoo.cpp(42) : error +	llfoo.cpp(42) : ERROR: something +	 +*/ + +namespace { +	bool checkLevelMap(const LevelMap& map, const std::string& key, +						LLError::ELevel& level) +	{ +		LevelMap::const_iterator i = map.find(key); +		if (i == map.end()) +		{ +			return false; +		} +		 +		level = i->second; +		return true; +	} +	 +	class LogLock +	{ +	public: +		LogLock(); +		~LogLock(); +		bool ok() const { return mOK; } +	private: +		bool mLocked; +		bool mOK; +	}; +	 +	LogLock::LogLock() +		: mLocked(false), mOK(false) +	{ +		if (!gLogMutexp) +		{ +			mOK = true; +			return; +		} +		 +		const int MAX_RETRIES = 5; +		for (int attempts = 0; attempts < MAX_RETRIES; ++attempts) +		{ +			apr_status_t s = apr_thread_mutex_trylock(gLogMutexp); +			if (!APR_STATUS_IS_EBUSY(s)) +			{ +				mLocked = true; +				mOK = true; +				return; +			} + +			ms_sleep(1); +			//apr_thread_yield(); +				// Just yielding won't necessarily work, I had problems with +				// this on Linux - doug 12/02/04 +		} + +		// We're hosed, we can't get the mutex.  Blah. +		std::cerr << "LogLock::LogLock: failed to get mutex for log" +					<< std::endl; +	} +	 +	LogLock::~LogLock() +	{ +		if (mLocked) +		{ +			apr_thread_mutex_unlock(gLogMutexp); +		} +	} +} + +namespace LLError +{ +	bool Log::shouldLog(CallSite& site) +	{ +		LogLock lock; +		if (!lock.ok()) +		{ +			return false; +		} +		 +		Globals& g = Globals::get(); +		Settings& s = Settings::get(); +		 +		s.shouldLogCallCounter += 1; +		 +		std::string class_name = className(site.mClassInfo); +		std::string function_name = functionName(site.mFunction); +		if (site.mClassInfo != typeid(NoClassInfo)) +		{ +			function_name = class_name + "::" + function_name; +		} + +		ELevel compareLevel = s.defaultLevel; + +		checkLevelMap(s.functionLevelMap, function_name, compareLevel) +		|| checkLevelMap(s.classLevelMap, class_name, compareLevel) +		|| checkLevelMap(s.fileLevelMap, abbreviateFile(site.mFile), compareLevel); + +		site.mCached = true; +		g.addCallSite(site); +		return site.mShouldLog = site.mLevel >= compareLevel; +	} + + +	std::ostringstream* Log::out() +	{ +		LogLock lock; +		if (lock.ok()) +		{ +			Globals& g = Globals::get(); + +			if (!g.messageStreamInUse) +			{ +				g.messageStreamInUse = true; +				return &g.messageStream; +			} +		} +		 +		return new std::ostringstream; +	} + +	void Log::flush(std::ostringstream* out, const CallSite& site) +	{ +		LogLock lock; +		if (!lock.ok()) +		{ +			return; +		} +		 +		Globals& g = Globals::get(); +		Settings& s = Settings::get(); + +		std::string message = out->str(); +		if (out == &g.messageStream) +		{ +			g.messageStream.clear(); +			g.messageStream.str(""); +			g.messageStreamInUse = false; +		} +		else +		{ +			delete out; +		} + +		if (site.mLevel == LEVEL_ERROR) +		{ +			std::ostringstream fatalMessage; +			fatalMessage << abbreviateFile(site.mFile) +						<< "(" << site.mLine << ") : error"; +			 +			writeToRecorders(site.mLevel, fatalMessage.str()); +		} +		 +		 +		std::ostringstream prefix; + +		switch (site.mLevel) +		{ +			case LEVEL_DEBUG:		prefix << "DEBUG: ";	break; +			case LEVEL_INFO:		prefix << "INFO: ";		break; +			case LEVEL_WARN:		prefix << "WARNING: ";	break; +			case LEVEL_ERROR:		prefix << "ERROR: ";	break; +			default:				prefix << "XXX: ";		break; +		}; +		 +		if (s.printLocation) +		{ +			prefix << abbreviateFile(site.mFile) +					<< "(" << site.mLine << ") : "; +		} +		 +		if (message.find(functionName(site.mFunction)) == std::string::npos) +		{ +	#if LL_WINDOWS +			// DevStudio: __FUNCTION__ already includes the full class name +	#else +			if (site.mClassInfo != typeid(NoClassInfo)) +			{ +				prefix << className(site.mClassInfo) << "::"; +			} +	#endif +			prefix << site.mFunction << ": "; +		} +		 +		prefix << message; +		message = prefix.str(); +		 +		writeToRecorders(site.mLevel, message); +		 +		if (site.mLevel == LEVEL_ERROR  &&  s.crashFunction) +		{ +			s.crashFunction(message); +		} +	} +} + + + + +namespace LLError +{ +	Settings* saveAndResetSettings() +	{ +		return Settings::saveAndReset(); +	} +	 +	void restoreSettings(Settings* s) +	{ +		return Settings::restore(s); +	} + +	std::string removePrefix(std::string& s, const std::string& p) +	{ +		std::string::size_type where = s.find(p); +		if (where == std::string::npos) +		{ +			return s; +		} +		 +		return std::string(s, where + p.size()); +	} +	 +	std::string abbreviateFile(const std::string& filePath) +	{ +		std::string f = filePath; + +#if LL_WINDOWS +		static std::string indra_prefix = "indra\\"; +#else +		static std::string indra_prefix = "indra/"; +#endif +		f = removePrefix(f, indra_prefix); + +#if LL_DARWIN +		static std::string newview_prefix = "newview/../"; +		f = removePrefix(f, newview_prefix); +#endif + +		return f; +	} + +	int shouldLogCallCount() +	{ +		Settings& s = Settings::get(); +		return s.shouldLogCallCounter; +	} + +	void crashAndLoop(const std::string& message) +	{ +		// Now, we go kaboom! +		int* crash = NULL; + +		*crash = 0; + +		while(true) +		{ +			// Loop forever, in case the crash didn't work? +		} +	} + +	std::string utcTime() +	{ +		time_t now = time(NULL); +		const size_t BUF_SIZE = 64; +		char time_str[BUF_SIZE];	/* Flawfinder: ignore */ +		 +		int chars = strftime(time_str, BUF_SIZE,  +								  "%Y-%m-%dT%H:%M:%SZ", +								  gmtime(&now)); + +		return chars ? time_str : "time error"; +	} +} + diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index d3d680ed33..84ac0fa7f0 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -1,8 +1,9 @@  /**    * @file llerror.h - * @brief Constants, functions, and macros for logging and runtime errors. + * @date   December 2006 + * @brief error message system   * - * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc. + * Copyright (c) 2006-$CurrentYear$, Linden Research, Inc.   * $License$   */ @@ -10,209 +11,208 @@  #define LL_LLERROR_H  #include <sstream> -#include <stdio.h> -#include <stdarg.h> - -#include "llerrorstream.h" -#include "llerrorbuffer.h" - -// Specific error codes -const S32 LL_ERR_NOERR = 0; -const S32 LL_ERR_ASSET_REQUEST_FAILED = -1; -//const S32 LL_ERR_ASSET_REQUEST_INVALID = -2; -const S32 LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3; -const S32 LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4; -const S32 LL_ERR_INSUFFICIENT_PERMISSIONS = -5; -const S32 LL_ERR_EOF = -39; -const S32 LL_ERR_CANNOT_OPEN_FILE = -42; -const S32 LL_ERR_FILE_NOT_FOUND = -43; -const S32 LL_ERR_FILE_EMPTY     = -44; -const S32 LL_ERR_TCP_TIMEOUT    = -23016; -const S32 LL_ERR_CIRCUIT_GONE   = -23017; - -// Error types - -#define LLERR_IMAGE				(1 << 1) // Image requests -#define LLERR_MESSAGE			(1 << 2) // Messaging -#define LLERR_PERF				(1 << 3) // Performance -#define LLERR_SQL				(1 << 4) // SQL statements -#define LLERR_DOUG				(1 << 5) // Doug's debugging -#define LLERR_USER_INPUT		(1 << 6) // Keyboard and mouse -#define LLERR_TIMING			(1 << 7) // Verbose time info -#define LLERR_TASK				(1 << 8) // Tracking tasks -#define LLERR_MSG_HANDLER		(1 << 9) // -#define LLERR_CIRCUIT_INFO		(1 << 10) // Message system circuit info -#define LLERR_PHYSICS			(1 << 11) // physics -#define LLERR_VFS				(1 << 12) // VFS -const U32 LLERR_ALL = 0xffff; -const U32 LLERR_NONE = 0x0; - -// Define one of these for different error levels in release... -// #define RELEASE_SHOW_DEBUG // Define this if you want your release builds to show lldebug output. -#define RELEASE_SHOW_INFO // Define this if you want your release builds to show llinfo output -#define RELEASE_SHOW_WARN // Define this if you want your release builds to show llwarn output. - - -////////////////////////////////////////// -// -//  Implementation - ignore -// -// -#ifdef _DEBUG -#define SHOW_DEBUG -#define SHOW_WARN -#define SHOW_INFO -#define SHOW_ASSERT -#else // _DEBUG - -#ifdef RELEASE_SHOW_DEBUG -#define SHOW_DEBUG -#endif - -#ifdef RELEASE_SHOW_WARN -#define SHOW_WARN -#endif - -#ifdef RELEASE_SHOW_INFO -#define SHOW_INFO -#endif - -#ifdef RELEASE_SHOW_ASSERT -#define SHOW_ASSERT -#endif - -#endif // _DEBUG - - -extern LLErrorStream gErrorStream; - - -// LL Error macros -// -// Usage: -// -// llerrs << "An error, oh my!" << variable << endl; -// llwarns << "Another error, fuck me!" << variable << endl; -// llwarnst(LLERR_IMAGE) << "Debug, mother fucker" << endl; -// -// NOTE: The output format of filename(lineno): is so that MS DevStudio -// can parse the output and automatically jump to that location - -inline std::string llerrno_string(int errnum) +#include <typeinfo> + +#include "llerrorlegacy.h" + + +/* Error Logging Facility + +	Information for most users: +	 +	Code can log messages with constuctions like this: +	 +		llinfos << "request to fizzbip agent " << agent_id +			<< " denied due to timeout" << llendl; +		 +	Messages can be logged to one of four increasing levels of concern, +	using one of four "streams": + +		lldebugs	- debug messages that are normally supressed +		llinfos		- informational messages that are normall shown +		llwarns		- warning messages that singal a problem +		llerrs		- error messages that are major, unrecoverable failures +		 +	The later (llerrs) automatically crashes the process after the message +	is logged. +	 +	Note that these "streams" are actually #define magic.  Rules for use: +		* they cannot be used as normal streams, only to start a message +		* messages written to them MUST be terminated with llendl +		* between the opening and closing, the << operator is indeed +		  writing onto a std::ostream, so all conversions and stream +		  formating are available +	 +	These messages are automatically logged with function name, and (if enabled) +	file and line of the message.  (Note: Existing messages that already include +	the function name don't get name printed twice.) +	 +	If you have a class, adding LOG_CLASS line to the declaration will cause +	all messages emitted from member functions (normal and static) to be tagged +	with the proper class name as well as the function name: +	 +		class LLFoo +		{ +			LOG_CLASS(LLFoo); +		public: +			... +		}; +	 +		void LLFoo::doSomething(int i) +		{ +			if (i > 100) +			{ +				llwanrs << "called with a big value for i: " << i << llendl;  +			} +			... +		} +	 +	will result in messages like: +	 +		WARN: LLFoo::doSomething: called with a big value for i: 283 +	 +	Which messages are logged and which are supressed can be controled at run +	time from the live file logcontrol.xml based on function, class and/or  +	source file.  See etc/logcontrol-dev.xml for details. +	 +	Lastly, logging is now very efficient in both compiled code and execution +	when skipped.  There is no need to wrap messages, even debugging ones, in +	#ifdef _DEBUG constructs.  lldebugs messages are compiled into all builds, +	even release.  Which means you can use them to help debug even when deployed +	to a real grid. +*/ + +namespace LLError  { -	std::stringstream res; -	res << "error(" << errnum << "):" << strerror(errnum) << " "; -	return res.str(); +	enum ELevel +	{ +		LEVEL_ALL = 0, +			// used to indicate that all messagess should be logged +			 +		LEVEL_DEBUG = 0, +		LEVEL_INFO = 1, +		LEVEL_WARN = 2, +		LEVEL_ERROR = 3,	// used to be called FATAL +		 +		LEVEL_NONE = 4 +			// not really a level +			// used to indicate that no messages should be logged +	}; +	 +	/*	Macro support +		The classes CallSite and Log are used by the logging macros below. +		They are not intended for general use. +	*/ +	 +	class CallSite; +	 +	class Log +	{ +	public: +		static bool shouldLog(CallSite&); +		static std::ostringstream* out(); +		static void flush(std::ostringstream*, const CallSite&); +	}; +	 +	class CallSite +	{ +		// Represents a specific place in the code where a message is logged +		// This is public because it is used by the macros below.  It is not +		// intended for public use. +	public: +		CallSite(ELevel, const char* file, int line, +				const std::type_info& class_info, const char* function); +						 +		bool shouldLog() +			{ return mCached ? mShouldLog : Log::shouldLog(*this); } +			// this member function needs to be in-line for efficiency +		 +		void invalidate(); +		 +	private: +		// these describe the call site and never change +		const ELevel			mLevel; +		const char* const		mFile; +		const int				mLine; +		const std::type_info&	mClassInfo; +		const char* const		mFunction; +		 +		// these implement a cache of the call to shouldLog() +		bool mCached; +		bool mShouldLog; +		 +		friend class Log; +	}; +	 +	 +	class End { }; +	inline std::ostream& operator<<(std::ostream& s, const End&) +		{ return s; } +		// used to indicate the end of a message +		 +	class NoClassInfo { }; +		// used to indicate no class info known for logging  } -inline std::string llerror_file_line(const char* file, S32 line) -{ -	std::stringstream res; -	res << file << "(" <<line << ")"; -	return res.str(); -} -// Used to throw an error which is always causes a system halt. -#define llerrs if (gErrorStream.isEnabledFor(LLErrorBuffer::FATAL)) \ -	{	std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::FATAL; \ -		llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : error\n"; \ -		llerror_oss <<  "ERROR: " << llerror_file_line(__FILE__, __LINE__) << " " - -// Used to show warnings -#define llwarns if (gErrorStream.isEnabledFor(LLErrorBuffer::WARN)) \ -	{	std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::WARN; \ -		if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : WARNING: "; \ -		else llerror_oss << "WARNING: "; \ -		llerror_oss - -// Alerts are for serious non-fatal situations that are not supposed to happen and need to alert someone -#define llalerts if (gErrorStream.isEnabledFor(LLErrorBuffer::WARN)) \ -	{	std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::WARN; \ -		if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : ALERT: "; \ -		else llerror_oss << "ALERT: ";  \ -		llerror_oss - -// Used to show informational messages that don't get disabled -#define llinfos if (gErrorStream.isEnabledFor(LLErrorBuffer::INFO)) \ -	{	std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::INFO; \ -		if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : INFO: "; \ -		else llerror_oss << "INFO: ";  \ -		llerror_oss - -#define llinfost(type) if (gErrorStream.isEnabledFor(LLErrorBuffer::INFO, type)) \ -	{	std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::INFO; \ -		if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : INFO: "; \ -		else llerror_oss << "INFO: [" << #type << "] ";  \ -		llerror_oss -  -// Used for general debugging output -#define lldebugs if (gErrorStream.isEnabledFor(LLErrorBuffer::DEBUG)) \ -	{	std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::DEBUG; \ -		if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : DEBUG: "; \ -		else llerror_oss << "DEBUG: ";  \ -		llerror_oss - -#define lldebugst(type) if (gErrorStream.isEnabledFor(LLErrorBuffer::DEBUG, type)) \ -	{	std::ostringstream llerror_oss; LLErrorBuffer::ELevel llerror_level = LLErrorBuffer::DEBUG; \ -		if (gErrorStream.getPrintLocation()) llerror_oss << llerror_file_line(__FILE__, __LINE__) << " : DEBUG: "; \ -		else llerror_oss << "DEBUG: [" << #type << "] ";  \ -		llerror_oss - -#define llendl std::endl; gErrorStream.crashOnError(llerror_oss, llerror_level); } -#define llendflush std::endl << std::flush; gErrorStream.crashOnError(llerror_oss, llerror_level); } -#define llcont llerror_oss - -#define llerror(msg, num)		llerrs << "Error # " << num << ": " << msg << llendl; - -#define llwarning(msg, num)		llwarns << "Warning # " << num << ": " << msg << llendl; - -#ifdef SHOW_ASSERT -#define llassert(func)			if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl; -#else -#define llassert(func) -#endif -#define llassert_always(func)	if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl; - -#ifdef SHOW_ASSERT -#define llverify(func)			if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl; -#else -#define llverify(func)			(func); // get rid of warning C4189 -#endif - -// handy compile-time assert - enforce those template parameters!  -#define cassert(expn) typedef char __C_ASSERT__[(expn)?1:-1]	/* Flawfinder: ignore */ -  -// Makes the app go down in flames, but on purpose! -void _llcrash_and_loop(); - -// Use as follows: -// llinfos << llformat("Test:%d (%.2f %.2f)", idx, x, y) << llendl; -// -// *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun) -// should perhaps be replaced with boost::format. -inline std::string llformat(const char *fmt, ...) -{ -	char tstr[1024];	/* Flawfinder: ignore */ -	va_list va; -	va_start(va, fmt); -#if LL_WINDOWS -	_vsnprintf(tstr, 1024, fmt, va); -#else -	vsnprintf(tstr, 1024, fmt, va);	/* Flawfinder: ignore */ -#endif -	va_end(va); -	return std::string(tstr); -} -// Helper class to temporarily change error level for the current scope. -class LLScopedErrorLevel -{ -public: -	LLScopedErrorLevel(LLErrorBuffer::ELevel error_level); -	~LLScopedErrorLevel(); - -private: -	LLErrorBuffer::ELevel mOrigErrorLevel; -}; -  +/* +	Class type information for logging + */ + +#define LOG_CLASS(s)	typedef s _LL_CLASS_TO_LOG +	// Declares class to tag logged messages with. +	// See top of file for example of how to use this +	 +typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; +	// Outside a class declartion, or in class without LOG_CLASS(), this +	// typedef causes the messages to not be associated with any class. + + + + + +/* +	Error Logging Macros +	See top of file for common usage.	 +*/ + +#define lllog(level) \ +	{ \ +		static LLError::CallSite _site( \ +			level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__);\ +		if (_site.shouldLog()) \ +		{ \ +			std::ostringstream* _out = LLError::Log::out(); \ +			(*_out) +	 +#define llendl \ +			LLError::End(); \ +			LLError::Log::flush(_out, _site); \ +		} \ +	} + +#define llinfos		lllog(LLError::LEVEL_INFO) +#define lldebugs	lllog(LLError::LEVEL_DEBUG) +#define llwarns		lllog(LLError::LEVEL_WARN) +#define llerrs		lllog(LLError::LEVEL_ERROR) + +#define llcont		(*_out) +	/* +		Use this construct if you need to do computation in the middle of a +		message: +		 +			llinfos << "the agent " << agend_id; +			switch (f) +			{ +				case FOP_SHRUGS:	llcont << "shrugs";				break; +				case FOP_TAPS:		llcont << "points at " << who;	break; +				case FOP_SAYS:		llcont << "says " << message;	break; +			} +			llcont << " for " << t << " seconds" << llendl; +		 +		Such computation is done iff the message will be logged. +	*/ + +  #endif // LL_LLERROR_H diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h new file mode 100644 index 0000000000..b1950eebf0 --- /dev/null +++ b/indra/llcommon/llerrorcontrol.h @@ -0,0 +1,123 @@ +/**  + * @file llerrorcontrol.h + * @date   December 2006 + * @brief error message system control + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLERRORCONTROL_H +#define LL_LLERRORCONTROL_H + +#include "llerror.h" + +#include <string> + +class LLFixedBuffer; +class LLSD; + +/* +	This is the part of the LLError namespace that manages the messages +	produced by the logging.  The logging support is defined in llerror.h. +	Most files do not need to include this. +	 +	These implementations are in llerror.cpp. +*/ + + +namespace LLError +{ +	void initForServer(const std::string& identity); +		// resets all logging settings to defaults needed by server processes +		// logs to stderr, syslog, and windows debug log +		// the identity string is used for in the syslog + +	void initForApplication(const std::string& dir); +		// resets all logging settings to defaults needed by applicaitons +		// logs to stderr and windows debug log +		// sets up log configuration from the file logcontrol.xml in dir + + +	/* +		Settings that control what is logged. +		Setting a level means log messages at that level or above. +	*/ +	 +	void setPrintLocation(bool); +	void setDefaultLevel(LLError::ELevel); +	void setFunctionLevel(const std::string& function_name, LLError::ELevel); +	void setClassLevel(const std::string& class_name, LLError::ELevel); +	void setFileLevel(const std::string& file_name, LLError::ELevel); +	 +	void configure(const LLSD&); +		// the LLSD can configure all of the settings +		// usually read automatically from the live errorlog.xml file + + +	/* +		Control functions. +	*/ + +	typedef void (*FatalFunction)(const std::string& message); +	void crashAndLoop(const std::string& message); +		// Default fatal funtion: divides by zero and loops forever + +	void setFatalFunction(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. +		 +	typedef std::string (*TimeFunction)(); +	std::string utcTime(); +	 +	void setTimeFunction(TimeFunction); +		// The function is use to return the current time, formatted for +		// display by those error recorders that want the time included. + + + +	class Recorder +	{ +		// An object that handles the actual output or error messages. +	public: +		virtual ~Recorder(); +		 +		virtual void recordMessage(LLError::ELevel, const std::string& message) = 0; +			// use the level for better display, not for filtering +			 +		virtual bool wantsTime(); // default returns false +			// override and return true if the recorder wants the time string +			// included in the text of the message +	}; +	 +	void addRecorder(Recorder*); +	void removeRecorder(Recorder*); +		// each error message is passed to each recorder via recordMessage() +	 +	void logToFile(const std::string& filename); +	void logToFixedBuffer(LLFixedBuffer*); +		// Utilities to add recorders for logging to a file or a fixed buffer +		// A second call to the same function will remove the logger added +		// with the first. +		// Passing the empty string or NULL to just removes any prior. +	std::string logFileName(); +		// returns name of current logging file, empty string if none + + +	/* +		Utilities for use by the unit tests of LLError itself. +	*/ + +	class Settings; +	Settings* saveAndResetSettings(); +	void restoreSettings(Settings *); +		 +	std::string abbreviateFile(const std::string& filePath); +	int shouldLogCallCount(); +	 +}; + +#endif // LL_LLERRORCONTROL_H + diff --git a/indra/llcommon/llerrorlegacy.h b/indra/llcommon/llerrorlegacy.h new file mode 100644 index 0000000000..19523512a6 --- /dev/null +++ b/indra/llcommon/llerrorlegacy.h @@ -0,0 +1,98 @@ +/**  + * @file llerrorlegacy.h + * @date   January 2007 + * @brief old things from the older error system + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLERRORLEGACY_H +#define LL_LLERRORLEGACY_H + + + +/* +	LEGACY -- DO NOT USE THIS STUFF ANYMORE +*/ + +// Specific error codes +const int LL_ERR_NOERR = 0; +const int LL_ERR_ASSET_REQUEST_FAILED = -1; +//const int LL_ERR_ASSET_REQUEST_INVALID = -2; +const int LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3; +const int LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4; +const int LL_ERR_INSUFFICIENT_PERMISSIONS = -5; +const int LL_ERR_EOF = -39; +const int LL_ERR_CANNOT_OPEN_FILE = -42; +const int LL_ERR_FILE_NOT_FOUND = -43; +const int LL_ERR_FILE_EMPTY     = -44; +const int LL_ERR_TCP_TIMEOUT    = -23016; +const int LL_ERR_CIRCUIT_GONE   = -23017; + + + +// Define one of these for different error levels in release... +// #define RELEASE_SHOW_DEBUG // Define this if you want your release builds to show lldebug output. +#define RELEASE_SHOW_INFO // Define this if you want your release builds to show llinfo output +#define RELEASE_SHOW_WARN // Define this if you want your release builds to show llwarn output. + + +////////////////////////////////////////// +// +//  Implementation - ignore +// +// +#ifdef _DEBUG +#define SHOW_DEBUG +#define SHOW_WARN +#define SHOW_INFO +#define SHOW_ASSERT +#else // _DEBUG + +#ifdef RELEASE_SHOW_DEBUG +#define SHOW_DEBUG +#endif + +#ifdef RELEASE_SHOW_WARN +#define SHOW_WARN +#endif + +#ifdef RELEASE_SHOW_INFO +#define SHOW_INFO +#endif + +#ifdef RELEASE_SHOW_ASSERT +#define SHOW_ASSERT +#endif + +#endif // _DEBUG + + + +#define lldebugst(type)			lldebugs +#define llendflush				llendl + + +#define llerror(msg, num)		llerrs << "Error # " << num << ": " << msg << llendl; + +#define llwarning(msg, num)		llwarns << "Warning # " << num << ": " << msg << llendl; + +#ifdef SHOW_ASSERT +#define llassert(func)			if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl; +#else +#define llassert(func) +#endif +#define llassert_always(func)	if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl; + +#ifdef SHOW_ASSERT +#define llverify(func)			if (!(func)) llerrs << "ASSERT (" << #func << ")" << llendl; +#else +#define llverify(func)			(func); // get rid of warning C4189 +#endif + +// handy compile-time assert - enforce those template parameters!  +#define cassert(expn) typedef char __C_ASSERT__[(expn)?1:-1]   /* Flawfinder: ignore */ +	//XXX: used in two places in llcommon/llskipmap.h + +#endif // LL_LLERRORLEGACY_H diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 16e2f5c5a1..4acd94f943 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -178,6 +178,7 @@ void llifstream::open(const char* _Filename,	/* Flawfinder: ignore */  	}  	llassert(_Filebuffer == NULL);  	_Filebuffer = new _Myfb(filep); +	_ShouldClose = true;  	_Myios::init(_Filebuffer);  } @@ -189,13 +190,17 @@ bool llifstream::is_open() const  }  llifstream::~llifstream()  {	 +	if (_ShouldClose) +	{ +		close(); +	}  	delete _Filebuffer;  }  llifstream::llifstream(const char *_Filename,  	ios_base::openmode _Mode,  	int _Prot) -	: std::basic_istream< char , std::char_traits< char > >(NULL,true),_Filebuffer(NULL) +	: std::basic_istream< char , std::char_traits< char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false)  {	// construct with named file and specified mode  	open(_Filename, _Mode | ios_base::in, _Prot);	/* Flawfinder: ignore */ diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 67de0f43fc..2899f51a60 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -68,7 +68,7 @@ public:  	typedef std::basic_ios<char,std::char_traits< char > > _Myios;  	llifstream() -		: std::basic_istream<char,std::char_traits< char > >(NULL,true),_Filebuffer(NULL) +		: std::basic_istream<char,std::char_traits< char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false)  	{	// construct unopened  	} @@ -78,7 +78,8 @@ public:  	explicit llifstream(_Filet *_File)  		: std::basic_istream<char,std::char_traits< char > >(NULL,true), -			_Filebuffer(new _Myfb(_File)) +			_Filebuffer(new _Myfb(_File)), +			_ShouldClose(false)  	{	// construct with specified C stream  	}  	virtual ~llifstream(); @@ -95,6 +96,7 @@ public:  private:  	_Myfb* _Filebuffer;	// the file buffer +	bool _ShouldClose;  }; diff --git a/indra/llcommon/llformat.cpp b/indra/llcommon/llformat.cpp new file mode 100644 index 0000000000..0c2a6d3b1e --- /dev/null +++ b/indra/llcommon/llformat.cpp @@ -0,0 +1,28 @@ +/**  + * @file llformat.cpp + * @date   January 2007 + * @brief string formatting utility + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#include "linden_common.h" + +#include "llformat.h" + +#include <stdarg.h> + +std::string llformat(const char *fmt, ...) +{ +	char tstr[1024];	/* Flawfinder: ignore */ +	va_list va; +	va_start(va, fmt); +#if LL_WINDOWS +	_vsnprintf(tstr, 1024, fmt, va); +#else +	vsnprintf(tstr, 1024, fmt, va);	/* Flawfinder: ignore */ +#endif +	va_end(va); +	return std::string(tstr); +} diff --git a/indra/llcommon/llformat.h b/indra/llcommon/llformat.h new file mode 100644 index 0000000000..7079656b72 --- /dev/null +++ b/indra/llcommon/llformat.h @@ -0,0 +1,23 @@ +/**  + * @file llformat.h + * @date   January 2007 + * @brief string formatting utility + * + * Copyright (c) 2007-$CurrentYear$, Linden Research, Inc. + * $License$ + */ + +#ifndef LL_LLFORMAT_H +#define LL_LLFORMAT_H + +#include <string> + +// Use as follows: +// llinfos << llformat("Test:%d (%.2f %.2f)", idx, x, y) << llendl; +// +// *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun) +// should perhaps be replaced with boost::format. + +std::string llformat(const char *fmt, ...); + +#endif // LL_LLFORMAT_H diff --git a/indra/llcommon/lllivefile.cpp b/indra/llcommon/lllivefile.cpp index 7dad6f82d8..df2e940352 100644 --- a/indra/llcommon/lllivefile.cpp +++ b/indra/llcommon/lllivefile.cpp @@ -8,24 +8,56 @@  #include "linden_common.h"  #include "lllivefile.h" +#include "llframetimer.h" +#include "lltimer.h" +class LLLiveFile::Impl +{ +public: +	Impl(const std::string &filename, const F32 refresh_period); +	~Impl(); +	 +	bool check(); +	 +	 +	bool mForceCheck; +	F32 mRefreshPeriod; +	LLFrameTimer mRefreshTimer; -LLLiveFile::LLLiveFile(const std::string &filename, const F32 refresh_period) : -mForceCheck(true), -mRefreshPeriod(refresh_period), -mFilename(filename), -mLastModTime(0), -mLastExists(false) +	std::string mFilename; +	time_t mLastModTime; +	bool mLastExists; +	 +	LLEventTimer* mEventTimer; +}; + +LLLiveFile::Impl::Impl(const std::string &filename, const F32 refresh_period) +	: mForceCheck(true), +	mRefreshPeriod(refresh_period), +	mFilename(filename), +	mLastModTime(0), +	mLastExists(false), +	mEventTimer(NULL)  {  } +LLLiveFile::Impl::~Impl() +{ +	delete mEventTimer; +} + +LLLiveFile::LLLiveFile(const std::string &filename, const F32 refresh_period) +	: impl(* new Impl(filename, refresh_period)) +{ +}  LLLiveFile::~LLLiveFile()  { +	delete &impl;  } -bool LLLiveFile::checkAndReload() +bool LLLiveFile::Impl::check()  {  	if (!mForceCheck && mRefreshTimer.getElapsedTimeF32() < mRefreshPeriod)  	{ @@ -46,9 +78,8 @@ bool LLLiveFile::checkAndReload()  		// broken somehow.  Clear flags and return.  		if (mLastExists)  		{ -			loadFile(); // Load the file, even though it's missing to allow it to clear state.  			mLastExists = false; -			return true; +			return true;	// no longer existing is a change!  		}  		return false;  	} @@ -68,7 +99,44 @@ bool LLLiveFile::checkAndReload()  	mLastExists = true;  	mLastModTime = stat_data.st_mtime; -	loadFile();  	return true;  } +bool LLLiveFile::checkAndReload() +{ +	bool changed = impl.check(); +	if (changed) +	{ +		loadFile(); +	} +	return changed; +} + +std::string LLLiveFile::filename() const +{ +	return impl.mFilename; +} + +namespace +{ +	class LiveFileEventTimer : public LLEventTimer +	{ +	public: +		LiveFileEventTimer(LLLiveFile& f, F32 refresh) +			: LLEventTimer(refresh), mLiveFile(f) +			{ } +			 +		void tick() +			{ mLiveFile.checkAndReload(); } +	 +	private: +		LLLiveFile& mLiveFile; +	}; +	 +} + +void LLLiveFile::addToEventTimer() +{ +	impl.mEventTimer = new LiveFileEventTimer(*this, impl.mRefreshPeriod); +} + diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h index 97c88a5c5c..a71844e5a9 100644 --- a/indra/llcommon/lllivefile.h +++ b/indra/llcommon/lllivefile.h @@ -9,7 +9,6 @@  #ifndef LL_LLLIVEFILE_H  #define LL_LLLIVEFILE_H -#include "llframetimer.h"  class LLLiveFile  { @@ -17,18 +16,22 @@ public:  	LLLiveFile(const std::string &filename, const F32 refresh_period = 5.f);  	virtual ~LLLiveFile(); -	bool checkAndReload(); // Returns true if the file changed in any way +	bool checkAndReload(); +		// Returns true if the file changed in any way +		// Call this before using anything that was read & cached from the file + +	std::string filename() const; + +	void addToEventTimer(); +		// Normally, just calling checkAndReload() is enough.  In some cases +		// though, you may need to let the live file periodically check itself.  protected:  	virtual void loadFile() = 0; // Implement this to load your file if it changed -	bool mForceCheck; -	F32 mRefreshPeriod; -	LLFrameTimer mRefreshTimer; - -	std::string mFilename; -	time_t mLastModTime; -	bool mLastExists; +private: +	class Impl; +	Impl& impl;  };  #endif //LL_LLLIVEFILE_H diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp index 25bd7ceac8..45d7acd417 100644 --- a/indra/llcommon/llsd.cpp +++ b/indra/llcommon/llsd.cpp @@ -10,6 +10,7 @@  #include <math.h>  #include "../llmath/llmath.h" +#include "llformat.h"  namespace {  	class ImplMap; diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 0555231010..a381af74d0 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -11,6 +11,7 @@  #include "stdtypes.h"  #include "llerror.h" +#include "llfile.h"  #include <algorithm>  #include <map>  #include <stdio.h> diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h index ad428ce565..20db115c6e 100644 --- a/indra/llcommon/llstringtable.h +++ b/indra/llcommon/llstringtable.h @@ -10,6 +10,8 @@  #ifndef LL_STRING_TABLE_H  #define LL_STRING_TABLE_H +#include "lldefs.h" +#include "llformat.h"  #include "llstl.h"  #include <list>  #include <set> diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index 4ba47e7689..9e1a0ee12c 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -4402,33 +4402,6 @@ void process_secured_template_checksum_request(LLMessageSystem* msg, void**)  	send_template_reply(msg, token);  } -void process_log_control(LLMessageSystem* msg, void**) -{ -	U8 level; -	U32 mask; -	BOOL time; -	BOOL location; -	BOOL remote_infos; - -	msg->getU8Fast(_PREHASH_Options, _PREHASH_Level, level); -	msg->getU32Fast(_PREHASH_Options, _PREHASH_Mask, mask); -	msg->getBOOLFast(_PREHASH_Options, _PREHASH_Time, time); -	msg->getBOOLFast(_PREHASH_Options, _PREHASH_Location, location); -	msg->getBOOLFast(_PREHASH_Options, _PREHASH_RemoteInfos, remote_infos); - -	gErrorStream.setLevel(LLErrorStream::ELevel(level)); -	gErrorStream.setDebugMask(mask); -	gErrorStream.setTime(time); -	gErrorStream.setPrintLocation(location); -	gErrorStream.setElevatedRemote(remote_infos); - -	llinfos << "Logging set to level " << gErrorStream.getLevel()  -		<< " mask " << std::hex << gErrorStream.getDebugMask() << std::dec -		<< " time " << gErrorStream.getTime() -		<< " loc " << gErrorStream.getPrintLocation() -		<< llendl; -} -  void process_log_messages(LLMessageSystem* msg, void**)  {  	U8 log_message; @@ -4749,7 +4722,6 @@ BOOL start_messaging_system(  	gMessageSystem->setHandlerFuncFast(_PREHASH_PacketAck,             process_packet_ack,	    NULL);  	gMessageSystem->setHandlerFuncFast(_PREHASH_TemplateChecksumRequest,  process_template_checksum_request,	NULL);  	gMessageSystem->setHandlerFuncFast(_PREHASH_SecuredTemplateChecksumRequest,  process_secured_template_checksum_request,	NULL); -	gMessageSystem->setHandlerFuncFast(_PREHASH_LogControl,			process_log_control,	NULL);  	gMessageSystem->setHandlerFuncFast(_PREHASH_LogMessages,			process_log_messages,	NULL);  	gMessageSystem->setHandlerFuncFast(_PREHASH_CreateTrustedCircuit,  				       process_create_trusted_circuit, diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index ec76e1f755..48d950e377 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -187,6 +187,8 @@ class LLMessagePollInfo;  class LLMessageSystem  { +	LOG_CLASS(LLMessageSystem); +	  public:  	U8										mSendBuffer[MAX_BUFFER_SIZE];  	// Encoded send buffer needs to be slightly larger since the zero @@ -757,7 +759,6 @@ BOOL start_messaging_system(  void end_messaging_system();  void null_message_callback(LLMessageSystem *msg, void **data); -void process_log_control(LLMessageSystem* msg, void**);  //  // Inlines diff --git a/indra/llmessage/message_prehash.cpp b/indra/llmessage/message_prehash.cpp index 18be31af58..c2d7c9c2a3 100644 --- a/indra/llmessage/message_prehash.cpp +++ b/indra/llmessage/message_prehash.cpp @@ -489,7 +489,6 @@ char * _PREHASH_NVPair;  char * _PREHASH_ObjectSpinStart;  char * _PREHASH_UseEstateSun;  char * _PREHASH_LogoutBlock; -char * _PREHASH_RelayLogControl;  char * _PREHASH_RegionID;  char * _PREHASH_Creator;  char * _PREHASH_ProposalText; @@ -1294,7 +1293,6 @@ char * _PREHASH_AssetBlock;  char * _PREHASH_AcceptNotices;  char * _PREHASH_SetGroupAcceptNotices;  char * _PREHASH_CloseCircuit; -char * _PREHASH_LogControl;  char * _PREHASH_TeleportFinish;  char * _PREHASH_PathRevolutions;  char * _PREHASH_ClassifiedInfoReply; @@ -1963,7 +1961,6 @@ void init_prehash_data()  	_PREHASH_ObjectSpinStart = gMessageStringTable.getString("ObjectSpinStart");  	_PREHASH_UseEstateSun = gMessageStringTable.getString("UseEstateSun");  	_PREHASH_LogoutBlock = gMessageStringTable.getString("LogoutBlock"); -	_PREHASH_RelayLogControl = gMessageStringTable.getString("RelayLogControl");  	_PREHASH_RegionID = gMessageStringTable.getString("RegionID");  	_PREHASH_Creator = gMessageStringTable.getString("Creator");  	_PREHASH_ProposalText = gMessageStringTable.getString("ProposalText"); @@ -2768,7 +2765,6 @@ void init_prehash_data()  	_PREHASH_AcceptNotices = gMessageStringTable.getString("AcceptNotices");  	_PREHASH_SetGroupAcceptNotices = gMessageStringTable.getString("SetGroupAcceptNotices");  	_PREHASH_CloseCircuit = gMessageStringTable.getString("CloseCircuit"); -	_PREHASH_LogControl = gMessageStringTable.getString("LogControl");  	_PREHASH_TeleportFinish = gMessageStringTable.getString("TeleportFinish");  	_PREHASH_PathRevolutions = gMessageStringTable.getString("PathRevolutions");  	_PREHASH_ClassifiedInfoReply = gMessageStringTable.getString("ClassifiedInfoReply"); diff --git a/indra/llmessage/message_prehash.h b/indra/llmessage/message_prehash.h index 0da2e02f83..63e23237f5 100644 --- a/indra/llmessage/message_prehash.h +++ b/indra/llmessage/message_prehash.h @@ -489,7 +489,6 @@ extern char * _PREHASH_NVPair;  extern char * _PREHASH_ObjectSpinStart;  extern char * _PREHASH_UseEstateSun;  extern char * _PREHASH_LogoutBlock; -extern char * _PREHASH_RelayLogControl;  extern char * _PREHASH_RegionID;  extern char * _PREHASH_Creator;  extern char * _PREHASH_ProposalText; @@ -1294,7 +1293,6 @@ extern char * _PREHASH_AssetBlock;  extern char * _PREHASH_AcceptNotices;  extern char * _PREHASH_SetGroupAcceptNotices;  extern char * _PREHASH_CloseCircuit; -extern char * _PREHASH_LogControl;  extern char * _PREHASH_TeleportFinish;  extern char * _PREHASH_PathRevolutions;  extern char * _PREHASH_ClassifiedInfoReply; diff --git a/indra/llui/llbutton.cpp b/indra/llui/llbutton.cpp index c02be6bb8d..ffa9262e21 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -584,7 +584,7 @@ void LLButton::draw()  		else  		{  			// no image -			llalerts << "No image for button " << mName << llendl; +			llwarns << "No image for button " << mName << llendl;  			// draw it in pink so we can find it  			gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0, LLColor4::pink1, FALSE);  		} diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index cf80a72282..4ff07e9976 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -28,6 +28,7 @@  #include "llcrypto.h"  #include "lldir.h"  #include "lleconomy.h" +#include "llerrorcontrol.h"  #include "llfiltersd2xmlrpc.h"  #include "llfocusmgr.h"  #include "imageids.h" @@ -421,7 +422,6 @@ BOOL idle_startup()  								  invalid_message_callback,  								  NULL); -			gErrorStream.setUTCTimestamp(gLogUTC);  			if (gSavedSettings.getBOOL("LogMessages") || gLogMessages)  			{  				llinfos << "Message logging activated!" << llendl; @@ -1679,7 +1679,7 @@ BOOL idle_startup()  			// Move the progress view in front of the UI  			gViewerWindow->moveProgressViewToFront(); -			gErrorStream.setFixedBuffer(gDebugView->mDebugConsolep); +			LLError::logToFixedBuffer(gDebugView->mDebugConsolep);  			// set initial visibility of debug console  			gDebugView->mDebugConsolep->setVisible(gSavedSettings.getBOOL("ShowDebugConsole"));  			gDebugView->mStatViewp->setVisible(gSavedSettings.getBOOL("ShowDebugStats")); diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 7ef1fd82c6..49578b186d 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -430,8 +430,6 @@ void handle_dump_image_list(void*);  void handle_fullscreen_debug(void*);  void handle_crash(void*);  void handle_dump_followcam(void*); -void handle_viewer_enable_circuit_log(void*); -void handle_viewer_disable_circuit_log(void*);  void handle_viewer_enable_message_log(void*);  void handle_viewer_disable_message_log(void*);  void handle_send_postcard(void*); @@ -818,10 +816,6 @@ void init_client_menu(LLMenuGL* menu)  		LLMenuGL* sub = NULL;  		sub = new LLMenuGL("Network"); -		sub->append(new LLMenuItemCallGL("Enable Circuit Log",   -			&handle_viewer_enable_circuit_log,  NULL)); -		sub->append(new LLMenuItemCallGL("Disable Circuit Log",  -			&handle_viewer_disable_circuit_log, NULL));  		sub->append(new LLMenuItemCallGL("Enable Message Log",    			&handle_viewer_enable_message_log,  NULL));  		sub->append(new LLMenuItemCallGL("Disable Message Log",  @@ -1307,11 +1301,6 @@ void init_server_menu(LLMenuGL* menu)  			&handle_normal_llinfo_log, &enable_god_customer_service));  		sub_menu->appendSeparator(); -		sub_menu->append(new LLMenuItemCallGL("Enable Circuit Log",   -			&handle_sim_enable_circuit_log,  &enable_god_customer_service)); -		sub_menu->append(new LLMenuItemCallGL("Disable Circuit Log",  -			&handle_sim_disable_circuit_log, &enable_god_customer_service)); -		sub_menu->appendSeparator();  		sub_menu->append(new LLMenuItemCallGL("Enable Message Log",    			&handle_sim_enable_message_log,  &enable_god_customer_service));  		sub_menu->append(new LLMenuItemCallGL("Disable Message Log",  @@ -6375,24 +6364,6 @@ void handle_dump_followcam(void*)  	LLFollowCamMgr::dump();  } -void handle_viewer_enable_circuit_log(void*) -{ -	llinfos << "Showing circuit information every " << gMessageSystem->mCircuitPrintFreq << " seconds" << llendl; -	gErrorStream.setLevel( LLErrorStream::DEBUG ); -	gErrorStream.setDebugFlag( LLERR_CIRCUIT_INFO ); -	// and dump stuff out immediately -	gMessageSystem->dumpCircuitInfo(); -} - -void handle_viewer_disable_circuit_log(void*) -{ -	llinfos << "Hiding circuit information" << llendl; -#if !LL_DEBUG -	gErrorStream.setLevel( LLErrorStream::INFO ); -#endif -	gErrorStream.clearDebugFlag( LLERR_CIRCUIT_INFO ); -} -  void handle_viewer_enable_message_log(void*)  {  	gMessageSystem->startLogging(); diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index a2ef97fc6b..50b84babd1 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -63,8 +63,6 @@  #include "lldqueueptr.h"  #include "llendianswizzle.h"  #include "llerror.h" -#include "llerrorbuffer.h" -#include "llerrorstream.h"  #include "llfasttimer.h"  #include "llfixedbuffer.h"  #include "llframetimer.h" diff --git a/indra/newview/llxmlrpctransaction.cpp b/indra/newview/llxmlrpctransaction.cpp index c2726997b2..cc8f955bc6 100644 --- a/indra/newview/llxmlrpctransaction.cpp +++ b/indra/newview/llxmlrpctransaction.cpp @@ -324,9 +324,9 @@ bool LLXMLRPCTransaction::Impl::process()  			if (curl_msg->data.result != CURLE_OK)  			{  				setCurlStatus(curl_msg->data.result); -				llalerts << "LLXMLRPCTransaction CURL error " +				llwarns << "LLXMLRPCTransaction CURL error "  					<< mCurlCode << ": " << mCurlErrorBuffer << llendl; -				llalerts << "LLXMLRPCTransaction request URI: " +				llwarns << "LLXMLRPCTransaction request URI: "  					<< mURI << llendl;  				return true; @@ -360,11 +360,11 @@ bool LLXMLRPCTransaction::Impl::process()  			{  				setStatus(LLXMLRPCTransaction::StatusXMLRPCError); -				llalerts << "LLXMLRPCTransaction XMLRPC " +				llwarns << "LLXMLRPCTransaction XMLRPC "  					<< (hasError ? "error " : "fault ")  					<< faultCode << ": "  					<< faultString << llendl; -				llalerts << "LLXMLRPCTransaction request URI: " +				llwarns << "LLXMLRPCTransaction request URI: "  					<< mURI << llendl;  			} diff --git a/indra/test/llhttpclient_tut.cpp b/indra/test/llhttpclient_tut.cpp index 98f24c1bdd..40cde1fd6c 100644 --- a/indra/test/llhttpclient_tut.cpp +++ b/indra/test/llhttpclient_tut.cpp @@ -16,6 +16,7 @@  #include "lltut.h"  #include "llhttpclient.h" +#include "llformat.h"  #include "llpipeutil.h"  #include "llpumpio.h" diff --git a/indra/test/llpipeutil.cpp b/indra/test/llpipeutil.cpp index 56789cfae8..3a15bfdb7e 100644 --- a/indra/test/llpipeutil.cpp +++ b/indra/test/llpipeutil.cpp @@ -12,6 +12,7 @@  #include <stdlib.h>  #include "llbufferstream.h" +#include "lldefs.h"  #include "llframetimer.h"  #include "llpumpio.h"  #include "llrand.h" diff --git a/indra/test/lltut.cpp b/indra/test/lltut.cpp index 96aad3da58..592d61137d 100644 --- a/indra/test/lltut.cpp +++ b/indra/test/lltut.cpp @@ -9,6 +9,8 @@   */  #include "lltut.h" + +#include "llformat.h"  #include "llsd.h"  namespace tut @@ -135,6 +137,20 @@ namespace tut  		}  	} +	void ensure_ends_with(const std::string& msg, +		const std::string& actual, const std::string& expectedEnd) +	{ +		if( actual.size() < expectedEnd.size() +			|| actual.rfind(expectedEnd) +				!= (actual.size() - expectedEnd.size()) ) +		{ +			std::stringstream ss; +			ss << msg << ": " << "expected to find " << expectedEnd +				<< " at end of actual " << actual; +			throw failure(ss.str().c_str()); +		} +	} +  	void ensure_contains(const std::string& msg,  		const std::string& actual, const std::string& expectedSubString)  	{ @@ -146,4 +162,16 @@ namespace tut  			throw failure(ss.str().c_str());  		}  	} + +	void ensure_does_not_contain(const std::string& msg, +		const std::string& actual, const std::string& expectedSubString) +	{ +		if( actual.find(expectedSubString, 0) != std::string::npos ) +		{ +			std::stringstream ss; +			ss << msg << ": " << "expected not to find " << expectedSubString +				<< " in actual " << actual; +			throw failure(ss.str().c_str()); +		} +	}  } diff --git a/indra/test/lltut.h b/indra/test/lltut.h index c750a99b8d..c2ec504857 100644 --- a/indra/test/lltut.h +++ b/indra/test/lltut.h @@ -68,8 +68,14 @@ namespace tut  	void ensure_starts_with(const std::string& msg,  		const std::string& actual, const std::string& expectedStart); +	void ensure_ends_with(const std::string& msg, +		const std::string& actual, const std::string& expectedEnd); +  	void ensure_contains(const std::string& msg,  		const std::string& actual, const std::string& expectedSubString); + +	void ensure_does_not_contain(const std::string& msg, +		const std::string& actual, const std::string& expectedSubString);  } diff --git a/indra/test/test.cpp b/indra/test/test.cpp index 2d727daaa5..312c52dc77 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -17,6 +17,7 @@   */  #include "linden_common.h" +#include "llerrorcontrol.h"  #include "lltut.h"  #include <apr-1/apr_pools.h> @@ -159,8 +160,18 @@ void stream_groups(std::ostream& s, const char* app)  	}  } +void wouldHaveCrashed(const std::string& message) +{ +	tut::fail("llerrs message: " + message); +} +  int main(int argc, char **argv)  { +	LLError::initForApplication("."); +	LLError::setFatalFunction(wouldHaveCrashed); +	LLError::setDefaultLevel(LLError::LEVEL_ERROR); +		// *FIX: should come from error config file +	  #ifdef CTYPE_WORKAROUND  	ctype_workaround();  #endif | 
