aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorDiva Canto2013-02-25 10:25:56 -0800
committerDiva Canto2013-02-25 10:25:56 -0800
commit66c5934d90061da2db7137c98951fc42bf407475 (patch)
treef3f44bbaf0a64c72004d16ded3447c4600fe8d81
parentMissing var in [LoginService] for HG Robust. (diff)
parentAdd StatsManager registration for region specific stats as collected (diff)
downloadopensim-SC-66c5934d90061da2db7137c98951fc42bf407475.zip
opensim-SC-66c5934d90061da2db7137c98951fc42bf407475.tar.gz
opensim-SC-66c5934d90061da2db7137c98951fc42bf407475.tar.bz2
opensim-SC-66c5934d90061da2db7137c98951fc42bf407475.tar.xz
Merge branch 'master' of ssh://opensimulator.org/var/git/opensim
Diffstat (limited to '')
-rwxr-xr-xOpenSim/Framework/Monitoring/Stats/CounterStat.cs439
-rw-r--r--OpenSim/Framework/Monitoring/Stats/Stat.cs2
-rw-r--r--OpenSim/Framework/Monitoring/StatsManager.cs29
-rw-r--r--OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs48
-rw-r--r--OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs328
5 files changed, 628 insertions, 218 deletions
diff --git a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs
index d81f182..caea30d 100755
--- a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs
+++ b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs
@@ -1,211 +1,228 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met: 6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright 7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright 9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the 12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products 13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission. 14 * derived from this software without specific prior written permission.
15 * 15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY 19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 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 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 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 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. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Linq; 30using System.Linq;
31using System.Text; 31using System.Text;
32 32
33using OpenMetaverse.StructuredData; 33using OpenMetaverse.StructuredData;
34 34
35namespace OpenSim.Framework.Monitoring 35namespace OpenSim.Framework.Monitoring
36{ 36{
37// Create a time histogram of events. The histogram is built in a wrap-around 37// Create a time histogram of events. The histogram is built in a wrap-around
38// array of equally distributed buckets. 38// array of equally distributed buckets.
39// For instance, a minute long histogram of second sized buckets would be: 39// For instance, a minute long histogram of second sized buckets would be:
40// new EventHistogram(60, 1000) 40// new EventHistogram(60, 1000)
41public class EventHistogram 41public class EventHistogram
42{ 42{
43 private int m_timeBase; 43 private int m_timeBase;
44 private int m_numBuckets; 44 private int m_numBuckets;
45 private int m_bucketMilliseconds; 45 private int m_bucketMilliseconds;
46 private int m_lastBucket; 46 private int m_lastBucket;
47 private int m_totalHistogramMilliseconds; 47 private int m_totalHistogramMilliseconds;
48 private long[] m_histogram; 48 private long[] m_histogram;
49 private object histoLock = new object(); 49 private object histoLock = new object();
50 50
51 public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) 51 public EventHistogram(int numberOfBuckets, int millisecondsPerBucket)
52 { 52 {
53 m_numBuckets = numberOfBuckets; 53 m_numBuckets = numberOfBuckets;
54 m_bucketMilliseconds = millisecondsPerBucket; 54 m_bucketMilliseconds = millisecondsPerBucket;
55 m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; 55 m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds;
56 56
57 m_histogram = new long[m_numBuckets]; 57 m_histogram = new long[m_numBuckets];
58 Zero(); 58 Zero();
59 m_lastBucket = 0; 59 m_lastBucket = 0;
60 m_timeBase = Util.EnvironmentTickCount(); 60 m_timeBase = Util.EnvironmentTickCount();
61 } 61 }
62 62
63 public void Event() 63 public void Event()
64 { 64 {
65 this.Event(1); 65 this.Event(1);
66 } 66 }
67 67
68 // Record an event at time 'now' in the histogram. 68 // Record an event at time 'now' in the histogram.
69 public void Event(int cnt) 69 public void Event(int cnt)
70 { 70 {
71 lock (histoLock) 71 lock (histoLock)
72 { 72 {
73 // The time as displaced from the base of the histogram 73 // The time as displaced from the base of the histogram
74 int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); 74 int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase);
75 75
76 // If more than the total time of the histogram, we just start over 76 // If more than the total time of the histogram, we just start over
77 if (bucketTime > m_totalHistogramMilliseconds) 77 if (bucketTime > m_totalHistogramMilliseconds)
78 { 78 {
79 Zero(); 79 Zero();
80 m_lastBucket = 0; 80 m_lastBucket = 0;
81 m_timeBase = Util.EnvironmentTickCount(); 81 m_timeBase = Util.EnvironmentTickCount();
82 } 82 }
83 else 83 else
84 { 84 {
85 // To which bucket should we add this event? 85 // To which bucket should we add this event?
86 int bucket = bucketTime / m_bucketMilliseconds; 86 int bucket = bucketTime / m_bucketMilliseconds;
87 87
88 // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. 88 // Advance m_lastBucket to the new bucket. Zero any buckets skipped over.
89 while (bucket != m_lastBucket) 89 while (bucket != m_lastBucket)
90 { 90 {
91 // Zero from just after the last bucket to the new bucket or the end 91 // Zero from just after the last bucket to the new bucket or the end
92 for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) 92 for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++)
93 { 93 {
94 m_histogram[jj] = 0; 94 m_histogram[jj] = 0;
95 } 95 }
96 m_lastBucket = bucket; 96 m_lastBucket = bucket;
97 // If the new bucket is off the end, wrap around to the beginning 97 // If the new bucket is off the end, wrap around to the beginning
98 if (bucket > m_numBuckets) 98 if (bucket > m_numBuckets)
99 { 99 {
100 bucket -= m_numBuckets; 100 bucket -= m_numBuckets;
101 m_lastBucket = 0; 101 m_lastBucket = 0;
102 m_histogram[m_lastBucket] = 0; 102 m_histogram[m_lastBucket] = 0;
103 m_timeBase += m_totalHistogramMilliseconds; 103 m_timeBase += m_totalHistogramMilliseconds;
104 } 104 }
105 } 105 }
106 } 106 }
107 m_histogram[m_lastBucket] += cnt; 107 m_histogram[m_lastBucket] += cnt;
108 } 108 }
109 } 109 }
110 110
111 // Get a copy of the current histogram 111 // Get a copy of the current histogram
112 public long[] GetHistogram() 112 public long[] GetHistogram()
113 { 113 {
114 long[] ret = new long[m_numBuckets]; 114 long[] ret = new long[m_numBuckets];
115 lock (histoLock) 115 lock (histoLock)
116 { 116 {
117 int indx = m_lastBucket + 1; 117 int indx = m_lastBucket + 1;
118 for (int ii = 0; ii < m_numBuckets; ii++, indx++) 118 for (int ii = 0; ii < m_numBuckets; ii++, indx++)
119 { 119 {
120 if (indx >= m_numBuckets) 120 if (indx >= m_numBuckets)
121 indx = 0; 121 indx = 0;
122 ret[ii] = m_histogram[indx]; 122 ret[ii] = m_histogram[indx];
123 } 123 }
124 } 124 }
125 return ret; 125 return ret;
126 } 126 }
127 127
128 // Get a copy of the current histogram 128 public OSDMap GetHistogramAsOSDMap()
129 public OSDArray GetHistogramAsOSDArray() 129 {
130 { 130 OSDMap ret = new OSDMap();
131 OSDArray ret = new OSDArray(m_numBuckets); 131
132 lock (histoLock) 132 ret.Add("Buckets", OSD.FromInteger(m_numBuckets));
133 { 133 ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds));
134 int indx = m_lastBucket + 1; 134 ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds));
135 for (int ii = 0; ii < m_numBuckets; ii++, indx++) 135
136 { 136 // Compute a number for the first bucket in the histogram.
137 if (indx >= m_numBuckets) 137 // This will allow readers to know how this histogram relates to any previously read histogram.
138 indx = 0; 138 int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1;
139 ret[ii] = OSD.FromLong(m_histogram[indx]); 139 ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum));
140 } 140
141 } 141 ret.Add("Values", GetHistogramAsOSDArray());
142 return ret; 142
143 } 143 return ret;
144 144 }
145 // Zero out the histogram 145 // Get a copy of the current histogram
146 public void Zero() 146 public OSDArray GetHistogramAsOSDArray()
147 { 147 {
148 lock (histoLock) 148 OSDArray ret = new OSDArray(m_numBuckets);
149 { 149 lock (histoLock)
150 for (int ii = 0; ii < m_numBuckets; ii++) 150 {
151 m_histogram[ii] = 0; 151 int indx = m_lastBucket + 1;
152 } 152 for (int ii = 0; ii < m_numBuckets; ii++, indx++)
153 } 153 {
154} 154 if (indx >= m_numBuckets)
155 155 indx = 0;
156// A statistic that wraps a counter. 156 ret[ii] = OSD.FromLong(m_histogram[indx]);
157// Built this way mostly so histograms and history can be created. 157 }
158public class CounterStat : Stat 158 }
159{ 159 return ret;
160 private SortedDictionary<string, EventHistogram> m_histograms; 160 }
161 private object counterLock = new object(); 161
162 162 // Zero out the histogram
163 public CounterStat( 163 public void Zero()
164 string shortName, 164 {
165 string name, 165 lock (histoLock)
166 string description, 166 {
167 string unitName, 167 for (int ii = 0; ii < m_numBuckets; ii++)
168 string category, 168 m_histogram[ii] = 0;
169 string container, 169 }
170 StatVerbosity verbosity) 170 }
171 : base(shortName, name, description, unitName, category, container, StatType.Push, null, verbosity) 171}
172 { 172
173 m_histograms = new SortedDictionary<string, EventHistogram>(); 173// A statistic that wraps a counter.
174 } 174// Built this way mostly so histograms and history can be created.
175 175public class CounterStat : Stat
176 // Histograms are presumably added at intialization time and the list does not change thereafter. 176{
177 // Thus no locking of the histogram list. 177 private SortedDictionary<string, EventHistogram> m_histograms;
178 public void AddHistogram(string histoName, EventHistogram histo) 178 private object counterLock = new object();
179 { 179
180 m_histograms.Add(histoName, histo); 180 public CounterStat(
181 } 181 string shortName,
182 182 string name,
183 public delegate void ProcessHistogram(string name, EventHistogram histo); 183 string description,
184 public void ForEachHistogram(ProcessHistogram process) 184 string unitName,
185 { 185 string category,
186 foreach (KeyValuePair<string, EventHistogram> kvp in m_histograms) 186 string container,
187 { 187 StatVerbosity verbosity)
188 process(kvp.Key, kvp.Value); 188 : base(shortName, name, description, unitName, category, container, StatType.Push, null, verbosity)
189 } 189 {
190 } 190 m_histograms = new SortedDictionary<string, EventHistogram>();
191 191 }
192 public void Event() 192
193 { 193 // Histograms are presumably added at intialization time and the list does not change thereafter.
194 this.Event(1); 194 // Thus no locking of the histogram list.
195 } 195 public void AddHistogram(string histoName, EventHistogram histo)
196 196 {
197 // Count the underlying counter. 197 m_histograms.Add(histoName, histo);
198 public void Event(int cnt) 198 }
199 { 199
200 lock (counterLock) 200 public delegate void ProcessHistogram(string name, EventHistogram histo);
201 { 201 public void ForEachHistogram(ProcessHistogram process)
202 base.Value += cnt; 202 {
203 203 foreach (KeyValuePair<string, EventHistogram> kvp in m_histograms)
204 foreach (EventHistogram histo in m_histograms.Values) 204 {
205 { 205 process(kvp.Key, kvp.Value);
206 histo.Event(cnt); 206 }
207 } 207 }
208 } 208
209 } 209 public void Event()
210} 210 {
211} 211 this.Event(1);
212 }
213
214 // Count the underlying counter.
215 public void Event(int cnt)
216 {
217 lock (counterLock)
218 {
219 base.Value += cnt;
220
221 foreach (EventHistogram histo in m_histograms.Values)
222 {
223 histo.Event(cnt);
224 }
225 }
226 }
227}
228}
diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs
index c8d9174..2e7665f 100644
--- a/OpenSim/Framework/Monitoring/Stats/Stat.cs
+++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs
@@ -211,7 +211,7 @@ namespace OpenSim.Framework.Monitoring
211 public virtual string ToConsoleString() 211 public virtual string ToConsoleString()
212 { 212 {
213 StringBuilder sb = new StringBuilder(); 213 StringBuilder sb = new StringBuilder();
214 sb.AppendFormat("{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName); 214 sb.AppendFormat("{0}.{1}.{2} : {3} {4}", Category, Container, ShortName, Value, UnitName);
215 215
216 AppendMeasuresOfInterest(sb); 216 AppendMeasuresOfInterest(sb);
217 217
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
index 910907e..24db6d4 100644
--- a/OpenSim/Framework/Monitoring/StatsManager.cs
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -85,6 +85,7 @@ namespace OpenSim.Framework.Monitoring
85 if (cmd.Length > 2) 85 if (cmd.Length > 2)
86 { 86 {
87 var categoryName = cmd[2]; 87 var categoryName = cmd[2];
88 var containerName = cmd.Length > 3 ? cmd[3] : String.Empty;
88 89
89 if (categoryName == AllSubCommand) 90 if (categoryName == AllSubCommand)
90 { 91 {
@@ -108,7 +109,20 @@ namespace OpenSim.Framework.Monitoring
108 } 109 }
109 else 110 else
110 { 111 {
111 OutputCategoryStatsToConsole(con, category); 112 if (String.IsNullOrEmpty(containerName))
113 OutputCategoryStatsToConsole(con, category);
114 else
115 {
116 SortedDictionary<string, Stat> container;
117 if (category.TryGetValue(containerName, out container))
118 {
119 OutputContainerStatsToConsole(con, container);
120 }
121 else
122 {
123 con.OutputFormat("No such container {0} in category {1}", containerName, categoryName);
124 }
125 }
112 } 126 }
113 } 127 }
114 } 128 }
@@ -124,10 +138,15 @@ namespace OpenSim.Framework.Monitoring
124 { 138 {
125 foreach (var container in category.Values) 139 foreach (var container in category.Values)
126 { 140 {
127 foreach (Stat stat in container.Values) 141 OutputContainerStatsToConsole(con, container);
128 { 142 }
129 con.Output(stat.ToConsoleString()); 143 }
130 } 144
145 private static void OutputContainerStatsToConsole( ICommandConsole con, SortedDictionary<string, Stat> container)
146 {
147 foreach (Stat stat in container.Values)
148 {
149 con.Output(stat.ToConsoleString());
131 } 150 }
132 } 151 }
133 152
diff --git a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs
index d84460a..4c9ee06 100644
--- a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs
@@ -33,6 +33,7 @@ using log4net;
33using Nini.Config; 33using Nini.Config;
34using OpenMetaverse; 34using OpenMetaverse;
35using OpenSim.Framework; 35using OpenSim.Framework;
36using OpenSim.Framework.Monitoring;
36using OpenSim.Framework.Servers; 37using OpenSim.Framework.Servers;
37using OpenSim.Region.CoreModules.Framework.Monitoring.Alerts; 38using OpenSim.Region.CoreModules.Framework.Monitoring.Alerts;
38using OpenSim.Region.CoreModules.Framework.Monitoring.Monitors; 39using OpenSim.Region.CoreModules.Framework.Monitoring.Monitors;
@@ -100,6 +101,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring
100 "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName), StatsPage); 101 "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName), StatsPage);
101 102
102 AddMonitors(); 103 AddMonitors();
104 RegisterStatsManagerRegionStatistics();
103 } 105 }
104 106
105 public void RemoveRegion(Scene scene) 107 public void RemoveRegion(Scene scene)
@@ -109,6 +111,9 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring
109 111
110 MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + m_scene.RegionInfo.RegionID); 112 MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + m_scene.RegionInfo.RegionID);
111 MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName)); 113 MainServer.Instance.RemoveHTTPHandler("GET", "/monitorstats/" + Uri.EscapeDataString(m_scene.RegionInfo.RegionName));
114
115 UnRegisterStatsManagerRegionStatistics();
116
112 m_scene = null; 117 m_scene = null;
113 } 118 }
114 119
@@ -399,6 +404,47 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring
399 { 404 {
400 m_log.Error("[Monitor] " + reporter.Name + " for " + m_scene.RegionInfo.RegionName + " reports " + reason + " (Fatal: " + fatal + ")"); 405 m_log.Error("[Monitor] " + reporter.Name + " for " + m_scene.RegionInfo.RegionName + " reports " + reason + " (Fatal: " + fatal + ")");
401 } 406 }
407
408 private List<Stat> registeredStats = new List<Stat>();
409 private void MakeStat(string pName, string pUnitName, Action<Stat> act)
410 {
411 Stat tempStat = new Stat(pName, pName, pName, pUnitName, "scene", m_scene.RegionInfo.RegionName, StatType.Pull, act, StatVerbosity.Info);
412 StatsManager.RegisterStat(tempStat);
413 registeredStats.Add(tempStat);
414 }
415 private void RegisterStatsManagerRegionStatistics()
416 {
417 string regionName = m_scene.RegionInfo.RegionName;
418
419 MakeStat("RootAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetRootAgentCount(); });
420 MakeStat("ChildAgents", "avatars", (s) => { s.Value = m_scene.SceneGraph.GetChildAgentCount(); });
421 MakeStat("TotalPrims", "objects", (s) => { s.Value = m_scene.SceneGraph.GetTotalObjectsCount(); });
422 MakeStat("ActivePrims", "objects", (s) => { s.Value = m_scene.SceneGraph.GetActiveObjectsCount(); });
423 MakeStat("ActiveScripts", "scripts", (s) => { s.Value = m_scene.SceneGraph.GetActiveScriptsCount(); });
424
425 MakeStat("TimeDilation", "sec/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[0]; });
426 MakeStat("SimFPS", "fps", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[1]; });
427 MakeStat("PhysicsFPS", "fps", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[2]; });
428 MakeStat("AgentUpdates", "updates/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[3]; });
429 MakeStat("FrameTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[8]; });
430 MakeStat("NetTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[9]; });
431 MakeStat("OtherTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[12]; });
432 MakeStat("PhysicsTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[10]; });
433 MakeStat("AgentTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[16]; });
434 MakeStat("ImageTime", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[11]; });
435 MakeStat("ScriptLines", "lines/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[20]; });
436 MakeStat("SimSpareMS", "ms/sec", (s) => { s.Value = m_scene.StatsReporter.LastReportedSimStats[21]; });
437 }
438
439 private void UnRegisterStatsManagerRegionStatistics()
440 {
441 foreach (Stat stat in registeredStats)
442 {
443 StatsManager.DeregisterStat(stat);
444 stat.Dispose();
445 }
446 registeredStats.Clear();
447 }
402 448
403 } 449 }
404} 450} \ No newline at end of file
diff --git a/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs b/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs
new file mode 100644
index 0000000..a3d2436
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Framework/Monitoring/ServerStats.cs
@@ -0,0 +1,328 @@
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 pUnit, string pContainer, Action<Stat> act)
144 {
145 Stat stat = new Stat(pName, pName, "", pUnit, CategoryServer, pContainer, StatType.Pull, act, StatVerbosity.Info);
146 StatsManager.RegisterStat(stat);
147 RegisteredStats.Add(pName, stat);
148 }
149
150 public void RegisterServerStats()
151 {
152 lastperformanceCounterSampleTime = Util.EnvironmentTickCount();
153 PerformanceCounter tempPC;
154 Stat tempStat;
155 string tempName;
156
157 try
158 {
159 tempName = "CPUPercent";
160 tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total");
161 processorPercentPerfCounter = new PerfCounterControl(tempPC);
162 // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy.
163 tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor,
164 StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); },
165 StatVerbosity.Info);
166 StatsManager.RegisterStat(tempStat);
167 RegisteredStats.Add(tempName, tempStat);
168
169 MakeStat("TotalProcessorTime", "sec", ContainerProcessor,
170 (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; });
171
172 MakeStat("UserProcessorTime", "sec", ContainerProcessor,
173 (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; });
174
175 MakeStat("PrivilegedProcessorTime", "sec", ContainerProcessor,
176 (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; });
177
178 MakeStat("Threads", "threads", ContainerProcessor,
179 (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; });
180 }
181 catch (Exception e)
182 {
183 m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e);
184 }
185
186 try
187 {
188 List<string> okInterfaceTypes = new List<string>(NetworkInterfaceTypes.Split(','));
189
190 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces();
191 foreach (NetworkInterface nic in nics)
192 {
193 if (nic.OperationalStatus != OperationalStatus.Up)
194 continue;
195
196 string nicInterfaceType = nic.NetworkInterfaceType.ToString();
197 if (!okInterfaceTypes.Contains(nicInterfaceType))
198 {
199 m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'. To include, add to [Monitoring]NetworkInterfaceTypes='Ethernet,Loopback'",
200 LogHeader, nic.Name, nicInterfaceType);
201 continue;
202 }
203
204 if (nic.Supports(NetworkInterfaceComponent.IPv4))
205 {
206 IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics();
207 if (nicStats != null)
208 {
209 MakeStat("BytesRcvd/" + nic.Name, "KB", ContainerNetwork,
210 (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); });
211 MakeStat("BytesSent/" + nic.Name, "KB", ContainerNetwork,
212 (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); });
213 MakeStat("TotalBytes/" + nic.Name, "KB", ContainerNetwork,
214 (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); });
215 }
216 }
217 }
218 }
219 catch (Exception e)
220 {
221 m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e);
222 }
223
224 MakeStat("ProcessMemory", "MB", ContainerMemory,
225 (s) => { s.Value = Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d; });
226 MakeStat("ObjectMemory", "MB", ContainerMemory,
227 (s) => { s.Value = GC.GetTotalMemory(false) / 1024d / 1024d; });
228 MakeStat("LastMemoryChurn", "MB/sec", ContainerMemory,
229 (s) => { s.Value = Math.Round(MemoryWatchdog.LastMemoryChurn * 1000d / 1024d / 1024d, 3); });
230 MakeStat("AverageMemoryChurn", "MB/sec", ContainerMemory,
231 (s) => { s.Value = Math.Round(MemoryWatchdog.AverageMemoryChurn * 1000d / 1024d / 1024d, 3); });
232 }
233
234 // Notes on performance counters:
235 // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx
236 // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c
237 // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters
238 private delegate double PerfCounterNextValue();
239 private void GetNextValue(Stat stat, PerfCounterControl perfControl)
240 {
241 GetNextValue(stat, perfControl, 1.0);
242 }
243 private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor)
244 {
245 if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval)
246 {
247 if (perfControl != null && perfControl.perfCounter != null)
248 {
249 try
250 {
251 // Kludge for factor to run double duty. If -1, subtract the value from one
252 if (factor == -1)
253 stat.Value = 1 - perfControl.perfCounter.NextValue();
254 else
255 stat.Value = perfControl.perfCounter.NextValue() / factor;
256 }
257 catch (Exception e)
258 {
259 m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e);
260 }
261 perfControl.lastFetch = Util.EnvironmentTickCount();
262 }
263 }
264 }
265
266 private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat);
267 private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor)
268 {
269 // Get the one nic that has the name of this stat
270 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
271 (network) => network.Name == stat.Description);
272 try
273 {
274 foreach (NetworkInterface nic in nics)
275 {
276 IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics();
277 if (intrStats != null)
278 stat.Value = Math.Round(getter(intrStats) / factor, 3);
279 break;
280 }
281 }
282 catch
283 {
284 // There are times interfaces go away so we just won't update the stat for this
285 m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description);
286 }
287 }
288}
289
290public class ServerStatsAggregator : Stat
291{
292 public ServerStatsAggregator(
293 string shortName,
294 string name,
295 string description,
296 string unitName,
297 string category,
298 string container
299 )
300 : base(
301 shortName,
302 name,
303 description,
304 unitName,
305 category,
306 container,
307 StatType.Push,
308 MeasuresOfInterest.None,
309 null,
310 StatVerbosity.Info)
311 {
312 }
313 public override string ToConsoleString()
314 {
315 StringBuilder sb = new StringBuilder();
316
317 return sb.ToString();
318 }
319
320 public override OSDMap ToOSDMap()
321 {
322 OSDMap ret = new OSDMap();
323
324 return ret;
325 }
326}
327
328}