summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorDon Kjer <don@lindenlab.com>2008-10-09 18:07:46 +0000
committerDon Kjer <don@lindenlab.com>2008-10-09 18:07:46 +0000
commit4ff16b735f59326514ad92ec38e3261cd996e05c (patch)
tree170416c912dc272e7e171f156494946e05444e55 /indra/llcommon
parentb807e3df990e6fad25cd0bca94d2959dac042b13 (diff)
QAR-907: svn merge -r 98908:98910 svn+ssh://svn/svn/linden/qa/sim-metrics/sim-metrics2-release-merge-98903 into release
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/llstat.cpp449
-rw-r--r--indra/llcommon/llstat.h113
-rw-r--r--indra/llcommon/llstatenums.h17
-rw-r--r--indra/llcommon/lltimer.cpp53
-rw-r--r--indra/llcommon/lltimer.h1
5 files changed, 594 insertions, 39 deletions
diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp
index 80492d2e31..c825b50365 100644
--- a/indra/llcommon/llstat.cpp
+++ b/indra/llcommon/llstat.cpp
@@ -31,17 +31,288 @@
#include "linden_common.h"
#include "llstat.h"
+#include "lllivefile.h"
+#include "llerrorcontrol.h"
#include "llframetimer.h"
#include "timing.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llstl.h"
+#include "u64.h"
+// statics
+BOOL LLPerfBlock::sStatsEnabled = FALSE; // Flag for detailed information
+LLPerfBlock::stat_map_t LLPerfBlock::sStatMap; // Map full path string to LLStatTime objects, tracks all active objects
+std::string LLPerfBlock::sCurrentStatPath = ""; // Something like "/total_time/physics/physics step"
-U64 LLStatAccum::sScaleTimes[IMPL_NUM_SCALES] =
+//------------------------------------------------------------------------
+// Live config file to trigger stats logging
+static const char STATS_CONFIG_FILE_NAME[] = "/dev/shm/simperf/simperf_proc_config.llsd";
+static const F32 STATS_CONFIG_REFRESH_RATE = 5.0; // seconds
+
+class LLStatsConfigFile : public LLLiveFile
+{
+public:
+ LLStatsConfigFile()
+ : LLLiveFile(filename(), STATS_CONFIG_REFRESH_RATE),
+ mChanged(false), mStatsp(NULL) { }
+
+ static std::string filename();
+
+protected:
+ /* virtual */ void loadFile();
+
+public:
+ void init(LLPerfStats* statsp);
+ static LLStatsConfigFile& instance();
+ // return the singleton stats config file
+
+ bool mChanged;
+
+protected:
+ LLPerfStats* mStatsp;
+};
+
+std::string LLStatsConfigFile::filename()
+{
+ return STATS_CONFIG_FILE_NAME;
+}
+
+void LLStatsConfigFile::init(LLPerfStats* statsp)
+{
+ mStatsp = statsp;
+}
+
+LLStatsConfigFile& LLStatsConfigFile::instance()
+{
+ static LLStatsConfigFile the_file;
+ return the_file;
+}
+
+
+/* virtual */
+// Load and parse the stats configuration file
+void LLStatsConfigFile::loadFile()
+{
+ if (!mStatsp)
+ {
+ llwarns << "Tries to load performance configure file without initializing LPerfStats" << llendl;
+ return;
+ }
+ mChanged = true;
+
+ LLSD stats_config;
+ {
+ llifstream file(filename().c_str());
+ if (file.is_open())
+ {
+ LLSDSerialize::fromXML(stats_config, file);
+ if (stats_config.isUndefined())
+ {
+ llinfos << "Performance statistics configuration file ill-formed, not recording statistics" << llendl;
+ mStatsp->setReportPerformanceDuration( 0.f );
+ return;
+ }
+ }
+ else
+ { // File went away, turn off stats if it was on
+ if ( mStatsp->frameStatsIsRunning() )
+ {
+ llinfos << "Performance statistics configuration file deleted, not recording statistics" << llendl;
+ mStatsp->setReportPerformanceDuration( 0.f );
+ }
+ return;
+ }
+ }
+
+ F32 duration = 0.f;
+ F32 interval = 0.f;
+
+ const char * w = "duration";
+ if (stats_config.has(w))
+ {
+ duration = (F32)stats_config[w].asReal();
+ }
+ w = "interval";
+ if (stats_config.has(w))
+ {
+ interval = (F32)stats_config[w].asReal();
+ }
+
+ mStatsp->setReportPerformanceDuration( duration );
+ mStatsp->setReportPerformanceInterval( interval );
+
+ if ( duration > 0 )
+ {
+ if ( interval == 0.f )
+ {
+ llinfos << "Recording performance stats every frame for " << duration << " sec" << llendl;
+ }
+ else
+ {
+ llinfos << "Recording performance stats every " << interval << " seconds for " << duration << " seconds" << llendl;
+ }
+ }
+ else
+ {
+ llinfos << "Performance stats recording turned off" << llendl;
+ }
+}
+
+
+//------------------------------------------------------------------------
+
+LLPerfStats::LLPerfStats(const std::string& process_name, S32 process_pid) :
+ mFrameStatsFileFailure(FALSE),
+ mSkipFirstFrameStats(FALSE),
+ mProcessName(process_name),
+ mProcessPID(process_pid),
+ mReportPerformanceStatInterval(1.f),
+ mReportPerformanceStatEnd(0.0)
+{ }
+
+LLPerfStats::~LLPerfStats()
+{
+ LLPerfBlock::clearDynamicStats();
+ mFrameStatsFile.close();
+}
+
+void LLPerfStats::init()
+{
+ // Initialize the stats config file instance.
+ (void) LLStatsConfigFile::instance().init(this);
+ (void) LLStatsConfigFile::instance().checkAndReload();
+}
+
+// Open file for statistics
+void LLPerfStats::openPerfStatsFile()
+{
+ if ( !mFrameStatsFile
+ && !mFrameStatsFileFailure )
+ {
+ std::string stats_file = llformat("/dev/shm/simperf/%s_proc.%d.llsd", mProcessName.c_str(), mProcessPID);
+ mFrameStatsFile.close();
+ mFrameStatsFile.clear();
+ mFrameStatsFile.open(stats_file, llofstream::out);
+ if ( mFrameStatsFile.fail() )
+ {
+ llinfos << "Error opening statistics log file " << stats_file << llendl;
+ mFrameStatsFileFailure = TRUE;
+ }
+ else
+ {
+ LLSD process_info = LLSD::emptyMap();
+ process_info["name"] = mProcessName;
+ process_info["pid"] = (LLSD::Integer) mProcessPID;
+ process_info["stat_rate"] = (LLSD::Integer) mReportPerformanceStatInterval;
+ // Add process-specific info.
+ addProcessHeaderInfo(process_info);
+
+ mFrameStatsFile << LLSDNotationStreamer(process_info) << std::endl;
+ }
+ }
+}
+
+// Dump out performance metrics over some time interval
+void LLPerfStats::dumpIntervalPerformanceStats()
+{
+ // Ensure output file is OK
+ openPerfStatsFile();
+
+ if ( mFrameStatsFile )
+ {
+ LLSD stats = LLSD::emptyMap();
+
+ LLStatAccum::TimeScale scale;
+ if ( getReportPerformanceInterval() == 0.f )
+ {
+ scale = LLStatAccum::SCALE_PER_FRAME;
+ }
+ else if ( getReportPerformanceInterval() < 0.5f )
+ {
+ scale = LLStatAccum::SCALE_100MS;
+ }
+ else
+ {
+ scale = LLStatAccum::SCALE_SECOND;
+ }
+
+ // Write LLSD into log
+ stats["utc_time"] = (LLSD::String) LLError::utcTime();
+ stats["timestamp"] = U64_to_str((totalTime() / 1000) + (gUTCOffset * 1000)); // milliseconds since epoch
+ stats["frame_number"] = (LLSD::Integer) LLFrameTimer::getFrameCount();
+
+ // Add process-specific frame info.
+ addProcessFrameInfo(stats, scale);
+ LLPerfBlock::addStatsToLLSDandReset( stats, scale );
+
+ mFrameStatsFile << LLSDNotationStreamer(stats) << std::endl;
+ }
+}
+
+// Set length of performance stat recording
+void LLPerfStats::setReportPerformanceDuration( F32 seconds )
+{
+ if ( seconds <= 0.f )
+ {
+ mReportPerformanceStatEnd = 0.0;
+ LLPerfBlock::setStatsEnabled( FALSE );
+ mFrameStatsFile.close();
+ LLPerfBlock::clearDynamicStats();
+ }
+ else
+ {
+ mReportPerformanceStatEnd = LLFrameTimer::getElapsedSeconds() + ((F64) seconds);
+ // Clear failure flag to try and create the log file once
+ mFrameStatsFileFailure = FALSE;
+ LLPerfBlock::setStatsEnabled( TRUE );
+ mSkipFirstFrameStats = TRUE; // Skip the first report (at the end of this frame)
+ }
+}
+
+void LLPerfStats::updatePerFrameStats()
+{
+ (void) LLStatsConfigFile::instance().checkAndReload();
+ static LLFrameTimer performance_stats_timer;
+ if ( frameStatsIsRunning() )
+ {
+ if ( mReportPerformanceStatInterval == 0 )
+ { // Record info every frame
+ if ( mSkipFirstFrameStats )
+ { // Skip the first time - was started this frame
+ mSkipFirstFrameStats = FALSE;
+ }
+ else
+ {
+ dumpIntervalPerformanceStats();
+ }
+ }
+ else
+ {
+ performance_stats_timer.setTimerExpirySec( getReportPerformanceInterval() );
+ if (performance_stats_timer.checkExpirationAndReset( mReportPerformanceStatInterval ))
+ {
+ dumpIntervalPerformanceStats();
+ }
+ }
+
+ if ( LLFrameTimer::getElapsedSeconds() > mReportPerformanceStatEnd )
+ { // Reached end of time, clear it to stop reporting
+ setReportPerformanceDuration(0.f); // Don't set mReportPerformanceStatEnd directly
+ llinfos << "Recording performance stats completed" << llendl;
+ }
+ }
+}
+
+
+//------------------------------------------------------------------------
+
+U64 LLStatAccum::sScaleTimes[NUM_SCALES] =
{
USEC_PER_SEC / 10, // 100 millisec
USEC_PER_SEC * 1, // seconds
USEC_PER_SEC * 60, // minutes
- USEC_PER_SEC * 60 * 2 // two minutes
#if ENABLE_LONG_TIME_STATS
// enable these when more time scales are desired
USEC_PER_SEC * 60*60, // hours
@@ -71,7 +342,7 @@ void LLStatAccum::reset(U64 when)
mRunning = TRUE;
mLastTime = when;
- for (int i = 0; i < IMPL_NUM_SCALES; ++i)
+ for (int i = 0; i < NUM_SCALES; ++i)
{
mBuckets[i].accum = 0.0;
mBuckets[i].endTime = when + sScaleTimes[i];
@@ -104,7 +375,7 @@ void LLStatAccum::sum(F64 value, U64 when)
// how long is this value for
U64 timeSpan = when - mLastTime;
- for (int i = 0; i < IMPL_NUM_SCALES; ++i)
+ for (int i = 0; i < NUM_SCALES; ++i)
{
Bucket& bucket = mBuckets[i];
@@ -150,7 +421,12 @@ F32 LLStatAccum::meanValue(TimeScale scale) const
{
return 0.0;
}
- if (scale < 0 || scale >= IMPL_NUM_SCALES)
+ if ( scale == SCALE_PER_FRAME )
+ { // Per-frame not supported here
+ scale = SCALE_100MS;
+ }
+
+ if (scale < 0 || scale >= NUM_SCALES)
{
llwarns << "llStatAccum::meanValue called for unsupported scale: "
<< scale << llendl;
@@ -260,25 +536,34 @@ void LLStatMeasure::sample(F64 value)
// ------------------------------------------------------------------------
-LLStatTime::LLStatTime(bool use_frame_timer)
- : LLStatAccum(use_frame_timer),
- mFrameNumber(0),
- mTotalTimeInFrame(0)
+LLStatTime::LLStatTime(const std::string & key)
+ : LLStatAccum(false),
+ mFrameNumber(LLFrameTimer::getFrameCount()),
+ mTotalTimeInFrame(0),
+ mKey(key)
+#if LL_DEBUG
+ , mRunning(FALSE)
+#endif
{
- mFrameNumber = LLFrameTimer::getFrameCount();
}
void LLStatTime::start()
{
// Reset frame accumluation if the frame number has changed
U32 frame_number = LLFrameTimer::getFrameCount();
- if ( frame_number != mFrameNumber)
+ if ( frame_number != mFrameNumber )
{
mFrameNumber = frame_number;
mTotalTimeInFrame = 0;
}
sum(0.0);
+
+#if LL_DEBUG
+ // Shouldn't be running already
+ llassert( !mRunning );
+ mRunning = TRUE;
+#endif
}
void LLStatTime::stop()
@@ -286,7 +571,149 @@ void LLStatTime::stop()
U64 end_time = getCurrentUsecs();
U64 duration = end_time - mLastTime;
sum(F64(duration), end_time);
+ //llinfos << "mTotalTimeInFrame incremented from " << mTotalTimeInFrame << " to " << (mTotalTimeInFrame + duration) << llendl;
mTotalTimeInFrame += duration;
+
+#if LL_DEBUG
+ mRunning = FALSE;
+#endif
+}
+
+/* virtual */ F32 LLStatTime::meanValue(TimeScale scale) const
+{
+ if ( LLStatAccum::SCALE_PER_FRAME == scale )
+ {
+ return mTotalTimeInFrame;
+ }
+ else
+ {
+ return LLStatAccum::meanValue(scale);
+ }
+}
+
+
+// ------------------------------------------------------------------------
+
+
+// Use this constructor for pre-defined LLStatTime objects
+LLPerfBlock::LLPerfBlock(LLStatTime* stat ) : mPredefinedStat(stat), mDynamicStat(NULL)
+{
+ if (mPredefinedStat)
+ {
+ // If dynamic stats are turned on, this will create a separate entry in the stat map.
+ initDynamicStat(mPredefinedStat->mKey);
+
+ // Start predefined stats. These stats are not part of the stat map.
+ mPredefinedStat->start();
+ }
+}
+
+// Use this constructor for dynamically created LLStatTime objects (not pre-defined) with a multi-part key.
+// These are also turned on or off via the switch passed in
+LLPerfBlock::LLPerfBlock( const char* key1, const char* key2 ) : mPredefinedStat(NULL), mDynamicStat(NULL)
+{
+ if (!sStatsEnabled) return;
+
+ if (NULL == key2 || strlen(key2) == 0)
+ {
+ initDynamicStat(key1);
+ }
+ else
+ {
+ std::ostringstream key;
+ key << key1 << "_" << key2;
+ initDynamicStat(key.str());
+ }
+}
+
+void LLPerfBlock::initDynamicStat(const std::string& key)
+{
+ // Early exit if dynamic stats aren't enabled.
+ if (!sStatsEnabled) return;
+
+ mLastPath = sCurrentStatPath; // Save and restore current path
+ sCurrentStatPath += "/" + key; // Add key to current path
+
+ // See if the LLStatTime object already exists
+ stat_map_t::iterator iter = sStatMap.find(sCurrentStatPath);
+ if ( iter == sStatMap.end() )
+ {
+ // StatEntry object doesn't exist, so create it
+ mDynamicStat = new StatEntry( key );
+ sStatMap[ sCurrentStatPath ] = mDynamicStat; // Set the entry for this path
+ }
+ else
+ {
+ // Found this path in the map, use the object there
+ mDynamicStat = (*iter).second; // Get StatEntry for the current path
+ }
+
+ if (mDynamicStat)
+ {
+ mDynamicStat->mStat.start();
+ mDynamicStat->mCount++;
+ }
+ else
+ {
+ llwarns << "Initialized NULL dynamic stat at '" << sCurrentStatPath << "'" << llendl;
+ sCurrentStatPath = mLastPath;
+ }
+}
+
+
+// Destructor does the time accounting
+LLPerfBlock::~LLPerfBlock()
+{
+ if (mPredefinedStat) mPredefinedStat->stop();
+ if (mDynamicStat)
+ {
+ mDynamicStat->mStat.stop();
+ sCurrentStatPath = mLastPath; // Restore the path in case sStatsEnabled changed during this block
+ }
+}
+
+
+// Clear the map of any dynamic stats. Static routine
+void LLPerfBlock::clearDynamicStats()
+{
+ std::for_each(sStatMap.begin(), sStatMap.end(), DeletePairedPointer());
+ sStatMap.clear();
+}
+
+// static - Extract the stat info into LLSD
+void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats,
+ LLStatAccum::TimeScale scale )
+{
+ // If we aren't in per-frame scale, we need to go from second to microsecond.
+ U32 scale_adjustment = 1;
+ if (LLStatAccum::SCALE_PER_FRAME != scale)
+ {
+ scale_adjustment = USEC_PER_SEC;
+ }
+ stat_map_t::iterator iter = sStatMap.begin();
+ for ( ; iter != sStatMap.end(); ++iter )
+ { // Put the entry into LLSD "/full/path/to/stat/" = microsecond total time
+ const std::string & stats_full_path = (*iter).first;
+
+ StatEntry * stat = (*iter).second;
+ if (stat)
+ {
+ if (stat->mCount > 0)
+ {
+ stats[stats_full_path] = LLSD::emptyMap();
+ stats[stats_full_path]["us"] = (LLSD::Integer) (scale_adjustment * stat->mStat.meanValue(scale));
+ if (stat->mCount > 1)
+ {
+ stats[stats_full_path]["count"] = (LLSD::Integer) stat->mCount;
+ }
+ stat->mCount = 0;
+ }
+ }
+ else
+ { // WTF? Shouldn't have a NULL pointer in the map.
+ llwarns << "Unexpected NULL dynamic stat at '" << stats_full_path << "'" << llendl;
+ }
+ }
}
diff --git a/indra/llcommon/llstat.h b/indra/llcommon/llstat.h
index d4dcb3a961..a590d75120 100644
--- a/indra/llcommon/llstat.h
+++ b/indra/llcommon/llstat.h
@@ -33,9 +33,13 @@
#define LL_LLSTAT_H
#include <deque>
+#include <map>
#include "lltimer.h"
#include "llframetimer.h"
+#include "llfile.h"
+
+class LLSD;
// Set this if longer stats are needed
#define ENABLE_LONG_TIME_STATS 0
@@ -58,19 +62,18 @@ public:
SCALE_100MS,
SCALE_SECOND,
SCALE_MINUTE,
- SCALE_TWO_MINUTE,
#if ENABLE_LONG_TIME_STATS
SCALE_HOUR,
SCALE_DAY,
SCALE_WEEK,
#endif
- NUM_SCALES
+ NUM_SCALES, // Use to size storage arrays
+ SCALE_PER_FRAME // For latest frame information - should be after NUM_SCALES since this doesn't go into the time buckets
};
- static const TimeScale IMPL_NUM_SCALES = (TimeScale)(SCALE_TWO_MINUTE + 1);
- static U64 sScaleTimes[IMPL_NUM_SCALES];
+ static U64 sScaleTimes[NUM_SCALES];
- F32 meanValue(TimeScale scale) const;
+ virtual F32 meanValue(TimeScale scale) const;
// see the subclasses for the specific meaning of value
F32 meanValueOverLast100ms() const { return meanValue(SCALE_100MS); }
@@ -86,8 +89,8 @@ public:
// Get current microseconds based on timer type
BOOL mUseFrameTimer;
-
BOOL mRunning;
+
U64 mLastTime;
struct Bucket
@@ -99,7 +102,7 @@ public:
F64 lastAccum;
};
- Bucket mBuckets[IMPL_NUM_SCALES];
+ Bucket mBuckets[NUM_SCALES];
BOOL mLastSampleValid;
F64 mLastSampleValue;
@@ -136,37 +139,115 @@ public:
};
-class LLTimeBlock;
-
class LLStatTime : public LLStatAccum
// gathers statistics about time spent in a block of code
// measure average duration per second in the block
{
public:
- LLStatTime(bool use_frame_timer = false);
+ LLStatTime( const std::string & key = "undefined" );
U32 mFrameNumber; // Current frame number
U64 mTotalTimeInFrame; // Total time (microseconds) accumulated during the last frame
+ void setKey( const std::string & key ) { mKey = key; };
+
+ virtual F32 meanValue(TimeScale scale) const;
+
private:
- void start();
+ void start(); // Start and stop measuring time block
void stop();
- friend class LLTimeBlock;
+
+ std::string mKey; // Tag representing this time block
+
+#if LL_DEBUG
+ BOOL mRunning; // TRUE if start() has been called
+#endif
+
+ friend class LLPerfBlock;
};
-class LLTimeBlock
+// ----------------------------------------------------------------------------
+
+
+// Use this class on the stack to record statistics about an area of code
+class LLPerfBlock
{
public:
- LLTimeBlock(LLStatTime& stat) : mStat(stat) { mStat.start(); }
- ~LLTimeBlock() { mStat.stop(); }
+ struct StatEntry
+ {
+ StatEntry(const std::string& key) : mStat(LLStatTime(key)), mCount(0) {}
+ LLStatTime mStat;
+ U32 mCount;
+ };
+ typedef std::map<std::string, StatEntry*> stat_map_t;
+
+ // Use this constructor for pre-defined LLStatTime objects
+ LLPerfBlock(LLStatTime* stat);
+
+ // Use this constructor for dynamically created LLStatTime objects (not pre-defined) with a multi-part key
+ LLPerfBlock( const char* key1, const char* key2 = NULL);
+
+
+ ~LLPerfBlock();
+
+ static void setStatsEnabled( BOOL enable ) { sStatsEnabled = enable; };
+ static S32 getStatsEnabled() { return sStatsEnabled; };
+
+ static void clearDynamicStats(); // Reset maps to clear out dynamic objects
+ static void addStatsToLLSDandReset( LLSD & stats, // Get current information and clear time bin
+ LLStatAccum::TimeScale scale );
+
private:
- LLStatTime& mStat;
+ // Initialize dynamically created LLStatTime objects
+ void initDynamicStat(const std::string& key);
+
+ std::string mLastPath; // Save sCurrentStatPath when this is called
+ LLStatTime * mPredefinedStat; // LLStatTime object to get data
+ StatEntry * mDynamicStat; // StatEntryobject to get data
+
+ static BOOL sStatsEnabled; // Normally FALSE
+ static stat_map_t sStatMap; // Map full path string to LLStatTime objects
+ static std::string sCurrentStatPath; // Something like "frame/physics/physics step"
};
+// ----------------------------------------------------------------------------
+class LLPerfStats
+{
+public:
+ LLPerfStats(const std::string& process_name = "unknown", S32 process_pid = 0);
+ virtual ~LLPerfStats();
+
+ virtual void init(); // Reset and start all stat timers
+ virtual void updatePerFrameStats();
+ // Override these function to add process-specific information to the performance log header and per-frame logging.
+ virtual void addProcessHeaderInfo(LLSD& info) { /* not implemented */ }
+ virtual void addProcessFrameInfo(LLSD& info, LLStatAccum::TimeScale scale) { /* not implemented */ }
+
+ // High-resolution frame stats
+ BOOL frameStatsIsRunning() { return (mReportPerformanceStatEnd > 0.); };
+ F32 getReportPerformanceInterval() const { return mReportPerformanceStatInterval; };
+ void setReportPerformanceInterval( F32 interval ) { mReportPerformanceStatInterval = interval; };
+ void setReportPerformanceDuration( F32 seconds );
+ void setProcessName(const std::string& process_name) { mProcessName = process_name; }
+ void setProcessPID(S32 process_pid) { mProcessPID = process_pid; }
+
+protected:
+ void openPerfStatsFile(); // Open file for high resolution metrics logging
+ void dumpIntervalPerformanceStats();
+ llofstream mFrameStatsFile; // File for per-frame stats
+ BOOL mFrameStatsFileFailure; // Flag to prevent repeat opening attempts
+ BOOL mSkipFirstFrameStats; // Flag to skip one (partial) frame report
+ std::string mProcessName;
+ S32 mProcessPID;
+private:
+ F32 mReportPerformanceStatInterval; // Seconds between performance stats
+ F64 mReportPerformanceStatEnd; // End time (seconds) for performance stats
+};
+// ----------------------------------------------------------------------------
class LLStat
{
public:
diff --git a/indra/llcommon/llstatenums.h b/indra/llcommon/llstatenums.h
index 45c4fa986b..fd175d4430 100644
--- a/indra/llcommon/llstatenums.h
+++ b/indra/llcommon/llstatenums.h
@@ -33,38 +33,41 @@
enum
{
- LL_SIM_STAT_TIME_DILATION,
+ LL_SIM_STAT_TIME_DILATION, // 0
LL_SIM_STAT_FPS,
LL_SIM_STAT_PHYSFPS,
LL_SIM_STAT_AGENTUPS,
LL_SIM_STAT_FRAMEMS,
- LL_SIM_STAT_NETMS,
+ LL_SIM_STAT_NETMS, // 5
LL_SIM_STAT_SIMOTHERMS,
LL_SIM_STAT_SIMPHYSICSMS,
LL_SIM_STAT_AGENTMS,
LL_SIM_STAT_IMAGESMS,
- LL_SIM_STAT_SCRIPTMS,
+ LL_SIM_STAT_SCRIPTMS, // 10
LL_SIM_STAT_NUMTASKS,
LL_SIM_STAT_NUMTASKSACTIVE,
LL_SIM_STAT_NUMAGENTMAIN,
LL_SIM_STAT_NUMAGENTCHILD,
- LL_SIM_STAT_NUMSCRIPTSACTIVE,
+ LL_SIM_STAT_NUMSCRIPTSACTIVE, // 15
LL_SIM_STAT_LSLIPS,
LL_SIM_STAT_INPPS,
LL_SIM_STAT_OUTPPS,
LL_SIM_STAT_PENDING_DOWNLOADS,
- LL_SIM_STAT_PENDING_UPLOADS,
+ LL_SIM_STAT_PENDING_UPLOADS, // 20
LL_SIM_STAT_VIRTUAL_SIZE_KB,
LL_SIM_STAT_RESIDENT_SIZE_KB,
LL_SIM_STAT_PENDING_LOCAL_UPLOADS,
LL_SIM_STAT_TOTAL_UNACKED_BYTES,
- LL_SIM_STAT_PHYSICS_PINNED_TASKS,
+ LL_SIM_STAT_PHYSICS_PINNED_TASKS, // 25
LL_SIM_STAT_PHYSICS_LOD_TASKS,
LL_SIM_STAT_SIMPHYSICSSTEPMS,
LL_SIM_STAT_SIMPHYSICSSHAPEMS,
LL_SIM_STAT_SIMPHYSICSOTHERMS,
- LL_SIM_STAT_SIMPHYSICSMEMORY,
+ LL_SIM_STAT_SIMPHYSICSMEMORY, // 30
LL_SIM_STAT_SCRIPT_EPS,
+ LL_SIM_STAT_SIMSPARETIME,
+ LL_SIM_STAT_SIMSLEEPTIME,
+ LL_SIM_STAT_IOPUMPTIME,
};
#endif
diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp
index 7570420d2c..2c34608064 100644
--- a/indra/llcommon/lltimer.cpp
+++ b/indra/llcommon/lltimer.cpp
@@ -83,15 +83,20 @@ void ms_sleep(U32 ms)
{
Sleep(ms);
}
+
+U32 micro_sleep(U64 us, U32 max_yields)
+{
+ // max_yields is unused; just fiddle with it to avoid warnings.
+ max_yields = 0;
+ ms_sleep(us / 1000);
+ return 0;
+}
#elif LL_LINUX || LL_SOLARIS || LL_DARWIN
-void ms_sleep(U32 ms)
+static void _sleep_loop(struct timespec& thiswait)
{
- long mslong = ms; // tv_nsec is a long
- struct timespec thiswait, nextwait;
+ struct timespec nextwait;
bool sleep_more = false;
- thiswait.tv_sec = ms / 1000;
- thiswait.tv_nsec = (mslong % 1000) * 1000000l;
do {
int result = nanosleep(&thiswait, &nextwait);
@@ -127,6 +132,44 @@ void ms_sleep(U32 ms)
}
} while (sleep_more);
}
+
+U32 micro_sleep(U64 us, U32 max_yields)
+{
+ U64 start = get_clock_count();
+ // This is kernel dependent. Currently, our kernel generates software clock
+ // interrupts at 250 Hz (every 4,000 microseconds).
+ const U64 KERNEL_SLEEP_INTERVAL_US = 4000;
+
+ S32 num_sleep_intervals = (us - (KERNEL_SLEEP_INTERVAL_US >> 1)) / KERNEL_SLEEP_INTERVAL_US;
+ if (num_sleep_intervals > 0)
+ {
+ U64 sleep_time = (num_sleep_intervals * KERNEL_SLEEP_INTERVAL_US) - (KERNEL_SLEEP_INTERVAL_US >> 1);
+ struct timespec thiswait;
+ thiswait.tv_sec = sleep_time / 1000000;
+ thiswait.tv_nsec = (sleep_time % 1000000) * 1000l;
+ _sleep_loop(thiswait);
+ }
+
+ U64 current_clock = get_clock_count();
+ U32 yields = 0;
+ while ( (yields < max_yields)
+ && (current_clock - start < us) )
+ {
+ sched_yield();
+ ++yields;
+ current_clock = get_clock_count();
+ }
+ return yields;
+}
+
+void ms_sleep(U32 ms)
+{
+ long mslong = ms; // tv_nsec is a long
+ struct timespec thiswait;
+ thiswait.tv_sec = ms / 1000;
+ thiswait.tv_nsec = (mslong % 1000) * 1000000l;
+ _sleep_loop(thiswait);
+}
#else
# error "architecture not supported"
#endif
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
index ea84f8ea2c..1916d67fda 100644
--- a/indra/llcommon/lltimer.h
+++ b/indra/llcommon/lltimer.h
@@ -117,6 +117,7 @@ void update_clock_frequencies();
// Sleep for milliseconds
void ms_sleep(U32 ms);
+U32 micro_sleep(U64 us, U32 max_yields = 0xFFFFFFFF);
// Returns the correct UTC time in seconds, like time(NULL).
// Useful on the viewer, which may have its local clock set wrong.