summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/indra_constants.h11
-rw-r--r--indra/llcommon/llapp.cpp2
-rw-r--r--indra/llcommon/llapr.cpp2
-rw-r--r--indra/llcommon/llcleanup.cpp7
-rw-r--r--indra/llcommon/llcleanup.h13
-rw-r--r--indra/llcommon/llcommon.cpp2
-rw-r--r--indra/llcommon/llcoros.cpp22
-rw-r--r--indra/llcommon/llerror.cpp279
-rw-r--r--indra/llcommon/llerror.h75
-rw-r--r--indra/llcommon/llerrorcontrol.h101
-rw-r--r--indra/llcommon/llexception.cpp25
-rw-r--r--indra/llcommon/llexception.h10
-rw-r--r--indra/llcommon/llkeybind.cpp395
-rw-r--r--indra/llcommon/llkeybind.h106
-rw-r--r--indra/llcommon/llleap.cpp31
-rw-r--r--indra/llcommon/llsingleton.cpp95
-rw-r--r--indra/llcommon/llsingleton.h84
-rw-r--r--indra/llcommon/llsys.cpp28
-rw-r--r--indra/llcommon/lluriparser.cpp87
-rw-r--r--indra/llcommon/lluriparser.h1
-rw-r--r--indra/llcommon/tests/llerror_test.cpp79
-rw-r--r--indra/llcommon/tests/wrapllerrs.h40
23 files changed, 987 insertions, 510 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 79aa18eaec..dd266630ea 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -72,6 +72,7 @@ set(llcommon_SOURCE_FILES
llinitparam.cpp
llinitdestroyclass.cpp
llinstancetracker.cpp
+ llkeybind.cpp
llleap.cpp
llleaplistener.cpp
llliveappconfig.cpp
@@ -183,6 +184,7 @@ set(llcommon_HEADER_FILES
llinitdestroyclass.h
llinitparam.h
llinstancetracker.h
+ llkeybind.h
llkeythrottle.h
llleap.h
llleaplistener.h
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
index e7b0e0ef8e..10b98f49aa 100644
--- a/indra/llcommon/indra_constants.h
+++ b/indra/llcommon/indra_constants.h
@@ -54,6 +54,17 @@ enum ETerrainBrushType
E_LANDBRUSH_INVALID = 6
};
+enum EMouseClickType{
+ CLICK_NONE = -1,
+ CLICK_LEFT = 0,
+ CLICK_MIDDLE,
+ CLICK_RIGHT,
+ CLICK_BUTTON4,
+ CLICK_BUTTON5,
+ CLICK_DOUBLELEFT,
+ CLICK_COUNT // 'size', CLICK_NONE does not counts
+};
+
// keys
// Bit masks for various keyboard modifier keys.
const MASK MASK_NONE = 0x0000;
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 252d08c830..6064a843ae 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -180,7 +180,7 @@ LLApp::~LLApp()
if(mExceptionHandler != 0) delete mExceptionHandler;
- SUBSYSTEM_CLEANUP(LLCommon);
+ SUBSYSTEM_CLEANUP_DBG(LLCommon);
}
// static
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index 984e90f376..db94765871 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -68,7 +68,7 @@ void ll_cleanup_apr()
{
gAPRInitialized = false;
- LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
+ LL_DEBUGS("APR") << "Cleaning up APR" << LL_ENDL;
LLThreadLocalPointerBase::destroyAllThreadLocalStorage();
diff --git a/indra/llcommon/llcleanup.cpp b/indra/llcommon/llcleanup.cpp
index c5283507bf..1f34c2036a 100644
--- a/indra/llcommon/llcleanup.cpp
+++ b/indra/llcommon/llcleanup.cpp
@@ -20,10 +20,13 @@
#include "llerror.h"
#include "llerrorcontrol.h"
-void log_subsystem_cleanup(const char* file, int line, const char* function,
+void log_subsystem_cleanup(LLError::ELevel level,
+ const char* file,
+ int line,
+ const char* function,
const char* classname)
{
- LL_INFOS("Cleanup") << LLError::abbreviateFile(file) << "(" << line << "): "
+ LL_VLOGS(level, "Cleanup") << LLError::abbreviateFile(file) << "(" << line << "): "
<< "calling " << classname << "::cleanupClass() in "
<< function << LL_ENDL;
}
diff --git a/indra/llcommon/llcleanup.h b/indra/llcommon/llcleanup.h
index a319171b5f..0f567ed5f6 100644
--- a/indra/llcommon/llcleanup.h
+++ b/indra/llcommon/llcleanup.h
@@ -21,13 +21,22 @@
// shutdown schemes.
#define SUBSYSTEM_CLEANUP(CLASSNAME) \
do { \
- log_subsystem_cleanup(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, #CLASSNAME); \
+ log_subsystem_cleanup(LLError::LEVEL_INFO, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, #CLASSNAME); \
+ CLASSNAME::cleanupClass(); \
+ } while (0)
+
+#define SUBSYSTEM_CLEANUP_DBG(CLASSNAME) \
+ do { \
+ log_subsystem_cleanup(LLError::LEVEL_DEBUG, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, #CLASSNAME); \
CLASSNAME::cleanupClass(); \
} while (0)
// Use ancient do { ... } while (0) macro trick to permit a block of
// statements with the same syntax as a single statement.
-void log_subsystem_cleanup(const char* file, int line, const char* function,
+void log_subsystem_cleanup(LLError::ELevel level,
+ const char* file,
+ int line,
+ const char* function,
const char* classname);
#endif /* ! defined(LL_LLCLEANUP_H) */
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
index 2d665c611b..96be913d17 100644
--- a/indra/llcommon/llcommon.cpp
+++ b/indra/llcommon/llcommon.cpp
@@ -63,7 +63,7 @@ void LLCommon::cleanupClass()
sMasterThreadRecorder = NULL;
LLTrace::set_master_thread_recorder(NULL);
LLThreadSafeRefCount::cleanupThreadSafeRefCount();
- SUBSYSTEM_CLEANUP(LLTimer);
+ SUBSYSTEM_CLEANUP_DBG(LLTimer);
if (sAprInitialized)
{
ll_cleanup_apr();
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 262929006d..23419a52a7 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -56,10 +56,6 @@
#include "stringize.h"
#include "llexception.h"
-#if LL_WINDOWS
-#include <excpt.h>
-#endif
-
// static
LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
{
@@ -253,29 +249,13 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
#if LL_WINDOWS
-static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
-
-U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
-{
- if (code == STATUS_MSC_EXCEPTION)
- {
- // C++ exception, go on
- return EXCEPTION_CONTINUE_SEARCH;
- }
- else
- {
- // handle it
- return EXCEPTION_EXECUTE_HANDLER;
- }
-}
-
void LLCoros::winlevel(const callable_t& callable)
{
__try
{
callable();
}
- __except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
+ __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation()))
{
// convert to C++ styled exception
// Note: it might be better to use _se_set_translator
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 922c003443..8355df9045 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -25,7 +25,6 @@
* $/LicenseInfo$
*/
-#define _LLERROR_CPP_
#include "linden_common.h"
#include "llerror.h"
@@ -333,12 +332,9 @@ namespace LLError
}
// huh, that's odd, we should see one or the other prefix -- but don't
// try to log unless logging is already initialized
- if (is_available())
- {
- // in Python, " or ".join(vector) -- but in C++, a PITB
- LL_DEBUGS() << "Did not see 'class' or 'struct' prefix on '"
- << name << "'" << LL_ENDL;
- }
+ // in Python, " or ".join(vector) -- but in C++, a PITB
+ LL_DEBUGS() << "Did not see 'class' or 'struct' prefix on '"
+ << name << "'" << LL_ENDL;
return name;
#else // neither GCC nor Visual Studio
@@ -439,12 +435,13 @@ namespace
typedef std::vector<LLError::RecorderPtr> Recorders;
typedef std::vector<LLError::CallSite*> CallSiteVector;
- class Globals : public LLSingleton<Globals>
+ class Globals
{
- LLSINGLETON(Globals);
+ public:
+ static Globals* getInstance();
+ protected:
+ Globals();
public:
- std::ostringstream messageStream;
- bool messageStreamInUse;
std::string mFatalMessage;
void addCallSite(LLError::CallSite&);
@@ -454,12 +451,17 @@ namespace
CallSiteVector callSites;
};
- Globals::Globals()
- : messageStream(),
- messageStreamInUse(false),
- callSites()
- {
- }
+ Globals::Globals() {}
+
+ Globals* Globals::getInstance()
+ {
+ // According to C++11 Function-Local Initialization
+ // of static variables is supposed to be thread safe
+ // without risk of deadlocks.
+ static Globals inst;
+
+ return &inst;
+ }
void Globals::addCallSite(LLError::CallSite& site)
{
@@ -500,7 +502,7 @@ namespace LLError
LevelMap mTagLevelMap;
std::map<std::string, unsigned int> mUniqueLogMessages;
- LLError::FatalHook mFatalHook;
+ LLError::FatalFunction mCrashFunction;
LLError::TimeFunction mTimeFunction;
Recorders mRecorders;
@@ -513,14 +515,17 @@ namespace LLError
typedef LLPointer<SettingsConfig> SettingsConfigPtr;
- class Settings : public LLSingleton<Settings>
+ class Settings
{
- LLSINGLETON(Settings);
+ public:
+ static Settings* getInstance();
+ protected:
+ Settings();
public:
SettingsConfigPtr getSettingsConfig();
void reset();
- SettingsStoragePtr saveAndReset();
+ SettingsStoragePtr saveAndReset();
void restore(SettingsStoragePtr pSettingsStorage);
private:
@@ -537,7 +542,7 @@ namespace LLError
mFileLevelMap(),
mTagLevelMap(),
mUniqueLogMessages(),
- mFatalHook(NULL),
+ mCrashFunction([](const std::string&){}),
mTimeFunction(NULL),
mRecorders(),
mShouldLogCallCounter(0)
@@ -554,6 +559,16 @@ namespace LLError
{
}
+ Settings* Settings::getInstance()
+ {
+ // According to C++11 Function-Local Initialization
+ // of static variables is supposed to be thread safe
+ // without risk of deadlocks.
+ static Settings inst;
+
+ return &inst;
+ }
+
SettingsConfigPtr Settings::getSettingsConfig()
{
return mSettingsConfig;
@@ -578,11 +593,6 @@ namespace LLError
SettingsConfigPtr newSettingsConfig(dynamic_cast<SettingsConfig *>(pSettingsStorage.get()));
mSettingsConfig = newSettingsConfig;
}
-
- bool is_available()
- {
- return Settings::instanceExists() && Globals::instanceExists();
- }
}
namespace LLError
@@ -709,9 +719,8 @@ namespace
LLError::Settings::getInstance()->reset();
LLError::setDefaultLevel(LLError::LEVEL_INFO);
-
- LLError::setAlwaysFlush(true);
- LLError::setEnabledLogTypesMask(0xFFFFFFFF);
+ LLError::setAlwaysFlush(true);
+ LLError::setEnabledLogTypesMask(0xFFFFFFFF);
LLError::setTimeFunction(LLError::utcTime);
// log_to_stderr is only false in the unit and integration tests to keep builds quieter
@@ -746,20 +755,16 @@ namespace LLError
commonInit(user_dir, app_dir, log_to_stderr);
}
- void setFatalHook(const FatalHook& fatal_hook)
+ void setFatalFunction(const FatalFunction& f)
{
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- LL_DEBUGS("FatalHook") << "set fatal hook to " << (fatal_hook ? "non-null" : "null")
- << " was " << (s->mFatalHook ? "non-null" : "null")
- << LL_ENDL;
- s->mFatalHook = fatal_hook;
- }
-
- FatalHook getFatalHook()
+ s->mCrashFunction = f;
+ }
+
+ FatalFunction getFatalFunction()
{
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- LL_DEBUGS("FatalHook") << "read fatal hook was " << (s->mFatalHook ? "non-null" : "null") << LL_ENDL;
- return s->mFatalHook;
+ return s->mCrashFunction;
}
std::string getFatalMessage()
@@ -1033,7 +1038,7 @@ namespace LLError
std::pair<boost::shared_ptr<RECORDER>, Recorders::iterator>
findRecorderPos()
{
- SettingsConfigPtr s = Settings::instance().getSettingsConfig();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
// Since we promise to return an iterator, use a classic iterator
// loop.
auto end{s->mRecorders.end()};
@@ -1076,7 +1081,7 @@ namespace LLError
auto found = findRecorderPos<RECORDER>();
if (found.first)
{
- SettingsConfigPtr s = Settings::instance().getSettingsConfig();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
s->mRecorders.erase(found.second);
}
return bool(found.first);
@@ -1312,14 +1317,6 @@ namespace LLError
return false;
}
- // If we hit a logging request very late during shutdown processing,
- // when either of the relevant LLSingletons has already been deleted,
- // DO NOT resurrect them.
- if (Settings::wasDeleted() || Globals::wasDeleted())
- {
- return false;
- }
-
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
s->mShouldLogCallCounter++;
@@ -1355,27 +1352,7 @@ namespace LLError
}
- std::ostringstream* Log::out()
- {
- LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5);
- // If we hit a logging request very late during shutdown processing,
- // when either of the relevant LLSingletons has already been deleted,
- // DO NOT resurrect them.
- if (lock.isLocked() && ! (Settings::wasDeleted() || Globals::wasDeleted()))
- {
- Globals* g = Globals::getInstance();
-
- if (!g->messageStreamInUse)
- {
- g->messageStreamInUse = true;
- return &g->messageStream;
- }
- }
-
- return new std::ostringstream;
- }
-
- void Log::flush(std::ostringstream* out, char* message)
+ void Log::flush(const std::ostringstream& out, const CallSite& site)
{
LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5);
if (!lock.isLocked())
@@ -1383,73 +1360,14 @@ namespace LLError
return;
}
- // If we hit a logging request very late during shutdown processing,
- // when either of the relevant LLSingletons has already been deleted,
- // DO NOT resurrect them.
- if (Settings::wasDeleted() || Globals::wasDeleted())
- {
- return;
- }
-
- if(strlen(out->str().c_str()) < 128)
- {
- strcpy(message, out->str().c_str());
- }
- else
- {
- strncpy(message, out->str().c_str(), 127);
- message[127] = '\0' ;
- }
-
- Globals* g = Globals::getInstance();
- if (out == &g->messageStream)
- {
- g->messageStream.clear();
- g->messageStream.str("");
- g->messageStreamInUse = false;
- }
- else
- {
- delete out;
- }
- return ;
- }
-
- ErrFatalHookResult Log::flush(std::ostringstream* out, const CallSite& site)
- {
- LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5);
- if (!lock.isLocked())
- {
- return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal
- }
-
- // If we hit a logging request very late during shutdown processing,
- // when either of the relevant LLSingletons has already been deleted,
- // DO NOT resurrect them.
- if (Settings::wasDeleted() || Globals::wasDeleted())
- {
- return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal
- }
-
Globals* g = Globals::getInstance();
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- std::string message = out->str();
- if (out == &g->messageStream)
- {
- g->messageStream.clear();
- g->messageStream.str("");
- g->messageStreamInUse = false;
- }
- else
- {
- delete out;
- }
-
+ std::string message = out.str();
if (site.mPrintOnce)
{
- std::ostringstream message_stream;
+ std::ostringstream message_stream;
std::map<std::string, unsigned int>::iterator messageIter = s->mUniqueLogMessages.find(message);
if (messageIter != s->mUniqueLogMessages.end())
@@ -1462,7 +1380,7 @@ namespace LLError
}
else
{
- return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal
+ return;
}
}
else
@@ -1470,27 +1388,17 @@ namespace LLError
message_stream << "ONCE: ";
s->mUniqueLogMessages[message] = 1;
}
- message_stream << message;
- message = message_stream.str();
+ message_stream << message;
+ message = message_stream.str();
}
writeToRecorders(site, message);
if (site.mLevel == LEVEL_ERROR)
{
- if (s->mFatalHook)
- {
- return s->mFatalHook(message);
- }
- else
- {
- return ERR_CRASH; // calling macro should crash
- }
+ g->mFatalMessage = message;
+ s->mCrashFunction(message);
}
- else
- {
- return ERR_DO_NOT_CRASH; // not ERROR, so do not crash
- }
}
}
@@ -1569,33 +1477,7 @@ namespace LLError
namespace LLError
{
- char** LLCallStacks::sBuffer = NULL ;
- S32 LLCallStacks::sIndex = 0 ;
-
- //static
- void LLCallStacks::allocateStackBuffer()
- {
- if(sBuffer == NULL)
- {
- sBuffer = new char*[512] ;
- sBuffer[0] = new char[512 * 128] ;
- for(S32 i = 1 ; i < 512 ; i++)
- {
- sBuffer[i] = sBuffer[i-1] + 128 ;
- }
- sIndex = 0 ;
- }
- }
-
- void LLCallStacks::freeStackBuffer()
- {
- if(sBuffer != NULL)
- {
- delete [] sBuffer[0] ;
- delete [] sBuffer ;
- sBuffer = NULL ;
- }
- }
+ LLCallStacks::StringVector LLCallStacks::sBuffer ;
//static
void LLCallStacks::push(const char* function, const int line)
@@ -1606,33 +1488,24 @@ namespace LLError
return;
}
- if(sBuffer == NULL)
- {
- allocateStackBuffer();
- }
-
- if(sIndex > 511)
+ if(sBuffer.size() > 511)
{
clear() ;
}
- strcpy(sBuffer[sIndex], function) ;
- sprintf(sBuffer[sIndex] + strlen(function), " line: %d ", line) ;
- sIndex++ ;
-
- return ;
+ std::ostringstream out;
+ insert(out, function, line);
+ sBuffer.push_back(out.str());
}
//static
- std::ostringstream* LLCallStacks::insert(const char* function, const int line)
+ void LLCallStacks::insert(std::ostream& out, const char* function, const int line)
{
- std::ostringstream* _out = LLError::Log::out();
- *_out << function << " line " << line << " " ;
- return _out ;
+ out << function << " line " << line << " " ;
}
//static
- void LLCallStacks::end(std::ostringstream* _out)
+ void LLCallStacks::end(const std::ostringstream& out)
{
LLMutexTrylock lock(getMutex<STACKS_MUTEX>(), 5);
if (!lock.isLocked())
@@ -1640,17 +1513,12 @@ namespace LLError
return;
}
- if(sBuffer == NULL)
- {
- allocateStackBuffer();
- }
-
- if(sIndex > 511)
+ if(sBuffer.size() > 511)
{
clear() ;
}
- LLError::Log::flush(_out, sBuffer[sIndex++]) ;
+ sBuffer.push_back(out.str());
}
//static
@@ -1662,33 +1530,30 @@ namespace LLError
return;
}
- if(sIndex > 0)
+ if(! sBuffer.empty())
{
LL_INFOS() << " ************* PRINT OUT LL CALL STACKS ************* " << LL_ENDL;
- while(sIndex > 0)
+ for (StringVector::const_reverse_iterator ri(sBuffer.rbegin()), re(sBuffer.rend());
+ ri != re; ++ri)
{
- sIndex-- ;
- LL_INFOS() << sBuffer[sIndex] << LL_ENDL;
+ LL_INFOS() << (*ri) << LL_ENDL;
}
LL_INFOS() << " *************** END OF LL CALL STACKS *************** " << LL_ENDL;
}
- if(sBuffer != NULL)
- {
- freeStackBuffer();
- }
+ cleanup();
}
//static
void LLCallStacks::clear()
{
- sIndex = 0 ;
+ sBuffer.clear();
}
//static
void LLCallStacks::cleanup()
{
- freeStackBuffer();
+ clear();
}
std::ostream& operator<<(std::ostream& out, const LLStacktrace&)
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 18d11cfa9d..d439136ca8 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -29,7 +29,9 @@
#define LL_LLERROR_H
#include <sstream>
+#include <string>
#include <typeinfo>
+#include <vector>
#include "stdtypes.h"
@@ -193,20 +195,12 @@ namespace LLError
*/
struct CallSite;
-
- enum ErrFatalHookResult { ERR_DO_NOT_CRASH, ERR_CRASH };
class LL_COMMON_API Log
{
public:
static bool shouldLog(CallSite&);
- static std::ostringstream* out();
-
- static void flush(std::ostringstream* out, char* message);
-
- // returns false iff the calling macro should crash
- static ErrFatalHookResult flush(std::ostringstream*, const CallSite&);
-
+ static void flush(const std::ostringstream&, const CallSite&);
static std::string demangle(const char* mangled);
/// classname<TYPE>()
template <typename T>
@@ -277,7 +271,6 @@ namespace LLError
class LL_COMMON_API NoClassInfo { };
// used to indicate no class info known for logging
-
//LLCallStacks keeps track of call stacks and output the call stacks to log file
//when LLAppViewer::handleViewerCrash() is triggered.
//
@@ -288,18 +281,15 @@ namespace LLError
class LL_COMMON_API LLCallStacks
{
private:
- static char** sBuffer ;
- static S32 sIndex ;
-
- static void allocateStackBuffer();
- static void freeStackBuffer();
+ typedef std::vector<std::string> StringVector;
+ static StringVector sBuffer ;
public:
static void push(const char* function, const int line) ;
- static std::ostringstream* insert(const char* function, const int line) ;
+ static void insert(std::ostream& out, const char* function, const int line) ;
static void print() ;
static void clear() ;
- static void end(std::ostringstream* _out) ;
+ static void end(const std::ostringstream& out) ;
static void cleanup();
};
@@ -313,10 +303,11 @@ namespace LLError
//this is cheaper than llcallstacks if no need to output other variables to call stacks.
#define LL_PUSH_CALLSTACKS() LLError::LLCallStacks::push(__FUNCTION__, __LINE__)
-#define llcallstacks \
- { \
- std::ostringstream* _out = LLError::LLCallStacks::insert(__FUNCTION__, __LINE__) ; \
- (*_out)
+#define llcallstacks \
+ { \
+ std::ostringstream _out; \
+ LLError::LLCallStacks::insert(_out, __FUNCTION__, __LINE__) ; \
+ _out
#define llcallstacksendl \
LLError::End(); \
@@ -362,11 +353,11 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \
lllog_test_()
-#define lllog_test_() \
- if (LL_UNLIKELY(_site.shouldLog())) \
- { \
- std::ostringstream* _out = LLError::Log::out(); \
- (*_out)
+#define lllog_test_() \
+ if (LL_UNLIKELY(_site.shouldLog())) \
+ { \
+ std::ostringstream _out; \
+ _out
#define lllog_site_args_(level, once, tags) \
level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \
@@ -385,29 +376,27 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
// LL_CONT << " for " << t << " seconds" << LL_ENDL;
//
//Such computation is done iff the message will be logged.
-#define LL_CONT (*_out)
+#define LL_CONT _out
#define LL_NEWLINE '\n'
-#ifdef _LLERROR_CPP_
-volatile int* gCauseCrash = NULL;
-#else
-volatile extern int* gCauseCrash;
-#endif // _LLERROR_CPP_
-
// Use this only in LL_ERRS or in a place that LL_ERRS may not be used
-#define LLERROR_CRASH \
-{ \
- *gCauseCrash = 0; \
- exit(*gCauseCrash); \
+#define LLERROR_CRASH \
+{ \
+ int* make_me_crash = NULL;\
+ *make_me_crash = 0; \
+ exit(*make_me_crash); \
}
-#define LL_ENDL \
- LLError::End(); \
- if (LLError::ERR_CRASH == LLError::Log::flush(_out, _site)) \
- LLERROR_CRASH \
- } \
- } while(0)
+#define LL_ENDL \
+ LLError::End(); \
+ LLError::Log::flush(_out, _site); \
+ if (_site.mLevel == LLError::LEVEL_ERROR) \
+ { \
+ LLERROR_CRASH \
+ } \
+ } \
+ } while(0)
// NEW Macros for debugging, allow the passing of a string tag
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
index d001a3bd03..e87bb7bf35 100644
--- a/indra/llcommon/llerrorcontrol.h
+++ b/indra/llcommon/llerrorcontrol.h
@@ -93,27 +93,42 @@ namespace LLError
Control functions.
*/
- // A FatalHook is called if set using setFatalHook; its return controls
- // whether or not the calling error logging code should crash.
- // ERR_DO_NOT_CRASH should be used only in test code.
- typedef boost::function<LLError::ErrFatalHookResult(const std::string&)> FatalHook;
-
- /// Supplement and control the default behavior of crashing on LL_ERRS
- /// This may be used to suppress crashes only in test code;
- /// otherwise, the FatalHook should always either return
- /// the result from the previous hook (see getFatalHook below),
- /// or return LLError::ERR_CRASH
- LL_COMMON_API void setFatalHook(const FatalHook& fatal_hook);
- // The fatal_hook function will be called when an message of LEVEL_ERROR
+ typedef boost::function<void(const std::string&)> FatalFunction;
+
+ LL_COMMON_API void setFatalFunction(const FatalFunction&);
+ // The fatal function will be called after an message of LEVEL_ERROR
// is logged. Note: supressing a LEVEL_ERROR message from being logged
- // (by, for example, setting a class level to LEVEL_NONE), will also
- // prevent the fatal_hook being called and the resulting deliberate crash
+ // (by, for example, setting a class level to LEVEL_NONE), will keep
+ // that message from causing the fatal function to be invoked.
+ // The passed FatalFunction will be the LAST log function called
+ // before LL_ERRS crashes its caller. A FatalFunction can throw an
+ // exception, or call exit(), to bypass the crash. It MUST disrupt the
+ // flow of control because no caller expects LL_ERRS to return.
- /// Retrieve the previously-set FatalHook
- LL_COMMON_API FatalHook getFatalHook();
+ LL_COMMON_API FatalFunction getFatalFunction();
+ // Retrieve the previously-set FatalFunction
LL_COMMON_API std::string getFatalMessage();
- // Retrieve the message last passed to LL_ERRS, if any
+ // Retrieve the message last passed to FatalFunction, if any
+
+ /// temporarily override the FatalFunction for the duration of a
+ /// particular scope, e.g. for unit tests
+ class LL_COMMON_API OverrideFatalFunction
+ {
+ public:
+ OverrideFatalFunction(const FatalFunction& func):
+ mPrev(getFatalFunction())
+ {
+ setFatalFunction(func);
+ }
+ ~OverrideFatalFunction()
+ {
+ setFatalFunction(mPrev);
+ }
+
+ private:
+ FatalFunction mPrev;
+ };
typedef std::string (*TimeFunction)();
LL_COMMON_API std::string utcTime();
@@ -134,14 +149,14 @@ namespace LLError
virtual void recordMessage(LLError::ELevel, const std::string& message) = 0;
// use the level for better display, not for filtering
- virtual bool enabled() { return true; }
+ virtual bool enabled() { return true; }
bool wantsTime();
bool wantsTags();
bool wantsLevel();
bool wantsLocation();
bool wantsFunctionName();
- bool wantsMultiline();
+ bool wantsMultiline();
void showTime(bool show);
void showTags(bool show);
@@ -152,15 +167,35 @@ namespace LLError
protected:
bool mWantsTime;
- bool mWantsTags;
- bool mWantsLevel;
- bool mWantsLocation;
- bool mWantsFunctionName;
- bool mWantsMultiline;
+ bool mWantsTags;
+ bool mWantsLevel;
+ bool mWantsLocation;
+ bool mWantsFunctionName;
+ bool mWantsMultiline;
};
typedef boost::shared_ptr<Recorder> RecorderPtr;
+ /**
+ * Instantiate GenericRecorder with a callable(level, message) to get
+ * control on every log message without having to code an explicit
+ * Recorder subclass.
+ */
+ template <typename CALLABLE>
+ class GenericRecorder: public Recorder
+ {
+ public:
+ GenericRecorder(const CALLABLE& callable):
+ mCallable(callable)
+ {}
+ void recordMessage(LLError::ELevel level, const std::string& message) override
+ {
+ mCallable(level, message);
+ }
+ private:
+ CALLABLE mCallable;
+ };
+
/**
* @NOTE: addRecorder() and removeRecorder() uses the boost::shared_ptr to allow for shared ownership
* while still ensuring that the allocated memory is eventually freed
@@ -168,6 +203,19 @@ namespace LLError
LL_COMMON_API void addRecorder(RecorderPtr);
LL_COMMON_API void removeRecorder(RecorderPtr);
// each error message is passed to each recorder via recordMessage()
+ /**
+ * Call addGenericRecorder() with a callable(level, message) to get
+ * control on every log message without having to code an explicit
+ * Recorder subclass. Save the returned RecorderPtr if you later want to
+ * call removeRecorder().
+ */
+ template <typename CALLABLE>
+ RecorderPtr addGenericRecorder(const CALLABLE& callable)
+ {
+ RecorderPtr ptr{ new GenericRecorder<CALLABLE>(callable) };
+ addRecorder(ptr);
+ return ptr;
+ }
LL_COMMON_API void logToFile(const std::string& filename);
LL_COMMON_API void logToStderr();
@@ -190,11 +238,6 @@ namespace LLError
LL_COMMON_API std::string abbreviateFile(const std::string& filePath);
LL_COMMON_API int shouldLogCallCount();
-
- // Check whether Globals exists. This should only be used by LLSingleton
- // infrastructure to avoid trying to log when our internal LLSingleton is
- // unavailable -- circularity ensues.
- LL_COMMON_API bool is_available();
};
#endif // LL_LLERRORCONTROL_H
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp
index 5ce8958687..b584b0ff8b 100644
--- a/indra/llcommon/llexception.cpp
+++ b/indra/llcommon/llexception.cpp
@@ -24,11 +24,14 @@
// `_GNU_SOURCE` macro or `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED` if
// _Unwind_Backtrace is available without `_GNU_SOURCE`."
#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED
+
#if LL_WINDOWS
// On Windows, header-only implementation causes macro collisions -- use
// prebuilt library
#define BOOST_STACKTRACE_LINK
+#include <excpt.h>
#endif // LL_WINDOWS
+
#include <boost/stacktrace.hpp>
// other Linden headers
#include "llerror.h"
@@ -85,3 +88,25 @@ void annotate_exception_(boost::exception& exc)
// Anyway, which of us is really going to examine more than 100 frames?
exc << errinfo_stacktrace(boost::stacktrace::stacktrace(1, 100));
}
+
+#if LL_WINDOWS
+
+// For windows SEH exception handling we sometimes need a filter that will
+// separate C++ exceptions from C SEH exceptions
+static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
+
+U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
+{
+ if (code == STATUS_MSC_EXCEPTION)
+ {
+ // C++ exception, go on
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+ else
+ {
+ // handle it
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+}
+
+#endif //LL_WINDOWS
diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h
index 422dd8810a..375bea4a57 100644
--- a/indra/llcommon/llexception.h
+++ b/indra/llcommon/llexception.h
@@ -102,4 +102,14 @@ void crash_on_unhandled_exception_(const char*, int, const char*, const std::str
log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT)
void log_unhandled_exception_(const char*, int, const char*, const std::string&);
+
+#if LL_WINDOWS
+
+// SEH exception filtering for use in __try __except
+// Separates C++ exceptions from C SEH exceptions
+// Todo: might be good idea to do some kind of seh_to_msc_wrapper(function, ARGS&&);
+U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop);
+
+#endif //LL_WINDOWS
+
#endif /* ! defined(LL_LLEXCEPTION_H) */
diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp
new file mode 100644
index 0000000000..38696c2258
--- /dev/null
+++ b/indra/llcommon/llkeybind.cpp
@@ -0,0 +1,395 @@
+/**
+ * @file llkeybind.cpp
+ * @brief Information about key combinations.
+ *
+ * $LicenseInfo:firstyear=2019&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2019, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llkeybind.h"
+
+#include "llsd.h"
+#include "llsdutil.h"
+
+LLKeyData::LLKeyData()
+ :
+ mMouse(CLICK_NONE),
+ mKey(KEY_NONE),
+ mMask(MASK_NONE),
+ mIgnoreMasks(false)
+{
+}
+
+LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask)
+ :
+ mMouse(mouse),
+ mKey(key),
+ mMask(mask),
+ mIgnoreMasks(false)
+{
+}
+
+LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, bool ignore_mask)
+ :
+ mMouse(mouse),
+ mKey(key),
+ mMask(MASK_NONE),
+ mIgnoreMasks(ignore_mask)
+{
+}
+
+LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask)
+ :
+ mMouse(mouse),
+ mKey(key),
+ mMask(mask),
+ mIgnoreMasks(ignore_mask)
+{
+}
+
+LLKeyData::LLKeyData(const LLSD &key_data)
+{
+ if (key_data.has("mouse"))
+ {
+ mMouse = (EMouseClickType)key_data["mouse"].asInteger();
+ }
+ if (key_data.has("key"))
+ {
+ mKey = key_data["key"].asInteger();
+ }
+ if (key_data.has("ignore_accelerators"))
+ {
+ mIgnoreMasks = key_data["ignore_accelerators"];
+ }
+ if (key_data.has("mask"))
+ {
+ mMask = key_data["mask"].asInteger();
+ }
+}
+
+LLSD LLKeyData::asLLSD() const
+{
+ LLSD data;
+ data["mouse"] = (LLSD::Integer)mMouse;
+ data["key"] = (LLSD::Integer)mKey;
+ data["mask"] = (LLSD::Integer)mMask;
+ if (mIgnoreMasks)
+ {
+ data["ignore_accelerators"] = (LLSD::Boolean)mIgnoreMasks;
+ }
+ return data;
+}
+
+bool LLKeyData::isEmpty() const
+{
+ return mMouse == CLICK_NONE && mKey == KEY_NONE;
+}
+
+void LLKeyData::reset()
+{
+ mMouse = CLICK_NONE;
+ mKey = KEY_NONE;
+ mMask = MASK_NONE;
+ mIgnoreMasks = false;
+}
+
+LLKeyData& LLKeyData::operator=(const LLKeyData& rhs)
+{
+ mMouse = rhs.mMouse;
+ mKey = rhs.mKey;
+ mMask = rhs.mMask;
+ mIgnoreMasks = rhs.mIgnoreMasks;
+ return *this;
+}
+
+bool LLKeyData::operator==(const LLKeyData& rhs)
+{
+ if (mMouse != rhs.mMouse) return false;
+ if (mKey != rhs.mKey) return false;
+ if (mMask != rhs.mMask) return false;
+ if (mIgnoreMasks != rhs.mIgnoreMasks) return false;
+ return true;
+}
+
+bool LLKeyData::operator!=(const LLKeyData& rhs)
+{
+ if (mMouse != rhs.mMouse) return true;
+ if (mKey != rhs.mKey) return true;
+ if (mMask != rhs.mMask) return true;
+ if (mIgnoreMasks != rhs.mIgnoreMasks) return true;
+ return false;
+}
+
+bool LLKeyData::canHandle(const LLKeyData& data) const
+{
+ if (data.mKey == mKey
+ && data.mMouse == mMouse
+ && ((mIgnoreMasks && (data.mMask & mMask) == mMask) || data.mMask == mMask))
+ {
+ return true;
+ }
+ return false;
+}
+
+bool LLKeyData::canHandle(EMouseClickType mouse, KEY key, MASK mask) const
+{
+ if (mouse == mMouse
+ && key == mKey
+ && ((mIgnoreMasks && (mask & mMask) == mMask) || mask == mMask))
+ {
+ return true;
+ }
+ return false;
+}
+
+// LLKeyBind
+
+LLKeyBind::LLKeyBind(const LLSD &key_bind)
+{
+ if (key_bind.isArray())
+ {
+ for (LLSD::array_const_iterator data = key_bind.beginArray(), endLists = key_bind.endArray();
+ data != endLists;
+ data++
+ )
+ {
+ mData.push_back(LLKeyData(*data));
+ }
+ }
+}
+
+bool LLKeyBind::operator==(const LLKeyBind& rhs)
+{
+ U32 size = mData.size();
+ if (size != rhs.mData.size()) return false;
+
+ for (U32 i = 0; i < size; i++)
+ {
+ if (mData[i] != rhs.mData[i]) return false;
+ }
+
+ return true;
+}
+
+bool LLKeyBind::operator!=(const LLKeyBind& rhs)
+{
+ U32 size = mData.size();
+ if (size != rhs.mData.size()) return true;
+
+ for (U32 i = 0; i < size; i++)
+ {
+ if (mData[i] != rhs.mData[i]) return true;
+ }
+
+ return false;
+}
+
+bool LLKeyBind::isEmpty() const
+{
+ for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++)
+ {
+ if (!iter->isEmpty()) return false;
+ }
+ return true;
+}
+
+LLSD LLKeyBind::asLLSD() const
+{
+ S32 last = mData.size() - 1;
+ while (mData[last].empty())
+ {
+ last--;
+ }
+
+ LLSD data;
+ for (S32 i = 0; i <= last; ++i)
+ {
+ // append even if empty to not affect visual representation
+ data.append(mData[i].asLLSD());
+ }
+ return data;
+}
+
+bool LLKeyBind::canHandle(EMouseClickType mouse, KEY key, MASK mask) const
+{
+ if (mouse == CLICK_NONE && key == KEY_NONE)
+ {
+ // assume placeholder
+ return false;
+ }
+
+ for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++)
+ {
+ if (iter->canHandle(mouse, key, mask))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool LLKeyBind::canHandleKey(KEY key, MASK mask) const
+{
+ return canHandle(CLICK_NONE, key, mask);
+}
+
+bool LLKeyBind::canHandleMouse(EMouseClickType mouse, MASK mask) const
+{
+ return canHandle(mouse, KEY_NONE, mask);
+}
+
+bool LLKeyBind::hasKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const
+{
+ if (mouse != CLICK_NONE || key != KEY_NONE)
+ {
+ for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++)
+ {
+ if (iter->mKey == key
+ && iter->mMask == mask
+ && iter->mMouse == mouse
+ && iter->mIgnoreMasks == ignore)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool LLKeyBind::hasKeyData(const LLKeyData& data) const
+{
+ return hasKeyData(data.mMouse, data.mKey, data.mMask, data.mIgnoreMasks);
+}
+
+bool LLKeyBind::hasKeyData(U32 index) const
+{
+ return mData.size() > index;
+}
+
+S32 LLKeyBind::findKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const
+{
+ if (mouse != CLICK_NONE || key != KEY_NONE)
+ {
+ for (S32 i = 0; i < mData.size(); ++i)
+ {
+ if (mData[i].mKey == key
+ && mData[i].mMask == mask
+ && mData[i].mMouse == mouse
+ && mData[i].mIgnoreMasks == ignore)
+ {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+S32 LLKeyBind::findKeyData(const LLKeyData& data) const
+{
+ return findKeyData(data.mMouse, data.mKey, data.mMask, data.mIgnoreMasks);
+}
+
+LLKeyData LLKeyBind::getKeyData(U32 index) const
+{
+ if (mData.size() > index)
+ {
+ return mData[index];
+ }
+ return LLKeyData();
+}
+
+bool LLKeyBind::addKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore)
+{
+ if (!hasKeyData(mouse, key, mask, ignore))
+ {
+ mData.push_back(LLKeyData(mouse, key, mask, ignore));
+ return true;
+ }
+ return false;
+}
+
+bool LLKeyBind::addKeyData(const LLKeyData& data)
+{
+ if (!hasKeyData(data))
+ {
+ mData.push_back(data);
+ return true;
+ }
+ return false;
+}
+
+void LLKeyBind::replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore, U32 index)
+{
+ replaceKeyData(LLKeyData(mouse, key, mask, ignore), index);
+}
+
+void LLKeyBind::replaceKeyData(const LLKeyData& data, U32 index)
+{
+ if (!data.isEmpty())
+ {
+ // if both click and key are none (isEmpty()), we are inserting a placeholder, we don't want to reset anything
+ // otherwise reset identical key
+ for (data_vector_t::iterator iter = mData.begin(); iter != mData.end(); iter++)
+ {
+ if (iter->mKey == data.mKey
+ && iter->mMouse == data.mMouse
+ && iter->mIgnoreMasks == data.mIgnoreMasks
+ && iter->mMask == data.mMask)
+ {
+ // Replacing only fully equal combinations even in case 'ignore' is set
+ // Reason: Simplicity and user might decide to do a 'move' command as W and Shift+Ctrl+W, and 'run' as Shift+W
+ iter->reset();
+ break;
+ }
+ }
+ }
+ if (mData.size() <= index)
+ {
+ mData.resize(index + 1);
+ }
+ mData[index] = data;
+}
+
+void LLKeyBind::resetKeyData(S32 index)
+{
+ if (mData.size() > index)
+ {
+ mData[index].reset();
+ }
+}
+
+void LLKeyBind::trimEmpty()
+{
+ S32 last = mData.size() - 1;
+ while (last >= 0 && mData[last].empty())
+ {
+ mData.erase(mData.begin() + last);
+ last--;
+ }
+}
+
+U32 LLKeyBind::getDataCount()
+{
+ return mData.size();
+}
+
diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h
new file mode 100644
index 0000000000..c6b4bd970f
--- /dev/null
+++ b/indra/llcommon/llkeybind.h
@@ -0,0 +1,106 @@
+/**
+ * @file llkeybind.h
+ * @brief Information about key combinations.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_KEYBIND_H
+#define LL_KEYBIND_H
+
+#include "indra_constants.h"
+
+// KeyData - single key combination (mouse/mask/keyboard)
+class LL_COMMON_API LLKeyData
+{
+public:
+ LLKeyData();
+ LLKeyData(EMouseClickType mouse, KEY key, MASK mask);
+ LLKeyData(EMouseClickType mouse, KEY key, bool ignore_mask);
+ LLKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask);
+ LLKeyData(const LLSD &key_data);
+
+ LLSD asLLSD() const;
+ bool isEmpty() const;
+ bool empty() const { return isEmpty(); };
+ void reset();
+ LLKeyData& operator=(const LLKeyData& rhs);
+ bool operator==(const LLKeyData& rhs);
+ bool operator!=(const LLKeyData& rhs);
+
+ bool canHandle(const LLKeyData& data) const;
+ bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const;
+
+ EMouseClickType mMouse;
+ KEY mKey;
+ MASK mMask;
+ // Either to expect exact match or ignore not expected masks as long as expected mask-bit is present
+ bool mIgnoreMasks;
+};
+
+// One function can bind to multiple Key options
+class LLKeyBind
+{
+public:
+ LLKeyBind() {}
+ LLKeyBind(const LLSD &key_bind);
+
+ bool operator==(const LLKeyBind& rhs);
+ bool operator!=(const LLKeyBind& rhs);
+ bool isEmpty() const;
+ bool empty() const { return isEmpty(); };
+
+ LLSD asLLSD() const;
+
+ bool canHandle(EMouseClickType mouse, KEY key, MASK mask) const;
+ bool canHandleKey(KEY key, MASK mask) const;
+ bool canHandleMouse(EMouseClickType mouse, MASK mask) const;
+
+ // contains specified combination
+ bool hasKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const;
+ bool hasKeyData(const LLKeyData& data) const;
+ bool hasKeyData(U32 index) const;
+
+ // index of contained LLKeyData
+ S32 findKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore) const;
+ S32 findKeyData(const LLKeyData& data) const;
+
+ LLKeyData getKeyData(U32 index) const;
+
+ // these methods enshure there will be no repeats
+ bool addKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore);
+ bool addKeyData(const LLKeyData& data);
+ void replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignore, U32 index);
+ void replaceKeyData(const LLKeyData& data, U32 index);
+ void resetKeyData(S32 index);
+ void clear() { mData.clear(); }
+ // if there any empty LLKeyData in the end of the array, remove them
+ void trimEmpty();
+ U32 getDataCount();
+
+private:
+ typedef std::vector<LLKeyData> data_vector_t;
+ data_vector_t mData;
+};
+
+
+#endif // LL_KEYBIND_H
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
index 8293c35516..e8ea0ab398 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -144,6 +144,11 @@ public:
mStderrConnection = childerr.getPump()
.listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1));
+ // For our lifespan, intercept any LL_ERRS so we can notify plugin
+ mRecorder = LLError::addGenericRecorder(
+ [this](LLError::ELevel level, const std::string& message)
+ { onError(level, message); });
+
// Send child a preliminary event reporting our own reply-pump name --
// which would otherwise be pretty tricky to guess!
wstdin(mReplyPump.getName(),
@@ -158,6 +163,7 @@ public:
virtual ~LLLeapImpl()
{
LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL;
+ LLError::removeRecorder(mRecorder);
}
// Listener for failed launch attempt
@@ -371,6 +377,30 @@ public:
return false;
}
+ void onError(LLError::ELevel level, const std::string& error)
+ {
+ if (level == LLError::LEVEL_ERROR)
+ {
+ // Notify plugin
+ LLSD event;
+ event["type"] = "error";
+ event["error"] = error;
+ mReplyPump.post(event);
+
+ // All the above really accomplished was to buffer the serialized
+ // event in our WritePipe. Have to pump mainloop a couple times to
+ // really write it out there... but time out in case we can't write.
+ LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
+ LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
+ LLSD nop;
+ F64 until = (LLTimer::getElapsedSeconds() + 2).value();
+ while (childin.size() && LLTimer::getElapsedSeconds() < until)
+ {
+ mainloop.post(nop);
+ }
+ }
+ }
+
private:
/// We always want to listen on mReplyPump with wstdin(); under some
/// circumstances we'll also echo other LLEventPumps to the plugin.
@@ -391,6 +421,7 @@ private:
mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection;
boost::scoped_ptr<LLEventPump::Blocker> mBlocker;
LLProcess::ReadPipe::size_type mExpect;
+ LLError::RecorderPtr mRecorder;
boost::scoped_ptr<LLLeapListener> mListener;
};
diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp
index f410bd6a92..6b1986d0e9 100644
--- a/indra/llcommon/llsingleton.cpp
+++ b/indra/llcommon/llsingleton.cpp
@@ -28,7 +28,7 @@
#include "llsingleton.h"
#include "llerror.h"
-#include "llerrorcontrol.h" // LLError::is_available()
+#include "llerrorcontrol.h"
#include "lldependencies.h"
#include "llexception.h"
#include "llcoros.h"
@@ -38,13 +38,6 @@
#include <sstream>
#include <stdexcept>
-namespace {
-void log(LLError::ELevel level,
- const char* p1, const char* p2, const char* p3, const char* p4);
-
-bool oktolog();
-} // anonymous namespace
-
// Our master list of all LLSingletons is itself an LLSingleton. We used to
// store it in a function-local static, but that could get destroyed before
// the last of the LLSingletons -- and ~LLSingletonBase() definitely wants to
@@ -220,8 +213,8 @@ void LLSingletonBase::pop_initializing()
if (list.empty())
{
- logerrs("Underflow in stack of currently-initializing LLSingletons at ",
- classname(this).c_str(), "::getInstance()");
+ logerrs({"Underflow in stack of currently-initializing LLSingletons at ",
+ classname(this), "::getInstance()"});
}
// Now we know list.back() exists: capture it
@@ -242,9 +235,9 @@ void LLSingletonBase::pop_initializing()
// Now validate the newly-popped LLSingleton.
if (back != this)
{
- logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ",
- classname(this).c_str(), "::getInstance() trying to pop ",
- classname(back).c_str());
+ logerrs({"Push/pop mismatch in stack of currently-initializing LLSingletons: ",
+ classname(this), "::getInstance() trying to pop ",
+ classname(back)});
}
// log AFTER popping so logging singletons don't cry circularity
@@ -279,8 +272,6 @@ void LLSingletonBase::reset_initializing(list_t::size_type size)
void LLSingletonBase::MasterList::LockedInitializing::log(const char* verb, const char* name)
{
- if (oktolog())
- {
LL_DEBUGS("LLSingleton") << verb << ' ' << demangle(name) << ';';
if (mList)
{
@@ -292,7 +283,6 @@ void LLSingletonBase::MasterList::LockedInitializing::log(const char* verb, cons
}
}
LL_ENDL;
- }
}
void LLSingletonBase::capture_dependency()
@@ -336,15 +326,15 @@ void LLSingletonBase::capture_dependency()
//
// Example: LLNotifications singleton initializes default channels.
// Channels register themselves with singleton once done.
- logdebugs("LLSingleton circularity: ", out.str().c_str(),
- classname(this).c_str(), "");
+ logdebugs({"LLSingleton circularity: ", out.str(),
+ classname(this)});
}
else
{
// Actual circularity with other singleton (or single singleton is used extensively).
// Dependency can be unclear.
- logwarns("LLSingleton circularity: ", out.str().c_str(),
- classname(this).c_str(), "");
+ logwarns({"LLSingleton circularity: ", out.str(),
+ classname(this)});
}
}
else
@@ -357,8 +347,8 @@ void LLSingletonBase::capture_dependency()
if (current->mDepends.insert(this).second)
{
// only log the FIRST time we hit this dependency!
- logdebugs(classname(current).c_str(),
- " depends on ", classname(this).c_str());
+ logdebugs({classname(current),
+ " depends on ", classname(this)});
}
}
}
@@ -393,7 +383,7 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort()
// extracts just the first (key) element from each sorted_iterator, then
// uses vec_t's range constructor... but frankly this is more
// straightforward, as long as we remember the above reserve() call!
- for (const SingletonDeps::sorted_iterator::value_type& pair : sdeps.sort())
+ for (const SingletonDeps::sorted_iterator::value_type pair : sdeps.sort())
{
ret.push_back(pair.first);
}
@@ -406,7 +396,7 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort()
void LLSingletonBase::cleanup_()
{
- logdebugs("calling ", classname(this).c_str(), "::cleanupSingleton()");
+ logdebugs({"calling ", classname(this), "::cleanupSingleton()"});
try
{
cleanupSingleton();
@@ -432,87 +422,64 @@ void LLSingletonBase::deleteAll()
if (! sp->mDeleteSingleton)
{
// This Should Not Happen... but carry on.
- logwarns(name.c_str(), "::mDeleteSingleton not initialized!");
+ logwarns({name, "::mDeleteSingleton not initialized!"});
}
else
{
// properly initialized: call it.
- logdebugs("calling ", name.c_str(), "::deleteSingleton()");
+ logdebugs({"calling ", name, "::deleteSingleton()"});
// From this point on, DO NOT DEREFERENCE sp!
sp->mDeleteSingleton();
}
}
catch (const std::exception& e)
{
- logwarns("Exception in ", name.c_str(), "::deleteSingleton(): ", e.what());
+ logwarns({"Exception in ", name, "::deleteSingleton(): ", e.what()});
}
catch (...)
{
- logwarns("Unknown exception in ", name.c_str(), "::deleteSingleton()");
+ logwarns({"Unknown exception in ", name, "::deleteSingleton()"});
}
}
}
/*---------------------------- Logging helpers -----------------------------*/
namespace {
-bool oktolog()
-{
- // See comments in log() below.
- return LLError::is_available();
-}
-void log(LLError::ELevel level,
- const char* p1, const char* p2, const char* p3, const char* p4)
+std::ostream& operator<<(std::ostream& out, const LLSingletonBase::string_params& args)
{
- // The is_available() test below ensures that we'll stop logging once
- // LLError has been cleaned up. If we had a similar portable test for
- // std::cerr, this would be a good place to use it.
-
- // Check LLError::is_available() because some of LLError's infrastructure
- // is itself an LLSingleton. If that LLSingleton has not yet been
- // initialized, trying to log will engage LLSingleton machinery... and
- // around and around we go.
- if (LLError::is_available())
- {
- LL_VLOGS(level, "LLSingleton") << p1 << p2 << p3 << p4 << LL_ENDL;
- }
- else
+ // However many args there are in args, stream each of them to 'out'.
+ for (auto arg : args)
{
- // Caller may be a test program, or something else whose stderr is
- // visible to the user.
- std::cerr << p1 << p2 << p3 << p4 << std::endl;
+ out << arg;
}
+ return out;
}
} // anonymous namespace
//static
-void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4)
+void LLSingletonBase::logwarns(const string_params& args)
{
- log(LLError::LEVEL_WARN, p1, p2, p3, p4);
+ LL_WARNS("LLSingleton") << args << LL_ENDL;
}
//static
-void LLSingletonBase::loginfos(const char* p1, const char* p2, const char* p3, const char* p4)
+void LLSingletonBase::loginfos(const string_params& args)
{
- log(LLError::LEVEL_INFO, p1, p2, p3, p4);
+ LL_INFOS("LLSingleton") << args << LL_ENDL;
}
//static
-void LLSingletonBase::logdebugs(const char* p1, const char* p2, const char* p3, const char* p4)
+void LLSingletonBase::logdebugs(const string_params& args)
{
- log(LLError::LEVEL_DEBUG, p1, p2, p3, p4);
+ LL_DEBUGS("LLSingleton") << args << LL_ENDL;
}
//static
-void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4)
+void LLSingletonBase::logerrs(const string_params& args)
{
- log(LLError::LEVEL_ERROR, p1, p2, p3, p4);
- // The other important side effect of LL_ERRS() is
- // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG)
- std::ostringstream out;
- out << p1 << p2 << p3 << p4;
- LLERROR_CRASH;
+ LL_ERRS("LLSingleton") << args << LL_ENDL;
}
std::string LLSingletonBase::demangle(const char* mangled)
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 30a5b21cf8..2eb39c6c8c 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -34,6 +34,7 @@
#include "lockstatic.h"
#include "llthread.h" // on_main_thread()
#include "llmainthreadtask.h"
+#include <initializer_list>
class LLSingletonBase: private boost::noncopyable
{
@@ -111,14 +112,13 @@ protected:
void capture_dependency();
// delegate logging calls to llsingleton.cpp
- static void logerrs(const char* p1, const char* p2="",
- const char* p3="", const char* p4="");
- static void logwarns(const char* p1, const char* p2="",
- const char* p3="", const char* p4="");
- static void loginfos(const char* p1, const char* p2="",
- const char* p3="", const char* p4="");
- static void logdebugs(const char* p1, const char* p2="",
- const char* p3="", const char* p4="");
+public:
+ typedef std::initializer_list<std::string_view> string_params;
+protected:
+ static void logerrs (const string_params&);
+ static void logwarns (const string_params&);
+ static void loginfos (const string_params&);
+ static void logdebugs(const string_params&);
static std::string demangle(const char* mangled);
// these classname() declarations restate template functions declared in
// llerror.h because we avoid #including that here
@@ -327,8 +327,8 @@ private:
// init stack to its previous size BEFORE logging so log-machinery
// LLSingletons don't record a dependency on DERIVED_TYPE!
LLSingleton_manage_master<DERIVED_TYPE>().reset_initializing(prev_size);
- logwarns("Error constructing ", classname<DERIVED_TYPE>().c_str(),
- ": ", err.what());
+ logwarns({"Error constructing ", classname<DERIVED_TYPE>(),
+ ": ", err.what()});
// There isn't a separate EInitState value meaning "we attempted
// to construct this LLSingleton subclass but could not," so use
// DELETED. That seems slightly more appropriate than UNINITIALIZED.
@@ -356,8 +356,8 @@ private:
// BEFORE logging, so log-machinery LLSingletons don't record a
// dependency on DERIVED_TYPE!
pop_initializing(lk->mInstance);
- logwarns("Error in ", classname<DERIVED_TYPE>().c_str(),
- "::initSingleton(): ", err.what());
+ logwarns({"Error in ", classname<DERIVED_TYPE>(),
+ "::initSingleton(): ", err.what()});
// Get rid of the instance entirely. This call depends on our
// recursive_mutex. We could have a deleteSingleton(LockStatic&)
// overload and pass lk, but we don't strictly need it.
@@ -506,9 +506,9 @@ public:
case CONSTRUCTING:
// here if DERIVED_TYPE's constructor (directly or indirectly)
// calls DERIVED_TYPE::getInstance()
- logerrs("Tried to access singleton ",
- classname<DERIVED_TYPE>().c_str(),
- " from singleton constructor!");
+ logerrs({"Tried to access singleton ",
+ classname<DERIVED_TYPE>(),
+ " from singleton constructor!"});
return nullptr;
case INITIALIZING:
@@ -523,9 +523,9 @@ public:
case DELETED:
// called after deleteSingleton()
- logwarns("Trying to access deleted singleton ",
- classname<DERIVED_TYPE>().c_str(),
- " -- creating new instance");
+ logwarns({"Trying to access deleted singleton ",
+ classname<DERIVED_TYPE>(),
+ " -- creating new instance"});
// fall through
case UNINITIALIZED:
case QUEUED:
@@ -552,8 +552,8 @@ public:
} // unlock 'lk'
// Per the comment block above, dispatch to the main thread.
- loginfos(classname<DERIVED_TYPE>().c_str(),
- "::getInstance() dispatching to main thread");
+ loginfos({classname<DERIVED_TYPE>(),
+ "::getInstance() dispatching to main thread"});
auto instance = LLMainThreadTask::dispatch(
[](){
// VERY IMPORTANT to call getInstance() on the main thread,
@@ -563,16 +563,16 @@ public:
// the main thread processes them, only the FIRST such request
// actually constructs the instance -- every subsequent one
// simply returns the existing instance.
- loginfos(classname<DERIVED_TYPE>().c_str(),
- "::getInstance() on main thread");
+ loginfos({classname<DERIVED_TYPE>(),
+ "::getInstance() on main thread"});
return getInstance();
});
// record the dependency chain tracked on THIS thread, not the main
// thread (consider a getInstance() overload with a tag param that
// suppresses dep tracking when dispatched to the main thread)
capture_dependency(instance);
- loginfos(classname<DERIVED_TYPE>().c_str(),
- "::getInstance() returning on requesting thread");
+ loginfos({classname<DERIVED_TYPE>(),
+ "::getInstance() returning on requesting thread"});
return instance;
}
@@ -641,16 +641,16 @@ private:
// For organizational purposes this function shouldn't be called twice
if (lk->mInitState != super::UNINITIALIZED)
{
- super::logerrs("Tried to initialize singleton ",
- super::template classname<DERIVED_TYPE>().c_str(),
- " twice!");
+ super::logerrs({"Tried to initialize singleton ",
+ super::template classname<DERIVED_TYPE>(),
+ " twice!"});
return nullptr;
}
else if (on_main_thread())
{
// on the main thread, simply construct instance while holding lock
- super::logdebugs(super::template classname<DERIVED_TYPE>().c_str(),
- "::initParamSingleton()");
+ super::logdebugs({super::template classname<DERIVED_TYPE>(),
+ "::initParamSingleton()"});
super::constructSingleton(lk, std::forward<Args>(args)...);
return lk->mInstance;
}
@@ -662,8 +662,8 @@ private:
lk->mInitState = super::QUEUED;
// very important to unlock here so main thread can actually process
lk.unlock();
- super::loginfos(super::template classname<DERIVED_TYPE>().c_str(),
- "::initParamSingleton() dispatching to main thread");
+ super::loginfos({super::template classname<DERIVED_TYPE>(),
+ "::initParamSingleton() dispatching to main thread"});
// Normally it would be the height of folly to reference-bind
// 'args' into a lambda to be executed on some other thread! By
// the time that thread executed the lambda, the references would
@@ -674,12 +674,12 @@ private:
// references.
auto instance = LLMainThreadTask::dispatch(
[&](){
- super::loginfos(super::template classname<DERIVED_TYPE>().c_str(),
- "::initParamSingleton() on main thread");
+ super::loginfos({super::template classname<DERIVED_TYPE>(),
+ "::initParamSingleton() on main thread"});
return initParamSingleton_(std::forward<Args>(args)...);
});
- super::loginfos(super::template classname<DERIVED_TYPE>().c_str(),
- "::initParamSingleton() returning on requesting thread");
+ super::loginfos({super::template classname<DERIVED_TYPE>(),
+ "::initParamSingleton() returning on requesting thread"});
return instance;
}
}
@@ -707,14 +707,14 @@ public:
{
case super::UNINITIALIZED:
case super::QUEUED:
- super::logerrs("Uninitialized param singleton ",
- super::template classname<DERIVED_TYPE>().c_str());
+ super::logerrs({"Uninitialized param singleton ",
+ super::template classname<DERIVED_TYPE>()});
break;
case super::CONSTRUCTING:
- super::logerrs("Tried to access param singleton ",
- super::template classname<DERIVED_TYPE>().c_str(),
- " from singleton constructor!");
+ super::logerrs({"Tried to access param singleton ",
+ super::template classname<DERIVED_TYPE>(),
+ " from singleton constructor!"});
break;
case super::INITIALIZING:
@@ -726,8 +726,8 @@ public:
return lk->mInstance;
case super::DELETED:
- super::logerrs("Trying to access deleted param singleton ",
- super::template classname<DERIVED_TYPE>().c_str());
+ super::logerrs({"Trying to access deleted param singleton ",
+ super::template classname<DERIVED_TYPE>()});
break;
}
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 1f8d558fbe..eff4dd91ea 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -55,6 +55,7 @@
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_integral.hpp>
#include <boost/type_traits/is_float.hpp>
+#include "llfasttimer.h"
using namespace llsd;
@@ -925,8 +926,12 @@ LLMemoryInfo& LLMemoryInfo::refresh()
return *this;
}
+static LLTrace::BlockTimerStatHandle FTM_MEMINFO_LOAD_STATS("MemInfo Load Stats");
+
LLSD LLMemoryInfo::loadStatsMap()
{
+ LL_RECORD_BLOCK_TIME(FTM_MEMINFO_LOAD_STATS);
+
// This implementation is derived from stream() code (as of 2011-06-29).
Stats stats;
@@ -948,24 +953,11 @@ LLSD LLMemoryInfo::loadStatsMap()
stats.add("Total Virtual KB", state.ullTotalVirtual/div);
stats.add("Avail Virtual KB", state.ullAvailVirtual/div);
- PERFORMANCE_INFORMATION perf;
- perf.cb = sizeof(perf);
- GetPerformanceInfo(&perf, sizeof(perf));
-
- SIZE_T pagekb(perf.PageSize/1024);
- stats.add("CommitTotal KB", perf.CommitTotal * pagekb);
- stats.add("CommitLimit KB", perf.CommitLimit * pagekb);
- stats.add("CommitPeak KB", perf.CommitPeak * pagekb);
- stats.add("PhysicalTotal KB", perf.PhysicalTotal * pagekb);
- stats.add("PhysicalAvail KB", perf.PhysicalAvailable * pagekb);
- stats.add("SystemCache KB", perf.SystemCache * pagekb);
- stats.add("KernelTotal KB", perf.KernelTotal * pagekb);
- stats.add("KernelPaged KB", perf.KernelPaged * pagekb);
- stats.add("KernelNonpaged KB", perf.KernelNonpaged * pagekb);
- stats.add("PageSize KB", pagekb);
- stats.add("HandleCount", perf.HandleCount);
- stats.add("ProcessCount", perf.ProcessCount);
- stats.add("ThreadCount", perf.ThreadCount);
+ // SL-12122 - Call to GetPerformanceInfo() was removed here. Took
+ // on order of 10 ms, causing unacceptable frame time spike every
+ // second, and results were never used. If this is needed in the
+ // future, must find a way to avoid frame time impact (e.g. move
+ // to another thread, call much less often).
PROCESS_MEMORY_COUNTERS_EX pmem;
pmem.cb = sizeof(pmem);
diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp
index c275b90120..e4f229dd16 100644
--- a/indra/llcommon/lluriparser.cpp
+++ b/indra/llcommon/lluriparser.cpp
@@ -29,10 +29,13 @@
#include "linden_common.h"
#include "lluriparser.h"
+#if LL_DARWIN
+#include <signal.h>
+#include <setjmp.h>
+#endif
+
LLUriParser::LLUriParser(const std::string& u) : mTmpScheme(false), mNormalizedTmp(false), mRes(0)
{
- mState.uri = &mUri;
-
if (u.find("://") == std::string::npos)
{
mNormalizedUri = "http://";
@@ -51,7 +54,7 @@ LLUriParser::~LLUriParser()
S32 LLUriParser::parse()
{
- mRes = uriParseUriA(&mState, mNormalizedUri.c_str());
+ mRes = uriParseSingleUriA(&mUri, mNormalizedUri.c_str(), NULL);
return mRes;
}
@@ -158,31 +161,69 @@ void LLUriParser::extractParts()
}
}
+#if LL_DARWIN
+typedef void(*sighandler_t)(int);
+jmp_buf return_to_normalize;
+void uri_signal_handler(int signal)
+{
+ // Apparently signal handler throwing an exception doesn't work.
+ // This is ugly and unsafe due to not unwinding content of uriparser library,
+ // but unless we have a way to catch this as NSexception, jump appears to be the only option.
+ longjmp(return_to_normalize, 1 /*setjmp will return this value*/);
+}
+#endif
+
S32 LLUriParser::normalize()
{
mNormalizedTmp = mTmpScheme;
if (!mRes)
{
- mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST);
-
- if (!mRes)
- {
- S32 chars_required;
- mRes = uriToStringCharsRequiredA(&mUri, &chars_required);
-
- if (!mRes)
- {
- chars_required++;
- std::vector<char> label_buf(chars_required);
- mRes = uriToStringA(&label_buf[0], &mUri, chars_required, NULL);
-
- if (!mRes)
- {
- mNormalizedUri = &label_buf[mTmpScheme ? 7 : 0];
- mTmpScheme = false;
- }
- }
- }
+#if LL_DARWIN
+ sighandler_t last_handler;
+ last_handler = signal(SIGILL, &uri_signal_handler); // illegal instruction
+ if (setjmp(return_to_normalize))
+ {
+ // Issue: external library crashed via signal
+ // If you encountered this, please try to figure out what's wrong:
+ // 1. Verify that library's input is 'sane'
+ // 2. Check if we have an NSexception to work with (unlikely)
+ // 3. See if passing same string causes exception to repeat
+ //
+ // Crash happens at uriNormalizeSyntaxExA
+ // Warning!!! This does not properly unwind stack,
+ // if this can be handled by NSexception, it needs to be remade
+ llassert(0);
+
+ LL_WARNS() << "Uriparser crashed with SIGILL, while processing: " << mNormalizedUri << LL_ENDL;
+ signal(SIGILL, last_handler);
+ return 1;
+ }
+#endif
+
+ mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST);
+
+#if LL_DARWIN
+ signal(SIGILL, last_handler);
+#endif
+
+ if (!mRes)
+ {
+ S32 chars_required;
+ mRes = uriToStringCharsRequiredA(&mUri, &chars_required);
+
+ if (!mRes)
+ {
+ chars_required++;
+ std::vector<char> label_buf(chars_required);
+ mRes = uriToStringA(&label_buf[0], &mUri, chars_required, NULL);
+
+ if (!mRes)
+ {
+ mNormalizedUri = &label_buf[mTmpScheme ? 7 : 0];
+ mTmpScheme = false;
+ }
+ }
+ }
}
if(mTmpScheme)
diff --git a/indra/llcommon/lluriparser.h b/indra/llcommon/lluriparser.h
index cfbf54f3c8..92626b9054 100644
--- a/indra/llcommon/lluriparser.h
+++ b/indra/llcommon/lluriparser.h
@@ -76,7 +76,6 @@ private:
std::string mFragment;
std::string mNormalizedUri;
- UriParserStateA mState;
UriUriA mUri;
S32 mRes;
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index cdc2bf8c87..148c18aabe 100644
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -26,6 +26,7 @@
*/
#include <vector>
+#include <stdexcept>
#include "linden_common.h"
@@ -69,25 +70,41 @@ namespace
namespace
{
- static bool fatalWasCalled;
- LLError::ErrFatalHookResult fatalHook(const std::string&)
+ static bool fatalWasCalled = false;
+ struct FatalWasCalled: public std::runtime_error
{
- fatalWasCalled = true;
- return LLError::ERR_DO_NOT_CRASH;
- }
+ FatalWasCalled(const std::string& what): std::runtime_error(what) {}
+ };
+ void fatalCall(const std::string& msg) { throw FatalWasCalled(msg); }
}
+// Because we use LLError::setFatalFunction(fatalCall), any LL_ERRS call we
+// issue will throw FatalWasCalled. But we want the test program to continue.
+// So instead of writing:
+// LL_ERRS("tag") << "some message" << LL_ENDL;
+// write:
+// CATCH(LL_ERRS("tag"), "some message");
+#define CATCH(logcall, expr) \
+ try \
+ { \
+ logcall << expr << LL_ENDL; \
+ } \
+ catch (const FatalWasCalled&) \
+ { \
+ fatalWasCalled = true; \
+ }
+
namespace tut
{
class TestRecorder : public LLError::Recorder
{
public:
TestRecorder()
- {
- showTime(false);
- }
+ {
+ showTime(false);
+ }
virtual ~TestRecorder()
- {}
+ {}
virtual void recordMessage(LLError::ELevel level,
const std::string& message)
@@ -124,7 +141,7 @@ namespace tut
mPriorErrorSettings = LLError::saveAndResetSettings();
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
- LLError::setFatalHook(fatalHook);
+ LLError::setFatalFunction(fatalCall);
LLError::addRecorder(mRecorder);
}
@@ -256,7 +273,7 @@ namespace
LL_DEBUGS("WriteTag","AnotherTag") << "one" << LL_ENDL;
LL_INFOS("WriteTag") << "two" << LL_ENDL;
LL_WARNS("WriteTag") << "three" << LL_ENDL;
- LL_ERRS("WriteTag") << "four" << LL_ENDL;
+ CATCH(LL_ERRS("WriteTag"), "four");
}
};
@@ -384,7 +401,7 @@ namespace
std::string errorReturningLocation()
{
- LL_ERRS() << "die" << LL_ENDL; int this_line = __LINE__;
+ int this_line = __LINE__; CATCH(LL_ERRS(), "die");
return locationString(this_line);
}
}
@@ -705,7 +722,7 @@ public:
static void doDebug() { LL_DEBUGS() << "add dice" << LL_ENDL; }
static void doInfo() { LL_INFOS() << "any idea" << LL_ENDL; }
static void doWarn() { LL_WARNS() << "aim west" << LL_ENDL; }
- static void doError() { LL_ERRS() << "ate eels" << LL_ENDL; }
+ static void doError() { CATCH(LL_ERRS(), "ate eels"); }
static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
};
@@ -716,7 +733,7 @@ public:
static void doDebug() { LL_DEBUGS() << "bed down" << LL_ENDL; }
static void doInfo() { LL_INFOS() << "buy iron" << LL_ENDL; }
static void doWarn() { LL_WARNS() << "bad word" << LL_ENDL; }
- static void doError() { LL_ERRS() << "big easy" << LL_ENDL; }
+ static void doError() { CATCH(LL_ERRS(), "big easy"); }
static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
};
@@ -781,33 +798,30 @@ namespace tut
// proper cached, efficient lookup of filtering
void ErrorTestObject::test<15>()
{
- LLError::setDefaultLevel(LLError::LEVEL_NONE);
-
- // Note that the setFatalHook in the ErrorTestData constructor
- // increments the shouldLogCallCount
+ LLError::setDefaultLevel(LLError::LEVEL_NONE);
TestAlpha::doInfo();
ensure_message_count(0);
- ensure_equals("first check", LLError::shouldLogCallCount(), 2);
+ ensure_equals("first check", LLError::shouldLogCallCount(), 1);
TestAlpha::doInfo();
ensure_message_count(0);
- ensure_equals("second check", LLError::shouldLogCallCount(), 2);
+ ensure_equals("second check", LLError::shouldLogCallCount(), 1);
LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG);
TestAlpha::doInfo();
ensure_message_count(1);
- ensure_equals("third check", LLError::shouldLogCallCount(), 3);
+ ensure_equals("third check", LLError::shouldLogCallCount(), 2);
TestAlpha::doInfo();
ensure_message_count(2);
- ensure_equals("fourth check", LLError::shouldLogCallCount(), 3);
+ ensure_equals("fourth check", LLError::shouldLogCallCount(), 2);
LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN);
TestAlpha::doInfo();
ensure_message_count(2);
- ensure_equals("fifth check", LLError::shouldLogCallCount(), 4);
+ ensure_equals("fifth check", LLError::shouldLogCallCount(), 3);
TestAlpha::doInfo();
ensure_message_count(2);
- ensure_equals("sixth check", LLError::shouldLogCallCount(), 4);
+ ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
}
template<> template<>
@@ -881,13 +895,10 @@ namespace tut
namespace
{
std::string writeTagWithSpaceReturningLocation()
- {
- LL_DEBUGS("Write Tag") << "not allowed" << LL_ENDL; int this_line = __LINE__;
-
- std::ostringstream location;
- location << LLError::abbreviateFile(__FILE__).c_str() << "(" << this_line << ")";
- return location.str();
- }
+ {
+ int this_line = __LINE__; CATCH(LL_DEBUGS("Write Tag"), "not allowed");
+ return locationString(this_line);
+ }
};
namespace tut
@@ -901,9 +912,9 @@ namespace tut
std::string location = writeTagWithSpaceReturningLocation();
std::string expected = "Space is not allowed in a log tag at " + location;
- ensure_message_field_equals(0, LEVEL_FIELD, "ERROR");
- ensure_message_field_equals(0, MSG_FIELD, expected);
- ensure("fatal callback called", fatalWasCalled);
+ ensure_message_field_equals(0, LEVEL_FIELD, "ERROR");
+ ensure_message_field_equals(0, MSG_FIELD, expected);
+ ensure("fatal callback called", fatalWasCalled);
}
}
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index a6c44d5fdd..3779fb41bc 100644
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -44,26 +44,24 @@
#include <list>
#include <string>
-// statically reference the function in test.cpp... it's short, we could
-// replicate, but better to reuse
-extern LLError::ErrFatalHookResult wouldHaveCrashed(const std::string& message);
-
struct WrapLLErrs
{
- WrapLLErrs()
+ WrapLLErrs():
// Resetting Settings discards the default Recorder that writes to
// stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the
// console output of successful tests, potentially confusing things.
- :mPriorErrorSettings(LLError::saveAndResetSettings())
- ,mPriorFatalHook(LLError::getFatalHook())
+ mPriorErrorSettings(LLError::saveAndResetSettings()),
+ // Save shutdown function called by LL_ERRS
+ mPriorFatal(LLError::getFatalFunction())
{
// Make LL_ERRS call our own operator() method
- LLError::setFatalHook(boost::bind(&WrapLLErrs::operator(), this, _1));
+ LLError::setFatalFunction(
+ [this](const std::string& message){ (*this)(message); });
}
~WrapLLErrs()
{
- LLError::setFatalHook(mPriorFatalHook);
+ LLError::setFatalFunction(mPriorFatal);
LLError::restoreSettings(mPriorErrorSettings);
}
@@ -72,7 +70,7 @@ struct WrapLLErrs
FatalException(const std::string& what): LLException(what) {}
};
- LLError::ErrFatalHookResult operator()(const std::string& message)
+ void operator()(const std::string& message)
{
// Save message for later in case consumer wants to sense the result directly
error = message;
@@ -108,7 +106,7 @@ struct WrapLLErrs
std::string error;
LLError::SettingsStoragePtr mPriorErrorSettings;
- LLError::FatalHook mPriorFatalHook;
+ LLError::FatalFunction mPriorFatal;
};
/**
@@ -198,12 +196,13 @@ public:
// with that output. If it turns out that saveAndResetSettings() has
// some bad effect, give up and just let the DEBUG level log messages
// display.
- : boost::noncopyable()
- , mOldSettings(LLError::saveAndResetSettings())
- , mPriorFatalHook(LLError::getFatalHook())
- , mRecorder(new CaptureLogRecorder())
+ : boost::noncopyable(),
+ mFatalFunction(LLError::getFatalFunction()),
+ mOldSettings(LLError::saveAndResetSettings()),
+ mRecorder(new CaptureLogRecorder())
{
- LLError::setFatalHook(wouldHaveCrashed);
+ // reinstate the FatalFunction we just reset
+ LLError::setFatalFunction(mFatalFunction);
LLError::setDefaultLevel(level);
LLError::addRecorder(mRecorder);
}
@@ -212,7 +211,6 @@ public:
{
LLError::removeRecorder(mRecorder);
LLError::restoreSettings(mOldSettings);
- LLError::setFatalHook(mPriorFatalHook);
}
/// Don't assume the message we want is necessarily the LAST log message
@@ -220,18 +218,18 @@ public:
/// for the sought string.
std::string messageWith(const std::string& search, bool required=true)
{
- return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->messageWith(search, required);
+ return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->messageWith(search, required);
}
std::ostream& streamto(std::ostream& out) const
{
- return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);
+ return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);
}
private:
+ LLError::FatalFunction mFatalFunction;
LLError::SettingsStoragePtr mOldSettings;
- LLError::FatalHook mPriorFatalHook;
- LLError::RecorderPtr mRecorder;
+ LLError::RecorderPtr mRecorder;
};
#endif /* ! defined(LL_WRAPLLERRS_H) */