phoenix-firestorm/indra/llcommon/lltracerecording.cpp

663 lines
16 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
{
///////////////////////////////////////////////////////////////////////
// Recording
///////////////////////////////////////////////////////////////////////
Recording::Recording()
: mElapsedSeconds(0),
mCountsFloat(new AccumulatorBuffer<CountAccumulator<F64> >()),
mMeasurementsFloat(new AccumulatorBuffer<MeasurementAccumulator<F64> >()),
mCounts(new AccumulatorBuffer<CountAccumulator<S64> >()),
mMeasurements(new AccumulatorBuffer<MeasurementAccumulator<S64> >()),
mStackTimers(new AccumulatorBuffer<TimeBlockAccumulator>()),
mMemStats(new AccumulatorBuffer<MemStatAccumulator>())
{}
Recording::Recording( const Recording& other )
{
llassert(other.mCountsFloat.notNull());
mSamplingTimer = other.mSamplingTimer;
mElapsedSeconds = other.mElapsedSeconds;
mCountsFloat = other.mCountsFloat;
mMeasurementsFloat = other.mMeasurementsFloat;
mCounts = other.mCounts;
mMeasurements = other.mMeasurements;
mStackTimers = other.mStackTimers;
mMemStats = other.mMemStats;
LLStopWatchControlsMixin<Recording>::setPlayState(other.getPlayState());
}
Recording::~Recording()
{
stop();
llassert(isStopped());
}
void Recording::update()
{
if (isStarted())
{
LLTrace::get_thread_recorder()->update(this);
mSamplingTimer.reset();
}
}
void Recording::handleReset()
{
mCountsFloat.write()->reset();
mMeasurementsFloat.write()->reset();
mCounts.write()->reset();
mMeasurements.write()->reset();
mStackTimers.write()->reset();
mMemStats.write()->reset();
mElapsedSeconds = 0.0;
mSamplingTimer.reset();
}
void Recording::handleStart()
{
mSamplingTimer.reset();
LLTrace::get_thread_recorder()->activate(this);
}
void Recording::handleStop()
{
mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
LLTrace::TimeBlock::processTimes();
LLTrace::get_thread_recorder()->deactivate(this);
}
void Recording::handleSplitTo(Recording& other)
{
stop();
other.restart();
syncTo(other);
}
void Recording::syncTo(Recording& other)
{
other.mCountsFloat.write()->reset(mCountsFloat);
other.mMeasurementsFloat.write()->reset(mMeasurementsFloat);
other.mCounts.write()->reset(mCounts);
other.mMeasurements.write()->reset(mMeasurements);
other.mStackTimers.write()->reset(mStackTimers);
other.mMemStats.write()->reset(mMemStats);
//TODO: figure out how to get seamless handoff of timing stats
}
void Recording::makePrimary()
{
mCountsFloat.write()->makePrimary();
mMeasurementsFloat.write()->makePrimary();
mCounts.write()->makePrimary();
mMeasurements.write()->makePrimary();
mStackTimers.write()->makePrimary();
mMemStats.write()->makePrimary();
ThreadRecorder* thread_recorder = get_thread_recorder().get();
AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = *mStackTimers.write();
// 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 Recording::isPrimary() const
{
return mCounts->isPrimary();
}
void Recording::makeUnique()
{
mCountsFloat.makeUnique();
mMeasurementsFloat.makeUnique();
mCounts.makeUnique();
mMeasurements.makeUnique();
mStackTimers.makeUnique();
mMemStats.makeUnique();
}
void Recording::appendRecording( const Recording& other )
{
mCountsFloat.write()->addSamples(*other.mCountsFloat);
mMeasurementsFloat.write()->addSamples(*other.mMeasurementsFloat);
mCounts.write()->addSamples(*other.mCounts);
mMeasurements.write()->addSamples(*other.mMeasurements);
mMemStats.write()->addSamples(*other.mMemStats);
mStackTimers.write()->addSamples(*other.mStackTimers);
mElapsedSeconds += other.mElapsedSeconds;
}
void Recording::mergeRecording( const Recording& other)
{
mCountsFloat.write()->addSamples(*other.mCountsFloat);
mMeasurementsFloat.write()->addSamples(*other.mMeasurementsFloat);
mCounts.write()->addSamples(*other.mCounts);
mMeasurements.write()->addSamples(*other.mMeasurements);
mMemStats.write()->addSamples(*other.mMemStats);
}
LLUnit<LLUnits::Seconds, F64> Recording::getSum(const TraceType<TimeBlockAccumulator>& stat) const
{
return ((F64)(*mStackTimers)[stat.getIndex()].mSelfTimeCounter + (F64)(*mStackTimers)[stat.getIndex()].mChildTimeCounter) / (F64)LLTrace::TimeBlock::countsPerSecond();
}
LLUnit<LLUnits::Seconds, F64> Recording::getSum(const TraceType<TimeBlockAccumulator::SelfTimeAspect>& stat) const
{
return (F64)(*mStackTimers)[stat.getIndex()].mSelfTimeCounter / (F64)LLTrace::TimeBlock::countsPerSecond();
}
U32 Recording::getSum(const TraceType<TimeBlockAccumulator::CallCountAspect>& stat) const
{
return (*mStackTimers)[stat.getIndex()].mCalls;
}
LLUnit<LLUnits::Seconds, F64> Recording::getPerSec(const TraceType<TimeBlockAccumulator>& stat) const
{
return ((F64)(*mStackTimers)[stat.getIndex()].mSelfTimeCounter + (F64)(*mStackTimers)[stat.getIndex()].mChildTimeCounter) / ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds);
}
LLUnit<LLUnits::Seconds, F64> Recording::getPerSec(const TraceType<TimeBlockAccumulator::SelfTimeAspect>& stat) const
{
return (F64)(*mStackTimers)[stat.getIndex()].mSelfTimeCounter / ((F64)LLTrace::TimeBlock::countsPerSecond() * mElapsedSeconds);
}
F32 Recording::getPerSec(const TraceType<TimeBlockAccumulator::CallCountAspect>& stat) const
{
return (F32)(*mStackTimers)[stat.getIndex()].mCalls / mElapsedSeconds;
}
LLUnit<LLUnits::Bytes, U32> Recording::getSum(const TraceType<MemStatAccumulator>& stat) const
{
return (*mMemStats)[stat.getIndex()].mAllocatedCount;
}
LLUnit<LLUnits::Bytes, F32> Recording::getPerSec(const TraceType<MemStatAccumulator>& stat) const
{
return (F32)(*mMemStats)[stat.getIndex()].mAllocatedCount / mElapsedSeconds;
}
F64 Recording::getSum( const TraceType<CountAccumulator<F64> >& stat ) const
{
return (*mCountsFloat)[stat.getIndex()].getSum();
}
S64 Recording::getSum( const TraceType<CountAccumulator<S64> >& stat ) const
{
return (*mCounts)[stat.getIndex()].getSum();
}
F64 Recording::getSum( const TraceType<MeasurementAccumulator<F64> >& stat ) const
{
return (F64)(*mMeasurementsFloat)[stat.getIndex()].getSum();
}
S64 Recording::getSum( const TraceType<MeasurementAccumulator<S64> >& stat ) const
{
return (S64)(*mMeasurements)[stat.getIndex()].getSum();
}
F64 Recording::getPerSec( const TraceType<CountAccumulator<F64> >& stat ) const
{
F64 sum = (*mCountsFloat)[stat.getIndex()].getSum();
return (sum != 0.0)
? (sum / mElapsedSeconds)
: 0.0;
}
F64 Recording::getPerSec( const TraceType<CountAccumulator<S64> >& stat ) const
{
S64 sum = (*mCounts)[stat.getIndex()].getSum();
return (sum != 0)
? ((F64)sum / mElapsedSeconds)
: 0.0;
}
U32 Recording::getSampleCount( const TraceType<CountAccumulator<F64> >& stat ) const
{
return (*mCountsFloat)[stat.getIndex()].getSampleCount();
}
U32 Recording::getSampleCount( const TraceType<CountAccumulator<S64> >& stat ) const
{
return (*mMeasurementsFloat)[stat.getIndex()].getSampleCount();
}
F64 Recording::getPerSec( const TraceType<MeasurementAccumulator<F64> >& stat ) const
{
F64 sum = (*mMeasurementsFloat)[stat.getIndex()].getSum();
return (sum != 0.0)
? (sum / mElapsedSeconds)
: 0.0;
}
F64 Recording::getPerSec( const TraceType<MeasurementAccumulator<S64> >& stat ) const
{
S64 sum = (*mMeasurements)[stat.getIndex()].getSum();
return (sum != 0)
? ((F64)sum / mElapsedSeconds)
: 0.0;
}
F64 Recording::getMin( const TraceType<MeasurementAccumulator<F64> >& stat ) const
{
return (*mMeasurementsFloat)[stat.getIndex()].getMin();
}
S64 Recording::getMin( const TraceType<MeasurementAccumulator<S64> >& stat ) const
{
return (*mMeasurements)[stat.getIndex()].getMin();
}
F64 Recording::getMax( const TraceType<MeasurementAccumulator<F64> >& stat ) const
{
return (*mMeasurementsFloat)[stat.getIndex()].getMax();
}
S64 Recording::getMax( const TraceType<MeasurementAccumulator<S64> >& stat ) const
{
return (*mMeasurements)[stat.getIndex()].getMax();
}
F64 Recording::getMean( const TraceType<MeasurementAccumulator<F64> >& stat ) const
{
return (*mMeasurementsFloat)[stat.getIndex()].getMean();
}
F64 Recording::getMean( const TraceType<MeasurementAccumulator<S64> >& stat ) const
{
return (*mMeasurements)[stat.getIndex()].getMean();
}
F64 Recording::getStandardDeviation( const TraceType<MeasurementAccumulator<F64> >& stat ) const
{
return (*mMeasurementsFloat)[stat.getIndex()].getStandardDeviation();
}
F64 Recording::getStandardDeviation( const TraceType<MeasurementAccumulator<S64> >& stat ) const
{
return (*mMeasurements)[stat.getIndex()].getStandardDeviation();
}
F64 Recording::getLastValue( const TraceType<MeasurementAccumulator<F64> >& stat ) const
{
return (*mMeasurementsFloat)[stat.getIndex()].getLastValue();
}
S64 Recording::getLastValue( const TraceType<MeasurementAccumulator<S64> >& stat ) const
{
return (*mMeasurements)[stat.getIndex()].getLastValue();
}
U32 Recording::getSampleCount( const TraceType<MeasurementAccumulator<F64> >& stat ) const
{
return (*mMeasurementsFloat)[stat.getIndex()].getSampleCount();
}
U32 Recording::getSampleCount( const TraceType<MeasurementAccumulator<S64> >& stat ) const
{
return (*mMeasurements)[stat.getIndex()].getSampleCount();
}
///////////////////////////////////////////////////////////////////////
// PeriodicRecording
///////////////////////////////////////////////////////////////////////
PeriodicRecording::PeriodicRecording( S32 num_periods, EPlayState state)
: mNumPeriods(num_periods),
mCurPeriod(0),
mTotalValid(false),
mRecordingPeriods( new Recording[num_periods])
{
llassert(mNumPeriods > 0);
setPlayState(state);
}
PeriodicRecording::PeriodicRecording(PeriodicRecording& other)
: mNumPeriods(other.mNumPeriods),
mCurPeriod(other.mCurPeriod),
mTotalValid(other.mTotalValid),
mTotalRecording(other.mTotalRecording)
{
mRecordingPeriods = new Recording[mNumPeriods];
for (S32 i = 0; i < mNumPeriods; i++)
{
mRecordingPeriods[i] = other.mRecordingPeriods[i];
}
}
PeriodicRecording::~PeriodicRecording()
{
delete[] mRecordingPeriods;
}
void PeriodicRecording::nextPeriod()
{
EPlayState play_state = getPlayState();
Recording& old_recording = getCurRecordingPeriod();
mCurPeriod = (mCurPeriod + 1) % mNumPeriods;
old_recording.splitTo(getCurRecordingPeriod());
switch(play_state)
{
case STOPPED:
getCurRecordingPeriod().stop();
break;
case PAUSED:
getCurRecordingPeriod().pause();
break;
case STARTED:
break;
}
// new period, need to recalculate total
mTotalValid = false;
}
Recording& PeriodicRecording::getTotalRecording()
{
if (!mTotalValid)
{
mTotalRecording.reset();
for (S32 i = mCurPeriod + 1; i < mCurPeriod + mNumPeriods; i++)
{
mTotalRecording.appendRecording(mRecordingPeriods[i % mNumPeriods]);
}
}
mTotalValid = true;
return mTotalRecording;
}
void PeriodicRecording::start()
{
getCurRecordingPeriod().start();
}
void PeriodicRecording::stop()
{
getCurRecordingPeriod().stop();
}
void PeriodicRecording::pause()
{
getCurRecordingPeriod().pause();
}
void PeriodicRecording::resume()
{
getCurRecordingPeriod().resume();
}
void PeriodicRecording::restart()
{
getCurRecordingPeriod().restart();
}
void PeriodicRecording::reset()
{
getCurRecordingPeriod().reset();
}
void PeriodicRecording::splitTo(PeriodicRecording& other)
{
getCurRecordingPeriod().splitTo(other.getCurRecordingPeriod());
}
void PeriodicRecording::splitFrom(PeriodicRecording& other)
{
getCurRecordingPeriod().splitFrom(other.getCurRecordingPeriod());
}
///////////////////////////////////////////////////////////////////////
// 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::start()
{
LLStopWatchControlsMixin<ExtendableRecording>::start();
mPotentialRecording.start();
}
void ExtendableRecording::stop()
{
LLStopWatchControlsMixin<ExtendableRecording>::stop();
mPotentialRecording.stop();
}
void ExtendableRecording::pause()
{
LLStopWatchControlsMixin<ExtendableRecording>::pause();
mPotentialRecording.pause();
}
void ExtendableRecording::resume()
{
LLStopWatchControlsMixin<ExtendableRecording>::resume();
mPotentialRecording.resume();
}
void ExtendableRecording::restart()
{
LLStopWatchControlsMixin<ExtendableRecording>::restart();
mAcceptedRecording.reset();
mPotentialRecording.restart();
}
void ExtendableRecording::reset()
{
LLStopWatchControlsMixin<ExtendableRecording>::reset();
mAcceptedRecording.reset();
mPotentialRecording.reset();
}
void ExtendableRecording::splitTo(ExtendableRecording& other)
{
LLStopWatchControlsMixin<ExtendableRecording>::splitTo(other);
mPotentialRecording.splitTo(other.mPotentialRecording);
}
void ExtendableRecording::splitFrom(ExtendableRecording& other)
{
LLStopWatchControlsMixin<ExtendableRecording>::splitFrom(other);
mPotentialRecording.splitFrom(other.mPotentialRecording);
}
PeriodicRecording& get_frame_recording()
{
static LLThreadLocalPointer<PeriodicRecording> sRecording(new PeriodicRecording(200, 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:
handleStop();
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;
}