summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/lltrace.cpp195
-rw-r--r--indra/llcommon/lltrace.h143
-rw-r--r--indra/llcommon/lltracerecording.cpp54
-rw-r--r--indra/llcommon/lltracerecording.h30
-rw-r--r--indra/llcommon/lltracethreadrecorder.cpp219
-rw-r--r--indra/llcommon/lltracethreadrecorder.h124
7 files changed, 450 insertions, 317 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 471558ea01..c0e9266aa9 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -102,6 +102,7 @@ set(llcommon_SOURCE_FILES
lltimer.cpp
lltrace.cpp
lltracerecording.cpp
+ lltracethreadrecorder.cpp
lluri.cpp
lluuid.cpp
llworkerthread.cpp
@@ -245,6 +246,7 @@ set(llcommon_HEADER_FILES
lltimer.h
lltrace.h
lltracerecording.h
+ lltracethreadrecorder.h
lltreeiterators.h
lltypeinfolookup.h
llunit.h
diff --git a/indra/llcommon/lltrace.cpp b/indra/llcommon/lltrace.cpp
index 6d928721de..2b1c8d8ce8 100644
--- a/indra/llcommon/lltrace.cpp
+++ b/indra/llcommon/lltrace.cpp
@@ -44,206 +44,19 @@ void cleanup()
gMasterThreadRecorder = NULL;
}
-LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder()
-{
- static LLThreadLocalPointer<ThreadRecorder> s_thread_recorder;
- return s_thread_recorder;
-
-}
-
-BlockTimer::Recorder::StackEntry BlockTimer::sCurRecorder;
-
-
-
MasterThreadRecorder& getMasterThreadRecorder()
{
llassert(gMasterThreadRecorder != NULL);
return *gMasterThreadRecorder;
}
-///////////////////////////////////////////////////////////////////////
-// ThreadRecorder
-///////////////////////////////////////////////////////////////////////
-
-ThreadRecorder::ThreadRecorder()
-: mPrimaryRecording(NULL)
-{
- get_thread_recorder() = this;
- mFullRecording.start();
-}
-
-ThreadRecorder::ThreadRecorder( const ThreadRecorder& other )
-: mFullRecording(other.mFullRecording),
- mPrimaryRecording(NULL)
-{
- get_thread_recorder() = this;
- mFullRecording.start();
-}
-
-ThreadRecorder::~ThreadRecorder()
-{
- get_thread_recorder() = NULL;
-}
-
-//TODO: remove this and use llviewerstats recording
-Recording* ThreadRecorder::getPrimaryRecording()
-{
- return mPrimaryRecording;
-}
-
-void ThreadRecorder::activate( Recording* recording )
-{
- mActiveRecordings.push_front(ActiveRecording(mPrimaryRecording, recording));
- mActiveRecordings.front().mBaseline.makePrimary();
- mPrimaryRecording = &mActiveRecordings.front().mBaseline;
-}
-
-//TODO: consider merging results down the list to one past the buffered item.
-// this would require 2 buffers per sampler, to separate current total from running total
-
-void ThreadRecorder::deactivate( Recording* recording )
-{
- for (std::list<ActiveRecording>::iterator it = mActiveRecordings.begin(), end_it = mActiveRecordings.end();
- it != end_it;
- ++it)
- {
- std::list<ActiveRecording>::iterator next_it = it;
- if (++next_it != mActiveRecordings.end())
- {
- next_it->mergeMeasurements((*it));
- }
-
- it->flushAccumulators(mPrimaryRecording);
-
- if (it->mTargetRecording == recording)
- {
- if (next_it != mActiveRecordings.end())
- {
- next_it->mBaseline.makePrimary();
- mPrimaryRecording = &next_it->mBaseline;
- }
- mActiveRecordings.erase(it);
- break;
- }
- }
-}
-
-ThreadRecorder::ActiveRecording::ActiveRecording( Recording* source, Recording* target )
-: mTargetRecording(target)
-{
- // take snapshots of current values rates and timers
- if (source)
- {
- mBaseline.mRates.write()->copyFrom(*source->mRates);
- mBaseline.mStackTimers.write()->copyFrom(*source->mStackTimers);
- }
-}
-
-void ThreadRecorder::ActiveRecording::mergeMeasurements(ThreadRecorder::ActiveRecording& other)
-{
- mBaseline.mMeasurements.write()->mergeSamples(*other.mBaseline.mMeasurements);
-}
-
-void ThreadRecorder::ActiveRecording::flushAccumulators(Recording* current)
-{
- // accumulate statistics-like measurements
- mTargetRecording->mMeasurements.write()->mergeSamples(*mBaseline.mMeasurements);
- // for rate-like measurements, merge total change since baseline
- mTargetRecording->mRates.write()->mergeDeltas(*mBaseline.mRates, *current->mRates);
- mTargetRecording->mStackTimers.write()->mergeDeltas(*mBaseline.mStackTimers, *current->mStackTimers);
- // reset baselines
- mBaseline.mRates.write()->copyFrom(*current->mRates);
- mBaseline.mStackTimers.write()->copyFrom(*current->mStackTimers);
-}
-
-///////////////////////////////////////////////////////////////////////
-// SlaveThreadRecorder
-///////////////////////////////////////////////////////////////////////
-
-SlaveThreadRecorder::SlaveThreadRecorder()
-: ThreadRecorder(getMasterThreadRecorder())
-{
- getMasterThreadRecorder().addSlaveThread(this);
-}
-
-SlaveThreadRecorder::~SlaveThreadRecorder()
-{
- getMasterThreadRecorder().removeSlaveThread(this);
-}
-
-void SlaveThreadRecorder::pushToMaster()
-{
- mFullRecording.stop();
- {
- LLMutexLock(getMasterThreadRecorder().getSlaveListMutex());
- mSharedData.copyFrom(mFullRecording);
- }
- mFullRecording.start();
-}
-
-void SlaveThreadRecorder::SharedData::copyFrom( const Recording& source )
-{
- LLMutexLock lock(&mRecorderMutex);
- mRecorder.mergeSamples(source);
-}
-
-void SlaveThreadRecorder::SharedData::copyTo( Recording& sink )
-{
- LLMutexLock lock(&mRecorderMutex);
- sink.mergeSamples(mRecorder);
-}
-
-///////////////////////////////////////////////////////////////////////
-// MasterThreadRecorder
-///////////////////////////////////////////////////////////////////////
-
-void MasterThreadRecorder::pullFromSlaveThreads()
-{
- LLMutexLock lock(&mSlaveListMutex);
-
- for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
- it != end_it;
- ++it)
- {
- (*it)->mRecorder->mSharedData.copyTo((*it)->mSlaveRecording);
- }
-}
-
-void MasterThreadRecorder::addSlaveThread( class SlaveThreadRecorder* child )
-{
- LLMutexLock lock(&mSlaveListMutex);
-
- mSlaveThreadRecorders.push_back(new SlaveThreadRecorderProxy(child));
-}
-
-void MasterThreadRecorder::removeSlaveThread( class SlaveThreadRecorder* child )
+LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder()
{
- LLMutexLock lock(&mSlaveListMutex);
+ static LLThreadLocalPointer<ThreadRecorder> s_thread_recorder;
+ return s_thread_recorder;
- for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
- it != end_it;
- ++it)
- {
- if ((*it)->mRecorder == child)
- {
- mSlaveThreadRecorders.erase(it);
- break;
- }
- }
}
-void MasterThreadRecorder::pushToMaster()
-{}
-
-MasterThreadRecorder::MasterThreadRecorder()
-{}
-
-///////////////////////////////////////////////////////////////////////
-// MasterThreadRecorder::SlaveThreadTraceProxy
-///////////////////////////////////////////////////////////////////////
-
-MasterThreadRecorder::SlaveThreadRecorderProxy::SlaveThreadRecorderProxy( class SlaveThreadRecorder* recorder)
-: mRecorder(recorder)
-{}
+BlockTimer::Recorder::StackEntry BlockTimer::sCurRecorder;
}
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
index 6a889f74df..d83ea77363 100644
--- a/indra/llcommon/lltrace.h
+++ b/indra/llcommon/lltrace.h
@@ -30,11 +30,10 @@
#include "stdtypes.h"
#include "llpreprocessor.h"
-#include "llmutex.h"
#include "llmemory.h"
-#include "lltimer.h"
#include "llrefcount.h"
#include "lltracerecording.h"
+#include "lltracethreadrecorder.h"
#include "llunit.h"
#include <list>
@@ -171,7 +170,7 @@ namespace LLTrace
template<typename ACCUMULATOR> LLThreadLocalPointer<ACCUMULATOR> AccumulatorBuffer<ACCUMULATOR>::sPrimaryStorage;
template<typename ACCUMULATOR>
- class LL_COMMON_API TraceType
+ class LL_COMMON_API TraceType
{
public:
TraceType(const std::string& name)
@@ -186,6 +185,7 @@ namespace LLTrace
}
ACCUMULATOR& getAccumulator(AccumulatorBuffer<ACCUMULATOR>* buffer) { return (*buffer)[mAccumulatorIndex]; }
+ const ACCUMULATOR& getAccumulator(AccumulatorBuffer<ACCUMULATOR>* buffer) const { return (*buffer)[mAccumulatorIndex]; }
protected:
std::string mName;
@@ -265,11 +265,11 @@ namespace LLTrace
mMax = 0;
}
- T getSum() { return mSum; }
- T getMin() { return mMin; }
- T getMax() { return mMax; }
- F32 getMean() { return mMean; }
- F32 getStandardDeviation() { return mStandardDeviation; }
+ T getSum() const { return mSum; }
+ T getMin() const { return mMin; }
+ T getMax() const { return mMax; }
+ F32 getMean() const { return mMean; }
+ F32 getStandardDeviation() const { return mStandardDeviation; }
private:
T mSum,
@@ -315,7 +315,7 @@ namespace LLTrace
mSum = 0;
}
- T getSum() { return mSum; }
+ T getSum() const { return mSum; }
private:
T mSum;
@@ -355,13 +355,6 @@ namespace LLTrace
{
base_measurement_t::sample(value.get());
}
-
- template<typename UNIT_T>
- typename T::value_t get()
- {
- UNIT_T value(*this);
- return value.get();
- }
};
template <typename T, typename IS_UNIT = void>
@@ -395,14 +388,35 @@ namespace LLTrace
{
getPrimaryAccumulator().add(value.get());
}
+ };
- template<typename UNIT_T>
- typename T::value_t get()
+ template <typename T>
+ class LL_COMMON_API Count
+ {
+ public:
+ Count(const std::string& name)
+ : mIncrease(name + "_increase"),
+ mDecrease(name + "_decrease"),
+ mTotal(name)
+ {}
+
+ void count(T value)
{
- UNIT_T value(*this);
- return value.get();
+ if (value < 0)
+ {
+ mDecrease.add(value * -1);
+ }
+ else
+ {
+ mIncrease.add(value);
+ }
+ mTotal.add(value);
}
-
+ private:
+ friend class LLTrace::Recording;
+ Rate<T> mIncrease;
+ Rate<T> mDecrease;
+ Rate<T> mTotal;
};
class LL_COMMON_API TimerAccumulator
@@ -527,93 +541,6 @@ namespace LLTrace
static Recorder::StackEntry sCurRecorder;
};
-
- class Recording;
-
- class LL_COMMON_API ThreadRecorder
- {
- public:
- ThreadRecorder();
- ThreadRecorder(const ThreadRecorder& other);
-
- virtual ~ThreadRecorder();
-
- void activate(Recording* recording);
- void deactivate(Recording* recording);
-
- virtual void pushToMaster() = 0;
-
- Recording* getPrimaryRecording();
- protected:
- struct ActiveRecording
- {
- ActiveRecording(Recording* source, Recording* target);
-
- Recording* mTargetRecording;
- Recording mBaseline;
-
- void mergeMeasurements(ActiveRecording& other);
- void flushAccumulators(Recording* current);
- };
- Recording* mPrimaryRecording;
- Recording mFullRecording;
- std::list<ActiveRecording> mActiveRecordings;
- };
-
- class LL_COMMON_API MasterThreadRecorder : public ThreadRecorder
- {
- public:
- MasterThreadRecorder();
-
- void addSlaveThread(class SlaveThreadRecorder* child);
- void removeSlaveThread(class SlaveThreadRecorder* child);
-
- /*virtual */ void pushToMaster();
-
- // call this periodically to gather stats data from slave threads
- void pullFromSlaveThreads();
-
- 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;
-
- slave_thread_recorder_list_t mSlaveThreadRecorders;
- LLMutex mSlaveListMutex;
- };
-
- class LL_COMMON_API SlaveThreadRecorder : public ThreadRecorder
- {
- public:
- SlaveThreadRecorder();
- ~SlaveThreadRecorder();
-
- // call this periodically to gather stats data for master thread to consume
- /*virtual*/ void pushToMaster();
-
- MasterThreadRecorder* mMaster;
-
- class SharedData
- {
- public:
- void copyFrom(const Recording& source);
- void copyTo(Recording& sink);
- private:
- LLMutex mRecorderMutex;
- Recording mRecorder;
- };
- SharedData mSharedData;
- };
}
#endif // LL_LLTRACE_H
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index 95cdb44e4b..e4cff551f9 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -110,46 +110,80 @@ void Recording::mergeDeltas(const Recording& baseline, const Recording& target)
}
-F32 Recording::getSum( Rate<F32>& stat )
+F32 Recording::getSum(const Rate<F32>& stat)
{
return stat.getAccumulator(mRates).getSum();
}
-F32 Recording::getSum( Measurement<F32>& stat )
+F32 Recording::getPerSec(const Rate<F32>& stat)
{
- return stat.getAccumulator(mMeasurements).getSum();
+ return stat.getAccumulator(mRates).getSum() / mElapsedSeconds;
}
-
-F32 Recording::getPerSec( Rate<F32>& stat )
+F32 Recording::getSum(const Measurement<F32>& stat)
{
- return stat.getAccumulator(mRates).getSum() / mElapsedSeconds;
+ return stat.getAccumulator(mMeasurements).getSum();
}
-F32 Recording::getMin( Measurement<F32>& stat )
+F32 Recording::getMin(const Measurement<F32>& stat)
{
return stat.getAccumulator(mMeasurements).getMin();
}
-F32 Recording::getMax( Measurement<F32>& stat )
+F32 Recording::getMax(const Measurement<F32>& stat)
{
return stat.getAccumulator(mMeasurements).getMax();
}
-F32 Recording::getMean( Measurement<F32>& stat )
+F32 Recording::getMean(const Measurement<F32>& stat)
{
return stat.getAccumulator(mMeasurements).getMean();
}
-F32 Recording::getStandardDeviation( Measurement<F32>& stat )
+F32 Recording::getStandardDeviation(const Measurement<F32>& stat)
{
return stat.getAccumulator(mMeasurements).getStandardDeviation();
}
+F32 Recording::getSum(const Count<F32>& stat)
+{
+ return getSum(stat.mTotal);
+}
+
+F32 Recording::getPerSec(const Count<F32>& stat)
+{
+ return getPerSec(stat.mTotal);
+}
+
+F32 Recording::getIncrease(const Count<F32>& stat)
+{
+ return getSum(stat.mIncrease);
+}
+
+F32 Recording::getIncreasePerSec(const Count<F32>& stat)
+{
+ return getPerSec(stat.mIncrease);
+}
+F32 Recording::getDecrease(const Count<F32>& stat)
+{
+ return getSum(stat.mDecrease);
+}
+F32 Recording::getDecreasePerSec(const Count<F32>& stat)
+{
+ return getPerSec(stat.mDecrease);
+}
+F32 Recording::getChurn(const Count<F32>& stat)
+{
+ return getIncrease(stat) + getDecrease(stat);
+}
+F32 Recording::getChurnPerSec(const Count<F32>& stat)
+{
+ return getIncreasePerSec(stat) + getDecreasePerSec(stat);
+}
}
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index 4d5793014f..f9bc6b61b2 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -37,6 +37,7 @@ namespace LLTrace
{
template<typename T, typename IS_UNIT> class Rate;
template<typename T, typename IS_UNIT> class Measurement;
+ template<typename T> class Count;
template<typename T> class AccumulatorBuffer;
template<typename T> class RateAccumulator;
template<typename T> class MeasurementAccumulator;
@@ -63,14 +64,27 @@ namespace LLTrace
bool isStarted() { return mIsStarted; }
- F32 getSum(Rate<F32, void>& stat);
- F32 getPerSec(Rate<F32, void>& stat);
-
- F32 getSum(Measurement<F32, void>& stat);
- F32 getMin(Measurement<F32, void>& stat);
- F32 getMax(Measurement<F32, void>& stat);
- F32 getMean(Measurement<F32, void>& stat);
- F32 getStandardDeviation(Measurement<F32, void>& stat);
+ // Rate accessors
+ F32 getSum(const Rate<F32, void>& stat);
+ F32 getPerSec(const Rate<F32, void>& stat);
+
+ // Measurement accessors
+ F32 getSum(const Measurement<F32, void>& stat);
+ F32 getPerSec(const Measurement<F32, void>& stat);
+ F32 getMin(const Measurement<F32, void>& stat);
+ F32 getMax(const Measurement<F32, void>& stat);
+ F32 getMean(const Measurement<F32, void>& stat);
+ F32 getStandardDeviation(const Measurement<F32, void>& stat);
+
+ // Count accessors
+ F32 getSum(const Count<F32>& stat);
+ F32 getPerSec(const Count<F32>& stat);
+ F32 getIncrease(const Count<F32>& stat);
+ F32 getIncreasePerSec(const Count<F32>& stat);
+ F32 getDecrease(const Count<F32>& stat);
+ F32 getDecreasePerSec(const Count<F32>& stat);
+ F32 getChurn(const Count<F32>& stat);
+ F32 getChurnPerSec(const Count<F32>& stat);
F64 getSampleTime() { return mElapsedSeconds; }
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
new file mode 100644
index 0000000000..9115a52fd1
--- /dev/null
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -0,0 +1,219 @@
+/**
+ * @file lltracethreadrecorder.cpp
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrace.h"
+
+namespace LLTrace
+{
+
+
+///////////////////////////////////////////////////////////////////////
+// ThreadRecorder
+///////////////////////////////////////////////////////////////////////
+
+ThreadRecorder::ThreadRecorder()
+: mPrimaryRecording(NULL)
+{
+ get_thread_recorder() = this;
+ mFullRecording.start();
+}
+
+ThreadRecorder::ThreadRecorder( const ThreadRecorder& other )
+: mFullRecording(other.mFullRecording),
+ mPrimaryRecording(NULL)
+{
+ get_thread_recorder() = this;
+ mFullRecording.start();
+}
+
+ThreadRecorder::~ThreadRecorder()
+{
+ get_thread_recorder() = NULL;
+}
+
+//TODO: remove this and use llviewerstats recording
+Recording* ThreadRecorder::getPrimaryRecording()
+{
+ return mPrimaryRecording;
+}
+
+void ThreadRecorder::activate( Recording* recording )
+{
+ mActiveRecordings.push_front(ActiveRecording(mPrimaryRecording, recording));
+ mActiveRecordings.front().mBaseline.makePrimary();
+ mPrimaryRecording = &mActiveRecordings.front().mBaseline;
+}
+
+//TODO: consider merging results down the list to one past the buffered item.
+// this would require 2 buffers per sampler, to separate current total from running total
+
+void ThreadRecorder::deactivate( Recording* recording )
+{
+ for (std::list<ActiveRecording>::iterator it = mActiveRecordings.begin(), end_it = mActiveRecordings.end();
+ it != end_it;
+ ++it)
+ {
+ std::list<ActiveRecording>::iterator next_it = it;
+ if (++next_it != mActiveRecordings.end())
+ {
+ next_it->mergeMeasurements((*it));
+ }
+
+ it->flushAccumulators(mPrimaryRecording);
+
+ if (it->mTargetRecording == recording)
+ {
+ if (next_it != mActiveRecordings.end())
+ {
+ next_it->mBaseline.makePrimary();
+ mPrimaryRecording = &next_it->mBaseline;
+ }
+ mActiveRecordings.erase(it);
+ break;
+ }
+ }
+}
+
+ThreadRecorder::ActiveRecording::ActiveRecording( Recording* source, Recording* target )
+: mTargetRecording(target)
+{
+ // take snapshots of current values rates and timers
+ if (source)
+ {
+ mBaseline.mRates.write()->copyFrom(*source->mRates);
+ mBaseline.mStackTimers.write()->copyFrom(*source->mStackTimers);
+ }
+}
+
+void ThreadRecorder::ActiveRecording::mergeMeasurements(ThreadRecorder::ActiveRecording& other)
+{
+ mBaseline.mMeasurements.write()->mergeSamples(*other.mBaseline.mMeasurements);
+}
+
+void ThreadRecorder::ActiveRecording::flushAccumulators(Recording* current)
+{
+ // accumulate statistics-like measurements
+ mTargetRecording->mMeasurements.write()->mergeSamples(*mBaseline.mMeasurements);
+ // for rate-like measurements, merge total change since baseline
+ mTargetRecording->mRates.write()->mergeDeltas(*mBaseline.mRates, *current->mRates);
+ mTargetRecording->mStackTimers.write()->mergeDeltas(*mBaseline.mStackTimers, *current->mStackTimers);
+ // reset baselines
+ mBaseline.mRates.write()->copyFrom(*current->mRates);
+ mBaseline.mStackTimers.write()->copyFrom(*current->mStackTimers);
+}
+
+///////////////////////////////////////////////////////////////////////
+// SlaveThreadRecorder
+///////////////////////////////////////////////////////////////////////
+
+SlaveThreadRecorder::SlaveThreadRecorder()
+: ThreadRecorder(getMasterThreadRecorder())
+{
+ getMasterThreadRecorder().addSlaveThread(this);
+}
+
+SlaveThreadRecorder::~SlaveThreadRecorder()
+{
+ getMasterThreadRecorder().removeSlaveThread(this);
+}
+
+void SlaveThreadRecorder::pushToMaster()
+{
+ mFullRecording.stop();
+ {
+ LLMutexLock(getMasterThreadRecorder().getSlaveListMutex());
+ mSharedData.copyFrom(mFullRecording);
+ }
+ mFullRecording.start();
+}
+
+void SlaveThreadRecorder::SharedData::copyFrom( const Recording& source )
+{
+ LLMutexLock lock(&mRecorderMutex);
+ mRecorder.mergeSamples(source);
+}
+
+void SlaveThreadRecorder::SharedData::copyTo( Recording& sink )
+{
+ LLMutexLock lock(&mRecorderMutex);
+ sink.mergeSamples(mRecorder);
+}
+
+///////////////////////////////////////////////////////////////////////
+// MasterThreadRecorder
+///////////////////////////////////////////////////////////////////////
+
+void MasterThreadRecorder::pullFromSlaveThreads()
+{
+ LLMutexLock lock(&mSlaveListMutex);
+
+ for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
+ it != end_it;
+ ++it)
+ {
+ (*it)->mRecorder->mSharedData.copyTo((*it)->mSlaveRecording);
+ }
+}
+
+void MasterThreadRecorder::addSlaveThread( class SlaveThreadRecorder* child )
+{
+ LLMutexLock lock(&mSlaveListMutex);
+
+ mSlaveThreadRecorders.push_back(new SlaveThreadRecorderProxy(child));
+}
+
+void MasterThreadRecorder::removeSlaveThread( class SlaveThreadRecorder* child )
+{
+ LLMutexLock lock(&mSlaveListMutex);
+
+ for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
+ it != end_it;
+ ++it)
+ {
+ if ((*it)->mRecorder == child)
+ {
+ mSlaveThreadRecorders.erase(it);
+ break;
+ }
+ }
+}
+
+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
new file mode 100644
index 0000000000..40441d0447
--- /dev/null
+++ b/indra/llcommon/lltracethreadrecorder.h
@@ -0,0 +1,124 @@
+/**
+ * @file lltrace.h
+ * @brief Runtime statistics accumulation.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTRACETHREADRECORDER_H
+#define LL_LLTRACETHREADRECORDER_H
+
+#include "stdtypes.h"
+#include "llpreprocessor.h"
+
+#include "llmutex.h"
+#include "lltracerecording.h"
+
+namespace LLTrace
+{
+ class LL_COMMON_API ThreadRecorder
+ {
+ public:
+ ThreadRecorder();
+ ThreadRecorder(const ThreadRecorder& other);
+
+ virtual ~ThreadRecorder();
+
+ void activate(Recording* recording);
+ void deactivate(Recording* recording);
+
+ virtual void pushToMaster() = 0;
+
+ Recording* getPrimaryRecording();
+ protected:
+ struct ActiveRecording
+ {
+ ActiveRecording(Recording* source, Recording* target);
+
+ Recording* mTargetRecording;
+ Recording mBaseline;
+
+ void mergeMeasurements(ActiveRecording& other);
+ void flushAccumulators(Recording* current);
+ };
+ Recording* mPrimaryRecording;
+ Recording mFullRecording;
+ std::list<ActiveRecording> mActiveRecordings;
+ };
+
+ class LL_COMMON_API MasterThreadRecorder : public ThreadRecorder
+ {
+ public:
+ MasterThreadRecorder();
+
+ void addSlaveThread(class SlaveThreadRecorder* child);
+ void removeSlaveThread(class SlaveThreadRecorder* child);
+
+ /*virtual */ void pushToMaster();
+
+ // call this periodically to gather stats data from slave threads
+ void pullFromSlaveThreads();
+
+ 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;
+
+ slave_thread_recorder_list_t mSlaveThreadRecorders;
+ LLMutex mSlaveListMutex;
+ };
+
+ class LL_COMMON_API SlaveThreadRecorder : public ThreadRecorder
+ {
+ public:
+ SlaveThreadRecorder();
+ ~SlaveThreadRecorder();
+
+ // call this periodically to gather stats data for master thread to consume
+ /*virtual*/ void pushToMaster();
+
+ MasterThreadRecorder* mMaster;
+
+ class SharedData
+ {
+ public:
+ void copyFrom(const Recording& source);
+ void copyTo(Recording& sink);
+ private:
+ LLMutex mRecorderMutex;
+ Recording mRecorder;
+ };
+ SharedData mSharedData;
+ };
+}
+
+#endif // LL_LLTRACETHREADRECORDER_H