From 34b2f2d1f841f7b7f93386d66be6943910cd055b Mon Sep 17 00:00:00 2001
From: Stinson Linden <stinson@lindenlab.com>
Date: Fri, 16 May 2014 22:44:25 +0100
Subject: [PATCH] MAINT-4009: First pass refactoring to eliminate memory
 related to error reporting that is not properly cleaned up.

---
 indra/llcommon/llerror.cpp            |  54 ++++++------
 indra/llcommon/llerrorcontrol.h       |  15 ++--
 indra/llcommon/tests/llerror_test.cpp |  76 ++++++++++-------
 indra/llcommon/tests/wrapllerrs.h     | 113 ++++++++++++++------------
 indra/newview/llviewerwindow.cpp      |  25 ++++--
 indra/test/test.cpp                   |  62 ++++++++++----
 6 files changed, 205 insertions(+), 140 deletions(-)

diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 4f721fabdf0..09a2d38e1ae 100755
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -357,7 +357,7 @@ namespace
 
 
 	typedef std::map<std::string, LLError::ELevel> LevelMap;
-	typedef std::vector<LLError::Recorder*> Recorders;
+	typedef std::vector<LLError::RecorderPtr> Recorders;
 	typedef std::vector<LLError::CallSite*> CallSiteVector;
 
 	class Globals : public LLSingleton<Globals>
@@ -419,8 +419,8 @@ namespace LLError
 		LLError::TimeFunction               mTimeFunction;
 		
 		Recorders                           mRecorders;
-		Recorder*                           mFileRecorder;
-		Recorder*                           mFixedBufferRecorder;
+		RecorderPtr                           mFileRecorder;
+		RecorderPtr                           mFixedBufferRecorder;
 		std::string                         mFileRecorderFileName;
 		
 		int									mShouldLogCallCounter;
@@ -437,14 +437,13 @@ namespace LLError
 			mDefaultLevel(LLError::LEVEL_DEBUG),
 			mCrashFunction(),
 			mTimeFunction(NULL),
-			mFileRecorder(NULL),
-			mFixedBufferRecorder(NULL),
+			mFileRecorder(),
+			mFixedBufferRecorder(),
 			mShouldLogCallCounter(0)
 			{ }
 		
 		~Settings()
 		{
-			for_each(mRecorders.begin(), mRecorders.end(), DeletePointer());
 			mRecorders.clear();
 		}
 		
@@ -603,11 +602,13 @@ namespace
 		// log_to_stderr is only false in the unit and integration tests to keep builds quieter
 		if (log_to_stderr && shouldLogToStderr())
 		{
-			LLError::addRecorder(new RecordToStderr(stderrLogWantsTime()));
+			LLError::RecorderPtr recordToStdErr(new RecordToStderr(stderrLogWantsTime()));
+			LLError::addRecorder(recordToStdErr);
 		}
 		
 #if LL_WINDOWS
-		LLError::addRecorder(new RecordToWinDebug);
+		LLError::RecorderPtr recordToWinDebug(new RecordToWinDebug());
+		LLError::addRecorder(recordToWinDebug);
 #endif
 
 		LogControlFile& e = LogControlFile::fromDirectory(dir);
@@ -635,7 +636,8 @@ namespace LLError
 		}
 		commonInit(dir);
 #if !LL_WINDOWS
-		addRecorder(new RecordToSyslog(identity));
+		LLError::RecorderPtr recordToSyslog(new RecordToSyslog(identity));
+		addRecorder(recordToSyslog);
 #endif
 	}
 
@@ -821,9 +823,9 @@ namespace LLError
 		return mWantsFunctionName;
 	}
 
-	void addRecorder(Recorder* recorder)
+	void addRecorder(RecorderPtr recorder)
 	{
-		if (recorder == NULL)
+		if (!recorder)
 		{
 			return;
 		}
@@ -831,9 +833,9 @@ namespace LLError
 		s.mRecorders.push_back(recorder);
 	}
 
-	void removeRecorder(Recorder* recorder)
+	void removeRecorder(RecorderPtr recorder)
 	{
-		if (recorder == NULL)
+		if (!recorder)
 		{
 			return;
 		}
@@ -850,8 +852,7 @@ namespace LLError
 		LLError::Settings& s = LLError::Settings::get();
 
 		removeRecorder(s.mFileRecorder);
-		delete s.mFileRecorder;
-		s.mFileRecorder = NULL;
+		s.mFileRecorder.reset();
 		s.mFileRecorderFileName.clear();
 		
 		if (file_name.empty())
@@ -859,16 +860,13 @@ namespace LLError
 			return;
 		}
 		
-		RecordToFile* f = new RecordToFile(file_name);
-		if (!f->okay())
+		RecorderPtr recordToFile(new RecordToFile(file_name));
+		if (boost::dynamic_pointer_cast<RecordToFile>(recordToFile)->okay())
 		{
-			delete f;
-			return;
+			s.mFileRecorderFileName = file_name;
+			s.mFileRecorder = recordToFile;
+			addRecorder(recordToFile);
 		}
-
-		s.mFileRecorderFileName = file_name;
-		s.mFileRecorder = f;
-		addRecorder(f);
 	}
 	
 	void logToFixedBuffer(LLLineBuffer* fixedBuffer)
@@ -876,16 +874,16 @@ namespace LLError
 		LLError::Settings& s = LLError::Settings::get();
 
 		removeRecorder(s.mFixedBufferRecorder);
-		delete s.mFixedBufferRecorder;
-		s.mFixedBufferRecorder = NULL;
+		s.mFixedBufferRecorder.reset();
 		
 		if (!fixedBuffer)
 		{
 			return;
 		}
 		
-		s.mFixedBufferRecorder = new RecordToFixedBuffer(fixedBuffer);
-		addRecorder(s.mFixedBufferRecorder);
+		RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer));
+		s.mFixedBufferRecorder = recordToFixedBuffer;
+		addRecorder(recordToFixedBuffer);
 	}
 
 	std::string logFileName()
@@ -906,7 +904,7 @@ namespace
 			i != s.mRecorders.end();
 			++i)
 		{
-			LLError::Recorder* r = *i;
+			LLError::RecorderPtr r = *i;
 			
 			std::ostringstream message_stream;
 
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
index aab695094c7..681f4442345 100755
--- a/indra/llcommon/llerrorcontrol.h
+++ b/indra/llcommon/llerrorcontrol.h
@@ -30,6 +30,7 @@
 
 #include "llerror.h"
 #include "boost/function.hpp"
+#include "boost/shared_ptr.hpp"
 #include <string>
 
 class LLSD;
@@ -156,16 +157,14 @@ namespace LLError
 				mWantsFunctionName;
 	};
 
+	typedef boost::shared_ptr<Recorder> RecorderPtr;
+
 	/**
-	 * @NOTE: addRecorder() conveys ownership to the underlying Settings
-	 * object -- when destroyed, it will @em delete the passed Recorder*!
-	 */
-	LL_COMMON_API void addRecorder(Recorder*);
-	/**
-	 * @NOTE: removeRecorder() reclaims ownership of the Recorder*: its
-	 * lifespan becomes the caller's problem.
+	 * @NOTE: addRecorder() and removeRecorder() uses the boost::shared_ptr to allow for shared ownership
+	 * while still ensuring that the allocated memory is eventually freed
 	 */
-	LL_COMMON_API void removeRecorder(Recorder*);
+	LL_COMMON_API void addRecorder(RecorderPtr);
+	LL_COMMON_API void removeRecorder(RecorderPtr);
 		// each error message is passed to each recorder via recordMessage()
 
 	LL_COMMON_API void logToFile(const std::string& filename);
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index b28c5ba4b31..6cecd6bbc8f 100755
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -56,9 +56,9 @@ namespace tut
 	{
 	public:
 		TestRecorder() { mWantsTime = false; }
-		~TestRecorder() { LLError::removeRecorder(this); }
+		virtual ~TestRecorder() {  }
 
-		void recordMessage(LLError::ELevel level,
+		virtual void recordMessage(LLError::ELevel level,
 						   const std::string& message)
 		{
 			mMessages.push_back(message);
@@ -85,15 +85,11 @@ namespace tut
 
 	struct ErrorTestData
 	{
-		// addRecorder() expects to be able to later delete the passed
-		// Recorder*. Even though removeRecorder() reclaims ownership, passing
-		// a pointer to a data member rather than a heap Recorder subclass
-		// instance would just be Wrong.
-		TestRecorder* mRecorder;
+		LLError::RecorderPtr mRecorder;
 		LLError::Settings* mPriorErrorSettings;
 
 		ErrorTestData():
-			mRecorder(new TestRecorder)
+			mRecorder(new TestRecorder())
 		{
 			fatalWasCalled = false;
 
@@ -106,13 +102,32 @@ namespace tut
 		~ErrorTestData()
 		{
 			LLError::removeRecorder(mRecorder);
-			delete mRecorder;
 			LLError::restoreSettings(mPriorErrorSettings);
 		}
 
+		int countMessages()
+		{
+			return boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->countMessages();
+		}
+
+		void clearMessages()
+		{
+			boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->clearMessages();
+		}
+
+		void setWantsTime(bool t)
+		{
+			boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->setWantsTime(t);
+		}
+
+		std::string message(int n)
+		{
+			return boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->message(n);
+		}
+
 		void ensure_message_count(int expectedCount)
 		{
-			ensure_equals("message count", mRecorder->countMessages(), expectedCount);
+			ensure_equals("message count", countMessages(), expectedCount);
 		}
 
 		void ensure_message_contains(int n, const std::string& expectedText)
@@ -120,7 +135,7 @@ namespace tut
 			std::ostringstream test_name;
 			test_name << "testing message " << n;
 
-			ensure_contains(test_name.str(), mRecorder->message(n), expectedText);
+			ensure_contains(test_name.str(), message(n), expectedText);
 		}
 
 		void ensure_message_does_not_contain(int n, const std::string& expectedText)
@@ -128,7 +143,7 @@ namespace tut
 			std::ostringstream test_name;
 			test_name << "testing message " << n;
 
-			ensure_does_not_contain(test_name.str(), mRecorder->message(n), expectedText);
+			ensure_does_not_contain(test_name.str(), message(n), expectedText);
 		}
 	};
 
@@ -385,15 +400,15 @@ namespace
 	}
 
 	typedef std::string (*LogFromFunction)(bool);
-	void testLogName(tut::TestRecorder* recorder, LogFromFunction f,
+	void testLogName(LLError::RecorderPtr recorder, LogFromFunction f,
 		const std::string& class_name = "")
 	{
-		recorder->clearMessages();
+		boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->clearMessages();
 		std::string name = f(false);
 		f(true);
 
-		std::string messageWithoutName = recorder->message(0);
-		std::string messageWithName = recorder->message(1);
+		std::string messageWithoutName = boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(0);
+		std::string messageWithName = boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(1);
 
 		ensure_has(name + " logged without name",
 			messageWithoutName, name);
@@ -528,12 +543,12 @@ namespace tut
 	{
 		LLError::setTimeFunction(roswell);
 
-		mRecorder->setWantsTime(false);
+		setWantsTime(false);
 		ufoSighting();
 		ensure_message_contains(0, "ufo");
 		ensure_message_does_not_contain(0, roswell());
 
-		mRecorder->setWantsTime(true);
+		setWantsTime(true);
 		ufoSighting();
 		ensure_message_contains(1, "ufo");
 		ensure_message_contains(1, roswell());
@@ -545,13 +560,13 @@ namespace tut
 	{
 		LLError::setPrintLocation(true);
 		LLError::setTimeFunction(roswell);
-		mRecorder->setWantsTime(true);
+		setWantsTime(true);
 		std::string location,
 					function;
 		writeReturningLocationAndFunction(location, function);
 
 		ensure_equals("order is location time type function message",
-			mRecorder->message(0),
+			message(0),
 			location + roswell() + " INFO: " + function + ": apple");
 	}
 
@@ -559,19 +574,19 @@ namespace tut
 		// multiple recorders
 	void ErrorTestObject::test<11>()
 	{
-		TestRecorder* altRecorder(new TestRecorder);
+		LLError::RecorderPtr altRecorder(new TestRecorder());
 		LLError::addRecorder(altRecorder);
 
 		LL_INFOS() << "boo" << LL_ENDL;
 
 		ensure_message_contains(0, "boo");
-		ensure_equals("alt recorder count", altRecorder->countMessages(), 1);
-		ensure_contains("alt recorder message 0", altRecorder->message(0), "boo");
+		ensure_equals("alt recorder count", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 1);
+		ensure_contains("alt recorder message 0", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(0), "boo");
 
 		LLError::setTimeFunction(roswell);
 
-		TestRecorder* anotherRecorder(new TestRecorder);
-		anotherRecorder->setWantsTime(true);
+		LLError::RecorderPtr anotherRecorder(new TestRecorder());
+		boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->setWantsTime(true);
 		LLError::addRecorder(anotherRecorder);
 
 		LL_INFOS() << "baz" << LL_ENDL;
@@ -579,10 +594,13 @@ namespace tut
 		std::string when = roswell();
 
 		ensure_message_does_not_contain(1, when);
-		ensure_equals("alt recorder count", altRecorder->countMessages(), 2);
-		ensure_does_not_contain("alt recorder message 1", altRecorder->message(1), when);
-		ensure_equals("another recorder count", anotherRecorder->countMessages(), 1);
-		ensure_contains("another recorder message 0", anotherRecorder->message(0), when);
+		ensure_equals("alt recorder count", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 2);
+		ensure_does_not_contain("alt recorder message 1", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(1), when);
+		ensure_equals("another recorder count", boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->countMessages(), 1);
+		ensure_contains("another recorder message 0", boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->message(0), when);
+
+		LLError::removeRecorder(altRecorder);
+		LLError::removeRecorder(anotherRecorder);
 	}
 }
 
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index 3137bd8fea9..0a528446713 100755
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -38,6 +38,7 @@
 #include "stringize.h"
 #include <boost/bind.hpp>
 #include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
 #include <list>
 #include <string>
 #include <stdexcept>
@@ -85,68 +86,25 @@ struct WrapLLErrs
     LLError::FatalFunction mPriorFatal;
 };
 
-/**
- * LLError::addRecorder() accepts ownership of the passed Recorder* -- it
- * expects to be able to delete it later. CaptureLog isa Recorder whose
- * pointer we want to be able to pass without any ownership implications.
- * For such cases, instantiate a new RecorderProxy(yourRecorder) and pass
- * that. Your heap RecorderProxy might later be deleted, but not yourRecorder.
- */
-class RecorderProxy: public LLError::Recorder
-{
-public:
-    RecorderProxy(LLError::Recorder* recorder):
-        mRecorder(recorder)
-    {}
-
-    virtual void recordMessage(LLError::ELevel level, const std::string& message)
-    {
-        mRecorder->recordMessage(level, message);
-    }
-
-    virtual bool wantsTime()
-    {
-        return mRecorder->wantsTime();
-    }
-
-private:
-    LLError::Recorder* mRecorder;
-};
-
 /**
  * Capture log messages. This is adapted (simplified) from the one in
  * llerror_test.cpp.
  */
-class CaptureLog : public LLError::Recorder, public boost::noncopyable
+class CaptureLogRecorder : public LLError::Recorder, public boost::noncopyable
 {
 public:
-    CaptureLog(LLError::ELevel level=LLError::LEVEL_DEBUG):
-        // Mostly what we're trying to accomplish by saving and resetting
-        // LLError::Settings is to bypass the default RecordToStderr and
-        // RecordToWinDebug Recorders. As these are visible only inside
-        // llerror.cpp, we can't just call LLError::removeRecorder() with
-        // each. For certain tests we need to produce, capture and examine
-        // DEBUG log messages -- but we don't want to spam the user's console
-        // with that output. If it turns out that saveAndResetSettings() has
-        // some bad effect, give up and just let the DEBUG level log messages
-        // display.
-        mOldSettings(LLError::saveAndResetSettings()),
-        mProxy(new RecorderProxy(this))
+    CaptureLogRecorder()
+		: LLError::Recorder(),
+		boost::noncopyable(),
+		mMessages()
     {
-        LLError::setFatalFunction(wouldHaveCrashed);
-        LLError::setDefaultLevel(level);
-        LLError::addRecorder(mProxy);
     }
 
-    ~CaptureLog()
+    virtual ~CaptureLogRecorder()
     {
-        LLError::removeRecorder(mProxy);
-        delete mProxy;
-        LLError::restoreSettings(mOldSettings);
     }
 
-    void recordMessage(LLError::ELevel level,
-                       const std::string& message)
+    virtual void recordMessage(LLError::ELevel level, const std::string& message)
     {
         mMessages.push_back(message);
     }
@@ -154,7 +112,7 @@ class CaptureLog : public LLError::Recorder, public boost::noncopyable
     /// Don't assume the message we want is necessarily the LAST log message
     /// emitted by the underlying code; search backwards through all messages
     /// for the sought string.
-    std::string messageWith(const std::string& search, bool required=true)
+    std::string messageWith(const std::string& search, bool required)
     {
         for (MessageList::const_reverse_iterator rmi(mMessages.rbegin()), rmend(mMessages.rend());
              rmi != rmend; ++rmi)
@@ -187,14 +145,63 @@ class CaptureLog : public LLError::Recorder, public boost::noncopyable
         return out;
     }
 
+private:
     typedef std::list<std::string> MessageList;
     MessageList mMessages;
+};
+
+/**
+ * Capture log messages. This is adapted (simplified) from the one in
+ * llerror_test.cpp.
+ */
+class CaptureLog : public boost::noncopyable
+{
+public:
+    CaptureLog(LLError::ELevel level=LLError::LEVEL_DEBUG)
+        // Mostly what we're trying to accomplish by saving and resetting
+        // LLError::Settings is to bypass the default RecordToStderr and
+        // RecordToWinDebug Recorders. As these are visible only inside
+        // llerror.cpp, we can't just call LLError::removeRecorder() with
+        // each. For certain tests we need to produce, capture and examine
+        // DEBUG log messages -- but we don't want to spam the user's console
+        // 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())
+    {
+        LLError::setFatalFunction(wouldHaveCrashed);
+        LLError::setDefaultLevel(level);
+        LLError::addRecorder(mRecorder);
+    }
+
+    ~CaptureLog()
+    {
+        LLError::removeRecorder(mRecorder);
+        LLError::restoreSettings(mOldSettings);
+    }
+
+    /// Don't assume the message we want is necessarily the LAST log message
+    /// emitted by the underlying code; search backwards through all messages
+    /// for the sought string.
+    std::string messageWith(const std::string& search, bool required=true)
+    {
+		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);
+    }
+
+private:
     LLError::Settings* mOldSettings;
-    LLError::Recorder* mProxy;
+	LLError::RecorderPtr mRecorder;
 };
 
 inline
-std::ostream& operator<<(std::ostream& out, const CaptureLog& log)
+std::ostream& operator<<(std::ostream& out, const CaptureLogRecorder& log)
 {
     return log.streamto(out);
 }
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 2cb8e6a3abd..8e5d5779681 100755
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -261,7 +261,7 @@ std::string	LLViewerWindow::sMovieBaseName;
 LLTrace::SampleStatHandle<> LLViewerWindow::sMouseVelocityStat("Mouse Velocity");
 
 
-class RecordToChatConsole : public LLError::Recorder, public LLSingleton<RecordToChatConsole>
+class RecordToChatConsoleRecorder : public LLError::Recorder
 {
 public:
 	virtual void recordMessage(LLError::ELevel level,
@@ -285,6 +285,22 @@ class RecordToChatConsole : public LLError::Recorder, public LLSingleton<RecordT
 	}
 };
 
+class RecordToChatConsole : public LLSingleton<RecordToChatConsole>
+{
+public:
+	RecordToChatConsole()
+		: LLSingleton<RecordToChatConsole>(),
+		mRecorder(new RecordToChatConsoleRecorder())
+	{
+	}
+
+	void startRecorder() { LLError::addRecorder(mRecorder); }
+	void stopRecorder() { LLError::removeRecorder(mRecorder); }
+
+private:
+	LLError::RecorderPtr mRecorder;
+};
+
 ////////////////////////////////////////////////////////////////////////////
 //
 // LLDebugText
@@ -1889,11 +1905,11 @@ void LLViewerWindow::initBase()
 	// optionally forward warnings to chat console/chat floater
 	// for qa runs and dev builds
 #if  !LL_RELEASE_FOR_DOWNLOAD
-	LLError::addRecorder(RecordToChatConsole::getInstance());
+	RecordToChatConsole::getInstance()->startRecorder();
 #else
 	if(gSavedSettings.getBOOL("QAMode"))
 	{
-		LLError::addRecorder(RecordToChatConsole::getInstance());
+		RecordToChatConsole::getInstance()->startRecorder();
 	}
 #endif
 
@@ -2040,8 +2056,7 @@ void LLViewerWindow::initWorldUI()
 void LLViewerWindow::shutdownViews()
 {
 	// clean up warning logger
-	LLError::removeRecorder(RecordToChatConsole::getInstance());
-
+	RecordToChatConsole::getInstance()->stopRecorder();
 	LL_INFOS() << "Warning logger is cleaned." << LL_ENDL ;
 
 	delete mDebugText;
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index 10f71a2843e..0dd21b6a056 100755
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -95,25 +95,20 @@ class LLReplayLog
 	virtual void replay(std::ostream&) {}
 };
 
-class LLReplayLogReal: public LLReplayLog, public LLError::Recorder, public boost::noncopyable
+class RecordToTempFile : public LLError::Recorder, public boost::noncopyable
 {
 public:
-	LLReplayLogReal(LLError::ELevel level, apr_pool_t* pool):
-		mOldSettings(LLError::saveAndResetSettings()),
-		mProxy(new RecorderProxy(this)),
-		mTempFile("log", "", pool),		// create file
-		mFile(mTempFile.getName().c_str()) // open it
+	RecordToTempFile(apr_pool_t* pPool)
+		: LLError::Recorder(),
+		boost::noncopyable(),
+		mTempFile("log", "", pPool),
+		mFile(mTempFile.getName().c_str())
 	{
-		LLError::setFatalFunction(wouldHaveCrashed);
-		LLError::setDefaultLevel(level);
-		LLError::addRecorder(mProxy);
 	}
 
-	virtual ~LLReplayLogReal()
+	virtual ~RecordToTempFile()
 	{
-		LLError::removeRecorder(mProxy);
-		delete mProxy;
-		LLError::restoreSettings(mOldSettings);
+		mFile.close();
 	}
 
 	virtual void recordMessage(LLError::ELevel level, const std::string& message)
@@ -121,13 +116,13 @@ class LLReplayLogReal: public LLReplayLog, public LLError::Recorder, public boos
 		mFile << message << std::endl;
 	}
 
-	virtual void reset()
+	void reset()
 	{
 		mFile.close();
 		mFile.open(mTempFile.getName().c_str());
 	}
 
-	virtual void replay(std::ostream& out)
+	void replay(std::ostream& out)
 	{
 		mFile.close();
 		std::ifstream inf(mTempFile.getName().c_str());
@@ -139,12 +134,45 @@ class LLReplayLogReal: public LLReplayLog, public LLError::Recorder, public boos
 	}
 
 private:
-	LLError::Settings* mOldSettings;
-	LLError::Recorder* mProxy;
 	NamedTempFile mTempFile;
 	std::ofstream mFile;
 };
 
+class LLReplayLogReal: public LLReplayLog, public boost::noncopyable
+{
+public:
+	LLReplayLogReal(LLError::ELevel level, apr_pool_t* pool)
+		: LLReplayLog(),
+		boost::noncopyable(),
+		mOldSettings(LLError::saveAndResetSettings()),
+		mRecorder(new RecordToTempFile(pool))
+	{
+		LLError::setFatalFunction(wouldHaveCrashed);
+		LLError::setDefaultLevel(level);
+		LLError::addRecorder(mRecorder);
+	}
+
+	virtual ~LLReplayLogReal()
+	{
+		LLError::removeRecorder(mRecorder);
+		LLError::restoreSettings(mOldSettings);
+	}
+
+	virtual void reset()
+	{
+		boost::dynamic_pointer_cast<RecordToTempFile>(mRecorder)->reset();
+	}
+
+	virtual void replay(std::ostream& out)
+	{
+		boost::dynamic_pointer_cast<RecordToTempFile>(mRecorder)->replay(out);
+	}
+
+private:
+	LLError::Settings* mOldSettings;
+	LLError::RecorderPtr mRecorder;
+};
+
 class LLTestCallback : public tut::callback
 {
 public:
-- 
GitLab