aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim
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
parentcorrect method doc for llRot2Axis() (diff)
downloadopensim-SC_OLD-0d2fd0d914581f755661455b8db2b9e399154632.zip
opensim-SC_OLD-0d2fd0d914581f755661455b8db2b9e399154632.tar.gz
opensim-SC_OLD-0d2fd0d914581f755661455b8db2b9e399154632.tar.bz2
opensim-SC_OLD-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
-rw-r--r--OpenSim/Region/Application/Application.cs1
-rw-r--r--OpenSim/Region/Application/OpenSim.cs2
-rw-r--r--OpenSim/Region/Application/OpenSimBase.cs7
-rw-r--r--OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs339
-rw-r--r--OpenSim/Server/Base/ServicesServerBase.cs16
9 files changed, 403 insertions, 421 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
diff --git a/OpenSim/Region/Application/Application.cs b/OpenSim/Region/Application/Application.cs
index c3e7ec2..e451aa8 100644
--- a/OpenSim/Region/Application/Application.cs
+++ b/OpenSim/Region/Application/Application.cs
@@ -124,6 +124,7 @@ namespace OpenSim
124 workerThreads = workerThreadsMax; 124 workerThreads = workerThreadsMax;
125 m_log.InfoFormat("[OPENSIM MAIN]: Limiting worker threads to {0}",workerThreads); 125 m_log.InfoFormat("[OPENSIM MAIN]: Limiting worker threads to {0}",workerThreads);
126 } 126 }
127
127 // Increase the number of IOCP threads available. 128 // Increase the number of IOCP threads available.
128 // Mono defaults to a tragically low number (24 on 6-core / 8GB Fedora 17) 129 // Mono defaults to a tragically low number (24 on 6-core / 8GB Fedora 17)
129 if (iocpThreads < iocpThreadsMin) 130 if (iocpThreads < iocpThreadsMin)
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs
index 11dd052..9325b12 100644
--- a/OpenSim/Region/Application/OpenSim.cs
+++ b/OpenSim/Region/Application/OpenSim.cs
@@ -372,7 +372,7 @@ namespace OpenSim
372 "Unload a module", HandleModules); 372 "Unload a module", HandleModules);
373 } 373 }
374 374
375 public override void ShutdownSpecific() 375 protected override void ShutdownSpecific()
376 { 376 {
377 if (m_shutdownCommandsFile != String.Empty) 377 if (m_shutdownCommandsFile != String.Empty)
378 { 378 {
diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs
index f9e0cf1..7ca87a3 100644
--- a/OpenSim/Region/Application/OpenSimBase.cs
+++ b/OpenSim/Region/Application/OpenSimBase.cs
@@ -231,10 +231,7 @@ namespace OpenSim
231 } 231 }
232 232
233 if (m_console != null) 233 if (m_console != null)
234 {
235 StatsManager.RegisterConsoleCommands(m_console);
236 AddPluginCommands(m_console); 234 AddPluginCommands(m_console);
237 }
238 } 235 }
239 236
240 protected virtual void AddPluginCommands(ICommandConsole console) 237 protected virtual void AddPluginCommands(ICommandConsole console)
@@ -880,7 +877,7 @@ namespace OpenSim
880 /// <summary> 877 /// <summary>
881 /// Performs any last-minute sanity checking and shuts down the region server 878 /// Performs any last-minute sanity checking and shuts down the region server
882 /// </summary> 879 /// </summary>
883 public override void ShutdownSpecific() 880 protected override void ShutdownSpecific()
884 { 881 {
885 if (proxyUrl.Length > 0) 882 if (proxyUrl.Length > 0)
886 { 883 {
@@ -900,6 +897,8 @@ namespace OpenSim
900 { 897 {
901 m_log.Error("[SHUTDOWN]: Ignoring failure during shutdown - ", e); 898 m_log.Error("[SHUTDOWN]: Ignoring failure during shutdown - ", e);
902 } 899 }
900
901 base.ShutdownSpecific();
903 } 902 }
904 903
905 /// <summary> 904 /// <summary>
diff --git a/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs b/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs
deleted file mode 100644
index 6e74ce0..0000000
--- a/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs
+++ /dev/null
@@ -1,339 +0,0 @@
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;
35
36using log4net;
37using Mono.Addins;
38using Nini.Config;
39
40using OpenSim.Framework;
41using OpenSim.Framework.Console;
42using OpenSim.Framework.Monitoring;
43using OpenSim.Region.Framework.Interfaces;
44using OpenSim.Region.Framework.Scenes;
45
46using OpenMetaverse.StructuredData;
47
48namespace OpenSim.Region.OptionalModules.Framework.Monitoring
49{
50[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ServerStatistics")]
51public class ServerStats : ISharedRegionModule
52{
53 private readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
54 private readonly string LogHeader = "[SERVER STATS]";
55
56 public bool Enabled = false;
57 private static Dictionary<string, Stat> RegisteredStats = new Dictionary<string, Stat>();
58
59 public readonly string CategoryServer = "server";
60
61 public readonly string ContainerProcessor = "processor";
62 public readonly string ContainerMemory = "memory";
63 public readonly string ContainerNetwork = "network";
64 public readonly string ContainerProcess = "process";
65
66 public string NetworkInterfaceTypes = "Ethernet";
67
68 readonly int performanceCounterSampleInterval = 500;
69 int lastperformanceCounterSampleTime = 0;
70
71 private class PerfCounterControl
72 {
73 public PerformanceCounter perfCounter;
74 public int lastFetch;
75 public string name;
76 public PerfCounterControl(PerformanceCounter pPc)
77 : this(pPc, String.Empty)
78 {
79 }
80 public PerfCounterControl(PerformanceCounter pPc, string pName)
81 {
82 perfCounter = pPc;
83 lastFetch = 0;
84 name = pName;
85 }
86 }
87
88 PerfCounterControl processorPercentPerfCounter = null;
89
90 #region ISharedRegionModule
91 // IRegionModuleBase.Name
92 public string Name { get { return "Server Stats"; } }
93 // IRegionModuleBase.ReplaceableInterface
94 public Type ReplaceableInterface { get { return null; } }
95 // IRegionModuleBase.Initialize
96 public void Initialise(IConfigSource source)
97 {
98 IConfig cfg = source.Configs["Monitoring"];
99
100 if (cfg != null)
101 Enabled = cfg.GetBoolean("ServerStatsEnabled", true);
102
103 if (Enabled)
104 {
105 NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet");
106 }
107 }
108 // IRegionModuleBase.Close
109 public void Close()
110 {
111 if (RegisteredStats.Count > 0)
112 {
113 foreach (Stat stat in RegisteredStats.Values)
114 {
115 StatsManager.DeregisterStat(stat);
116 stat.Dispose();
117 }
118 RegisteredStats.Clear();
119 }
120 }
121 // IRegionModuleBase.AddRegion
122 public void AddRegion(Scene scene)
123 {
124 }
125 // IRegionModuleBase.RemoveRegion
126 public void RemoveRegion(Scene scene)
127 {
128 }
129 // IRegionModuleBase.RegionLoaded
130 public void RegionLoaded(Scene scene)
131 {
132 }
133 // ISharedRegionModule.PostInitialize
134 public void PostInitialise()
135 {
136 if (RegisteredStats.Count == 0)
137 {
138 RegisterServerStats();
139 }
140 }
141 #endregion ISharedRegionModule
142
143 private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act)
144 {
145 string desc = pDesc;
146 if (desc == null)
147 desc = pName;
148 Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, act, StatVerbosity.Info);
149 StatsManager.RegisterStat(stat);
150 RegisteredStats.Add(pName, stat);
151 }
152
153 public void RegisterServerStats()
154 {
155 lastperformanceCounterSampleTime = Util.EnvironmentTickCount();
156 PerformanceCounter tempPC;
157 Stat tempStat;
158 string tempName;
159
160 try
161 {
162 tempName = "CPUPercent";
163 tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total");
164 processorPercentPerfCounter = new PerfCounterControl(tempPC);
165 // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy.
166 tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor,
167 StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); },
168 StatVerbosity.Info);
169 StatsManager.RegisterStat(tempStat);
170 RegisteredStats.Add(tempName, tempStat);
171
172 MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor,
173 (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; });
174
175 MakeStat("UserProcessorTime", null, "sec", ContainerProcessor,
176 (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; });
177
178 MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor,
179 (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; });
180
181 MakeStat("Threads", null, "threads", ContainerProcessor,
182 (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; });
183 }
184 catch (Exception e)
185 {
186 m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e);
187 }
188
189 try
190 {
191 List<string> okInterfaceTypes = new List<string>(NetworkInterfaceTypes.Split(','));
192
193 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces();
194 foreach (NetworkInterface nic in nics)
195 {
196 if (nic.OperationalStatus != OperationalStatus.Up)
197 continue;
198
199 string nicInterfaceType = nic.NetworkInterfaceType.ToString();
200 if (!okInterfaceTypes.Contains(nicInterfaceType))
201 {
202 m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.",
203 LogHeader, nic.Name, nicInterfaceType);
204 m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}",
205 LogHeader, NetworkInterfaceTypes);
206 continue;
207 }
208
209 if (nic.Supports(NetworkInterfaceComponent.IPv4))
210 {
211 IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics();
212 if (nicStats != null)
213 {
214 MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork,
215 (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); });
216 MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork,
217 (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); });
218 MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork,
219 (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); });
220 }
221 }
222 // TODO: add IPv6 (it may actually happen someday)
223 }
224 }
225 catch (Exception e)
226 {
227 m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e);
228 }
229
230 MakeStat("ProcessMemory", null, "MB", ContainerMemory,
231 (s) => { s.Value = Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d; });
232 MakeStat("ObjectMemory", null, "MB", ContainerMemory,
233 (s) => { s.Value = GC.GetTotalMemory(false) / 1024d / 1024d; });
234 MakeStat("LastMemoryChurn", null, "MB/sec", ContainerMemory,
235 (s) => { s.Value = Math.Round(MemoryWatchdog.LastMemoryChurn * 1000d / 1024d / 1024d, 3); });
236 MakeStat("AverageMemoryChurn", null, "MB/sec", ContainerMemory,
237 (s) => { s.Value = Math.Round(MemoryWatchdog.AverageMemoryChurn * 1000d / 1024d / 1024d, 3); });
238 }
239
240 // Notes on performance counters:
241 // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx
242 // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c
243 // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters
244 private delegate double PerfCounterNextValue();
245 private void GetNextValue(Stat stat, PerfCounterControl perfControl)
246 {
247 GetNextValue(stat, perfControl, 1.0);
248 }
249 private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor)
250 {
251 if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval)
252 {
253 if (perfControl != null && perfControl.perfCounter != null)
254 {
255 try
256 {
257 // Kludge for factor to run double duty. If -1, subtract the value from one
258 if (factor == -1)
259 stat.Value = 1 - perfControl.perfCounter.NextValue();
260 else
261 stat.Value = perfControl.perfCounter.NextValue() / factor;
262 }
263 catch (Exception e)
264 {
265 m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e);
266 }
267 perfControl.lastFetch = Util.EnvironmentTickCount();
268 }
269 }
270 }
271
272 // Lookup the nic that goes with this stat and set the value by using a fetch action.
273 // Not sure about closure with delegates inside delegates.
274 private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat);
275 private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor)
276 {
277 // Get the one nic that has the name of this stat
278 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
279 (network) => network.Name == stat.Description);
280 try
281 {
282 foreach (NetworkInterface nic in nics)
283 {
284 IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics();
285 if (intrStats != null)
286 {
287 double newVal = Math.Round(getter(intrStats) / factor, 3);
288 stat.Value = newVal;
289 }
290 break;
291 }
292 }
293 catch
294 {
295 // There are times interfaces go away so we just won't update the stat for this
296 m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description);
297 }
298 }
299}
300
301public class ServerStatsAggregator : Stat
302{
303 public ServerStatsAggregator(
304 string shortName,
305 string name,
306 string description,
307 string unitName,
308 string category,
309 string container
310 )
311 : base(
312 shortName,
313 name,
314 description,
315 unitName,
316 category,
317 container,
318 StatType.Push,
319 MeasuresOfInterest.None,
320 null,
321 StatVerbosity.Info)
322 {
323 }
324 public override string ToConsoleString()
325 {
326 StringBuilder sb = new StringBuilder();
327
328 return sb.ToString();
329 }
330
331 public override OSDMap ToOSDMap()
332 {
333 OSDMap ret = new OSDMap();
334
335 return ret;
336 }
337}
338
339}
diff --git a/OpenSim/Server/Base/ServicesServerBase.cs b/OpenSim/Server/Base/ServicesServerBase.cs
index b13c87d..8243900 100644
--- a/OpenSim/Server/Base/ServicesServerBase.cs
+++ b/OpenSim/Server/Base/ServicesServerBase.cs
@@ -190,16 +190,7 @@ namespace OpenSim.Server.Base
190 } 190 }
191 191
192 RegisterCommonCommands(); 192 RegisterCommonCommands();
193 193 RegisterCommonComponents(Config);
194 // Register the quit command
195 //
196 MainConsole.Instance.Commands.AddCommand("General", false, "quit",
197 "quit",
198 "Quit the application", HandleQuit);
199
200 MainConsole.Instance.Commands.AddCommand("General", false, "shutdown",
201 "shutdown",
202 "Quit the application", HandleQuit);
203 194
204 // Allow derived classes to perform initialization that 195 // Allow derived classes to perform initialization that
205 // needs to be done after the console has opened 196 // needs to be done after the console has opened
@@ -231,11 +222,12 @@ namespace OpenSim.Server.Base
231 return 0; 222 return 0;
232 } 223 }
233 224
234 protected virtual void HandleQuit(string module, string[] args) 225 protected override void ShutdownSpecific()
235 { 226 {
236 m_Running = false; 227 m_Running = false;
237 m_log.Info("[CONSOLE] Quitting"); 228 m_log.Info("[CONSOLE] Quitting");
238 229
230 base.ShutdownSpecific();
239 } 231 }
240 232
241 protected virtual void ReadConfig() 233 protected virtual void ReadConfig()
@@ -246,4 +238,4 @@ namespace OpenSim.Server.Base
246 { 238 {
247 } 239 }
248 } 240 }
249} 241} \ No newline at end of file