summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorRichard Linden <none@none>2013-06-27 00:08:58 -0700
committerRichard Linden <none@none>2013-06-27 00:08:58 -0700
commit0f178ec33debc6d92f3b2aa2392e640eb342a095 (patch)
treeeb13db7dc222dda47b6a78c0e40299ef1e74fafc /indra/llcommon
parent88fee7f87fc4a987a05002fedfcae11d6b42ba59 (diff)
parent808d3eff198d65e5a870abb670786935fc8356bd (diff)
Automated merge with http://bitbucket.org/lindenlab/viewer-interesting
Diffstat (limited to 'indra/llcommon')
-rwxr-xr-xindra/llcommon/CMakeLists.txt2
-rwxr-xr-xindra/llcommon/llfasttimer.h7
-rwxr-xr-xindra/llcommon/llthread.cpp4
-rw-r--r--indra/llcommon/llthreadlocalstorage.cpp2
-rw-r--r--indra/llcommon/llthreadlocalstorage.h31
-rw-r--r--indra/llcommon/lltrace.cpp24
-rw-r--r--indra/llcommon/lltrace.h594
-rw-r--r--indra/llcommon/lltraceaccumulators.cpp123
-rw-r--r--indra/llcommon/lltraceaccumulators.h661
-rw-r--r--indra/llcommon/lltracerecording.cpp117
-rw-r--r--indra/llcommon/lltracerecording.h35
-rw-r--r--indra/llcommon/lltracethreadrecorder.cpp145
-rw-r--r--indra/llcommon/lltracethreadrecorder.h47
13 files changed, 930 insertions, 862 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index bf99a4c3a0..0c76fd46c0 100755
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -103,6 +103,7 @@ set(llcommon_SOURCE_FILES
llthreadsafequeue.cpp
lltimer.cpp
lltrace.cpp
+ lltraceaccumulators.cpp
lltracerecording.cpp
lltracethreadrecorder.cpp
lluri.cpp
@@ -231,6 +232,7 @@ set(llcommon_HEADER_FILES
llthreadsafequeue.h
lltimer.h
lltrace.h
+ lltraceaccumulators.h
lltracerecording.h
lltracethreadrecorder.h
lltreeiterators.h
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index 642c99ccce..ab8612a8ad 100755
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -38,13 +38,6 @@ class LLMutex;
namespace LLTrace
{
-struct BlockTimerStackRecord
-{
- class BlockTimer* mActiveTimer;
- class TimeBlock* mTimeBlock;
- U64 mChildTime;
-};
-
class ThreadTimerStack
: public BlockTimerStackRecord,
public LLThreadLocalSingleton<ThreadTimerStack>
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index 118568d5ef..e8e546e769 100755
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -93,7 +93,7 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
{
LLThread *threadp = (LLThread *)datap;
- LLTrace::ThreadRecorder* thread_recorder = new LLTrace::SlaveThreadRecorder(LLTrace::getUIThreadRecorder());
+ LLTrace::SlaveThreadRecorder thread_recorder(LLTrace::getUIThreadRecorder());
#if !LL_DARWIN
sThreadID = threadp->mID;
@@ -107,8 +107,6 @@ void *APR_THREAD_FUNC LLThread::staticRun(apr_thread_t *apr_threadp, void *datap
// We're done with the run function, this thread is done executing now.
threadp->mStatus = STOPPED;
- delete thread_recorder;
-
return NULL;
}
diff --git a/indra/llcommon/llthreadlocalstorage.cpp b/indra/llcommon/llthreadlocalstorage.cpp
index 32d94331a6..03c306cc7f 100644
--- a/indra/llcommon/llthreadlocalstorage.cpp
+++ b/indra/llcommon/llthreadlocalstorage.cpp
@@ -88,6 +88,7 @@ void LLThreadLocalPointerBase::destroyStorage()
}
}
+//static
void LLThreadLocalPointerBase::initAllThreadLocalStorage()
{
if (!sInitialized)
@@ -102,6 +103,7 @@ void LLThreadLocalPointerBase::initAllThreadLocalStorage()
}
}
+//static
void LLThreadLocalPointerBase::destroyAllThreadLocalStorage()
{
if (sInitialized)
diff --git a/indra/llcommon/llthreadlocalstorage.h b/indra/llcommon/llthreadlocalstorage.h
index a15f9185b1..d6399d5131 100644
--- a/indra/llcommon/llthreadlocalstorage.h
+++ b/indra/llcommon/llthreadlocalstorage.h
@@ -145,7 +145,7 @@ public:
#if LL_DARWIN
pthread_setspecific(sInstanceKey, NULL);
#else
- sInstance = NULL;
+ sData.mInstance = NULL;
#endif
setInitState(DELETED);
}
@@ -182,7 +182,7 @@ public:
llerrs << "Could not set thread local storage" << llendl;
}
#else
- sInstance = instancep;
+ sData.mInstance = instancep;
#endif
setInitState(INITIALIZING);
instancep->initSingleton();
@@ -197,7 +197,7 @@ public:
#if LL_DARWIN
return (DERIVED_TYPE*)pthread_getspecific(sInstanceKey);
#else
- return sInstance;
+ return sData.mInstance;
#endif
}
@@ -247,7 +247,7 @@ private:
createTLSInitState();
return (EInitState)(int)pthread_getspecific(sInitStateKey);
#else
- return sInitState;
+ return sData.mInitState;
#endif
}
@@ -257,18 +257,21 @@ private:
createTLSInitState();
pthread_setspecific(sInitStateKey, (void*)state);
#else
- sInitState = state;
+ sData.mInitState = state;
#endif
}
LLThreadLocalSingleton(const LLThreadLocalSingleton& other);
virtual void initSingleton() {}
+ struct SingletonData
+ {
+ DERIVED_TYPE* mInstance;
+ EInitState mInitState;
+ };
#ifdef LL_WINDOWS
- static __declspec(thread) DERIVED_TYPE* sInstance;
- static __declspec(thread) EInitState sInitState;
+ static __declspec(thread) SingletonData sData;
#elif LL_LINUX
- static __thread DERIVED_TYPE* sInstance;
- static __thread EInitState sInitState;
+ static __thread SingletonData sData;
#elif LL_DARWIN
static pthread_key_t sInstanceKey;
static pthread_key_t sInitStateKey;
@@ -277,16 +280,10 @@ private:
#if LL_WINDOWS
template<typename DERIVED_TYPE>
-__declspec(thread) DERIVED_TYPE* LLThreadLocalSingleton<DERIVED_TYPE>::sInstance = NULL;
-
-template<typename DERIVED_TYPE>
-__declspec(thread) typename LLThreadLocalSingleton<DERIVED_TYPE>::EInitState LLThreadLocalSingleton<DERIVED_TYPE>::sInitState = LLThreadLocalSingleton<DERIVED_TYPE>::UNINITIALIZED;
+__declspec(thread) typename LLThreadLocalSingleton<DERIVED_TYPE>::SingletonData LLThreadLocalSingleton<DERIVED_TYPE>::sData = {NULL, LLThreadLocalSingleton<DERIVED_TYPE>::UNINITIALIZED};
#elif LL_LINUX
template<typename DERIVED_TYPE>
-__thread DERIVED_TYPE* LLThreadLocalSingleton<DERIVED_TYPE>::sInstance = NULL;
-
-template<typename DERIVED_TYPE>
-__thread typename LLThreadLocalSingleton<DERIVED_TYPE>::EInitState LLThreadLocalSingleton<DERIVED_TYPE>::sInitState = LLThreadLocalSingleton<DERIVED_TYPE>::UNINITIALIZED;
+__thread typename LLThreadLocalSingleton<DERIVED_TYPE>::SingletonData LLThreadLocalSingleton<DERIVED_TYPE>::sData = {NULL, LLThreadLocalSingleton<DERIVED_TYPE>::UNINITIALIZED};
#elif LL_DARWIN
template<typename DERIVED_TYPE>
pthread_key_t LLThreadLocalSingleton<DERIVED_TYPE>::sInstanceKey;
diff --git a/indra/llcommon/lltrace.cpp b/indra/llcommon/lltrace.cpp
index 59a4b42c97..25807c7b2c 100644
--- a/indra/llcommon/lltrace.cpp
+++ b/indra/llcommon/lltrace.cpp
@@ -35,8 +35,6 @@ static S32 sInitializationCount = 0;
namespace LLTrace
{
-static MasterThreadRecorder* gUIThreadRecorder = NULL;
-
void init()
{
if (sInitializationCount++ == 0)
@@ -59,28 +57,6 @@ void cleanup()
}
}
-MasterThreadRecorder& getUIThreadRecorder()
-{
- llassert(gUIThreadRecorder != NULL);
- return *gUIThreadRecorder;
-}
-
-LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder_ptr()
-{
- static LLThreadLocalPointer<ThreadRecorder> s_thread_recorder;
- return s_thread_recorder;
-}
-
-const LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder()
-{
- return get_thread_recorder_ptr();
-}
-
-void set_thread_recorder(ThreadRecorder* recorder)
-{
- get_thread_recorder_ptr() = recorder;
-}
-
TimeBlockTreeNode::TimeBlockTreeNode()
: mBlock(NULL),
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
index 884a316a3b..72ef51c232 100644
--- a/indra/llcommon/lltrace.h
+++ b/indra/llcommon/lltrace.h
@@ -32,7 +32,7 @@
#include "llmemory.h"
#include "llrefcount.h"
-#include "llunit.h"
+#include "lltraceaccumulators.h"
#include "llthreadlocalstorage.h"
#include "lltimer.h"
@@ -57,185 +57,6 @@ void init();
void cleanup();
bool isInitialized();
-const LLThreadLocalPointer<class ThreadRecorder>& get_thread_recorder();
-void set_thread_recorder(class ThreadRecorder*);
-
-class MasterThreadRecorder& getUIThreadRecorder();
-
-template<typename ACCUMULATOR>
-class AccumulatorBuffer : public LLRefCount
-{
- typedef AccumulatorBuffer<ACCUMULATOR> self_t;
- static const U32 DEFAULT_ACCUMULATOR_BUFFER_SIZE = 64;
-private:
- struct StaticAllocationMarker { };
-
- AccumulatorBuffer(StaticAllocationMarker m)
- : mStorageSize(0),
- mStorage(NULL)
- {}
-
-public:
-
- AccumulatorBuffer(const AccumulatorBuffer& other = *getDefaultBuffer())
- : mStorageSize(0),
- mStorage(NULL)
- {
- resize(other.mStorageSize);
- for (S32 i = 0; i < sNextStorageSlot; i++)
- {
- mStorage[i] = other.mStorage[i];
- }
- }
-
- ~AccumulatorBuffer()
- {
- if (isPrimary())
- {
- LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL);
- }
- delete[] mStorage;
- }
-
- LL_FORCE_INLINE ACCUMULATOR& operator[](size_t index)
- {
- return mStorage[index];
- }
-
- LL_FORCE_INLINE const ACCUMULATOR& operator[](size_t index) const
- {
- return mStorage[index];
- }
-
- void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other, bool append = true)
- {
- llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize > sNextStorageSlot);
- for (size_t i = 0; i < sNextStorageSlot; i++)
- {
- mStorage[i].addSamples(other.mStorage[i], append);
- }
- }
-
- void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other)
- {
- llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize > sNextStorageSlot);
- for (size_t i = 0; i < sNextStorageSlot; i++)
- {
- mStorage[i] = other.mStorage[i];
- }
- }
-
- void reset(const AccumulatorBuffer<ACCUMULATOR>* other = NULL)
- {
- llassert(mStorageSize >= sNextStorageSlot);
- for (size_t i = 0; i < sNextStorageSlot; i++)
- {
- mStorage[i].reset(other ? &other->mStorage[i] : NULL);
- }
- }
-
- void flush(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp)
- {
- llassert(mStorageSize >= sNextStorageSlot);
- for (size_t i = 0; i < sNextStorageSlot; i++)
- {
- mStorage[i].flush(time_stamp);
- }
- }
-
- void makePrimary()
- {
- LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(mStorage);
- }
-
- bool isPrimary() const
- {
- return LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance() == mStorage;
- }
-
- LL_FORCE_INLINE static ACCUMULATOR* getPrimaryStorage()
- {
- ACCUMULATOR* accumulator = LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance();
- return accumulator ? accumulator : getDefaultBuffer()->mStorage;
- }
-
- // NOTE: this is not thread-safe. We assume that slots are reserved in the main thread before any child threads are spawned
- size_t reserveSlot()
- {
-#ifndef LL_RELEASE_FOR_DOWNLOAD
- if (LLTrace::isInitialized())
- {
- llerrs << "Attempting to declare trace object after program initialization. Trace objects should be statically initialized." << llendl;
- }
-#endif
- size_t next_slot = sNextStorageSlot++;
- if (next_slot >= mStorageSize)
- {
- resize(mStorageSize + (mStorageSize >> 2));
- }
- llassert(mStorage && next_slot < mStorageSize);
- return next_slot;
- }
-
- void resize(size_t new_size)
- {
- if (new_size <= mStorageSize) return;
-
- ACCUMULATOR* old_storage = mStorage;
- mStorage = new ACCUMULATOR[new_size];
- if (old_storage)
- {
- for (S32 i = 0; i < mStorageSize; i++)
- {
- mStorage[i] = old_storage[i];
- }
- }
- mStorageSize = new_size;
- delete[] old_storage;
-
- self_t* default_buffer = getDefaultBuffer();
- if (this != default_buffer
- && new_size > default_buffer->size())
- {
- //NB: this is not thread safe, but we assume that all resizing occurs during static initialization
- default_buffer->resize(new_size);
- }
- }
-
- size_t size() const
- {
- return getNumIndices();
- }
-
- static size_t getNumIndices()
- {
- return sNextStorageSlot;
- }
-
- static self_t* getDefaultBuffer()
- {
- static bool sInitialized = false;
- if (!sInitialized)
- {
- // this buffer is allowed to leak so that trace calls from global destructors have somewhere to put their data
- // so as not to trigger an access violation
- sDefaultBuffer = new AccumulatorBuffer(StaticAllocationMarker());
- sInitialized = true;
- sDefaultBuffer->resize(DEFAULT_ACCUMULATOR_BUFFER_SIZE);
- }
- return sDefaultBuffer;
- }
-
-private:
- ACCUMULATOR* mStorage;
- size_t mStorageSize;
- static size_t sNextStorageSlot;
- static self_t* sDefaultBuffer;
-};
-
-template<typename ACCUMULATOR> size_t AccumulatorBuffer<ACCUMULATOR>::sNextStorageSlot = 0;
-template<typename ACCUMULATOR> AccumulatorBuffer<ACCUMULATOR>* AccumulatorBuffer<ACCUMULATOR>::sDefaultBuffer = NULL;
-
template<typename ACCUMULATOR>
class TraceType
: public LLInstanceTracker<TraceType<ACCUMULATOR>, std::string>
@@ -267,344 +88,6 @@ protected:
const size_t mAccumulatorIndex;
};
-class EventAccumulator
-{
-public:
- typedef F64 value_t;
- typedef F64 mean_t;
-
- EventAccumulator()
- : mSum(0),
- mMin((std::numeric_limits<F64>::max)()),
- mMax((std::numeric_limits<F64>::min)()),
- mMean(0),
- mVarianceSum(0),
- mNumSamples(0),
- mLastValue(0)
- {}
-
- void record(F64 value)
- {
- mNumSamples++;
- mSum += value;
- // NOTE: both conditions will hold on first pass through
- if (value < mMin)
- {
- mMin = value;
- }
- if (value > mMax)
- {
- mMax = value;
- }
- F64 old_mean = mMean;
- mMean += (value - old_mean) / (F64)mNumSamples;
- mVarianceSum += (value - old_mean) * (value - mMean);
- mLastValue = value;
- }
-
- void addSamples(const EventAccumulator& other, bool append)
- {
- if (other.mNumSamples)
- {
- mSum += other.mSum;
-
- // NOTE: both conditions will hold first time through
- if (other.mMin < mMin) { mMin = other.mMin; }
- if (other.mMax > mMax) { mMax = other.mMax; }
-
- // combine variance (and hence standard deviation) of 2 different sized sample groups using
- // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm
- F64 n_1 = (F64)mNumSamples,
- n_2 = (F64)other.mNumSamples;
- F64 m_1 = mMean,
- m_2 = other.mMean;
- F64 v_1 = mVarianceSum / mNumSamples,
- v_2 = other.mVarianceSum / other.mNumSamples;
- if (n_1 == 0)
- {
- mVarianceSum = other.mVarianceSum;
- }
- else if (n_2 == 0)
- {
- // don't touch variance
- // mVarianceSum = mVarianceSum;
- }
- else
- {
- mVarianceSum = (F64)mNumSamples
- * ((((n_1 - 1.f) * v_1)
- + ((n_2 - 1.f) * v_2)
- + (((n_1 * n_2) / (n_1 + n_2))
- * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2))))
- / (n_1 + n_2 - 1.f));
- }
-
- F64 weight = (F64)mNumSamples / (F64)(mNumSamples + other.mNumSamples);
- mNumSamples += other.mNumSamples;
- mMean = mMean * weight + other.mMean * (1.f - weight);
- if (append) mLastValue = other.mLastValue;
- }
- }
-
- void reset(const EventAccumulator* other)
- {
- mNumSamples = 0;
- mSum = 0;
- mMin = std::numeric_limits<F64>::max();
- mMax = std::numeric_limits<F64>::min();
- mMean = 0;
- mVarianceSum = 0;
- mLastValue = other ? other->mLastValue : 0;
- }
-
- void flush(LLUnitImplicit<F64, LLUnits::Seconds>) {}
-
- F64 getSum() const { return mSum; }
- F64 getMin() const { return mMin; }
- F64 getMax() const { return mMax; }
- F64 getLastValue() const { return mLastValue; }
- F64 getMean() const { return mMean; }
- F64 getStandardDeviation() const { return sqrtf(mVarianceSum / mNumSamples); }
- U32 getSampleCount() const { return mNumSamples; }
-
-private:
- F64 mSum,
- mMin,
- mMax,
- mLastValue;
-
- F64 mMean,
- mVarianceSum;
-
- U32 mNumSamples;
-};
-
-
-class SampleAccumulator
-{
-public:
- typedef F64 value_t;
- typedef F64 mean_t;
-
- SampleAccumulator()
- : mSum(0),
- mMin((std::numeric_limits<F64>::max)()),
- mMax((std::numeric_limits<F64>::min)()),
- mMean(0),
- mVarianceSum(0),
- mLastSampleTimeStamp(LLTimer::getTotalSeconds()),
- mTotalSamplingTime(0),
- mNumSamples(0),
- mLastValue(0),
- mHasValue(false)
- {}
-
- void sample(F64 value)
- {
- LLUnitImplicit<F64, LLUnits::Seconds> time_stamp = LLTimer::getTotalSeconds();
- LLUnitImplicit<F64, LLUnits::Seconds> delta_time = time_stamp - mLastSampleTimeStamp;
- mLastSampleTimeStamp = time_stamp;
-
- if (mHasValue)
- {
- mTotalSamplingTime += delta_time;
- mSum += mLastValue * delta_time;
-
- // NOTE: both conditions will hold first time through
- if (value < mMin) { mMin = value; }
- if (value > mMax) { mMax = value; }
-
- F64 old_mean = mMean;
- mMean += (delta_time / mTotalSamplingTime) * (mLastValue - old_mean);
- mVarianceSum += delta_time * (mLastValue - old_mean) * (mLastValue - mMean);
- }
-
- mLastValue = value;
- mNumSamples++;
- mHasValue = true;
- }
-
- void addSamples(const SampleAccumulator& other, bool append)
- {
- if (other.mTotalSamplingTime)
- {
- mSum += other.mSum;
-
- // NOTE: both conditions will hold first time through
- if (other.mMin < mMin) { mMin = other.mMin; }
- if (other.mMax > mMax) { mMax = other.mMax; }
-
- // combine variance (and hence standard deviation) of 2 different sized sample groups using
- // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm
- F64 n_1 = mTotalSamplingTime,
- n_2 = other.mTotalSamplingTime;
- F64 m_1 = mMean,
- m_2 = other.mMean;
- F64 v_1 = mVarianceSum / mTotalSamplingTime,
- v_2 = other.mVarianceSum / other.mTotalSamplingTime;
- if (n_1 == 0)
- {
- mVarianceSum = other.mVarianceSum;
- }
- else if (n_2 == 0)
- {
- // variance is unchanged
- // mVarianceSum = mVarianceSum;
- }
- else
- {
- mVarianceSum = mTotalSamplingTime
- * ((((n_1 - 1.f) * v_1)
- + ((n_2 - 1.f) * v_2)
- + (((n_1 * n_2) / (n_1 + n_2))
- * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2))))
- / (n_1 + n_2 - 1.f));
- }
-
- llassert(other.mTotalSamplingTime > 0);
- F64 weight = mTotalSamplingTime / (mTotalSamplingTime + other.mTotalSamplingTime);
- mNumSamples += other.mNumSamples;
- mTotalSamplingTime += other.mTotalSamplingTime;
- mMean = (mMean * weight) + (other.mMean * (1.0 - weight));
- if (append)
- {
- mLastValue = other.mLastValue;
- mLastSampleTimeStamp = other.mLastSampleTimeStamp;
- mHasValue |= other.mHasValue;
- }
- }
- }
-
- void reset(const SampleAccumulator* other)
- {
- mNumSamples = 0;
- mSum = 0;
- mMin = std::numeric_limits<F64>::max();
- mMax = std::numeric_limits<F64>::min();
- mMean = other ? other->mLastValue : 0;
- mVarianceSum = 0;
- mLastSampleTimeStamp = LLTimer::getTotalSeconds();
- mTotalSamplingTime = 0;
- mLastValue = other ? other->mLastValue : 0;
- mHasValue = other ? other->mHasValue : false;
- }
-
- void flush(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp)
- {
- LLUnitImplicit<F64, LLUnits::Seconds> delta_time = time_stamp - mLastSampleTimeStamp;
-
- if (mHasValue)
- {
- mSum += mLastValue * delta_time;
- mTotalSamplingTime += delta_time;
- }
- mLastSampleTimeStamp = time_stamp;
- }
-
- F64 getSum() const { return mSum; }
- F64 getMin() const { return mMin; }
- F64 getMax() const { return mMax; }
- F64 getLastValue() const { return mLastValue; }
- F64 getMean() const { return mMean; }
- F64 getStandardDeviation() const { return sqrtf(mVarianceSum / mTotalSamplingTime); }
- U32 getSampleCount() const { return mNumSamples; }
-
-private:
- F64 mSum,
- mMin,
- mMax,
- mLastValue;
-
- bool mHasValue;
-
- F64 mMean,
- mVarianceSum;
-
- LLUnitImplicit<F64, LLUnits::Seconds> mLastSampleTimeStamp,
- mTotalSamplingTime;
-
- U32 mNumSamples;
-};
-
-class CountAccumulator
-{
-public:
- typedef F64 value_t;
- typedef F64 mean_t;
-
- CountAccumulator()
- : mSum(0),
- mNumSamples(0)
- {}
-
- void add(F64 value)
- {
- mNumSamples++;
- mSum += value;
- }
-
- void addSamples(const CountAccumulator& other, bool /*append*/)
- {
- mSum += other.mSum;
- mNumSamples += other.mNumSamples;
- }
-
- void reset(const CountAccumulator* other)
- {
- mNumSamples = 0;
- mSum = 0;
- }
-
- void flush(LLUnitImplicit<F64, LLUnits::Seconds>) {}
-
- F64 getSum() const { return mSum; }
-
- U32 getSampleCount() const { return mNumSamples; }
-
-private:
- F64 mSum;
-
- U32 mNumSamples;
-};
-
-class TimeBlockAccumulator
-{
-public:
- typedef LLUnit<F64, LLUnits::Seconds> value_t;
- typedef LLUnit<F64, LLUnits::Seconds> mean_t;
- typedef TimeBlockAccumulator self_t;
-
- // fake classes that allows us to view different facets of underlying statistic
- struct CallCountFacet
- {
- typedef U32 value_t;
- typedef F32 mean_t;
- };
-
- struct SelfTimeFacet
- {
- typedef LLUnit<F64, LLUnits::Seconds> value_t;
- typedef LLUnit<F64, LLUnits::Seconds> mean_t;
- };
-
- TimeBlockAccumulator();
- void addSamples(const self_t& other, bool /*append*/);
- void reset(const self_t* other);
- void flush(LLUnitImplicit<F64, LLUnits::Seconds>) {}
-
- //
- // members
- //
- U64 mStartTotalTimeCounter,
- mTotalTimeCounter,
- mSelfTimeCounter;
- U32 mCalls;
- class TimeBlock* mParent; // last acknowledged parent of this time block
- class TimeBlock* mLastCaller; // used to bootstrap tree construction
- U16 mActiveCount; // number of timers with this ID active on stack
- bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
-
-};
template<>
class TraceType<TimeBlockAccumulator::CallCountFacet>
@@ -628,23 +111,6 @@ public:
{}
};
-class TimeBlock;
-class TimeBlockTreeNode
-{
-public:
- TimeBlockTreeNode();
-
- void setParent(TimeBlock* parent);
- TimeBlock* getParent() { return mParent; }
-
- TimeBlock* mBlock;
- TimeBlock* mParent;
- std::vector<TimeBlock*> mChildren;
- bool mCollapsed;
- bool mNeedsSorting;
-};
-
-
template <typename T = F64>
class EventStatHandle
: public TraceType<EventAccumulator>
@@ -712,64 +178,6 @@ void add(CountStatHandle<T>& count, VALUE_T value)
count.getPrimaryAccumulator()->add(storage_value(converted_value));
}
-
-struct MemStatAccumulator
-{
- typedef MemStatAccumulator self_t;
-
- // fake classes that allows us to view different facets of underlying statistic
- struct AllocationCountFacet
- {
- typedef U32 value_t;
- typedef F32 mean_t;
- };
-
- struct DeallocationCountFacet
- {
- typedef U32 value_t;
- typedef F32 mean_t;
- };
-
- struct ChildMemFacet
- {
- typedef LLUnit<F64, LLUnits::Bytes> value_t;
- typedef LLUnit<F64, LLUnits::Bytes> mean_t;
- };
-
- MemStatAccumulator()
- : mAllocatedCount(0),
- mDeallocatedCount(0)
- {}
-
- void addSamples(const MemStatAccumulator& other, bool append)
- {
- mSize.addSamples(other.mSize, append);
- mChildSize.addSamples(other.mChildSize, append);
- mAllocatedCount += other.mAllocatedCount;
- mDeallocatedCount += other.mDeallocatedCount;
- }
-
- void reset(const MemStatAccumulator* other)
- {
- mSize.reset(other ? &other->mSize : NULL);
- mChildSize.reset(other ? &other->mChildSize : NULL);
- mAllocatedCount = 0;
- mDeallocatedCount = 0;
- }
-
- void flush(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp)
- {
- mSize.flush(time_stamp);
- mChildSize.flush(time_stamp);
- }
-
- SampleAccumulator mSize,
- mChildSize;
- int mAllocatedCount,
- mDeallocatedCount;
-};
-
-
template<>
class TraceType<MemStatAccumulator::AllocationCountFacet>
: public TraceType<MemStatAccumulator>
diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp
new file mode 100644
index 0000000000..950c1d97d1
--- /dev/null
+++ b/indra/llcommon/lltraceaccumulators.cpp
@@ -0,0 +1,123 @@
+/**
+ * @file lltracesampler.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 "lltraceaccumulators.h"
+#include "lltracethreadrecorder.h"
+
+namespace LLTrace
+{
+
+
+///////////////////////////////////////////////////////////////////////
+// AccumulatorBufferGroup
+///////////////////////////////////////////////////////////////////////
+
+AccumulatorBufferGroup::AccumulatorBufferGroup()
+{}
+
+void AccumulatorBufferGroup::handOffTo(AccumulatorBufferGroup& other)
+{
+ other.mCounts.reset(&mCounts);
+ other.mSamples.reset(&mSamples);
+ other.mEvents.reset(&mEvents);
+ other.mStackTimers.reset(&mStackTimers);
+ other.mMemStats.reset(&mMemStats);
+}
+
+void AccumulatorBufferGroup::makePrimary()
+{
+ mCounts.makePrimary();
+ mSamples.makePrimary();
+ mEvents.makePrimary();
+ mStackTimers.makePrimary();
+ mMemStats.makePrimary();
+
+ ThreadRecorder* thread_recorder = get_thread_recorder().get();
+ AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = mStackTimers;
+ // update stacktimer parent pointers
+ for (S32 i = 0, end_i = mStackTimers.size(); i < end_i; i++)
+ {
+ TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(i);
+ if (tree_node)
+ {
+ timer_accumulator_buffer[i].mParent = tree_node->mParent;
+ }
+ }
+}
+
+//static
+void AccumulatorBufferGroup::clearPrimary()
+{
+ AccumulatorBuffer<CountAccumulator>::clearPrimary();
+ AccumulatorBuffer<SampleAccumulator>::clearPrimary();
+ AccumulatorBuffer<EventAccumulator>::clearPrimary();
+ AccumulatorBuffer<TimeBlockAccumulator>::clearPrimary();
+ AccumulatorBuffer<MemStatAccumulator>::clearPrimary();
+}
+
+bool AccumulatorBufferGroup::isPrimary() const
+{
+ return mCounts.isPrimary();
+}
+
+void AccumulatorBufferGroup::append( const AccumulatorBufferGroup& other )
+{
+ mCounts.addSamples(other.mCounts);
+ mSamples.addSamples(other.mSamples);
+ mEvents.addSamples(other.mEvents);
+ mMemStats.addSamples(other.mMemStats);
+ mStackTimers.addSamples(other.mStackTimers);
+}
+
+void AccumulatorBufferGroup::merge( const AccumulatorBufferGroup& other)
+{
+ mCounts.addSamples(other.mCounts, false);
+ mSamples.addSamples(other.mSamples, false);
+ mEvents.addSamples(other.mEvents, false);
+ mMemStats.addSamples(other.mMemStats, false);
+ // for now, hold out timers from merge, need to be displayed per thread
+ //mStackTimers.addSamples(other.mStackTimers, false);
+}
+
+void AccumulatorBufferGroup::reset(AccumulatorBufferGroup* other)
+{
+ mCounts.reset(other ? &other->mCounts : NULL);
+ mSamples.reset(other ? &other->mSamples : NULL);
+ mEvents.reset(other ? &other->mEvents : NULL);
+ mStackTimers.reset(other ? &other->mStackTimers : NULL);
+ mMemStats.reset(other ? &other->mMemStats : NULL);
+}
+
+void AccumulatorBufferGroup::sync()
+{
+ LLUnitImplicit<F64, LLUnits::Seconds> time_stamp = LLTimer::getTotalSeconds();
+
+ mSamples.sync(time_stamp);
+ mMemStats.sync(time_stamp);
+}
+
+}
diff --git a/indra/llcommon/lltraceaccumulators.h b/indra/llcommon/lltraceaccumulators.h
new file mode 100644
index 0000000000..825cc9e3a8
--- /dev/null
+++ b/indra/llcommon/lltraceaccumulators.h
@@ -0,0 +1,661 @@
+/**
+ * @file lltraceaccumulators.h
+ * @brief Storage for accumulating statistics
+ *
+ * $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_LLTRACEACCUMULATORS_H
+#define LL_LLTRACEACCUMULATORS_H
+
+
+#include "stdtypes.h"
+#include "llpreprocessor.h"
+#include "llunit.h"
+#include "lltimer.h"
+#include "llrefcount.h"
+
+namespace LLTrace
+{
+
+ template<typename ACCUMULATOR>
+ class AccumulatorBuffer : public LLRefCount
+ {
+ typedef AccumulatorBuffer<ACCUMULATOR> self_t;
+ static const U32 DEFAULT_ACCUMULATOR_BUFFER_SIZE = 64;
+ private:
+ struct StaticAllocationMarker { };
+
+ AccumulatorBuffer(StaticAllocationMarker m)
+ : mStorageSize(0),
+ mStorage(NULL)
+ {}
+
+ public:
+
+ AccumulatorBuffer(const AccumulatorBuffer& other = *getDefaultBuffer())
+ : mStorageSize(0),
+ mStorage(NULL)
+ {
+ resize(other.mStorageSize);
+ for (S32 i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i] = other.mStorage[i];
+ }
+ }
+
+ ~AccumulatorBuffer()
+ {
+ if (isPrimary())
+ {
+ LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL);
+ }
+ delete[] mStorage;
+ }
+
+ LL_FORCE_INLINE ACCUMULATOR& operator[](size_t index)
+ {
+ return mStorage[index];
+ }
+
+ LL_FORCE_INLINE const ACCUMULATOR& operator[](size_t index) const
+ {
+ return mStorage[index];
+ }
+
+ void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other, bool append = true)
+ {
+ llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize > sNextStorageSlot);
+ for (size_t i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i].addSamples(other.mStorage[i], append);
+ }
+ }
+
+ void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other)
+ {
+ llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize > sNextStorageSlot);
+ for (size_t i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i] = other.mStorage[i];
+ }
+ }
+
+ void reset(const AccumulatorBuffer<ACCUMULATOR>* other = NULL)
+ {
+ llassert(mStorageSize >= sNextStorageSlot);
+ for (size_t i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i].reset(other ? &other->mStorage[i] : NULL);
+ }
+ }
+
+ void sync(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp)
+ {
+ llassert(mStorageSize >= sNextStorageSlot);
+ for (size_t i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i].sync(time_stamp);
+ }
+ }
+
+ void makePrimary()
+ {
+ LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(mStorage);
+ }
+
+ bool isPrimary() const
+ {
+ return LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance() == mStorage;
+ }
+
+ static void clearPrimary()
+ {
+ LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL);
+ }
+
+ LL_FORCE_INLINE static ACCUMULATOR* getPrimaryStorage()
+ {
+ ACCUMULATOR* accumulator = LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance();
+ return accumulator ? accumulator : getDefaultBuffer()->mStorage;
+ }
+
+ // NOTE: this is not thread-safe. We assume that slots are reserved in the main thread before any child threads are spawned
+ size_t reserveSlot()
+ {
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ if (LLTrace::isInitialized())
+ {
+ llerrs << "Attempting to declare trace object after program initialization. Trace objects should be statically initialized." << llendl;
+ }
+#endif
+ size_t next_slot = sNextStorageSlot++;
+ if (next_slot >= mStorageSize)
+ {
+ resize(mStorageSize + (mStorageSize >> 2));
+ }
+ llassert(mStorage && next_slot < mStorageSize);
+ return next_slot;
+ }
+
+ void resize(size_t new_size)
+ {
+ if (new_size <= mStorageSize) return;
+
+ ACCUMULATOR* old_storage = mStorage;
+ mStorage = new ACCUMULATOR[new_size];
+ if (old_storage)
+ {
+ for (S32 i = 0; i < mStorageSize; i++)
+ {
+ mStorage[i] = old_storage[i];
+ }
+ }
+ mStorageSize = new_size;
+ delete[] old_storage;
+
+ self_t* default_buffer = getDefaultBuffer();
+ if (this != default_buffer
+ && new_size > default_buffer->size())
+ {
+ //NB: this is not thread safe, but we assume that all resizing occurs during static initialization
+ default_buffer->resize(new_size);
+ }
+ }
+
+ size_t size() const
+ {
+ return getNumIndices();
+ }
+
+ static size_t getNumIndices()
+ {
+ return sNextStorageSlot;
+ }
+
+ static self_t* getDefaultBuffer()
+ {
+ static bool sInitialized = false;
+ if (!sInitialized)
+ {
+ // this buffer is allowed to leak so that trace calls from global destructors have somewhere to put their data
+ // so as not to trigger an access violation
+ sDefaultBuffer = new AccumulatorBuffer(StaticAllocationMarker());
+ sInitialized = true;
+ sDefaultBuffer->resize(DEFAULT_ACCUMULATOR_BUFFER_SIZE);
+ }
+ return sDefaultBuffer;
+ }
+
+ private:
+ ACCUMULATOR* mStorage;
+ size_t mStorageSize;
+ static size_t sNextStorageSlot;
+ static self_t* sDefaultBuffer;
+ };
+
+ template<typename ACCUMULATOR> size_t AccumulatorBuffer<ACCUMULATOR>::sNextStorageSlot = 0;
+ template<typename ACCUMULATOR> AccumulatorBuffer<ACCUMULATOR>* AccumulatorBuffer<ACCUMULATOR>::sDefaultBuffer = NULL;
+
+
+ class EventAccumulator
+ {
+ public:
+ typedef F64 value_t;
+ typedef F64 mean_t;
+
+ EventAccumulator()
+ : mSum(0),
+ mMin((std::numeric_limits<F64>::max)()),
+ mMax((std::numeric_limits<F64>::min)()),
+ mMean(0),
+ mVarianceSum(0),
+ mNumSamples(0),
+ mLastValue(0)
+ {}
+
+ void record(F64 value)
+ {
+ mNumSamples++;
+ mSum += value;
+ // NOTE: both conditions will hold on first pass through
+ if (value < mMin)
+ {
+ mMin = value;
+ }
+ if (value > mMax)
+ {
+ mMax = value;
+ }
+ F64 old_mean = mMean;
+ mMean += (value - old_mean) / (F64)mNumSamples;
+ mVarianceSum += (value - old_mean) * (value - mMean);
+ mLastValue = value;
+ }
+
+ void addSamples(const EventAccumulator& other, bool append)
+ {
+ if (other.mNumSamples)
+ {
+ mSum += other.mSum;
+
+ // NOTE: both conditions will hold first time through
+ if (other.mMin < mMin) { mMin = other.mMin; }
+ if (other.mMax > mMax) { mMax = other.mMax; }
+
+ // combine variance (and hence standard deviation) of 2 different sized sample groups using
+ // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm
+ F64 n_1 = (F64)mNumSamples,
+ n_2 = (F64)other.mNumSamples;
+ F64 m_1 = mMean,
+ m_2 = other.mMean;
+ F64 v_1 = mVarianceSum / mNumSamples,
+ v_2 = other.mVarianceSum / other.mNumSamples;
+ if (n_1 == 0)
+ {
+ mVarianceSum = other.mVarianceSum;
+ }
+ else if (n_2 == 0)
+ {
+ // don't touch variance
+ // mVarianceSum = mVarianceSum;
+ }
+ else
+ {
+ mVarianceSum = (F64)mNumSamples
+ * ((((n_1 - 1.f) * v_1)
+ + ((n_2 - 1.f) * v_2)
+ + (((n_1 * n_2) / (n_1 + n_2))
+ * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2))))
+ / (n_1 + n_2 - 1.f));
+ }
+
+ F64 weight = (F64)mNumSamples / (F64)(mNumSamples + other.mNumSamples);
+ mNumSamples += other.mNumSamples;
+ mMean = mMean * weight + other.mMean * (1.f - weight);
+ if (append) mLastValue = other.mLastValue;
+ }
+ }
+
+ void reset(const EventAccumulator* other)
+ {
+ mNumSamples = 0;
+ mSum = 0;
+ mMin = std::numeric_limits<F64>::max();
+ mMax = std::numeric_limits<F64>::min();
+ mMean = 0;
+ mVarianceSum = 0;
+ mLastValue = other ? other->mLastValue : 0;
+ }
+
+ void sync(LLUnitImplicit<F64, LLUnits::Seconds>) {}
+
+ F64 getSum() const { return mSum; }
+ F64 getMin() const { return mMin; }
+ F64 getMax() const { return mMax; }
+ F64 getLastValue() const { return mLastValue; }
+ F64 getMean() const { return mMean; }
+ F64 getStandardDeviation() const { return sqrtf(mVarianceSum / mNumSamples); }
+ U32 getSampleCount() const { return mNumSamples; }
+
+ private:
+ F64 mSum,
+ mMin,
+ mMax,
+ mLastValue;
+
+ F64 mMean,
+ mVarianceSum;
+
+ U32 mNumSamples;
+ };
+
+
+ class SampleAccumulator
+ {
+ public:
+ typedef F64 value_t;
+ typedef F64 mean_t;
+
+ SampleAccumulator()
+ : mSum(0),
+ mMin((std::numeric_limits<F64>::max)()),
+ mMax((std::numeric_limits<F64>::min)()),
+ mMean(0),
+ mVarianceSum(0),
+ mLastSampleTimeStamp(LLTimer::getTotalSeconds()),
+ mTotalSamplingTime(0),
+ mNumSamples(0),
+ mLastValue(0),
+ mHasValue(false)
+ {}
+
+ void sample(F64 value)
+ {
+ LLUnitImplicit<F64, LLUnits::Seconds> time_stamp = LLTimer::getTotalSeconds();
+ LLUnitImplicit<F64, LLUnits::Seconds> delta_time = time_stamp - mLastSampleTimeStamp;
+ mLastSampleTimeStamp = time_stamp;
+
+ if (mHasValue)
+ {
+ mTotalSamplingTime += delta_time;
+ mSum += mLastValue * delta_time;
+
+ // NOTE: both conditions will hold first time through
+ if (value < mMin) { mMin = value; }
+ if (value > mMax) { mMax = value; }
+
+ F64 old_mean = mMean;
+ mMean += (delta_time / mTotalSamplingTime) * (mLastValue - old_mean);
+ mVarianceSum += delta_time * (mLastValue - old_mean) * (mLastValue - mMean);
+ }
+
+ mLastValue = value;
+ mNumSamples++;
+ mHasValue = true;
+ }
+
+ void addSamples(const SampleAccumulator& other, bool append)
+ {
+ if (other.mTotalSamplingTime)
+ {
+ mSum += other.mSum;
+
+ // NOTE: both conditions will hold first time through
+ if (other.mMin < mMin) { mMin = other.mMin; }
+ if (other.mMax > mMax) { mMax = other.mMax; }
+
+ // combine variance (and hence standard deviation) of 2 different sized sample groups using
+ // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm
+ F64 n_1 = mTotalSamplingTime,
+ n_2 = other.mTotalSamplingTime;
+ F64 m_1 = mMean,
+ m_2 = other.mMean;
+ F64 v_1 = mVarianceSum / mTotalSamplingTime,
+ v_2 = other.mVarianceSum / other.mTotalSamplingTime;
+ if (n_1 == 0)
+ {
+ mVarianceSum = other.mVarianceSum;
+ }
+ else if (n_2 == 0)
+ {
+ // variance is unchanged
+ // mVarianceSum = mVarianceSum;
+ }
+ else
+ {
+ mVarianceSum = mTotalSamplingTime
+ * ((((n_1 - 1.f) * v_1)
+ + ((n_2 - 1.f) * v_2)
+ + (((n_1 * n_2) / (n_1 + n_2))
+ * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2))))
+ / (n_1 + n_2 - 1.f));
+ }
+
+ llassert(other.mTotalSamplingTime > 0);
+ F64 weight = mTotalSamplingTime / (mTotalSamplingTime + other.mTotalSamplingTime);
+ mNumSamples += other.mNumSamples;
+ mTotalSamplingTime += other.mTotalSamplingTime;
+ mMean = (mMean * weight) + (other.mMean * (1.0 - weight));
+ if (append)
+ {
+ mLastValue = other.mLastValue;
+ mLastSampleTimeStamp = other.mLastSampleTimeStamp;
+ mHasValue |= other.mHasValue;
+ }
+ }
+ }
+
+ void reset(const SampleAccumulator* other)
+ {
+ mNumSamples = 0;
+ mSum = 0;
+ mMin = std::numeric_limits<F64>::max();
+ mMax = std::numeric_limits<F64>::min();
+ mMean = other ? other->mLastValue : 0;
+ mVarianceSum = 0;
+ mLastSampleTimeStamp = LLTimer::getTotalSeconds();
+ mTotalSamplingTime = 0;
+ mLastValue = other ? other->mLastValue : 0;
+ mHasValue = other ? other->mHasValue : false;
+ }
+
+ void sync(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp)
+ {
+ LLUnitImplicit<F64, LLUnits::Seconds> delta_time = time_stamp - mLastSampleTimeStamp;
+
+ if (mHasValue)
+ {
+ mSum += mLastValue * delta_time;
+ mTotalSamplingTime += delta_time;
+ }
+ mLastSampleTimeStamp = time_stamp;
+ }
+
+ F64 getSum() const { return mSum; }
+ F64 getMin() const { return mMin; }
+ F64 getMax() const { return mMax; }
+ F64 getLastValue() const { return mLastValue; }
+ F64 getMean() const { return mMean; }
+ F64 getStandardDeviation() const { return sqrtf(mVarianceSum / mTotalSamplingTime); }
+ U32 getSampleCount() const { return mNumSamples; }
+
+ private:
+ F64 mSum,
+ mMin,
+ mMax,
+ mLastValue;
+
+ bool mHasValue;
+
+ F64 mMean,
+ mVarianceSum;
+
+ LLUnitImplicit<F64, LLUnits::Seconds> mLastSampleTimeStamp,
+ mTotalSamplingTime;
+
+ U32 mNumSamples;
+ };
+
+ class CountAccumulator
+ {
+ public:
+ typedef F64 value_t;
+ typedef F64 mean_t;
+
+ CountAccumulator()
+ : mSum(0),
+ mNumSamples(0)
+ {}
+
+ void add(F64 value)
+ {
+ mNumSamples++;
+ mSum += value;
+ }
+
+ void addSamples(const CountAccumulator& other, bool /*append*/)
+ {
+ mSum += other.mSum;
+ mNumSamples += other.mNumSamples;
+ }
+
+ void reset(const CountAccumulator* other)
+ {
+ mNumSamples = 0;
+ mSum = 0;
+ }
+
+ void sync(LLUnitImplicit<F64, LLUnits::Seconds>) {}
+
+ F64 getSum() const { return mSum; }
+
+ U32 getSampleCount() const { return mNumSamples; }
+
+ private:
+ F64 mSum;
+
+ U32 mNumSamples;
+ };
+
+ class TimeBlockAccumulator
+ {
+ public:
+ typedef LLUnit<F64, LLUnits::Seconds> value_t;
+ typedef LLUnit<F64, LLUnits::Seconds> mean_t;
+ typedef TimeBlockAccumulator self_t;
+
+ // fake classes that allows us to view different facets of underlying statistic
+ struct CallCountFacet
+ {
+ typedef U32 value_t;
+ typedef F32 mean_t;
+ };
+
+ struct SelfTimeFacet
+ {
+ typedef LLUnit<F64, LLUnits::Seconds> value_t;
+ typedef LLUnit<F64, LLUnits::Seconds> mean_t;
+ };
+
+ TimeBlockAccumulator();
+ void addSamples(const self_t& other, bool /*append*/);
+ void reset(const self_t* other);
+ void sync(LLUnitImplicit<F64, LLUnits::Seconds>) {}
+
+ //
+ // members
+ //
+ U64 mStartTotalTimeCounter,
+ mTotalTimeCounter,
+ mSelfTimeCounter;
+ U32 mCalls;
+ class TimeBlock* mParent; // last acknowledged parent of this time block
+ class TimeBlock* mLastCaller; // used to bootstrap tree construction
+ U16 mActiveCount; // number of timers with this ID active on stack
+ bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
+
+ };
+
+ class TimeBlock;
+ class TimeBlockTreeNode
+ {
+ public:
+ TimeBlockTreeNode();
+
+ void setParent(TimeBlock* parent);
+ TimeBlock* getParent() { return mParent; }
+
+ TimeBlock* mBlock;
+ TimeBlock* mParent;
+ std::vector<TimeBlock*> mChildren;
+ bool mCollapsed;
+ bool mNeedsSorting;
+ };
+
+ struct BlockTimerStackRecord
+ {
+ class BlockTimer* mActiveTimer;
+ class TimeBlock* mTimeBlock;
+ U64 mChildTime;
+ };
+
+ struct MemStatAccumulator
+ {
+ typedef MemStatAccumulator self_t;
+
+ // fake classes that allows us to view different facets of underlying statistic
+ struct AllocationCountFacet
+ {
+ typedef U32 value_t;
+ typedef F32 mean_t;
+ };
+
+ struct DeallocationCountFacet
+ {
+ typedef U32 value_t;
+ typedef F32 mean_t;
+ };
+
+ struct ChildMemFacet
+ {
+ typedef LLUnit<F64, LLUnits::Bytes> value_t;
+ typedef LLUnit<F64, LLUnits::Bytes> mean_t;
+ };
+
+ MemStatAccumulator()
+ : mAllocatedCount(0),
+ mDeallocatedCount(0)
+ {}
+
+ void addSamples(const MemStatAccumulator& other, bool append)
+ {
+ mSize.addSamples(other.mSize, append);
+ mChildSize.addSamples(other.mChildSize, append);
+ mAllocatedCount += other.mAllocatedCount;
+ mDeallocatedCount += other.mDeallocatedCount;
+ }
+
+ void reset(const MemStatAccumulator* other)
+ {
+ mSize.reset(other ? &other->mSize : NULL);
+ mChildSize.reset(other ? &other->mChildSize : NULL);
+ mAllocatedCount = 0;
+ mDeallocatedCount = 0;
+ }
+
+ void sync(LLUnitImplicit<F64, LLUnits::Seconds> time_stamp)
+ {
+ mSize.sync(time_stamp);
+ mChildSize.sync(time_stamp);
+ }
+
+ SampleAccumulator mSize,
+ mChildSize;
+ int mAllocatedCount,
+ mDeallocatedCount;
+ };
+
+ struct AccumulatorBufferGroup : public LLRefCount
+ {
+ AccumulatorBufferGroup();
+
+ void handOffTo(AccumulatorBufferGroup& other);
+ void makePrimary();
+ bool isPrimary() const;
+ static void clearPrimary();
+
+ void append(const AccumulatorBufferGroup& other);
+ void merge(const AccumulatorBufferGroup& other);
+ void reset(AccumulatorBufferGroup* other = NULL);
+ void sync();
+
+ AccumulatorBuffer<CountAccumulator> mCounts;
+ AccumulatorBuffer<SampleAccumulator> mSamples;
+ AccumulatorBuffer<EventAccumulator> mEvents;
+ AccumulatorBuffer<TimeBlockAccumulator> mStackTimers;
+ AccumulatorBuffer<MemStatAccumulator> mMemStats;
+ };
+}
+
+#endif // LL_LLTRACEACCUMULATORS_H
+
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index d34434f161..0938317eaa 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -33,85 +33,7 @@
namespace LLTrace
{
-
-
-///////////////////////////////////////////////////////////////////////
-// RecordingBuffers
-///////////////////////////////////////////////////////////////////////
-
-RecordingBuffers::RecordingBuffers()
-{}
-
-void RecordingBuffers::handOffTo(RecordingBuffers& other)
-{
- other.mCounts.reset(&mCounts);
- other.mSamples.reset(&mSamples);
- other.mEvents.reset(&mEvents);
- other.mStackTimers.reset(&mStackTimers);
- other.mMemStats.reset(&mMemStats);
-}
-
-void RecordingBuffers::makePrimary()
-{
- mCounts.makePrimary();
- mSamples.makePrimary();
- mEvents.makePrimary();
- mStackTimers.makePrimary();
- mMemStats.makePrimary();
-
- ThreadRecorder* thread_recorder = get_thread_recorder().get();
- AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = mStackTimers;
- // update stacktimer parent pointers
- for (S32 i = 0, end_i = mStackTimers.size(); i < end_i; i++)
- {
- TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(i);
- if (tree_node)
- {
- timer_accumulator_buffer[i].mParent = tree_node->mParent;
- }
- }
-}
-
-bool RecordingBuffers::isPrimary() const
-{
- return mCounts.isPrimary();
-}
-
-void RecordingBuffers::append( const RecordingBuffers& other )
-{
- mCounts.addSamples(other.mCounts);
- mSamples.addSamples(other.mSamples);
- mEvents.addSamples(other.mEvents);
- mMemStats.addSamples(other.mMemStats);
- mStackTimers.addSamples(other.mStackTimers);
-}
-
-void RecordingBuffers::merge( const RecordingBuffers& other)
-{
- mCounts.addSamples(other.mCounts, false);
- mSamples.addSamples(other.mSamples, false);
- mEvents.addSamples(other.mEvents, false);
- mMemStats.addSamples(other.mMemStats, false);
- // for now, hold out timers from merge, need to be displayed per thread
- //mStackTimers.addSamples(other.mStackTimers, false);
-}
-
-void RecordingBuffers::reset(RecordingBuffers* other)
-{
- mCounts.reset(other ? &other->mCounts : NULL);
- mSamples.reset(other ? &other->mSamples : NULL);
- mEvents.reset(other ? &other->mEvents : NULL);
- mStackTimers.reset(other ? &other->mStackTimers : NULL);
- mMemStats.reset(other ? &other->mMemStats : NULL);
-}
-
-void RecordingBuffers::flush()
-{
- LLUnitImplicit<F64, LLUnits::Seconds> time_stamp = LLTimer::getTotalSeconds();
-
- mSamples.flush(time_stamp);
-}
-
+
///////////////////////////////////////////////////////////////////////
// Recording
///////////////////////////////////////////////////////////////////////
@@ -119,7 +41,7 @@ void RecordingBuffers::flush()
Recording::Recording()
: mElapsedSeconds(0)
{
- mBuffers = new RecordingBuffers();
+ mBuffers = new AccumulatorBufferGroup();
}
Recording::Recording( const Recording& other )
@@ -132,17 +54,17 @@ Recording& Recording::operator = (const Recording& other)
// this will allow us to seamlessly start without affecting any data we've acquired from other
setPlayState(PAUSED);
- Recording& mutable_other = const_cast<Recording&>(other);
- mutable_other.update();
+ const_cast<Recording&>(other).update();
EPlayState other_play_state = other.getPlayState();
- mBuffers = mutable_other.mBuffers;
-
- LLStopWatchControlsMixin<Recording>::setPlayState(other_play_state);
+ mBuffers = other.mBuffers;
// above call will clear mElapsedSeconds as a side effect, so copy it here
mElapsedSeconds = other.mElapsedSeconds;
mSamplingTimer = other.mSamplingTimer;
+
+ setPlayState(other_play_state);
+
return *this;
}
@@ -151,7 +73,7 @@ Recording::~Recording()
{
if (isStarted() && LLTrace::get_thread_recorder().notNull())
{
- LLTrace::get_thread_recorder()->deactivate(this);
+ LLTrace::get_thread_recorder()->deactivate(mBuffers.write());
}
}
@@ -159,8 +81,10 @@ void Recording::update()
{
if (isStarted())
{
- mBuffers.write()->flush();
- LLTrace::get_thread_recorder()->bringUpToDate(this);
+ mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
+ AccumulatorBufferGroup* buffers = mBuffers.write();
+ LLTrace::get_thread_recorder()->bringUpToDate(buffers);
+
mSamplingTimer.reset();
}
}
@@ -176,14 +100,14 @@ void Recording::handleReset()
void Recording::handleStart()
{
mSamplingTimer.reset();
- LLTrace::get_thread_recorder()->activate(this);
+ LLTrace::get_thread_recorder()->activate(mBuffers.write());
}
void Recording::handleStop()
{
mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
- mBuffers.write()->flush();
- LLTrace::get_thread_recorder()->deactivate(this);
+ AccumulatorBufferGroup* buffers = mBuffers.write();
+ LLTrace::get_thread_recorder()->deactivate(buffers);
}
void Recording::handleSplitTo(Recording& other)
@@ -191,19 +115,14 @@ void Recording::handleSplitTo(Recording& other)
mBuffers.write()->handOffTo(*other.mBuffers.write());
}
-void Recording::appendRecording( const Recording& other )
+void Recording::appendRecording( Recording& other )
{
update();
+ other.update();
mBuffers.write()->append(*other.mBuffers);
mElapsedSeconds += other.mElapsedSeconds;
}
-void Recording::mergeRecording( const Recording& other)
-{
- update();
- mBuffers.write()->merge(*other.mBuffers);
-}
-
LLUnit<F64, LLUnits::Seconds> Recording::getSum(const TraceType<TimeBlockAccumulator>& stat)
{
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
@@ -710,8 +629,6 @@ F64 PeriodicRecording::getPeriodMean( const TraceType<SampleAccumulator>& stat,
void ExtendableRecording::extend()
{
- // stop recording to get latest data
- mPotentialRecording.update();
// push the data back to accepted recording
mAcceptedRecording.appendRecording(mPotentialRecording);
// flush data, so we can start from scratch
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
index b839e85de0..355dbabb1c 100644
--- a/indra/llcommon/lltracerecording.h
+++ b/indra/llcommon/lltracerecording.h
@@ -32,7 +32,7 @@
#include "llpointer.h"
#include "lltimer.h"
-#include "lltrace.h"
+#include "lltraceaccumulators.h"
class LLStopWatchControlsMixinCommon
{
@@ -81,6 +81,7 @@ class LLStopWatchControlsMixin
: public LLStopWatchControlsMixinCommon
{
public:
+
typedef LLStopWatchControlsMixin<DERIVED> self_t;
virtual void splitTo(DERIVED& other)
{
@@ -98,6 +99,11 @@ public:
static_cast<self_t&>(other).handleSplitTo(*static_cast<DERIVED*>(this));
}
private:
+ self_t& operator = (const self_t& other)
+ {
+ // don't do anything, derived class must implement logic
+ }
+
// atomically stop this object while starting the other
// no data can be missed in between stop and start
virtual void handleSplitTo(DERIVED& other) {};
@@ -106,26 +112,6 @@ private:
namespace LLTrace
{
- struct RecordingBuffers : public LLRefCount
- {
- RecordingBuffers();
-
- void handOffTo(RecordingBuffers& other);
- void makePrimary();
- bool isPrimary() const;
-
- void append(const RecordingBuffers& other);
- void merge(const RecordingBuffers& other);
- void reset(RecordingBuffers* other = NULL);
- void flush();
-
- AccumulatorBuffer<CountAccumulator> mCounts;
- AccumulatorBuffer<SampleAccumulator> mSamples;
- AccumulatorBuffer<EventAccumulator> mEvents;
- AccumulatorBuffer<TimeBlockAccumulator> mStackTimers;
- AccumulatorBuffer<MemStatAccumulator> mMemStats;
- };
-
class Recording
: public LLStopWatchControlsMixin<Recording>
{
@@ -138,10 +124,7 @@ namespace LLTrace
Recording& operator = (const Recording& other);
// accumulate data from subsequent, non-overlapping recording
- void appendRecording(const Recording& other);
-
- // gather data from recording, ignoring time relationship (for example, pulling data from slave threads)
- void mergeRecording(const Recording& other);
+ void appendRecording(Recording& other);
// grab latest recorded data
void update();
@@ -291,7 +274,7 @@ namespace LLTrace
LLTimer mSamplingTimer;
LLUnit<F64, LLUnits::Seconds> mElapsedSeconds;
- LLCopyOnWritePointer<RecordingBuffers> mBuffers;
+ LLCopyOnWritePointer<AccumulatorBufferGroup> mBuffers;
};
class LL_COMMON_API PeriodicRecording
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
index 54006f4e5b..7192564c94 100644
--- a/indra/llcommon/lltracethreadrecorder.cpp
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -31,6 +31,7 @@
namespace LLTrace
{
+MasterThreadRecorder* gUIThreadRecorder = NULL;
///////////////////////////////////////////////////////////////////////
// ThreadRecorder
@@ -49,7 +50,7 @@ ThreadRecorder::ThreadRecorder()
mNumTimeBlockTreeNodes = AccumulatorBuffer<TimeBlockAccumulator>::getDefaultBuffer()->size();
mTimeBlockTreeNodes = new TimeBlockTreeNode[mNumTimeBlockTreeNodes];
- mThreadRecording.start();
+ activate(&mThreadRecordingBuffers);
// initialize time block parent pointers
for (LLInstanceTracker<TimeBlock>::instance_iter it = LLInstanceTracker<TimeBlock>::beginInstances(), end_it = LLInstanceTracker<TimeBlock>::endInstances();
@@ -72,6 +73,8 @@ ThreadRecorder::ThreadRecorder()
ThreadRecorder::~ThreadRecorder()
{
+ deactivate(&mThreadRecordingBuffers);
+
delete mRootTimer;
if (!mActiveRecordings.empty())
@@ -84,7 +87,7 @@ ThreadRecorder::~ThreadRecorder()
delete[] mTimeBlockTreeNodes;
}
-TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode(S32 index)
+TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode( S32 index )
{
if (0 <= index && index < mNumTimeBlockTreeNodes)
{
@@ -94,23 +97,33 @@ TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode(S32 index)
}
-void ThreadRecorder::activate( Recording* recording )
+void ThreadRecorder::activate( AccumulatorBufferGroup* recording )
{
+ active_recording_list_t::reverse_iterator it, end_it;
+ for (it = mActiveRecordings.rbegin(), end_it = mActiveRecordings.rend();
+ it != end_it;
+ ++it)
+ {
+ llassert((*it)->mTargetRecording != recording);
+ }
+
ActiveRecording* active_recording = new ActiveRecording(recording);
if (!mActiveRecordings.empty())
{
- mActiveRecordings.back()->mPartialRecording.handOffTo(active_recording->mPartialRecording);
+ AccumulatorBufferGroup& prev_active_recording = mActiveRecordings.back()->mPartialRecording;
+ prev_active_recording.sync();
+ prev_active_recording.handOffTo(active_recording->mPartialRecording);
}
mActiveRecordings.push_back(active_recording);
mActiveRecordings.back()->mPartialRecording.makePrimary();
}
-ThreadRecorder::active_recording_list_t::reverse_iterator ThreadRecorder::bringUpToDate( Recording* recording )
+ThreadRecorder::active_recording_list_t::reverse_iterator ThreadRecorder::bringUpToDate( AccumulatorBufferGroup* recording )
{
if (mActiveRecordings.empty()) return mActiveRecordings.rend();
- mActiveRecordings.back()->mPartialRecording.flush();
+ mActiveRecordings.back()->mPartialRecording.sync();
TimeBlock::updateTimes();
active_recording_list_t::reverse_iterator it, end_it;
@@ -148,34 +161,38 @@ ThreadRecorder::active_recording_list_t::reverse_iterator ThreadRecorder::bringU
return it;
}
-void ThreadRecorder::deactivate( Recording* recording )
+void ThreadRecorder::deactivate( AccumulatorBufferGroup* recording )
{
active_recording_list_t::reverse_iterator it = bringUpToDate(recording);
if (it != mActiveRecordings.rend())
{
- // and if we've found the recording we wanted to update
- active_recording_list_t::reverse_iterator next_it = it;
- ++next_it;
- if (next_it != mActiveRecordings.rend())
- {
- (*next_it)->mPartialRecording.makePrimary();
- }
-
active_recording_list_t::iterator recording_to_remove = (++it).base();
+ bool was_primary = (*recording_to_remove)->mPartialRecording.isPrimary();
llassert((*recording_to_remove)->mTargetRecording == recording);
delete *recording_to_remove;
mActiveRecordings.erase(recording_to_remove);
+ if (was_primary)
+ {
+ if (mActiveRecordings.empty())
+ {
+ AccumulatorBufferGroup::clearPrimary();
+ }
+ else
+ {
+ mActiveRecordings.back()->mPartialRecording.makePrimary();
+ }
+ }
}
}
-ThreadRecorder::ActiveRecording::ActiveRecording( Recording* target )
+ThreadRecorder::ActiveRecording::ActiveRecording( AccumulatorBufferGroup* target )
: mTargetRecording(target)
{
}
void ThreadRecorder::ActiveRecording::movePartialToTarget()
{
- mTargetRecording->mBuffers.write()->append(mPartialRecording);
+ mTargetRecording->append(mPartialRecording);
// reset based on self to keep history
mPartialRecording.reset(&mPartialRecording);
}
@@ -197,79 +214,49 @@ SlaveThreadRecorder::~SlaveThreadRecorder()
}
void SlaveThreadRecorder::pushToMaster()
-{
- mThreadRecording.stop();
- {
- LLMutexLock(mMasterRecorder.getSlaveListMutex());
- mSharedData.appendFrom(mThreadRecording);
+{
+ { LLMutexLock lock(&mSharedRecordingMutex);
+ LLTrace::get_thread_recorder()->bringUpToDate(&mThreadRecordingBuffers);
+ mSharedRecordingBuffers.append(mThreadRecordingBuffers);
}
- mThreadRecording.start();
-}
-
-void SlaveThreadRecorder::SharedData::appendFrom( const Recording& source )
-{
- LLMutexLock lock(&mRecordingMutex);
- appendRecording(source);
}
-void SlaveThreadRecorder::SharedData::appendTo( Recording& sink )
-{
- LLMutexLock lock(&mRecordingMutex);
- sink.appendRecording(*this);
-}
-
-void SlaveThreadRecorder::SharedData::mergeFrom( const RecordingBuffers& source )
-{
- LLMutexLock lock(&mRecordingMutex);
- mBuffers.write()->merge(source);
-}
-
-void SlaveThreadRecorder::SharedData::mergeTo( RecordingBuffers& sink )
-{
- LLMutexLock lock(&mRecordingMutex);
- sink.merge(*mBuffers);
-}
-
-void SlaveThreadRecorder::SharedData::reset()
-{
- LLMutexLock lock(&mRecordingMutex);
- Recording::reset();
-}
-
-
///////////////////////////////////////////////////////////////////////
// MasterThreadRecorder
///////////////////////////////////////////////////////////////////////
static LLFastTimer::DeclareTimer FTM_PULL_TRACE_DATA_FROM_SLAVES("Pull slave trace data");
+
void MasterThreadRecorder::pullFromSlaveThreads()
{
- LLFastTimer _(FTM_PULL_TRACE_DATA_FROM_SLAVES);
+ /*LLFastTimer _(FTM_PULL_TRACE_DATA_FROM_SLAVES);
if (mActiveRecordings.empty()) return;
- LLMutexLock lock(&mSlaveListMutex);
+ { LLMutexLock lock(&mSlaveListMutex);
- RecordingBuffers& target_recording_buffers = mActiveRecordings.back()->mPartialRecording;
+ AccumulatorBufferGroup& target_recording_buffers = mActiveRecordings.back()->mPartialRecording;
+ target_recording_buffers.sync();
for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
- it != end_it;
- ++it)
- {
- // ignore block timing info for now
- (*it)->mSharedData.mergeTo(target_recording_buffers);
- (*it)->mSharedData.reset();
+ it != end_it;
+ ++it)
+ { LLMutexLock lock(&(*it)->mSharedRecordingMutex);
+
+ target_recording_buffers.merge((*it)->mSharedRecordingBuffers);
+ (*it)->mSharedRecordingBuffers.reset();
}
+ }*/
}
+// called by slave thread
void MasterThreadRecorder::addSlaveThread( class SlaveThreadRecorder* child )
-{
- LLMutexLock lock(&mSlaveListMutex);
+{ LLMutexLock lock(&mSlaveListMutex);
mSlaveThreadRecorders.push_back(child);
}
+// called by slave thread
void MasterThreadRecorder::removeSlaveThread( class SlaveThreadRecorder* child )
-{
- LLMutexLock lock(&mSlaveListMutex);
+{ LLMutexLock lock(&mSlaveListMutex);
for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end();
it != end_it;
@@ -289,4 +276,28 @@ void MasterThreadRecorder::pushToMaster()
MasterThreadRecorder::MasterThreadRecorder()
{}
+
+MasterThreadRecorder& getUIThreadRecorder()
+{
+ llassert(gUIThreadRecorder != NULL);
+ return *gUIThreadRecorder;
+}
+
+LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder_ptr()
+{
+ static LLThreadLocalPointer<ThreadRecorder> s_thread_recorder;
+ return s_thread_recorder;
+}
+
+const LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder()
+{
+ return get_thread_recorder_ptr();
+}
+
+void set_thread_recorder(ThreadRecorder* recorder)
+{
+ get_thread_recorder_ptr() = recorder;
+}
+
+
}
diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h
index bf3701304f..6b7a8e5865 100644
--- a/indra/llcommon/lltracethreadrecorder.h
+++ b/indra/llcommon/lltracethreadrecorder.h
@@ -31,7 +31,8 @@
#include "llpreprocessor.h"
#include "llmutex.h"
-#include "lltracerecording.h"
+#include "lltraceaccumulators.h"
+#include "llthreadlocalstorage.h"
namespace LLTrace
{
@@ -45,9 +46,9 @@ namespace LLTrace
virtual ~ThreadRecorder();
- void activate(Recording* recording);
- void deactivate(Recording* recording);
- active_recording_list_t::reverse_iterator bringUpToDate(Recording* recording);
+ void activate(AccumulatorBufferGroup* recording);
+ void deactivate(AccumulatorBufferGroup* recording);
+ active_recording_list_t::reverse_iterator bringUpToDate(AccumulatorBufferGroup* recording);
virtual void pushToMaster() = 0;
@@ -56,20 +57,21 @@ namespace LLTrace
protected:
struct ActiveRecording
{
- ActiveRecording(Recording* target);
+ ActiveRecording(AccumulatorBufferGroup* target);
- Recording* mTargetRecording;
- RecordingBuffers mPartialRecording;
+ AccumulatorBufferGroup* mTargetRecording;
+ AccumulatorBufferGroup mPartialRecording;
void movePartialToTarget();
};
- Recording mThreadRecording;
+ AccumulatorBufferGroup mThreadRecordingBuffers;
active_recording_list_t mActiveRecordings;
class BlockTimer* mRootTimer;
TimeBlockTreeNode* mTimeBlockTreeNodes;
size_t mNumTimeBlockTreeNodes;
+ BlockTimerStackRecord mBlockTimerStackRecord;
};
class LL_COMMON_API MasterThreadRecorder : public ThreadRecorder
@@ -85,9 +87,6 @@ namespace LLTrace
// call this periodically to gather stats data from slave threads
void pullFromSlaveThreads();
- LLMutex* getSlaveListMutex() { return &mSlaveListMutex; }
-
-
private:
typedef std::list<class SlaveThreadRecorder*> slave_thread_recorder_list_t;
@@ -105,22 +104,20 @@ namespace LLTrace
// call this periodically to gather stats data for master thread to consume
/*virtual*/ void pushToMaster();
- MasterThreadRecorder* mMaster;
-
- class SharedData : public Recording
- {
- public:
- void appendFrom(const Recording& source);
- void appendTo(Recording& sink);
- void mergeFrom(const RecordingBuffers& source);
- void mergeTo(RecordingBuffers& sink);
- void reset();
- private:
- LLMutex mRecordingMutex;
- };
- SharedData mSharedData;
+ private:
+ friend class MasterThreadRecorder;
+ LLMutex mSharedRecordingMutex;
+ AccumulatorBufferGroup mSharedRecordingBuffers;
MasterThreadRecorder& mMasterRecorder;
};
+
+ //FIXME: let user code set up thread recorder topology
+ extern MasterThreadRecorder* gUIThreadRecorder ;
+
+ const LLThreadLocalPointer<class ThreadRecorder>& get_thread_recorder();
+ void set_thread_recorder(class ThreadRecorder*);
+ class MasterThreadRecorder& getUIThreadRecorder();
+
}
#endif // LL_LLTRACETHREADRECORDER_H