aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework')
-rw-r--r--OpenSim/Framework/Monitoring/BaseStatsCollector.cs14
-rw-r--r--OpenSim/Framework/Monitoring/MemoryWatchdog.cs8
-rw-r--r--OpenSim/Framework/Monitoring/ServerStatsCollector.cs349
-rw-r--r--OpenSim/Framework/Monitoring/Stats/Stat.cs24
-rw-r--r--OpenSim/Framework/Monitoring/StatsManager.cs130
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs46
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs30
-rw-r--r--OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs20
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs95
-rw-r--r--OpenSim/Framework/Util.cs104
10 files changed, 635 insertions, 185 deletions
diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
index 23dba09..96536e8 100644
--- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
@@ -1,4 +1,4 @@
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 *
@@ -44,16 +44,16 @@ namespace OpenSim.Framework.Monitoring
44 sb.Append("MEMORY STATISTICS"); 44 sb.Append("MEMORY STATISTICS");
45 sb.Append(Environment.NewLine); 45 sb.Append(Environment.NewLine);
46 sb.AppendFormat( 46 sb.AppendFormat(
47 "Allocated to OpenSim objects: {0} MB\n", 47 "Heap allocated to OpenSim : {0} MB\n",
48 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); 48 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
49 49
50 sb.AppendFormat( 50 sb.AppendFormat(
51 "OpenSim last object memory churn : {0} MB/s\n", 51 "Last heap allocation rate : {0} MB/s\n",
52 Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3)); 52 Math.Round((MemoryWatchdog.LastHeapAllocationRate * 1000) / 1024.0 / 1024, 3));
53 53
54 sb.AppendFormat( 54 sb.AppendFormat(
55 "OpenSim average object memory churn : {0} MB/s\n", 55 "Average heap allocation rate: {0} MB/s\n",
56 Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3)); 56 Math.Round((MemoryWatchdog.AverageHeapAllocationRate * 1000) / 1024.0 / 1024, 3));
57 57
58 Process myprocess = Process.GetCurrentProcess(); 58 Process myprocess = Process.GetCurrentProcess();
59 if (!myprocess.HasExited) 59 if (!myprocess.HasExited)
diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
index c6010cd..c474622 100644
--- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
+++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
@@ -60,17 +60,17 @@ namespace OpenSim.Framework.Monitoring
60 private static bool m_enabled; 60 private static bool m_enabled;
61 61
62 /// <summary> 62 /// <summary>
63 /// Last memory churn in bytes per millisecond. 63 /// Average heap allocation rate in bytes per millisecond.
64 /// </summary> 64 /// </summary>
65 public static double AverageMemoryChurn 65 public static double AverageHeapAllocationRate
66 { 66 {
67 get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; } 67 get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; }
68 } 68 }
69 69
70 /// <summary> 70 /// <summary>
71 /// Average memory churn in bytes per millisecond. 71 /// Last heap allocation in bytes
72 /// </summary> 72 /// </summary>
73 public static double LastMemoryChurn 73 public static double LastHeapAllocationRate
74 { 74 {
75 get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; } 75 get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; }
76 } 76 }
diff --git a/OpenSim/Framework/Monitoring/ServerStatsCollector.cs b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
new file mode 100644
index 0000000..ac0f0bc
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
@@ -0,0 +1,349 @@
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 ContainerThreadpool = "threadpool";
53 public readonly string ContainerProcessor = "processor";
54 public readonly string ContainerMemory = "memory";
55 public readonly string ContainerNetwork = "network";
56 public readonly string ContainerProcess = "process";
57
58 public string NetworkInterfaceTypes = "Ethernet";
59
60 readonly int performanceCounterSampleInterval = 500;
61// int lastperformanceCounterSampleTime = 0;
62
63 private class PerfCounterControl
64 {
65 public PerformanceCounter perfCounter;
66 public int lastFetch;
67 public string name;
68 public PerfCounterControl(PerformanceCounter pPc)
69 : this(pPc, String.Empty)
70 {
71 }
72 public PerfCounterControl(PerformanceCounter pPc, string pName)
73 {
74 perfCounter = pPc;
75 lastFetch = 0;
76 name = pName;
77 }
78 }
79
80 PerfCounterControl processorPercentPerfCounter = null;
81
82 // IRegionModuleBase.Initialize
83 public void Initialise(IConfigSource source)
84 {
85 IConfig cfg = source.Configs["Monitoring"];
86
87 if (cfg != null)
88 Enabled = cfg.GetBoolean("ServerStatsEnabled", true);
89
90 if (Enabled)
91 {
92 NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet");
93 }
94 }
95
96 public void Start()
97 {
98 if (RegisteredStats.Count == 0)
99 RegisterServerStats();
100 }
101
102 public void Close()
103 {
104 if (RegisteredStats.Count > 0)
105 {
106 foreach (Stat stat in RegisteredStats.Values)
107 {
108 StatsManager.DeregisterStat(stat);
109 stat.Dispose();
110 }
111 RegisteredStats.Clear();
112 }
113 }
114
115 private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act)
116 {
117 MakeStat(pName, pDesc, pUnit, pContainer, act, MeasuresOfInterest.None);
118 }
119
120 private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action<Stat> act, MeasuresOfInterest moi)
121 {
122 string desc = pDesc;
123 if (desc == null)
124 desc = pName;
125 Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, moi, act, StatVerbosity.Debug);
126 StatsManager.RegisterStat(stat);
127 RegisteredStats.Add(pName, stat);
128 }
129
130 public void RegisterServerStats()
131 {
132// lastperformanceCounterSampleTime = Util.EnvironmentTickCount();
133 PerformanceCounter tempPC;
134 Stat tempStat;
135 string tempName;
136
137 try
138 {
139 tempName = "CPUPercent";
140 tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total");
141 processorPercentPerfCounter = new PerfCounterControl(tempPC);
142 // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy.
143 tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor,
144 StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter, Util.IsWindows() ? 1 : -1); },
145 StatVerbosity.Info);
146 StatsManager.RegisterStat(tempStat);
147 RegisteredStats.Add(tempName, tempStat);
148
149 MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor,
150 (s) => { s.Value = Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds; });
151
152 MakeStat("UserProcessorTime", null, "sec", ContainerProcessor,
153 (s) => { s.Value = Process.GetCurrentProcess().UserProcessorTime.TotalSeconds; });
154
155 MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor,
156 (s) => { s.Value = Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds; });
157
158 MakeStat("Threads", null, "threads", ContainerProcessor,
159 (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; });
160 }
161 catch (Exception e)
162 {
163 m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e);
164 }
165
166 MakeStat("BuiltinThreadpoolWorkerThreadsAvailable", null, "threads", ContainerThreadpool,
167 s =>
168 {
169 int workerThreads, iocpThreads;
170 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
171 s.Value = workerThreads;
172 });
173
174 MakeStat("BuiltinThreadpoolIOCPThreadsAvailable", null, "threads", ContainerThreadpool,
175 s =>
176 {
177 int workerThreads, iocpThreads;
178 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
179 s.Value = iocpThreads;
180 });
181
182 if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool && Util.GetSmartThreadPoolInfo() != null)
183 {
184 MakeStat("STPMaxThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxThreads);
185 MakeStat("STPMinThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MinThreads);
186 MakeStat("STPConcurrency", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxConcurrentWorkItems);
187 MakeStat("STPActiveThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().ActiveThreads);
188 MakeStat("STPInUseThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().InUseThreads);
189 MakeStat("STPWorkItemsWaiting", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().WaitingCallbacks);
190 }
191
192 MakeStat(
193 "HTTPRequestsMade",
194 "Number of outbound HTTP requests made",
195 "requests",
196 ContainerNetwork,
197 s => s.Value = WebUtil.RequestNumber,
198 MeasuresOfInterest.AverageChangeOverTime);
199
200 try
201 {
202 List<string> okInterfaceTypes = new List<string>(NetworkInterfaceTypes.Split(','));
203
204 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces();
205 foreach (NetworkInterface nic in nics)
206 {
207 if (nic.OperationalStatus != OperationalStatus.Up)
208 continue;
209
210 string nicInterfaceType = nic.NetworkInterfaceType.ToString();
211 if (!okInterfaceTypes.Contains(nicInterfaceType))
212 {
213 m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.",
214 LogHeader, nic.Name, nicInterfaceType);
215 m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}",
216 LogHeader, NetworkInterfaceTypes);
217 continue;
218 }
219
220 if (nic.Supports(NetworkInterfaceComponent.IPv4))
221 {
222 IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics();
223 if (nicStats != null)
224 {
225 MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork,
226 (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); });
227 MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork,
228 (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); });
229 MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork,
230 (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); });
231 }
232 }
233 // TODO: add IPv6 (it may actually happen someday)
234 }
235 }
236 catch (Exception e)
237 {
238 m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e);
239 }
240
241 MakeStat("ProcessMemory", null, "MB", ContainerMemory,
242 (s) => { s.Value = Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d, 3); });
243 MakeStat("HeapMemory", null, "MB", ContainerMemory,
244 (s) => { s.Value = Math.Round(GC.GetTotalMemory(false) / 1024d / 1024d, 3); });
245 MakeStat("LastHeapAllocationRate", null, "MB/sec", ContainerMemory,
246 (s) => { s.Value = Math.Round(MemoryWatchdog.LastHeapAllocationRate * 1000d / 1024d / 1024d, 3); });
247 MakeStat("AverageHeapAllocationRate", null, "MB/sec", ContainerMemory,
248 (s) => { s.Value = Math.Round(MemoryWatchdog.AverageHeapAllocationRate * 1000d / 1024d / 1024d, 3); });
249 }
250
251 // Notes on performance counters:
252 // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx
253 // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c
254 // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters
255 private delegate double PerfCounterNextValue();
256 private void GetNextValue(Stat stat, PerfCounterControl perfControl)
257 {
258 GetNextValue(stat, perfControl, 1.0);
259 }
260 private void GetNextValue(Stat stat, PerfCounterControl perfControl, double factor)
261 {
262 if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval)
263 {
264 if (perfControl != null && perfControl.perfCounter != null)
265 {
266 try
267 {
268 // Kludge for factor to run double duty. If -1, subtract the value from one
269 if (factor == -1)
270 stat.Value = 1 - perfControl.perfCounter.NextValue();
271 else
272 stat.Value = perfControl.perfCounter.NextValue() / factor;
273 }
274 catch (Exception e)
275 {
276 m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e);
277 }
278 perfControl.lastFetch = Util.EnvironmentTickCount();
279 }
280 }
281 }
282
283 // Lookup the nic that goes with this stat and set the value by using a fetch action.
284 // Not sure about closure with delegates inside delegates.
285 private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat);
286 private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor)
287 {
288 // Get the one nic that has the name of this stat
289 IEnumerable<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(
290 (network) => network.Name == stat.Description);
291 try
292 {
293 foreach (NetworkInterface nic in nics)
294 {
295 IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics();
296 if (intrStats != null)
297 {
298 double newVal = Math.Round(getter(intrStats) / factor, 3);
299 stat.Value = newVal;
300 }
301 break;
302 }
303 }
304 catch
305 {
306 // There are times interfaces go away so we just won't update the stat for this
307 m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description);
308 }
309 }
310 }
311
312 public class ServerStatsAggregator : Stat
313 {
314 public ServerStatsAggregator(
315 string shortName,
316 string name,
317 string description,
318 string unitName,
319 string category,
320 string container
321 )
322 : base(
323 shortName,
324 name,
325 description,
326 unitName,
327 category,
328 container,
329 StatType.Push,
330 MeasuresOfInterest.None,
331 null,
332 StatVerbosity.Info)
333 {
334 }
335 public override string ToConsoleString()
336 {
337 StringBuilder sb = new StringBuilder();
338
339 return sb.ToString();
340 }
341
342 public override OSDMap ToOSDMap()
343 {
344 OSDMap ret = new OSDMap();
345
346 return ret;
347 }
348 }
349}
diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs
index 2e7665f..c57ee0c 100644
--- a/OpenSim/Framework/Monitoring/Stats/Stat.cs
+++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs
@@ -27,8 +27,10 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Linq;
31using System.Reflection;
30using System.Text; 32using System.Text;
31 33using log4net;
32using OpenMetaverse.StructuredData; 34using OpenMetaverse.StructuredData;
33 35
34namespace OpenSim.Framework.Monitoring 36namespace OpenSim.Framework.Monitoring
@@ -38,6 +40,10 @@ namespace OpenSim.Framework.Monitoring
38 /// </summary> 40 /// </summary>
39 public class Stat : IDisposable 41 public class Stat : IDisposable
40 { 42 {
43// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44
45 public static readonly char[] DisallowedShortNameCharacters = { '.' };
46
41 /// <summary> 47 /// <summary>
42 /// Category of this stat (e.g. cache, scene, etc). 48 /// Category of this stat (e.g. cache, scene, etc).
43 /// </summary> 49 /// </summary>
@@ -95,7 +101,7 @@ namespace OpenSim.Framework.Monitoring
95 /// <remarks> 101 /// <remarks>
96 /// Will be null if no measures of interest require samples. 102 /// Will be null if no measures of interest require samples.
97 /// </remarks> 103 /// </remarks>
98 private static Queue<double> m_samples; 104 private Queue<double> m_samples;
99 105
100 /// <summary> 106 /// <summary>
101 /// Maximum number of statistical samples. 107 /// Maximum number of statistical samples.
@@ -162,6 +168,12 @@ namespace OpenSim.Framework.Monitoring
162 throw new Exception( 168 throw new Exception(
163 string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category)); 169 string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category));
164 170
171 foreach (char c in DisallowedShortNameCharacters)
172 {
173 if (shortName.IndexOf(c) != -1)
174 throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c));
175 }
176
165 ShortName = shortName; 177 ShortName = shortName;
166 Name = name; 178 Name = name;
167 Description = description; 179 Description = description;
@@ -204,6 +216,8 @@ namespace OpenSim.Framework.Monitoring
204 if (m_samples.Count >= m_maxSamples) 216 if (m_samples.Count >= m_maxSamples)
205 m_samples.Dequeue(); 217 m_samples.Dequeue();
206 218
219// m_log.DebugFormat("[STAT]: Recording value {0} for {1}", newValue, Name);
220
207 m_samples.Enqueue(newValue); 221 m_samples.Enqueue(newValue);
208 } 222 }
209 } 223 }
@@ -242,6 +256,10 @@ namespace OpenSim.Framework.Monitoring
242 256
243 lock (m_samples) 257 lock (m_samples)
244 { 258 {
259// m_log.DebugFormat(
260// "[STAT]: Samples for {0} are {1}",
261// Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray()));
262
245 foreach (double s in m_samples) 263 foreach (double s in m_samples)
246 { 264 {
247 if (lastSample != null) 265 if (lastSample != null)
@@ -253,7 +271,7 @@ namespace OpenSim.Framework.Monitoring
253 271
254 int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1; 272 int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1;
255 273
256 sb.AppendFormat(", {0:0.##}{1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName); 274 sb.AppendFormat(", {0:0.##} {1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName);
257 } 275 }
258 } 276 }
259 } 277 }
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
index 24db6d4..12d3a75 100644
--- a/OpenSim/Framework/Monitoring/StatsManager.cs
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -27,6 +27,7 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Linq;
30using System.Text; 31using System.Text;
31 32
32namespace OpenSim.Framework.Monitoring 33namespace OpenSim.Framework.Monitoring
@@ -54,13 +55,13 @@ namespace OpenSim.Framework.Monitoring
54 public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats 55 public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats
55 = new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>(); 56 = new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>();
56 57
57 private static AssetStatsCollector assetStats; 58// private static AssetStatsCollector assetStats;
58 private static UserStatsCollector userStats; 59// private static UserStatsCollector userStats;
59 private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); 60// private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
60 61
61 public static AssetStatsCollector AssetStats { get { return assetStats; } } 62// public static AssetStatsCollector AssetStats { get { return assetStats; } }
62 public static UserStatsCollector UserStats { get { return userStats; } } 63// public static UserStatsCollector UserStats { get { return userStats; } }
63 public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } 64 public static SimExtraStatsCollector SimExtraStats { get; set; }
64 65
65 public static void RegisterConsoleCommands(ICommandConsole console) 66 public static void RegisterConsoleCommands(ICommandConsole console)
66 { 67 {
@@ -68,12 +69,14 @@ namespace OpenSim.Framework.Monitoring
68 "General", 69 "General",
69 false, 70 false,
70 "show stats", 71 "show stats",
71 "show stats [list|all|<category>]", 72 "show stats [list|all|(<category>[.<container>])+",
72 "Show statistical information for this server", 73 "Show statistical information for this server",
73 "If no final argument is specified then legacy statistics information is currently shown.\n" 74 "If no final argument is specified then legacy statistics information is currently shown.\n"
74 + "If list is specified then statistic categories are shown.\n" 75 + "'list' argument will show statistic categories.\n"
75 + "If all is specified then all registered statistics are shown.\n" 76 + "'all' will show all statistics.\n"
76 + "If a category name is specified then only statistics from that category are shown.\n" 77 + "A <category> name will show statistics from that category.\n"
78 + "A <category>.<container> name will show statistics from that category in that container.\n"
79 + "More than one name can be given separated by spaces.\n"
77 + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS", 80 + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
78 HandleShowStatsCommand); 81 HandleShowStatsCommand);
79 } 82 }
@@ -84,43 +87,47 @@ namespace OpenSim.Framework.Monitoring
84 87
85 if (cmd.Length > 2) 88 if (cmd.Length > 2)
86 { 89 {
87 var categoryName = cmd[2]; 90 foreach (string name in cmd.Skip(2))
88 var containerName = cmd.Length > 3 ? cmd[3] : String.Empty;
89
90 if (categoryName == AllSubCommand)
91 { 91 {
92 foreach (var category in RegisteredStats.Values) 92 string[] components = name.Split('.');
93
94 string categoryName = components[0];
95 string containerName = components.Length > 1 ? components[1] : null;
96
97 if (categoryName == AllSubCommand)
93 { 98 {
94 OutputCategoryStatsToConsole(con, category); 99 OutputAllStatsToConsole(con);
95 } 100 }
96 } 101 else if (categoryName == ListSubCommand)
97 else if (categoryName == ListSubCommand)
98 {
99 con.Output("Statistic categories available are:");
100 foreach (string category in RegisteredStats.Keys)
101 con.OutputFormat(" {0}", category);
102 }
103 else
104 {
105 SortedDictionary<string, SortedDictionary<string, Stat>> category;
106 if (!RegisteredStats.TryGetValue(categoryName, out category))
107 { 102 {
108 con.OutputFormat("No such category as {0}", categoryName); 103 con.Output("Statistic categories available are:");
104 foreach (string category in RegisteredStats.Keys)
105 con.OutputFormat(" {0}", category);
109 } 106 }
110 else 107 else
111 { 108 {
112 if (String.IsNullOrEmpty(containerName)) 109 SortedDictionary<string, SortedDictionary<string, Stat>> category;
113 OutputCategoryStatsToConsole(con, category); 110 if (!RegisteredStats.TryGetValue(categoryName, out category))
111 {
112 con.OutputFormat("No such category as {0}", categoryName);
113 }
114 else 114 else
115 { 115 {
116 SortedDictionary<string, Stat> container; 116 if (String.IsNullOrEmpty(containerName))
117 if (category.TryGetValue(containerName, out container))
118 { 117 {
119 OutputContainerStatsToConsole(con, container); 118 OutputCategoryStatsToConsole(con, category);
120 } 119 }
121 else 120 else
122 { 121 {
123 con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); 122 SortedDictionary<string, Stat> container;
123 if (category.TryGetValue(containerName, out container))
124 {
125 OutputContainerStatsToConsole(con, container);
126 }
127 else
128 {
129 con.OutputFormat("No such container {0} in category {1}", containerName, categoryName);
130 }
124 } 131 }
125 } 132 }
126 } 133 }
@@ -129,7 +136,18 @@ namespace OpenSim.Framework.Monitoring
129 else 136 else
130 { 137 {
131 // Legacy 138 // Legacy
132 con.Output(SimExtraStats.Report()); 139 if (SimExtraStats != null)
140 con.Output(SimExtraStats.Report());
141 else
142 OutputAllStatsToConsole(con);
143 }
144 }
145
146 private static void OutputAllStatsToConsole(ICommandConsole con)
147 {
148 foreach (var category in RegisteredStats.Values)
149 {
150 OutputCategoryStatsToConsole(con, category);
133 } 151 }
134 } 152 }
135 153
@@ -150,27 +168,27 @@ namespace OpenSim.Framework.Monitoring
150 } 168 }
151 } 169 }
152 170
153 /// <summary> 171// /// <summary>
154 /// Start collecting statistics related to assets. 172// /// Start collecting statistics related to assets.
155 /// Should only be called once. 173// /// Should only be called once.
156 /// </summary> 174// /// </summary>
157 public static AssetStatsCollector StartCollectingAssetStats() 175// public static AssetStatsCollector StartCollectingAssetStats()
158 { 176// {
159 assetStats = new AssetStatsCollector(); 177// assetStats = new AssetStatsCollector();
160 178//
161 return assetStats; 179// return assetStats;
162 } 180// }
163 181//
164 /// <summary> 182// /// <summary>
165 /// Start collecting statistics related to users. 183// /// Start collecting statistics related to users.
166 /// Should only be called once. 184// /// Should only be called once.
167 /// </summary> 185// /// </summary>
168 public static UserStatsCollector StartCollectingUserStats() 186// public static UserStatsCollector StartCollectingUserStats()
169 { 187// {
170 userStats = new UserStatsCollector(); 188// userStats = new UserStatsCollector();
171 189//
172 return userStats; 190// return userStats;
173 } 191// }
174 192
175 /// <summary> 193 /// <summary>
176 /// Registers a statistic. 194 /// Registers a statistic.
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 6c04c69..bfd67c7 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();
97 99
98 m_console.Commands.AddCommand("General", false, "shutdown", 100 base.ShutdownSpecific();
99 "shutdown", 101
100 "Quit the application", HandleQuit); 102 Environment.Exit(0);
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>
@@ -153,25 +150,8 @@ namespace OpenSim.Framework.Servers
153 timeTaken.Minutes, timeTaken.Seconds); 150 timeTaken.Minutes, timeTaken.Seconds);
154 } 151 }
155 152
156 /// <summary> 153 public string osSecret
157 /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
158 /// </summary>
159 public virtual void Shutdown()
160 { 154 {
161 ShutdownSpecific();
162
163 m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting...");
164 RemovePIDFile();
165
166 Environment.Exit(0);
167 }
168
169 private void HandleQuit(string module, string[] args)
170 {
171 Shutdown();
172 }
173
174 public string osSecret {
175 // Secret uuid for the simulator 155 // Secret uuid for the simulator
176 get { return m_osSecret; } 156 get { return m_osSecret; }
177 } 157 }
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index d29cc61..80d1080 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -54,7 +54,6 @@ namespace OpenSim.Framework.Servers.HttpServer
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter(); 55 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
56 56
57
58 /// <summary> 57 /// <summary>
59 /// This is a pending websocket request before it got an sucessful upgrade response. 58 /// This is a pending websocket request before it got an sucessful upgrade response.
60 /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to 59 /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to
@@ -81,6 +80,11 @@ namespace OpenSim.Framework.Servers.HttpServer
81 /// </remarks> 80 /// </remarks>
82 public int RequestNumber { get; private set; } 81 public int RequestNumber { get; private set; }
83 82
83 /// <summary>
84 /// Statistic for holding number of requests processed.
85 /// </summary>
86 private Stat m_requestsProcessedStat;
87
84 private volatile int NotSocketErrors = 0; 88 private volatile int NotSocketErrors = 0;
85 public volatile bool HTTPDRunning = false; 89 public volatile bool HTTPDRunning = false;
86 90
@@ -437,9 +441,8 @@ namespace OpenSim.Framework.Servers.HttpServer
437 } 441 }
438 } 442 }
439 443
440 public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) 444 private void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request)
441 { 445 {
442
443 OSHttpRequest req = new OSHttpRequest(context, request); 446 OSHttpRequest req = new OSHttpRequest(context, request);
444 WebSocketRequestDelegate dWebSocketRequestDelegate = null; 447 WebSocketRequestDelegate dWebSocketRequestDelegate = null;
445 lock (m_WebSocketHandlers) 448 lock (m_WebSocketHandlers)
@@ -455,8 +458,7 @@ namespace OpenSim.Framework.Servers.HttpServer
455 458
456 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context); 459 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context);
457 resp.ReuseContext = true; 460 resp.ReuseContext = true;
458 HandleRequest(req, resp); 461 HandleRequest(req, resp);
459
460 462
461 // !!!HACK ALERT!!! 463 // !!!HACK ALERT!!!
462 // There seems to be a bug in the underlying http code that makes subsequent requests 464 // There seems to be a bug in the underlying http code that makes subsequent requests
@@ -1870,6 +1872,21 @@ namespace OpenSim.Framework.Servers.HttpServer
1870 // useful without inbound HTTP. 1872 // useful without inbound HTTP.
1871 throw e; 1873 throw e;
1872 } 1874 }
1875
1876 m_requestsProcessedStat
1877 = new Stat(
1878 "HTTPRequestsServed",
1879 "Number of inbound HTTP requests processed",
1880 "",
1881 "requests",
1882 "httpserver",
1883 Port.ToString(),
1884 StatType.Pull,
1885 MeasuresOfInterest.AverageChangeOverTime,
1886 stat => stat.Value = RequestNumber,
1887 StatVerbosity.Debug);
1888
1889 StatsManager.RegisterStat(m_requestsProcessedStat);
1873 } 1890 }
1874 1891
1875 public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err) 1892 public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err)
@@ -1902,6 +1919,9 @@ namespace OpenSim.Framework.Servers.HttpServer
1902 public void Stop() 1919 public void Stop()
1903 { 1920 {
1904 HTTPDRunning = false; 1921 HTTPDRunning = false;
1922
1923 StatsManager.DeregisterStat(m_requestsProcessedStat);
1924
1905 try 1925 try
1906 { 1926 {
1907 m_PollServiceManager.Stop(); 1927 m_PollServiceManager.Stop();
diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
index ee96b47..de89e2e 100644
--- a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
+++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
@@ -75,7 +75,7 @@ namespace OpenSim.Framework.Servers.HttpServer
75 /// <summary> 75 /// <summary>
76 /// This is a regular HTTP Request... This may be removed in the future. 76 /// This is a regular HTTP Request... This may be removed in the future.
77 /// </summary> 77 /// </summary>
78 public event RegularHttpRequestDelegate OnRegularHttpRequest; 78// public event RegularHttpRequestDelegate OnRegularHttpRequest;
79 79
80 /// <summary> 80 /// <summary>
81 /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired 81 /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired
@@ -304,15 +304,14 @@ namespace OpenSim.Framework.Servers.HttpServer
304 if (d != null) 304 if (d != null)
305 d(this, new UpgradeCompletedEventArgs()); 305 d(this, new UpgradeCompletedEventArgs());
306 } 306 }
307 catch (IOException fail) 307 catch (IOException)
308 { 308 {
309 Close(string.Empty); 309 Close(string.Empty);
310 } 310 }
311 catch (ObjectDisposedException fail) 311 catch (ObjectDisposedException)
312 { 312 {
313 Close(string.Empty); 313 Close(string.Empty);
314 } 314 }
315
316 } 315 }
317 316
318 /// <summary> 317 /// <summary>
@@ -414,8 +413,6 @@ namespace OpenSim.Framework.Servers.HttpServer
414 _socketState.Header = pheader; 413 _socketState.Header = pheader;
415 } 414 }
416 415
417
418
419 if (_socketState.FrameComplete) 416 if (_socketState.FrameComplete)
420 { 417 {
421 ProcessFrame(_socketState); 418 ProcessFrame(_socketState);
@@ -424,7 +421,6 @@ namespace OpenSim.Framework.Servers.HttpServer
424 _socketState.ExpectedBytes = 0; 421 _socketState.ExpectedBytes = 0;
425 422
426 } 423 }
427
428 } 424 }
429 } 425 }
430 else 426 else
@@ -457,8 +453,7 @@ namespace OpenSim.Framework.Servers.HttpServer
457 _socketState.ReceivedBytes.Clear(); 453 _socketState.ReceivedBytes.Clear();
458 _socketState.ExpectedBytes = 0; 454 _socketState.ExpectedBytes = 0;
459 // do some processing 455 // do some processing
460 } 456 }
461
462 } 457 }
463 } 458 }
464 if (offset > 0) 459 if (offset > 0)
@@ -477,13 +472,12 @@ namespace OpenSim.Framework.Servers.HttpServer
477 { 472 {
478 // We can't read the stream anymore... 473 // We can't read the stream anymore...
479 } 474 }
480
481 } 475 }
482 catch (IOException fail) 476 catch (IOException)
483 { 477 {
484 Close(string.Empty); 478 Close(string.Empty);
485 } 479 }
486 catch (ObjectDisposedException fail) 480 catch (ObjectDisposedException)
487 { 481 {
488 Close(string.Empty); 482 Close(string.Empty);
489 } 483 }
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs
index b9e3c18..eb8c9f8 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)
@@ -646,7 +667,68 @@ namespace OpenSim.Framework.Servers
646 sb.AppendFormat("Total threads active: {0}\n\n", totalThreads); 667 sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
647 668
648 sb.Append("Main threadpool (excluding script engine pools)\n"); 669 sb.Append("Main threadpool (excluding script engine pools)\n");
649 sb.Append(Util.GetThreadPoolReport()); 670 sb.Append(GetThreadPoolReport());
671
672 return sb.ToString();
673 }
674
675 /// <summary>
676 /// Get a thread pool report.
677 /// </summary>
678 /// <returns></returns>
679 public static string GetThreadPoolReport()
680 {
681 string threadPoolUsed = null;
682 int maxThreads = 0;
683 int minThreads = 0;
684 int allocatedThreads = 0;
685 int inUseThreads = 0;
686 int waitingCallbacks = 0;
687 int completionPortThreads = 0;
688
689 StringBuilder sb = new StringBuilder();
690 if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
691 {
692 STPInfo stpi = Util.GetSmartThreadPoolInfo();
693
694 // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool.
695 if (stpi != null)
696 {
697 threadPoolUsed = "SmartThreadPool";
698 maxThreads = stpi.MaxThreads;
699 minThreads = stpi.MinThreads;
700 inUseThreads = stpi.InUseThreads;
701 allocatedThreads = stpi.ActiveThreads;
702 waitingCallbacks = stpi.WaitingCallbacks;
703 }
704 }
705 else if (
706 Util.FireAndForgetMethod == FireAndForgetMethod.QueueUserWorkItem
707 || Util.FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem)
708 {
709 threadPoolUsed = "BuiltInThreadPool";
710 ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads);
711 ThreadPool.GetMinThreads(out minThreads, out completionPortThreads);
712 int availableThreads;
713 ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
714 inUseThreads = maxThreads - availableThreads;
715 allocatedThreads = -1;
716 waitingCallbacks = -1;
717 }
718
719 if (threadPoolUsed != null)
720 {
721 sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed);
722 sb.AppendFormat("Max threads : {0}\n", maxThreads);
723 sb.AppendFormat("Min threads : {0}\n", minThreads);
724 sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString());
725 sb.AppendFormat("In use threads : {0}\n", inUseThreads);
726 sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString());
727 }
728 else
729 {
730 sb.AppendFormat("Thread pool not used\n");
731 }
650 732
651 return sb.ToString(); 733 return sb.ToString();
652 } 734 }
@@ -698,5 +780,16 @@ namespace OpenSim.Framework.Servers
698 if (m_console != null) 780 if (m_console != null)
699 m_console.OutputFormat(format, components); 781 m_console.OutputFormat(format, components);
700 } 782 }
783
784 public virtual void Shutdown()
785 {
786 m_serverStatsCollector.Close();
787 ShutdownSpecific();
788 }
789
790 /// <summary>
791 /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
792 /// </summary>
793 protected virtual void ShutdownSpecific() {}
701 } 794 }
702} 795}
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 0852b49..7675be8 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -89,9 +89,30 @@ namespace OpenSim.Framework
89 } 89 }
90 90
91 /// <summary> 91 /// <summary>
92 /// Class for delivering SmartThreadPool statistical information
93 /// </summary>
94 /// <remarks>
95 /// We do it this way so that we do not directly expose STP.
96 /// </remarks>
97 public class STPInfo
98 {
99 public string Name { get; set; }
100 public STPStartInfo STPStartInfo { get; set; }
101 public WIGStartInfo WIGStartInfo { get; set; }
102 public bool IsIdle { get; set; }
103 public bool IsShuttingDown { get; set; }
104 public int MaxThreads { get; set; }
105 public int MinThreads { get; set; }
106 public int InUseThreads { get; set; }
107 public int ActiveThreads { get; set; }
108 public int WaitingCallbacks { get; set; }
109 public int MaxConcurrentWorkItems { get; set; }
110 }
111
112 /// <summary>
92 /// Miscellaneous utility functions 113 /// Miscellaneous utility functions
93 /// </summary> 114 /// </summary>
94 public class Util 115 public static class Util
95 { 116 {
96 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 117 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
97 118
@@ -1864,74 +1885,31 @@ namespace OpenSim.Framework
1864 } 1885 }
1865 1886
1866 /// <summary> 1887 /// <summary>
1867 /// Get a thread pool report. 1888 /// Get information about the current state of the smart thread pool.
1868 /// </summary> 1889 /// </summary>
1869 /// <returns></returns> 1890 /// <returns>
1870 public static string GetThreadPoolReport() 1891 /// null if this isn't the pool being used for non-scriptengine threads.
1892 /// </returns>
1893 public static STPInfo GetSmartThreadPoolInfo()
1871 { 1894 {
1872 string threadPoolUsed = null; 1895 if (m_ThreadPool == null)
1873 int maxThreads = 0; 1896 return null;
1874 int minThreads = 0;
1875 int allocatedThreads = 0;
1876 int inUseThreads = 0;
1877 int waitingCallbacks = 0;
1878 int completionPortThreads = 0;
1879
1880 StringBuilder sb = new StringBuilder();
1881 if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
1882 {
1883 // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool.
1884 if (m_ThreadPool != null)
1885 {
1886 threadPoolUsed = "SmartThreadPool";
1887 maxThreads = m_ThreadPool.MaxThreads;
1888 minThreads = m_ThreadPool.MinThreads;
1889 inUseThreads = m_ThreadPool.InUseThreads;
1890 allocatedThreads = m_ThreadPool.ActiveThreads;
1891 waitingCallbacks = m_ThreadPool.WaitingCallbacks;
1892 }
1893 }
1894 else if (
1895 FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem
1896 || FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem)
1897 {
1898 threadPoolUsed = "BuiltInThreadPool";
1899 ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads);
1900 ThreadPool.GetMinThreads(out minThreads, out completionPortThreads);
1901 int availableThreads;
1902 ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
1903 inUseThreads = maxThreads - availableThreads;
1904 allocatedThreads = -1;
1905 waitingCallbacks = -1;
1906 }
1907 1897
1908 if (threadPoolUsed != null) 1898 STPInfo stpi = new STPInfo();
1909 { 1899 stpi.Name = m_ThreadPool.Name;
1910 sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed); 1900 stpi.STPStartInfo = m_ThreadPool.STPStartInfo;
1911 sb.AppendFormat("Max threads : {0}\n", maxThreads); 1901 stpi.IsIdle = m_ThreadPool.IsIdle;
1912 sb.AppendFormat("Min threads : {0}\n", minThreads); 1902 stpi.IsShuttingDown = m_ThreadPool.IsShuttingdown;
1913 sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString()); 1903 stpi.MaxThreads = m_ThreadPool.MaxThreads;
1914 sb.AppendFormat("In use threads : {0}\n", inUseThreads); 1904 stpi.MinThreads = m_ThreadPool.MinThreads;
1915 sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString()); 1905 stpi.InUseThreads = m_ThreadPool.InUseThreads;
1916 } 1906 stpi.ActiveThreads = m_ThreadPool.ActiveThreads;
1917 else 1907 stpi.WaitingCallbacks = m_ThreadPool.WaitingCallbacks;
1918 { 1908 stpi.MaxConcurrentWorkItems = m_ThreadPool.Concurrency;
1919 sb.AppendFormat("Thread pool not used\n");
1920 }
1921 1909
1922 return sb.ToString(); 1910 return stpi;
1923 } 1911 }
1924 1912
1925// private static object SmartThreadPoolCallback(object o)
1926// {
1927// object[] array = (object[])o;
1928// WaitCallback callback = (WaitCallback)array[0];
1929// object obj = array[1];
1930//
1931// callback(obj);
1932// return null;
1933// }
1934
1935 #endregion FireAndForget Threading Pattern 1913 #endregion FireAndForget Threading Pattern
1936 1914
1937 /// <summary> 1915 /// <summary>