PageRenderTime 100ms CodeModel.GetById 21ms app.highlight 71ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/llstat.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1306 lines | 1031 code | 185 blank | 90 comment | 164 complexity | 9a02005714c71f45511f669373c78bfe MD5 | raw file
   1/** 
   2 * @file llstat.cpp
   3 *
   4 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
   5 * Second Life Viewer Source Code
   6 * Copyright (C) 2010, Linden Research, Inc.
   7 * 
   8 * This library is free software; you can redistribute it and/or
   9 * modify it under the terms of the GNU Lesser General Public
  10 * License as published by the Free Software Foundation;
  11 * version 2.1 of the License only.
  12 * 
  13 * This library is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16 * Lesser General Public License for more details.
  17 * 
  18 * You should have received a copy of the GNU Lesser General Public
  19 * License along with this library; if not, write to the Free Software
  20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  21 * 
  22 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
  23 * $/LicenseInfo$
  24 */
  25
  26#include "linden_common.h"
  27
  28#include "llstat.h"
  29#include "lllivefile.h"
  30#include "llerrorcontrol.h"
  31#include "llframetimer.h"
  32#include "timing.h"
  33#include "llsd.h"
  34#include "llsdserialize.h"
  35#include "llstl.h"
  36#include "u64.h"
  37
  38
  39// statics
  40S32	            LLPerfBlock::sStatsFlags = LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS;       // Control what is being recorded
  41LLPerfBlock::stat_map_t    LLPerfBlock::sStatMap;    // Map full path string to LLStatTime objects, tracks all active objects
  42std::string        LLPerfBlock::sCurrentStatPath = "";    // Something like "/total_time/physics/physics step"
  43LLStat::stat_map_t LLStat::sStatList;
  44
  45//------------------------------------------------------------------------
  46// Live config file to trigger stats logging
  47static const char    STATS_CONFIG_FILE_NAME[]            = "/dev/shm/simperf/simperf_proc_config.llsd";
  48static const F32    STATS_CONFIG_REFRESH_RATE            = 5.0;        // seconds
  49
  50class LLStatsConfigFile : public LLLiveFile
  51{
  52public:
  53    LLStatsConfigFile()
  54        : LLLiveFile(filename(), STATS_CONFIG_REFRESH_RATE),
  55        mChanged(false), mStatsp(NULL) { }
  56
  57    static std::string filename();
  58    
  59protected:
  60    /* virtual */ bool loadFile();
  61
  62public:
  63    void init(LLPerfStats* statsp);
  64    static LLStatsConfigFile& instance();
  65        // return the singleton stats config file
  66
  67    bool mChanged;
  68
  69protected:
  70    LLPerfStats*    mStatsp;
  71};
  72
  73std::string LLStatsConfigFile::filename()
  74{
  75    return STATS_CONFIG_FILE_NAME;
  76}
  77
  78void LLStatsConfigFile::init(LLPerfStats* statsp)
  79{
  80    mStatsp = statsp;
  81}
  82
  83LLStatsConfigFile& LLStatsConfigFile::instance()
  84{
  85    static LLStatsConfigFile the_file;
  86    return the_file;
  87}
  88
  89
  90/* virtual */
  91// Load and parse the stats configuration file
  92bool LLStatsConfigFile::loadFile()
  93{
  94    if (!mStatsp)
  95    {
  96        llwarns << "Tries to load performance configure file without initializing LPerfStats" << llendl;
  97        return false;
  98    }
  99    mChanged = true;
 100    
 101    LLSD stats_config;
 102    {
 103        llifstream file(filename().c_str());
 104        if (file.is_open())
 105        {
 106            LLSDSerialize::fromXML(stats_config, file);
 107            if (stats_config.isUndefined())
 108            {
 109                llinfos << "Performance statistics configuration file ill-formed, not recording statistics" << llendl;
 110                mStatsp->setReportPerformanceDuration( 0.f );
 111                return false;
 112            }
 113        }
 114        else 
 115        {    // File went away, turn off stats if it was on
 116            if ( mStatsp->frameStatsIsRunning() )
 117            {
 118                llinfos << "Performance statistics configuration file deleted, not recording statistics" << llendl;
 119                mStatsp->setReportPerformanceDuration( 0.f );
 120            }
 121            return true;
 122        }
 123    }
 124
 125    F32 duration = 0.f;
 126    F32 interval = 0.f;
 127	S32 flags = LLPerfBlock::LLSTATS_BASIC_STATS;
 128
 129    const char * w = "duration";
 130    if (stats_config.has(w))
 131    {
 132        duration = (F32)stats_config[w].asReal();
 133    } 
 134    w = "interval";
 135    if (stats_config.has(w))
 136    {
 137        interval = (F32)stats_config[w].asReal();
 138    } 
 139    w = "flags";
 140    if (stats_config.has(w))
 141    {
 142		flags = (S32)stats_config[w].asInteger();
 143		if (flags == LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS &&
 144			duration > 0)
 145		{   // No flags passed in, but have a duration, so reset to basic stats
 146			flags = LLPerfBlock::LLSTATS_BASIC_STATS;
 147		}
 148    } 
 149
 150    mStatsp->setReportPerformanceDuration( duration, flags );
 151    mStatsp->setReportPerformanceInterval( interval );
 152
 153    if ( duration > 0 )
 154    {
 155        if ( interval == 0.f )
 156        {
 157            llinfos << "Recording performance stats every frame for " << duration << " sec" << llendl;
 158        }
 159        else
 160        {
 161            llinfos << "Recording performance stats every " << interval << " seconds for " << duration << " seconds" << llendl;
 162        }
 163    }
 164    else
 165    {
 166        llinfos << "Performance stats recording turned off" << llendl;
 167    }
 168	return true;
 169}
 170
 171
 172//------------------------------------------------------------------------
 173
 174LLPerfStats::LLPerfStats(const std::string& process_name, S32 process_pid) : 
 175    mFrameStatsFileFailure(FALSE),
 176    mSkipFirstFrameStats(FALSE),
 177    mProcessName(process_name),
 178    mProcessPID(process_pid),
 179    mReportPerformanceStatInterval(1.f),
 180    mReportPerformanceStatEnd(0.0) 
 181{ }
 182
 183LLPerfStats::~LLPerfStats()
 184{
 185    LLPerfBlock::clearDynamicStats();
 186    mFrameStatsFile.close();
 187}
 188
 189void LLPerfStats::init()
 190{
 191    // Initialize the stats config file instance.
 192    (void) LLStatsConfigFile::instance().init(this);
 193    (void) LLStatsConfigFile::instance().checkAndReload();
 194}
 195
 196// Open file for statistics
 197void    LLPerfStats::openPerfStatsFile()
 198{
 199    if ( !mFrameStatsFile
 200        && !mFrameStatsFileFailure )
 201    {
 202        std::string stats_file = llformat("/dev/shm/simperf/%s_proc.%d.llsd", mProcessName.c_str(), mProcessPID);
 203        mFrameStatsFile.close();
 204        mFrameStatsFile.clear();
 205        mFrameStatsFile.open(stats_file, llofstream::out);
 206        if ( mFrameStatsFile.fail() )
 207        {
 208            llinfos << "Error opening statistics log file " << stats_file << llendl;
 209            mFrameStatsFileFailure = TRUE;
 210        }
 211        else
 212        {
 213            LLSD process_info = LLSD::emptyMap();
 214            process_info["name"] = mProcessName;
 215            process_info["pid"] = (LLSD::Integer) mProcessPID;
 216            process_info["stat_rate"] = (LLSD::Integer) mReportPerformanceStatInterval;
 217            // Add process-specific info.
 218            addProcessHeaderInfo(process_info);
 219
 220            mFrameStatsFile << LLSDNotationStreamer(process_info) << std::endl; 
 221        }
 222    }
 223}
 224
 225// Dump out performance metrics over some time interval
 226void LLPerfStats::dumpIntervalPerformanceStats()
 227{
 228    // Ensure output file is OK
 229    openPerfStatsFile();
 230
 231    if ( mFrameStatsFile )
 232    {
 233        LLSD stats = LLSD::emptyMap();
 234
 235        LLStatAccum::TimeScale scale;
 236        if ( getReportPerformanceInterval() == 0.f )
 237        {
 238            scale = LLStatAccum::SCALE_PER_FRAME;
 239        }
 240        else if ( getReportPerformanceInterval() < 0.5f )
 241        {
 242            scale = LLStatAccum::SCALE_100MS;
 243        }
 244        else
 245        {
 246            scale = LLStatAccum::SCALE_SECOND;
 247        }
 248
 249        // Write LLSD into log
 250        stats["utc_time"] = (LLSD::String) LLError::utcTime();
 251        stats["timestamp"] = U64_to_str((totalTime() / 1000) + (gUTCOffset * 1000));    // milliseconds since epoch
 252        stats["frame_number"] = (LLSD::Integer) LLFrameTimer::getFrameCount();
 253
 254        // Add process-specific frame info.
 255        addProcessFrameInfo(stats, scale);
 256        LLPerfBlock::addStatsToLLSDandReset( stats, scale );
 257
 258        mFrameStatsFile << LLSDNotationStreamer(stats) << std::endl; 
 259    }
 260}
 261
 262// Set length of performance stat recording.  
 263// If turning stats on, caller must provide flags
 264void    LLPerfStats::setReportPerformanceDuration( F32 seconds, S32 flags /* = LLSTATS_NO_OPTIONAL_STATS */ )
 265{ 
 266	if ( seconds <= 0.f )
 267	{
 268		mReportPerformanceStatEnd = 0.0;
 269		LLPerfBlock::setStatsFlags(LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS);		// Make sure all recording is off
 270		mFrameStatsFile.close();
 271		LLPerfBlock::clearDynamicStats();
 272	}
 273	else
 274	{
 275		mReportPerformanceStatEnd = LLFrameTimer::getElapsedSeconds() + ((F64) seconds);
 276		// Clear failure flag to try and create the log file once
 277		mFrameStatsFileFailure = FALSE;
 278		mSkipFirstFrameStats = TRUE;		// Skip the first report (at the end of this frame)
 279		LLPerfBlock::setStatsFlags(flags);
 280	}
 281}
 282
 283void LLPerfStats::updatePerFrameStats()
 284{
 285    (void) LLStatsConfigFile::instance().checkAndReload();
 286	static LLFrameTimer performance_stats_timer;
 287	if ( frameStatsIsRunning() )
 288	{
 289		if ( mReportPerformanceStatInterval == 0 )
 290		{	// Record info every frame
 291			if ( mSkipFirstFrameStats )
 292			{	// Skip the first time - was started this frame
 293				mSkipFirstFrameStats = FALSE;
 294			}
 295			else
 296			{
 297				dumpIntervalPerformanceStats();
 298			}
 299		}
 300		else
 301		{
 302			performance_stats_timer.setTimerExpirySec( getReportPerformanceInterval() );
 303			if (performance_stats_timer.checkExpirationAndReset( mReportPerformanceStatInterval ))
 304			{
 305				dumpIntervalPerformanceStats();
 306			}
 307		}
 308		
 309		if ( LLFrameTimer::getElapsedSeconds() > mReportPerformanceStatEnd )
 310		{	// Reached end of time, clear it to stop reporting
 311			setReportPerformanceDuration(0.f);			// Don't set mReportPerformanceStatEnd directly	
 312            llinfos << "Recording performance stats completed" << llendl;
 313		}
 314	}
 315}
 316
 317
 318//------------------------------------------------------------------------
 319
 320U64 LLStatAccum::sScaleTimes[NUM_SCALES] =
 321{
 322	USEC_PER_SEC / 10,				// 100 millisec
 323	USEC_PER_SEC * 1,				// seconds
 324	USEC_PER_SEC * 60,				// minutes
 325#if ENABLE_LONG_TIME_STATS
 326	// enable these when more time scales are desired
 327	USEC_PER_SEC * 60*60,			// hours
 328	USEC_PER_SEC * 24*60*60,		// days
 329	USEC_PER_SEC * 7*24*60*60,		// weeks
 330#endif
 331};
 332
 333
 334
 335LLStatAccum::LLStatAccum(bool useFrameTimer)
 336	: mUseFrameTimer(useFrameTimer),
 337	  mRunning(FALSE),
 338	  mLastTime(0),
 339	  mLastSampleValue(0.0),
 340	  mLastSampleValid(FALSE)
 341{
 342}
 343
 344LLStatAccum::~LLStatAccum()
 345{
 346}
 347
 348
 349
 350void LLStatAccum::reset(U64 when)
 351{
 352	mRunning = TRUE;
 353	mLastTime = when;
 354
 355	for (int i = 0; i < NUM_SCALES; ++i)
 356	{
 357		mBuckets[i].accum = 0.0;
 358		mBuckets[i].endTime = when + sScaleTimes[i];
 359		mBuckets[i].lastValid = false;
 360	}
 361}
 362
 363void LLStatAccum::sum(F64 value)
 364{
 365	sum(value, getCurrentUsecs());
 366}
 367
 368void LLStatAccum::sum(F64 value, U64 when)
 369{
 370	if (!mRunning)
 371	{
 372		reset(when);
 373		return;
 374	}
 375	if (when < mLastTime)
 376	{
 377		// This happens a LOT on some dual core systems.
 378		lldebugs << "LLStatAccum::sum clock has gone backwards from "
 379			<< mLastTime << " to " << when << ", resetting" << llendl;
 380
 381		reset(when);
 382		return;
 383	}
 384
 385	// how long is this value for
 386	U64 timeSpan = when - mLastTime;
 387
 388	for (int i = 0; i < NUM_SCALES; ++i)
 389	{
 390		Bucket& bucket = mBuckets[i];
 391
 392		if (when < bucket.endTime)
 393		{
 394			bucket.accum += value;
 395		}
 396		else
 397		{
 398			U64 timeScale = sScaleTimes[i];
 399
 400			U64 timeLeft = when - bucket.endTime;
 401				// how much time is left after filling this bucket
 402			
 403			if (timeLeft < timeScale)
 404			{
 405				F64 valueLeft = value * timeLeft / timeSpan;
 406
 407				bucket.lastValid = true;
 408				bucket.lastAccum = bucket.accum + (value - valueLeft);
 409				bucket.accum = valueLeft;
 410				bucket.endTime += timeScale;
 411			}
 412			else
 413			{
 414				U64 timeTail = timeLeft % timeScale;
 415
 416				bucket.lastValid = true;
 417				bucket.lastAccum = value * timeScale / timeSpan;
 418				bucket.accum = value * timeTail / timeSpan;
 419				bucket.endTime += (timeLeft - timeTail) + timeScale;
 420			}
 421		}
 422	}
 423
 424	mLastTime = when;
 425}
 426
 427
 428F32 LLStatAccum::meanValue(TimeScale scale) const
 429{
 430	if (!mRunning)
 431	{
 432		return 0.0;
 433	}
 434	if ( scale == SCALE_PER_FRAME )
 435	{	// Per-frame not supported here
 436		scale = SCALE_100MS;
 437	}
 438
 439	if (scale < 0 || scale >= NUM_SCALES)
 440	{
 441		llwarns << "llStatAccum::meanValue called for unsupported scale: "
 442			<< scale << llendl;
 443		return 0.0;
 444	}
 445
 446	const Bucket& bucket = mBuckets[scale];
 447
 448	F64 value = bucket.accum;
 449	U64 timeLeft = bucket.endTime - mLastTime;
 450	U64 scaleTime = sScaleTimes[scale];
 451
 452	if (bucket.lastValid)
 453	{
 454		value += bucket.lastAccum * timeLeft / scaleTime;
 455	}
 456	else if (timeLeft < scaleTime)
 457	{
 458		value *= scaleTime / (scaleTime - timeLeft);
 459	}
 460	else
 461	{
 462		value = 0.0;
 463	}
 464
 465	return (F32)(value / scaleTime);
 466}
 467
 468
 469U64 LLStatAccum::getCurrentUsecs() const
 470{
 471	if (mUseFrameTimer)
 472	{
 473		return LLFrameTimer::getTotalTime();
 474	}
 475	else
 476	{
 477		return totalTime();
 478	}
 479}
 480
 481
 482// ------------------------------------------------------------------------
 483
 484LLStatRate::LLStatRate(bool use_frame_timer)
 485	: LLStatAccum(use_frame_timer)
 486{
 487}
 488
 489void LLStatRate::count(U32 value)
 490{
 491	sum((F64)value * sScaleTimes[SCALE_SECOND]);
 492}
 493
 494
 495void LLStatRate::mark()
 496 { 
 497	// Effectively the same as count(1), but sets mLastSampleValue
 498	U64 when = getCurrentUsecs();
 499
 500	if ( mRunning 
 501		 && (when > mLastTime) )
 502	{	// Set mLastSampleValue to the time from the last mark()
 503		F64 duration = ((F64)(when - mLastTime)) / sScaleTimes[SCALE_SECOND];
 504		if ( duration > 0.0 )
 505		{
 506			mLastSampleValue = 1.0 / duration;
 507		}
 508		else
 509		{
 510			mLastSampleValue = 0.0;
 511		}
 512	}
 513
 514	sum( (F64) sScaleTimes[SCALE_SECOND], when);
 515 }
 516
 517
 518// ------------------------------------------------------------------------
 519
 520
 521LLStatMeasure::LLStatMeasure(bool use_frame_timer)
 522	: LLStatAccum(use_frame_timer)
 523{
 524}
 525
 526void LLStatMeasure::sample(F64 value)
 527{
 528	U64 when = getCurrentUsecs();
 529
 530	if (mLastSampleValid)
 531	{
 532		F64 avgValue = (value + mLastSampleValue) / 2.0;
 533		F64 interval = (F64)(when - mLastTime);
 534
 535		sum(avgValue * interval, when);
 536	}
 537	else
 538	{
 539		reset(when);
 540	}
 541
 542	mLastSampleValid = TRUE;
 543	mLastSampleValue = value;
 544}
 545
 546
 547// ------------------------------------------------------------------------
 548
 549LLStatTime::LLStatTime(const std::string & key)
 550	: LLStatAccum(false),
 551	  mFrameNumber(LLFrameTimer::getFrameCount()),
 552	  mTotalTimeInFrame(0),
 553	  mKey(key)
 554#if LL_DEBUG
 555	  , mRunning(FALSE)
 556#endif
 557{
 558}
 559
 560void LLStatTime::start()
 561{
 562	// Reset frame accumluation if the frame number has changed
 563	U32 frame_number = LLFrameTimer::getFrameCount();
 564	if ( frame_number != mFrameNumber )
 565	{
 566		mFrameNumber = frame_number;
 567		mTotalTimeInFrame = 0;
 568	}
 569
 570	sum(0.0);
 571
 572#if LL_DEBUG
 573	// Shouldn't be running already
 574	llassert( !mRunning );
 575	mRunning = TRUE;
 576#endif
 577}
 578
 579void LLStatTime::stop()
 580{
 581	U64 end_time = getCurrentUsecs();
 582	U64 duration = end_time - mLastTime;
 583	sum(F64(duration), end_time);
 584	//llinfos << "mTotalTimeInFrame incremented from  " << mTotalTimeInFrame << " to " << (mTotalTimeInFrame + duration) << llendl; 
 585	mTotalTimeInFrame += duration;
 586
 587#if LL_DEBUG
 588	mRunning = FALSE;
 589#endif
 590}
 591
 592/* virtual */ F32 LLStatTime::meanValue(TimeScale scale) const
 593{
 594    if ( LLStatAccum::SCALE_PER_FRAME == scale )
 595    {
 596        return mTotalTimeInFrame;
 597    }
 598    else
 599    {
 600        return LLStatAccum::meanValue(scale);
 601    }
 602}
 603
 604
 605// ------------------------------------------------------------------------
 606
 607
 608// Use this constructor for pre-defined LLStatTime objects
 609LLPerfBlock::LLPerfBlock(LLStatTime* stat ) : mPredefinedStat(stat), mDynamicStat(NULL)
 610{
 611    if (mPredefinedStat)
 612    {
 613        // If dynamic stats are turned on, this will create a separate entry in the stat map.
 614        initDynamicStat(mPredefinedStat->mKey);
 615
 616        // Start predefined stats.  These stats are not part of the stat map.
 617        mPredefinedStat->start();
 618    }
 619}
 620
 621// Use this constructor for normal, optional LLPerfBlock time slices
 622LLPerfBlock::LLPerfBlock( const char* key ) : mPredefinedStat(NULL), mDynamicStat(NULL)
 623{
 624    if ((sStatsFlags & LLSTATS_BASIC_STATS) == 0)
 625	{	// These are off unless the base set is enabled
 626		return;
 627	}
 628
 629	initDynamicStat(key);
 630}
 631
 632	
 633// Use this constructor for dynamically created LLPerfBlock time slices
 634// that are only enabled by specific control flags
 635LLPerfBlock::LLPerfBlock( const char* key1, const char* key2, S32 flags ) : mPredefinedStat(NULL), mDynamicStat(NULL)
 636{
 637    if ((sStatsFlags & flags) == 0)
 638	{
 639		return;
 640	}
 641
 642    if (NULL == key2 || strlen(key2) == 0)
 643    {
 644        initDynamicStat(key1);
 645    }
 646    else
 647    {
 648        std::ostringstream key;
 649        key << key1 << "_" << key2;
 650        initDynamicStat(key.str());
 651    }
 652}
 653
 654// Set up the result data map if dynamic stats are enabled
 655void LLPerfBlock::initDynamicStat(const std::string& key)
 656{
 657    // Early exit if dynamic stats aren't enabled.
 658    if (sStatsFlags == LLSTATS_NO_OPTIONAL_STATS) 
 659		return;
 660
 661    mLastPath = sCurrentStatPath;		// Save and restore current path
 662    sCurrentStatPath += "/" + key;		// Add key to current path
 663
 664    // See if the LLStatTime object already exists
 665    stat_map_t::iterator iter = sStatMap.find(sCurrentStatPath);
 666    if ( iter == sStatMap.end() )
 667    {
 668        // StatEntry object doesn't exist, so create it
 669        mDynamicStat = new StatEntry( key );
 670        sStatMap[ sCurrentStatPath ] = mDynamicStat;	// Set the entry for this path
 671    }
 672    else
 673    {
 674        // Found this path in the map, use the object there
 675        mDynamicStat = (*iter).second;		// Get StatEntry for the current path
 676    }
 677
 678    if (mDynamicStat)
 679    {
 680        mDynamicStat->mStat.start();
 681        mDynamicStat->mCount++;
 682    }
 683    else
 684    {
 685        llwarns << "Initialized NULL dynamic stat at '" << sCurrentStatPath << "'" << llendl;
 686       sCurrentStatPath = mLastPath;
 687    }
 688}
 689
 690
 691// Destructor does the time accounting
 692LLPerfBlock::~LLPerfBlock()
 693{
 694    if (mPredefinedStat) mPredefinedStat->stop();
 695    if (mDynamicStat)
 696    {
 697        mDynamicStat->mStat.stop();
 698        sCurrentStatPath = mLastPath;	// Restore the path in case sStatsEnabled changed during this block
 699    }
 700}
 701
 702
 703// Clear the map of any dynamic stats.  Static routine
 704void LLPerfBlock::clearDynamicStats()
 705{
 706    std::for_each(sStatMap.begin(), sStatMap.end(), DeletePairedPointer());
 707    sStatMap.clear();
 708}
 709
 710// static - Extract the stat info into LLSD
 711void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats,
 712										  LLStatAccum::TimeScale scale )
 713{
 714    // If we aren't in per-frame scale, we need to go from second to microsecond.
 715    U32 scale_adjustment = 1;
 716    if (LLStatAccum::SCALE_PER_FRAME != scale)
 717    {
 718        scale_adjustment = USEC_PER_SEC;
 719    }
 720	stat_map_t::iterator iter = sStatMap.begin();
 721	for ( ; iter != sStatMap.end(); ++iter )
 722	{	// Put the entry into LLSD "/full/path/to/stat/" = microsecond total time
 723		const std::string & stats_full_path = (*iter).first;
 724
 725		StatEntry * stat = (*iter).second;
 726		if (stat)
 727		{
 728            if (stat->mCount > 0)
 729            {
 730                stats[stats_full_path] = LLSD::emptyMap();
 731                stats[stats_full_path]["us"] = (LLSD::Integer) (scale_adjustment * stat->mStat.meanValue(scale));
 732                if (stat->mCount > 1)
 733                {
 734                    stats[stats_full_path]["count"] = (LLSD::Integer) stat->mCount;
 735                }
 736                stat->mCount = 0;
 737            }
 738		}
 739		else
 740		{	// Shouldn't have a NULL pointer in the map.
 741            llwarns << "Unexpected NULL dynamic stat at '" << stats_full_path << "'" << llendl;
 742		}
 743	}	
 744}
 745
 746
 747// ------------------------------------------------------------------------
 748
 749LLTimer LLStat::sTimer;
 750LLFrameTimer LLStat::sFrameTimer;
 751
 752void LLStat::init()
 753{
 754	llassert(mNumBins > 0);
 755	mNumValues = 0;
 756	mLastValue = 0.f;
 757	mLastTime = 0.f;
 758	mCurBin = (mNumBins-1);
 759	mNextBin = 0;
 760	mBins      = new F32[mNumBins];
 761	mBeginTime = new F64[mNumBins];
 762	mTime      = new F64[mNumBins];
 763	mDT        = new F32[mNumBins];
 764	for (U32 i = 0; i < mNumBins; i++)
 765	{
 766		mBins[i]      = 0.f;
 767		mBeginTime[i] = 0.0;
 768		mTime[i]      = 0.0;
 769		mDT[i]        = 0.f;
 770	}
 771
 772	if (!mName.empty())
 773	{
 774		stat_map_t::iterator iter = sStatList.find(mName);
 775		if (iter != sStatList.end())
 776			llwarns << "LLStat with duplicate name: " << mName << llendl;
 777		sStatList.insert(std::make_pair(mName, this));
 778	}
 779}
 780
 781LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer)
 782	: mUseFrameTimer(use_frame_timer),
 783	  mNumBins(num_bins)
 784{
 785	init();
 786}
 787
 788LLStat::LLStat(std::string name, U32 num_bins, BOOL use_frame_timer)
 789	: mUseFrameTimer(use_frame_timer),
 790	  mNumBins(num_bins),
 791	  mName(name)
 792{
 793	init();
 794}
 795
 796LLStat::~LLStat()
 797{
 798	delete[] mBins;
 799	delete[] mBeginTime;
 800	delete[] mTime;
 801	delete[] mDT;
 802
 803	if (!mName.empty())
 804	{
 805		// handle multiple entries with the same name
 806		stat_map_t::iterator iter = sStatList.find(mName);
 807		while (iter != sStatList.end() && iter->second != this)
 808			++iter;
 809		sStatList.erase(iter);
 810	}
 811}
 812
 813void LLStat::reset()
 814{
 815	U32 i;
 816
 817	mNumValues = 0;
 818	mLastValue = 0.f;
 819	mCurBin = (mNumBins-1);
 820	delete[] mBins;
 821	delete[] mBeginTime;
 822	delete[] mTime;
 823	delete[] mDT;
 824	mBins      = new F32[mNumBins];
 825	mBeginTime = new F64[mNumBins];
 826	mTime      = new F64[mNumBins];
 827	mDT        = new F32[mNumBins];
 828	for (i = 0; i < mNumBins; i++)
 829	{
 830		mBins[i]      = 0.f;
 831		mBeginTime[i] = 0.0;
 832		mTime[i]      = 0.0;
 833		mDT[i]        = 0.f;
 834	}
 835}
 836
 837void LLStat::setBeginTime(const F64 time)
 838{
 839	mBeginTime[mNextBin] = time;
 840}
 841
 842void LLStat::addValueTime(const F64 time, const F32 value)
 843{
 844	if (mNumValues < mNumBins)
 845	{
 846		mNumValues++;
 847	}
 848
 849	// Increment the bin counters.
 850	mCurBin++;
 851	if ((U32)mCurBin == mNumBins)
 852	{
 853		mCurBin = 0;
 854	}
 855	mNextBin++;
 856	if ((U32)mNextBin == mNumBins)
 857	{
 858		mNextBin = 0;
 859	}
 860
 861	mBins[mCurBin] = value;
 862	mTime[mCurBin] = time;
 863	mDT[mCurBin] = (F32)(mTime[mCurBin] - mBeginTime[mCurBin]);
 864	//this value is used to prime the min/max calls
 865	mLastTime = mTime[mCurBin];
 866	mLastValue = value;
 867
 868	// Set the begin time for the next stat segment.
 869	mBeginTime[mNextBin] = mTime[mCurBin];
 870	mTime[mNextBin] = mTime[mCurBin];
 871	mDT[mNextBin] = 0.f;
 872}
 873
 874void LLStat::start()
 875{
 876	if (mUseFrameTimer)
 877	{
 878		mBeginTime[mNextBin] = sFrameTimer.getElapsedSeconds();
 879	}
 880	else
 881	{
 882		mBeginTime[mNextBin] = sTimer.getElapsedTimeF64();
 883	}
 884}
 885
 886void LLStat::addValue(const F32 value)
 887{
 888	if (mNumValues < mNumBins)
 889	{
 890		mNumValues++;
 891	}
 892
 893	// Increment the bin counters.
 894	mCurBin++;
 895	if ((U32)mCurBin == mNumBins)
 896	{
 897		mCurBin = 0;
 898	}
 899	mNextBin++;
 900	if ((U32)mNextBin == mNumBins)
 901	{
 902		mNextBin = 0;
 903	}
 904
 905	mBins[mCurBin] = value;
 906	if (mUseFrameTimer)
 907	{
 908		mTime[mCurBin] = sFrameTimer.getElapsedSeconds();
 909	}
 910	else
 911	{
 912		mTime[mCurBin] = sTimer.getElapsedTimeF64();
 913	}
 914	mDT[mCurBin] = (F32)(mTime[mCurBin] - mBeginTime[mCurBin]);
 915
 916	//this value is used to prime the min/max calls
 917	mLastTime = mTime[mCurBin];
 918	mLastValue = value;
 919
 920	// Set the begin time for the next stat segment.
 921	mBeginTime[mNextBin] = mTime[mCurBin];
 922	mTime[mNextBin] = mTime[mCurBin];
 923	mDT[mNextBin] = 0.f;
 924}
 925
 926
 927F32 LLStat::getMax() const
 928{
 929	U32 i;
 930	F32 current_max = mLastValue;
 931	if (mNumBins == 0)
 932	{
 933		current_max = 0.f;
 934	}
 935	else
 936	{
 937		for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
 938		{
 939			// Skip the bin we're currently filling.
 940			if (i == (U32)mNextBin)
 941			{
 942				continue;
 943			}
 944			if (mBins[i] > current_max)
 945			{
 946				current_max = mBins[i];
 947			}
 948		}
 949	}
 950	return current_max;
 951}
 952
 953F32 LLStat::getMean() const
 954{
 955	U32 i;
 956	F32 current_mean = 0.f;
 957	U32 samples = 0;
 958	for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
 959	{
 960		// Skip the bin we're currently filling.
 961		if (i == (U32)mNextBin)
 962		{
 963			continue;
 964		}
 965		current_mean += mBins[i];
 966		samples++;
 967	}
 968
 969	// There will be a wrap error at 2^32. :)
 970	if (samples != 0)
 971	{
 972		current_mean /= samples;
 973	}
 974	else
 975	{
 976		current_mean = 0.f;
 977	}
 978	return current_mean;
 979}
 980
 981F32 LLStat::getMin() const
 982{
 983	U32 i;
 984	F32 current_min = mLastValue;
 985
 986	if (mNumBins == 0)
 987	{
 988		current_min = 0.f;
 989	}
 990	else
 991	{
 992		for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
 993		{
 994			// Skip the bin we're currently filling.
 995			if (i == (U32)mNextBin)
 996			{
 997				continue;
 998			}
 999			if (mBins[i] < current_min)
1000			{
1001				current_min = mBins[i];
1002			}
1003		}
1004	}
1005	return current_min;
1006}
1007
1008F32 LLStat::getSum() const
1009{
1010	U32 i;
1011	F32 sum = 0.f;
1012	for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
1013	{
1014		// Skip the bin we're currently filling.
1015		if (i == (U32)mNextBin)
1016		{
1017			continue;
1018		}
1019		sum += mBins[i];
1020	}
1021
1022	return sum;
1023}
1024
1025F32 LLStat::getSumDuration() const
1026{
1027	U32 i;
1028	F32 sum = 0.f;
1029	for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
1030	{
1031		// Skip the bin we're currently filling.
1032		if (i == (U32)mNextBin)
1033		{
1034			continue;
1035		}
1036		sum += mDT[i];
1037	}
1038
1039	return sum;
1040}
1041
1042F32 LLStat::getPrev(S32 age) const
1043{
1044	S32 bin;
1045	bin = mCurBin - age;
1046
1047	while (bin < 0)
1048	{
1049		bin += mNumBins;
1050	}
1051
1052	if (bin == mNextBin)
1053	{
1054		// Bogus for bin we're currently working on.
1055		return 0.f;
1056	}
1057	return mBins[bin];
1058}
1059
1060F32 LLStat::getPrevPerSec(S32 age) const
1061{
1062	S32 bin;
1063	bin = mCurBin - age;
1064
1065	while (bin < 0)
1066	{
1067		bin += mNumBins;
1068	}
1069
1070	if (bin == mNextBin)
1071	{
1072		// Bogus for bin we're currently working on.
1073		return 0.f;
1074	}
1075	return mBins[bin] / mDT[bin];
1076}
1077
1078F64 LLStat::getPrevBeginTime(S32 age) const
1079{
1080	S32 bin;
1081	bin = mCurBin - age;
1082
1083	while (bin < 0)
1084	{
1085		bin += mNumBins;
1086	}
1087
1088	if (bin == mNextBin)
1089	{
1090		// Bogus for bin we're currently working on.
1091		return 0.f;
1092	}
1093
1094	return mBeginTime[bin];
1095}
1096
1097F64 LLStat::getPrevTime(S32 age) const
1098{
1099	S32 bin;
1100	bin = mCurBin - age;
1101
1102	while (bin < 0)
1103	{
1104		bin += mNumBins;
1105	}
1106
1107	if (bin == mNextBin)
1108	{
1109		// Bogus for bin we're currently working on.
1110		return 0.f;
1111	}
1112
1113	return mTime[bin];
1114}
1115
1116F32 LLStat::getBin(S32 bin) const
1117{
1118	return mBins[bin];
1119}
1120
1121F32 LLStat::getBinPerSec(S32 bin) const
1122{
1123	return mBins[bin] / mDT[bin];
1124}
1125
1126F64 LLStat::getBinBeginTime(S32 bin) const
1127{
1128	return mBeginTime[bin];
1129}
1130
1131F64 LLStat::getBinTime(S32 bin) const
1132{
1133	return mTime[bin];
1134}
1135
1136F32 LLStat::getCurrent() const
1137{
1138	return mBins[mCurBin];
1139}
1140
1141F32 LLStat::getCurrentPerSec() const
1142{
1143	return mBins[mCurBin] / mDT[mCurBin];
1144}
1145
1146F64 LLStat::getCurrentBeginTime() const
1147{
1148	return mBeginTime[mCurBin];
1149}
1150
1151F64 LLStat::getCurrentTime() const
1152{
1153	return mTime[mCurBin];
1154}
1155
1156F32 LLStat::getCurrentDuration() const
1157{
1158	return mDT[mCurBin];
1159}
1160
1161F32 LLStat::getMeanPerSec() const
1162{
1163	U32 i;
1164	F32 value = 0.f;
1165	F32 dt    = 0.f;
1166
1167	for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
1168	{
1169		// Skip the bin we're currently filling.
1170		if (i == (U32)mNextBin)
1171		{
1172			continue;
1173		}
1174		value += mBins[i];
1175		dt    += mDT[i];
1176	}
1177
1178	if (dt > 0.f)
1179	{
1180		return value/dt;
1181	}
1182	else
1183	{
1184		return 0.f;
1185	}
1186}
1187
1188F32 LLStat::getMeanDuration() const
1189{
1190	F32 dur = 0.0f;
1191	U32 count = 0;
1192	for (U32 i=0; (i < mNumBins) && (i < mNumValues); i++)
1193	{
1194		if (i == (U32)mNextBin)
1195		{
1196			continue;
1197		}
1198		dur += mDT[i];
1199		count++;
1200	}
1201
1202	if (count > 0)
1203	{
1204		dur /= F32(count);
1205		return dur;
1206	}
1207	else
1208	{
1209		return 0.f;
1210	}
1211}
1212
1213F32 LLStat::getMaxPerSec() const
1214{
1215	U32 i;
1216	F32 value;
1217
1218	if (mNextBin != 0)
1219	{
1220		value = mBins[0]/mDT[0];
1221	}
1222	else if (mNumValues > 0)
1223	{
1224		value = mBins[1]/mDT[1];
1225	}
1226	else
1227	{
1228		value = 0.f;
1229	}
1230
1231	for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
1232	{
1233		// Skip the bin we're currently filling.
1234		if (i == (U32)mNextBin)
1235		{
1236			continue;
1237		}
1238		value = llmax(value, mBins[i]/mDT[i]);
1239	}
1240	return value;
1241}
1242
1243F32 LLStat::getMinPerSec() const
1244{
1245	U32 i;
1246	F32 value;
1247	
1248	if (mNextBin != 0)
1249	{
1250		value = mBins[0]/mDT[0];
1251	}
1252	else if (mNumValues > 0)
1253	{
1254		value = mBins[1]/mDT[1];
1255	}
1256	else
1257	{
1258		value = 0.f;
1259	}
1260
1261	for (i = 0; (i < mNumBins) && (i < mNumValues); i++)
1262	{
1263		// Skip the bin we're currently filling.
1264		if (i == (U32)mNextBin)
1265		{
1266			continue;
1267		}
1268		value = llmin(value, mBins[i]/mDT[i]);
1269	}
1270	return value;
1271}
1272
1273F32 LLStat::getMinDuration() const
1274{
1275	F32 dur = 0.0f;
1276	for (U32 i=0; (i < mNumBins) && (i < mNumValues); i++)
1277	{
1278		dur = llmin(dur, mDT[i]);
1279	}
1280	return dur;
1281}
1282
1283U32 LLStat::getNumValues() const
1284{
1285	return mNumValues;
1286}
1287
1288S32 LLStat::getNumBins() const
1289{
1290	return mNumBins;
1291}
1292
1293S32 LLStat::getCurBin() const
1294{
1295	return mCurBin;
1296}
1297
1298S32 LLStat::getNextBin() const
1299{
1300	return mNextBin;
1301}
1302
1303F64 LLStat::getLastTime() const
1304{
1305	return mLastTime;
1306}