diff options
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/CMakeLists.txt | 5 | ||||
-rw-r--r-- | indra/llcommon/llapp.cpp | 23 | ||||
-rw-r--r-- | indra/llcommon/llcoros.h | 20 | ||||
-rw-r--r-- | indra/llcommon/llerror.cpp | 164 | ||||
-rw-r--r-- | indra/llcommon/llerrorcontrol.h | 22 | ||||
-rw-r--r-- | indra/llcommon/llevents.cpp | 23 | ||||
-rw-r--r-- | indra/llcommon/llevents.h | 20 | ||||
-rw-r--r-- | indra/llcommon/llinitparam.h | 3 | ||||
-rw-r--r-- | indra/llcommon/llleap.cpp | 51 | ||||
-rw-r--r-- | indra/llcommon/llleap.h | 14 | ||||
-rw-r--r-- | indra/llcommon/llpreprocessor.h | 2 | ||||
-rw-r--r-- | indra/llcommon/stringize.h | 52 | ||||
-rw-r--r-- | indra/llcommon/tests/llerror_test.cpp | 96 | ||||
-rw-r--r-- | indra/llcommon/tests/wrapllerrs.h | 14 |
14 files changed, 334 insertions, 175 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index d9eb13d65a..42ad56f1b0 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -255,6 +255,11 @@ 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) + list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES}) if(LLCOMMON_LINK_SHARED) diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 6cc9e804d4..421af3006e 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -392,7 +392,7 @@ void LLApp::setupErrorHandling(bool second_instance) #if LL_WINDOWS -#if LL_SEND_CRASH_REPORTS +#if LL_SEND_CRASH_REPORTS && ! defined(LL_BUGSPLAT) EnableCrashingOnCrashes(); // This sets a callback to handle w32 signals to the console window. @@ -454,8 +454,15 @@ void LLApp::setupErrorHandling(bool second_instance) mExceptionHandler->set_handle_debug_exceptions(true); } } -#endif -#else +#endif // LL_SEND_CRASH_REPORTS && ! defined(LL_BUGSPLAT) +#else // ! LL_WINDOWS + +#if defined(LL_BUGSPLAT) + // Don't install our own signal handlers -- BugSplat needs to hook them, + // or it's completely ineffectual. + bool installHandler = false; + +#else // ! LL_BUGSPLAT // // Start up signal handling. // @@ -463,9 +470,11 @@ void LLApp::setupErrorHandling(bool second_instance) // thread, asynchronous signals can be delivered to any thread (in theory) // setup_signals(); - + // Add google breakpad exception handler configured for Darwin/Linux. bool installHandler = true; +#endif // ! LL_BUGSPLAT + #if LL_DARWIN // For the special case of Darwin, we do not want to install the handler if // the process is being debugged as the app will exit with value ABRT (6) if @@ -498,7 +507,7 @@ void LLApp::setupErrorHandling(bool second_instance) // installing the handler. installHandler = true; } - #endif + #endif // ! LL_RELEASE_FOR_DOWNLOAD if(installHandler && (mExceptionHandler == 0)) { @@ -514,9 +523,9 @@ void LLApp::setupErrorHandling(bool second_instance) google_breakpad::MinidumpDescriptor desc(mDumpPath); mExceptionHandler = new google_breakpad::ExceptionHandler(desc, NULL, unix_minidump_callback, NULL, true, -1); } -#endif +#endif // LL_LINUX -#endif +#endif // ! LL_WINDOWS startErrorThread(); } diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 8fb27af6a4..c551413811 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -170,6 +170,26 @@ public: static bool get_consuming(); /** + * RAII control of the consuming flag + */ + class OverrideConsuming + { + public: + OverrideConsuming(bool consuming): + mPrevConsuming(get_consuming()) + { + set_consuming(consuming); + } + ~OverrideConsuming() + { + set_consuming(mPrevConsuming); + } + + private: + bool mPrevConsuming; + }; + + /** * Please do NOT directly use boost::dcoroutines::future! It is essential * to maintain the "current" coroutine at every context switch. This * Future wraps the essential boost::dcoroutines::future functionality diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 49ed8b495d..7cfd1409b1 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -132,8 +132,6 @@ namespace { mFile.sync_with_stdio(false); } } - mWantsTime = true; - mWantsTags = true; } ~RecordToFile() @@ -175,7 +173,7 @@ namespace { public: RecordToStderr(bool timestamp) : mUseANSI(ANSI_PROBE) { - mWantsTime = timestamp; + this->showMultiline(true); } virtual bool enabled() override @@ -241,7 +239,13 @@ namespace { class RecordToFixedBuffer : public LLError::Recorder { public: - RecordToFixedBuffer(LLLineBuffer* buffer) : mBuffer(buffer) { } + RecordToFixedBuffer(LLLineBuffer* buffer) + : mBuffer(buffer) + { + this->showMultiline(true); + this->showTags(false); + this->showLocation(false); + } virtual bool enabled() override { @@ -263,7 +267,11 @@ namespace { { public: RecordToWinDebug() - {} + { + this->showMultiline(true); + this->showTags(false); + this->showLocation(false); + } virtual bool enabled() override { @@ -412,6 +420,7 @@ namespace public: std::ostringstream messageStream; bool messageStreamInUse; + std::string mFatalMessage; void addCallSite(LLError::CallSite&); void invalidateCallSites(); @@ -454,8 +463,6 @@ namespace LLError public: virtual ~SettingsConfig(); - bool mPrintLocation; - LLError::ELevel mDefaultLevel; bool mLogAlwaysFlush; @@ -500,7 +507,6 @@ namespace LLError SettingsConfig::SettingsConfig() : LLRefCount(), - mPrintLocation(false), mDefaultLevel(LLError::LEVEL_DEBUG), mLogAlwaysFlush(true), mEnabledLogTypesMask(255), @@ -706,23 +712,22 @@ namespace LLError commonInit(user_dir, app_dir, log_to_stderr); } - void setPrintLocation(bool print) + void setFatalFunction(const FatalFunction& f) { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - s->mPrintLocation = print; + s->mCrashFunction = f; } - void setFatalFunction(const FatalFunction& f) + FatalFunction getFatalFunction() { SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - s->mCrashFunction = f; + return s->mCrashFunction; } - FatalFunction getFatalFunction() - { - SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig(); - return s->mCrashFunction; - } + std::string getFatalMessage() + { + return Globals::getInstance()->mFatalMessage; + } void setTimeFunction(TimeFunction f) { @@ -845,7 +850,6 @@ namespace LLError s->mTagLevelMap.clear(); s->mUniqueLogMessages.clear(); - setPrintLocation(config["print-location"]); setDefaultLevel(decodeLevel(config["default-level"])); if (config.has("log-always-flush")) { @@ -876,11 +880,12 @@ namespace LLError namespace LLError { Recorder::Recorder() - : mWantsTime(false), - mWantsTags(false), - mWantsLevel(true), - mWantsLocation(false), - mWantsFunctionName(true) + : mWantsTime(true) + , mWantsTags(true) + , mWantsLevel(true) + , mWantsLocation(true) + , mWantsFunctionName(true) + , mWantsMultiline(false) { } @@ -917,6 +922,42 @@ namespace LLError return mWantsFunctionName; } + // virtual + bool Recorder::wantsMultiline() + { + return mWantsMultiline; + } + + void Recorder::showTime(bool show) + { + mWantsTime = show; + } + + void Recorder::showTags(bool show) + { + mWantsTags = show; + } + + void Recorder::showLevel(bool show) + { + mWantsLevel = show; + } + + void Recorder::showLocation(bool show) + { + mWantsLocation = show; + } + + void Recorder::showFunctionName(bool show) + { + mWantsFunctionName = show; + } + + void Recorder::showMultiline(bool show) + { + mWantsMultiline = show; + } + void addRecorder(RecorderPtr recorder) { if (!recorder) @@ -949,17 +990,15 @@ namespace LLError s->mFileRecorder.reset(); s->mFileRecorderFileName.clear(); - if (file_name.empty()) + if (!file_name.empty()) { - return; - } - - RecorderPtr recordToFile(new RecordToFile(file_name)); - if (boost::dynamic_pointer_cast<RecordToFile>(recordToFile)->okay()) - { - s->mFileRecorderFileName = file_name; - s->mFileRecorder = recordToFile; - addRecorder(recordToFile); + RecorderPtr recordToFile(new RecordToFile(file_name)); + if (boost::dynamic_pointer_cast<RecordToFile>(recordToFile)->okay()) + { + s->mFileRecorderFileName = file_name; + s->mFileRecorder = recordToFile; + addRecorder(recordToFile); + } } } @@ -970,14 +1009,12 @@ namespace LLError removeRecorder(s->mFixedBufferRecorder); s->mFixedBufferRecorder.reset(); - if (!fixedBuffer) + if (fixedBuffer) { - return; - } - - RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer)); - s->mFixedBufferRecorder = recordToFixedBuffer; - addRecorder(recordToFixedBuffer); + RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer)); + s->mFixedBufferRecorder = recordToFixedBuffer; + addRecorder(recordToFixedBuffer); + } } std::string logFileName() @@ -989,8 +1026,9 @@ namespace LLError namespace { - void addEscapedMessage(std::ostream& out, const std::string& message) + std::string escapedMessageLines(const std::string& message) { + std::ostringstream out; size_t written_out = 0; size_t all_content = message.length(); size_t escape_char_index; // always relative to start of message @@ -1026,13 +1064,16 @@ namespace // write whatever was left out << message.substr(written_out, std::string::npos); } + return out.str(); } - void writeToRecorders(const LLError::CallSite& site, const std::string& escaped_message, bool show_location = true, bool show_time = true, bool show_tags = true, bool show_level = true, bool show_function = true) + void writeToRecorders(const LLError::CallSite& site, const std::string& message) { LLError::ELevel level = site.mLevel; LLError::SettingsConfigPtr s = LLError::Settings::getInstance()->getSettingsConfig(); - + + std::string escaped_message; + for (Recorders::const_iterator i = s->mRecorders.begin(); i != s->mRecorders.end(); ++i) @@ -1052,7 +1093,7 @@ namespace } message_stream << " "; - if (show_level && r->wantsLevel()) + if (r->wantsLevel()) { message_stream << site.mLevelString; } @@ -1064,19 +1105,30 @@ namespace } message_stream << " "; - if (r->wantsLocation() || level == LLError::LEVEL_ERROR || s->mPrintLocation) + if (r->wantsLocation() || level == LLError::LEVEL_ERROR) { message_stream << site.mLocationString; } message_stream << " "; - if (show_function && r->wantsFunctionName()) + if (r->wantsFunctionName()) { message_stream << site.mFunctionString; } message_stream << " : "; - message_stream << escaped_message; + if (r->wantsMultiline()) + { + message_stream << message; + } + else + { + if (escaped_message.empty()) + { + escaped_message = escapedMessageLines(message); + } + message_stream << escaped_message; + } r->recordMessage(level, message_stream.str()); } @@ -1320,10 +1372,11 @@ namespace LLError delete out; } - std::ostringstream message_stream; if (site.mPrintOnce) { + std::ostringstream message_stream; + std::map<std::string, unsigned int>::iterator messageIter = s->mUniqueLogMessages.find(message); if (messageIter != s->mUniqueLogMessages.end()) { @@ -1343,15 +1396,19 @@ namespace LLError message_stream << "ONCE: "; s->mUniqueLogMessages[message] = 1; } + message_stream << message; + message = message_stream.str(); } - addEscapedMessage(message_stream, message); + writeToRecorders(site, message); - writeToRecorders(site, message_stream.str()); - - if (site.mLevel == LEVEL_ERROR && s->mCrashFunction) + if (site.mLevel == LEVEL_ERROR) { - s->mCrashFunction(message_stream.str()); + g->mFatalMessage = message; + if (s->mCrashFunction) + { + s->mCrashFunction(message); + } } } } @@ -1656,3 +1713,4 @@ bool debugLoggingEnabled(const std::string& tag) } + diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 1730f0c640..276d22fc36 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -106,6 +106,9 @@ namespace LLError LL_COMMON_API FatalFunction getFatalFunction(); // Retrieve the previously-set FatalFunction + LL_COMMON_API std::string getFatalMessage(); + // Retrieve the message last passed to FatalFunction, if any + /// temporarily override the FatalFunction for the duration of a /// particular scope, e.g. for unit tests class LL_COMMON_API OverrideFatalFunction @@ -151,13 +154,22 @@ namespace LLError bool wantsLevel(); bool wantsLocation(); bool wantsFunctionName(); + bool wantsMultiline(); + + void showTime(bool show); + void showTags(bool show); + void showLevel(bool show); + void showLocation(bool show); + void showFunctionName(bool show); + void showMultiline(bool show); protected: - bool mWantsTime, - mWantsTags, - mWantsLevel, - mWantsLocation, - mWantsFunctionName; + bool mWantsTime; + bool mWantsTags; + bool mWantsLevel; + bool mWantsLocation; + bool mWantsFunctionName; + bool mWantsMultiline; }; typedef boost::shared_ptr<Recorder> RecorderPtr; diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index dce97b5411..eedd8c92b5 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -545,10 +545,8 @@ bool LLEventStream::post(const LLSD& event) *****************************************************************************/ bool LLEventMailDrop::post(const LLSD& event) { - bool posted = false; - - if (!mSignal->empty()) - posted = LLEventStream::post(event); + // forward the call to our base class + bool posted = LLEventStream::post(event); if (!posted) { // if the event was not handled we will save it for later so that it can @@ -564,16 +562,25 @@ LLBoundListener LLEventMailDrop::listen_impl(const std::string& name, const NameList& after, const NameList& before) { - if (!mEventHistory.empty()) + // Before actually connecting this listener for subsequent post() calls, + // first feed each of the saved events, in order, to the new listener. + // Remove any that this listener consumes -- Effective STL, Item 9. + for (auto hi(mEventHistory.begin()), hend(mEventHistory.end()); hi != hend; ) { - if (listener(mEventHistory.front())) + if (listener(*hi)) { - mEventHistory.pop_front(); + // new listener consumed this event, erase it + hi = mEventHistory.erase(hi); + } + else + { + // listener did not consume this event, just move along + ++hi; } } + // let base class perform the actual connection return LLEventStream::listen_impl(name, listener, after, before); - } diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 1d51c660ed..5d60c63810 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -650,15 +650,21 @@ public: * LLEventMailDrop *****************************************************************************/ /** - * LLEventMailDrop is a specialization of LLEventStream. Events are posted normally, - * however if no listeners return that they have handled the event it is placed in - * a queue. Subsequent attaching listeners will receive stored events from the queue - * until a listener indicates that the event has been handled. In order to receive - * multiple events from a mail drop the listener must disconnect and reconnect. + * LLEventMailDrop is a specialization of LLEventStream. Events are posted + * normally, however if no listener returns that it has handled the event + * (returns true), it is placed in a queue. Subsequent attaching listeners + * will receive stored events from the queue until some listener indicates + * that the event has been handled. + * + * LLEventMailDrop completely decouples the timing of post() calls from + * listen() calls: every event posted to an LLEventMailDrop is eventually seen + * by all listeners, until some listener consumes it. The caveat is that each + * event *must* eventually reach a listener that will consume it, else the + * queue will grow to arbitrary length. * * @NOTE: When using an LLEventMailDrop (or LLEventQueue) with a LLEventTimeout or - * LLEventFilter attaching the filter downstream using Timeout's constructor will - * cause the MailDrop to discharge any of it's stored events. The timeout should + * LLEventFilter attaching the filter downstream, using Timeout's constructor will + * cause the MailDrop to discharge any of its stored events. The timeout should * instead be connected upstream using its listen() method. * See llcoro::suspendUntilEventOnWithTimeout() for an example. */ diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index f1f4226c40..7f5b9b4ac2 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -2115,6 +2115,9 @@ namespace LLInitParam typedef typename super_t::iterator iterator; typedef typename super_t::const_iterator const_iterator; + using super_t::operator(); + using super_t::operator const container_t&; + explicit Multiple(const char* name = "") : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount) {} diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index c87d2a3e58..cf8f8cc6a5 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -47,9 +47,9 @@ class LLLeapImpl: public LLLeap LOG_CLASS(LLLeap); public: // Called only by LLLeap::create() - LLLeapImpl(const std::string& desc, const std::vector<std::string>& plugin): + LLLeapImpl(const LLProcess::Params& cparams): // We might reassign mDesc in the constructor body if it's empty here. - mDesc(desc), + mDesc(cparams.desc), // We expect multiple LLLeapImpl instances. Definitely tweak // mDonePump's name for uniqueness. mDonePump("LLLeap", true), @@ -67,17 +67,17 @@ public: // this class or method name. mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2))) { - // Rule out empty vector - if (plugin.empty()) + // Rule out unpopulated Params block + if (! cparams.executable.isProvided()) { LLTHROW(Error("no plugin command")); } // Don't leave desc empty either, but in this case, if we weren't // given one, we'll fake one. - if (desc.empty()) + if (mDesc.empty()) { - mDesc = LLProcess::basename(plugin[0]); + mDesc = LLProcess::basename(cparams.executable); // how about a toLower() variant that returns the transformed string?! std::string desclower(mDesc); LLStringUtil::toLower(desclower); @@ -87,9 +87,9 @@ public: // notice Python specially: we provide Python LLSD serialization // support, so there's a pretty good reason to implement plugins // in that language. - if (plugin.size() >= 2 && (desclower == "python" || desclower == "python.exe")) + if (cparams.args.size() && (desclower == "python" || desclower == "python.exe")) { - mDesc = LLProcess::basename(plugin[1]); + mDesc = LLProcess::basename(cparams.args()[0]); } } @@ -97,14 +97,10 @@ public: mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1)); // Okay, launch child. - LLProcess::Params params; + // Get a modifiable copy of params block to set files and postend. + LLProcess::Params params(cparams); + // copy our deduced mDesc back into the params block params.desc = mDesc; - std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end()); - params.executable = *pi++; - for ( ; pi != pend; ++pi) - { - params.args.add(*pi); - } params.files.add(LLProcess::FileParam("pipe")); // stdin params.files.add(LLProcess::FileParam("pipe")); // stdout params.files.add(LLProcess::FileParam("pipe")); // stderr @@ -429,17 +425,17 @@ private: boost::scoped_ptr<LLLeapListener> mListener; }; -// This must follow the declaration of LLLeapImpl, so it may as well be last. -LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc) +// These must follow the declaration of LLLeapImpl, so they may as well be last. +LLLeap* LLLeap::create(const LLProcess::Params& params, bool exc) { // If caller is willing to permit exceptions, just instantiate. if (exc) - return new LLLeapImpl(desc, plugin); + return new LLLeapImpl(params); // Caller insists on suppressing LLLeap::Error. Very well, catch it. try { - return new LLLeapImpl(desc, plugin); + return new LLLeapImpl(params); } catch (const LLLeap::Error&) { @@ -447,6 +443,23 @@ LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& } } +LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc) +{ + LLProcess::Params params; + params.desc = desc; + std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end()); + // could validate here, but let's rely on LLLeapImpl's constructor + if (pi != pend) + { + params.executable = *pi++; + } + for ( ; pi != pend; ++pi) + { + params.args.add(*pi); + } + return create(params, exc); +} + LLLeap* LLLeap::create(const std::string& desc, const std::string& plugin, bool exc) { // Use LLStringUtil::getTokens() to parse the command line diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h index 8aac8a64c5..7cecdf2f8f 100644 --- a/indra/llcommon/llleap.h +++ b/indra/llcommon/llleap.h @@ -14,6 +14,7 @@ #include "llinstancetracker.h" #include "llexception.h" +#include "llprocess.h" #include <string> #include <vector> @@ -62,6 +63,19 @@ public: bool exc=true); /** + * Pass an LLProcess::Params instance to specify desc, executable, args et al. + * + * Note that files and postend are set implicitly; any values you set in + * those fields will be disregarded. + * + * Pass exc=false to suppress LLLeap::Error exception. Obviously in that + * case the caller cannot discover the nature of the error, merely that an + * error of some kind occurred (because create() returned NULL). Either + * way, the error is logged. + */ + static LLLeap* create(const LLProcess::Params& params, bool exc=true); + + /** * Exception thrown for invalid create() arguments, e.g. no plugin * program. This is more resiliant than an LL_ERRS failure, because the * string(s) passed to create() might come from an external source. This diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index 2879038c36..ef015fdce4 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -198,6 +198,8 @@ #define LL_TO_STRING_HELPER(x) #x #define LL_TO_STRING(x) LL_TO_STRING_HELPER(x) +#define LL_TO_WSTRING_HELPER(x) L#x +#define LL_TO_WSTRING(x) LL_TO_WSTRING_HELPER(x) #define LL_FILE_LINENO_MSG(msg) __FILE__ "(" LL_TO_STRING(__LINE__) ") : " msg #define LL_GLUE_IMPL(x, y) x##y #define LL_GLUE_TOKENS(x, y) LL_GLUE_IMPL(x, y) diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index a5a90d7297..38dd198ad3 100644 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -30,7 +30,6 @@ #define LL_STRINGIZE_H #include <sstream> -#include <boost/phoenix/phoenix.hpp> #include <llstring.h> /** @@ -53,12 +52,7 @@ std::basic_string<CHARTYPE> gstringize(const T& item) */ inline std::string stringize(const std::wstring& item) { - LL_WARNS() << "WARNING: Possible narrowing" << LL_ENDL; - - std::string s; - - s = wstring_to_utf8str(item); - return gstringize<char>(s); + return wstring_to_utf8str(item); } /** @@ -76,7 +70,10 @@ std::string stringize(const T& item) */ inline std::wstring wstringize(const std::string& item) { - return gstringize<wchar_t>(item.c_str()); + // utf8str_to_wstring() returns LLWString, which isn't necessarily the + // same as std::wstring + LLWString s(utf8str_to_wstring(item)); + return std::wstring(s.begin(), s.end()); } /** @@ -91,10 +88,10 @@ std::wstring wstringize(const T& item) /** * stringize_f(functor) */ -template <typename Functor> -std::string stringize_f(Functor const & f) +template <typename CHARTYPE, typename Functor> +std::basic_string<CHARTYPE> stringize_f(Functor const & f) { - std::ostringstream out; + std::basic_ostringstream<CHARTYPE> out; f(out); return out.str(); } @@ -108,31 +105,37 @@ std::string stringize_f(Functor const & f) * return out.str(); * @endcode */ -#define STRINGIZE(EXPRESSION) (stringize_f(boost::phoenix::placeholders::arg1 << EXPRESSION)) +#define STRINGIZE(EXPRESSION) (stringize_f<char>([&](std::ostream& out){ out << EXPRESSION; })) +/** + * WSTRINGIZE() is the wstring equivalent of STRINGIZE() + */ +#define WSTRINGIZE(EXPRESSION) (stringize_f<wchar_t>([&](std::wostream& out){ out << EXPRESSION; })) /** * destringize(str) * defined for symmetry with stringize - * *NOTE - this has distinct behavior from boost::lexical_cast<T> regarding + * @NOTE - this has distinct behavior from boost::lexical_cast<T> regarding * leading/trailing whitespace and handling of bad_lexical_cast exceptions + * @NOTE - no need for dewstringize(), since passing std::wstring will Do The + * Right Thing */ -template <typename T> -T destringize(std::string const & str) +template <typename T, typename CHARTYPE> +T destringize(std::basic_string<CHARTYPE> const & str) { - T val; - std::istringstream in(str); - in >> val; + T val; + std::basic_istringstream<CHARTYPE> in(str); + in >> val; return val; } /** * destringize_f(str, functor) */ -template <typename Functor> -void destringize_f(std::string const & str, Functor const & f) +template <typename CHARTYPE, typename Functor> +void destringize_f(std::basic_string<CHARTYPE> const & str, Functor const & f) { - std::istringstream in(str); + std::basic_istringstream<CHARTYPE> in(str); f(in); } @@ -143,8 +146,11 @@ void destringize_f(std::string const & str, Functor const & f) * std::istringstream in(str); * in >> item1 >> item2 >> item3 ... ; * @endcode + * @NOTE - once we get generic lambdas, we shouldn't need DEWSTRINGIZE() any + * more since DESTRINGIZE() should do the right thing with a std::wstring. But + * until then, the lambda we pass must accept the right std::basic_istream. */ -#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), (boost::phoenix::placeholders::arg1 >> EXPRESSION))) - +#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), [&](std::istream& in){in >> EXPRESSION;})) +#define DEWSTRINGIZE(STR, EXPRESSION) (destringize_f((STR), [&](std::wistream& in){in >> EXPRESSION;})) #endif /* ! defined(LL_STRINGIZE_H) */ diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index ce0dbce075..8e1f4c14ac 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -78,8 +78,12 @@ namespace tut class TestRecorder : public LLError::Recorder { public: - TestRecorder() { mWantsTime = false; mWantsTags = true; } - virtual ~TestRecorder() { } + TestRecorder() + { + showTime(false); + } + virtual ~TestRecorder() + {} virtual void recordMessage(LLError::ELevel level, const std::string& message) @@ -90,8 +94,6 @@ namespace tut int countMessages() { return (int) mMessages.size(); } void clearMessages() { mMessages.clear(); } - void setWantsTime(bool t) { mWantsTime = t; } - std::string message(int n) { std::ostringstream test_name; @@ -139,9 +141,14 @@ namespace tut } void setWantsTime(bool t) - { - boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->setWantsTime(t); - } + { + boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->showTime(t); + } + + void setWantsMultiline(bool t) + { + boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->showMultiline(t); + } std::string message(int n) { @@ -378,27 +385,6 @@ namespace } } -namespace tut -{ - template<> template<> - void ErrorTestObject::test<5>() - // file and line information in log messages - { - std::string location = writeReturningLocation(); - // expecting default to not print location information - - LLError::setPrintLocation(true); - writeReturningLocation(); - - LLError::setPrintLocation(false); - writeReturningLocation(); - - ensure_message_does_not_contain(0, location); - ensure_message_field_equals(1, LOCATION_FIELD, location); - ensure_message_does_not_contain(2, location); - } -} - /* The following helper functions and class members all log a simple message from some particular function scope. Each function takes a bool argument that indicates if it should log its own name or not (in the manner that @@ -512,6 +498,39 @@ namespace } } +namespace +{ + void writeMsgNeedsEscaping() + { + LL_DEBUGS("WriteTag") << "backslash\\" << LL_ENDL; + LL_INFOS("WriteTag") << "newline\nafternewline" << LL_ENDL; + LL_WARNS("WriteTag") << "return\rafterreturn" << LL_ENDL; + + LL_DEBUGS("WriteTag") << "backslash\\backslash\\" << LL_ENDL; + LL_INFOS("WriteTag") << "backslash\\newline\nanothernewline\nafternewline" << LL_ENDL; + LL_WARNS("WriteTag") << "backslash\\returnnewline\r\n\\afterbackslash" << LL_ENDL; + } +}; + +namespace tut +{ + template<> template<> + void ErrorTestObject::test<5>() + // backslash, return, and newline are not escaped with backslashes + { + LLError::setDefaultLevel(LLError::LEVEL_DEBUG); + setWantsMultiline(true); + writeMsgNeedsEscaping(); // but should not be now + ensure_message_field_equals(0, MSG_FIELD, "backslash\\"); + ensure_message_field_equals(1, MSG_FIELD, "newline\nafternewline"); + ensure_message_field_equals(2, MSG_FIELD, "return\rafterreturn"); + ensure_message_field_equals(3, MSG_FIELD, "backslash\\backslash\\"); + ensure_message_field_equals(4, MSG_FIELD, "backslash\\newline\nanothernewline\nafternewline"); + ensure_message_field_equals(5, MSG_FIELD, "backslash\\returnnewline\r\n\\afterbackslash"); + ensure_message_count(6); + } +} + namespace tut { template<> template<> @@ -583,7 +602,6 @@ namespace tut // special handling of LL_ERRS() calls void ErrorTestObject::test<8>() { - LLError::setPrintLocation(false); std::string location = errorReturningLocation(); ensure_message_field_equals(0, LOCATION_FIELD, location); @@ -630,15 +648,15 @@ namespace tut // output order void ErrorTestObject::test<10>() { - LLError::setPrintLocation(true); LLError::setTimeFunction(roswell); setWantsTime(true); + std::string location, function; writeReturningLocationAndFunction(location, function); ensure_equals("order is time level tags location function message", - message(0), + message(0), roswell() + " INFO " + "# " /* no tag */ + location + " " + function + " : " + "apple"); } @@ -658,7 +676,7 @@ namespace tut LLError::setTimeFunction(roswell); LLError::RecorderPtr anotherRecorder(new TestRecorder()); - boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->setWantsTime(true); + boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->showTime(true); LLError::addRecorder(anotherRecorder); LL_INFOS() << "baz" << LL_ENDL; @@ -835,20 +853,6 @@ namespace tut } } -namespace -{ - void writeMsgNeedsEscaping() - { - LL_DEBUGS("WriteTag") << "backslash\\" << LL_ENDL; - LL_INFOS("WriteTag") << "newline\nafternewline" << LL_ENDL; - LL_WARNS("WriteTag") << "return\rafterreturn" << LL_ENDL; - - LL_DEBUGS("WriteTag") << "backslash\\backslash\\" << LL_ENDL; - LL_INFOS("WriteTag") << "backslash\\newline\nanothernewline\nafternewline" << LL_ENDL; - LL_WARNS("WriteTag") << "backslash\\returnnewline\r\n\\afterbackslash" << LL_ENDL; - } -}; - namespace tut { template<> template<> diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index 9a4bbbd630..08fbf19b1c 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -109,6 +109,12 @@ public: mMessages.push_back(message); } + friend inline + std::ostream& operator<<(std::ostream& out, const CaptureLogRecorder& log) + { + return log.streamto(out); + } + /// Don't assume the message we want is necessarily the LAST log message /// emitted by the underlying code; search backwards through all messages /// for the sought string. @@ -126,7 +132,7 @@ public: throw tut::failure(STRINGIZE("failed to find '" << search << "' in captured log messages:\n" - << boost::ref(*this))); + << *this)); } std::ostream& streamto(std::ostream& out) const @@ -200,10 +206,4 @@ private: LLError::RecorderPtr mRecorder; }; -inline -std::ostream& operator<<(std::ostream& out, const CaptureLogRecorder& log) -{ - return log.streamto(out); -} - #endif /* ! defined(LL_WRAPLLERRS_H) */ |