diff options
Diffstat (limited to 'OpenSim/Framework/Monitoring')
-rw-r--r-- | OpenSim/Framework/Monitoring/BaseStatsCollector.cs | 23 | ||||
-rw-r--r-- | OpenSim/Framework/Monitoring/MemoryWatchdog.cs | 10 | ||||
-rw-r--r-- | OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs | 19 | ||||
-rw-r--r-- | OpenSim/Framework/Monitoring/StatsManager.cs | 360 | ||||
-rw-r--r-- | OpenSim/Framework/Monitoring/Watchdog.cs | 35 |
5 files changed, 433 insertions, 14 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/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/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index d78fa6a..4844336 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs | |||
@@ -25,6 +25,9 @@ | |||
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 | ||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | |||
28 | namespace OpenSim.Framework.Monitoring | 31 | namespace OpenSim.Framework.Monitoring |
29 | { | 32 | { |
30 | /// <summary> | 33 | /// <summary> |
@@ -32,6 +35,24 @@ namespace OpenSim.Framework.Monitoring | |||
32 | /// </summary> | 35 | /// </summary> |
33 | public class StatsManager | 36 | public class StatsManager |
34 | { | 37 | { |
38 | // Subcommand used to list other stats. | ||
39 | public const string AllSubCommand = "all"; | ||
40 | |||
41 | // Subcommand used to list other stats. | ||
42 | public const string ListSubCommand = "list"; | ||
43 | |||
44 | // All subcommands | ||
45 | public static HashSet<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand }; | ||
46 | |||
47 | /// <summary> | ||
48 | /// Registered stats categorized by category/container/shortname | ||
49 | /// </summary> | ||
50 | /// <remarks> | ||
51 | /// Do not add or remove directly from this dictionary. | ||
52 | /// </remarks> | ||
53 | public static Dictionary<string, Dictionary<string, Dictionary<string, Stat>>> RegisteredStats | ||
54 | = new Dictionary<string, Dictionary<string, Dictionary<string, Stat>>>(); | ||
55 | |||
35 | private static AssetStatsCollector assetStats; | 56 | private static AssetStatsCollector assetStats; |
36 | private static UserStatsCollector userStats; | 57 | private static UserStatsCollector userStats; |
37 | private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); | 58 | private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); |
@@ -40,6 +61,75 @@ namespace OpenSim.Framework.Monitoring | |||
40 | public static UserStatsCollector UserStats { get { return userStats; } } | 61 | public static UserStatsCollector UserStats { get { return userStats; } } |
41 | public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } | 62 | public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } |
42 | 63 | ||
64 | public static void RegisterConsoleCommands(ICommandConsole console) | ||
65 | { | ||
66 | console.Commands.AddCommand( | ||
67 | "General", | ||
68 | false, | ||
69 | "show stats", | ||
70 | "show stats [list|all|<category>]", | ||
71 | "Show statistical information for this server", | ||
72 | "If no final argument is specified then legacy statistics information is currently shown.\n" | ||
73 | + "If list is specified then statistic categories are shown.\n" | ||
74 | + "If all is specified then all registered statistics are shown.\n" | ||
75 | + "If a category name is specified then only statistics from that category are shown.\n" | ||
76 | + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS", | ||
77 | HandleShowStatsCommand); | ||
78 | } | ||
79 | |||
80 | public static void HandleShowStatsCommand(string module, string[] cmd) | ||
81 | { | ||
82 | ICommandConsole con = MainConsole.Instance; | ||
83 | |||
84 | if (cmd.Length > 2) | ||
85 | { | ||
86 | var categoryName = cmd[2]; | ||
87 | |||
88 | if (categoryName == AllSubCommand) | ||
89 | { | ||
90 | foreach (var category in RegisteredStats.Values) | ||
91 | { | ||
92 | OutputCategoryStatsToConsole(con, category); | ||
93 | } | ||
94 | } | ||
95 | else if (categoryName == ListSubCommand) | ||
96 | { | ||
97 | con.Output("Statistic categories available are:"); | ||
98 | foreach (string category in RegisteredStats.Keys) | ||
99 | con.OutputFormat(" {0}", category); | ||
100 | } | ||
101 | else | ||
102 | { | ||
103 | Dictionary<string, Dictionary<string, Stat>> category; | ||
104 | if (!RegisteredStats.TryGetValue(categoryName, out category)) | ||
105 | { | ||
106 | con.OutputFormat("No such category as {0}", categoryName); | ||
107 | } | ||
108 | else | ||
109 | { | ||
110 | OutputCategoryStatsToConsole(con, category); | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | else | ||
115 | { | ||
116 | // Legacy | ||
117 | con.Output(SimExtraStats.Report()); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | private static void OutputCategoryStatsToConsole( | ||
122 | ICommandConsole con, Dictionary<string, Dictionary<string, Stat>> category) | ||
123 | { | ||
124 | foreach (var container in category.Values) | ||
125 | { | ||
126 | foreach (Stat stat in container.Values) | ||
127 | { | ||
128 | con.Output(stat.ToConsoleString()); | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
43 | /// <summary> | 133 | /// <summary> |
44 | /// Start collecting statistics related to assets. | 134 | /// Start collecting statistics related to assets. |
45 | /// Should only be called once. | 135 | /// Should only be called once. |
@@ -61,5 +151,275 @@ namespace OpenSim.Framework.Monitoring | |||
61 | 151 | ||
62 | return userStats; | 152 | return userStats; |
63 | } | 153 | } |
154 | |||
155 | /// <summary> | ||
156 | /// Registers a statistic. | ||
157 | /// </summary> | ||
158 | /// <param name='stat'></param> | ||
159 | /// <returns></returns> | ||
160 | public static bool RegisterStat(Stat stat) | ||
161 | { | ||
162 | Dictionary<string, Dictionary<string, Stat>> category = null, newCategory; | ||
163 | Dictionary<string, Stat> container = null, newContainer; | ||
164 | |||
165 | lock (RegisteredStats) | ||
166 | { | ||
167 | // Stat name is not unique across category/container/shortname key. | ||
168 | // XXX: For now just return false. This is to avoid problems in regression tests where all tests | ||
169 | // in a class are run in the same instance of the VM. | ||
170 | if (TryGetStat(stat, out category, out container)) | ||
171 | return false; | ||
172 | |||
173 | // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. | ||
174 | // This means that we don't need to lock or copy them on iteration, which will be a much more | ||
175 | // common operation after startup. | ||
176 | if (container != null) | ||
177 | newContainer = new Dictionary<string, Stat>(container); | ||
178 | else | ||
179 | newContainer = new Dictionary<string, Stat>(); | ||
180 | |||
181 | if (category != null) | ||
182 | newCategory = new Dictionary<string, Dictionary<string, Stat>>(category); | ||
183 | else | ||
184 | newCategory = new Dictionary<string, Dictionary<string, Stat>>(); | ||
185 | |||
186 | newContainer[stat.ShortName] = stat; | ||
187 | newCategory[stat.Container] = newContainer; | ||
188 | RegisteredStats[stat.Category] = newCategory; | ||
189 | } | ||
190 | |||
191 | return true; | ||
192 | } | ||
193 | |||
194 | /// <summary> | ||
195 | /// Deregister a statistic | ||
196 | /// </summary>> | ||
197 | /// <param name='stat'></param> | ||
198 | /// <returns></returns | ||
199 | public static bool DeregisterStat(Stat stat) | ||
200 | { | ||
201 | Dictionary<string, Dictionary<string, Stat>> category = null, newCategory; | ||
202 | Dictionary<string, Stat> container = null, newContainer; | ||
203 | |||
204 | lock (RegisteredStats) | ||
205 | { | ||
206 | if (!TryGetStat(stat, out category, out container)) | ||
207 | return false; | ||
208 | |||
209 | newContainer = new Dictionary<string, Stat>(container); | ||
210 | newContainer.Remove(stat.ShortName); | ||
211 | |||
212 | newCategory = new Dictionary<string, Dictionary<string, Stat>>(category); | ||
213 | newCategory.Remove(stat.Container); | ||
214 | |||
215 | newCategory[stat.Container] = newContainer; | ||
216 | RegisteredStats[stat.Category] = newCategory; | ||
217 | |||
218 | return true; | ||
219 | } | ||
220 | } | ||
221 | |||
222 | public static bool TryGetStats(string category, out Dictionary<string, Dictionary<string, Stat>> stats) | ||
223 | { | ||
224 | return RegisteredStats.TryGetValue(category, out stats); | ||
225 | } | ||
226 | |||
227 | public static bool TryGetStat( | ||
228 | Stat stat, | ||
229 | out Dictionary<string, Dictionary<string, Stat>> category, | ||
230 | out Dictionary<string, Stat> container) | ||
231 | { | ||
232 | category = null; | ||
233 | container = null; | ||
234 | |||
235 | lock (RegisteredStats) | ||
236 | { | ||
237 | if (RegisteredStats.TryGetValue(stat.Category, out category)) | ||
238 | { | ||
239 | if (category.TryGetValue(stat.Container, out container)) | ||
240 | { | ||
241 | if (container.ContainsKey(stat.ShortName)) | ||
242 | return true; | ||
243 | } | ||
244 | } | ||
245 | } | ||
246 | |||
247 | return false; | ||
248 | } | ||
249 | } | ||
250 | |||
251 | /// <summary> | ||
252 | /// Stat type. | ||
253 | /// </summary> | ||
254 | /// <remarks> | ||
255 | /// A push stat is one which is continually updated and so it's value can simply by read. | ||
256 | /// A pull stat is one where reading the value triggers a collection method - the stat is not continually updated. | ||
257 | /// </remarks> | ||
258 | public enum StatType | ||
259 | { | ||
260 | Push, | ||
261 | Pull | ||
262 | } | ||
263 | |||
264 | /// <summary> | ||
265 | /// Verbosity of stat. | ||
266 | /// </summary> | ||
267 | /// <remarks> | ||
268 | /// Info will always be displayed. | ||
269 | /// </remarks> | ||
270 | public enum StatVerbosity | ||
271 | { | ||
272 | Debug, | ||
273 | Info | ||
274 | } | ||
275 | |||
276 | /// <summary> | ||
277 | /// Holds individual static details | ||
278 | /// </summary> | ||
279 | public class Stat | ||
280 | { | ||
281 | /// <summary> | ||
282 | /// Category of this stat (e.g. cache, scene, etc). | ||
283 | /// </summary> | ||
284 | public string Category { get; private set; } | ||
285 | |||
286 | /// <summary> | ||
287 | /// Containing name for this stat. | ||
288 | /// FIXME: In the case of a scene, this is currently the scene name (though this leaves | ||
289 | /// us with a to-be-resolved problem of non-unique region names). | ||
290 | /// </summary> | ||
291 | /// <value> | ||
292 | /// The container. | ||
293 | /// </value> | ||
294 | public string Container { get; private set; } | ||
295 | |||
296 | public StatType StatType { get; private set; } | ||
297 | |||
298 | /// <summary> | ||
299 | /// Action used to update this stat when the value is requested if it's a pull type. | ||
300 | /// </summary> | ||
301 | public Action<Stat> PullAction { get; private set; } | ||
302 | |||
303 | public StatVerbosity Verbosity { get; private set; } | ||
304 | public string ShortName { get; private set; } | ||
305 | public string Name { get; private set; } | ||
306 | public string Description { get; private set; } | ||
307 | public virtual string UnitName { get; private set; } | ||
308 | |||
309 | public virtual double Value | ||
310 | { | ||
311 | get | ||
312 | { | ||
313 | // Asking for an update here means that the updater cannot access this value without infinite recursion. | ||
314 | // XXX: A slightly messy but simple solution may be to flick a flag so we can tell if this is being | ||
315 | // called by the pull action and just return the value. | ||
316 | if (StatType == StatType.Pull) | ||
317 | PullAction(this); | ||
318 | |||
319 | return m_value; | ||
320 | } | ||
321 | |||
322 | set | ||
323 | { | ||
324 | m_value = value; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | private double m_value; | ||
329 | |||
330 | /// <summary> | ||
331 | /// Constructor | ||
332 | /// </summary> | ||
333 | /// <param name='shortName'>Short name for the stat. Must not contain spaces. e.g. "LongFrames"</param> | ||
334 | /// <param name='name'>Human readable name for the stat. e.g. "Long frames"</param> | ||
335 | /// <param name='description'>Description of stat</param> | ||
336 | /// <param name='unitName'> | ||
337 | /// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value. | ||
338 | /// e.g. " frames" | ||
339 | /// </param> | ||
340 | /// <param name='category'>Category under which this stat should appear, e.g. "scene". Do not capitalize.</param> | ||
341 | /// <param name='container'>Entity to which this stat relates. e.g. scene name if this is a per scene stat.</param> | ||
342 | /// <param name='type'>Push or pull</param> | ||
343 | /// <param name='pullAction'>Pull stats need an action to update the stat on request. Push stats should set null here.</param> | ||
344 | /// <param name='verbosity'>Verbosity of stat. Controls whether it will appear in short stat display or only full display.</param> | ||
345 | public Stat( | ||
346 | string shortName, | ||
347 | string name, | ||
348 | string description, | ||
349 | string unitName, | ||
350 | string category, | ||
351 | string container, | ||
352 | StatType type, | ||
353 | Action<Stat> pullAction, | ||
354 | StatVerbosity verbosity) | ||
355 | { | ||
356 | if (StatsManager.SubCommands.Contains(category)) | ||
357 | throw new Exception( | ||
358 | string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category)); | ||
359 | |||
360 | ShortName = shortName; | ||
361 | Name = name; | ||
362 | Description = description; | ||
363 | UnitName = unitName; | ||
364 | Category = category; | ||
365 | Container = container; | ||
366 | StatType = type; | ||
367 | |||
368 | if (StatType == StatType.Push && pullAction != null) | ||
369 | throw new Exception("A push stat cannot have a pull action"); | ||
370 | else | ||
371 | PullAction = pullAction; | ||
372 | |||
373 | Verbosity = verbosity; | ||
374 | } | ||
375 | |||
376 | public virtual string ToConsoleString() | ||
377 | { | ||
378 | return string.Format( | ||
379 | "{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName); | ||
380 | } | ||
381 | } | ||
382 | |||
383 | public class PercentageStat : Stat | ||
384 | { | ||
385 | public int Antecedent { get; set; } | ||
386 | public int Consequent { get; set; } | ||
387 | |||
388 | public override double Value | ||
389 | { | ||
390 | get | ||
391 | { | ||
392 | int c = Consequent; | ||
393 | |||
394 | // Avoid any chance of a multi-threaded divide-by-zero | ||
395 | if (c == 0) | ||
396 | return 0; | ||
397 | |||
398 | return (double)Antecedent / c * 100; | ||
399 | } | ||
400 | |||
401 | set | ||
402 | { | ||
403 | throw new Exception("Cannot set value on a PercentageStat"); | ||
404 | } | ||
405 | } | ||
406 | |||
407 | public PercentageStat( | ||
408 | string shortName, | ||
409 | string name, | ||
410 | string description, | ||
411 | string category, | ||
412 | string container, | ||
413 | StatType type, | ||
414 | Action<Stat> pullAction, | ||
415 | StatVerbosity verbosity) | ||
416 | : base(shortName, name, description, "%", category, container, type, pullAction, verbosity) {} | ||
417 | |||
418 | public override string ToConsoleString() | ||
419 | { | ||
420 | return string.Format( | ||
421 | "{0}.{1}.{2} : {3:0.##}{4} ({5}/{6})", | ||
422 | Category, Container, ShortName, Value, UnitName, Antecedent, Consequent); | ||
423 | } | ||
64 | } | 424 | } |
65 | } \ No newline at end of file | 425 | } \ No newline at end of file |
diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs index b709baa..28d6d5c 100644 --- a/OpenSim/Framework/Monitoring/Watchdog.cs +++ b/OpenSim/Framework/Monitoring/Watchdog.cs | |||
@@ -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 | } |