aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2013-06-17 22:39:00 +0100
committerJustin Clark-Casey (justincc)2013-06-17 22:39:00 +0100
commit0d2fd0d914581f755661455b8db2b9e399154632 (patch)
treeb3c8f5634d946d8e623f761eb7001f254d2af9be /OpenSim/Framework
parentcorrect method doc for llRot2Axis() (diff)
downloadopensim-SC-0d2fd0d914581f755661455b8db2b9e399154632.zip
opensim-SC-0d2fd0d914581f755661455b8db2b9e399154632.tar.gz
opensim-SC-0d2fd0d914581f755661455b8db2b9e399154632.tar.bz2
opensim-SC-0d2fd0d914581f755661455b8db2b9e399154632.tar.xz
Make general server stats available on the robust console as well as the simulator console
This means the "show stats" command is now active on the robust console.
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/Monitoring/ServerStatsCollector.cs309
-rw-r--r--OpenSim/Framework/Monitoring/StatsManager.cs72
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs46
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs32
4 files changed, 394 insertions, 65 deletions
diff --git a/OpenSim/Framework/Monitoring/ServerStatsCollector.cs b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
new file mode 100644
index 0000000..80d0a89
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
@@ -0,0 +1,309 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Diagnostics;
31using System.Linq;
32using System.Net.NetworkInformation;
33using System.Text;
34using System.Threading;
35using log4net;
36using Nini.Config;
37using OpenMetaverse.StructuredData;
38using OpenSim.Framework;
39
40namespace OpenSim.Framework.Monitoring
41{
42 public class ServerStatsCollector
43 {
44 private readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
45 private readonly string LogHeader = "[SERVER STATS]";
46
47 public bool Enabled = false;
48 private static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>();
49
50 public readonly string CategoryServer = "server";
51
52 public readonly string ContainerProcessor = "processor";
53 public readonly string ContainerMemory = "memory";
54 public readonly string ContainerNetwork = "network";
55 public readonly string ContainerProcess = "process";
56
57 public string NetworkInterfaceTypes = "Ethernet";
58
59 readonly int performanceCounterSampleInterval = 500;
60// int lastperformanceCounterSampleTime = 0;
61
62 private class PerfCounterControl
63 {
64 public PerformanceCounter perfCounter;
65 public int lastFetch;
66 public string name;
67 public PerfCounterControl(PerformanceCounter pPc)
68 : this(pPc, String.Empty)
69 {
70 }
71 public PerfCounterControl(PerformanceCounter pPc, string pName)
72 {
73 perfCounter = pPc;
74 lastFetch = 0;
75 name = pName;
76 }
77 }
78
79 PerfCounterControl processorPercentPerfCounter = null;
80
81 // IRegionModuleBase.Initialize
82 public void Initialise(IConfigSource source)
83 {
84 IConfig cfg = source.Configs["Monitoring"];
85
86 if (cfg != null)
87 Enabled = cfg.GetBoolean("ServerStatsEnabled", true);
88
89 if (Enabled)
90 {
91 NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet");
92 }
93 }
94
95 public void Start()
96 {
97 if (RegisteredStats.Count == 0)
98 RegisterServerStats();
99 }
100
101 public void Close()
102 {
103 if (RegisteredStats.Count > 0)
104 {
105 foreach (Stat stat in RegisteredStats.Values)
106 {
107 StatsManager.DeregisterStat(stat);
108 stat.Dispose();
109 }
110 RegisteredStats.Clear();
111 }
112 }
113
114 private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act)
115 {
116 string desc = pDesc;
117 if (desc == null)
118 desc = pName;
119 Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, act, StatVerbosity.Info);
120 StatsManager.RegisterStat(stat);
121 RegisteredStats.Add(pName, stat);
122 }
123
124 public void RegisterServerStats()
125 {
126// lastperformanceCounterSampleTime = Util.EnvironmentTickCount();
127 PerformanceCounter tempPC;
128 Stat tempStat;
129 string tempName;
130
131 try
132 {
133 tempName = "CPUPercent";
134 tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total");
135 processorPercentPerfCounter = new PerfCounterControl(tempPC);
136 // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy.
137 tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor,
138 StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); },
139 StatVerbosity.Info);
140 StatsManager.RegisterStat(tempStat);
141 RegisteredStats.Add(tempName, tempStat);
142
143 MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor,
144 (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; });
145
146 MakeStat("UserProcessorTime", null, "sec", ContainerProcessor,
147 (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; });
148
149 MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor,
150 (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; });
151
152 MakeStat("Threads", null, "threads", ContainerProcessor,
153 (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; });
154 }
155 catch (Exception e)
156 {
157 m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e);
158 }
159
160 try
161 {
162 List<string> okInterfaceTypes = new List<string>(NetworkInterfaceTypes.Split(','));
163
164 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces();
165 foreach (NetworkInterface nic in nics)
166 {
167 if (nic.OperationalStatus != OperationalStatus.Up)
168 continue;
169
170 string nicInterfaceType = nic.NetworkInterfaceType.ToString();
171 if (!okInterfaceTypes.Contains(nicInterfaceType))
172 {
173 m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.",
174 LogHeader, nic.Name, nicInterfaceType);
175 m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}",
176 LogHeader, NetworkInterfaceTypes);
177 continue;
178 }
179
180 if (nic.Supports(NetworkInterfaceComponent.IPv4))
181 {
182 IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics();
183 if (nicStats != null)
184 {
185 MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork,
186 (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); });
187 MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork,
188 (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); });
189 MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork,
190 (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); });
191 }
192 }
193 // TODO: add IPv6 (it may actually happen someday)
194 }
195 }
196 catch (Exception e)
197 {
198 m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e);
199 }
200
201 MakeStat("ProcessMemory", null, "MB", ContainerMemory,
202 (s) => { s.Value = Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d; });
203 MakeStat("ObjectMemory", null, "MB", ContainerMemory,
204 (s) => { s.Value = GC.GetTotalMemory(false) / 1024d / 1024d; });
205 MakeStat("LastMemoryChurn", null, "MB/sec", ContainerMemory,
206 (s) => { s.Value = Math.Round(MemoryWatchdog.LastMemoryChurn * 1000d / 1024d / 1024d, 3); });
207 MakeStat("AverageMemoryChurn", null, "MB/sec", ContainerMemory,
208 (s) => { s.Value = Math.Round(MemoryWatchdog.AverageMemoryChurn * 1000d / 1024d / 1024d, 3); });
209 }
210
211 // Notes on performance counters:
212 // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx
213 // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c
214 // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters
215 private delegate double PerfCounterNextValue();
216 private void GetNextValue(Stat stat, PerfCounterControl perfControl)
217 {
218 GetNextValue(stat, perfControl, 1.0);
219 }
220 private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor)
221 {
222 if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval)
223 {
224 if (perfControl != null && perfControl.perfCounter != null)
225 {
226 try
227 {
228 // Kludge for factor to run double duty. If -1, subtract the value from one
229 if (factor == -1)
230 stat.Value = 1 - perfControl.perfCounter.NextValue();
231 else
232 stat.Value = perfControl.perfCounter.NextValue() / factor;
233 }
234 catch (Exception e)
235 {
236 m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e);
237 }
238 perfControl.lastFetch = Util.EnvironmentTickCount();
239 }
240 }
241 }
242
243 // Lookup the nic that goes with this stat and set the value by using a fetch action.
244 // Not sure about closure with delegates inside delegates.
245 private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat);
246 private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor)
247 {
248 // Get the one nic that has the name of this stat
249 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
250 (network) => network.Name == stat.Description);
251 try
252 {
253 foreach (NetworkInterface nic in nics)
254 {
255 IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics();
256 if (intrStats != null)
257 {
258 double newVal = Math.Round(getter(intrStats) / factor, 3);
259 stat.Value = newVal;
260 }
261 break;
262 }
263 }
264 catch
265 {
266 // There are times interfaces go away so we just won't update the stat for this
267 m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description);
268 }
269 }
270 }
271
272 public class ServerStatsAggregator : Stat
273 {
274 public ServerStatsAggregator(
275 string shortName,
276 string name,
277 string description,
278 string unitName,
279 string category,
280 string container
281 )
282 : base(
283 shortName,
284 name,
285 description,
286 unitName,
287 category,
288 container,
289 StatType.Push,
290 MeasuresOfInterest.None,
291 null,
292 StatVerbosity.Info)
293 {
294 }
295 public override string ToConsoleString()
296 {
297 StringBuilder sb = new StringBuilder();
298
299 return sb.ToString();
300 }
301
302 public override OSDMap ToOSDMap()
303 {
304 OSDMap ret = new OSDMap();
305
306 return ret;
307 }
308 }
309}
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
index 24db6d4..3aee984 100644
--- a/OpenSim/Framework/Monitoring/StatsManager.cs
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -54,13 +54,13 @@ namespace OpenSim.Framework.Monitoring
54 public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats 54 public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats
55 = new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>(); 55 = new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>();
56 56
57 private static AssetStatsCollector assetStats; 57// private static AssetStatsCollector assetStats;
58 private static UserStatsCollector userStats; 58// private static UserStatsCollector userStats;
59 private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); 59// private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
60 60
61 public static AssetStatsCollector AssetStats { get { return assetStats; } } 61// public static AssetStatsCollector AssetStats { get { return assetStats; } }
62 public static UserStatsCollector UserStats { get { return userStats; } } 62// public static UserStatsCollector UserStats { get { return userStats; } }
63 public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } 63 public static SimExtraStatsCollector SimExtraStats { get; set; }
64 64
65 public static void RegisterConsoleCommands(ICommandConsole console) 65 public static void RegisterConsoleCommands(ICommandConsole console)
66 { 66 {
@@ -89,10 +89,7 @@ namespace OpenSim.Framework.Monitoring
89 89
90 if (categoryName == AllSubCommand) 90 if (categoryName == AllSubCommand)
91 { 91 {
92 foreach (var category in RegisteredStats.Values) 92 OutputAllStatsToConsole(con);
93 {
94 OutputCategoryStatsToConsole(con, category);
95 }
96 } 93 }
97 else if (categoryName == ListSubCommand) 94 else if (categoryName == ListSubCommand)
98 { 95 {
@@ -129,7 +126,18 @@ namespace OpenSim.Framework.Monitoring
129 else 126 else
130 { 127 {
131 // Legacy 128 // Legacy
132 con.Output(SimExtraStats.Report()); 129 if (SimExtraStats != null)
130 con.Output(SimExtraStats.Report());
131 else
132 OutputAllStatsToConsole(con);
133 }
134 }
135
136 private static void OutputAllStatsToConsole(ICommandConsole con)
137 {
138 foreach (var category in RegisteredStats.Values)
139 {
140 OutputCategoryStatsToConsole(con, category);
133 } 141 }
134 } 142 }
135 143
@@ -150,27 +158,27 @@ namespace OpenSim.Framework.Monitoring
150 } 158 }
151 } 159 }
152 160
153 /// <summary> 161// /// <summary>
154 /// Start collecting statistics related to assets. 162// /// Start collecting statistics related to assets.
155 /// Should only be called once. 163// /// Should only be called once.
156 /// </summary> 164// /// </summary>
157 public static AssetStatsCollector StartCollectingAssetStats() 165// public static AssetStatsCollector StartCollectingAssetStats()
158 { 166// {
159 assetStats = new AssetStatsCollector(); 167// assetStats = new AssetStatsCollector();
160 168//
161 return assetStats; 169// return assetStats;
162 } 170// }
163 171//
164 /// <summary> 172// /// <summary>
165 /// Start collecting statistics related to users. 173// /// Start collecting statistics related to users.
166 /// Should only be called once. 174// /// Should only be called once.
167 /// </summary> 175// /// </summary>
168 public static UserStatsCollector StartCollectingUserStats() 176// public static UserStatsCollector StartCollectingUserStats()
169 { 177// {
170 userStats = new UserStatsCollector(); 178// userStats = new UserStatsCollector();
171 179//
172 return userStats; 180// return userStats;
173 } 181// }
174 182
175 /// <summary> 183 /// <summary>
176 /// Registers a statistic. 184 /// Registers a statistic.
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 035b3ad..4ab6908 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -86,26 +86,23 @@ namespace OpenSim.Framework.Servers
86 /// </summary> 86 /// </summary>
87 protected virtual void StartupSpecific() 87 protected virtual void StartupSpecific()
88 { 88 {
89 if (m_console == null) 89 StatsManager.SimExtraStats = new SimExtraStatsCollector();
90 return;
91
92 RegisterCommonCommands(); 90 RegisterCommonCommands();
93 91 RegisterCommonComponents(Config);
94 m_console.Commands.AddCommand("General", false, "quit", 92 }
95 "quit", 93
96 "Quit the application", HandleQuit); 94 protected override void ShutdownSpecific()
95 {
96 m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting...");
97
98 RemovePIDFile();
99
100 base.ShutdownSpecific();
97 101
98 m_console.Commands.AddCommand("General", false, "shutdown", 102 Environment.Exit(0);
99 "shutdown",
100 "Quit the application", HandleQuit);
101 } 103 }
102 104
103 /// <summary> 105 /// <summary>
104 /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
105 /// </summary>
106 public virtual void ShutdownSpecific() {}
107
108 /// <summary>
109 /// Provides a list of help topics that are available. Overriding classes should append their topics to the 106 /// Provides a list of help topics that are available. Overriding classes should append their topics to the
110 /// information returned when the base method is called. 107 /// information returned when the base method is called.
111 /// </summary> 108 /// </summary>
@@ -143,25 +140,8 @@ namespace OpenSim.Framework.Servers
143 timeTaken.Minutes, timeTaken.Seconds); 140 timeTaken.Minutes, timeTaken.Seconds);
144 } 141 }
145 142
146 /// <summary> 143 public string osSecret
147 /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
148 /// </summary>
149 public virtual void Shutdown()
150 { 144 {
151 ShutdownSpecific();
152
153 m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting...");
154 RemovePIDFile();
155
156 Environment.Exit(0);
157 }
158
159 private void HandleQuit(string module, string[] args)
160 {
161 Shutdown();
162 }
163
164 public string osSecret {
165 // Secret uuid for the simulator 145 // Secret uuid for the simulator
166 get { return m_osSecret; } 146 get { return m_osSecret; }
167 } 147 }
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs
index 2c4a687..5358444 100644
--- a/OpenSim/Framework/Servers/ServerBase.cs
+++ b/OpenSim/Framework/Servers/ServerBase.cs
@@ -62,6 +62,8 @@ namespace OpenSim.Framework.Servers
62 62
63 protected string m_pidFile = String.Empty; 63 protected string m_pidFile = String.Empty;
64 64
65 protected ServerStatsCollector m_serverStatsCollector;
66
65 /// <summary> 67 /// <summary>
66 /// Server version information. Usually VersionInfo + information about git commit, operating system, etc. 68 /// Server version information. Usually VersionInfo + information about git commit, operating system, etc.
67 /// </summary> 69 /// </summary>
@@ -259,6 +261,25 @@ namespace OpenSim.Framework.Servers
259 "force gc", 261 "force gc",
260 "Manually invoke runtime garbage collection. For debugging purposes", 262 "Manually invoke runtime garbage collection. For debugging purposes",
261 HandleForceGc); 263 HandleForceGc);
264
265 m_console.Commands.AddCommand(
266 "General", false, "quit",
267 "quit",
268 "Quit the application", (mod, args) => Shutdown());
269
270 m_console.Commands.AddCommand(
271 "General", false, "shutdown",
272 "shutdown",
273 "Quit the application", (mod, args) => Shutdown());
274
275 StatsManager.RegisterConsoleCommands(m_console);
276 }
277
278 public void RegisterCommonComponents(IConfigSource configSource)
279 {
280 m_serverStatsCollector = new ServerStatsCollector();
281 m_serverStatsCollector.Initialise(configSource);
282 m_serverStatsCollector.Start();
262 } 283 }
263 284
264 private void HandleForceGc(string module, string[] args) 285 private void HandleForceGc(string module, string[] args)
@@ -698,5 +719,16 @@ namespace OpenSim.Framework.Servers
698 if (m_console != null) 719 if (m_console != null)
699 m_console.OutputFormat(format, components); 720 m_console.OutputFormat(format, components);
700 } 721 }
722
723 public virtual void Shutdown()
724 {
725 m_serverStatsCollector.Close();
726 ShutdownSpecific();
727 }
728
729 /// <summary>
730 /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
731 /// </summary>
732 protected virtual void ShutdownSpecific() {}
701 } 733 }
702} \ No newline at end of file 734} \ No newline at end of file