Start up crash of LLErrorThread due to corrupt map in CheckLevelMap

Wrapped gSettings and the global objects returned by Settings::get() and
Globals::get() in AIThreadSafe, forcing thread-safe access.

This solves the problem of possible corruption of the various LevelMap's
in LLError::Settings due to thread unsafe accesses.
This commit is contained in:
Aleric Inglewood
2011-05-05 15:41:19 +02:00
parent 86a19e8e91
commit 92696623e0
15 changed files with 261 additions and 215 deletions

View File

@@ -55,6 +55,7 @@
#include "llsdserialize.h"
#include "llstl.h"
#include "lltimer.h"
#include "aithreadsafe.h"
extern apr_thread_mutex_t* gCallStacksLogMutexp;
@@ -357,12 +358,15 @@ namespace
void addCallSite(LLError::CallSite&);
void invalidateCallSites();
static Globals& get();
static AIThreadSafeSimple<Globals>& get();
// return the one instance of the globals
private:
CallSiteVector callSites;
friend class AIThreadSafeSimpleDC<Globals>; // Calls constructor.
friend class AIThreadSafeSimple<Globals>; // Calls destructor.
Globals()
: messageStreamInUse(false)
{ }
@@ -386,7 +390,7 @@ namespace
callSites.clear();
}
Globals& Globals::get()
AIThreadSafeSimple<Globals>& Globals::get()
{
/* This pattern, of returning a reference to a static function
variable, is to ensure that this global is constructed before
@@ -394,8 +398,8 @@ namespace
is.
See C++ FAQ Lite, sections 10.12 through 10.14
*/
static Globals* globals = new Globals;
return *globals;
static AIThreadSafeSimpleDCRootPool<Globals>* ts_globals_ptr = new AIThreadSafeSimpleDCRootPool<Globals>;
return *ts_globals_ptr;
}
}
@@ -424,13 +428,16 @@ namespace LLError
int shouldLogCallCounter;
static Settings& get();
static AIThreadSafeSimple<Settings>& get();
static void reset();
static Settings* saveAndReset();
static void restore(Settings*);
static AIThreadSafeSimple<Settings>* saveAndReset();
static void restore(AIThreadSafeSimple<Settings>*);
private:
friend class AIThreadSafeBits<Settings>; // Calls destructor.
friend class AIThreadSafeSimpleDC<Settings>; // Calls constructor.
Settings()
: printLocation(false),
defaultLevel(LLError::LEVEL_DEBUG),
@@ -446,53 +453,42 @@ namespace LLError
for_each(recorders.begin(), recorders.end(),
DeletePointer());
}
static Settings*& getPtr();
static AIThreadSafeSimple<Settings>* sSettings;
};
// Pointer to current AIThreadSafeSimple<Settings> object if any (NULL otherwise).
AIThreadSafeSimple<Settings>* Settings::sSettings;
Settings& Settings::get()
AIThreadSafeSimple<Settings>& Settings::get()
{
Settings* p = getPtr();
if (!p)
if (!sSettings)
{
reset();
p = getPtr();
}
return *p;
return *sSettings;
}
void Settings::reset()
{
Globals::get().invalidateCallSites();
Settings*& p = getPtr();
delete p;
p = new Settings();
AIAccess<Globals>(Globals::get())->invalidateCallSites();
delete sSettings;
sSettings = new AIThreadSafeSimpleDC<Settings>;
}
Settings* Settings::saveAndReset()
AIThreadSafeSimple<Settings>* Settings::saveAndReset()
{
Globals::get().invalidateCallSites();
Settings*& p = getPtr();
Settings* originalSettings = p;
p = new Settings();
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIThreadSafeSimple<Settings>* originalSettings = sSettings;
sSettings = new AIThreadSafeSimpleDC<Settings>;
return originalSettings;
}
void Settings::restore(Settings* originalSettings)
void Settings::restore(AIThreadSafeSimple<Settings>* originalSettings)
{
Globals::get().invalidateCallSites();
Settings*& p = getPtr();
delete p;
p = originalSettings;
}
Settings*& Settings::getPtr()
{
static Settings* currentSettings = NULL;
return currentSettings;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
delete sSettings;
sSettings = originalSettings;
}
}
@@ -596,68 +592,64 @@ namespace LLError
commonInit(dir);
}
void setPrintLocation(AIAccess<Settings> const& settings_w, bool print)
{
settings_w->printLocation = print;
}
void setPrintLocation(bool print)
{
Settings& s = Settings::get();
s.printLocation = print;
setPrintLocation(AIAccess<Settings>(Settings::get()), print);
}
void setFatalFunction(const FatalFunction& f)
{
Settings& s = Settings::get();
s.crashFunction = f;
AIAccess<Settings>(Settings::get())->crashFunction = f;
}
FatalFunction getFatalFunction()
{
Settings& s = Settings::get();
return s.crashFunction;
return AIAccess<Settings>(Settings::get())->crashFunction;
}
void setTimeFunction(TimeFunction f)
{
Settings& s = Settings::get();
s.timeFunction = f;
AIAccess<Settings>(Settings::get())->timeFunction = f;
}
void setDefaultLevel(AIAccess<Settings> const& settings_w, ELevel level)
{
AIAccess<Globals>(Globals::get())->invalidateCallSites();
settings_w->defaultLevel = level;
}
void setDefaultLevel(ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.defaultLevel = level;
setDefaultLevel(AIAccess<Settings>(Settings::get()), level);
}
void setFunctionLevel(const std::string& function_name, ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.functionLevelMap[function_name] = level;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIAccess<Settings>(Settings::get())->functionLevelMap[function_name] = level;
}
void setClassLevel(const std::string& class_name, ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.classLevelMap[class_name] = level;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIAccess<Settings>(Settings::get())->classLevelMap[class_name] = level;
}
void setFileLevel(const std::string& file_name, ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.fileLevelMap[file_name] = level;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIAccess<Settings>(Settings::get())->fileLevelMap[file_name] = level;
}
void setTagLevel(const std::string& tag_name, ELevel level)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
g.invalidateCallSites();
s.tagLevelMap[tag_name] = level;
AIAccess<Globals>(Globals::get())->invalidateCallSites();
AIAccess<Settings>(Settings::get())->tagLevelMap[tag_name] = level;
}
}
@@ -701,18 +693,16 @@ namespace LLError
{
void configure(const LLSD& config)
{
Globals& g = Globals::get();
Settings& s = Settings::get();
AIAccess<Settings> settings_w(Settings::get());
AIAccess<Globals>(Globals::get())->invalidateCallSites();
settings_w->functionLevelMap.clear();
settings_w->classLevelMap.clear();
settings_w->fileLevelMap.clear();
settings_w->tagLevelMap.clear();
settings_w->uniqueLogMessages.clear();
g.invalidateCallSites();
s.functionLevelMap.clear();
s.classLevelMap.clear();
s.fileLevelMap.clear();
s.tagLevelMap.clear();
s.uniqueLogMessages.clear();
setPrintLocation(config["print-location"]);
setDefaultLevel(decodeLevel(config["default-level"]));
setPrintLocation(settings_w, config["print-location"]);
setDefaultLevel(settings_w, decodeLevel(config["default-level"]));
LLSD sets = config["settings"];
LLSD::array_const_iterator a, end;
@@ -722,10 +712,10 @@ namespace LLError
ELevel level = decodeLevel(entry["level"]);
setLevels(s.functionLevelMap, entry["functions"], level);
setLevels(s.classLevelMap, entry["classes"], level);
setLevels(s.fileLevelMap, entry["files"], level);
setLevels(s.tagLevelMap, entry["tags"], level);
setLevels(settings_w->functionLevelMap, entry["functions"], level);
setLevels(settings_w->classLevelMap, entry["classes"], level);
setLevels(settings_w->fileLevelMap, entry["files"], level);
setLevels(settings_w->tagLevelMap, entry["tags"], level);
}
}
}
@@ -742,26 +732,34 @@ namespace LLError
void addRecorder(Recorder* recorder)
void addRecorder(AIAccess<Settings> const& settings_w, Recorder* recorder)
{
if (recorder == NULL)
{
return;
}
Settings& s = Settings::get();
s.recorders.push_back(recorder);
settings_w->recorders.push_back(recorder);
}
void addRecorder(Recorder* recorder)
{
addRecorder(AIAccess<Settings>(Settings::get()), recorder);
}
void removeRecorder(AIAccess<Settings> const& settings_w, Recorder* recorder)
{
if (recorder == NULL)
{
return;
}
settings_w->recorders.erase(
std::remove(settings_w->recorders.begin(), settings_w->recorders.end(), recorder),
settings_w->recorders.end());
}
void removeRecorder(Recorder* recorder)
{
if (recorder == NULL)
{
return;
}
Settings& s = Settings::get();
s.recorders.erase(
std::remove(s.recorders.begin(), s.recorders.end(), recorder),
s.recorders.end());
removeRecorder(AIAccess<Settings>(Settings::get()), recorder);
}
}
@@ -769,12 +767,12 @@ namespace LLError
{
void logToFile(const std::string& file_name)
{
LLError::Settings& s = LLError::Settings::get();
AIAccess<Settings> settings_w(Settings::get());
removeRecorder(s.fileRecorder);
delete s.fileRecorder;
s.fileRecorder = NULL;
s.fileRecorderFileName.clear();
removeRecorder(settings_w, settings_w->fileRecorder);
delete settings_w->fileRecorder;
settings_w->fileRecorder = NULL;
settings_w->fileRecorderFileName.clear();
if (file_name.empty())
{
@@ -788,54 +786,51 @@ namespace LLError
return;
}
s.fileRecorderFileName = file_name;
s.fileRecorder = f;
addRecorder(f);
settings_w->fileRecorderFileName = file_name;
settings_w->fileRecorder = f;
addRecorder(settings_w, f);
}
void logToFixedBuffer(LLLineBuffer* fixedBuffer)
{
LLError::Settings& s = LLError::Settings::get();
AIAccess<Settings> settings_w(Settings::get());
removeRecorder(s.fixedBufferRecorder);
delete s.fixedBufferRecorder;
s.fixedBufferRecorder = NULL;
removeRecorder(settings_w, settings_w->fixedBufferRecorder);
delete settings_w->fixedBufferRecorder;
settings_w->fixedBufferRecorder = NULL;
if (!fixedBuffer)
{
return;
}
s.fixedBufferRecorder = new RecordToFixedBuffer(fixedBuffer);
addRecorder(s.fixedBufferRecorder);
settings_w->fixedBufferRecorder = new RecordToFixedBuffer(fixedBuffer);
addRecorder(settings_w, settings_w->fixedBufferRecorder);
}
std::string logFileName()
{
LLError::Settings& s = LLError::Settings::get();
return s.fileRecorderFileName;
return AIAccess<Settings>(Settings::get())->fileRecorderFileName;
}
}
namespace
{
void writeToRecorders(LLError::ELevel level, const std::string& message)
void writeToRecorders(AIAccess<LLError::Settings> const& settings_w, LLError::ELevel level, const std::string& message)
{
LLError::Settings& s = LLError::Settings::get();
std::string messageWithTime;
for (Recorders::const_iterator i = s.recorders.begin();
i != s.recorders.end();
for (Recorders::const_iterator i = settings_w->recorders.begin();
i != settings_w->recorders.end();
++i)
{
LLError::Recorder* r = *i;
if (r->wantsTime() && s.timeFunction != NULL)
if (r->wantsTime() && settings_w->timeFunction != NULL)
{
if (messageWithTime.empty())
{
messageWithTime = s.timeFunction() + " " + message;
messageWithTime = settings_w->timeFunction() + " " + message;
}
r->recordMessage(level, messageWithTime);
@@ -955,10 +950,9 @@ namespace LLError
return false;
}
Globals& g = Globals::get();
Settings& s = Settings::get();
AIAccess<Settings> settings_w(Settings::get());
s.shouldLogCallCounter += 1;
settings_w->shouldLogCallCounter += 1;
std::string class_name = className(site.mClassInfo);
std::string function_name = functionName(site.mFunction);
@@ -967,20 +961,20 @@ namespace LLError
function_name = class_name + "::" + function_name;
}
ELevel compareLevel = s.defaultLevel;
ELevel compareLevel = settings_w->defaultLevel;
// The most specific match found will be used as the log level,
// since the computation short circuits.
// So, in increasing order of importance:
// Default < Broad Tag < File < Class < Function < Narrow Tag
((site.mNarrowTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mNarrowTag, compareLevel) : false)
|| checkLevelMap(s.functionLevelMap, function_name, compareLevel)
|| checkLevelMap(s.classLevelMap, class_name, compareLevel)
|| checkLevelMap(s.fileLevelMap, abbreviateFile(site.mFile), compareLevel)
|| ((site.mBroadTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mBroadTag, compareLevel) : false);
((site.mNarrowTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mNarrowTag, compareLevel) : false)
|| checkLevelMap(settings_w->functionLevelMap, function_name, compareLevel)
|| checkLevelMap(settings_w->classLevelMap, class_name, compareLevel)
|| checkLevelMap(settings_w->fileLevelMap, abbreviateFile(site.mFile), compareLevel)
|| ((site.mBroadTag != NULL) ? checkLevelMap(settings_w->tagLevelMap, site.mBroadTag, compareLevel) : false);
site.mCached = true;
g.addCallSite(site);
AIAccess<Globals>(Globals::get())->addCallSite(site);
return site.mShouldLog = site.mLevel >= compareLevel;
}
@@ -990,16 +984,16 @@ namespace LLError
LogLock lock;
if (lock.ok())
{
Globals& g = Globals::get();
AIAccess<Globals> globals(Globals::get());
if (!g.messageStreamInUse)
if (!globals->messageStreamInUse)
{
g.messageStreamInUse = true;
return &g.messageStream;
globals->messageStreamInUse = true;
return &globals->messageStream; // Returns pointer to member of unlocked object, apparently "protected" by having set globals->messageStreamInUse.
}
}
return new std::ostringstream;
return new std::ostringstream; // Holy memory leak.
}
void Log::flush(std::ostringstream* out, char* message)
@@ -1020,12 +1014,12 @@ namespace LLError
message[127] = '\0' ;
}
Globals& g = Globals::get();
if (out == &g.messageStream)
AIAccess<Globals> globals(Globals::get());
if (out == &globals->messageStream)
{
g.messageStream.clear();
g.messageStream.str("");
g.messageStreamInUse = false;
globals->messageStream.clear();
globals->messageStream.str("");
globals->messageStreamInUse = false;
}
else
{
@@ -1042,28 +1036,31 @@ namespace LLError
return;
}
Globals& g = Globals::get();
Settings& s = Settings::get();
std::string message = out->str();
if (out == &g.messageStream)
{
g.messageStream.clear();
g.messageStream.str("");
g.messageStreamInUse = false;
}
else
{
delete out;
AIAccess<Globals> globals(Globals::get());
if (out == &globals->messageStream)
{
globals->messageStream.clear();
globals->messageStream.str("");
globals->messageStreamInUse = false;
}
else
{
delete out;
}
}
AIAccess<Settings> settings_w(Settings::get());
if (site.mLevel == LEVEL_ERROR)
{
std::ostringstream fatalMessage;
fatalMessage << abbreviateFile(site.mFile)
<< "(" << site.mLine << ") : error";
writeToRecorders(site.mLevel, fatalMessage.str());
writeToRecorders(settings_w, site.mLevel, fatalMessage.str());
}
@@ -1078,7 +1075,7 @@ namespace LLError
default: prefix << "XXX: "; break;
};
if (s.printLocation)
if (settings_w->printLocation)
{
prefix << abbreviateFile(site.mFile)
<< "(" << site.mLine << ") : ";
@@ -1096,8 +1093,8 @@ namespace LLError
if (site.mPrintOnce)
{
std::map<std::string, unsigned int>::iterator messageIter = s.uniqueLogMessages.find(message);
if (messageIter != s.uniqueLogMessages.end())
std::map<std::string, unsigned int>::iterator messageIter = settings_w->uniqueLogMessages.find(message);
if (messageIter != settings_w->uniqueLogMessages.end())
{
messageIter->second++;
unsigned int num_messages = messageIter->second;
@@ -1113,14 +1110,14 @@ namespace LLError
else
{
prefix << "ONCE: ";
s.uniqueLogMessages[message] = 1;
settings_w->uniqueLogMessages[message] = 1;
}
}
if (site.mPrintOnce)
{
std::map<std::string, unsigned int>::iterator messageIter = s.uniqueLogMessages.find(message);
if (messageIter != s.uniqueLogMessages.end())
std::map<std::string, unsigned int>::iterator messageIter = settings_w->uniqueLogMessages.find(message);
if (messageIter != settings_w->uniqueLogMessages.end())
{
messageIter->second++;
unsigned int num_messages = messageIter->second;
@@ -1136,18 +1133,18 @@ namespace LLError
else
{
prefix << "ONCE: ";
s.uniqueLogMessages[message] = 1;
settings_w->uniqueLogMessages[message] = 1;
}
}
prefix << message;
message = prefix.str();
writeToRecorders(site.mLevel, message);
writeToRecorders(settings_w, site.mLevel, message);
if (site.mLevel == LEVEL_ERROR && s.crashFunction)
if (site.mLevel == LEVEL_ERROR && settings_w->crashFunction)
{
s.crashFunction(message);
settings_w->crashFunction(message);
}
}
}
@@ -1157,14 +1154,16 @@ namespace LLError
namespace LLError
{
Settings* saveAndResetSettings()
class ThreadSafeSettings { };
ThreadSafeSettings* saveAndResetSettings()
{
return Settings::saveAndReset();
return reinterpret_cast<ThreadSafeSettings*>(Settings::saveAndReset());
}
void restoreSettings(Settings* s)
void restoreSettings(ThreadSafeSettings* s)
{
return Settings::restore(s);
Settings::restore(reinterpret_cast<AIThreadSafeSimple<Settings>*>(s));
}
std::string removePrefix(std::string& s, const std::string& p)
@@ -1210,8 +1209,7 @@ namespace LLError
int shouldLogCallCount()
{
Settings& s = Settings::get();
return s.shouldLogCallCounter;
return AIAccess<Settings>(Settings::get())->shouldLogCallCounter;
}
#if LL_WINDOWS