summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt9
-rw-r--r--indra/llcommon/llapp.cpp10
-rw-r--r--indra/llcommon/llerror.cpp70
-rw-r--r--indra/llcommon/llerror.h32
-rw-r--r--indra/llcommon/llerrorcontrol.h47
-rw-r--r--indra/llcommon/llleap.cpp31
-rw-r--r--indra/llcommon/llsingleton.cpp10
-rw-r--r--indra/llcommon/tests/llerror_test.cpp25
-rw-r--r--indra/llcommon/tests/wrapllerrs.h28
9 files changed, 119 insertions, 143 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index af41b9e460..e3afa5eca4 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -5,6 +5,7 @@ project(llcommon)
include(00-Common)
include(LLCommon)
+include(bugsplat)
include(Linking)
include(Boost)
include(LLSharedLibs)
@@ -257,10 +258,10 @@ set(llcommon_HEADER_FILES
set_source_files_properties(${llcommon_HEADER_FILES}
PROPERTIES HEADER_FILE_ONLY TRUE)
-if (BUGSPLAT_DB)
- set_source_files_properties(llapp.cpp
- PROPERTIES COMPILE_DEFINITIONS "LL_BUGSPLAT")
-endif (BUGSPLAT_DB)
+if (USE_BUGSPLAT)
+ set_source_files_properties(${llcommon_SOURCE_FILES}
+ PROPERTIES COMPILE_DEFINITIONS "${BUGSPLAT_DEFINE}")
+endif (USE_BUGSPLAT)
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 421af3006e..27960371f1 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -526,7 +526,12 @@ void LLApp::setupErrorHandling(bool second_instance)
#endif // LL_LINUX
#endif // ! LL_WINDOWS
+
+#ifdef LL_BUGSPLAT
+ // do not start our own error thread
+#else // ! LL_BUGSPLAT
startErrorThread();
+#endif
}
void LLApp::startErrorThread()
@@ -774,7 +779,9 @@ void setup_signals()
act.sa_flags = SA_SIGINFO;
// Synchronous signals
+# ifndef LL_BUGSPLAT
sigaction(SIGABRT, &act, NULL);
+# endif
sigaction(SIGALRM, &act, NULL);
sigaction(SIGBUS, &act, NULL);
sigaction(SIGFPE, &act, NULL);
@@ -811,7 +818,9 @@ void clear_signals()
act.sa_flags = SA_SIGINFO;
// Synchronous signals
+# ifndef LL_BUGSPLAT
sigaction(SIGABRT, &act, NULL);
+# endif
sigaction(SIGALRM, &act, NULL);
sigaction(SIGBUS, &act, NULL);
sigaction(SIGFPE, &act, NULL);
@@ -864,6 +873,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
return;
case SIGABRT:
+ // Note that this handler is not set for SIGABRT when using Bugsplat
// Abort just results in termination of the app, no funky error handling.
if (LLApp::sLogInSignal)
{
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index b46f49ba34..d98229fb0a 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -25,6 +25,7 @@
* $/LicenseInfo$
*/
+#define _LLERROR_CPP_
#include "linden_common.h"
#include "llerror.h"
@@ -488,7 +489,7 @@ namespace LLError
LevelMap mTagLevelMap;
std::map<std::string, unsigned int> mUniqueLogMessages;
- LLError::FatalFunction mCrashFunction;
+ LLError::FatalHook mFatalHook;
LLError::TimeFunction mTimeFunction;
Recorders mRecorders;
@@ -528,7 +529,7 @@ namespace LLError
mFileLevelMap(),
mTagLevelMap(),
mUniqueLogMessages(),
- mCrashFunction(NULL),
+ mFatalHook(NULL),
mTimeFunction(NULL),
mRecorders(),
mFileRecorder(),
@@ -689,7 +690,6 @@ namespace
LLError::setDefaultLevel(LLError::LEVEL_INFO);
LLError::setAlwaysFlush(true);
LLError::setEnabledLogTypesMask(0xFFFFFFFF);
- LLError::setFatalFunction(LLError::crashAndLoop);
LLError::setTimeFunction(LLError::utcTime);
// log_to_stderr is only false in the unit and integration tests to keep builds quieter
@@ -725,16 +725,20 @@ namespace LLError
commonInit(user_dir, app_dir, log_to_stderr);
}
- void setFatalFunction(const FatalFunction& f)
+ void setFatalHook(const FatalHook& fatal_hook)
{
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- s->mCrashFunction = f;
- }
-
- FatalFunction getFatalFunction()
+ LL_DEBUGS("FatalHook") << "set fatal hook to " << (fatal_hook ? "non-null" : "null")
+ << " was " << (s->mFatalHook ? "non-null" : "null")
+ << LL_ENDL;
+ s->mFatalHook = fatal_hook;
+ }
+
+ FatalHook getFatalHook()
{
SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- return s->mCrashFunction;
+ LL_DEBUGS("FatalHook") << "read fatal hook was " << (s->mFatalHook ? "non-null" : "null") << LL_ENDL;
+ return s->mFatalHook;
}
std::string getFatalMessage()
@@ -1312,12 +1316,12 @@ namespace LLError
return ;
}
- void Log::flush(std::ostringstream* out, const CallSite& site)
+ ErrFatalHookResult Log::flush(std::ostringstream* out, const CallSite& site)
{
LLMutexTrylock lock(&gLogMutex,5);
if (!lock.isLocked())
{
- return;
+ return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal
}
// If we hit a logging request very late during shutdown processing,
@@ -1325,7 +1329,7 @@ namespace LLError
// DO NOT resurrect them.
if (Settings::wasDeleted() || Globals::wasDeleted())
{
- return;
+ return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal
}
Globals* g = Globals::getInstance();
@@ -1359,7 +1363,7 @@ namespace LLError
}
else
{
- return;
+ return ERR_DO_NOT_CRASH; // because this wasn't logged, it cannot be fatal
}
}
else
@@ -1375,12 +1379,19 @@ namespace LLError
if (site.mLevel == LEVEL_ERROR)
{
- g->mFatalMessage = message;
- if (s->mCrashFunction)
- {
- s->mCrashFunction(message);
- }
+ if (s->mFatalHook)
+ {
+ return s->mFatalHook(message);
+ }
+ else
+ {
+ return ERR_CRASH; // calling macro should crash
+ }
}
+ else
+ {
+ return ERR_DO_NOT_CRASH; // not ERROR, so do not crash
+ }
}
}
@@ -1443,29 +1454,6 @@ namespace LLError
return s->mShouldLogCallCounter;
}
-#if LL_WINDOWS
- // VC80 was optimizing the error away.
- #pragma optimize("", off)
-#endif
- void crashAndLoop(const std::string& message)
- {
- // Now, we go kaboom!
- int* make_me_crash = NULL;
-
- *make_me_crash = 0;
-
- while(true)
- {
- // Loop forever, in case the crash didn't work?
- }
-
- // this is an attempt to let Coverity and other semantic scanners know that this function won't be returning ever.
- exit(EXIT_FAILURE);
- }
-#if LL_WINDOWS
- #pragma optimize("", on)
-#endif
-
std::string utcTime()
{
time_t now = time(NULL);
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 0a78229555..6bdb2e852f 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -194,13 +194,19 @@ namespace LLError
struct CallSite;
+ enum ErrFatalHookResult { ERR_DO_NOT_CRASH, ERR_CRASH };
+
class LL_COMMON_API Log
{
public:
static bool shouldLog(CallSite&);
static std::ostringstream* out();
+
static void flush(std::ostringstream* out, char* message);
- static void flush(std::ostringstream*, const CallSite&);
+
+ // returns false iff the calling macro should crash
+ static ErrFatalHookResult flush(std::ostringstream*, const CallSite&);
+
static std::string demangle(const char* mangled);
};
@@ -266,7 +272,7 @@ namespace LLError
//when LLAppViewer::handleViewerCrash() is triggered.
//
//Note: to be simple, efficient and necessary to keep track of correct call stacks,
- //LLCallStacks is designed not to be thread-safe.
+ //LLCallStacks is designed not to be thread-safe.
//so try not to use it in multiple parallel threads at same time.
//Used in a single thread at a time is fine.
class LL_COMMON_API LLCallStacks
@@ -367,10 +373,24 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
#define LL_NEWLINE '\n'
-#define LL_ENDL \
- LLError::End(); \
- LLError::Log::flush(_out, _site); \
- } \
+#ifdef _LLERROR_CPP_
+volatile int* gCauseCrash = NULL;
+#else
+volatile extern int* gCauseCrash;
+#endif // _LLERROR_CPP_
+
+// Use this only in LL_ERRS or in a place that LL_ERRS may not be used
+#define LLERROR_CRASH \
+{ \
+ *gCauseCrash = 0; \
+ exit(*gCauseCrash); \
+}
+
+#define LL_ENDL \
+ LLError::End(); \
+ if (LLError::ERR_CRASH == LLError::Log::flush(_out, _site)) \
+ LLERROR_CRASH \
+ } \
} while(0)
// NEW Macros for debugging, allow the passing of a string tag
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
index 276d22fc36..e7c5241cc9 100644
--- a/indra/llcommon/llerrorcontrol.h
+++ b/indra/llcommon/llerrorcontrol.h
@@ -93,40 +93,27 @@ namespace LLError
Control functions.
*/
- typedef boost::function<void(const std::string&)> FatalFunction;
- LL_COMMON_API void crashAndLoop(const std::string& message);
- // Default fatal function: access null pointer and loops forever
-
- LL_COMMON_API void setFatalFunction(const FatalFunction&);
- // The fatal function will be called when an message of LEVEL_ERROR
+ // A FatalHook is called if set using setFatalHook; its return controls
+ // whether or not the calling error logging code should crash.
+ // ERR_DO_NOT_CRASH should be used only in test code.
+ typedef boost::function<LLError::ErrFatalHookResult(const std::string&)> FatalHook;
+
+ /// Supplement and control the default behavior of crashing on LL_ERRS
+ /// This may be used to suppress crashes only in test code;
+ /// otherwise, the FatalHook should always either return
+ /// the result from the previous hook (see getFatalHook below),
+ /// or return LLError::ERR_CRASH
+ LL_COMMON_API void setFatalHook(const FatalHook& fatal_hook);
+ // The fatal_hook function will be called when an message of LEVEL_ERROR
// is logged. Note: supressing a LEVEL_ERROR message from being logged
- // (by, for example, setting a class level to LEVEL_NONE), will keep
- // the that message from causing the fatal funciton to be invoked.
+ // (by, for example, setting a class level to LEVEL_NONE), will also
+ // prevent the fatal_hook being called and the resulting deliberate crash
- LL_COMMON_API FatalFunction getFatalFunction();
- // Retrieve the previously-set FatalFunction
+ /// Retrieve the previously-set FatalHook
+ LL_COMMON_API FatalHook getFatalHook();
LL_COMMON_API std::string getFatalMessage();
- // Retrieve the message last passed to FatalFunction, if any
-
- /// temporarily override the FatalFunction for the duration of a
- /// particular scope, e.g. for unit tests
- class LL_COMMON_API OverrideFatalFunction
- {
- public:
- OverrideFatalFunction(const FatalFunction& func):
- mPrev(getFatalFunction())
- {
- setFatalFunction(func);
- }
- ~OverrideFatalFunction()
- {
- setFatalFunction(mPrev);
- }
-
- private:
- FatalFunction mPrev;
- };
+ // Retrieve the message last passed to LL_ERRS, if any
typedef std::string (*TimeFunction)();
LL_COMMON_API std::string utcTime();
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
index cf8f8cc6a5..8293c35516 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -59,7 +59,6 @@ public:
// pump name -- so it should NOT need tweaking for uniqueness.
mReplyPump(LLUUID::generateNewID().asString()),
mExpect(0),
- mPrevFatalFunction(LLError::getFatalFunction()),
// Instantiate a distinct LLLeapListener for this plugin. (Every
// plugin will want its own collection of managed listeners, etc.)
// Pass it a callback to our connect() method, so it can send events
@@ -145,9 +144,6 @@ public:
mStderrConnection = childerr.getPump()
.listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1));
- // For our lifespan, intercept any LL_ERRS so we can notify plugin
- LLError::setFatalFunction(boost::bind(&LLLeapImpl::fatalFunction, this, _1));
-
// Send child a preliminary event reporting our own reply-pump name --
// which would otherwise be pretty tricky to guess!
wstdin(mReplyPump.getName(),
@@ -162,8 +158,6 @@ public:
virtual ~LLLeapImpl()
{
LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL;
- // Restore original FatalFunction
- LLError::setFatalFunction(mPrevFatalFunction);
}
// Listener for failed launch attempt
@@ -377,30 +371,6 @@ public:
return false;
}
- void fatalFunction(const std::string& error)
- {
- // Notify plugin
- LLSD event;
- event["type"] = "error";
- event["error"] = error;
- mReplyPump.post(event);
-
- // All the above really accomplished was to buffer the serialized
- // event in our WritePipe. Have to pump mainloop a couple times to
- // really write it out there... but time out in case we can't write.
- LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
- LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
- LLSD nop;
- F64 until = (LLTimer::getElapsedSeconds() + 2).value();
- while (childin.size() && LLTimer::getElapsedSeconds() < until)
- {
- mainloop.post(nop);
- }
-
- // forward the call to the previous FatalFunction
- mPrevFatalFunction(error);
- }
-
private:
/// We always want to listen on mReplyPump with wstdin(); under some
/// circumstances we'll also echo other LLEventPumps to the plugin.
@@ -421,7 +391,6 @@ private:
mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection;
boost::scoped_ptr<LLEventPump::Blocker> mBlocker;
LLProcess::ReadPipe::size_type mExpect;
- LLError::FatalFunction mPrevFatalFunction;
boost::scoped_ptr<LLLeapListener> mListener;
};
diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp
index c45c144570..6f254ef670 100644
--- a/indra/llcommon/llsingleton.cpp
+++ b/indra/llcommon/llsingleton.cpp
@@ -461,15 +461,7 @@ void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, co
// https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG)
std::ostringstream out;
out << p1 << p2 << p3 << p4;
- auto crash = LLError::getFatalFunction();
- if (crash)
- {
- crash(out.str());
- }
- else
- {
- LLError::crashAndLoop(out.str());
- }
+ LLERROR_CRASH;
}
std::string LLSingletonBase::demangle(const char* mangled)
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index 8e1f4c14ac..cdc2bf8c87 100644
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -70,7 +70,11 @@ namespace
namespace
{
static bool fatalWasCalled;
- void fatalCall(const std::string&) { fatalWasCalled = true; }
+ LLError::ErrFatalHookResult fatalHook(const std::string&)
+ {
+ fatalWasCalled = true;
+ return LLError::ERR_DO_NOT_CRASH;
+ }
}
namespace tut
@@ -120,7 +124,7 @@ namespace tut
mPriorErrorSettings = LLError::saveAndResetSettings();
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
- LLError::setFatalFunction(fatalCall);
+ LLError::setFatalHook(fatalHook);
LLError::addRecorder(mRecorder);
}
@@ -777,30 +781,33 @@ namespace tut
// proper cached, efficient lookup of filtering
void ErrorTestObject::test<15>()
{
- LLError::setDefaultLevel(LLError::LEVEL_NONE);
+ LLError::setDefaultLevel(LLError::LEVEL_NONE);
+
+ // Note that the setFatalHook in the ErrorTestData constructor
+ // increments the shouldLogCallCount
TestAlpha::doInfo();
ensure_message_count(0);
- ensure_equals("first check", LLError::shouldLogCallCount(), 1);
+ ensure_equals("first check", LLError::shouldLogCallCount(), 2);
TestAlpha::doInfo();
ensure_message_count(0);
- ensure_equals("second check", LLError::shouldLogCallCount(), 1);
+ ensure_equals("second check", LLError::shouldLogCallCount(), 2);
LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG);
TestAlpha::doInfo();
ensure_message_count(1);
- ensure_equals("third check", LLError::shouldLogCallCount(), 2);
+ ensure_equals("third check", LLError::shouldLogCallCount(), 3);
TestAlpha::doInfo();
ensure_message_count(2);
- ensure_equals("fourth check", LLError::shouldLogCallCount(), 2);
+ ensure_equals("fourth check", LLError::shouldLogCallCount(), 3);
LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN);
TestAlpha::doInfo();
ensure_message_count(2);
- ensure_equals("fifth check", LLError::shouldLogCallCount(), 3);
+ ensure_equals("fifth check", LLError::shouldLogCallCount(), 4);
TestAlpha::doInfo();
ensure_message_count(2);
- ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
+ ensure_equals("sixth check", LLError::shouldLogCallCount(), 4);
}
template<> template<>
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index b07d5afbd8..a6c44d5fdd 100644
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -46,25 +46,24 @@
// statically reference the function in test.cpp... it's short, we could
// replicate, but better to reuse
-extern void wouldHaveCrashed(const std::string& message);
+extern LLError::ErrFatalHookResult wouldHaveCrashed(const std::string& message);
struct WrapLLErrs
{
- WrapLLErrs():
+ WrapLLErrs()
// Resetting Settings discards the default Recorder that writes to
// stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the
// console output of successful tests, potentially confusing things.
- mPriorErrorSettings(LLError::saveAndResetSettings()),
- // Save shutdown function called by LL_ERRS
- mPriorFatal(LLError::getFatalFunction())
+ :mPriorErrorSettings(LLError::saveAndResetSettings())
+ ,mPriorFatalHook(LLError::getFatalHook())
{
// Make LL_ERRS call our own operator() method
- LLError::setFatalFunction(boost::bind(&WrapLLErrs::operator(), this, _1));
+ LLError::setFatalHook(boost::bind(&WrapLLErrs::operator(), this, _1));
}
~WrapLLErrs()
{
- LLError::setFatalFunction(mPriorFatal);
+ LLError::setFatalHook(mPriorFatalHook);
LLError::restoreSettings(mPriorErrorSettings);
}
@@ -73,7 +72,7 @@ struct WrapLLErrs
FatalException(const std::string& what): LLException(what) {}
};
- void operator()(const std::string& message)
+ LLError::ErrFatalHookResult operator()(const std::string& message)
{
// Save message for later in case consumer wants to sense the result directly
error = message;
@@ -109,7 +108,7 @@ struct WrapLLErrs
std::string error;
LLError::SettingsStoragePtr mPriorErrorSettings;
- LLError::FatalFunction mPriorFatal;
+ LLError::FatalHook mPriorFatalHook;
};
/**
@@ -199,11 +198,12 @@ public:
// with that output. If it turns out that saveAndResetSettings() has
// some bad effect, give up and just let the DEBUG level log messages
// display.
- : boost::noncopyable(),
- mOldSettings(LLError::saveAndResetSettings()),
- mRecorder(new CaptureLogRecorder())
+ : boost::noncopyable()
+ , mOldSettings(LLError::saveAndResetSettings())
+ , mPriorFatalHook(LLError::getFatalHook())
+ , mRecorder(new CaptureLogRecorder())
{
- LLError::setFatalFunction(wouldHaveCrashed);
+ LLError::setFatalHook(wouldHaveCrashed);
LLError::setDefaultLevel(level);
LLError::addRecorder(mRecorder);
}
@@ -212,6 +212,7 @@ public:
{
LLError::removeRecorder(mRecorder);
LLError::restoreSettings(mOldSettings);
+ LLError::setFatalHook(mPriorFatalHook);
}
/// Don't assume the message we want is necessarily the LAST log message
@@ -229,6 +230,7 @@ public:
private:
LLError::SettingsStoragePtr mOldSettings;
+ LLError::FatalHook mPriorFatalHook;
LLError::RecorderPtr mRecorder;
};