945 lines
23 KiB
C++
945 lines
23 KiB
C++
/**
|
|
* @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 "lltrace.h"
|
|
#include "llfasttimer.h"
|
|
#include "lltracerecording.h"
|
|
#include "lltracethreadrecorder.h"
|
|
#include "llthread.h"
|
|
|
|
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()
|
|
{
|
|
mSamples.flush();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// Recording
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
Recording::Recording()
|
|
: mElapsedSeconds(0)
|
|
{
|
|
mBuffers = new RecordingBuffers();
|
|
}
|
|
|
|
Recording::Recording( 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);
|
|
EPlayState other_play_state = other.getPlayState();
|
|
mutable_other.pause();
|
|
|
|
mBuffers = other.mBuffers;
|
|
|
|
LLStopWatchControlsMixin<Recording>::setPlayState(other_play_state);
|
|
mutable_other.setPlayState(other_play_state);
|
|
|
|
// above call will clear mElapsedSeconds as a side effect, so copy it here
|
|
mElapsedSeconds = other.mElapsedSeconds;
|
|
mSamplingTimer = other.mSamplingTimer;
|
|
}
|
|
|
|
|
|
Recording::~Recording()
|
|
{
|
|
if (isStarted() && LLTrace::get_thread_recorder().notNull())
|
|
{
|
|
LLTrace::get_thread_recorder()->deactivate(this);
|
|
}
|
|
}
|
|
|
|
void Recording::update()
|
|
{
|
|
if (isStarted())
|
|
{
|
|
mBuffers.write()->flush();
|
|
LLTrace::get_thread_recorder()->bringUpToDate(this);
|
|
mSamplingTimer.reset();
|
|
}
|
|
}
|
|
|
|
void Recording::handleReset()
|
|
{
|
|
mBuffers.write()->reset();
|
|
|
|
mElapsedSeconds = 0.0;
|
|
mSamplingTimer.reset();
|
|
}
|
|
|
|
void Recording::handleStart()
|
|
{
|
|
mSamplingTimer.reset();
|
|
LLTrace::get_thread_recorder()->activate(this);
|
|
}
|
|
|
|
void Recording::handleStop()
|
|
{
|
|
mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
|
|
mBuffers.write()->flush();
|
|
LLTrace::get_thread_recorder()->deactivate(this);
|
|
}
|
|
|
|
void Recording::handleSplitTo(Recording& other)
|
|
{
|
|
mBuffers.write()->handOffTo(*other.mBuffers.write());
|
|
}
|
|
|
|
void Recording::appendRecording( const Recording& other )
|
|
{
|
|
EPlayState play_state = getPlayState();
|
|
{
|
|
pause();
|
|
mBuffers.write()->append(*other.mBuffers);
|
|
mElapsedSeconds += other.mElapsedSeconds;
|
|
}
|
|
setPlayState(play_state);
|
|
}
|
|
|
|
void Recording::mergeRecording( const Recording& other)
|
|
{
|
|
EPlayState play_state = getPlayState();
|
|
{
|
|
pause();
|
|
mBuffers.write()->merge(*other.mBuffers);
|
|
}
|
|
setPlayState(play_state);
|
|
}
|
|
|
|
LLUnit<LLUnits::Seconds, F64> Recording::getSum(const TraceType<TimeBlockAccumulator>& stat)
|
|
{
|
|
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
|
|
update();
|
|
return (F64)(accumulator.mTotalTimeCounter - accumulator.mStartTotalTimeCounter)
|
|
/ (F64)LLTrace::TimeBlock::countsPerSecond();
|
|
}
|
|
|
|
LLUnit<LLUnits::Seconds, F64> Recording::getSum(const TraceType<TimeBlockAccumulator::SelfTimeFacet>& stat)
|
|
{
|
|
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
|
|
update();
|
|
return (F64)(accumulator.mSelfTimeCounter) / (F64)LLTrace::TimeBlock::countsPerSecond();
|
|
}
|
|
|
|
|
|
U32 Recording::getSum(const TraceType<TimeBlockAccumulator::CallCountFacet>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mStackTimers[stat.getIndex()].mCalls;
|
|
}
|
|
|
|
LLUnit<LLUnits::Seconds, F64> Recording::getPerSec(const TraceType<TimeBlockAccumulator>& stat)
|
|
{
|
|
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
|
|
|
|
update();
|
|
return (F64)(accumulator.mTotalTimeCounter - accumulator.mStartTotalTimeCounter)
|
|
/ ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds);
|
|
}
|
|
|
|
LLUnit<LLUnits::Seconds, F64> Recording::getPerSec(const TraceType<TimeBlockAccumulator::SelfTimeFacet>& stat)
|
|
{
|
|
const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
|
|
|
|
update();
|
|
return (F64)(accumulator.mSelfTimeCounter)
|
|
/ ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds);
|
|
}
|
|
|
|
F32 Recording::getPerSec(const TraceType<TimeBlockAccumulator::CallCountFacet>& stat)
|
|
{
|
|
update();
|
|
return (F32)mBuffers->mStackTimers[stat.getIndex()].mCalls / mElapsedSeconds;
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getMin(const TraceType<MemStatAccumulator>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mSize.getMin();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getMean(const TraceType<MemStatAccumulator>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mSize.getMean();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getMax(const TraceType<MemStatAccumulator>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mSize.getMax();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getStandardDeviation(const TraceType<MemStatAccumulator>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mSize.getStandardDeviation();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getLastValue(const TraceType<MemStatAccumulator>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mSize.getLastValue();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getMin(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMin();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getMean(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMean();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getMax(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getMax();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getStandardDeviation(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getStandardDeviation();
|
|
}
|
|
|
|
LLUnit<LLUnits::Bytes, F64> Recording::getLastValue(const TraceType<MemStatAccumulator::ChildMemFacet>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mChildSize.getLastValue();
|
|
}
|
|
|
|
U32 Recording::getSum(const TraceType<MemStatAccumulator::AllocationCountFacet>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mAllocatedCount;
|
|
}
|
|
|
|
U32 Recording::getSum(const TraceType<MemStatAccumulator::DeallocationCountFacet>& stat)
|
|
{
|
|
update();
|
|
return mBuffers->mMemStats[stat.getIndex()].mAllocatedCount;
|
|
}
|
|
|
|
|
|
F64 Recording::getSum( const TraceType<CountAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mCounts[stat.getIndex()].getSum();
|
|
}
|
|
|
|
F64 Recording::getSum( const TraceType<EventAccumulator>& stat )
|
|
{
|
|
update();
|
|
return (F64)mBuffers->mEvents[stat.getIndex()].getSum();
|
|
}
|
|
|
|
F64 Recording::getPerSec( const TraceType<CountAccumulator>& stat )
|
|
{
|
|
update();
|
|
F64 sum = mBuffers->mCounts[stat.getIndex()].getSum();
|
|
return (sum != 0.0)
|
|
? (sum / mElapsedSeconds)
|
|
: 0.0;
|
|
}
|
|
|
|
U32 Recording::getSampleCount( const TraceType<CountAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mCounts[stat.getIndex()].getSampleCount();
|
|
}
|
|
|
|
F64 Recording::getMin( const TraceType<SampleAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mSamples[stat.getIndex()].getMin();
|
|
}
|
|
|
|
F64 Recording::getMax( const TraceType<SampleAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mSamples[stat.getIndex()].getMax();
|
|
}
|
|
|
|
F64 Recording::getMean( const TraceType<SampleAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mSamples[stat.getIndex()].getMean();
|
|
}
|
|
|
|
F64 Recording::getStandardDeviation( const TraceType<SampleAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mSamples[stat.getIndex()].getStandardDeviation();
|
|
}
|
|
|
|
F64 Recording::getLastValue( const TraceType<SampleAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mSamples[stat.getIndex()].getLastValue();
|
|
}
|
|
|
|
U32 Recording::getSampleCount( const TraceType<SampleAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mSamples[stat.getIndex()].getSampleCount();
|
|
}
|
|
|
|
F64 Recording::getMin( const TraceType<EventAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mEvents[stat.getIndex()].getMin();
|
|
}
|
|
|
|
F64 Recording::getMax( const TraceType<EventAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mEvents[stat.getIndex()].getMax();
|
|
}
|
|
|
|
F64 Recording::getMean( const TraceType<EventAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mEvents[stat.getIndex()].getMean();
|
|
}
|
|
|
|
F64 Recording::getStandardDeviation( const TraceType<EventAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mEvents[stat.getIndex()].getStandardDeviation();
|
|
}
|
|
|
|
F64 Recording::getLastValue( const TraceType<EventAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mEvents[stat.getIndex()].getLastValue();
|
|
}
|
|
|
|
U32 Recording::getSampleCount( const TraceType<EventAccumulator>& stat )
|
|
{
|
|
update();
|
|
return mBuffers->mEvents[stat.getIndex()].getSampleCount();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// PeriodicRecording
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
PeriodicRecording::PeriodicRecording( U32 num_periods, EPlayState state)
|
|
: mAutoResize(num_periods == 0),
|
|
mCurPeriod(0),
|
|
mRecordingPeriods(num_periods ? num_periods : 1)
|
|
{
|
|
setPlayState(state);
|
|
}
|
|
|
|
void PeriodicRecording::nextPeriod()
|
|
{
|
|
if (mAutoResize)
|
|
{
|
|
mRecordingPeriods.push_back(Recording());
|
|
}
|
|
|
|
Recording& old_recording = getCurRecording();
|
|
|
|
mCurPeriod = (mCurPeriod + 1) % mRecordingPeriods.size();
|
|
old_recording.splitTo(getCurRecording());
|
|
}
|
|
|
|
|
|
void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other )
|
|
{
|
|
if (other.mRecordingPeriods.empty()) return;
|
|
|
|
EPlayState play_state = getPlayState();
|
|
pause();
|
|
|
|
EPlayState other_play_state = other.getPlayState();
|
|
other.pause();
|
|
|
|
U32 other_recording_count = other.mRecordingPeriods.size();
|
|
|
|
Recording& other_oldest_recording = other.mRecordingPeriods[(other.mCurPeriod + 1) % other.mRecordingPeriods.size()];
|
|
|
|
// if I have a recording of any length, then close it off and start a fresh one
|
|
if (getCurRecording().getDuration().value())
|
|
{
|
|
nextPeriod();
|
|
}
|
|
getCurRecording().appendRecording(other_oldest_recording);
|
|
|
|
if (other_recording_count > 1)
|
|
{
|
|
if (mAutoResize)
|
|
{
|
|
for (S32 other_index = (other.mCurPeriod + 2) % other_recording_count,
|
|
end_index = (other.mCurPeriod + 1) % other_recording_count;
|
|
other_index != end_index;
|
|
other_index = (other_index + 1) % other_recording_count)
|
|
{
|
|
llassert(other.mRecordingPeriods[other_index].getDuration() != 0.f
|
|
&& (mRecordingPeriods.empty()
|
|
|| other.mRecordingPeriods[other_index].getDuration() != mRecordingPeriods.back().getDuration()));
|
|
mRecordingPeriods.push_back(other.mRecordingPeriods[other_index]);
|
|
}
|
|
|
|
mCurPeriod = mRecordingPeriods.size() - 1;
|
|
}
|
|
else
|
|
{
|
|
size_t num_to_copy = llmin( mRecordingPeriods.size(), other.mRecordingPeriods.size() - 1);
|
|
std::vector<Recording>::iterator src_it = other.mRecordingPeriods.begin()
|
|
+ ( (other.mCurPeriod + 1 // oldest period
|
|
+ (other.mRecordingPeriods.size() - num_to_copy)) // minus room for copy
|
|
% other.mRecordingPeriods.size());
|
|
std::vector<Recording>::iterator dest_it = mRecordingPeriods.begin() + ((mCurPeriod + 1) % mRecordingPeriods.size());
|
|
|
|
for(S32 i = 0; i < num_to_copy; i++)
|
|
{
|
|
*dest_it = *src_it;
|
|
|
|
if (++src_it == other.mRecordingPeriods.end())
|
|
{
|
|
src_it = other.mRecordingPeriods.begin();
|
|
}
|
|
|
|
if (++dest_it == mRecordingPeriods.end())
|
|
{
|
|
dest_it = mRecordingPeriods.begin();
|
|
}
|
|
}
|
|
|
|
mCurPeriod = (mCurPeriod + num_to_copy) % mRecordingPeriods.size();
|
|
}
|
|
}
|
|
|
|
nextPeriod();
|
|
|
|
setPlayState(play_state);
|
|
other.setPlayState(other_play_state);
|
|
}
|
|
|
|
LLUnit<LLUnits::Seconds, F64> PeriodicRecording::getDuration() const
|
|
{
|
|
LLUnit<LLUnits::Seconds, F64> duration;
|
|
size_t num_periods = mRecordingPeriods.size();
|
|
for (size_t i = 1; i <= num_periods; i++)
|
|
{
|
|
size_t index = (mCurPeriod + num_periods - i) % num_periods;
|
|
duration += mRecordingPeriods[index].getDuration();
|
|
}
|
|
return duration;
|
|
}
|
|
|
|
|
|
LLTrace::Recording PeriodicRecording::snapshotCurRecording() const
|
|
{
|
|
Recording recording_copy(getCurRecording());
|
|
recording_copy.stop();
|
|
return recording_copy;
|
|
}
|
|
|
|
|
|
Recording& PeriodicRecording::getLastRecording()
|
|
{
|
|
return getPrevRecording(1);
|
|
}
|
|
|
|
const Recording& PeriodicRecording::getLastRecording() const
|
|
{
|
|
return getPrevRecording(1);
|
|
}
|
|
|
|
Recording& PeriodicRecording::getCurRecording()
|
|
{
|
|
return mRecordingPeriods[mCurPeriod];
|
|
}
|
|
|
|
const Recording& PeriodicRecording::getCurRecording() const
|
|
{
|
|
return mRecordingPeriods[mCurPeriod];
|
|
}
|
|
|
|
Recording& PeriodicRecording::getPrevRecording( U32 offset )
|
|
{
|
|
U32 num_periods = mRecordingPeriods.size();
|
|
offset = llclamp(offset, 0u, num_periods - 1);
|
|
return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods];
|
|
}
|
|
|
|
const Recording& PeriodicRecording::getPrevRecording( U32 offset ) const
|
|
{
|
|
U32 num_periods = mRecordingPeriods.size();
|
|
offset = llclamp(offset, 0u, num_periods - 1);
|
|
return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods];
|
|
}
|
|
|
|
void PeriodicRecording::handleStart()
|
|
{
|
|
getCurRecording().start();
|
|
}
|
|
|
|
void PeriodicRecording::handleStop()
|
|
{
|
|
getCurRecording().pause();
|
|
}
|
|
|
|
void PeriodicRecording::handleReset()
|
|
{
|
|
if (mAutoResize)
|
|
{
|
|
mRecordingPeriods.clear();
|
|
mRecordingPeriods.push_back(Recording());
|
|
}
|
|
else
|
|
{
|
|
for (std::vector<Recording>::iterator it = mRecordingPeriods.begin(), end_it = mRecordingPeriods.end();
|
|
it != end_it;
|
|
++it)
|
|
{
|
|
it->reset();
|
|
}
|
|
}
|
|
mCurPeriod = 0;
|
|
getCurRecording().setPlayState(getPlayState());
|
|
}
|
|
|
|
void PeriodicRecording::handleSplitTo(PeriodicRecording& other)
|
|
{
|
|
getCurRecording().splitTo(other.getCurRecording());
|
|
}
|
|
|
|
|
|
F64 PeriodicRecording::getPeriodMean( const TraceType<EventAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
|
{
|
|
size_t total_periods = mRecordingPeriods.size();
|
|
num_periods = llmin(num_periods, total_periods);
|
|
|
|
F64 mean = 0;
|
|
if (num_periods <= 0) { return mean; }
|
|
|
|
S32 total_sample_count = 0;
|
|
|
|
for (S32 i = 1; i <= num_periods; i++)
|
|
{
|
|
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
|
if (mRecordingPeriods[index].getDuration() > 0.f)
|
|
{
|
|
S32 period_sample_count = mRecordingPeriods[index].getSampleCount(stat);
|
|
mean += mRecordingPeriods[index].getMean(stat) * period_sample_count;
|
|
total_sample_count += period_sample_count;
|
|
}
|
|
}
|
|
|
|
if (total_sample_count)
|
|
{
|
|
mean = mean / total_sample_count;
|
|
}
|
|
return mean;
|
|
}
|
|
|
|
F64 PeriodicRecording::getPeriodMin( const TraceType<EventAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
|
{
|
|
size_t total_periods = mRecordingPeriods.size();
|
|
num_periods = llmin(num_periods, total_periods);
|
|
|
|
F64 min_val = std::numeric_limits<F64>::max();
|
|
for (S32 i = 1; i <= num_periods; i++)
|
|
{
|
|
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
|
min_val = llmin(min_val, mRecordingPeriods[index].getMin(stat));
|
|
}
|
|
return min_val;
|
|
}
|
|
|
|
F64 PeriodicRecording::getPeriodMax( const TraceType<EventAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
|
{
|
|
size_t total_periods = mRecordingPeriods.size();
|
|
num_periods = llmin(num_periods, total_periods);
|
|
|
|
F64 max_val = std::numeric_limits<F64>::min();
|
|
for (S32 i = 1; i <= num_periods; i++)
|
|
{
|
|
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
|
max_val = llmax(max_val, mRecordingPeriods[index].getMax(stat));
|
|
}
|
|
return max_val;
|
|
}
|
|
|
|
F64 PeriodicRecording::getPeriodMin( const TraceType<SampleAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
|
{
|
|
size_t total_periods = mRecordingPeriods.size();
|
|
num_periods = llmin(num_periods, total_periods);
|
|
|
|
F64 min_val = std::numeric_limits<F64>::max();
|
|
for (S32 i = 1; i <= num_periods; i++)
|
|
{
|
|
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
|
min_val = llmin(min_val, mRecordingPeriods[index].getMin(stat));
|
|
}
|
|
return min_val;
|
|
}
|
|
|
|
F64 PeriodicRecording::getPeriodMax(const TraceType<SampleAccumulator>& stat, size_t num_periods /*= U32_MAX*/)
|
|
{
|
|
size_t total_periods = mRecordingPeriods.size();
|
|
num_periods = llmin(num_periods, total_periods);
|
|
|
|
F64 max_val = std::numeric_limits<F64>::min();
|
|
for (S32 i = 1; i <= num_periods; i++)
|
|
{
|
|
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
|
max_val = llmax(max_val, mRecordingPeriods[index].getMax(stat));
|
|
}
|
|
return max_val;
|
|
}
|
|
|
|
|
|
F64 PeriodicRecording::getPeriodMean( const TraceType<SampleAccumulator>& stat, size_t num_periods /*= U32_MAX*/ )
|
|
{
|
|
size_t total_periods = mRecordingPeriods.size();
|
|
num_periods = llmin(num_periods, total_periods);
|
|
|
|
LLUnit<LLUnits::Seconds, F64> total_duration = 0.f;
|
|
|
|
F64 mean = 0;
|
|
if (num_periods <= 0) { return mean; }
|
|
|
|
for (S32 i = 1; i <= num_periods; i++)
|
|
{
|
|
S32 index = (mCurPeriod + total_periods - i) % total_periods;
|
|
if (mRecordingPeriods[index].getDuration() > 0.f)
|
|
{
|
|
LLUnit<LLUnits::Seconds, F64> recording_duration = mRecordingPeriods[index].getDuration();
|
|
mean += mRecordingPeriods[index].getMean(stat) * recording_duration.value();
|
|
total_duration += recording_duration;
|
|
}
|
|
}
|
|
|
|
if (total_duration.value())
|
|
{
|
|
mean = mean / total_duration;
|
|
}
|
|
return mean;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// ExtendableRecording
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
void ExtendableRecording::extend()
|
|
{
|
|
// stop recording to get latest data
|
|
mPotentialRecording.stop();
|
|
// push the data back to accepted recording
|
|
mAcceptedRecording.appendRecording(mPotentialRecording);
|
|
// flush data, so we can start from scratch
|
|
mPotentialRecording.reset();
|
|
// go back to play state we were in initially
|
|
mPotentialRecording.setPlayState(getPlayState());
|
|
}
|
|
|
|
void ExtendableRecording::handleStart()
|
|
{
|
|
mPotentialRecording.start();
|
|
}
|
|
|
|
void ExtendableRecording::handleStop()
|
|
{
|
|
mPotentialRecording.pause();
|
|
}
|
|
|
|
void ExtendableRecording::handleReset()
|
|
{
|
|
mAcceptedRecording.reset();
|
|
mPotentialRecording.reset();
|
|
}
|
|
|
|
void ExtendableRecording::handleSplitTo(ExtendableRecording& other)
|
|
{
|
|
mPotentialRecording.splitTo(other.mPotentialRecording);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// ExtendablePeriodicRecording
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
ExtendablePeriodicRecording::ExtendablePeriodicRecording()
|
|
: mAcceptedRecording(0),
|
|
mPotentialRecording(0)
|
|
{}
|
|
|
|
void ExtendablePeriodicRecording::extend()
|
|
{
|
|
llassert(mPotentialRecording.getPlayState() == getPlayState());
|
|
// stop recording to get latest data
|
|
mPotentialRecording.pause();
|
|
// push the data back to accepted recording
|
|
mAcceptedRecording.appendPeriodicRecording(mPotentialRecording);
|
|
// flush data, so we can start from scratch
|
|
mPotentialRecording.reset();
|
|
// go back to play state we were in initially
|
|
mPotentialRecording.setPlayState(getPlayState());
|
|
}
|
|
|
|
|
|
void ExtendablePeriodicRecording::handleStart()
|
|
{
|
|
mPotentialRecording.start();
|
|
}
|
|
|
|
void ExtendablePeriodicRecording::handleStop()
|
|
{
|
|
mPotentialRecording.pause();
|
|
}
|
|
|
|
void ExtendablePeriodicRecording::handleReset()
|
|
{
|
|
mAcceptedRecording.reset();
|
|
mPotentialRecording.reset();
|
|
}
|
|
|
|
void ExtendablePeriodicRecording::handleSplitTo(ExtendablePeriodicRecording& other)
|
|
{
|
|
mPotentialRecording.splitTo(other.mPotentialRecording);
|
|
}
|
|
|
|
|
|
PeriodicRecording& get_frame_recording()
|
|
{
|
|
static LLThreadLocalPointer<PeriodicRecording> sRecording(new PeriodicRecording(1000, PeriodicRecording::STARTED));
|
|
return *sRecording;
|
|
}
|
|
|
|
}
|
|
|
|
void LLStopWatchControlsMixinCommon::start()
|
|
{
|
|
switch (mPlayState)
|
|
{
|
|
case STOPPED:
|
|
handleReset();
|
|
handleStart();
|
|
break;
|
|
case PAUSED:
|
|
handleStart();
|
|
break;
|
|
case STARTED:
|
|
handleReset();
|
|
break;
|
|
default:
|
|
llassert(false);
|
|
break;
|
|
}
|
|
mPlayState = STARTED;
|
|
}
|
|
|
|
void LLStopWatchControlsMixinCommon::stop()
|
|
{
|
|
switch (mPlayState)
|
|
{
|
|
case STOPPED:
|
|
break;
|
|
case PAUSED:
|
|
break;
|
|
case STARTED:
|
|
handleStop();
|
|
break;
|
|
default:
|
|
llassert(false);
|
|
break;
|
|
}
|
|
mPlayState = STOPPED;
|
|
}
|
|
|
|
void LLStopWatchControlsMixinCommon::pause()
|
|
{
|
|
switch (mPlayState)
|
|
{
|
|
case STOPPED:
|
|
break;
|
|
case PAUSED:
|
|
break;
|
|
case STARTED:
|
|
handleStop();
|
|
break;
|
|
default:
|
|
llassert(false);
|
|
break;
|
|
}
|
|
mPlayState = PAUSED;
|
|
}
|
|
|
|
void LLStopWatchControlsMixinCommon::resume()
|
|
{
|
|
switch (mPlayState)
|
|
{
|
|
case STOPPED:
|
|
handleStart();
|
|
break;
|
|
case PAUSED:
|
|
handleStart();
|
|
break;
|
|
case STARTED:
|
|
break;
|
|
default:
|
|
llassert(false);
|
|
break;
|
|
}
|
|
mPlayState = STARTED;
|
|
}
|
|
|
|
void LLStopWatchControlsMixinCommon::restart()
|
|
{
|
|
switch (mPlayState)
|
|
{
|
|
case STOPPED:
|
|
handleReset();
|
|
handleStart();
|
|
break;
|
|
case PAUSED:
|
|
handleReset();
|
|
handleStart();
|
|
break;
|
|
case STARTED:
|
|
handleReset();
|
|
break;
|
|
default:
|
|
llassert(false);
|
|
break;
|
|
}
|
|
mPlayState = STARTED;
|
|
}
|
|
|
|
void LLStopWatchControlsMixinCommon::reset()
|
|
{
|
|
handleReset();
|
|
}
|
|
|
|
void LLStopWatchControlsMixinCommon::setPlayState( EPlayState state )
|
|
{
|
|
switch(state)
|
|
{
|
|
case STOPPED:
|
|
stop();
|
|
break;
|
|
case PAUSED:
|
|
pause();
|
|
break;
|
|
case STARTED:
|
|
start();
|
|
break;
|
|
default:
|
|
llassert(false);
|
|
break;
|
|
}
|
|
|
|
mPlayState = state;
|
|
}
|