aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Monitoring
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Monitoring')
-rw-r--r--OpenSim/Framework/Monitoring/AssetStatsCollector.cs104
-rw-r--r--OpenSim/Framework/Monitoring/BaseStatsCollector.cs79
-rw-r--r--OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs41
-rw-r--r--OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs49
-rw-r--r--OpenSim/Framework/Monitoring/MemoryWatchdog.cs129
-rw-r--r--OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs463
-rw-r--r--OpenSim/Framework/Monitoring/StatsManager.cs65
-rw-r--r--OpenSim/Framework/Monitoring/UserStatsCollector.cs92
-rw-r--r--OpenSim/Framework/Monitoring/Watchdog.cs334
9 files changed, 1356 insertions, 0 deletions
diff --git a/OpenSim/Framework/Monitoring/AssetStatsCollector.cs b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs
new file mode 100644
index 0000000..2a4d45b
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs
@@ -0,0 +1,104 @@
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.Timers;
30
31namespace OpenSim.Framework.Monitoring
32{
33 /// <summary>
34 /// Asset service statistics collection
35 /// </summary>
36 public class AssetStatsCollector : BaseStatsCollector
37 {
38 private Timer ageStatsTimer = new Timer(24 * 60 * 60 * 1000);
39 private DateTime startTime = DateTime.Now;
40
41 private long assetRequestsToday;
42 private long assetRequestsNotFoundToday;
43 private long assetRequestsYesterday;
44 private long assetRequestsNotFoundYesterday;
45
46 public long AssetRequestsToday { get { return assetRequestsToday; } }
47 public long AssetRequestsNotFoundToday { get { return assetRequestsNotFoundToday; } }
48 public long AssetRequestsYesterday { get { return assetRequestsYesterday; } }
49 public long AssetRequestsNotFoundYesterday { get { return assetRequestsNotFoundYesterday; } }
50
51 public AssetStatsCollector()
52 {
53 ageStatsTimer.Elapsed += new ElapsedEventHandler(OnAgeing);
54 ageStatsTimer.Enabled = true;
55 }
56
57 private void OnAgeing(object source, ElapsedEventArgs e)
58 {
59 assetRequestsYesterday = assetRequestsToday;
60
61 // There is a possibility that an asset request could occur between the execution of these
62 // two statements. But we're better off without the synchronization overhead.
63 assetRequestsToday = 0;
64
65 assetRequestsNotFoundYesterday = assetRequestsNotFoundToday;
66 assetRequestsNotFoundToday = 0;
67 }
68
69 /// <summary>
70 /// Record that an asset request failed to find an asset
71 /// </summary>
72 public void AddNotFoundRequest()
73 {
74 assetRequestsNotFoundToday++;
75 }
76
77 /// <summary>
78 /// Record that a request was made to the asset server
79 /// </summary>
80 public void AddRequest()
81 {
82 assetRequestsToday++;
83 }
84
85 /// <summary>
86 /// Report back collected statistical information.
87 /// </summary>
88 /// <returns></returns>
89 override public string Report()
90 {
91 double elapsedHours = (DateTime.Now - startTime).TotalHours;
92 if (elapsedHours <= 0) { elapsedHours = 1; } // prevent divide by zero
93
94 long assetRequestsTodayPerHour = (long)Math.Round(AssetRequestsToday / elapsedHours);
95 long assetRequestsYesterdayPerHour = (long)Math.Round(AssetRequestsYesterday / 24.0);
96
97 return string.Format(
98@"Asset requests today : {0} ({1} per hour) of which {2} were not found
99Asset requests yesterday : {3} ({4} per hour) of which {5} were not found",
100 AssetRequestsToday, assetRequestsTodayPerHour, AssetRequestsNotFoundToday,
101 AssetRequestsYesterday, assetRequestsYesterdayPerHour, AssetRequestsNotFoundYesterday);
102 }
103 }
104}
diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
new file mode 100644
index 0000000..9ee0876
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
@@ -0,0 +1,79 @@
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.Diagnostics;
30using System.Text;
31using OpenMetaverse;
32using OpenMetaverse.StructuredData;
33
34namespace OpenSim.Framework.Monitoring
35{
36 /// <summary>
37 /// Statistics which all collectors are interested in reporting
38 /// </summary>
39 public class BaseStatsCollector : IStatsCollector
40 {
41 public virtual string Report()
42 {
43 StringBuilder sb = new StringBuilder(Environment.NewLine);
44 sb.Append("MEMORY STATISTICS");
45 sb.Append(Environment.NewLine);
46 sb.Append(
47 string.Format(
48 "Allocated to OpenSim objects: {0} MB\n",
49 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)));
50
51 Process myprocess = Process.GetCurrentProcess();
52 if (!myprocess.HasExited)
53 {
54 myprocess.Refresh();
55 sb.Append(
56 string.Format(
57 "Process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n",
58 Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0),
59 Math.Round(Process.GetCurrentProcess().PagedMemorySize64 / 1024.0 / 1024.0),
60 Math.Round(Process.GetCurrentProcess().VirtualMemorySize64 / 1024.0 / 1024.0)));
61 sb.Append(
62 string.Format(
63 "Peak process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n",
64 Math.Round(Process.GetCurrentProcess().PeakWorkingSet64 / 1024.0 / 1024.0),
65 Math.Round(Process.GetCurrentProcess().PeakPagedMemorySize64 / 1024.0 / 1024.0),
66 Math.Round(Process.GetCurrentProcess().PeakVirtualMemorySize64 / 1024.0 / 1024.0)));
67 }
68 else
69 sb.Append("Process reported as Exited \n");
70
71 return sb.ToString();
72 }
73
74 public virtual string XReport(string uptime, string version)
75 {
76 return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ;
77 }
78 }
79}
diff --git a/OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs b/OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs
new file mode 100644
index 0000000..86a6620
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs
@@ -0,0 +1,41 @@
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
28namespace OpenSim.Framework.Monitoring.Interfaces
29{
30 /// <summary>
31 /// Implemented by objects which allow statistical information to be pulled from them.
32 /// </summary>
33 public interface IPullStatsProvider
34 {
35 /// <summary>
36 /// Provide statistical information. Only temporary one long string.
37 /// </summary>
38 /// <returns></returns>
39 string GetStats();
40 }
41}
diff --git a/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs
new file mode 100644
index 0000000..99f75e3
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs
@@ -0,0 +1,49 @@
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
28namespace OpenSim.Framework.Monitoring
29{
30 /// <summary>
31 /// Implemented by classes which collect up non-viewer statistical information
32 /// </summary>
33 public interface IStatsCollector
34 {
35 /// <summary>
36 /// Report back collected statistical information.
37 /// </summary>
38 /// <returns></returns>
39 string Report();
40
41 /// <summary>
42 /// Report back collected statistical information in json
43 /// </summary>
44 /// <returns>
45 /// A <see cref="System.String"/>
46 /// </returns>
47 string XReport(string uptime, string version);
48 }
49}
diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
new file mode 100644
index 0000000..a23cf1f
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
@@ -0,0 +1,129 @@
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.Linq;
31using System.Reflection;
32using System.Threading;
33using log4net;
34
35namespace OpenSim.Framework.Monitoring
36{
37 /// <summary>
38 /// Experimental watchdog for memory usage.
39 /// </summary>
40 public static class MemoryWatchdog
41 {
42// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
43
44 /// <summary>
45 /// Is this watchdog active?
46 /// </summary>
47 public static bool Enabled
48 {
49 get { return m_enabled; }
50 set
51 {
52// m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value);
53
54 if (value && !m_enabled)
55 UpdateLastRecord(GC.GetTotalMemory(false), Util.EnvironmentTickCount());
56
57 m_enabled = value;
58 }
59 }
60 private static bool m_enabled;
61
62 /// <summary>
63 /// Average memory churn in bytes per millisecond.
64 /// </summary>
65 public static double AverageMemoryChurn
66 {
67 get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; }
68 }
69
70 /// <summary>
71 /// Maximum number of statistical samples.
72 /// </summary>
73 /// <remarks>
74 /// At the moment this corresponds to 1 minute since the sampling rate is every 2.5 seconds as triggered from
75 /// the main Watchdog.
76 /// </remarks>
77 private static int m_maxSamples = 24;
78
79 /// <summary>
80 /// Time when the watchdog was last updated.
81 /// </summary>
82 private static int m_lastUpdateTick;
83
84 /// <summary>
85 /// Memory used at time of last watchdog update.
86 /// </summary>
87 private static long m_lastUpdateMemory;
88
89 /// <summary>
90 /// Memory churn rate per millisecond.
91 /// </summary>
92// private static double m_churnRatePerMillisecond;
93
94 /// <summary>
95 /// Historical samples for calculating moving average.
96 /// </summary>
97 private static Queue<double> m_samples = new Queue<double>(m_maxSamples);
98
99 public static void Update()
100 {
101 int now = Util.EnvironmentTickCount();
102 long memoryNow = GC.GetTotalMemory(false);
103 long memoryDiff = memoryNow - m_lastUpdateMemory;
104
105 if (memoryDiff >= 0)
106 {
107 if (m_samples.Count >= m_maxSamples)
108 m_samples.Dequeue();
109
110 double elapsed = Util.EnvironmentTickCountSubtract(now, m_lastUpdateTick);
111
112 // This should never happen since it's not useful for updates to occur with no time elapsed, but
113 // protect ourselves from a divide-by-zero just in case.
114 if (elapsed == 0)
115 return;
116
117 m_samples.Enqueue(memoryDiff / (double)elapsed);
118 }
119
120 UpdateLastRecord(memoryNow, now);
121 }
122
123 private static void UpdateLastRecord(long memoryNow, int timeNow)
124 {
125 m_lastUpdateMemory = memoryNow;
126 m_lastUpdateTick = timeNow;
127 }
128 }
129} \ No newline at end of file
diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
new file mode 100644
index 0000000..cdd7cc7
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
@@ -0,0 +1,463 @@
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.Text;
31using OpenMetaverse;
32using OpenMetaverse.StructuredData;
33using OpenSim.Framework.Monitoring.Interfaces;
34
35namespace OpenSim.Framework.Monitoring
36{
37 /// <summary>
38 /// Collects sim statistics which aren't already being collected for the linden viewer's statistics pane
39 /// </summary>
40 public class SimExtraStatsCollector : BaseStatsCollector
41 {
42 private long abnormalClientThreadTerminations;
43
44// private long assetsInCache;
45// private long texturesInCache;
46// private long assetCacheMemoryUsage;
47// private long textureCacheMemoryUsage;
48// private TimeSpan assetRequestTimeAfterCacheMiss;
49// private long blockedMissingTextureRequests;
50
51// private long assetServiceRequestFailures;
52// private long inventoryServiceRetrievalFailures;
53
54 private volatile float timeDilation;
55 private volatile float simFps;
56 private volatile float physicsFps;
57 private volatile float agentUpdates;
58 private volatile float rootAgents;
59 private volatile float childAgents;
60 private volatile float totalPrims;
61 private volatile float activePrims;
62 private volatile float totalFrameTime;
63 private volatile float netFrameTime;
64 private volatile float physicsFrameTime;
65 private volatile float otherFrameTime;
66 private volatile float imageFrameTime;
67 private volatile float inPacketsPerSecond;
68 private volatile float outPacketsPerSecond;
69 private volatile float unackedBytes;
70 private volatile float agentFrameTime;
71 private volatile float pendingDownloads;
72 private volatile float pendingUploads;
73 private volatile float activeScripts;
74 private volatile float scriptLinesPerSecond;
75
76 /// <summary>
77 /// Number of times that a client thread terminated because of an exception
78 /// </summary>
79 public long AbnormalClientThreadTerminations { get { return abnormalClientThreadTerminations; } }
80
81// /// <summary>
82// /// These statistics are being collected by push rather than pull. Pull would be simpler, but I had the
83// /// notion of providing some flow statistics (which pull wouldn't give us). Though admittedly these
84// /// haven't yet been implemented...
85// /// </summary>
86// public long AssetsInCache { get { return assetsInCache; } }
87//
88// /// <value>
89// /// Currently unused
90// /// </value>
91// public long TexturesInCache { get { return texturesInCache; } }
92//
93// /// <value>
94// /// Currently misleading since we can't currently subtract removed asset memory usage without a performance hit
95// /// </value>
96// public long AssetCacheMemoryUsage { get { return assetCacheMemoryUsage; } }
97//
98// /// <value>
99// /// Currently unused
100// /// </value>
101// public long TextureCacheMemoryUsage { get { return textureCacheMemoryUsage; } }
102
103 public float TimeDilation { get { return timeDilation; } }
104 public float SimFps { get { return simFps; } }
105 public float PhysicsFps { get { return physicsFps; } }
106 public float AgentUpdates { get { return agentUpdates; } }
107 public float RootAgents { get { return rootAgents; } }
108 public float ChildAgents { get { return childAgents; } }
109 public float TotalPrims { get { return totalPrims; } }
110 public float ActivePrims { get { return activePrims; } }
111 public float TotalFrameTime { get { return totalFrameTime; } }
112 public float NetFrameTime { get { return netFrameTime; } }
113 public float PhysicsFrameTime { get { return physicsFrameTime; } }
114 public float OtherFrameTime { get { return otherFrameTime; } }
115 public float ImageFrameTime { get { return imageFrameTime; } }
116 public float InPacketsPerSecond { get { return inPacketsPerSecond; } }
117 public float OutPacketsPerSecond { get { return outPacketsPerSecond; } }
118 public float UnackedBytes { get { return unackedBytes; } }
119 public float AgentFrameTime { get { return agentFrameTime; } }
120 public float PendingDownloads { get { return pendingDownloads; } }
121 public float PendingUploads { get { return pendingUploads; } }
122 public float ActiveScripts { get { return activeScripts; } }
123 public float ScriptLinesPerSecond { get { return scriptLinesPerSecond; } }
124
125// /// <summary>
126// /// This is the time it took for the last asset request made in response to a cache miss.
127// /// </summary>
128// public TimeSpan AssetRequestTimeAfterCacheMiss { get { return assetRequestTimeAfterCacheMiss; } }
129//
130// /// <summary>
131// /// Number of persistent requests for missing textures we have started blocking from clients. To some extent
132// /// this is just a temporary statistic to keep this problem in view - the root cause of this lies either
133// /// in a mishandling of the reply protocol, related to avatar appearance or may even originate in graphics
134// /// driver bugs on clients (though this seems less likely).
135// /// </summary>
136// public long BlockedMissingTextureRequests { get { return blockedMissingTextureRequests; } }
137//
138// /// <summary>
139// /// Record the number of times that an asset request has failed. Failures are effectively exceptions, such as
140// /// request timeouts. If an asset service replies that a particular asset cannot be found, this is not counted
141// /// as a failure
142// /// </summary>
143// public long AssetServiceRequestFailures { get { return assetServiceRequestFailures; } }
144
145 /// <summary>
146 /// Number of known failures to retrieve avatar inventory from the inventory service. This does not
147 /// cover situations where the inventory service accepts the request but never returns any data, since
148 /// we do not yet timeout this situation.
149 /// </summary>
150 /// <remarks>Commented out because we do not cache inventory at this point</remarks>
151// public long InventoryServiceRetrievalFailures { get { return inventoryServiceRetrievalFailures; } }
152
153 /// <summary>
154 /// Retrieve the total frame time (in ms) of the last frame
155 /// </summary>
156 //public float TotalFrameTime { get { return totalFrameTime; } }
157
158 /// <summary>
159 /// Retrieve the physics update component (in ms) of the last frame
160 /// </summary>
161 //public float PhysicsFrameTime { get { return physicsFrameTime; } }
162
163 /// <summary>
164 /// Retain a dictionary of all packet queues stats reporters
165 /// </summary>
166 private IDictionary<UUID, PacketQueueStatsCollector> packetQueueStatsCollectors
167 = new Dictionary<UUID, PacketQueueStatsCollector>();
168
169 public void AddAbnormalClientThreadTermination()
170 {
171 abnormalClientThreadTerminations++;
172 }
173
174// public void AddAsset(AssetBase asset)
175// {
176// assetsInCache++;
177// //assetCacheMemoryUsage += asset.Data.Length;
178// }
179//
180// public void RemoveAsset(UUID uuid)
181// {
182// assetsInCache--;
183// }
184//
185// public void AddTexture(AssetBase image)
186// {
187// if (image.Data != null)
188// {
189// texturesInCache++;
190//
191// // This could have been a pull stat, though there was originally a nebulous idea to measure flow rates
192// textureCacheMemoryUsage += image.Data.Length;
193// }
194// }
195//
196// /// <summary>
197// /// Signal that the asset cache has been cleared.
198// /// </summary>
199// public void ClearAssetCacheStatistics()
200// {
201// assetsInCache = 0;
202// assetCacheMemoryUsage = 0;
203// texturesInCache = 0;
204// textureCacheMemoryUsage = 0;
205// }
206//
207// public void AddAssetRequestTimeAfterCacheMiss(TimeSpan ts)
208// {
209// assetRequestTimeAfterCacheMiss = ts;
210// }
211//
212// public void AddBlockedMissingTextureRequest()
213// {
214// blockedMissingTextureRequests++;
215// }
216//
217// public void AddAssetServiceRequestFailure()
218// {
219// assetServiceRequestFailures++;
220// }
221
222// public void AddInventoryServiceRetrievalFailure()
223// {
224// inventoryServiceRetrievalFailures++;
225// }
226
227 /// <summary>
228 /// Register as a packet queue stats provider
229 /// </summary>
230 /// <param name="uuid">An agent UUID</param>
231 /// <param name="provider"></param>
232 public void RegisterPacketQueueStatsProvider(UUID uuid, IPullStatsProvider provider)
233 {
234 lock (packetQueueStatsCollectors)
235 {
236 // FIXME: If the region service is providing more than one region, then the child and root agent
237 // queues are wrongly replacing each other here.
238 packetQueueStatsCollectors[uuid] = new PacketQueueStatsCollector(provider);
239 }
240 }
241
242 /// <summary>
243 /// Deregister a packet queue stats provider
244 /// </summary>
245 /// <param name="uuid">An agent UUID</param>
246 public void DeregisterPacketQueueStatsProvider(UUID uuid)
247 {
248 lock (packetQueueStatsCollectors)
249 {
250 packetQueueStatsCollectors.Remove(uuid);
251 }
252 }
253
254 /// <summary>
255 /// This is the method on which the classic sim stats reporter (which collects stats for
256 /// client purposes) sends information to listeners.
257 /// </summary>
258 /// <param name="pack"></param>
259 public void ReceiveClassicSimStatsPacket(SimStats stats)
260 {
261 // FIXME: SimStats shouldn't allow an arbitrary stat packing order (which is inherited from the original
262 // SimStatsPacket that was being used).
263 timeDilation = stats.StatsBlock[0].StatValue;
264 simFps = stats.StatsBlock[1].StatValue;
265 physicsFps = stats.StatsBlock[2].StatValue;
266 agentUpdates = stats.StatsBlock[3].StatValue;
267 rootAgents = stats.StatsBlock[4].StatValue;
268 childAgents = stats.StatsBlock[5].StatValue;
269 totalPrims = stats.StatsBlock[6].StatValue;
270 activePrims = stats.StatsBlock[7].StatValue;
271 totalFrameTime = stats.StatsBlock[8].StatValue;
272 netFrameTime = stats.StatsBlock[9].StatValue;
273 physicsFrameTime = stats.StatsBlock[10].StatValue;
274 otherFrameTime = stats.StatsBlock[11].StatValue;
275 imageFrameTime = stats.StatsBlock[12].StatValue;
276 inPacketsPerSecond = stats.StatsBlock[13].StatValue;
277 outPacketsPerSecond = stats.StatsBlock[14].StatValue;
278 unackedBytes = stats.StatsBlock[15].StatValue;
279 agentFrameTime = stats.StatsBlock[16].StatValue;
280 pendingDownloads = stats.StatsBlock[17].StatValue;
281 pendingUploads = stats.StatsBlock[18].StatValue;
282 activeScripts = stats.StatsBlock[19].StatValue;
283 scriptLinesPerSecond = stats.StatsBlock[20].StatValue;
284 }
285
286 /// <summary>
287 /// Report back collected statistical information.
288 /// </summary>
289 /// <returns></returns>
290 public override string Report()
291 {
292 StringBuilder sb = new StringBuilder(Environment.NewLine);
293// sb.Append("ASSET STATISTICS");
294// sb.Append(Environment.NewLine);
295
296 /*
297 sb.Append(
298 string.Format(
299@"Asset cache contains {0,6} non-texture assets using {1,10} K
300Texture cache contains {2,6} texture assets using {3,10} K
301Latest asset request time after cache miss: {4}s
302Blocked client requests for missing textures: {5}
303Asset service request failures: {6}"+ Environment.NewLine,
304 AssetsInCache, Math.Round(AssetCacheMemoryUsage / 1024.0),
305 TexturesInCache, Math.Round(TextureCacheMemoryUsage / 1024.0),
306 assetRequestTimeAfterCacheMiss.Milliseconds / 1000.0,
307 BlockedMissingTextureRequests,
308 AssetServiceRequestFailures));
309 */
310
311 /*
312 sb.Append(
313 string.Format(
314@"Asset cache contains {0,6} assets
315Latest asset request time after cache miss: {1}s
316Blocked client requests for missing textures: {2}
317Asset service request failures: {3}" + Environment.NewLine,
318 AssetsInCache,
319 assetRequestTimeAfterCacheMiss.Milliseconds / 1000.0,
320 BlockedMissingTextureRequests,
321 AssetServiceRequestFailures));
322 */
323
324 sb.Append(Environment.NewLine);
325 sb.Append("CONNECTION STATISTICS");
326 sb.Append(Environment.NewLine);
327 sb.Append(
328 string.Format(
329 "Abnormal client thread terminations: {0}" + Environment.NewLine,
330 abnormalClientThreadTerminations));
331
332// sb.Append(Environment.NewLine);
333// sb.Append("INVENTORY STATISTICS");
334// sb.Append(Environment.NewLine);
335// sb.Append(
336// string.Format(
337// "Initial inventory caching failures: {0}" + Environment.NewLine,
338// InventoryServiceRetrievalFailures));
339
340 sb.Append(Environment.NewLine);
341 sb.Append("FRAME STATISTICS");
342 sb.Append(Environment.NewLine);
343 sb.Append("Dilatn SimFPS PhyFPS AgntUp RootAg ChldAg Prims AtvPrm AtvScr ScrLPS");
344 sb.Append(Environment.NewLine);
345 sb.Append(
346 string.Format(
347 "{0,6:0.00} {1,6:0} {2,6:0.0} {3,6:0.0} {4,6:0} {5,6:0} {6,6:0} {7,6:0} {8,6:0} {9,6:0}",
348 timeDilation, simFps, physicsFps, agentUpdates, rootAgents,
349 childAgents, totalPrims, activePrims, activeScripts, scriptLinesPerSecond));
350
351 sb.Append(Environment.NewLine);
352 sb.Append(Environment.NewLine);
353 // There is no script frame time currently because we don't yet collect it
354 sb.Append("PktsIn PktOut PendDl PendUl UnackB TotlFt NetFt PhysFt OthrFt AgntFt ImgsFt");
355 sb.Append(Environment.NewLine);
356 sb.Append(
357 string.Format(
358 "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}",
359 inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
360 netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
361 sb.Append(Environment.NewLine);
362
363 /*
364 sb.Append(Environment.NewLine);
365 sb.Append("PACKET QUEUE STATISTICS");
366 sb.Append(Environment.NewLine);
367 sb.Append("Agent UUID ");
368 sb.Append(
369 string.Format(
370 " {0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}",
371 "Send", "In", "Out", "Resend", "Land", "Wind", "Cloud", "Task", "Texture", "Asset"));
372 sb.Append(Environment.NewLine);
373
374 foreach (UUID key in packetQueueStatsCollectors.Keys)
375 {
376 sb.Append(string.Format("{0}: ", key));
377 sb.Append(packetQueueStatsCollectors[key].Report());
378 sb.Append(Environment.NewLine);
379 }
380 */
381
382 sb.Append(base.Report());
383
384 return sb.ToString();
385 }
386
387 /// <summary>
388 /// Report back collected statistical information as json serialization.
389 /// </summary>
390 /// <returns></returns>
391 public override string XReport(string uptime, string version)
392 {
393 OSDMap args = new OSDMap(30);
394// args["AssetsInCache"] = OSD.FromString (String.Format ("{0:0.##}", AssetsInCache));
395// args["TimeAfterCacheMiss"] = OSD.FromString (String.Format ("{0:0.##}",
396// assetRequestTimeAfterCacheMiss.Milliseconds / 1000.0));
397// args["BlockedMissingTextureRequests"] = OSD.FromString (String.Format ("{0:0.##}",
398// BlockedMissingTextureRequests));
399// args["AssetServiceRequestFailures"] = OSD.FromString (String.Format ("{0:0.##}",
400// AssetServiceRequestFailures));
401// args["abnormalClientThreadTerminations"] = OSD.FromString (String.Format ("{0:0.##}",
402// abnormalClientThreadTerminations));
403// args["InventoryServiceRetrievalFailures"] = OSD.FromString (String.Format ("{0:0.##}",
404// InventoryServiceRetrievalFailures));
405 args["Dilatn"] = OSD.FromString (String.Format ("{0:0.##}", timeDilation));
406 args["SimFPS"] = OSD.FromString (String.Format ("{0:0.##}", simFps));
407 args["PhyFPS"] = OSD.FromString (String.Format ("{0:0.##}", physicsFps));
408 args["AgntUp"] = OSD.FromString (String.Format ("{0:0.##}", agentUpdates));
409 args["RootAg"] = OSD.FromString (String.Format ("{0:0.##}", rootAgents));
410 args["ChldAg"] = OSD.FromString (String.Format ("{0:0.##}", childAgents));
411 args["Prims"] = OSD.FromString (String.Format ("{0:0.##}", totalPrims));
412 args["AtvPrm"] = OSD.FromString (String.Format ("{0:0.##}", activePrims));
413 args["AtvScr"] = OSD.FromString (String.Format ("{0:0.##}", activeScripts));
414 args["ScrLPS"] = OSD.FromString (String.Format ("{0:0.##}", scriptLinesPerSecond));
415 args["PktsIn"] = OSD.FromString (String.Format ("{0:0.##}", inPacketsPerSecond));
416 args["PktOut"] = OSD.FromString (String.Format ("{0:0.##}", outPacketsPerSecond));
417 args["PendDl"] = OSD.FromString (String.Format ("{0:0.##}", pendingDownloads));
418 args["PendUl"] = OSD.FromString (String.Format ("{0:0.##}", pendingUploads));
419 args["UnackB"] = OSD.FromString (String.Format ("{0:0.##}", unackedBytes));
420 args["TotlFt"] = OSD.FromString (String.Format ("{0:0.##}", totalFrameTime));
421 args["NetFt"] = OSD.FromString (String.Format ("{0:0.##}", netFrameTime));
422 args["PhysFt"] = OSD.FromString (String.Format ("{0:0.##}", physicsFrameTime));
423 args["OthrFt"] = OSD.FromString (String.Format ("{0:0.##}", otherFrameTime));
424 args["AgntFt"] = OSD.FromString (String.Format ("{0:0.##}", agentFrameTime));
425 args["ImgsFt"] = OSD.FromString (String.Format ("{0:0.##}", imageFrameTime));
426 args["Memory"] = OSD.FromString (base.XReport (uptime, version));
427 args["Uptime"] = OSD.FromString (uptime);
428 args["Version"] = OSD.FromString (version);
429
430 string strBuffer = "";
431 strBuffer = OSDParser.SerializeJsonString(args);
432
433 return strBuffer;
434 }
435 }
436
437 /// <summary>
438 /// Pull packet queue stats from packet queues and report
439 /// </summary>
440 public class PacketQueueStatsCollector : IStatsCollector
441 {
442 private IPullStatsProvider m_statsProvider;
443
444 public PacketQueueStatsCollector(IPullStatsProvider provider)
445 {
446 m_statsProvider = provider;
447 }
448
449 /// <summary>
450 /// Report back collected statistical information.
451 /// </summary>
452 /// <returns></returns>
453 public string Report()
454 {
455 return m_statsProvider.GetStats();
456 }
457
458 public string XReport(string uptime, string version)
459 {
460 return "";
461 }
462 }
463}
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
new file mode 100644
index 0000000..d78fa6a
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -0,0 +1,65 @@
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
28namespace OpenSim.Framework.Monitoring
29{
30 /// <summary>
31 /// Singleton used to provide access to statistics reporters
32 /// </summary>
33 public class StatsManager
34 {
35 private static AssetStatsCollector assetStats;
36 private static UserStatsCollector userStats;
37 private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
38
39 public static AssetStatsCollector AssetStats { get { return assetStats; } }
40 public static UserStatsCollector UserStats { get { return userStats; } }
41 public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
42
43 /// <summary>
44 /// Start collecting statistics related to assets.
45 /// Should only be called once.
46 /// </summary>
47 public static AssetStatsCollector StartCollectingAssetStats()
48 {
49 assetStats = new AssetStatsCollector();
50
51 return assetStats;
52 }
53
54 /// <summary>
55 /// Start collecting statistics related to users.
56 /// Should only be called once.
57 /// </summary>
58 public static UserStatsCollector StartCollectingUserStats()
59 {
60 userStats = new UserStatsCollector();
61
62 return userStats;
63 }
64 }
65} \ No newline at end of file
diff --git a/OpenSim/Framework/Monitoring/UserStatsCollector.cs b/OpenSim/Framework/Monitoring/UserStatsCollector.cs
new file mode 100644
index 0000000..e89c8e6
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/UserStatsCollector.cs
@@ -0,0 +1,92 @@
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.Timers;
29
30namespace OpenSim.Framework.Monitoring
31{
32 /// <summary>
33 /// Collects user service statistics
34 /// </summary>
35 public class UserStatsCollector : BaseStatsCollector
36 {
37 private Timer ageStatsTimer = new Timer(24 * 60 * 60 * 1000);
38
39 private int successfulLoginsToday;
40 public int SuccessfulLoginsToday { get { return successfulLoginsToday; } }
41
42 private int successfulLoginsYesterday;
43 public int SuccessfulLoginsYesterday { get { return successfulLoginsYesterday; } }
44
45 private int successfulLogins;
46 public int SuccessfulLogins { get { return successfulLogins; } }
47
48 private int logouts;
49 public int Logouts { get { return logouts; } }
50
51 public UserStatsCollector()
52 {
53 ageStatsTimer.Elapsed += new ElapsedEventHandler(OnAgeing);
54 ageStatsTimer.Enabled = true;
55 }
56
57 private void OnAgeing(object source, ElapsedEventArgs e)
58 {
59 successfulLoginsYesterday = successfulLoginsToday;
60
61 // There is a possibility that an asset request could occur between the execution of these
62 // two statements. But we're better off without the synchronization overhead.
63 successfulLoginsToday = 0;
64 }
65
66 /// <summary>
67 /// Record a successful login
68 /// </summary>
69 public void AddSuccessfulLogin()
70 {
71 successfulLogins++;
72 successfulLoginsToday++;
73 }
74
75 public void AddLogout()
76 {
77 logouts++;
78 }
79
80 /// <summary>
81 /// Report back collected statistical information.
82 /// </summary>
83 /// <returns></returns>
84 override public string Report()
85 {
86 return string.Format(
87@"Successful logins total : {0}, today : {1}, yesterday : {2}
88 Logouts total : {3}",
89 SuccessfulLogins, SuccessfulLoginsToday, SuccessfulLoginsYesterday, Logouts);
90 }
91 }
92}
diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs
new file mode 100644
index 0000000..e4db964
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/Watchdog.cs
@@ -0,0 +1,334 @@
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.Linq;
31using System.Threading;
32using log4net;
33
34namespace OpenSim.Framework.Monitoring
35{
36 /// <summary>
37 /// Manages launching threads and keeping watch over them for timeouts
38 /// </summary>
39 public static class Watchdog
40 {
41 /// <summary>Timer interval in milliseconds for the watchdog timer</summary>
42 const double WATCHDOG_INTERVAL_MS = 2500.0d;
43
44 /// <summary>Default timeout in milliseconds before a thread is considered dead</summary>
45 public const int DEFAULT_WATCHDOG_TIMEOUT_MS = 5000;
46
47 [System.Diagnostics.DebuggerDisplay("{Thread.Name}")]
48 public class ThreadWatchdogInfo
49 {
50 public Thread Thread { get; private set; }
51
52 /// <summary>
53 /// Approximate tick when this thread was started.
54 /// </summary>
55 /// <remarks>
56 /// Not terribly good since this quickly wraps around.
57 /// </remarks>
58 public int FirstTick { get; private set; }
59
60 /// <summary>
61 /// Last time this heartbeat update was invoked
62 /// </summary>
63 public int LastTick { get; set; }
64
65 /// <summary>
66 /// Number of milliseconds before we notify that the thread is having a problem.
67 /// </summary>
68 public int Timeout { get; set; }
69
70 /// <summary>
71 /// Is this thread considered timed out?
72 /// </summary>
73 public bool IsTimedOut { get; set; }
74
75 /// <summary>
76 /// Will this thread trigger the alarm function if it has timed out?
77 /// </summary>
78 public bool AlarmIfTimeout { get; set; }
79
80 /// <summary>
81 /// Method execute if alarm goes off. If null then no alarm method is fired.
82 /// </summary>
83 public Func<string> AlarmMethod { get; set; }
84
85 public ThreadWatchdogInfo(Thread thread, int timeout)
86 {
87 Thread = thread;
88 Timeout = timeout;
89 FirstTick = Environment.TickCount & Int32.MaxValue;
90 LastTick = FirstTick;
91 }
92 }
93
94 /// <summary>
95 /// This event is called whenever a tracked thread is
96 /// stopped or has not called UpdateThread() in time<
97 /// /summary>
98 public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
99
100 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
101 private static Dictionary<int, ThreadWatchdogInfo> m_threads;
102 private static System.Timers.Timer m_watchdogTimer;
103
104 /// <summary>
105 /// Last time the watchdog thread ran.
106 /// </summary>
107 /// <remarks>
108 /// Should run every WATCHDOG_INTERVAL_MS
109 /// </remarks>
110 public static int LastWatchdogThreadTick { get; private set; }
111
112 static Watchdog()
113 {
114 m_threads = new Dictionary<int, ThreadWatchdogInfo>();
115 m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
116 m_watchdogTimer.AutoReset = false;
117 m_watchdogTimer.Elapsed += WatchdogTimerElapsed;
118
119 // Set now so we don't get alerted on the first run
120 LastWatchdogThreadTick = Environment.TickCount & Int32.MaxValue;
121
122 m_watchdogTimer.Start();
123 }
124
125 /// <summary>
126 /// Start a new thread that is tracked by the watchdog timer.
127 /// </summary>
128 /// <param name="start">The method that will be executed in a new thread</param>
129 /// <param name="name">A name to give to the new thread</param>
130 /// <param name="priority">Priority to run the thread at</param>
131 /// <param name="isBackground">True to run this thread as a background thread, otherwise false</param>
132 /// <param name="alarmIfTimeout">Trigger an alarm function is we have timed out</param>
133 /// <returns>The newly created Thread object</returns>
134 public static Thread StartThread(
135 ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout)
136 {
137 return StartThread(start, name, priority, isBackground, alarmIfTimeout, null, DEFAULT_WATCHDOG_TIMEOUT_MS);
138 }
139
140 /// <summary>
141 /// Start a new thread that is tracked by the watchdog timer
142 /// </summary>
143 /// <param name="start">The method that will be executed in a new thread</param>
144 /// <param name="name">A name to give to the new thread</param>
145 /// <param name="priority">Priority to run the thread at</param>
146 /// <param name="isBackground">True to run this thread as a background
147 /// thread, otherwise false</param>
148 /// <param name="alarmIfTimeout">Trigger an alarm function is we have timed out</param>
149 /// <param name="alarmMethod">
150 /// Alarm method to call if alarmIfTimeout is true and there is a timeout.
151 /// Normally, this will just return some useful debugging information.
152 /// </param>
153 /// <param name="timeout">Number of milliseconds to wait until we issue a warning about timeout.</param>
154 /// <returns>The newly created Thread object</returns>
155 public static Thread StartThread(
156 ThreadStart start, string name, ThreadPriority priority, bool isBackground,
157 bool alarmIfTimeout, Func<string> alarmMethod, int timeout)
158 {
159 Thread thread = new Thread(start);
160 thread.Name = name;
161 thread.Priority = priority;
162 thread.IsBackground = isBackground;
163
164 ThreadWatchdogInfo twi
165 = new ThreadWatchdogInfo(thread, timeout)
166 { AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod };
167
168 m_log.DebugFormat(
169 "[WATCHDOG]: Started tracking thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
170
171 lock (m_threads)
172 m_threads.Add(twi.Thread.ManagedThreadId, twi);
173
174 thread.Start();
175
176 return thread;
177 }
178
179 /// <summary>
180 /// Marks the current thread as alive
181 /// </summary>
182 public static void UpdateThread()
183 {
184 UpdateThread(Thread.CurrentThread.ManagedThreadId);
185 }
186
187 /// <summary>
188 /// Stops watchdog tracking on the current thread
189 /// </summary>
190 /// <returns>
191 /// True if the thread was removed from the list of tracked
192 /// threads, otherwise false
193 /// </returns>
194 public static bool RemoveThread()
195 {
196 return RemoveThread(Thread.CurrentThread.ManagedThreadId);
197 }
198
199 private static bool RemoveThread(int threadID)
200 {
201 lock (m_threads)
202 return m_threads.Remove(threadID);
203 }
204
205 public static bool AbortThread(int threadID)
206 {
207 lock (m_threads)
208 {
209 if (m_threads.ContainsKey(threadID))
210 {
211 ThreadWatchdogInfo twi = m_threads[threadID];
212 twi.Thread.Abort();
213 RemoveThread(threadID);
214
215 return true;
216 }
217 else
218 {
219 return false;
220 }
221 }
222 }
223
224 private static void UpdateThread(int threadID)
225 {
226 ThreadWatchdogInfo threadInfo;
227
228 // Although TryGetValue is not a thread safe operation, we use a try/catch here instead
229 // of a lock for speed. Adding/removing threads is a very rare operation compared to
230 // UpdateThread(), and a single UpdateThread() failure here and there won't break
231 // anything
232 try
233 {
234 if (m_threads.TryGetValue(threadID, out threadInfo))
235 {
236 threadInfo.LastTick = Environment.TickCount & Int32.MaxValue;
237 threadInfo.IsTimedOut = false;
238 }
239 else
240 {
241 m_log.WarnFormat("[WATCHDOG]: Asked to update thread {0} which is not being monitored", threadID);
242 }
243 }
244 catch { }
245 }
246
247 /// <summary>
248 /// Get currently watched threads for diagnostic purposes
249 /// </summary>
250 /// <returns></returns>
251 public static ThreadWatchdogInfo[] GetThreadsInfo()
252 {
253 lock (m_threads)
254 return m_threads.Values.ToArray();
255 }
256
257 /// <summary>
258 /// Return the current thread's watchdog info.
259 /// </summary>
260 /// <returns>The watchdog info. null if the thread isn't being monitored.</returns>
261 public static ThreadWatchdogInfo GetCurrentThreadInfo()
262 {
263 lock (m_threads)
264 {
265 if (m_threads.ContainsKey(Thread.CurrentThread.ManagedThreadId))
266 return m_threads[Thread.CurrentThread.ManagedThreadId];
267 }
268
269 return null;
270 }
271
272 /// <summary>
273 /// Check watched threads. Fire alarm if appropriate.
274 /// </summary>
275 /// <param name="sender"></param>
276 /// <param name="e"></param>
277 private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
278 {
279 int now = Environment.TickCount & Int32.MaxValue;
280 int msElapsed = now - LastWatchdogThreadTick;
281
282 if (msElapsed > WATCHDOG_INTERVAL_MS * 2)
283 m_log.WarnFormat(
284 "[WATCHDOG]: {0} ms since Watchdog last ran. Interval should be approximately {1} ms",
285 msElapsed, WATCHDOG_INTERVAL_MS);
286
287 LastWatchdogThreadTick = Environment.TickCount & Int32.MaxValue;
288
289 Action<ThreadWatchdogInfo> callback = OnWatchdogTimeout;
290
291 if (callback != null)
292 {
293 List<ThreadWatchdogInfo> callbackInfos = null;
294
295 lock (m_threads)
296 {
297 foreach (ThreadWatchdogInfo threadInfo in m_threads.Values)
298 {
299 if (threadInfo.Thread.ThreadState == ThreadState.Stopped)
300 {
301 RemoveThread(threadInfo.Thread.ManagedThreadId);
302
303 if (callbackInfos == null)
304 callbackInfos = new List<ThreadWatchdogInfo>();
305
306 callbackInfos.Add(threadInfo);
307 }
308 else if (!threadInfo.IsTimedOut && now - threadInfo.LastTick >= threadInfo.Timeout)
309 {
310 threadInfo.IsTimedOut = true;
311
312 if (threadInfo.AlarmIfTimeout)
313 {
314 if (callbackInfos == null)
315 callbackInfos = new List<ThreadWatchdogInfo>();
316
317 callbackInfos.Add(threadInfo);
318 }
319 }
320 }
321 }
322
323 if (callbackInfos != null)
324 foreach (ThreadWatchdogInfo callbackInfo in callbackInfos)
325 callback(callbackInfo);
326 }
327
328 if (MemoryWatchdog.Enabled)
329 MemoryWatchdog.Update();
330
331 m_watchdogTimer.Start();
332 }
333 }
334}