summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2016-08-31 14:25:55 -0400
committerNat Goodspeed <nat@lindenlab.com>2016-08-31 14:25:55 -0400
commitf5ccfff54299becdbff81661622375d319e5ea7b (patch)
tree8b3d8b8c9112307b500933ab9c4d01a894f21070 /indra
parent37d3993a59f01bae55049b08d196733121b54f7f (diff)
MAINT-5232: Add DEBUG logging to LLSingleton operations.
Specifically, log as LLSingleton captures inter-Singleton dependencies. Also log cleanupAll() calls to cleanupSingleton() and deleteAll() calls to deleteSingleton(), since they happen in an implicitly-determined order. But do not log anything during the implicit LLSingletonBase::deleteAll() call triggered by the runtime destroying the last LLSingleton's static data. That's too late in the run; even std::cerr might already have been destroyed!
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/llsingleton.cpp29
1 files changed, 28 insertions, 1 deletions
diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp
index c3e8f6c7c7..76789e438e 100644
--- a/indra/llcommon/llsingleton.cpp
+++ b/indra/llcommon/llsingleton.cpp
@@ -36,6 +36,11 @@
#include <sstream>
#include <stdexcept>
+namespace {
+void log(LLError::ELevel level,
+ const char* p1="", const char* p2="", const char* p3="", const char* p4="");
+} // anonymous namespace
+
// Our master list of all LLSingletons is itself an LLSingleton. We used to
// store it in a function-local static, but that could get destroyed before
// the last of the LLSingletons -- and ~LLSingletonBase() definitely wants to
@@ -172,7 +177,13 @@ void LLSingletonBase::capture_dependency(EInitState initState)
// Record the dependency.
// initializing.back() is the LLSingletonBase* currently being
// initialized. Store 'this' in its mDepends set.
- initializing.back()->mDepends.insert(this);
+ LLSingletonBase* current(initializing.back());
+ if (current->mDepends.insert(this).second)
+ {
+ // only log the FIRST time we hit this dependency!
+ log(LLError::LEVEL_DEBUG, demangle(typeid(*current).name()).c_str(),
+ " depends on ", demangle(typeid(*this).name()).c_str());
+ }
}
}
}
@@ -229,6 +240,8 @@ void LLSingletonBase::cleanupAll()
{
sp->mCleaned = true;
+ log(LLError::LEVEL_DEBUG, "calling ",
+ demangle(typeid(*sp).name()).c_str(), "::cleanupSingleton()");
try
{
sp->cleanupSingleton();
@@ -267,6 +280,7 @@ void LLSingletonBase::deleteAll()
else
{
// properly initialized: call it.
+ log(LLError::LEVEL_DEBUG, "calling ", name.c_str(), "::deleteSingleton()");
// From this point on, DO NOT DEREFERENCE sp!
sp->mDeleteSingleton();
}
@@ -320,6 +334,19 @@ namespace {
void log(LLError::ELevel level,
const char* p1, const char* p2, const char* p3, const char* p4)
{
+ // Check whether we're in the implicit final LLSingletonBase::deleteAll()
+ // call. We've carefully arranged for deleteAll() to be called when the
+ // last SingletonLifetimeManager instance is destroyed -- in other words,
+ // when the last translation unit containing an LLSingleton instance
+ // cleans up static data. That could happen after std::cerr is destroyed!
+ // 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. As we do not, just
+ // don't log anything during implicit final deleteAll(). Detect that by
+ // the master refcount having gone to zero.
+ if (sMasterRefcount.refcount == 0)
+ return;
+
// 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