From 860ff2f7e2a7fe932dfb7c148f0dbc0067018038 Mon Sep 17 00:00:00 2001
From: Richard Linden <none@none>
Date: Wed, 7 Nov 2012 00:38:21 -0800
Subject: [PATCH] SH-3499 WIP Ensure asset stats output is correct fixed trace
 data gathering and routing from background thread simplified slave->master
 thread communication (eliminated redundant recording and proxy object)
 improved performance of fast timer data gathering (slow iterators)

---
 indra/llcommon/llapr.h                        |   3 +-
 indra/llcommon/llfasttimer.cpp                |   8 +-
 indra/llcommon/llqueuedthread.cpp             |   5 +-
 indra/llcommon/lltrace.h                      |   5 +-
 indra/llcommon/lltracerecording.cpp           | 189 +++++++++++++-----
 indra/llcommon/lltracerecording.h             | 112 ++++++-----
 indra/llcommon/lltracethreadrecorder.cpp      |  19 +-
 indra/llcommon/lltracethreadrecorder.h        |  11 +-
 indra/newview/llviewerassetstats.cpp          |   2 +-
 indra/newview/llviewerstats.cpp               |   5 +-
 .../newview/tests/llviewerassetstats_test.cpp |   1 -
 11 files changed, 224 insertions(+), 136 deletions(-)

diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index 821274aeb30..510725ffc63 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -334,14 +334,13 @@ class LLThreadLocalPointer : public LLThreadLocalPointerBase
 	{}
 
 	explicit LLThreadLocalPointer(T* value)
-		:	LLThreadLocalPointerBase(&cleanup)
 	{
 		set(value);
 	}
 
 
 	LLThreadLocalPointer(const LLThreadLocalPointer<T>& other)
-	:	LLThreadLocalPointerBase(other, &cleanup)
+	:	LLThreadLocalPointerBase(other)
 	{
 		set(other.get());		
 	}
diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
index 0abaf730638..4f67004773f 100644
--- a/indra/llcommon/llfasttimer.cpp
+++ b/indra/llcommon/llfasttimer.cpp
@@ -319,7 +319,7 @@ void LLFastTimer::NamedTimer::buildHierarchy()
 
 	// set up initial tree
 	{
-		for (instance_iter it = beginInstances(); it != endInstances(); ++it)
+		for (instance_iter it = beginInstances(), end_it = endInstances(); it != end_it; ++it)
 		{
 			NamedTimer& timer = *it;
 			if (&timer == NamedTimerFactory::instance().getRootTimer()) continue;
@@ -449,7 +449,7 @@ void LLFastTimer::NamedTimer::resetFrame()
 		LLSD sd;
 
 		{
-			for (instance_iter it = beginInstances(); it != endInstances(); ++it)
+			for (instance_iter it = beginInstances(), end_it = endInstances(); it != end_it; ++it)
 			{
 				NamedTimer& timer = *it;
 				FrameState& info = timer.getFrameState();
@@ -472,7 +472,7 @@ void LLFastTimer::NamedTimer::resetFrame()
 	}
 
 	// reset for next frame
-	for (instance_iter it = beginInstances(); it != endInstances(); ++it)
+	for (instance_iter it = beginInstances(), end_it = endInstances(); it != end_it; ++it)
 	{
 		NamedTimer& timer = *it;
 			
@@ -512,7 +512,7 @@ void LLFastTimer::NamedTimer::reset()
 
 	// reset all history
 	{
-		for (instance_iter it = beginInstances(); it != endInstances(); ++it)
+		for (instance_iter it = beginInstances(), end_it = endInstances(); it != end_it; ++it)
 		{
 			NamedTimer& timer = *it;
 			if (&timer != NamedTimerFactory::instance().getRootTimer()) 
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
index 218f6dbcd0b..956642e97a9 100644
--- a/indra/llcommon/llqueuedthread.cpp
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -112,8 +112,6 @@ void LLQueuedThread::shutdown()
 // virtual
 S32 LLQueuedThread::update(F32 max_time_ms)
 {
-	LLTrace::get_thread_recorder()->pushToMaster();
-
 	if (!mStarted)
 	{
 		if (!mThreaded)
@@ -511,6 +509,9 @@ void LLQueuedThread::run()
 		threadedUpdate();
 		
 		int res = processNextRequest();
+
+		LLTrace::get_thread_recorder()->pushToMaster();
+
 		if (res == 0)
 		{
 			mIdleThread = TRUE;
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
index 8ad391e6e5d..e2530a8a24f 100644
--- a/indra/llcommon/lltrace.h
+++ b/indra/llcommon/lltrace.h
@@ -220,8 +220,7 @@ namespace LLTrace
 			return AccumulatorBuffer<ACCUMULATOR>::getPrimaryStorage()[mAccumulatorIndex];
 		}
 
-		ACCUMULATOR& getAccumulator(AccumulatorBuffer<ACCUMULATOR>* buffer) { return (*buffer)[mAccumulatorIndex]; }
-		const ACCUMULATOR& getAccumulator(const AccumulatorBuffer<ACCUMULATOR>* buffer) const { return (*buffer)[mAccumulatorIndex]; }
+		size_t getIndex() const { return mAccumulatorIndex; }
 
 	protected:
 		std::string	mName;
@@ -396,6 +395,8 @@ namespace LLTrace
 
 		T	getSum() const { return (T)mSum; }
 
+		U32 getSampleCount() const { return mNumSamples; }
+
 	private:
 		T	mSum;
 
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index 9cdd89c223d..435c49106f3 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -117,29 +117,29 @@ void Recording::appendRecording( const Recording& other )
 
 F64 Recording::getSum( const TraceType<CountAccumulator<F64> >& stat ) const
 {
-	return stat.getAccumulator(mCountsFloat).getSum();
+	return (*mCountsFloat)[stat.getIndex()].getSum();
 }
 
 S64 Recording::getSum( const TraceType<CountAccumulator<S64> >& stat ) const
 {
-	return stat.getAccumulator(mCounts).getSum();
+	return (*mCounts)[stat.getIndex()].getSum();
 }
 
 F64 Recording::getSum( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return (F64)stat.getAccumulator(mMeasurementsFloat).getSum();
+	return (F64)(*mMeasurementsFloat)[stat.getIndex()].getSum();
 }
 
 S64 Recording::getSum( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return (S64)stat.getAccumulator(mMeasurements).getSum();
+	return (S64)(*mMeasurements)[stat.getIndex()].getSum();
 }
 
 
 
 F64 Recording::getPerSec( const TraceType<CountAccumulator<F64> >& stat ) const
 {
-	F64 sum = stat.getAccumulator(mCountsFloat).getSum();
+	F64 sum = (*mCountsFloat)[stat.getIndex()].getSum();
 	return  (sum != 0.0) 
 		? (sum / mElapsedSeconds)
 		: 0.0;
@@ -147,15 +147,26 @@ F64 Recording::getPerSec( const TraceType<CountAccumulator<F64> >& stat ) const
 
 F64 Recording::getPerSec( const TraceType<CountAccumulator<S64> >& stat ) const
 {
-	S64 sum = stat.getAccumulator(mCounts).getSum();
+	S64 sum = (*mCounts)[stat.getIndex()].getSum();
 	return (sum != 0) 
 		? ((F64)sum / mElapsedSeconds)
 		: 0.0;
 }
 
+U32 Recording::getSampleCount( const TraceType<CountAccumulator<F64> >& stat ) const
+{
+	return (*mCountsFloat)[stat.getIndex()].getSampleCount();
+}
+
+U32 Recording::getSampleCount( const TraceType<CountAccumulator<S64> >& stat ) const
+{
+	return (*mMeasurementsFloat)[stat.getIndex()].getSampleCount();
+}
+
+
 F64 Recording::getPerSec( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	F64 sum = stat.getAccumulator(mMeasurementsFloat).getSum();
+	F64 sum = (*mMeasurementsFloat)[stat.getIndex()].getSum();
 	return  (sum != 0.0) 
 		? (sum / mElapsedSeconds)
 		: 0.0;
@@ -163,7 +174,7 @@ F64 Recording::getPerSec( const TraceType<MeasurementAccumulator<F64> >& stat )
 
 F64 Recording::getPerSec( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	S64 sum = stat.getAccumulator(mMeasurements).getSum();
+	S64 sum = (*mMeasurements)[stat.getIndex()].getSum();
 	return (sum != 0) 
 		? ((F64)sum / mElapsedSeconds)
 		: 0.0;
@@ -171,62 +182,62 @@ F64 Recording::getPerSec( const TraceType<MeasurementAccumulator<S64> >& stat )
 
 F64 Recording::getMin( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurementsFloat).getMin();
+	return (*mMeasurementsFloat)[stat.getIndex()].getMin();
 }
 
 S64 Recording::getMin( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurements).getMin();
+	return (*mMeasurements)[stat.getIndex()].getMin();
 }
 
 F64 Recording::getMax( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurementsFloat).getMax();
+	return (*mMeasurementsFloat)[stat.getIndex()].getMax();
 }
 
 S64 Recording::getMax( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurements).getMax();
+	return (*mMeasurements)[stat.getIndex()].getMax();
 }
 
 F64 Recording::getMean( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurementsFloat).getMean();
+	return (*mMeasurementsFloat)[stat.getIndex()].getMean();
 }
 
 F64 Recording::getMean( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurements).getMean();
+	return (*mMeasurements)[stat.getIndex()].getMean();
 }
 
 F64 Recording::getStandardDeviation( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurementsFloat).getStandardDeviation();
+	return (*mMeasurementsFloat)[stat.getIndex()].getStandardDeviation();
 }
 
 F64 Recording::getStandardDeviation( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurements).getStandardDeviation();
+	return (*mMeasurements)[stat.getIndex()].getStandardDeviation();
 }
 
 F64 Recording::getLastValue( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurementsFloat).getLastValue();
+	return (*mMeasurementsFloat)[stat.getIndex()].getLastValue();
 }
 
 S64 Recording::getLastValue( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurements).getLastValue();
+	return (*mMeasurements)[stat.getIndex()].getLastValue();
 }
 
 U32 Recording::getSampleCount( const TraceType<MeasurementAccumulator<F64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurementsFloat).getSampleCount();
+	return (*mMeasurementsFloat)[stat.getIndex()].getSampleCount();
 }
 
 U32 Recording::getSampleCount( const TraceType<MeasurementAccumulator<S64> >& stat ) const
 {
-	return stat.getAccumulator(mMeasurements).getSampleCount();
+	return (*mMeasurements)[stat.getIndex()].getSampleCount();
 }
 
 
@@ -235,13 +246,14 @@ U32 Recording::getSampleCount( const TraceType<MeasurementAccumulator<S64> >& st
 // PeriodicRecording
 ///////////////////////////////////////////////////////////////////////
 
-PeriodicRecording::PeriodicRecording( S32 num_periods ) 
+PeriodicRecording::PeriodicRecording( S32 num_periods, EStopWatchState state) 
 :	mNumPeriods(num_periods),
 	mCurPeriod(0),
 	mTotalValid(false),
 	mRecordingPeriods( new Recording[num_periods])
 {
 	llassert(mNumPeriods > 0);
+	initTo(state);
 }
 
 PeriodicRecording::~PeriodicRecording()
@@ -252,7 +264,7 @@ PeriodicRecording::~PeriodicRecording()
 
 void PeriodicRecording::nextPeriod()
 {
-	EPlayState play_state = getPlayState();
+	EStopWatchState play_state = getPlayState();
 	Recording& old_recording = getCurRecordingPeriod();
 	mCurPeriod = (mCurPeriod + 1) % mNumPeriods;
 	old_recording.splitTo(getCurRecordingPeriod());
@@ -286,24 +298,44 @@ Recording& PeriodicRecording::getTotalRecording()
 	return mTotalRecording;
 }
 
-void PeriodicRecording::handleStart()
+void PeriodicRecording::start()
+{
+	getCurRecordingPeriod().start();
+}
+
+void PeriodicRecording::stop()
+{
+	getCurRecordingPeriod().stop();
+}
+
+void PeriodicRecording::pause()
 {
-	getCurRecordingPeriod().handleStart();
+	getCurRecordingPeriod().pause();
 }
 
-void PeriodicRecording::handleStop()
+void PeriodicRecording::resume()
 {
-	getCurRecordingPeriod().handleStop();
+	getCurRecordingPeriod().resume();
 }
 
-void PeriodicRecording::handleReset()
+void PeriodicRecording::restart()
 {
-	getCurRecordingPeriod().handleReset();
+	getCurRecordingPeriod().restart();
 }
 
-void PeriodicRecording::handleSplitTo( PeriodicRecording& other )
+void PeriodicRecording::reset()
 {
-	getCurRecordingPeriod().handleSplitTo(other.getCurRecordingPeriod());
+	getCurRecordingPeriod().reset();
+}
+
+void PeriodicRecording::splitTo(PeriodicRecording& other)
+{
+	getCurRecordingPeriod().splitTo(other.getCurRecordingPeriod());
+}
+
+void PeriodicRecording::splitFrom(PeriodicRecording& other)
+{
+	getCurRecordingPeriod().splitFrom(other.getCurRecordingPeriod());
 }
 
 
@@ -317,38 +349,59 @@ void ExtendableRecording::extend()
 	mPotentialRecording.reset();
 }
 
-void ExtendableRecording::handleStart()
+void ExtendableRecording::start()
 {
-	mPotentialRecording.handleStart();
+	mPotentialRecording.start();
 }
 
-void ExtendableRecording::handleStop()
+void ExtendableRecording::stop()
 {
-	mPotentialRecording.handleStop();
+	mPotentialRecording.stop();
 }
 
-void ExtendableRecording::handleReset()
+void ExtendableRecording::pause()
 {
-	mAcceptedRecording.handleReset();
-	mPotentialRecording.handleReset();
+	mPotentialRecording.pause();
 }
 
-void ExtendableRecording::handleSplitTo( ExtendableRecording& other )
+void ExtendableRecording::resume()
 {
-	mPotentialRecording.handleSplitTo(other.mPotentialRecording);
+	mPotentialRecording.resume();
+}
+
+void ExtendableRecording::restart()
+{
+	mAcceptedRecording.reset();
+	mPotentialRecording.restart();
+}
+
+void ExtendableRecording::reset()
+{
+	mAcceptedRecording.reset();
+	mPotentialRecording.reset();
+}
+
+void ExtendableRecording::splitTo(ExtendableRecording& other)
+{
+	mPotentialRecording.splitTo(other.mPotentialRecording);
+}
+
+void ExtendableRecording::splitFrom(ExtendableRecording& other)
+{
+	mPotentialRecording.splitFrom(other.mPotentialRecording);
 }
 
 PeriodicRecording& get_frame_recording()
 {
-	static PeriodicRecording sRecording(64);
-	return sRecording;
+	static LLThreadLocalPointer<PeriodicRecording> sRecording(new PeriodicRecording(64, PeriodicRecording::STARTED));
+	return *sRecording;
 }
 
 }
 
 void LLStopWatchControlsMixinCommon::start()
 {
-	switch (mPlayState)
+	switch (mState)
 	{
 	case STOPPED:
 		handleReset();
@@ -360,13 +413,16 @@ void LLStopWatchControlsMixinCommon::start()
 	case STARTED:
 		handleReset();
 		break;
+	default:
+		llassert(false);
+		break;
 	}
-	mPlayState = STARTED;
+	mState = STARTED;
 }
 
 void LLStopWatchControlsMixinCommon::stop()
 {
-	switch (mPlayState)
+	switch (mState)
 	{
 	case STOPPED:
 		break;
@@ -376,13 +432,16 @@ void LLStopWatchControlsMixinCommon::stop()
 	case STARTED:
 		handleStop();
 		break;
+	default:
+		llassert(false);
+		break;
 	}
-	mPlayState = STOPPED;
+	mState = STOPPED;
 }
 
 void LLStopWatchControlsMixinCommon::pause()
 {
-	switch (mPlayState)
+	switch (mState)
 	{
 	case STOPPED:
 		break;
@@ -391,13 +450,16 @@ void LLStopWatchControlsMixinCommon::pause()
 	case STARTED:
 		handleStop();
 		break;
+	default:
+		llassert(false);
+		break;
 	}
-	mPlayState = PAUSED;
+	mState = PAUSED;
 }
 
 void LLStopWatchControlsMixinCommon::resume()
 {
-	switch (mPlayState)
+	switch (mState)
 	{
 	case STOPPED:
 		handleStart();
@@ -407,13 +469,16 @@ void LLStopWatchControlsMixinCommon::resume()
 		break;
 	case STARTED:
 		break;
+	default:
+		llassert(false);
+		break;
 	}
-	mPlayState = STARTED;
+	mState = STARTED;
 }
 
 void LLStopWatchControlsMixinCommon::restart()
 {
-	switch (mPlayState)
+	switch (mState)
 	{
 	case STOPPED:
 		handleReset();
@@ -426,11 +491,33 @@ void LLStopWatchControlsMixinCommon::restart()
 	case STARTED:
 		handleReset();
 		break;
+	default:
+		llassert(false);
+		break;
 	}
-	mPlayState = STARTED;
+	mState = STARTED;
 }
 
 void LLStopWatchControlsMixinCommon::reset()
 {
 	handleReset();
 }
+
+void LLStopWatchControlsMixinCommon::initTo( EStopWatchState state )
+{
+	switch(state)
+	{
+	case STOPPED:
+		break;
+	case PAUSED:
+		break;
+	case STARTED:
+		handleStart();
+		break;
+	default:
+		llassert(false);
+		break;
+	}
+
+	mState = state;
+}
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index e9949491503..31901b599ca 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -39,43 +39,42 @@ class LL_COMMON_API LLStopWatchControlsMixinCommon
 public:
 	virtual ~LLStopWatchControlsMixinCommon() {}
 
-	enum EPlayState
+	enum EStopWatchState
 	{
 		STOPPED,
 		PAUSED,
 		STARTED
 	};
 
-	void start();
-	void stop();
-	void pause();
-	void resume();
-	void restart();
-	void reset();
+	virtual void start();
+	virtual void stop();
+	virtual void pause();
+	virtual void resume();
+	virtual void restart();
+	virtual void reset();
 
-	bool isStarted() const { return mPlayState == STARTED; }
-	bool isPaused() const  { return mPlayState == PAUSED; }
-	bool isStopped() const { return mPlayState == STOPPED; }
-	EPlayState getPlayState() const { return mPlayState; }
+	bool isStarted() const { return mState == STARTED; }
+	bool isPaused() const  { return mState == PAUSED; }
+	bool isStopped() const { return mState == STOPPED; }
+	EStopWatchState getPlayState() const { return mState; }
 
 protected:
 	LLStopWatchControlsMixinCommon()
-	:	mPlayState(STOPPED)
+	:	mState(STOPPED)
 	{}
 
-	// derived classes can call this from their constructor in order
-	// to enforce invariants
-	void forceState(EPlayState state) { mPlayState = state; }
-
+	// derived classes can call this from their copy constructor in order
+	// to duplicate play state of source
+	void initTo(EStopWatchState state);
 private:
-	// trigger data accumulation (without reset)
-	virtual void handleStart() = 0;
-	// stop data accumulation, should put object in queryable state
-	virtual void handleStop() = 0;
-	// clear accumulated values, can be called while started
-	virtual void handleReset() = 0;
-
-	EPlayState mPlayState;
+	// trigger active behavior (without reset)
+	virtual void handleStart(){};
+	// stop active behavior
+	virtual void handleStop(){};
+	// clear accumulated state, can be called while started
+	virtual void handleReset(){};
+
+	EStopWatchState mState;
 };
 
 template<typename DERIVED>
@@ -83,19 +82,20 @@ class LLStopWatchControlsMixin
 :	public LLStopWatchControlsMixinCommon
 {
 public:
-	void splitTo(DERIVED& other)
+	typedef LLStopWatchControlsMixin<DERIVED> self_t;
+	virtual void splitTo(DERIVED& other)
 	{
 		handleSplitTo(other);
 	}
 
-	void splitFrom(DERIVED& other)
+	virtual void splitFrom(DERIVED& other)
 	{
-		other.handleSplitTo(*this);
+		static_cast<self_t&>(other).handleSplitTo(*static_cast<DERIVED*>(this));
 	}
 private:
 	// atomically stop this object while starting the other
 	// no data can be missed in between stop and start
-	virtual void handleSplitTo(DERIVED& other) = 0;
+	virtual void handleSplitTo(DERIVED& other) {};
 
 };
 
@@ -108,20 +108,20 @@ namespace LLTrace
 
 		Recording(const Recording& other)
 		{
-			mSamplingTimer = other.mSamplingTimer;
-			mElapsedSeconds = other.mElapsedSeconds;
-			mCountsFloat = other.mCountsFloat;
+			mSamplingTimer     = other.mSamplingTimer;
+			mElapsedSeconds    = other.mElapsedSeconds;
+			mCountsFloat       = other.mCountsFloat;
 			mMeasurementsFloat = other.mMeasurementsFloat;
-			mCounts = other.mCounts;
-			mMeasurements = other.mMeasurements;
-			mStackTimers = other.mStackTimers;
+			mCounts            = other.mCounts;
+			mMeasurements      = other.mMeasurements;
+			mStackTimers       = other.mStackTimers;
 
+			LLStopWatchControlsMixin::initTo(other.getPlayState());
 			if (other.isStarted())
 			{
 				handleStart();
 			}
 
-			forceState(other.getPlayState());
 		}
 		~Recording();
 
@@ -149,6 +149,10 @@ namespace LLTrace
 			return (T)getPerSec(static_cast<const TraceType<CountAccumulator<StorageType<T>::type_t> >&> (stat));
 		}
 
+		U32 getSampleCount(const TraceType<CountAccumulator<F64> >& stat) const;
+		U32 getSampleCount(const TraceType<CountAccumulator<S64> >& stat) const;
+
+
 		// Measurement accessors
 		F64 getSum(const TraceType<MeasurementAccumulator<F64> >& stat) const;
 		S64 getSum(const TraceType<MeasurementAccumulator<S64> >& stat) const;
@@ -211,14 +215,15 @@ namespace LLTrace
 
 		LLUnit::Seconds<F64> getDuration() const { return mElapsedSeconds; }
 
+	private:
+		friend class ThreadRecorder;
+
 		// implementation for LLStopWatchControlsMixin
 		/*virtual*/ void handleStart();
 		/*virtual*/ void handleStop();
 		/*virtual*/ void handleReset();
 		/*virtual*/ void handleSplitTo(Recording& other);
 
-	private:
-		friend class ThreadRecorder;
 		// returns data for current thread
 		class ThreadRecorder* getThreadRecorder(); 
 
@@ -236,7 +241,7 @@ namespace LLTrace
 	:	public LLStopWatchControlsMixin<PeriodicRecording>
 	{
 	public:
-		PeriodicRecording(S32 num_periods);
+		PeriodicRecording(S32 num_periods, EStopWatchState state = STOPPED);
 		~PeriodicRecording();
 
 		void nextPeriod();
@@ -359,15 +364,17 @@ namespace LLTrace
 			return mean;
 		}
 
-	private:
-
 		// implementation for LLStopWatchControlsMixin
-		/*virtual*/ void handleStart();
-		/*virtual*/ void handleStop();
-		/*virtual*/ void handleReset();
-
-		/*virtual*/ void handleSplitTo(PeriodicRecording& other);
+		/*virtual*/ void start();
+		/*virtual*/ void stop();
+		/*virtual*/ void pause();
+		/*virtual*/ void resume();
+		/*virtual*/ void restart();
+		/*virtual*/ void reset();
+		/*virtual*/ void splitTo(PeriodicRecording& other);
+		/*virtual*/ void splitFrom(PeriodicRecording& other);
 
+	private:
 		Recording*	mRecordingPeriods;
 		Recording	mTotalRecording;
 		bool		mTotalValid;
@@ -382,13 +389,16 @@ namespace LLTrace
 	{
 		void extend();
 
-	private:
 		// implementation for LLStopWatchControlsMixin
-		/*virtual*/ void handleStart();
-		/*virtual*/ void handleStop();
-		/*virtual*/ void handleReset();
-		/*virtual*/ void handleSplitTo(ExtendableRecording& other);
-
+		/*virtual*/ void start();
+		/*virtual*/ void stop();
+		/*virtual*/ void pause();
+		/*virtual*/ void resume();
+		/*virtual*/ void restart();
+		/*virtual*/ void reset();
+		/*virtual*/ void splitTo(ExtendableRecording& other);
+		/*virtual*/ void splitFrom(ExtendableRecording& other);
+	private:
 		Recording mAcceptedRecording;
 		Recording mPotentialRecording;
 	};
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
index af66a694920..5a6ff14f973 100644
--- a/indra/llcommon/lltracethreadrecorder.cpp
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -171,15 +171,20 @@ void SlaveThreadRecorder::SharedData::copyTo( Recording& sink )
 // MasterThreadRecorder
 ///////////////////////////////////////////////////////////////////////
 
+LLFastTimer::DeclareTimer FTM_PULL_TRACE_DATA_FROM_SLAVES("Pull slave trace data");
 void MasterThreadRecorder::pullFromSlaveThreads()
 {
+	LLFastTimer _(FTM_PULL_TRACE_DATA_FROM_SLAVES);
+	if (mActiveRecordings.empty()) return;
+
 	LLMutexLock lock(&mSlaveListMutex);
 
+	Recording& target_recording = mActiveRecordings.front().mBaseline;
 	for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
 		it != end_it;
 		++it)
 	{
-		(*it)->mRecorder->mSharedData.copyTo((*it)->mSlaveRecording);
+		(*it)->mSharedData.copyTo(target_recording);
 	}
 }
 
@@ -187,7 +192,7 @@ void MasterThreadRecorder::addSlaveThread( class SlaveThreadRecorder* child )
 {
 	LLMutexLock lock(&mSlaveListMutex);
 
-	mSlaveThreadRecorders.push_back(new SlaveThreadRecorderProxy(child));
+	mSlaveThreadRecorders.push_back(child);
 }
 
 void MasterThreadRecorder::removeSlaveThread( class SlaveThreadRecorder* child )
@@ -198,7 +203,7 @@ void MasterThreadRecorder::removeSlaveThread( class SlaveThreadRecorder* child )
 		it != end_it;
 		++it)
 	{
-		if ((*it)->mRecorder == child)
+		if ((*it) == child)
 		{
 			mSlaveThreadRecorders.erase(it);
 			break;
@@ -212,12 +217,4 @@ void MasterThreadRecorder::pushToMaster()
 MasterThreadRecorder::MasterThreadRecorder()
 {}
 
-///////////////////////////////////////////////////////////////////////
-// MasterThreadRecorder::SlaveThreadTraceProxy
-///////////////////////////////////////////////////////////////////////
-
-MasterThreadRecorder::SlaveThreadRecorderProxy::SlaveThreadRecorderProxy( class SlaveThreadRecorder* recorder) 
-:	mRecorder(recorder)
-{}
-
 }
diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h
index c9231265aff..44fe67384be 100644
--- a/indra/llcommon/lltracethreadrecorder.h
+++ b/indra/llcommon/lltracethreadrecorder.h
@@ -81,17 +81,8 @@ namespace LLTrace
 		LLMutex* getSlaveListMutex() { return &mSlaveListMutex; }
 
 	private:
-		struct SlaveThreadRecorderProxy
-		{
-			SlaveThreadRecorderProxy(class SlaveThreadRecorder* recorder);
 
-			class SlaveThreadRecorder*	mRecorder;
-			Recording					mSlaveRecording;
-		private:
-			//no need to copy these and then have to duplicate the storage
-			SlaveThreadRecorderProxy(const SlaveThreadRecorderProxy& other) {}
-		};
-		typedef std::list<SlaveThreadRecorderProxy*> slave_thread_recorder_list_t;
+		typedef std::list<class SlaveThreadRecorder*> slave_thread_recorder_list_t;
 
 		slave_thread_recorder_list_t	mSlaveThreadRecorders;
 		LLMutex							mSlaveListMutex;
diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp
index 000d061bece..bad2f5b4cdd 100755
--- a/indra/newview/llviewerassetstats.cpp
+++ b/indra/newview/llviewerassetstats.cpp
@@ -494,7 +494,7 @@ void record_response(LLViewerAssetType::EType at, bool with_http, bool is_temp,
 {
 	const EViewerAssetCategories eac(asset_type_to_category(at, with_http, is_temp));
 
-	sResponse[int(eac)].sample<LLTrace::Seconds>(duration);
+	sResponse[int(eac)].sample<LLTrace::Microseconds>(duration);
 }
 
 void record_avatar_stats()
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index a42368cec4b..317fe5eaf8e 100755
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -349,7 +349,10 @@ void update_statistics()
 	}
 
 	LLStatViewer::FPS.add(1);
-	LLStatViewer::FPS_SAMPLE.sample(LLTrace::get_frame_recording().getTotalRecording().getPerSec(LLStatViewer::FPS));
+	if (LLTrace::get_frame_recording().getTotalRecording().getSampleCount(LLStatViewer::FPS))
+	{
+		LLStatViewer::FPS_SAMPLE.sample(LLTrace::get_frame_recording().getTotalRecording().getPerSec(LLStatViewer::FPS));
+	}
 	F32 layer_bits = (F32)(gVLManager.getLandBits() + gVLManager.getWindBits() + gVLManager.getCloudBits());
 	LLStatViewer::LAYERS_KBIT.add<LLTrace::Bits>(layer_bits);
 	LLStatViewer::OBJECT_KBIT.add(gObjectData);
diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp
index 1f299abe414..ecdd78a1da0 100755
--- a/indra/newview/tests/llviewerassetstats_test.cpp
+++ b/indra/newview/tests/llviewerassetstats_test.cpp
@@ -266,7 +266,6 @@ namespace tut
 		ensure("Global gViewerAssetStats should still be NULL", (NULL == gViewerAssetStats));
 
 		LLSD sd_full = it->asLLSD(false);
-		llinfos << ll_pretty_print_sd(sd_full) << llendl;
 
 		// Default (NULL) region ID doesn't produce LLSD results so should
 		// get an empty map back from output
-- 
GitLab