aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorRobert Adams2013-02-23 16:49:02 -0800
committerRobert Adams2013-02-24 07:47:59 -0800
commit8e67ad25b07c6e934e7df86b3baffa2ab85145c1 (patch)
tree8705e68c9e3341c2e1754299ecc004dd499b505f
parentMore clarification on the [Groups] section (diff)
downloadopensim-SC_OLD-8e67ad25b07c6e934e7df86b3baffa2ab85145c1.zip
opensim-SC_OLD-8e67ad25b07c6e934e7df86b3baffa2ab85145c1.tar.gz
opensim-SC_OLD-8e67ad25b07c6e934e7df86b3baffa2ab85145c1.tar.bz2
opensim-SC_OLD-8e67ad25b07c6e934e7df86b3baffa2ab85145c1.tar.xz
Addition of ServerStats shared region module which collects and registers
server wide statistics (CPU%, network bytes sent, ...) with StatsManager.
-rw-r--r--OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs438
1 files changed, 438 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs b/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs
new file mode 100644
index 0000000..8f60c8d
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs
@@ -0,0 +1,438 @@
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
67 readonly int performanceCounterSampleInterval = 500;
68 int lastperformanceCounterSampleTime = 0;
69
70 private class PerfCounterControl
71 {
72 public PerformanceCounter perfCounter;
73 public int lastFetch;
74 public string name;
75 public PerfCounterControl(PerformanceCounter pPc)
76 : this(pPc, String.Empty)
77 {
78 }
79 public PerfCounterControl(PerformanceCounter pPc, string pName)
80 {
81 perfCounter = pPc;
82 lastFetch = 0;
83 name = pName;
84 }
85 }
86
87 PerfCounterControl processorPercentPerfCounter = null;
88
89 PerfCounterControl processThreadCountPerfCounter = null;
90 PerfCounterControl processVirtualBytesPerfCounter = null;
91 PerfCounterControl processWorkingSetPerfCounter = null;
92
93 PerfCounterControl dotNETCLRMemoryAllocatedBytesPerSecPerfCounter = null;
94 PerfCounterControl dotNETCLRMemoryGen0HeapSizePerfCounter = null;
95 PerfCounterControl dotNETCLRMemoryGen1HeapSizePerfCounter = null;
96 PerfCounterControl dotNETCLRMemoryGen2HeapSizePerfCounter = null;
97
98 PerfCounterControl dotNETCLRLaTTotalContentionsPerfCounter = null;
99 PerfCounterControl dotNETCLRLaTContentionsPerSecPerfCounter = null;
100 PerfCounterControl dotNETCLRLaTLogicalThreadsPerfCounter = null;
101 PerfCounterControl dotNETCLRLaTPhysicalThreadsPerfCounter = null;
102
103 #region ISharedRegionModule
104 // IRegionModuleBase.Name
105 public string Name { get { return "Server Stats"; } }
106 // IRegionModuleBase.ReplaceableInterface
107 public Type ReplaceableInterface { get { return null; } }
108 // IRegionModuleBase.Initialize
109 public void Initialise(IConfigSource source)
110 {
111 IConfig cnfg = source.Configs["Statistics"];
112
113 if (cnfg != null)
114 Enabled = cnfg.GetBoolean("Enabled", true);
115 }
116 // IRegionModuleBase.Close
117 public void Close()
118 {
119 if (RegisteredStats.Count > 0)
120 {
121 foreach (Stat stat in RegisteredStats.Values)
122 {
123 StatsManager.DeregisterStat(stat);
124 stat.Dispose();
125 }
126 RegisteredStats.Clear();
127 }
128 }
129 // IRegionModuleBase.AddRegion
130 public void AddRegion(Scene scene)
131 {
132 }
133 // IRegionModuleBase.RemoveRegion
134 public void RemoveRegion(Scene scene)
135 {
136 }
137 // IRegionModuleBase.RegionLoaded
138 public void RegionLoaded(Scene scene)
139 {
140 }
141 // ISharedRegionModule.PostInitialize
142 public void PostInitialise()
143 {
144 if (RegisteredStats.Count == 0)
145 {
146 RegisterServerStats();
147 }
148 }
149 #endregion ISharedRegionModule
150
151 public void RegisterServerStats()
152 {
153 lastperformanceCounterSampleTime = Util.EnvironmentTickCount();
154 PerformanceCounter tempPC;
155 Stat tempStat;
156 string tempName;
157
158 try
159 {
160 tempName = "CPU_Percent";
161 tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total");
162 processorPercentPerfCounter = new PerfCounterControl(tempPC);
163 // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy.
164 tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor,
165 StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); },
166 StatVerbosity.Info);
167 StatsManager.RegisterStat(tempStat);
168 RegisteredStats.Add(tempName, tempStat);
169
170 /* Performance counters are not the way to go. Ick. Find another way.
171 tempName = "Thread_Count";
172 tempPC = new PerformanceCounter("Process", "Thread Count", AppDomain.CurrentDomain.FriendlyName);
173 processThreadCountPerfCounter = new PerfCounterControl(tempPC);
174 tempStat = new Stat("Thread_Count", "Thread_Count", "", "threads", CategoryServer, ContainerProcess,
175 StatType.Pull, (s) => { GetNextValue(s, processThreadCountPerfCounter); }, StatVerbosity.Info);
176 StatsManager.RegisterStat(tempStat);
177 RegisteredStats.Add(tempName, tempStat);
178
179 tempName = "Virtual_Bytes";
180 tempPC = new PerformanceCounter("Process", "Virtual Bytes", AppDomain.CurrentDomain.FriendlyName);
181 processVirtualBytesPerfCounter = new PerfCounterControl(tempPC);
182 tempStat = new Stat("Virtual_Bytes", "Virtual_Bytes", "", "MB", CategoryServer, ContainerProcess,
183 StatType.Pull, (s) => { GetNextValue(s, processVirtualBytesPerfCounter, 1024.0*1024.0); }, StatVerbosity.Info);
184 StatsManager.RegisterStat(tempStat);
185 RegisteredStats.Add(tempName, tempStat);
186
187 tempName = "Working_Set";
188 tempPC = new PerformanceCounter("Process", "Working Set", AppDomain.CurrentDomain.FriendlyName);
189 processWorkingSetPerfCounter = new PerfCounterControl(tempPC);
190 tempStat = new Stat("Working_Set", "Working_Set", "", "MB", CategoryServer, ContainerProcess,
191 StatType.Pull, (s) => { GetNextValue(s, processWorkingSetPerfCounter, 1024.0*1024.0); }, StatVerbosity.Info);
192 StatsManager.RegisterStat(tempStat);
193 RegisteredStats.Add(tempName, tempStat);
194 */
195 }
196 catch (Exception e)
197 {
198 m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e);
199 }
200
201 try
202 {
203 /* The ".NET CLR *" categories aren't working for me.
204 tempName = ""Bytes_Allocated_Per_Sec";
205 tempPC = new PerformanceCounter(".NET CLR Memory", "Allocated Bytes/sec", AppDomain.CurrentDomain.FriendlyName);
206 dotNETCLRMemoryAllocatedBytesPerSecPerfCounter = new PerfCounterControl(tempPC, tempStat);
207 tempStat = new Stat(tempName, tempName, "", "bytes/sec", ServerCategory, MemoryContainer,
208 StatType.Pull, (s) => { GetNextValue(s, dotNETCLRMemoryAllocatedBytesPerSecPerfCounter); }, StatVerbosity.Info);
209 StatsManager.RegisterStat(tempStat);
210 RegisteredStats.Add(tempName, tempStat);
211
212 tempName = "Gen_0_Heap_Size";
213 tempPC = new PerformanceCounter(".NET CLR Memory", "Gen 0 heap size", AppDomain.CurrentDomain.FriendlyName);
214 dotNETCLRMemoryGen0HeapSizePerfCounter = new PerfCounterControl(tempPC, tempStat);
215 tempStat = new Stat("Gen_0_Heap_Size", "Gen_0_Heap_Size", "", "bytes", ServerCategory, MemoryContainer,
216 StatType.Pull, (s) => { GetNextValue(s, dotNETCLRMemoryGen0HeapSizePerfCounter); }, StatVerbosity.Info);
217 StatsManager.RegisterStat(tempStat);
218 RegisteredStats.Add(tempName, tempStat);
219
220 tempName = "Gen_1_Heap_Size";
221 tempPC = new PerformanceCounter(".NET CLR Memory", "Gen 1 heap size", AppDomain.CurrentDomain.FriendlyName);
222 dotNETCLRMemoryGen1HeapSizePerfCounter = new PerfCounterControl(tempPC, tempStat);
223 tempStat = new Stat("Gen_1_Heap_Size", "Gen_1_Heap_Size", "", "bytes", ServerCategory, MemoryContainer,
224 StatType.Pull, (s) => { GetNextValue(s, dotNETCLRMemoryGen1HeapSizePerfCounter); }, StatVerbosity.Info);
225 StatsManager.RegisterStat(tempStat);
226 RegisteredStats.Add(tempName, tempStat);
227
228 tempName = "Gen_2_Heap_Size";
229 tempPC = new PerformanceCounter(".NET CLR Memory", "Gen 2 heap size", AppDomain.CurrentDomain.FriendlyName);
230 dotNETCLRMemoryGen2HeapSizePerfCounter = new PerfCounterControl(tempPC, tempStat);
231 tempStat = new Stat("Gen_2_Heap_Size", "Gen_2_Heap_Size", "", "bytes", ServerCategory, MemoryContainer,
232 StatType.Pull, (s) => { GetNextValue(s, dotNETCLRMemoryGen2HeapSizePerfCounter); }, StatVerbosity.Info);
233 StatsManager.RegisterStat(tempStat);
234 RegisteredStats.Add(tempName, tempStat);
235
236 tempName = "Total_Lock_Contentions";
237 tempPC = new PerformanceCounter(".NET CLR LocksAndThreads", "Total # of Contentions");
238 dotNETCLRLaTTotalContentionsPerfCounter = new PerfCounterControl(tempPC, tempStat);
239 tempStat = new Stat("Total_Lock_Contentions", "Total_Lock_Contentions", "", "contentions", ServerCategory, ProcessContainer,
240 StatType.Pull, (s) => { GetNextValue(s, dotNETCLRLaTTotalContentionsPerfCounter); }, StatVerbosity.Info);
241 StatsManager.RegisterStat(tempStat);
242 RegisteredStats.Add(tempName, tempStat);
243
244 tempName = "Lock_Contentions";
245 tempPC = new PerformanceCounter(".NET CLR LocksAndThreads", "Contention Rate / sec");
246 dotNETCLRLaTContentionsPerSecPerfCounter = new PerfCounterControl(tempPC, tempStat);
247 tempStat = new Stat("Lock_Contentions", "Lock_Contentions", "", "contentions/sec", ServerCategory, ProcessContainer,
248 StatType.Pull, (s) => { GetNextValue(s, dotNETCLRLaTContentionsPerSecPerfCounter); }, StatVerbosity.Info);
249 StatsManager.RegisterStat(tempStat);
250 RegisteredStats.Add(tempName, tempStat);
251
252 tempName = "Logical_Threads";
253 tempPC = new PerformanceCounter(".NET CLR LocksAndThreads", "# of current logical Threads");
254 dotNETCLRLaTLogicalThreadsPerfCounter = new PerfCounterControl(tempPC, tempStat);
255 tempStat = new Stat("Logicial_Threads", "Logicial_Threads", "", "threads", ServerCategory, ProcessContainer,
256 StatType.Pull, (s) => { GetNextValue(s, dotNETCLRLaTLogicalThreadsPerfCounter); }, StatVerbosity.Info);
257 StatsManager.RegisterStat(tempStat);
258 RegisteredStats.Add(tempName, tempStat);
259
260 tempName = "Physical_Threads";
261 tempPC = new PerformanceCounter(".NET CLR LocksAndThreads", "# of current physical Threads");
262 dotNETCLRLaTPhysicalThreadsPerfCounter = new PerfCounterControl(tempPC, tempStat);
263 tempStat = new Stat("Physical_Threads", "Physical_Threads", "", "threads", ServerCategory, ProcessContainer,
264 StatType.Pull, (s) => { GetNextValue(s, dotNETCLRLaTPhysicalThreadsPerfCounter); }, StatVerbosity.Info);
265 StatsManager.RegisterStat(tempStat);
266 RegisteredStats.Add(tempName, tempStat);
267 */
268 }
269 catch (Exception e)
270 {
271 m_log.ErrorFormat("{0} Exception creating '.NET CLR Memory': {1}", LogHeader, e);
272 }
273
274 try
275 {
276 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces();
277 // IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
278 // (network) => network.NetworkInterfaceType == NetworkInterfaceType.Ethernet);
279 // IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
280 // (network) => network.OperationalStatus == OperationalStatus.Up);
281
282 foreach (NetworkInterface nic in nics)
283 {
284 if (nic.OperationalStatus != OperationalStatus.Up || nic.NetworkInterfaceType != NetworkInterfaceType.Ethernet)
285 continue;
286
287 if (nic.Supports(NetworkInterfaceComponent.IPv4))
288 {
289 IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics();
290 if (nicStats != null)
291 {
292 tempName = "Bytes_Rcvd/" + nic.Name;
293 tempStat = new Stat(tempName, tempName, nic.Name, "KB", CategoryServer, ContainerNetwork,
294 StatType.Pull, (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); }, StatVerbosity.Info);
295 StatsManager.RegisterStat(tempStat);
296 RegisteredStats.Add(tempName, tempStat);
297
298 tempName = "Bytes_Sent/" + nic.Name;
299 tempStat = new Stat(tempName, tempName, nic.Name, "KB", CategoryServer, ContainerNetwork,
300 StatType.Pull, (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); }, StatVerbosity.Info);
301 StatsManager.RegisterStat(tempStat);
302 RegisteredStats.Add(tempName, tempStat);
303
304 tempName = "Total_Bytes/" + nic.Name;
305 tempStat = new Stat(tempName, tempName, nic.Name, "KB", CategoryServer, ContainerNetwork,
306 StatType.Pull, (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); }, StatVerbosity.Info);
307 StatsManager.RegisterStat(tempStat);
308 RegisteredStats.Add(tempName, tempStat);
309 }
310 }
311 }
312 }
313 catch (Exception e)
314 {
315 m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e);
316 }
317
318 tempName = "Process_Memory";
319 tempStat = new Stat(tempName, tempName, "", "MB", CategoryServer, ContainerMemory,
320 StatType.Pull, (s) => { s.Value = Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d; }, StatVerbosity.Info);
321 StatsManager.RegisterStat(tempStat);
322 RegisteredStats.Add(tempName, tempStat);
323
324 tempName = "Object_Memory";
325 tempStat = new Stat(tempName, tempName, "", "MB", CategoryServer, ContainerMemory,
326 StatType.Pull, (s) => { s.Value = GC.GetTotalMemory(false) / 1024d / 1024d; }, StatVerbosity.Info);
327 StatsManager.RegisterStat(tempStat);
328 RegisteredStats.Add(tempName, tempStat);
329
330 tempName = "Last_Memory_Churn";
331 tempStat = new Stat(tempName, tempName, "", "MB/sec", CategoryServer, ContainerMemory,
332 StatType.Pull, (s) => { s.Value = Math.Round(MemoryWatchdog.LastMemoryChurn * 1000d / 1024d / 1024d, 3); }, StatVerbosity.Info);
333 StatsManager.RegisterStat(tempStat);
334 RegisteredStats.Add(tempName, tempStat);
335
336 tempName = "Average_Memory_Churn";
337 tempStat = new Stat(tempName, tempName, "", "MB/sec", CategoryServer, ContainerMemory,
338 StatType.Pull, (s) => { s.Value = Math.Round(MemoryWatchdog.AverageMemoryChurn * 1000d / 1024d / 1024d, 3); }, StatVerbosity.Info);
339 StatsManager.RegisterStat(tempStat);
340 RegisteredStats.Add(tempName, tempStat);
341
342 }
343
344 // Notes on performance counters:
345 // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx
346 // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c
347 // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters
348 private delegate double PerfCounterNextValue();
349 private void GetNextValue(Stat stat, PerfCounterControl perfControl)
350 {
351 GetNextValue(stat, perfControl, 1.0);
352 }
353 private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor)
354 {
355 if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval)
356 {
357 if (perfControl != null && perfControl.perfCounter != null)
358 {
359 try
360 {
361 // Kludge for factor to run double duty. If -1, subtract the value from one
362 if (factor == -1)
363 stat.Value = 1 - perfControl.perfCounter.NextValue();
364 else
365 stat.Value = perfControl.perfCounter.NextValue() / factor;
366 }
367 catch (Exception e)
368 {
369 m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e);
370 }
371 perfControl.lastFetch = Util.EnvironmentTickCount();
372 }
373 }
374 }
375
376 private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat);
377 private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor)
378 {
379 // Get the one nic that has the name of this stat
380 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
381 (network) => network.Name == stat.Description);
382 try
383 {
384 foreach (NetworkInterface nic in nics)
385 {
386 IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics();
387 if (intrStats != null)
388 stat.Value = Math.Round(getter(intrStats) / factor, 3);
389 break;
390 }
391 }
392 catch
393 {
394 // There are times interfaces go away so we just won't update the stat for this
395 m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description);
396 }
397 }
398}
399
400public class ServerStatsAggregator : Stat
401{
402 public ServerStatsAggregator(
403 string shortName,
404 string name,
405 string description,
406 string unitName,
407 string category,
408 string container
409 )
410 : base(
411 shortName,
412 name,
413 description,
414 unitName,
415 category,
416 container,
417 StatType.Push,
418 MeasuresOfInterest.None,
419 null,
420 StatVerbosity.Info)
421 {
422 }
423 public override string ToConsoleString()
424 {
425 StringBuilder sb = new StringBuilder();
426
427 return sb.ToString();
428 }
429
430 public override OSDMap ToOSDMap()
431 {
432 OSDMap ret = new OSDMap();
433
434 return ret;
435 }
436}
437
438}