diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs index cdd7cc711e..8ac9090b06 100644 --- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs @@ -355,10 +355,19 @@ Asset service request failures: {3}" + Environment.NewLine, sb.Append(Environment.NewLine); sb.Append( string.Format( - "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}", + "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}\n\n", inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime, netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime)); - sb.Append(Environment.NewLine); + + foreach (KeyValuePair kvp in StatsManager.RegisteredStats) + { + Stat stat = kvp.Value; + + if (stat.Category == "scene" && stat.Verbosity == StatVerbosity.Info) + { + sb.AppendFormat("Slow frames ({0}): {1}\n", stat.Container, stat.Value); + } + } /* sb.Append(Environment.NewLine); diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index d78fa6a1f0..02df0ac106 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -25,6 +25,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; +using System.Collections.Generic; + namespace OpenSim.Framework.Monitoring { /// @@ -32,6 +35,14 @@ namespace OpenSim.Framework.Monitoring /// public class StatsManager { + /// + /// Registered stats. + /// + /// + /// Do not add or remove from this dictionary. + /// + public static Dictionary RegisteredStats = new Dictionary(); + private static AssetStatsCollector assetStats; private static UserStatsCollector userStats; private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); @@ -61,5 +72,108 @@ namespace OpenSim.Framework.Monitoring return userStats; } + + public static bool RegisterStat(Stat stat) + { + lock (RegisteredStats) + { + if (RegisteredStats.ContainsKey(stat.UniqueName)) + { + // XXX: For now just return false. This is to avoid problems in regression tests where all tests + // in a class are run in the same instance of the VM. + return false; + +// throw new Exception( +// "StatsManager already contains stat with ShortName {0} in Category {1}", stat.ShortName, stat.Category); + } + + // We take a replace-on-write approach here so that we don't need to generate a new Dictionary + Dictionary newRegisteredStats = new Dictionary(RegisteredStats); + newRegisteredStats[stat.UniqueName] = stat; + RegisteredStats = newRegisteredStats; + } + + return true; + } + + public static bool DeregisterStat(Stat stat) + { + lock (RegisteredStats) + { + if (!RegisteredStats.ContainsKey(stat.UniqueName)) + return false; + + Dictionary newRegisteredStats = new Dictionary(RegisteredStats); + newRegisteredStats.Remove(stat.UniqueName); + RegisteredStats = newRegisteredStats; + + return true; + } + } + } + + /// + /// Verbosity of stat. + /// + /// + /// Info will always be displayed. + /// + public enum StatVerbosity + { + Debug, + Info + } + + /// + /// Holds individual static details + /// + public class Stat + { + /// + /// Unique stat name used for indexing. Each ShortName in a Category must be unique. + /// + public string UniqueName { get; private set; } + + /// + /// Category of this stat (e.g. cache, scene, etc). + /// + public string Category { get; private set; } + + /// + /// Containing name for this stat. + /// FIXME: In the case of a scene, this is currently the scene name (though this leaves + /// us with a to-be-resolved problem of non-unique region names). + /// + /// + /// The container. + /// + public string Container { get; private set; } + + public StatVerbosity Verbosity { get; private set; } + public string ShortName { get; private set; } + public string Name { get; private set; } + public string Description { get; private set; } + public string UnitName { get; private set; } + + public double Value { get; set; } + + public Stat( + string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description) + { + ShortName = shortName; + Name = name; + UnitName = unitName; + Category = category; + Container = container; + Verbosity = verbosity; + Description = description; + + UniqueName = GenUniqueName(Container, Category, ShortName); + } + + public static string GenUniqueName(string container, string category, string shortName) + { + return string.Format("{0}+{1}+{2}", container, category, shortName); + } } } \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index 96317c3de3..b7b5ea21d8 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -47,6 +47,7 @@ namespace OpenSim.Region.Framework.Scenes = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public const string LastReportedObjectUpdateStatName = "LastReportedObjectUpdates"; + public const string SlowFramesStatName = "SlowFrames"; public delegate void SendStatResult(SimStats stats); @@ -128,6 +129,16 @@ namespace OpenSim.Region.Framework.Scenes get { return lastReportedSimStats; } } + /// + /// Number of frames that have taken longer to process than Scene.MIN_FRAME_TIME + /// + public Stat SlowFramesStat { get; private set; } + + /// + /// The threshold at which we log a slow frame. + /// + public int SlowFramesStatReportThreshold { get; private set; } + /// /// Extra sim statistics that are used by monitors but not sent to the client. /// @@ -225,6 +236,22 @@ namespace OpenSim.Region.Framework.Scenes if (StatsManager.SimExtraStats != null) OnSendStatsResult += StatsManager.SimExtraStats.ReceiveClassicSimStatsPacket; + + /// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit + /// longer than ideal (which in itself is a concern). + SlowFramesStatReportThreshold = (int)Math.Ceiling(m_scene.MinFrameTime * 1000 * 1.2); + + SlowFramesStat + = new Stat( + "SlowFrames", + "Slow Frames", + "frames", + "scene", + m_scene.Name, + StatVerbosity.Info, + "Number of frames where frame time has been significantly longer than the desired frame time."); + + StatsManager.RegisterStat(SlowFramesStat); } public void Close() @@ -418,6 +445,7 @@ namespace OpenSim.Region.Framework.Scenes lock (m_lastReportedExtraSimStats) { m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / m_statsUpdateFactor; + m_lastReportedExtraSimStats[SlowFramesStat.ShortName] = (float)SlowFramesStat.Value; Dictionary physicsStats = m_scene.PhysicsScene.GetStats(); @@ -535,6 +563,11 @@ namespace OpenSim.Region.Framework.Scenes public void addFrameMS(int ms) { m_frameMS += ms; + + // At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit + // longer than ideal due to the inaccuracy of the Sleep in Scene.Update() (which in itself is a concern). + if (ms > SlowFramesStatReportThreshold) + SlowFramesStat.Value++; } public void AddSpareMS(int ms)