/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the OpenSimulator Project nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections; using System.Collections.Generic; using System.Reflection; using log4net; using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Monitoring; using OpenSim.Framework.Servers; using OpenSim.Region.CoreModules.Framework.Monitoring.Alerts; using OpenSim.Region.CoreModules.Framework.Monitoring.Monitors; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using Mono.Addins; namespace OpenSim.Region.CoreModules.Framework.Monitoring { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MonitorModule")] public class MonitorModule : INonSharedRegionModule { /// /// Is this module enabled? /// public bool Enabled { get; private set; } private Scene m_scene; /// /// These are monitors where we know the static details in advance. /// /// /// Dynamic monitors also exist (we don't know any of the details of what stats we get back here) /// but these are currently hardcoded. /// private readonly List m_staticMonitors = new List(); private readonly List m_alerts = new List(); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); public MonitorModule() { Enabled = true; } #region Implementation of INonSharedRegionModule public void Initialise(IConfigSource source) { IConfig cnfg = source.Configs["Monitoring"]; if (cnfg != null) Enabled = cnfg.GetBoolean("Enabled", true); if (!Enabled) return; } public void AddRegion(Scene scene) { if (!Enabled) return; m_scene = scene; m_scene.AddCommand("General", this, "monitor report", "monitor report", "Returns a variety of statistics about the current region and/or simulator", DebugMonitors); MainServer.Instance.AddHTTPHandler("/monitorstats/" + m_scene.RegionInfo.RegionID, StatsPage); MainServer.Instance.AddHTTPHandler( "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName), StatsPage); AddMonitors(); RegisterStatsManagerRegionStatistics(); } public void RemoveRegion(Scene scene) { if (!Enabled) return; MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + m_scene.RegionInfo.RegionID); MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName)); UnRegisterStatsManagerRegionStatistics(); m_scene = null; } public void Close() { } public string Name { get { return "Region Health Monitoring Module"; } } public void RegionLoaded(Scene scene) { } public Type ReplaceableInterface { get { return null; } } #endregion public void AddMonitors() { m_staticMonitors.Add(new AgentCountMonitor(m_scene)); m_staticMonitors.Add(new ChildAgentCountMonitor(m_scene)); m_staticMonitors.Add(new GCMemoryMonitor()); m_staticMonitors.Add(new ObjectCountMonitor(m_scene)); m_staticMonitors.Add(new PhysicsFrameMonitor(m_scene)); m_staticMonitors.Add(new PhysicsUpdateFrameMonitor(m_scene)); m_staticMonitors.Add(new PWSMemoryMonitor()); m_staticMonitors.Add(new ThreadCountMonitor()); m_staticMonitors.Add(new TotalFrameMonitor(m_scene)); m_staticMonitors.Add(new EventFrameMonitor(m_scene)); m_staticMonitors.Add(new LandFrameMonitor(m_scene)); m_staticMonitors.Add(new LastFrameTimeMonitor(m_scene)); m_staticMonitors.Add( new GenericMonitor( m_scene, "TimeDilationMonitor", "Time Dilation", m => m.Scene.StatsReporter.LastReportedSimStats[0], m => m.GetValue().ToString())); m_staticMonitors.Add( new GenericMonitor( m_scene, "SimFPSMonitor", "Sim FPS", m => m.Scene.StatsReporter.LastReportedSimStats[1], m => string.Format("{0}", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "PhysicsFPSMonitor", "Physics FPS", m => m.Scene.StatsReporter.LastReportedSimStats[2], m => string.Format("{0}", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "AgentUpdatesPerSecondMonitor", "Agent Updates", m => m.Scene.StatsReporter.LastReportedSimStats[3], m => string.Format("{0} per second", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "ActiveObjectCountMonitor", "Active Objects", m => m.Scene.StatsReporter.LastReportedSimStats[7], m => string.Format("{0}", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "ActiveScriptsMonitor", "Active Scripts", m => m.Scene.StatsReporter.LastReportedSimStats[19], m => string.Format("{0}", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "ScriptEventsPerSecondMonitor", "Script Events", m => m.Scene.StatsReporter.LastReportedSimStats[23], m => string.Format("{0} per second", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "InPacketsPerSecondMonitor", "In Packets", m => m.Scene.StatsReporter.LastReportedSimStats[13], m => string.Format("{0} per second", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "OutPacketsPerSecondMonitor", "Out Packets", m => m.Scene.StatsReporter.LastReportedSimStats[14], m => string.Format("{0} per second", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "UnackedBytesMonitor", "Unacked Bytes", m => m.Scene.StatsReporter.LastReportedSimStats[15], m => string.Format("{0}", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "PendingDownloadsMonitor", "Pending Downloads", m => m.Scene.StatsReporter.LastReportedSimStats[17], m => string.Format("{0}", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "PendingUploadsMonitor", "Pending Uploads", m => m.Scene.StatsReporter.LastReportedSimStats[18], m => string.Format("{0}", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "TotalFrameTimeMonitor", "Total Frame Time", m => m.Scene.StatsReporter.LastReportedSimStats[8], m => string.Format("{0} ms", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "NetFrameTimeMonitor", "Net Frame Time", m => m.Scene.StatsReporter.LastReportedSimStats[9], m => string.Format("{0} ms", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "PhysicsFrameTimeMonitor", "Physics Frame Time", m => m.Scene.StatsReporter.LastReportedSimStats[10], m => string.Format("{0} ms", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "SimulationFrameTimeMonitor", "Simulation Frame Time", m => m.Scene.StatsReporter.LastReportedSimStats[12], m => string.Format("{0} ms", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "AgentFrameTimeMonitor", "Agent Frame Time", m => m.Scene.StatsReporter.LastReportedSimStats[16], m => string.Format("{0} ms", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "ImagesFrameTimeMonitor", "Images Frame Time", m => m.Scene.StatsReporter.LastReportedSimStats[11], m => string.Format("{0} ms", m.GetValue()))); m_staticMonitors.Add( new GenericMonitor( m_scene, "SpareFrameTimeMonitor", "Spare Frame Time", m => m.Scene.StatsReporter.LastReportedSimStats[38], m => string.Format("{0} ms", m.GetValue()))); m_alerts.Add(new DeadlockAlert(m_staticMonitors.Find(x => x is LastFrameTimeMonitor) as LastFrameTimeMonitor)); foreach (IAlert alert in m_alerts) { alert.OnTriggerAlert += OnTriggerAlert; } } public void DebugMonitors(string module, string[] args) { foreach (IMonitor monitor in m_staticMonitors) { MainConsole.Instance.OutputFormat( "[MONITOR MODULE]: {0} reports {1} = {2}", m_scene.RegionInfo.RegionName, monitor.GetFriendlyName(), monitor.GetFriendlyValue()); } foreach (KeyValuePair tuple in m_scene.StatsReporter.GetExtraSimStats()) { MainConsole.Instance.OutputFormat( "[MONITOR MODULE]: {0} reports {1} = {2}", m_scene.RegionInfo.RegionName, tuple.Key, tuple.Value); } } public void TestAlerts() { foreach (IAlert alert in m_alerts) { alert.Test(); } } public Hashtable StatsPage(Hashtable request) { // If request was for a specific monitor // eg url/?monitor=Monitor.Name if (request.ContainsKey("monitor")) { string monID = (string) request["monitor"]; foreach (IMonitor monitor in m_staticMonitors) { string elemName = monitor.ToString(); if (elemName.StartsWith(monitor.GetType().Namespace)) elemName = elemName.Substring(monitor.GetType().Namespace.Length + 1); if (elemName == monID || monitor.ToString() == monID) { Hashtable ereply3 = new Hashtable(); ereply3["int_response_code"] = 404; // 200 OK ereply3["str_response_string"] = monitor.GetValue().ToString(); ereply3["content_type"] = "text/plain"; return ereply3; } } // FIXME: Arguably this should also be done with dynamic monitors but I'm not sure what the above code // is even doing. Why are we inspecting the type of the monitor??? // No monitor with that name Hashtable ereply2 = new Hashtable(); ereply2["int_response_code"] = 404; // 200 OK ereply2["str_response_string"] = "No such monitor"; ereply2["content_type"] = "text/plain"; return ereply2; } string xml = ""; foreach (IMonitor monitor in m_staticMonitors) { string elemName = monitor.GetName(); xml += "<" + elemName + ">" + monitor.GetValue().ToString() + ""; // m_log.DebugFormat("[MONITOR MODULE]: {0} = {1}", elemName, monitor.GetValue()); } foreach (KeyValuePair tuple in m_scene.StatsReporter.GetExtraSimStats()) { xml += "<" + tuple.Key + ">" + tuple.Value + ""; } xml += ""; Hashtable ereply = new Hashtable(); ereply["int_response_code"] = 200; // 200 OK ereply["str_response_string"] = xml; ereply["content_type"] = "text/xml"; return ereply; } void OnTriggerAlert(System.Type reporter, string reason, bool fatal) { m_log.Error("[Monitor] " + reporter.Name + " for " + m_scene.RegionInfo.RegionName + " reports " + reason + " (Fatal: " + fatal + ")"); } private List registeredStats = new List(); private void MakeStat(string pName, string pUnitName, Action act) { Stat tempStat = new Stat(pName, pName, pName, pUnitName, "scene", m_scene.RegionInfo.RegionName, StatType.Pull, act, StatVerbosity.Info); StatsManager.RegisterStat(tempStat); registeredStats.Add(tempStat); } private void RegisterStatsManagerRegionStatistics() { MakeStat("RootAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetRootAgentCount(); }); MakeStat("ChildAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetChildAgentCount(); }); MakeStat("TotalPrims", "objects", (s) => { s.Value = m_scene.SceneGraph.GetTotalObjectsCount(); }); MakeStat("ActivePrims", "objects", (s) => { s.Value = m_scene.SceneGraph.GetActiveObjectsCount(); }); MakeStat("ActiveScripts", "scripts", (s) => { s.Value = m_scene.SceneGraph.GetActiveScriptsCount(); }); MakeStat("TimeDilation", "sec/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[0]; }); MakeStat("SimFPS", "fps", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[1]; }); MakeStat("PhysicsFPS", "fps", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[2]; }); MakeStat("AgentUpdates", "updates/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[3]; }); MakeStat("FrameTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[8]; }); MakeStat("NetTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[9]; }); MakeStat("OtherTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[12]; }); MakeStat("PhysicsTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[10]; }); MakeStat("AgentTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[16]; }); MakeStat("ImageTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[11]; }); MakeStat("ScriptLines", "lines/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[20]; }); MakeStat("SimSpareMS", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[21]; }); } private void UnRegisterStatsManagerRegionStatistics() { foreach (Stat stat in registeredStats) { StatsManager.DeregisterStat(stat); stat.Dispose(); } registeredStats.Clear(); } } }