From 5de2f0a8970244866dc8b511caa3c8626955264f Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Fri, 21 Jun 2013 10:58:44 -0700 Subject: SH-3931 FIX Interesting: Add graphs to visualize scene load metrics - potential fix for bad times --- indra/llcommon/lltracerecording.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp index 33002929ea..52ecb463be 100644 --- a/indra/llcommon/lltracerecording.cpp +++ b/indra/llcommon/lltracerecording.cpp @@ -161,6 +161,7 @@ void Recording::update() { mBuffers.write()->flush(); LLTrace::get_thread_recorder()->bringUpToDate(this); + mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); mSamplingTimer.reset(); } } -- cgit v1.2.3 From 8bddaeec6647e735415f9bd72a4e1313e11fe720 Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Sat, 22 Jun 2013 12:00:18 -0700 Subject: fixed scene load monitor resetting to eagerly due to spurious camer amotion pulled swap() out of ui time block cleaned up internal lltrace dependencies, factored out common accumulator definitions --- indra/llcommon/CMakeLists.txt | 2 + indra/llcommon/llthread.cpp | 4 +- indra/llcommon/llthreadlocalstorage.cpp | 2 + indra/llcommon/llthreadlocalstorage.h | 31 +- indra/llcommon/lltrace.cpp | 24 -- indra/llcommon/lltrace.h | 594 +--------------------------- indra/llcommon/lltraceaccumulators.cpp | 112 ++++++ indra/llcommon/lltraceaccumulators.h | 648 +++++++++++++++++++++++++++++++ indra/llcommon/lltracerecording.cpp | 113 +----- indra/llcommon/lltracerecording.h | 29 +- indra/llcommon/lltracethreadrecorder.cpp | 108 +++--- indra/llcommon/lltracethreadrecorder.h | 45 +-- 12 files changed, 871 insertions(+), 841 deletions(-) create mode 100644 indra/llcommon/lltraceaccumulators.cpp create mode 100644 indra/llcommon/lltraceaccumulators.h (limited to 'indra/llcommon') 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/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 -__declspec(thread) DERIVED_TYPE* LLThreadLocalSingleton::sInstance = NULL; - -template -__declspec(thread) typename LLThreadLocalSingleton::EInitState LLThreadLocalSingleton::sInitState = LLThreadLocalSingleton::UNINITIALIZED; +__declspec(thread) typename LLThreadLocalSingleton::SingletonData LLThreadLocalSingleton::sData = {NULL, LLThreadLocalSingleton::UNINITIALIZED}; #elif LL_LINUX template -__thread DERIVED_TYPE* LLThreadLocalSingleton::sInstance = NULL; - -template -__thread typename LLThreadLocalSingleton::EInitState LLThreadLocalSingleton::sInitState = LLThreadLocalSingleton::UNINITIALIZED; +__thread typename LLThreadLocalSingleton::SingletonData LLThreadLocalSingleton::sData = {NULL, LLThreadLocalSingleton::UNINITIALIZED}; #elif LL_DARWIN template pthread_key_t LLThreadLocalSingleton::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& get_thread_recorder_ptr() -{ - static LLThreadLocalPointer s_thread_recorder; - return s_thread_recorder; -} - -const LLThreadLocalPointer& 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 fb7ffb0a29..36a3eb8fe8 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" @@ -80,185 +80,6 @@ void init(); void cleanup(); bool isInitialized(); -const LLThreadLocalPointer& get_thread_recorder(); -void set_thread_recorder(class ThreadRecorder*); - -class MasterThreadRecorder& getUIThreadRecorder(); - -template -class AccumulatorBuffer : public LLRefCount -{ - typedef AccumulatorBuffer 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::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& 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& other) - { - llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize > sNextStorageSlot); - for (size_t i = 0; i < sNextStorageSlot; i++) - { - mStorage[i] = other.mStorage[i]; - } - } - - void reset(const AccumulatorBuffer* other = NULL) - { - llassert(mStorageSize >= sNextStorageSlot); - for (size_t i = 0; i < sNextStorageSlot; i++) - { - mStorage[i].reset(other ? &other->mStorage[i] : NULL); - } - } - - void flush(LLUnitImplicit time_stamp) - { - llassert(mStorageSize >= sNextStorageSlot); - for (size_t i = 0; i < sNextStorageSlot; i++) - { - mStorage[i].flush(time_stamp); - } - } - - void makePrimary() - { - LLThreadLocalSingletonPointer::setInstance(mStorage); - } - - bool isPrimary() const - { - return LLThreadLocalSingletonPointer::getInstance() == mStorage; - } - - LL_FORCE_INLINE static ACCUMULATOR* getPrimaryStorage() - { - ACCUMULATOR* accumulator = LLThreadLocalSingletonPointer::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 size_t AccumulatorBuffer::sNextStorageSlot = 0; -template AccumulatorBuffer* AccumulatorBuffer::sDefaultBuffer = NULL; - template class TraceType : public LLInstanceTracker, std::string> @@ -290,344 +111,6 @@ protected: const size_t mAccumulatorIndex; }; -class EventAccumulator -{ -public: - typedef F64 value_t; - typedef F64 mean_t; - - EventAccumulator() - : mSum(0), - mMin((std::numeric_limits::max)()), - mMax((std::numeric_limits::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::max(); - mMax = std::numeric_limits::min(); - mMean = 0; - mVarianceSum = 0; - mLastValue = other ? other->mLastValue : 0; - } - - void flush(LLUnitImplicit) {} - - 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::max)()), - mMax((std::numeric_limits::min)()), - mMean(0), - mVarianceSum(0), - mLastSampleTimeStamp(LLTimer::getTotalSeconds()), - mTotalSamplingTime(0), - mNumSamples(0), - mLastValue(0), - mHasValue(false) - {} - - void sample(F64 value) - { - LLUnitImplicit time_stamp = LLTimer::getTotalSeconds(); - LLUnitImplicit 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::max(); - mMax = std::numeric_limits::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 time_stamp) - { - LLUnitImplicit 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 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 getSum() const { return mSum; } - - U32 getSampleCount() const { return mNumSamples; } - -private: - F64 mSum; - - U32 mNumSamples; -}; - -class TimeBlockAccumulator -{ -public: - typedef LLUnit value_t; - typedef LLUnit 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 value_t; - typedef LLUnit mean_t; - }; - - TimeBlockAccumulator(); - void addSamples(const self_t& other, bool /*append*/); - void reset(const self_t* other); - void flush(LLUnitImplicit) {} - - // - // 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 @@ -651,23 +134,6 @@ public: {} }; -class TimeBlock; -class TimeBlockTreeNode -{ -public: - TimeBlockTreeNode(); - - void setParent(TimeBlock* parent); - TimeBlock* getParent() { return mParent; } - - TimeBlock* mBlock; - TimeBlock* mParent; - std::vector mChildren; - bool mCollapsed; - bool mNeedsSorting; -}; - - template class EventStatHandle : public TraceType @@ -735,64 +201,6 @@ void add(CountStatHandle& 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 value_t; - typedef LLUnit 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 time_stamp) - { - mSize.flush(time_stamp); - mChildSize.flush(time_stamp); - } - - SampleAccumulator mSize, - mChildSize; - int mAllocatedCount, - mDeallocatedCount; -}; - - template<> class TraceType : public TraceType diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp new file mode 100644 index 0000000000..5948696418 --- /dev/null +++ b/indra/llcommon/lltraceaccumulators.cpp @@ -0,0 +1,112 @@ +/** + * @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& 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 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::flush() +{ + LLUnitImplicit time_stamp = LLTimer::getTotalSeconds(); + + mSamples.flush(time_stamp); +} + +} diff --git a/indra/llcommon/lltraceaccumulators.h b/indra/llcommon/lltraceaccumulators.h new file mode 100644 index 0000000000..7994dcc217 --- /dev/null +++ b/indra/llcommon/lltraceaccumulators.h @@ -0,0 +1,648 @@ +/** + * @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 + class AccumulatorBuffer : public LLRefCount + { + typedef AccumulatorBuffer 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::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& 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& other) + { + llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize > sNextStorageSlot); + for (size_t i = 0; i < sNextStorageSlot; i++) + { + mStorage[i] = other.mStorage[i]; + } + } + + void reset(const AccumulatorBuffer* other = NULL) + { + llassert(mStorageSize >= sNextStorageSlot); + for (size_t i = 0; i < sNextStorageSlot; i++) + { + mStorage[i].reset(other ? &other->mStorage[i] : NULL); + } + } + + void flush(LLUnitImplicit time_stamp) + { + llassert(mStorageSize >= sNextStorageSlot); + for (size_t i = 0; i < sNextStorageSlot; i++) + { + mStorage[i].flush(time_stamp); + } + } + + void makePrimary() + { + LLThreadLocalSingletonPointer::setInstance(mStorage); + } + + bool isPrimary() const + { + return LLThreadLocalSingletonPointer::getInstance() == mStorage; + } + + LL_FORCE_INLINE static ACCUMULATOR* getPrimaryStorage() + { + ACCUMULATOR* accumulator = LLThreadLocalSingletonPointer::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 size_t AccumulatorBuffer::sNextStorageSlot = 0; + template AccumulatorBuffer* AccumulatorBuffer::sDefaultBuffer = NULL; + + + class EventAccumulator + { + public: + typedef F64 value_t; + typedef F64 mean_t; + + EventAccumulator() + : mSum(0), + mMin((std::numeric_limits::max)()), + mMax((std::numeric_limits::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::max(); + mMax = std::numeric_limits::min(); + mMean = 0; + mVarianceSum = 0; + mLastValue = other ? other->mLastValue : 0; + } + + void flush(LLUnitImplicit) {} + + 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::max)()), + mMax((std::numeric_limits::min)()), + mMean(0), + mVarianceSum(0), + mLastSampleTimeStamp(LLTimer::getTotalSeconds()), + mTotalSamplingTime(0), + mNumSamples(0), + mLastValue(0), + mHasValue(false) + {} + + void sample(F64 value) + { + LLUnitImplicit time_stamp = LLTimer::getTotalSeconds(); + LLUnitImplicit 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::max(); + mMax = std::numeric_limits::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 time_stamp) + { + LLUnitImplicit 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 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 getSum() const { return mSum; } + + U32 getSampleCount() const { return mNumSamples; } + + private: + F64 mSum; + + U32 mNumSamples; + }; + + class TimeBlockAccumulator + { + public: + typedef LLUnit value_t; + typedef LLUnit 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 value_t; + typedef LLUnit mean_t; + }; + + TimeBlockAccumulator(); + void addSamples(const self_t& other, bool /*append*/); + void reset(const self_t* other); + void flush(LLUnitImplicit) {} + + // + // 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 mChildren; + bool mCollapsed; + bool mNeedsSorting; + }; + + 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 value_t; + typedef LLUnit 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 time_stamp) + { + mSize.flush(time_stamp); + mChildSize.flush(time_stamp); + } + + SampleAccumulator mSize, + mChildSize; + int mAllocatedCount, + mDeallocatedCount; + }; + + struct AccumulatorBufferGroup : public LLRefCount + { + AccumulatorBufferGroup(); + + void handOffTo(AccumulatorBufferGroup& other); + void makePrimary(); + bool isPrimary() const; + + void append(const AccumulatorBufferGroup& other); + void merge(const AccumulatorBufferGroup& other); + void reset(AccumulatorBufferGroup* other = NULL); + void flush(); + + AccumulatorBuffer mCounts; + AccumulatorBuffer mSamples; + AccumulatorBuffer mEvents; + AccumulatorBuffer mStackTimers; + AccumulatorBuffer mMemStats; + }; +} + +#endif // LL_LLTRACEACCUMULATORS_H + diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp index ff1589d12d..c30f204fa4 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& 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 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,11 +54,10 @@ 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(other); - mutable_other.update(); + const_cast(other).update(); EPlayState other_play_state = other.getPlayState(); - mBuffers = mutable_other.mBuffers; + mBuffers = other.mBuffers; LLStopWatchControlsMixin::setPlayState(other_play_state); @@ -151,7 +72,7 @@ Recording::~Recording() { if (isStarted() && LLTrace::get_thread_recorder().notNull()) { - LLTrace::get_thread_recorder()->deactivate(this); + LLTrace::get_thread_recorder()->deactivate(mBuffers.write()); } } @@ -159,9 +80,11 @@ void Recording::update() { if (isStarted()) { - mBuffers.write()->flush(); - LLTrace::get_thread_recorder()->bringUpToDate(this); mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); + AccumulatorBufferGroup* buffers = mBuffers.write(); + buffers->flush(); + LLTrace::get_thread_recorder()->bringUpToDate(buffers); + mSamplingTimer.reset(); } } @@ -177,14 +100,15 @@ 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(); + buffers->flush(); + LLTrace::get_thread_recorder()->deactivate(buffers); } void Recording::handleSplitTo(Recording& other) @@ -192,19 +116,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 Recording::getSum(const TraceType& stat) { const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; @@ -711,8 +630,6 @@ F64 PeriodicRecording::getPeriodMean( const TraceType& 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..38eaa47f9f 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 { @@ -106,26 +106,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 mCounts; - AccumulatorBuffer mSamples; - AccumulatorBuffer mEvents; - AccumulatorBuffer mStackTimers; - AccumulatorBuffer mMemStats; - }; - class Recording : public LLStopWatchControlsMixin { @@ -138,10 +118,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 +268,7 @@ namespace LLTrace LLTimer mSamplingTimer; LLUnit mElapsedSeconds; - LLCopyOnWritePointer mBuffers; + LLCopyOnWritePointer mBuffers; }; class LL_COMMON_API PeriodicRecording diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp index 54006f4e5b..c571e013e1 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::getDefaultBuffer()->size(); mTimeBlockTreeNodes = new TimeBlockTreeNode[mNumTimeBlockTreeNodes]; - mThreadRecording.start(); + activate(&mThreadRecordingBuffers); // initialize time block parent pointers for (LLInstanceTracker::instance_iter it = LLInstanceTracker::beginInstances(), end_it = LLInstanceTracker::endInstances(); @@ -72,6 +73,8 @@ ThreadRecorder::ThreadRecorder() ThreadRecorder::~ThreadRecorder() { + deactivate(&mThreadRecordingBuffers); + delete mRootTimer; if (!mActiveRecordings.empty()) @@ -94,7 +97,7 @@ TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode(S32 index) } -void ThreadRecorder::activate( Recording* recording ) +void ThreadRecorder::activate( AccumulatorBufferGroup* recording ) { ActiveRecording* active_recording = new ActiveRecording(recording); if (!mActiveRecordings.empty()) @@ -106,7 +109,7 @@ void ThreadRecorder::activate( Recording* 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(); @@ -148,7 +151,7 @@ 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()) @@ -168,14 +171,14 @@ void ThreadRecorder::deactivate( Recording* recording ) } } -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,46 +200,14 @@ SlaveThreadRecorder::~SlaveThreadRecorder() } void SlaveThreadRecorder::pushToMaster() -{ - mThreadRecording.stop(); - { - LLMutexLock(mMasterRecorder.getSlaveListMutex()); - mSharedData.appendFrom(mThreadRecording); +{ + { LLMutexLock lock(&mSharedRecordingMutex); + mThreadRecordingBuffers.flush(); + 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 /////////////////////////////////////////////////////////////////////// @@ -247,29 +218,30 @@ void MasterThreadRecorder::pullFromSlaveThreads() LLFastTimer _(FTM_PULL_TRACE_DATA_FROM_SLAVES); if (mActiveRecordings.empty()) return; - LLMutexLock lock(&mSlaveListMutex); + { LLMutexLock lock(&mSlaveListMutex); - RecordingBuffers& target_recording_buffers = mActiveRecordings.back()->mPartialRecording; - 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(); + AccumulatorBufferGroup& target_recording_buffers = mActiveRecordings.back()->mPartialRecording; + for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end(); + 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 +261,28 @@ void MasterThreadRecorder::pushToMaster() MasterThreadRecorder::MasterThreadRecorder() {} + +MasterThreadRecorder& getUIThreadRecorder() +{ + llassert(gUIThreadRecorder != NULL); + return *gUIThreadRecorder; +} + +LLThreadLocalPointer& get_thread_recorder_ptr() +{ + static LLThreadLocalPointer s_thread_recorder; + return s_thread_recorder; +} + +const LLThreadLocalPointer& 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..0680c2c590 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,14 +57,14 @@ 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; @@ -85,9 +86,6 @@ namespace LLTrace // call this periodically to gather stats data from slave threads void pullFromSlaveThreads(); - LLMutex* getSlaveListMutex() { return &mSlaveListMutex; } - - private: typedef std::list slave_thread_recorder_list_t; @@ -105,22 +103,21 @@ namespace LLTrace // call this periodically to gather stats data for master thread to consume /*virtual*/ void pushToMaster(); + private: + friend class MasterThreadRecorder; 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; + LLMutex mSharedRecordingMutex; + AccumulatorBufferGroup mSharedRecordingBuffers; MasterThreadRecorder& mMasterRecorder; }; + + //FIXME: let user code set up thread recorder topology + extern MasterThreadRecorder* gUIThreadRecorder ; + + const LLThreadLocalPointer& get_thread_recorder(); + void set_thread_recorder(class ThreadRecorder*); + class MasterThreadRecorder& getUIThreadRecorder(); + } #endif // LL_LLTRACETHREADRECORDER_H -- cgit v1.2.3 From 808d3eff198d65e5a870abb670786935fc8356bd Mon Sep 17 00:00:00 2001 From: Richard Linden Date: Thu, 27 Jun 2013 00:07:21 -0700 Subject: SH-4299 WIP: Interesting: High fps shown temporarily off scale in statistics console fixed some lltrace logic errors more consistent syncing of timestamps of sample values in recording stack selection of primary buffers was completely incorrect assignment of recordings got wrong play state due to implicit operator = defined in base class fixed asset stats only working up to the first send --- indra/llcommon/llfasttimer.h | 7 ---- indra/llcommon/lltraceaccumulators.cpp | 15 +++++++-- indra/llcommon/lltraceaccumulators.h | 37 ++++++++++++++------- indra/llcommon/lltracerecording.cpp | 7 ++-- indra/llcommon/lltracerecording.h | 6 ++++ indra/llcommon/lltracethreadrecorder.cpp | 57 ++++++++++++++++++++------------ indra/llcommon/lltracethreadrecorder.h | 2 +- 7 files changed, 84 insertions(+), 47 deletions(-) (limited to 'indra/llcommon') 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 diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp index 5948696418..950c1d97d1 100644 --- a/indra/llcommon/lltraceaccumulators.cpp +++ b/indra/llcommon/lltraceaccumulators.cpp @@ -69,6 +69,16 @@ void AccumulatorBufferGroup::makePrimary() } } +//static +void AccumulatorBufferGroup::clearPrimary() +{ + AccumulatorBuffer::clearPrimary(); + AccumulatorBuffer::clearPrimary(); + AccumulatorBuffer::clearPrimary(); + AccumulatorBuffer::clearPrimary(); + AccumulatorBuffer::clearPrimary(); +} + bool AccumulatorBufferGroup::isPrimary() const { return mCounts.isPrimary(); @@ -102,11 +112,12 @@ void AccumulatorBufferGroup::reset(AccumulatorBufferGroup* other) mMemStats.reset(other ? &other->mMemStats : NULL); } -void AccumulatorBufferGroup::flush() +void AccumulatorBufferGroup::sync() { LLUnitImplicit time_stamp = LLTimer::getTotalSeconds(); - mSamples.flush(time_stamp); + mSamples.sync(time_stamp); + mMemStats.sync(time_stamp); } } diff --git a/indra/llcommon/lltraceaccumulators.h b/indra/llcommon/lltraceaccumulators.h index 7994dcc217..825cc9e3a8 100644 --- a/indra/llcommon/lltraceaccumulators.h +++ b/indra/llcommon/lltraceaccumulators.h @@ -109,12 +109,12 @@ namespace LLTrace } } - void flush(LLUnitImplicit time_stamp) + void sync(LLUnitImplicit time_stamp) { llassert(mStorageSize >= sNextStorageSlot); for (size_t i = 0; i < sNextStorageSlot; i++) { - mStorage[i].flush(time_stamp); + mStorage[i].sync(time_stamp); } } @@ -128,6 +128,11 @@ namespace LLTrace return LLThreadLocalSingletonPointer::getInstance() == mStorage; } + static void clearPrimary() + { + LLThreadLocalSingletonPointer::setInstance(NULL); + } + LL_FORCE_INLINE static ACCUMULATOR* getPrimaryStorage() { ACCUMULATOR* accumulator = LLThreadLocalSingletonPointer::getInstance(); @@ -302,7 +307,7 @@ namespace LLTrace mLastValue = other ? other->mLastValue : 0; } - void flush(LLUnitImplicit) {} + void sync(LLUnitImplicit) {} F64 getSum() const { return mSum; } F64 getMin() const { return mMin; } @@ -434,7 +439,7 @@ namespace LLTrace mHasValue = other ? other->mHasValue : false; } - void flush(LLUnitImplicit time_stamp) + void sync(LLUnitImplicit time_stamp) { LLUnitImplicit delta_time = time_stamp - mLastSampleTimeStamp; @@ -500,7 +505,7 @@ namespace LLTrace mSum = 0; } - void flush(LLUnitImplicit) {} + void sync(LLUnitImplicit) {} F64 getSum() const { return mSum; } @@ -535,7 +540,7 @@ namespace LLTrace TimeBlockAccumulator(); void addSamples(const self_t& other, bool /*append*/); void reset(const self_t* other); - void flush(LLUnitImplicit) {} + void sync(LLUnitImplicit) {} // // members @@ -566,6 +571,13 @@ namespace LLTrace bool mCollapsed; bool mNeedsSorting; }; + + struct BlockTimerStackRecord + { + class BlockTimer* mActiveTimer; + class TimeBlock* mTimeBlock; + U64 mChildTime; + }; struct MemStatAccumulator { @@ -611,16 +623,16 @@ namespace LLTrace mDeallocatedCount = 0; } - void flush(LLUnitImplicit time_stamp) + void sync(LLUnitImplicit time_stamp) { - mSize.flush(time_stamp); - mChildSize.flush(time_stamp); + mSize.sync(time_stamp); + mChildSize.sync(time_stamp); } SampleAccumulator mSize, - mChildSize; + mChildSize; int mAllocatedCount, - mDeallocatedCount; + mDeallocatedCount; }; struct AccumulatorBufferGroup : public LLRefCount @@ -630,11 +642,12 @@ namespace LLTrace 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 flush(); + void sync(); AccumulatorBuffer mCounts; AccumulatorBuffer mSamples; diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp index c30f204fa4..0938317eaa 100644 --- a/indra/llcommon/lltracerecording.cpp +++ b/indra/llcommon/lltracerecording.cpp @@ -59,11 +59,12 @@ Recording& Recording::operator = (const Recording& other) mBuffers = other.mBuffers; - LLStopWatchControlsMixin::setPlayState(other_play_state); - // 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; } @@ -82,7 +83,6 @@ void Recording::update() { mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); AccumulatorBufferGroup* buffers = mBuffers.write(); - buffers->flush(); LLTrace::get_thread_recorder()->bringUpToDate(buffers); mSamplingTimer.reset(); @@ -107,7 +107,6 @@ void Recording::handleStop() { mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); AccumulatorBufferGroup* buffers = mBuffers.write(); - buffers->flush(); LLTrace::get_thread_recorder()->deactivate(buffers); } diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h index 38eaa47f9f..355dbabb1c 100644 --- a/indra/llcommon/lltracerecording.h +++ b/indra/llcommon/lltracerecording.h @@ -81,6 +81,7 @@ class LLStopWatchControlsMixin : public LLStopWatchControlsMixinCommon { public: + typedef LLStopWatchControlsMixin self_t; virtual void splitTo(DERIVED& other) { @@ -98,6 +99,11 @@ public: static_cast(other).handleSplitTo(*static_cast(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) {}; diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp index c571e013e1..7192564c94 100644 --- a/indra/llcommon/lltracethreadrecorder.cpp +++ b/indra/llcommon/lltracethreadrecorder.cpp @@ -87,7 +87,7 @@ ThreadRecorder::~ThreadRecorder() delete[] mTimeBlockTreeNodes; } -TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode(S32 index) +TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode( S32 index ) { if (0 <= index && index < mNumTimeBlockTreeNodes) { @@ -99,10 +99,20 @@ TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode(S32 index) 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); @@ -113,7 +123,7 @@ ThreadRecorder::active_recording_list_t::reverse_iterator ThreadRecorder::bringU { 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; @@ -156,18 +166,22 @@ 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(); + } + } } } @@ -202,7 +216,6 @@ SlaveThreadRecorder::~SlaveThreadRecorder() void SlaveThreadRecorder::pushToMaster() { { LLMutexLock lock(&mSharedRecordingMutex); - mThreadRecordingBuffers.flush(); LLTrace::get_thread_recorder()->bringUpToDate(&mThreadRecordingBuffers); mSharedRecordingBuffers.append(mThreadRecordingBuffers); } @@ -213,23 +226,25 @@ void SlaveThreadRecorder::pushToMaster() /////////////////////////////////////////////////////////////////////// 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); - AccumulatorBufferGroup& target_recording_buffers = mActiveRecordings.back()->mPartialRecording; - for (slave_thread_recorder_list_t::iterator it = mSlaveThreadRecorders.begin(), end_it = mSlaveThreadRecorders.end(); - it != end_it; - ++it) - { LLMutexLock lock(&(*it)->mSharedRecordingMutex); + 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) + { LLMutexLock lock(&(*it)->mSharedRecordingMutex); - target_recording_buffers.merge((*it)->mSharedRecordingBuffers); - (*it)->mSharedRecordingBuffers.reset(); - } + target_recording_buffers.merge((*it)->mSharedRecordingBuffers); + (*it)->mSharedRecordingBuffers.reset(); } + }*/ } // called by slave thread diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h index 0680c2c590..6b7a8e5865 100644 --- a/indra/llcommon/lltracethreadrecorder.h +++ b/indra/llcommon/lltracethreadrecorder.h @@ -71,6 +71,7 @@ namespace LLTrace class BlockTimer* mRootTimer; TimeBlockTreeNode* mTimeBlockTreeNodes; size_t mNumTimeBlockTreeNodes; + BlockTimerStackRecord mBlockTimerStackRecord; }; class LL_COMMON_API MasterThreadRecorder : public ThreadRecorder @@ -105,7 +106,6 @@ namespace LLTrace private: friend class MasterThreadRecorder; - MasterThreadRecorder* mMaster; LLMutex mSharedRecordingMutex; AccumulatorBufferGroup mSharedRecordingBuffers; MasterThreadRecorder& mMasterRecorder; -- cgit v1.2.3