/* * 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.Generic; using System.Collections.Concurrent; using System.Diagnostics; using System.Linq; using System.Text; using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework.Monitoring.Interfaces; namespace OpenSim.Framework.Monitoring { /// /// Collects sim statistics which aren't already being collected for the linden viewer's statistics pane /// public class SimExtraStatsCollector : BaseStatsCollector { /// /// Retain a dictionary of all packet queues stats reporters /// private readonly Dictionary packetQueueStatsCollectors = new Dictionary(); /// /// Register as a packet queue stats provider /// /// An agent UUID /// public void RegisterPacketQueueStatsProvider(UUID uuid, IPullStatsProvider provider) { lock (packetQueueStatsCollectors) { // FIXME: If the region service is providing more than one region, then the child and root agent // queues are wrongly replacing each other here. packetQueueStatsCollectors[uuid] = new PacketQueueStatsCollector(provider); } } /// /// Deregister a packet queue stats provider /// /// An agent UUID public void DeregisterPacketQueueStatsProvider(UUID uuid) { lock (packetQueueStatsCollectors) { packetQueueStatsCollectors.Remove(uuid); } } private UUID firstReceivedRegion; private readonly ConcurrentDictionary ReceivedStats = new ConcurrentDictionary(); private readonly ConcurrentDictionary ReceivedStatsByName = new ConcurrentDictionary(); /// /// This is the method on which the classic sim stats reporter (which collects stats for /// client purposes) sends information to listeners. /// /// public void ReceiveClassicSimStatsPacket(SimStats stats) { UUID id = stats.RegionUUID; if (!id.IsZero()) { if(ReceivedStats.Count == 0) firstReceivedRegion = id; ReceivedStats[id] = stats; ReceivedStatsByName[stats.RegionName.ToLower()] = stats; } } /// /// Report back collected statistical information. /// /// public override string Report(IScene scene) { SimStats sdata = null; if (ReceivedStats.Count > 0) { if (scene == null) ReceivedStats.TryGetValue(firstReceivedRegion, out sdata); else ReceivedStats.TryGetValue(scene.RegionInfo.RegionID, out sdata); } StringBuilder sb = new StringBuilder(); sb.AppendLine("CONNECTION STATISTICS"); List stats = StatsManager.GetStatsFromEachContainer("clientstack", "ClientLogoutsDueToNoReceives"); sb.AppendFormat( "Client logouts due to no data receive timeout: {0}\n\n", stats != null ? stats.Sum(s => s.Value).ToString() : "unknown"); sb.Append(Environment.NewLine); if(sdata != null) { float[] data = sdata.StatsValues; sb.AppendFormat("{0} FRAME STATISTICS", sdata.RegionName); sb.Append(Environment.NewLine); sb.Append("Dilatn SimFPS PhyFPS AgntUp RootAg ChldAg Prims AtvPrm AtvScr ScrEPS\n"); sb.AppendFormat( "{0,6:0.00} {1,6:0} {2,6:0.0} {3,6:0.0} {4,6:0} {5,6:0} {6,6:0} {7,6:0} {8,6:0} {9,6:0}\n", data[(int)StatsIndex.TimeDilation], data[(int)StatsIndex.SimFPS], data[(int)StatsIndex.PhysicsFPS], data[(int)StatsIndex.AgentUpdates], data[(int)StatsIndex.Agents], data[(int)StatsIndex.ChildAgents], data[(int)StatsIndex.TotalPrim], data[(int)StatsIndex.ActivePrim], data[(int)StatsIndex.ActiveScripts], data[(int)StatsIndex.ScriptEps]); sb.Append(Environment.NewLine); // There is no script frame time currently because we don't yet collect it sb.Append("PktsIn PktOut PendDl PendUl UnackB TotlFt NetFt PhysFt OthrFt AgntFt ImgsFt\n"); sb.AppendFormat( "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.00} {6,6:0.00} {7,6:0.00} {8,6:0.00} {9,6:0.00} {10,6:0.00}\n", data[(int)StatsIndex.InPacketsPerSecond], data[(int)StatsIndex.OutPacketsPerSecond], data[(int)StatsIndex.PendingDownloads], data[(int)StatsIndex.PendingUploads], data[(int)StatsIndex.UnAckedBytes], data[(int)StatsIndex.FrameMS], data[(int)StatsIndex.NetMS], data[(int)StatsIndex.PhysicsMS], data[(int)StatsIndex.OtherMS] , data[(int)StatsIndex.AgentMS], data[(int)StatsIndex.ImageMS]); } sb.Append(base.Report()); return sb.ToString(); } /// /// Report back collected statistical information as json serialization. /// /// public override string XReport(string uptime, string version, string scene) { return OSDParser.SerializeJsonString(OReport(uptime, version, scene)); } /// /// Report back collected statistical information as an OSDMap /// /// public override OSDMap OReport(string uptime, string version, string scene) { // Get the amount of physical memory, allocated with the instance of this program, in kilobytes; // the working set is the set of memory pages currently visible to this program in physical RAM // memory and includes both shared (e.g. system libraries) and private data int numberThreads = 0; int numberThreadsRunning = 0; double memUsage = 0; using(Process p = Process.GetCurrentProcess()) { memUsage = p.WorkingSet64 / 1024.0; numberThreads = p.Threads.Count; // Get the number of threads from the system that are currently // running foreach (ProcessThread currentThread in p.Threads) { if (currentThread != null && currentThread.ThreadState == ThreadState.Running) numberThreadsRunning++; } } SimStats sdata = null; if (ReceivedStats.Count > 0) { if (scene == null || string.IsNullOrEmpty(scene)) ReceivedStats.TryGetValue(firstReceivedRegion, out sdata); else { if(UUID.TryParse(scene, out UUID id)) ReceivedStats.TryGetValue(id, out sdata); else ReceivedStatsByName.TryGetValue(scene.ToLower(), out sdata); } } OSDMap args = new OSDMap(33); if(sdata != null && sdata.StatsValues != null) { float[] data = sdata.StatsValues; args["Dilatn"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.TimeDilation])); args["SimFPS"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.SimFPS])); args["PhyFPS"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.PhysicsFPS])); args["AgntUp"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.AgentUpdates])); args["RootAg"] = OSD.FromString (String.Format ("{0}", (int)data[(int)StatsIndex.Agents])); args["ChldAg"] = OSD.FromString(String.Format("{0}", (int)data[(int)StatsIndex.ChildAgents])); args["NPCAg"] = OSD.FromString(String.Format("{0}", (int)data[(int)StatsIndex.NPCs])); args["Prims"] = OSD.FromString (String.Format ("{0}", (int)data[(int)StatsIndex.TotalPrim])); args["AtvPrm"] = OSD.FromString (String.Format ("{0}", (int)data[(int)StatsIndex.ActivePrim])); args["AtvScr"] = OSD.FromString (String.Format ("{0}", (int)data[(int)StatsIndex.ActiveScripts])); args["ScrLPS"] = OSD.FromString(String.Format("{0:0.##}", data[(int)StatsIndex.LSLScriptLinesPerSecond])); args["ScrEPS"] = OSD.FromString(String.Format("{0:0.##}", data[(int)StatsIndex.ScriptEps])); args["PktsIn"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.InPacketsPerSecond])); args["PktOut"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.OutPacketsPerSecond])); args["PendDl"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.PendingDownloads])); args["PendUl"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.PendingUploads])); args["UnackB"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.UnAckedBytes])); args["TotlFt"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.FrameMS])); args["NetFt"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.NetMS])); args["PhysFt"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.PhysicsMS])); args["OthrFt"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.OtherMS])); args["AgntFt"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.AgentMS])); args["ImgsFt"] = OSD.FromString (String.Format ("{0:0.##}", data[(int)StatsIndex.ImageMS])); args["FrameDilatn"] = OSD.FromString(String.Format("{0:0.#}", data[(int)StatsIndex.FrameDilation2])); args["Logging in Users"] = OSD.FromString(String.Format("{0:0.#}", data[(int)StatsIndex.UsersLoggingIn])); args["GeoPrims"] = OSD.FromString(String.Format("{0:0.#}", data[(int)StatsIndex.TotalGeoPrim])); args["Mesh Objects"] = OSD.FromString(String.Format("{0:0.##}", data[(int)StatsIndex.TotalMesh])); args["Script Engine Thread Count"] = OSD.FromString(String.Format("{0:0.#}", data[(int)StatsIndex.ScriptEngineThreadCount])); args["RegionName"] = sdata.RegionName; } else args["Error"] = "No Region data"; args["Util Thread Count"] = OSD.FromString(String.Format("{0:0.##}", Util.GetSmartThreadPoolInfo().InUseThreads)); args["System Thread Count"] = OSD.FromString(String.Format("{0:0.##}", numberThreads)); args["System Thread Active"] = OSD.FromString(String.Format("{0:0.##}", numberThreadsRunning)); args["ProcMem"] = OSD.FromString(String.Format("{0:0.##}", memUsage)); args["Memory"] = OSD.FromString(base.XReport(uptime, version)); args["Uptime"] = OSD.FromString(uptime); args["Version"] = OSD.FromString(version); return args; } } /// /// Pull packet queue stats from packet queues and report /// public class PacketQueueStatsCollector : IStatsCollector { private IPullStatsProvider m_statsProvider; public PacketQueueStatsCollector(IPullStatsProvider provider) { m_statsProvider = provider; } /// /// Report back collected statistical information. /// /// public string Report() { return m_statsProvider.GetStats(); } public string XReport(string uptime, string version) { return ""; } public OSDMap OReport(string uptime, string version) { OSDMap ret = new OSDMap(); return ret; } } }