aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Monitoring
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Monitoring')
-rw-r--r--OpenSim/Framework/Monitoring/BaseStatsCollector.cs23
-rw-r--r--OpenSim/Framework/Monitoring/MemoryWatchdog.cs10
-rw-r--r--OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs33
-rw-r--r--OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs19
-rw-r--r--OpenSim/Framework/Monitoring/Stats/PercentageStat.cs88
-rw-r--r--OpenSim/Framework/Monitoring/Stats/Stat.cs238
-rw-r--r--OpenSim/Framework/Monitoring/StatsManager.cs239
-rw-r--r--OpenSim/Framework/Monitoring/Watchdog.cs39
8 files changed, 674 insertions, 15 deletions
diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
index 9ee0876..446e3c0 100644
--- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
@@ -43,27 +43,32 @@ namespace OpenSim.Framework.Monitoring
43 StringBuilder sb = new StringBuilder(Environment.NewLine); 43 StringBuilder sb = new StringBuilder(Environment.NewLine);
44 sb.Append("MEMORY STATISTICS"); 44 sb.Append("MEMORY STATISTICS");
45 sb.Append(Environment.NewLine); 45 sb.Append(Environment.NewLine);
46 sb.Append( 46 sb.AppendFormat(
47 string.Format(
48 "Allocated to OpenSim objects: {0} MB\n", 47 "Allocated to OpenSim objects: {0} MB\n",
49 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0))); 48 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
49
50 sb.AppendFormat(
51 "OpenSim last object memory churn : {0} MB/s\n",
52 Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3));
53
54 sb.AppendFormat(
55 "OpenSim average object memory churn : {0} MB/s\n",
56 Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3));
50 57
51 Process myprocess = Process.GetCurrentProcess(); 58 Process myprocess = Process.GetCurrentProcess();
52 if (!myprocess.HasExited) 59 if (!myprocess.HasExited)
53 { 60 {
54 myprocess.Refresh(); 61 myprocess.Refresh();
55 sb.Append( 62 sb.AppendFormat(
56 string.Format(
57 "Process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n", 63 "Process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n",
58 Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0), 64 Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0),
59 Math.Round(Process.GetCurrentProcess().PagedMemorySize64 / 1024.0 / 1024.0), 65 Math.Round(Process.GetCurrentProcess().PagedMemorySize64 / 1024.0 / 1024.0),
60 Math.Round(Process.GetCurrentProcess().VirtualMemorySize64 / 1024.0 / 1024.0))); 66 Math.Round(Process.GetCurrentProcess().VirtualMemorySize64 / 1024.0 / 1024.0));
61 sb.Append( 67 sb.AppendFormat(
62 string.Format(
63 "Peak process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n", 68 "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), 69 Math.Round(Process.GetCurrentProcess().PeakWorkingSet64 / 1024.0 / 1024.0),
65 Math.Round(Process.GetCurrentProcess().PeakPagedMemorySize64 / 1024.0 / 1024.0), 70 Math.Round(Process.GetCurrentProcess().PeakPagedMemorySize64 / 1024.0 / 1024.0),
66 Math.Round(Process.GetCurrentProcess().PeakVirtualMemorySize64 / 1024.0 / 1024.0))); 71 Math.Round(Process.GetCurrentProcess().PeakVirtualMemorySize64 / 1024.0 / 1024.0));
67 } 72 }
68 else 73 else
69 sb.Append("Process reported as Exited \n"); 74 sb.Append("Process reported as Exited \n");
diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
index a23cf1f..c6010cd 100644
--- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
+++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs
@@ -60,7 +60,7 @@ namespace OpenSim.Framework.Monitoring
60 private static bool m_enabled; 60 private static bool m_enabled;
61 61
62 /// <summary> 62 /// <summary>
63 /// Average memory churn in bytes per millisecond. 63 /// Last memory churn in bytes per millisecond.
64 /// </summary> 64 /// </summary>
65 public static double AverageMemoryChurn 65 public static double AverageMemoryChurn
66 { 66 {
@@ -68,6 +68,14 @@ namespace OpenSim.Framework.Monitoring
68 } 68 }
69 69
70 /// <summary> 70 /// <summary>
71 /// Average memory churn in bytes per millisecond.
72 /// </summary>
73 public static double LastMemoryChurn
74 {
75 get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; }
76 }
77
78 /// <summary>
71 /// Maximum number of statistical samples. 79 /// Maximum number of statistical samples.
72 /// </summary> 80 /// </summary>
73 /// <remarks> 81 /// <remarks>
diff --git a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..1f2bb40
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
1using System.Reflection;
2using System.Runtime.CompilerServices;
3using System.Runtime.InteropServices;
4
5// General Information about an assembly is controlled through the following
6// set of attributes. Change these attribute values to modify the information
7// associated with an assembly.
8[assembly: AssemblyTitle("OpenSim.Framework.Monitoring")]
9[assembly: AssemblyDescription("")]
10[assembly: AssemblyConfiguration("")]
11[assembly: AssemblyCompany("http://opensimulator.org")]
12[assembly: AssemblyProduct("OpenSim")]
13[assembly: AssemblyCopyright("OpenSimulator developers")]
14[assembly: AssemblyTrademark("")]
15[assembly: AssemblyCulture("")]
16
17// Setting ComVisible to false makes the types in this assembly not visible
18// to COM components. If you need to access a type in this assembly from
19// COM, set the ComVisible attribute to true on that type.
20[assembly: ComVisible(false)]
21
22// The following GUID is for the ID of the typelib if this project is exposed to COM
23[assembly: Guid("74506fe3-2f9d-44c1-94c9-a30f79d9e0cb")]
24
25// Version information for an assembly consists of the following four values:
26//
27// Major Version
28// Minor Version
29// Build Number
30// Revision
31//
32[assembly: AssemblyVersion("0.7.5.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
index cdd7cc7..aa86202 100644
--- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
@@ -355,10 +355,25 @@ Asset service request failures: {3}" + Environment.NewLine,
355 sb.Append(Environment.NewLine); 355 sb.Append(Environment.NewLine);
356 sb.Append( 356 sb.Append(
357 string.Format( 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}", 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}\n\n",
359 inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime, 359 inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
360 netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime)); 360 netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
361 sb.Append(Environment.NewLine); 361
362 Dictionary<string, Dictionary<string, Stat>> sceneStats;
363
364 if (StatsManager.TryGetStats("scene", out sceneStats))
365 {
366 foreach (KeyValuePair<string, Dictionary<string, Stat>> kvp in sceneStats)
367 {
368 foreach (Stat stat in kvp.Value.Values)
369 {
370 if (stat.Verbosity == StatVerbosity.Info)
371 {
372 sb.AppendFormat("{0} ({1}): {2}{3}\n", stat.Name, stat.Container, stat.Value, stat.UnitName);
373 }
374 }
375 }
376 }
362 377
363 /* 378 /*
364 sb.Append(Environment.NewLine); 379 sb.Append(Environment.NewLine);
diff --git a/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs
new file mode 100644
index 0000000..60bed55
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs
@@ -0,0 +1,88 @@
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;
31
32namespace OpenSim.Framework.Monitoring
33{
34 public class PercentageStat : Stat
35 {
36 public long Antecedent { get; set; }
37 public long Consequent { get; set; }
38
39 public override double Value
40 {
41 get
42 {
43 // Asking for an update here means that the updater cannot access this value without infinite recursion.
44 // XXX: A slightly messy but simple solution may be to flick a flag so we can tell if this is being
45 // called by the pull action and just return the value.
46 if (StatType == StatType.Pull)
47 PullAction(this);
48
49 long c = Consequent;
50
51 // Avoid any chance of a multi-threaded divide-by-zero
52 if (c == 0)
53 return 0;
54
55 return (double)Antecedent / c * 100;
56 }
57
58 set
59 {
60 throw new InvalidOperationException("Cannot set value on a PercentageStat");
61 }
62 }
63
64 public PercentageStat(
65 string shortName,
66 string name,
67 string description,
68 string category,
69 string container,
70 StatType type,
71 Action<Stat> pullAction,
72 StatVerbosity verbosity)
73 : base(shortName, name, description, "%", category, container, type, pullAction, verbosity) {}
74
75 public override string ToConsoleString()
76 {
77 StringBuilder sb = new StringBuilder();
78
79 sb.AppendFormat(
80 "{0}.{1}.{2} : {3:0.##}{4} ({5}/{6})",
81 Category, Container, ShortName, Value, UnitName, Antecedent, Consequent);
82
83 AppendMeasuresOfInterest(sb);
84
85 return sb.ToString();
86 }
87 }
88} \ No newline at end of file
diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs
new file mode 100644
index 0000000..f91251b
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs
@@ -0,0 +1,238 @@
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;
31
32namespace OpenSim.Framework.Monitoring
33{
34 /// <summary>
35 /// Holds individual statistic details
36 /// </summary>
37 public class Stat
38 {
39 /// <summary>
40 /// Category of this stat (e.g. cache, scene, etc).
41 /// </summary>
42 public string Category { get; private set; }
43
44 /// <summary>
45 /// Containing name for this stat.
46 /// FIXME: In the case of a scene, this is currently the scene name (though this leaves
47 /// us with a to-be-resolved problem of non-unique region names).
48 /// </summary>
49 /// <value>
50 /// The container.
51 /// </value>
52 public string Container { get; private set; }
53
54 public StatType StatType { get; private set; }
55
56 public MeasuresOfInterest MeasuresOfInterest { get; private set; }
57
58 /// <summary>
59 /// Action used to update this stat when the value is requested if it's a pull type.
60 /// </summary>
61 public Action<Stat> PullAction { get; private set; }
62
63 public StatVerbosity Verbosity { get; private set; }
64 public string ShortName { get; private set; }
65 public string Name { get; private set; }
66 public string Description { get; private set; }
67 public virtual string UnitName { get; private set; }
68
69 public virtual double Value
70 {
71 get
72 {
73 // Asking for an update here means that the updater cannot access this value without infinite recursion.
74 // XXX: A slightly messy but simple solution may be to flick a flag so we can tell if this is being
75 // called by the pull action and just return the value.
76 if (StatType == StatType.Pull)
77 PullAction(this);
78
79 return m_value;
80 }
81
82 set
83 {
84 m_value = value;
85 }
86 }
87
88 private double m_value;
89
90 /// <summary>
91 /// Historical samples for calculating measures of interest average.
92 /// </summary>
93 /// <remarks>
94 /// Will be null if no measures of interest require samples.
95 /// </remarks>
96 private static Queue<double> m_samples;
97
98 /// <summary>
99 /// Maximum number of statistical samples.
100 /// </summary>
101 /// <remarks>
102 /// At the moment this corresponds to 1 minute since the sampling rate is every 2.5 seconds as triggered from
103 /// the main Watchdog.
104 /// </remarks>
105 private static int m_maxSamples = 24;
106
107 public Stat(
108 string shortName,
109 string name,
110 string description,
111 string unitName,
112 string category,
113 string container,
114 StatType type,
115 Action<Stat> pullAction,
116 StatVerbosity verbosity)
117 : this(
118 shortName,
119 name,
120 description,
121 unitName,
122 category,
123 container,
124 type,
125 MeasuresOfInterest.None,
126 pullAction,
127 verbosity)
128 {
129 }
130
131 /// <summary>
132 /// Constructor
133 /// </summary>
134 /// <param name='shortName'>Short name for the stat. Must not contain spaces. e.g. "LongFrames"</param>
135 /// <param name='name'>Human readable name for the stat. e.g. "Long frames"</param>
136 /// <param name='description'>Description of stat</param>
137 /// <param name='unitName'>
138 /// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value.
139 /// e.g. " frames"
140 /// </param>
141 /// <param name='category'>Category under which this stat should appear, e.g. "scene". Do not capitalize.</param>
142 /// <param name='container'>Entity to which this stat relates. e.g. scene name if this is a per scene stat.</param>
143 /// <param name='type'>Push or pull</param>
144 /// <param name='pullAction'>Pull stats need an action to update the stat on request. Push stats should set null here.</param>
145 /// <param name='moi'>Measures of interest</param>
146 /// <param name='verbosity'>Verbosity of stat. Controls whether it will appear in short stat display or only full display.</param>
147 public Stat(
148 string shortName,
149 string name,
150 string description,
151 string unitName,
152 string category,
153 string container,
154 StatType type,
155 MeasuresOfInterest moi,
156 Action<Stat> pullAction,
157 StatVerbosity verbosity)
158 {
159 if (StatsManager.SubCommands.Contains(category))
160 throw new Exception(
161 string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category));
162
163 ShortName = shortName;
164 Name = name;
165 Description = description;
166 UnitName = unitName;
167 Category = category;
168 Container = container;
169 StatType = type;
170
171 if (StatType == StatType.Push && pullAction != null)
172 throw new Exception("A push stat cannot have a pull action");
173 else
174 PullAction = pullAction;
175
176 MeasuresOfInterest = moi;
177
178 if ((moi & MeasuresOfInterest.AverageChangeOverTime) == MeasuresOfInterest.AverageChangeOverTime)
179 m_samples = new Queue<double>(m_maxSamples);
180
181 Verbosity = verbosity;
182 }
183
184 /// <summary>
185 /// Record a value in the sample set.
186 /// </summary>
187 /// <remarks>
188 /// Do not call this if MeasuresOfInterest.None
189 /// </remarks>
190 public void RecordValue()
191 {
192 double newValue = Value;
193
194 lock (m_samples)
195 {
196 if (m_samples.Count >= m_maxSamples)
197 m_samples.Dequeue();
198
199 m_samples.Enqueue(newValue);
200 }
201 }
202
203 public virtual string ToConsoleString()
204 {
205 StringBuilder sb = new StringBuilder();
206 sb.AppendFormat("{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName);
207
208 AppendMeasuresOfInterest(sb);
209
210 return sb.ToString();
211 }
212
213 protected void AppendMeasuresOfInterest(StringBuilder sb)
214 {
215 if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime)
216 == MeasuresOfInterest.AverageChangeOverTime)
217 {
218 double totalChange = 0;
219 double? lastSample = null;
220
221 lock (m_samples)
222 {
223 foreach (double s in m_samples)
224 {
225 if (lastSample != null)
226 totalChange += s - (double)lastSample;
227
228 lastSample = s;
229 }
230 }
231
232 int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1;
233
234 sb.AppendFormat(", {0:0.##}{1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName);
235 }
236 }
237 }
238} \ No newline at end of file
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
index d78fa6a..0762b01 100644
--- a/OpenSim/Framework/Monitoring/StatsManager.cs
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -25,6 +25,10 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System;
29using System.Collections.Generic;
30using System.Text;
31
28namespace OpenSim.Framework.Monitoring 32namespace OpenSim.Framework.Monitoring
29{ 33{
30 /// <summary> 34 /// <summary>
@@ -32,6 +36,24 @@ namespace OpenSim.Framework.Monitoring
32 /// </summary> 36 /// </summary>
33 public class StatsManager 37 public class StatsManager
34 { 38 {
39 // Subcommand used to list other stats.
40 public const string AllSubCommand = "all";
41
42 // Subcommand used to list other stats.
43 public const string ListSubCommand = "list";
44
45 // All subcommands
46 public static HashSet<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand };
47
48 /// <summary>
49 /// Registered stats categorized by category/container/shortname
50 /// </summary>
51 /// <remarks>
52 /// Do not add or remove directly from this dictionary.
53 /// </remarks>
54 public static Dictionary<string, Dictionary<string, Dictionary<string, Stat>>> RegisteredStats
55 = new Dictionary<string, Dictionary<string, Dictionary<string, Stat>>>();
56
35 private static AssetStatsCollector assetStats; 57 private static AssetStatsCollector assetStats;
36 private static UserStatsCollector userStats; 58 private static UserStatsCollector userStats;
37 private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); 59 private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
@@ -40,6 +62,75 @@ namespace OpenSim.Framework.Monitoring
40 public static UserStatsCollector UserStats { get { return userStats; } } 62 public static UserStatsCollector UserStats { get { return userStats; } }
41 public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } 63 public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
42 64
65 public static void RegisterConsoleCommands(ICommandConsole console)
66 {
67 console.Commands.AddCommand(
68 "General",
69 false,
70 "show stats",
71 "show stats [list|all|<category>]",
72 "Show statistical information for this server",
73 "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 + "If all is specified then all registered statistics are shown.\n"
76 + "If a category name is specified then only statistics from that category are shown.\n"
77 + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
78 HandleShowStatsCommand);
79 }
80
81 public static void HandleShowStatsCommand(string module, string[] cmd)
82 {
83 ICommandConsole con = MainConsole.Instance;
84
85 if (cmd.Length > 2)
86 {
87 var categoryName = cmd[2];
88
89 if (categoryName == AllSubCommand)
90 {
91 foreach (var category in RegisteredStats.Values)
92 {
93 OutputCategoryStatsToConsole(con, category);
94 }
95 }
96 else if (categoryName == ListSubCommand)
97 {
98 con.Output("Statistic categories available are:");
99 foreach (string category in RegisteredStats.Keys)
100 con.OutputFormat(" {0}", category);
101 }
102 else
103 {
104 Dictionary<string, Dictionary<string, Stat>> category;
105 if (!RegisteredStats.TryGetValue(categoryName, out category))
106 {
107 con.OutputFormat("No such category as {0}", categoryName);
108 }
109 else
110 {
111 OutputCategoryStatsToConsole(con, category);
112 }
113 }
114 }
115 else
116 {
117 // Legacy
118 con.Output(SimExtraStats.Report());
119 }
120 }
121
122 private static void OutputCategoryStatsToConsole(
123 ICommandConsole con, Dictionary<string, Dictionary<string, Stat>> category)
124 {
125 foreach (var container in category.Values)
126 {
127 foreach (Stat stat in container.Values)
128 {
129 con.Output(stat.ToConsoleString());
130 }
131 }
132 }
133
43 /// <summary> 134 /// <summary>
44 /// Start collecting statistics related to assets. 135 /// Start collecting statistics related to assets.
45 /// Should only be called once. 136 /// Should only be called once.
@@ -61,5 +152,153 @@ namespace OpenSim.Framework.Monitoring
61 152
62 return userStats; 153 return userStats;
63 } 154 }
155
156 /// <summary>
157 /// Registers a statistic.
158 /// </summary>
159 /// <param name='stat'></param>
160 /// <returns></returns>
161 public static bool RegisterStat(Stat stat)
162 {
163 Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
164 Dictionary<string, Stat> container = null, newContainer;
165
166 lock (RegisteredStats)
167 {
168 // Stat name is not unique across category/container/shortname key.
169 // XXX: For now just return false. This is to avoid problems in regression tests where all tests
170 // in a class are run in the same instance of the VM.
171 if (TryGetStat(stat, out category, out container))
172 return false;
173
174 // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed.
175 // This means that we don't need to lock or copy them on iteration, which will be a much more
176 // common operation after startup.
177 if (container != null)
178 newContainer = new Dictionary<string, Stat>(container);
179 else
180 newContainer = new Dictionary<string, Stat>();
181
182 if (category != null)
183 newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
184 else
185 newCategory = new Dictionary<string, Dictionary<string, Stat>>();
186
187 newContainer[stat.ShortName] = stat;
188 newCategory[stat.Container] = newContainer;
189 RegisteredStats[stat.Category] = newCategory;
190 }
191
192 return true;
193 }
194
195 /// <summary>
196 /// Deregister a statistic
197 /// </summary>>
198 /// <param name='stat'></param>
199 /// <returns></returns
200 public static bool DeregisterStat(Stat stat)
201 {
202 Dictionary<string, Dictionary<string, Stat>> category = null, newCategory;
203 Dictionary<string, Stat> container = null, newContainer;
204
205 lock (RegisteredStats)
206 {
207 if (!TryGetStat(stat, out category, out container))
208 return false;
209
210 newContainer = new Dictionary<string, Stat>(container);
211 newContainer.Remove(stat.ShortName);
212
213 newCategory = new Dictionary<string, Dictionary<string, Stat>>(category);
214 newCategory.Remove(stat.Container);
215
216 newCategory[stat.Container] = newContainer;
217 RegisteredStats[stat.Category] = newCategory;
218
219 return true;
220 }
221 }
222
223 public static bool TryGetStats(string category, out Dictionary<string, Dictionary<string, Stat>> stats)
224 {
225 return RegisteredStats.TryGetValue(category, out stats);
226 }
227
228 public static bool TryGetStat(
229 Stat stat,
230 out Dictionary<string, Dictionary<string, Stat>> category,
231 out Dictionary<string, Stat> container)
232 {
233 category = null;
234 container = null;
235
236 lock (RegisteredStats)
237 {
238 if (RegisteredStats.TryGetValue(stat.Category, out category))
239 {
240 if (category.TryGetValue(stat.Container, out container))
241 {
242 if (container.ContainsKey(stat.ShortName))
243 return true;
244 }
245 }
246 }
247
248 return false;
249 }
250
251 public static void RecordStats()
252 {
253 lock (RegisteredStats)
254 {
255 foreach (Dictionary<string, Dictionary<string, Stat>> category in RegisteredStats.Values)
256 {
257 foreach (Dictionary<string, Stat> container in category.Values)
258 {
259 foreach (Stat stat in container.Values)
260 {
261 if (stat.MeasuresOfInterest != MeasuresOfInterest.None)
262 stat.RecordValue();
263 }
264 }
265 }
266 }
267 }
268 }
269
270 /// <summary>
271 /// Stat type.
272 /// </summary>
273 /// <remarks>
274 /// A push stat is one which is continually updated and so it's value can simply by read.
275 /// A pull stat is one where reading the value triggers a collection method - the stat is not continually updated.
276 /// </remarks>
277 public enum StatType
278 {
279 Push,
280 Pull
281 }
282
283 /// <summary>
284 /// Measures of interest for this stat.
285 /// </summary>
286 [Flags]
287 public enum MeasuresOfInterest
288 {
289 None,
290 AverageChangeOverTime
291 }
292
293 /// <summary>
294 /// Verbosity of stat.
295 /// </summary>
296 /// <remarks>
297 /// Info will always be displayed.
298 /// </remarks>
299 public enum StatVerbosity
300 {
301 Debug,
302 Info
64 } 303 }
65} \ No newline at end of file 304} \ No newline at end of file
diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs
index b709baa..69d2db5 100644
--- a/OpenSim/Framework/Monitoring/Watchdog.cs
+++ b/OpenSim/Framework/Monitoring/Watchdog.cs
@@ -39,7 +39,7 @@ namespace OpenSim.Framework.Monitoring
39 public static class Watchdog 39 public static class Watchdog
40 { 40 {
41 /// <summary>Timer interval in milliseconds for the watchdog timer</summary> 41 /// <summary>Timer interval in milliseconds for the watchdog timer</summary>
42 const double WATCHDOG_INTERVAL_MS = 2500.0d; 42 public const double WATCHDOG_INTERVAL_MS = 2500.0d;
43 43
44 /// <summary>Default timeout in milliseconds before a thread is considered dead</summary> 44 /// <summary>Default timeout in milliseconds before a thread is considered dead</summary>
45 public const int DEFAULT_WATCHDOG_TIMEOUT_MS = 5000; 45 public const int DEFAULT_WATCHDOG_TIMEOUT_MS = 5000;
@@ -89,6 +89,17 @@ namespace OpenSim.Framework.Monitoring
89 FirstTick = Environment.TickCount & Int32.MaxValue; 89 FirstTick = Environment.TickCount & Int32.MaxValue;
90 LastTick = FirstTick; 90 LastTick = FirstTick;
91 } 91 }
92
93 public ThreadWatchdogInfo(ThreadWatchdogInfo previousTwi)
94 {
95 Thread = previousTwi.Thread;
96 FirstTick = previousTwi.FirstTick;
97 LastTick = previousTwi.LastTick;
98 Timeout = previousTwi.Timeout;
99 IsTimedOut = previousTwi.IsTimedOut;
100 AlarmIfTimeout = previousTwi.AlarmIfTimeout;
101 AlarmMethod = previousTwi.AlarmMethod;
102 }
92 } 103 }
93 104
94 /// <summary> 105 /// <summary>
@@ -220,7 +231,25 @@ namespace OpenSim.Framework.Monitoring
220 private static bool RemoveThread(int threadID) 231 private static bool RemoveThread(int threadID)
221 { 232 {
222 lock (m_threads) 233 lock (m_threads)
223 return m_threads.Remove(threadID); 234 {
235 ThreadWatchdogInfo twi;
236 if (m_threads.TryGetValue(threadID, out twi))
237 {
238 m_log.DebugFormat(
239 "[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
240
241 m_threads.Remove(threadID);
242
243 return true;
244 }
245 else
246 {
247 m_log.WarnFormat(
248 "[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID);
249
250 return false;
251 }
252 }
224 } 253 }
225 254
226 public static bool AbortThread(int threadID) 255 public static bool AbortThread(int threadID)
@@ -335,7 +364,9 @@ namespace OpenSim.Framework.Monitoring
335 if (callbackInfos == null) 364 if (callbackInfos == null)
336 callbackInfos = new List<ThreadWatchdogInfo>(); 365 callbackInfos = new List<ThreadWatchdogInfo>();
337 366
338 callbackInfos.Add(threadInfo); 367 // Send a copy of the watchdog info to prevent race conditions where the watchdog
368 // thread updates the monitoring info after an alarm has been sent out.
369 callbackInfos.Add(new ThreadWatchdogInfo(threadInfo));
339 } 370 }
340 } 371 }
341 } 372 }
@@ -349,6 +380,8 @@ namespace OpenSim.Framework.Monitoring
349 if (MemoryWatchdog.Enabled) 380 if (MemoryWatchdog.Enabled)
350 MemoryWatchdog.Update(); 381 MemoryWatchdog.Update();
351 382
383 StatsManager.RecordStats();
384
352 m_watchdogTimer.Start(); 385 m_watchdogTimer.Start();
353 } 386 }
354 } 387 }