Skip to content
Snippets Groups Projects
Commit c71e6222 authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

MAINT-5232: Add DEBUG logging to LLSingleton dependency tracking.

Specifically, add DEBUG logging to the code that maintains the stack of
LLSingletons currently being initialized. This involves passing
LLSingletonBase's constructor the name of LLSingleton's template parameter
subclass, since during that constructor typeid(*this).name() will only produce
"LLSingletonBase".

Also add logdebugs() and oktolog() helper functions.
parent 4af7e496
Branches
Tags
No related merge requests found
...@@ -38,7 +38,11 @@ ...@@ -38,7 +38,11 @@
namespace { namespace {
void log(LLError::ELevel level, void log(LLError::ELevel level,
const char* p1="", const char* p2="", const char* p3="", const char* p4=""); const char* p1, const char* p2, const char* p3, const char* p4);
void logdebugs(const char* p1="", const char* p2="", const char* p3="", const char* p4="");
bool oktolog();
} // anonymous namespace } // anonymous namespace
// Our master list of all LLSingletons is itself an LLSingleton. We used to // Our master list of all LLSingletons is itself an LLSingleton. We used to
...@@ -95,38 +99,64 @@ LLSingletonBase::list_t& LLSingletonBase::get_initializing() ...@@ -95,38 +99,64 @@ LLSingletonBase::list_t& LLSingletonBase::get_initializing()
return sList; return sList;
} }
LLSingletonBase::LLSingletonBase(): LLSingletonBase::LLSingletonBase(const char* name):
mCleaned(false), mCleaned(false),
mDeleteSingleton(NULL) mDeleteSingleton(NULL)
{ {
// Make this the currently-initializing LLSingleton. // Make this the currently-initializing LLSingleton.
push_initializing(); push_initializing(name);
} }
LLSingletonBase::~LLSingletonBase() {} LLSingletonBase::~LLSingletonBase() {}
void LLSingletonBase::push_initializing() void LLSingletonBase::push_initializing(const char* name)
{ {
// log BEFORE pushing so logging singletons don't cry circularity
log_initializing("Pushing", name);
get_initializing().push_back(this); get_initializing().push_back(this);
} }
void LLSingletonBase::pop_initializing() void LLSingletonBase::pop_initializing()
{ {
list_t& list(get_initializing()); list_t& list(get_initializing());
if (list.empty()) if (list.empty())
{ {
logerrs("Underflow in stack of currently-initializing LLSingletons at ", logerrs("Underflow in stack of currently-initializing LLSingletons at ",
demangle(typeid(*this).name()).c_str(), "::getInstance()"); demangle(typeid(*this).name()).c_str(), "::getInstance()");
} }
if (list.back() != this)
{ // Now we know list.back() exists: capture it
LLSingletonBase* back(list.back()); LLSingletonBase* back(list.back());
// and pop it
list.pop_back();
if (back != this)
{
logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ", logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ",
demangle(typeid(*this).name()).c_str(), "::getInstance() trying to pop ", demangle(typeid(*this).name()).c_str(), "::getInstance() trying to pop ",
demangle(typeid(*back).name()).c_str()); demangle(typeid(*back).name()).c_str());
} }
// Here we're sure that list.back() == this. Whew, pop it.
list.pop_back(); // log AFTER popping so logging singletons don't cry circularity
log_initializing("Popping", typeid(*back).name());
}
//static
void LLSingletonBase::log_initializing(const char* verb, const char* name)
{
if (oktolog())
{
LL_DEBUGS("LLSingleton") << verb << ' ' << demangle(name) << ';';
list_t& list(get_initializing());
for (list_t::const_reverse_iterator ri(list.rbegin()), rend(list.rend());
ri != rend; ++ri)
{
LLSingletonBase* sb(*ri);
LL_CONT << ' ' << demangle(typeid(*sb).name());
}
LL_ENDL;
}
} }
void LLSingletonBase::capture_dependency(EInitState initState) void LLSingletonBase::capture_dependency(EInitState initState)
...@@ -181,7 +211,7 @@ void LLSingletonBase::capture_dependency(EInitState initState) ...@@ -181,7 +211,7 @@ void LLSingletonBase::capture_dependency(EInitState initState)
if (current->mDepends.insert(this).second) if (current->mDepends.insert(this).second)
{ {
// only log the FIRST time we hit this dependency! // only log the FIRST time we hit this dependency!
log(LLError::LEVEL_DEBUG, demangle(typeid(*current).name()).c_str(), logdebugs(demangle(typeid(*current).name()).c_str(),
" depends on ", demangle(typeid(*this).name()).c_str()); " depends on ", demangle(typeid(*this).name()).c_str());
} }
} }
...@@ -240,7 +270,7 @@ void LLSingletonBase::cleanupAll() ...@@ -240,7 +270,7 @@ void LLSingletonBase::cleanupAll()
{ {
sp->mCleaned = true; sp->mCleaned = true;
log(LLError::LEVEL_DEBUG, "calling ", logdebugs("calling ",
demangle(typeid(*sp).name()).c_str(), "::cleanupSingleton()"); demangle(typeid(*sp).name()).c_str(), "::cleanupSingleton()");
try try
{ {
...@@ -280,7 +310,7 @@ void LLSingletonBase::deleteAll() ...@@ -280,7 +310,7 @@ void LLSingletonBase::deleteAll()
else else
{ {
// properly initialized: call it. // properly initialized: call it.
log(LLError::LEVEL_DEBUG, "calling ", name.c_str(), "::deleteSingleton()"); logdebugs("calling ", name.c_str(), "::deleteSingleton()");
// From this point on, DO NOT DEREFERENCE sp! // From this point on, DO NOT DEREFERENCE sp!
sp->mDeleteSingleton(); sp->mDeleteSingleton();
} }
...@@ -331,6 +361,12 @@ void intrusive_ptr_release(LLSingletonBase::MasterRefcount* mrc) ...@@ -331,6 +361,12 @@ void intrusive_ptr_release(LLSingletonBase::MasterRefcount* mrc)
/*---------------------------- Logging helpers -----------------------------*/ /*---------------------------- Logging helpers -----------------------------*/
namespace { namespace {
bool oktolog()
{
// See comments in log() below.
return sMasterRefcount.refcount && LLError::is_available();
}
void log(LLError::ELevel level, void log(LLError::ELevel level,
const char* p1, const char* p2, const char* p3, const char* p4) const char* p1, const char* p2, const char* p3, const char* p4)
{ {
...@@ -362,6 +398,11 @@ void log(LLError::ELevel level, ...@@ -362,6 +398,11 @@ void log(LLError::ELevel level,
std::cerr << p1 << p2 << p3 << p4 << std::endl; std::cerr << p1 << p2 << p3 << p4 << std::endl;
} }
} }
void logdebugs(const char* p1, const char* p2, const char* p3, const char* p4)
{
log(LLError::LEVEL_DEBUG, p1, p2, p3, p4);
}
} // anonymous namespace } // anonymous namespace
//static //static
......
...@@ -66,8 +66,10 @@ class LLSingletonBase: private boost::noncopyable ...@@ -66,8 +66,10 @@ class LLSingletonBase: private boost::noncopyable
} EInitState; } EInitState;
// Base-class constructor should only be invoked by the DERIVED_TYPE // Base-class constructor should only be invoked by the DERIVED_TYPE
// constructor. // constructor, which passes the DERIVED_TYPE class name for logging
LLSingletonBase(); // purposes. Within LLSingletonBase::LLSingletonBase, of course the
// formula typeid(*this).name() produces "LLSingletonBase".
LLSingletonBase(const char* name);
virtual ~LLSingletonBase(); virtual ~LLSingletonBase();
// Every new LLSingleton should be added to/removed from the master list // Every new LLSingleton should be added to/removed from the master list
...@@ -87,11 +89,15 @@ class LLSingletonBase: private boost::noncopyable ...@@ -87,11 +89,15 @@ class LLSingletonBase: private boost::noncopyable
// single C++ scope, else we'd use RAII to track it. But we do know that // single C++ scope, else we'd use RAII to track it. But we do know that
// LLSingletonBase's constructor definitely runs just before // LLSingletonBase's constructor definitely runs just before
// LLSingleton's, which runs just before the specific subclass's. // LLSingleton's, which runs just before the specific subclass's.
void push_initializing(); void push_initializing(const char*);
// LLSingleton is, and must remain, the only caller to initSingleton(). // LLSingleton is, and must remain, the only caller to initSingleton().
// That being the case, we control exactly when it happens -- and we can // That being the case, we control exactly when it happens -- and we can
// pop the stack immediately thereafter. // pop the stack immediately thereafter.
void pop_initializing(); void pop_initializing();
private:
// logging
static void log_initializing(const char* verb, const char* name);
protected:
// If a given call to B::getInstance() happens during either A::A() or // If a given call to B::getInstance() happens during either A::A() or
// A::initSingleton(), record that A directly depends on B. // A::initSingleton(), record that A directly depends on B.
void capture_dependency(EInitState); void capture_dependency(EInitState);
...@@ -281,7 +287,9 @@ class LLSingleton : public LLSingletonBase ...@@ -281,7 +287,9 @@ class LLSingleton : public LLSingletonBase
}; };
protected: protected:
LLSingleton() // Use typeid(DERIVED_TYPE) rather than typeid(*this) because, until our
// constructor completes, *this isn't yet a full-fledged DERIVED_TYPE.
LLSingleton(): LLSingletonBase(typeid(DERIVED_TYPE).name())
{ {
// populate base-class function pointer with the static // populate base-class function pointer with the static
// deleteSingleton() function for this particular specialization // deleteSingleton() function for this particular specialization
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment