aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Monitoring
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/Monitoring/BaseStatsCollector.cs26
-rw-r--r--OpenSim/Framework/Monitoring/Checks/Check.cs8
-rw-r--r--OpenSim/Framework/Monitoring/ChecksManager.cs45
-rw-r--r--OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs2
-rw-r--r--OpenSim/Framework/Monitoring/JobEngine.cs162
-rw-r--r--OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs10
-rw-r--r--OpenSim/Framework/Monitoring/ServerStatsCollector.cs77
-rwxr-xr-x[-rw-r--r--]OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs56
-rwxr-xr-x[-rw-r--r--]OpenSim/Framework/Monitoring/Stats/CounterStat.cs0
-rwxr-xr-x[-rw-r--r--]OpenSim/Framework/Monitoring/Stats/EventHistogram.cs346
-rw-r--r--OpenSim/Framework/Monitoring/Stats/Stat.cs51
-rw-r--r--OpenSim/Framework/Monitoring/StatsLogger.cs6
-rw-r--r--OpenSim/Framework/Monitoring/StatsManager.cs106
-rw-r--r--OpenSim/Framework/Monitoring/Watchdog.cs61
-rw-r--r--OpenSim/Framework/Monitoring/WorkManager.cs48
15 files changed, 558 insertions, 446 deletions
diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
index 20495f6..e513abd 100644
--- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
@@ -43,7 +43,6 @@ 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
47 sb.AppendFormat( 46 sb.AppendFormat(
48 "Heap allocated to OpenSim : {0} MB\n", 47 "Heap allocated to OpenSim : {0} MB\n",
49 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); 48 Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
@@ -56,13 +55,30 @@ namespace OpenSim.Framework.Monitoring
56 "Average heap allocation rate: {0} MB/s\n", 55 "Average heap allocation rate: {0} MB/s\n",
57 Math.Round((MemoryWatchdog.AverageHeapAllocationRate * 1000) / 1024.0 / 1024, 3)); 56 Math.Round((MemoryWatchdog.AverageHeapAllocationRate * 1000) / 1024.0 / 1024, 3));
58 57
59 sb.AppendFormat( 58 Process myprocess = Process.GetCurrentProcess();
60 "Process memory : {0} MB\n", 59// if (!myprocess.HasExited)
61 Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0)); 60 try
61 {
62 myprocess.Refresh();
63 sb.AppendFormat(
64 "Process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n",
65 Math.Round(myprocess.WorkingSet64 / 1024.0 / 1024.0),
66 Math.Round(myprocess.PagedMemorySize64 / 1024.0 / 1024.0),
67 Math.Round(myprocess.VirtualMemorySize64 / 1024.0 / 1024.0));
68 sb.AppendFormat(
69 "Peak process memory: Physical {0} MB \t Paged {1} MB \t Virtual {2} MB\n",
70 Math.Round(myprocess.PeakWorkingSet64 / 1024.0 / 1024.0),
71 Math.Round(myprocess.PeakPagedMemorySize64 / 1024.0 / 1024.0),
72 Math.Round(myprocess.PeakVirtualMemorySize64 / 1024.0 / 1024.0));
73 }
74 catch
75 { }
76// else
77// sb.Append("Process reported as Exited \n");
62 78
63 return sb.ToString(); 79 return sb.ToString();
64 } 80 }
65 81
66 public virtual string XReport(string uptime, string version) 82 public virtual string XReport(string uptime, string version)
67 { 83 {
68 return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ; 84 return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ;
diff --git a/OpenSim/Framework/Monitoring/Checks/Check.cs b/OpenSim/Framework/Monitoring/Checks/Check.cs
index 594386a..9a1bd45 100644
--- a/OpenSim/Framework/Monitoring/Checks/Check.cs
+++ b/OpenSim/Framework/Monitoring/Checks/Check.cs
@@ -79,7 +79,7 @@ namespace OpenSim.Framework.Monitoring
79 string category, 79 string category,
80 string container, 80 string container,
81 Func<Check, bool> checkFunc, 81 Func<Check, bool> checkFunc,
82 StatVerbosity verbosity) 82 StatVerbosity verbosity)
83 { 83 {
84 if (ChecksManager.SubCommands.Contains(category)) 84 if (ChecksManager.SubCommands.Contains(category))
85 throw new Exception( 85 throw new Exception(
@@ -108,9 +108,9 @@ namespace OpenSim.Framework.Monitoring
108 public virtual string ToConsoleString() 108 public virtual string ToConsoleString()
109 { 109 {
110 return string.Format( 110 return string.Format(
111 "{0}.{1}.{2} - {3}", 111 "{0}.{1}.{2} - {3}",
112 Category, 112 Category,
113 Container, 113 Container,
114 ShortName, 114 ShortName,
115 Description); 115 Description);
116 } 116 }
diff --git a/OpenSim/Framework/Monitoring/ChecksManager.cs b/OpenSim/Framework/Monitoring/ChecksManager.cs
index e4a7f8c..ff3b041 100644
--- a/OpenSim/Framework/Monitoring/ChecksManager.cs
+++ b/OpenSim/Framework/Monitoring/ChecksManager.cs
@@ -132,8 +132,8 @@ namespace OpenSim.Framework.Monitoring
132 /// <returns></returns> 132 /// <returns></returns>
133 public static bool RegisterCheck(Check check) 133 public static bool RegisterCheck(Check check)
134 { 134 {
135 SortedDictionary<string, SortedDictionary<string, Check>> category = null, newCategory; 135 SortedDictionary<string, SortedDictionary<string, Check>> category = null;
136 SortedDictionary<string, Check> container = null, newContainer; 136 SortedDictionary<string, Check> container = null;
137 137
138 lock (RegisteredChecks) 138 lock (RegisteredChecks)
139 { 139 {
@@ -146,19 +146,15 @@ namespace OpenSim.Framework.Monitoring
146 // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. 146 // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed.
147 // This means that we don't need to lock or copy them on iteration, which will be a much more 147 // This means that we don't need to lock or copy them on iteration, which will be a much more
148 // common operation after startup. 148 // common operation after startup.
149 if (container != null) 149 if (container == null)
150 newContainer = new SortedDictionary<string, Check>(container); 150 container = new SortedDictionary<string, Check>();
151 else
152 newContainer = new SortedDictionary<string, Check>();
153 151
154 if (category != null) 152 if (category == null)
155 newCategory = new SortedDictionary<string, SortedDictionary<string, Check>>(category); 153 category = new SortedDictionary<string, SortedDictionary<string, Check>>();
156 else
157 newCategory = new SortedDictionary<string, SortedDictionary<string, Check>>();
158 154
159 newContainer[check.ShortName] = check; 155 container[check.ShortName] = check;
160 newCategory[check.Container] = newContainer; 156 category[check.Container] = container;
161 RegisteredChecks[check.Category] = newCategory; 157 RegisteredChecks[check.Category] = category;
162 } 158 }
163 159
164 return true; 160 return true;
@@ -171,23 +167,24 @@ namespace OpenSim.Framework.Monitoring
171 /// <returns></returns> 167 /// <returns></returns>
172 public static bool DeregisterCheck(Check check) 168 public static bool DeregisterCheck(Check check)
173 { 169 {
174 SortedDictionary<string, SortedDictionary<string, Check>> category = null, newCategory; 170 SortedDictionary<string, SortedDictionary<string, Check>> category = null;
175 SortedDictionary<string, Check> container = null, newContainer; 171 SortedDictionary<string, Check> container = null;
176 172
177 lock (RegisteredChecks) 173 lock (RegisteredChecks)
178 { 174 {
179 if (!TryGetCheckParents(check, out category, out container)) 175 if (!TryGetCheckParents(check, out category, out container))
180 return false; 176 return false;
181 177
182 newContainer = new SortedDictionary<string, Check>(container); 178 if(container != null)
183 newContainer.Remove(check.ShortName); 179 {
184 180 container.Remove(check.ShortName);
185 newCategory = new SortedDictionary<string, SortedDictionary<string, Check>>(category); 181 if(category != null && container.Count == 0)
186 newCategory.Remove(check.Container); 182 {
187 183 category.Remove(check.Container);
188 newCategory[check.Container] = newContainer; 184 if(category.Count == 0)
189 RegisteredChecks[check.Category] = newCategory; 185 RegisteredChecks.Remove(check.Category);
190 186 }
187 }
191 return true; 188 return true;
192 } 189 }
193 } 190 }
diff --git a/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs
index 40df562..e326e8b 100644
--- a/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs
@@ -39,7 +39,7 @@ namespace OpenSim.Framework.Monitoring
39 /// </summary> 39 /// </summary>
40 /// <returns></returns> 40 /// <returns></returns>
41 string Report(); 41 string Report();
42 42
43 /// <summary> 43 /// <summary>
44 /// Report back collected statistical information in json 44 /// Report back collected statistical information in json
45 /// </summary> 45 /// </summary>
diff --git a/OpenSim/Framework/Monitoring/JobEngine.cs b/OpenSim/Framework/Monitoring/JobEngine.cs
index 6db9a67..115871e 100644
--- a/OpenSim/Framework/Monitoring/JobEngine.cs
+++ b/OpenSim/Framework/Monitoring/JobEngine.cs
@@ -40,6 +40,8 @@ namespace OpenSim.Framework.Monitoring
40 40
41 public int LogLevel { get; set; } 41 public int LogLevel { get; set; }
42 42
43 private object JobLock = new object();
44
43 public string Name { get; private set; } 45 public string Name { get; private set; }
44 46
45 public string LoggingName { get; private set; } 47 public string LoggingName { get; private set; }
@@ -47,7 +49,7 @@ namespace OpenSim.Framework.Monitoring
47 /// <summary> 49 /// <summary>
48 /// Is this engine running? 50 /// Is this engine running?
49 /// </summary> 51 /// </summary>
50 public bool IsRunning { get; private set; } 52 public bool IsRunning { get; private set; }
51 53
52 /// <summary> 54 /// <summary>
53 /// The current job that the engine is running. 55 /// The current job that the engine is running.
@@ -55,7 +57,8 @@ namespace OpenSim.Framework.Monitoring
55 /// <remarks> 57 /// <remarks>
56 /// Will be null if no job is currently running. 58 /// Will be null if no job is currently running.
57 /// </remarks> 59 /// </remarks>
58 public Job CurrentJob { get; private set; } 60 private Job m_currentJob;
61 public Job CurrentJob { get { return m_currentJob;} }
59 62
60 /// <summary> 63 /// <summary>
61 /// Number of jobs waiting to be processed. 64 /// Number of jobs waiting to be processed.
@@ -71,96 +74,64 @@ namespace OpenSim.Framework.Monitoring
71 /// Controls whether we need to warn in the log about exceeding the max queue size. 74 /// Controls whether we need to warn in the log about exceeding the max queue size.
72 /// </summary> 75 /// </summary>
73 /// <remarks> 76 /// <remarks>
74 /// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in 77 /// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in
75 /// order to avoid spamming the log with lots of warnings. 78 /// order to avoid spamming the log with lots of warnings.
76 /// </remarks> 79 /// </remarks>
77 private bool m_warnOverMaxQueue = true; 80 private bool m_warnOverMaxQueue = true;
78 81
79 private BlockingCollection<Job> m_jobQueue; 82 private BlockingCollection<Job> m_jobQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
80 83
81 private CancellationTokenSource m_cancelSource; 84 private CancellationTokenSource m_cancelSource;
82 85
83 /// <summary> 86 private int m_timeout = -1;
84 /// Used to signal that we are ready to complete stop.
85 /// </summary>
86 private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false);
87 87
88 public JobEngine(string name, string loggingName) 88 private bool m_threadRunnig = false;
89
90 public JobEngine(string name, string loggingName, int timeout = -1)
89 { 91 {
90 Name = name; 92 Name = name;
91 LoggingName = loggingName; 93 LoggingName = loggingName;
92 94 m_timeout = timeout;
93 RequestProcessTimeoutOnStop = 5000; 95 RequestProcessTimeoutOnStop = 5000;
94 } 96 }
95 97
96 public void Start() 98 public void Start()
97 { 99 {
98 lock (this) 100 lock (JobLock)
99 { 101 {
100 if (IsRunning) 102 if (IsRunning)
101 return; 103 return;
102 104
103 IsRunning = true; 105 IsRunning = true;
104 106
105 m_finishedProcessingAfterStop.Reset();
106
107 m_jobQueue = new BlockingCollection<Job>(new ConcurrentQueue<Job>(), 5000);
108 m_cancelSource = new CancellationTokenSource(); 107 m_cancelSource = new CancellationTokenSource();
109 108 WorkManager.RunInThreadPool(ProcessRequests, null, Name, false);
110 WorkManager.StartThread( 109 m_threadRunnig = true;
111 ProcessRequests,
112 Name,
113 ThreadPriority.Normal,
114 false,
115 true,
116 null,
117 int.MaxValue);
118 } 110 }
119 } 111 }
120 112
121 public void Stop() 113 public void Stop()
122 { 114 {
123 lock (this) 115 lock (JobLock)
124 { 116 {
125 try 117 try
126 { 118 {
127 if (!IsRunning) 119 if (!IsRunning)
128 return; 120 return;
129 121
130 IsRunning = false; 122 m_log.DebugFormat("[JobEngine] Stopping {0}", Name);
131 123
132 int requestsLeft = m_jobQueue.Count; 124 IsRunning = false;
133 125 if(m_threadRunnig)
134 if (requestsLeft <= 0)
135 { 126 {
136 m_cancelSource.Cancel(); 127 m_cancelSource.Cancel();
137 } 128 m_threadRunnig = false;
138 else
139 {
140 m_log.InfoFormat("[{0}]: Waiting to write {1} events after stop.", LoggingName, requestsLeft);
141
142 while (requestsLeft > 0)
143 {
144 if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop))
145 {
146 // After timeout no events have been written
147 if (requestsLeft == m_jobQueue.Count)
148 {
149 m_log.WarnFormat(
150 "[{0}]: No requests processed after {1} ms wait. Discarding remaining {2} requests",
151 LoggingName, RequestProcessTimeoutOnStop, requestsLeft);
152
153 break;
154 }
155 }
156
157 requestsLeft = m_jobQueue.Count;
158 }
159 } 129 }
160 } 130 }
161 finally 131 finally
162 { 132 {
163 m_cancelSource.Dispose(); 133 if(m_cancelSource != null)
134 m_cancelSource.Dispose();
164 } 135 }
165 } 136 }
166 } 137 }
@@ -169,7 +140,7 @@ namespace OpenSim.Framework.Monitoring
169 /// Make a job. 140 /// Make a job.
170 /// </summary> 141 /// </summary>
171 /// <remarks> 142 /// <remarks>
172 /// We provide this method to replace the constructor so that we can later pool job objects if necessary to 143 /// We provide this method to replace the constructor so that we can later pool job objects if necessary to
173 /// reduce memory churn. Normally one would directly call QueueJob() with parameters anyway. 144 /// reduce memory churn. Normally one would directly call QueueJob() with parameters anyway.
174 /// </remarks> 145 /// </remarks>
175 /// <returns></returns> 146 /// <returns></returns>
@@ -219,6 +190,18 @@ namespace OpenSim.Framework.Monitoring
219 /// </param> 190 /// </param>
220 public bool QueueJob(Job job) 191 public bool QueueJob(Job job)
221 { 192 {
193 lock(JobLock)
194 {
195 if(!IsRunning)
196 return false;
197
198 if(!m_threadRunnig)
199 {
200 WorkManager.RunInThreadPool(ProcessRequests, null, Name, false);
201 m_threadRunnig = true;
202 }
203 }
204
222 if (m_jobQueue.Count < m_jobQueue.BoundedCapacity) 205 if (m_jobQueue.Count < m_jobQueue.BoundedCapacity)
223 { 206 {
224 m_jobQueue.Add(job); 207 m_jobQueue.Add(job);
@@ -238,56 +221,53 @@ namespace OpenSim.Framework.Monitoring
238 221
239 m_warnOverMaxQueue = false; 222 m_warnOverMaxQueue = false;
240 } 223 }
241
242 return false; 224 return false;
243 } 225 }
244 } 226 }
245 227
246 private void ProcessRequests() 228 private void ProcessRequests(Object o)
247 { 229 {
248 try 230 while(IsRunning)
249 { 231 {
250 while (IsRunning || m_jobQueue.Count > 0) 232 try
251 { 233 {
252 try 234 if(!m_jobQueue.TryTake(out m_currentJob, m_timeout, m_cancelSource.Token))
253 { 235 {
254 CurrentJob = m_jobQueue.Take(m_cancelSource.Token); 236 lock(JobLock)
255 } 237 m_threadRunnig = false;
256 catch (ObjectDisposedException e) 238 break;
257 {
258 // If we see this whilst not running then it may be due to a race where this thread checks
259 // IsRunning after the stopping thread sets it to false and disposes of the cancellation source.
260 if (IsRunning)
261 throw e;
262 else
263 break;
264 } 239 }
240 }
241 catch(ObjectDisposedException e)
242 {
243 m_log.DebugFormat("[JobEngine] {0} stopping ignoring {1} jobs in queue",
244 Name,m_jobQueue.Count);
245 break;
246 }
247 catch(OperationCanceledException)
248 {
249 break;
250 }
265 251
266 if (LogLevel >= 1) 252 if(LogLevel >= 1)
267 m_log.DebugFormat("[{0}]: Processing job {1}", LoggingName, CurrentJob.Name); 253 m_log.DebugFormat("[{0}]: Processing job {1}",LoggingName,m_currentJob.Name);
268 254
269 try 255 try
270 { 256 {
271 CurrentJob.Action(); 257 m_currentJob.Action();
272 } 258 }
273 catch (Exception e) 259 catch(Exception e)
274 { 260 {
275 m_log.Error( 261 m_log.Error(
276 string.Format( 262 string.Format(
277 "[{0}]: Job {1} failed, continuing. Exception ", LoggingName, CurrentJob.Name), e); 263 "[{0}]: Job {1} failed, continuing. Exception ",LoggingName,m_currentJob.Name),e);
278 } 264 }
279 265
280 if (LogLevel >= 1) 266 if(LogLevel >= 1)
281 m_log.DebugFormat("[{0}]: Processed job {1}", LoggingName, CurrentJob.Name); 267 m_log.DebugFormat("[{0}]: Processed job {1}",LoggingName,m_currentJob.Name);
282 268
283 CurrentJob = null; 269 m_currentJob = null;
284 }
285 }
286 catch (OperationCanceledException)
287 {
288 } 270 }
289
290 m_finishedProcessingAfterStop.Set();
291 } 271 }
292 272
293 public class Job 273 public class Job
@@ -320,7 +300,7 @@ namespace OpenSim.Framework.Monitoring
320 CommonId = commonId; 300 CommonId = commonId;
321 Action = action; 301 Action = action;
322 } 302 }
323 303
324 /// <summary> 304 /// <summary>
325 /// Make a job. It needs to be separately queued. 305 /// Make a job. It needs to be separately queued.
326 /// </summary> 306 /// </summary>
@@ -338,4 +318,4 @@ namespace OpenSim.Framework.Monitoring
338 } 318 }
339 } 319 }
340 } 320 }
341} \ No newline at end of file 321}
diff --git a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs
index a617b93..2ff2014 100644
--- a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs
@@ -2,7 +2,7 @@
2using System.Runtime.CompilerServices; 2using System.Runtime.CompilerServices;
3using System.Runtime.InteropServices; 3using System.Runtime.InteropServices;
4 4
5// General Information about an assembly is controlled through the following 5// General Information about an assembly is controlled through the following
6// set of attributes. Change these attribute values to modify the information 6// set of attributes. Change these attribute values to modify the information
7// associated with an assembly. 7// associated with an assembly.
8[assembly: AssemblyTitle("OpenSim.Framework.Monitoring")] 8[assembly: AssemblyTitle("OpenSim.Framework.Monitoring")]
@@ -14,8 +14,8 @@ using System.Runtime.InteropServices;
14[assembly: AssemblyTrademark("")] 14[assembly: AssemblyTrademark("")]
15[assembly: AssemblyCulture("")] 15[assembly: AssemblyCulture("")]
16 16
17// Setting ComVisible to false makes the types in this assembly not visible 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 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. 19// COM, set the ComVisible attribute to true on that type.
20[assembly: ComVisible(false)] 20[assembly: ComVisible(false)]
21 21
@@ -25,9 +25,9 @@ using System.Runtime.InteropServices;
25// Version information for an assembly consists of the following four values: 25// Version information for an assembly consists of the following four values:
26// 26//
27// Major Version 27// Major Version
28// Minor Version 28// Minor Version
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.8.3.*")] 32[assembly: AssemblyVersion(OpenSim.VersionInfo.AssemblyVersionNumber)]
33 33
diff --git a/OpenSim/Framework/Monitoring/ServerStatsCollector.cs b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
index 77315bb..a26a6e0 100644
--- a/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs
@@ -88,7 +88,7 @@ namespace OpenSim.Framework.Monitoring
88 IConfig cfg = source.Configs["Monitoring"]; 88 IConfig cfg = source.Configs["Monitoring"];
89 89
90 if (cfg != null) 90 if (cfg != null)
91 Enabled = cfg.GetBoolean("ServerStatsEnabled", true); 91 Enabled = cfg.GetBoolean("ServerStatsEnabled", false);
92 92
93 if (Enabled) 93 if (Enabled)
94 { 94 {
@@ -98,12 +98,18 @@ namespace OpenSim.Framework.Monitoring
98 98
99 public void Start() 99 public void Start()
100 { 100 {
101 if(!Enabled)
102 return;
103
101 if (RegisteredStats.Count == 0) 104 if (RegisteredStats.Count == 0)
102 RegisterServerStats(); 105 RegisterServerStats();
103 } 106 }
104 107
105 public void Close() 108 public void Close()
106 { 109 {
110 if(!Enabled)
111 return;
112
107 if (RegisteredStats.Count > 0) 113 if (RegisteredStats.Count > 0)
108 { 114 {
109 foreach (Stat stat in RegisteredStats.Values) 115 foreach (Stat stat in RegisteredStats.Values)
@@ -167,18 +173,18 @@ namespace OpenSim.Framework.Monitoring
167 } 173 }
168 174
169 MakeStat("BuiltinThreadpoolWorkerThreadsAvailable", null, "threads", ContainerThreadpool, 175 MakeStat("BuiltinThreadpoolWorkerThreadsAvailable", null, "threads", ContainerThreadpool,
170 s => 176 s =>
171 { 177 {
172 int workerThreads, iocpThreads; 178 int workerThreads, iocpThreads;
173 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); 179 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
174 s.Value = workerThreads; 180 s.Value = workerThreads;
175 }); 181 });
176 182
177 MakeStat("BuiltinThreadpoolIOCPThreadsAvailable", null, "threads", ContainerThreadpool, 183 MakeStat("BuiltinThreadpoolIOCPThreadsAvailable", null, "threads", ContainerThreadpool,
178 s => 184 s =>
179 { 185 {
180 int workerThreads, iocpThreads; 186 int workerThreads, iocpThreads;
181 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); 187 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
182 s.Value = iocpThreads; 188 s.Value = iocpThreads;
183 }); 189 });
184 190
@@ -193,10 +199,10 @@ namespace OpenSim.Framework.Monitoring
193 } 199 }
194 200
195 MakeStat( 201 MakeStat(
196 "HTTPRequestsMade", 202 "HTTPRequestsMade",
197 "Number of outbound HTTP requests made", 203 "Number of outbound HTTP requests made",
198 "requests", 204 "requests",
199 ContainerNetwork, 205 ContainerNetwork,
200 s => s.Value = WebUtil.RequestNumber, 206 s => s.Value = WebUtil.RequestNumber,
201 MeasuresOfInterest.AverageChangeOverTime); 207 MeasuresOfInterest.AverageChangeOverTime);
202 208
@@ -249,9 +255,52 @@ namespace OpenSim.Framework.Monitoring
249 (s) => { s.Value = Math.Round(MemoryWatchdog.LastHeapAllocationRate * 1000d / 1024d / 1024d, 3); }); 255 (s) => { s.Value = Math.Round(MemoryWatchdog.LastHeapAllocationRate * 1000d / 1024d / 1024d, 3); });
250 MakeStat("AverageHeapAllocationRate", null, "MB/sec", ContainerMemory, 256 MakeStat("AverageHeapAllocationRate", null, "MB/sec", ContainerMemory,
251 (s) => { s.Value = Math.Round(MemoryWatchdog.AverageHeapAllocationRate * 1000d / 1024d / 1024d, 3); }); 257 (s) => { s.Value = Math.Round(MemoryWatchdog.AverageHeapAllocationRate * 1000d / 1024d / 1024d, 3); });
258
259 MakeStat("ProcessResident", null, "MB", ContainerProcess,
260 (s) =>
261 {
262 Process myprocess = Process.GetCurrentProcess();
263 myprocess.Refresh();
264 s.Value = Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0);
265 });
266 MakeStat("ProcessPaged", null, "MB", ContainerProcess,
267 (s) =>
268 {
269 Process myprocess = Process.GetCurrentProcess();
270 myprocess.Refresh();
271 s.Value = Math.Round(Process.GetCurrentProcess().PagedMemorySize64 / 1024.0 / 1024.0);
272 });
273 MakeStat("ProcessVirtual", null, "MB", ContainerProcess,
274 (s) =>
275 {
276 Process myprocess = Process.GetCurrentProcess();
277 myprocess.Refresh();
278 s.Value = Math.Round(Process.GetCurrentProcess().VirtualMemorySize64 / 1024.0 / 1024.0);
279 });
280 MakeStat("PeakProcessResident", null, "MB", ContainerProcess,
281 (s) =>
282 {
283 Process myprocess = Process.GetCurrentProcess();
284 myprocess.Refresh();
285 s.Value = Math.Round(Process.GetCurrentProcess().PeakWorkingSet64 / 1024.0 / 1024.0);
286 });
287 MakeStat("PeakProcessPaged", null, "MB", ContainerProcess,
288 (s) =>
289 {
290 Process myprocess = Process.GetCurrentProcess();
291 myprocess.Refresh();
292 s.Value = Math.Round(Process.GetCurrentProcess().PeakPagedMemorySize64 / 1024.0 / 1024.0);
293 });
294 MakeStat("PeakProcessVirtual", null, "MB", ContainerProcess,
295 (s) =>
296 {
297 Process myprocess = Process.GetCurrentProcess();
298 myprocess.Refresh();
299 s.Value = Math.Round(Process.GetCurrentProcess().PeakVirtualMemorySize64 / 1024.0 / 1024.0);
300 });
252 } 301 }
253 302
254 // Notes on performance counters: 303 // Notes on performance counters:
255 // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx 304 // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx
256 // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c 305 // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c
257 // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters 306 // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters
diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
index e4df7ee..88a0297 100644..100755
--- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
@@ -34,6 +34,7 @@ using OpenMetaverse;
34using OpenMetaverse.StructuredData; 34using OpenMetaverse.StructuredData;
35using OpenSim.Framework.Monitoring.Interfaces; 35using OpenSim.Framework.Monitoring.Interfaces;
36 36
37
37namespace OpenSim.Framework.Monitoring 38namespace OpenSim.Framework.Monitoring
38{ 39{
39 /// <summary> 40 /// <summary>
@@ -71,6 +72,11 @@ namespace OpenSim.Framework.Monitoring
71 private volatile float pendingDownloads; 72 private volatile float pendingDownloads;
72 private volatile float pendingUploads; 73 private volatile float pendingUploads;
73 private volatile float activeScripts; 74 private volatile float activeScripts;
75 private volatile float spareTime;
76 private volatile float sleepTime;
77 private volatile float physicsStep;
78
79
74 private volatile float scriptLinesPerSecond; 80 private volatile float scriptLinesPerSecond;
75 private volatile float m_frameDilation; 81 private volatile float m_frameDilation;
76 private volatile float m_usersLoggingIn; 82 private volatile float m_usersLoggingIn;
@@ -84,17 +90,17 @@ namespace OpenSim.Framework.Monitoring
84// /// haven't yet been implemented... 90// /// haven't yet been implemented...
85// /// </summary> 91// /// </summary>
86// public long AssetsInCache { get { return assetsInCache; } } 92// public long AssetsInCache { get { return assetsInCache; } }
87// 93//
88// /// <value> 94// /// <value>
89// /// Currently unused 95// /// Currently unused
90// /// </value> 96// /// </value>
91// public long TexturesInCache { get { return texturesInCache; } } 97// public long TexturesInCache { get { return texturesInCache; } }
92// 98//
93// /// <value> 99// /// <value>
94// /// Currently misleading since we can't currently subtract removed asset memory usage without a performance hit 100// /// Currently misleading since we can't currently subtract removed asset memory usage without a performance hit
95// /// </value> 101// /// </value>
96// public long AssetCacheMemoryUsage { get { return assetCacheMemoryUsage; } } 102// public long AssetCacheMemoryUsage { get { return assetCacheMemoryUsage; } }
97// 103//
98// /// <value> 104// /// <value>
99// /// Currently unused 105// /// Currently unused
100// /// </value> 106// /// </value>
@@ -121,7 +127,7 @@ namespace OpenSim.Framework.Monitoring
121 public float PendingUploads { get { return pendingUploads; } } 127 public float PendingUploads { get { return pendingUploads; } }
122 public float ActiveScripts { get { return activeScripts; } } 128 public float ActiveScripts { get { return activeScripts; } }
123 public float ScriptLinesPerSecond { get { return scriptLinesPerSecond; } } 129 public float ScriptLinesPerSecond { get { return scriptLinesPerSecond; } }
124 130
125// /// <summary> 131// /// <summary>
126// /// This is the time it took for the last asset request made in response to a cache miss. 132// /// This is the time it took for the last asset request made in response to a cache miss.
127// /// </summary> 133// /// </summary>
@@ -171,7 +177,7 @@ namespace OpenSim.Framework.Monitoring
171// assetsInCache++; 177// assetsInCache++;
172// //assetCacheMemoryUsage += asset.Data.Length; 178// //assetCacheMemoryUsage += asset.Data.Length;
173// } 179// }
174// 180//
175// public void RemoveAsset(UUID uuid) 181// public void RemoveAsset(UUID uuid)
176// { 182// {
177// assetsInCache--; 183// assetsInCache--;
@@ -198,7 +204,7 @@ namespace OpenSim.Framework.Monitoring
198// texturesInCache = 0; 204// texturesInCache = 0;
199// textureCacheMemoryUsage = 0; 205// textureCacheMemoryUsage = 0;
200// } 206// }
201// 207//
202// public void AddAssetRequestTimeAfterCacheMiss(TimeSpan ts) 208// public void AddAssetRequestTimeAfterCacheMiss(TimeSpan ts)
203// { 209// {
204// assetRequestTimeAfterCacheMiss = ts; 210// assetRequestTimeAfterCacheMiss = ts;
@@ -253,7 +259,7 @@ namespace OpenSim.Framework.Monitoring
253 /// <param name="pack"></param> 259 /// <param name="pack"></param>
254 public void ReceiveClassicSimStatsPacket(SimStats stats) 260 public void ReceiveClassicSimStatsPacket(SimStats stats)
255 { 261 {
256 // FIXME: SimStats shouldn't allow an arbitrary stat packing order (which is inherited from the original 262 // FIXME: SimStats shouldn't allow an arbitrary stat packing order (which is inherited from the original
257 // SimStatsPacket that was being used). 263 // SimStatsPacket that was being used).
258 264
259 // For an unknown reason the original designers decided not to 265 // For an unknown reason the original designers decided not to
@@ -270,8 +276,8 @@ namespace OpenSim.Framework.Monitoring
270 totalFrameTime = stats.StatsBlock[8].StatValue; 276 totalFrameTime = stats.StatsBlock[8].StatValue;
271 netFrameTime = stats.StatsBlock[9].StatValue; 277 netFrameTime = stats.StatsBlock[9].StatValue;
272 physicsFrameTime = stats.StatsBlock[10].StatValue; 278 physicsFrameTime = stats.StatsBlock[10].StatValue;
273 otherFrameTime = stats.StatsBlock[11].StatValue; 279 imageFrameTime = stats.StatsBlock[11].StatValue;
274 imageFrameTime = stats.StatsBlock[12].StatValue; 280 otherFrameTime = stats.StatsBlock[12].StatValue;
275 inPacketsPerSecond = stats.StatsBlock[13].StatValue; 281 inPacketsPerSecond = stats.StatsBlock[13].StatValue;
276 outPacketsPerSecond = stats.StatsBlock[14].StatValue; 282 outPacketsPerSecond = stats.StatsBlock[14].StatValue;
277 unackedBytes = stats.StatsBlock[15].StatValue; 283 unackedBytes = stats.StatsBlock[15].StatValue;
@@ -279,12 +285,16 @@ namespace OpenSim.Framework.Monitoring
279 pendingDownloads = stats.StatsBlock[17].StatValue; 285 pendingDownloads = stats.StatsBlock[17].StatValue;
280 pendingUploads = stats.StatsBlock[18].StatValue; 286 pendingUploads = stats.StatsBlock[18].StatValue;
281 activeScripts = stats.StatsBlock[19].StatValue; 287 activeScripts = stats.StatsBlock[19].StatValue;
282 scriptLinesPerSecond = stats.StatsBlock[20].StatValue; 288 sleepTime = stats.StatsBlock[20].StatValue;
283 m_frameDilation = stats.StatsBlock[22].StatValue; 289 spareTime = stats.StatsBlock[21].StatValue;
284 m_usersLoggingIn = stats.StatsBlock[23].StatValue; 290 physicsStep = stats.StatsBlock[22].StatValue;
285 m_totalGeoPrims = stats.StatsBlock[24].StatValue; 291
286 m_totalMeshes = stats.StatsBlock[25].StatValue; 292 scriptLinesPerSecond = stats.ExtraStatsBlock[0].StatValue;
287 m_inUseThreads = stats.StatsBlock[26].StatValue; 293 m_frameDilation = stats.ExtraStatsBlock[1].StatValue;
294 m_usersLoggingIn = stats.ExtraStatsBlock[2].StatValue;
295 m_totalGeoPrims = stats.ExtraStatsBlock[3].StatValue;
296 m_totalMeshes = stats.ExtraStatsBlock[4].StatValue;
297 m_inUseThreads = stats.ExtraStatsBlock[5].StatValue;
288 } 298 }
289 299
290 /// <summary> 300 /// <summary>
@@ -296,7 +306,7 @@ namespace OpenSim.Framework.Monitoring
296 StringBuilder sb = new StringBuilder(Environment.NewLine); 306 StringBuilder sb = new StringBuilder(Environment.NewLine);
297// sb.Append("ASSET STATISTICS"); 307// sb.Append("ASSET STATISTICS");
298// sb.Append(Environment.NewLine); 308// sb.Append(Environment.NewLine);
299 309
300 /* 310 /*
301 sb.Append( 311 sb.Append(
302 string.Format( 312 string.Format(
@@ -332,7 +342,7 @@ Asset service request failures: {3}" + Environment.NewLine,
332 List<Stat> stats = StatsManager.GetStatsFromEachContainer("clientstack", "ClientLogoutsDueToNoReceives"); 342 List<Stat> stats = StatsManager.GetStatsFromEachContainer("clientstack", "ClientLogoutsDueToNoReceives");
333 343
334 sb.AppendFormat( 344 sb.AppendFormat(
335 "Client logouts due to no data receive timeout: {0}\n\n", 345 "Client logouts due to no data receive timeout: {0}\n\n",
336 stats != null ? stats.Sum(s => s.Value).ToString() : "unknown"); 346 stats != null ? stats.Sum(s => s.Value).ToString() : "unknown");
337 347
338// sb.Append(Environment.NewLine); 348// sb.Append(Environment.NewLine);
@@ -433,10 +443,10 @@ Asset service request failures: {3}" + Environment.NewLine,
433 foreach (ProcessThread currentThread in 443 foreach (ProcessThread currentThread in
434 Process.GetCurrentProcess().Threads) 444 Process.GetCurrentProcess().Threads)
435 { 445 {
436 // A known issue with the current process .Threads property is 446 // A known issue with the current process .Threads property is
437 // that it can return null threads, thus don't count those as 447 // that it can return null threads, thus don't count those as
438 // running threads and prevent the program function from failing 448 // running threads and prevent the program function from failing
439 if (currentThread != null && 449 if (currentThread != null &&
440 currentThread.ThreadState == ThreadState.Running) 450 currentThread.ThreadState == ThreadState.Running)
441 { 451 {
442 numberThreadsRunning++; 452 numberThreadsRunning++;
@@ -495,7 +505,7 @@ Asset service request failures: {3}" + Environment.NewLine,
495 "{0:0.##}", numberThreadsRunning)); 505 "{0:0.##}", numberThreadsRunning));
496 args["ProcMem"] = OSD.FromString(String.Format("{0:#,###,###.##}", 506 args["ProcMem"] = OSD.FromString(String.Format("{0:#,###,###.##}",
497 memUsage)); 507 memUsage));
498 508
499 return args; 509 return args;
500 } 510 }
501 } 511 }
@@ -521,12 +531,12 @@ Asset service request failures: {3}" + Environment.NewLine,
521 { 531 {
522 return m_statsProvider.GetStats(); 532 return m_statsProvider.GetStats();
523 } 533 }
524 534
525 public string XReport(string uptime, string version) 535 public string XReport(string uptime, string version)
526 { 536 {
527 return ""; 537 return "";
528 } 538 }
529 539
530 public OSDMap OReport(string uptime, string version) 540 public OSDMap OReport(string uptime, string version)
531 { 541 {
532 OSDMap ret = new OSDMap(); 542 OSDMap ret = new OSDMap();
diff --git a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs
index 318cf1c..318cf1c 100644..100755
--- a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs
+++ b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs
diff --git a/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs
index f51f322..bc56372 100644..100755
--- a/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs
+++ b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs
@@ -1,173 +1,173 @@
1/* 1/*
2 * Copyright (c) Contributors, http://opensimulator.org/ 2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders. 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met: 6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright 7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright 9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the 12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products 13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission. 14 * derived from this software without specific prior written permission.
15 * 15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY 19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Linq; 30using System.Linq;
31using System.Text; 31using System.Text;
32 32
33using OpenMetaverse.StructuredData; 33using OpenMetaverse.StructuredData;
34 34
35namespace OpenSim.Framework.Monitoring 35namespace OpenSim.Framework.Monitoring
36{ 36{
37// Create a time histogram of events. The histogram is built in a wrap-around 37// Create a time histogram of events. The histogram is built in a wrap-around
38// array of equally distributed buckets. 38// array of equally distributed buckets.
39// For instance, a minute long histogram of second sized buckets would be: 39// For instance, a minute long histogram of second sized buckets would be:
40// new EventHistogram(60, 1000) 40// new EventHistogram(60, 1000)
41public class EventHistogram 41public class EventHistogram
42{ 42{
43 private int m_timeBase; 43 private int m_timeBase;
44 private int m_numBuckets; 44 private int m_numBuckets;
45 private int m_bucketMilliseconds; 45 private int m_bucketMilliseconds;
46 private int m_lastBucket; 46 private int m_lastBucket;
47 private int m_totalHistogramMilliseconds; 47 private int m_totalHistogramMilliseconds;
48 private long[] m_histogram; 48 private long[] m_histogram;
49 private object histoLock = new object(); 49 private object histoLock = new object();
50 50
51 public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) 51 public EventHistogram(int numberOfBuckets, int millisecondsPerBucket)
52 { 52 {
53 m_numBuckets = numberOfBuckets; 53 m_numBuckets = numberOfBuckets;
54 m_bucketMilliseconds = millisecondsPerBucket; 54 m_bucketMilliseconds = millisecondsPerBucket;
55 m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; 55 m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds;
56 56
57 m_histogram = new long[m_numBuckets]; 57 m_histogram = new long[m_numBuckets];
58 Zero(); 58 Zero();
59 m_lastBucket = 0; 59 m_lastBucket = 0;
60 m_timeBase = Util.EnvironmentTickCount(); 60 m_timeBase = Util.EnvironmentTickCount();
61 } 61 }
62 62
63 public void Event() 63 public void Event()
64 { 64 {
65 this.Event(1); 65 this.Event(1);
66 } 66 }
67 67
68 // Record an event at time 'now' in the histogram. 68 // Record an event at time 'now' in the histogram.
69 public void Event(int cnt) 69 public void Event(int cnt)
70 { 70 {
71 lock (histoLock) 71 lock (histoLock)
72 { 72 {
73 // The time as displaced from the base of the histogram 73 // The time as displaced from the base of the histogram
74 int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); 74 int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase);
75 75
76 // If more than the total time of the histogram, we just start over 76 // If more than the total time of the histogram, we just start over
77 if (bucketTime > m_totalHistogramMilliseconds) 77 if (bucketTime > m_totalHistogramMilliseconds)
78 { 78 {
79 Zero(); 79 Zero();
80 m_lastBucket = 0; 80 m_lastBucket = 0;
81 m_timeBase = Util.EnvironmentTickCount(); 81 m_timeBase = Util.EnvironmentTickCount();
82 } 82 }
83 else 83 else
84 { 84 {
85 // To which bucket should we add this event? 85 // To which bucket should we add this event?
86 int bucket = bucketTime / m_bucketMilliseconds; 86 int bucket = bucketTime / m_bucketMilliseconds;
87 87
88 // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. 88 // Advance m_lastBucket to the new bucket. Zero any buckets skipped over.
89 while (bucket != m_lastBucket) 89 while (bucket != m_lastBucket)
90 { 90 {
91 // Zero from just after the last bucket to the new bucket or the end 91 // Zero from just after the last bucket to the new bucket or the end
92 for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) 92 for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++)
93 { 93 {
94 m_histogram[jj] = 0; 94 m_histogram[jj] = 0;
95 } 95 }
96 m_lastBucket = bucket; 96 m_lastBucket = bucket;
97 // If the new bucket is off the end, wrap around to the beginning 97 // If the new bucket is off the end, wrap around to the beginning
98 if (bucket > m_numBuckets) 98 if (bucket > m_numBuckets)
99 { 99 {
100 bucket -= m_numBuckets; 100 bucket -= m_numBuckets;
101 m_lastBucket = 0; 101 m_lastBucket = 0;
102 m_histogram[m_lastBucket] = 0; 102 m_histogram[m_lastBucket] = 0;
103 m_timeBase += m_totalHistogramMilliseconds; 103 m_timeBase += m_totalHistogramMilliseconds;
104 } 104 }
105 } 105 }
106 } 106 }
107 m_histogram[m_lastBucket] += cnt; 107 m_histogram[m_lastBucket] += cnt;
108 } 108 }
109 } 109 }
110 110
111 // Get a copy of the current histogram 111 // Get a copy of the current histogram
112 public long[] GetHistogram() 112 public long[] GetHistogram()
113 { 113 {
114 long[] ret = new long[m_numBuckets]; 114 long[] ret = new long[m_numBuckets];
115 lock (histoLock) 115 lock (histoLock)
116 { 116 {
117 int indx = m_lastBucket + 1; 117 int indx = m_lastBucket + 1;
118 for (int ii = 0; ii < m_numBuckets; ii++, indx++) 118 for (int ii = 0; ii < m_numBuckets; ii++, indx++)
119 { 119 {
120 if (indx >= m_numBuckets) 120 if (indx >= m_numBuckets)
121 indx = 0; 121 indx = 0;
122 ret[ii] = m_histogram[indx]; 122 ret[ii] = m_histogram[indx];
123 } 123 }
124 } 124 }
125 return ret; 125 return ret;
126 } 126 }
127 127
128 public OSDMap GetHistogramAsOSDMap() 128 public OSDMap GetHistogramAsOSDMap()
129 { 129 {
130 OSDMap ret = new OSDMap(); 130 OSDMap ret = new OSDMap();
131 131
132 ret.Add("Buckets", OSD.FromInteger(m_numBuckets)); 132 ret.Add("Buckets", OSD.FromInteger(m_numBuckets));
133 ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds)); 133 ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds));
134 ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds)); 134 ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds));
135 135
136 // Compute a number for the first bucket in the histogram. 136 // Compute a number for the first bucket in the histogram.
137 // This will allow readers to know how this histogram relates to any previously read histogram. 137 // This will allow readers to know how this histogram relates to any previously read histogram.
138 int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1; 138 int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1;
139 ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum)); 139 ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum));
140 140
141 ret.Add("Values", GetHistogramAsOSDArray()); 141 ret.Add("Values", GetHistogramAsOSDArray());
142 142
143 return ret; 143 return ret;
144 } 144 }
145 // Get a copy of the current histogram 145 // Get a copy of the current histogram
146 public OSDArray GetHistogramAsOSDArray() 146 public OSDArray GetHistogramAsOSDArray()
147 { 147 {
148 OSDArray ret = new OSDArray(m_numBuckets); 148 OSDArray ret = new OSDArray(m_numBuckets);
149 lock (histoLock) 149 lock (histoLock)
150 { 150 {
151 int indx = m_lastBucket + 1; 151 int indx = m_lastBucket + 1;
152 for (int ii = 0; ii < m_numBuckets; ii++, indx++) 152 for (int ii = 0; ii < m_numBuckets; ii++, indx++)
153 { 153 {
154 if (indx >= m_numBuckets) 154 if (indx >= m_numBuckets)
155 indx = 0; 155 indx = 0;
156 ret[ii] = OSD.FromLong(m_histogram[indx]); 156 ret[ii] = OSD.FromLong(m_histogram[indx]);
157 } 157 }
158 } 158 }
159 return ret; 159 return ret;
160 } 160 }
161 161
162 // Zero out the histogram 162 // Zero out the histogram
163 public void Zero() 163 public void Zero()
164 { 164 {
165 lock (histoLock) 165 lock (histoLock)
166 { 166 {
167 for (int ii = 0; ii < m_numBuckets; ii++) 167 for (int ii = 0; ii < m_numBuckets; ii++)
168 m_histogram[ii] = 0; 168 m_histogram[ii] = 0;
169 } 169 }
170 } 170 }
171} 171}
172 172
173} 173}
diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs
index a7cb2a6..2402acd 100644
--- a/OpenSim/Framework/Monitoring/Stats/Stat.cs
+++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs
@@ -121,17 +121,17 @@ namespace OpenSim.Framework.Monitoring
121 string container, 121 string container,
122 StatType type, 122 StatType type,
123 Action<Stat> pullAction, 123 Action<Stat> pullAction,
124 StatVerbosity verbosity) 124 StatVerbosity verbosity)
125 : this( 125 : this(
126 shortName, 126 shortName,
127 name, 127 name,
128 description, 128 description,
129 unitName, 129 unitName,
130 category, 130 category,
131 container, 131 container,
132 type, 132 type,
133 MeasuresOfInterest.None, 133 MeasuresOfInterest.None,
134 pullAction, 134 pullAction,
135 verbosity) 135 verbosity)
136 { 136 {
137 } 137 }
@@ -227,11 +227,11 @@ namespace OpenSim.Framework.Monitoring
227 { 227 {
228 StringBuilder sb = new StringBuilder(); 228 StringBuilder sb = new StringBuilder();
229 sb.AppendFormat( 229 sb.AppendFormat(
230 "{0}.{1}.{2} : {3}{4}", 230 "{0}.{1}.{2} : {3}{4}",
231 Category, 231 Category,
232 Container, 232 Container,
233 ShortName, 233 ShortName,
234 Value, 234 Value,
235 string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName)); 235 string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName));
236 236
237 AppendMeasuresOfInterest(sb); 237 AppendMeasuresOfInterest(sb);
@@ -239,6 +239,17 @@ namespace OpenSim.Framework.Monitoring
239 return sb.ToString(); 239 return sb.ToString();
240 } 240 }
241 241
242 public virtual OSDMap ToBriefOSDMap()
243 {
244 OSDMap ret = new OSDMap();
245
246 ret.Add("Value", OSD.FromReal(Value));
247
248 double lastChangeOverTime, averageChangeOverTime;
249
250 return ret;
251 }
252
242 public virtual OSDMap ToOSDMap() 253 public virtual OSDMap ToOSDMap()
243 { 254 {
244 OSDMap ret = new OSDMap(); 255 OSDMap ret = new OSDMap();
@@ -279,7 +290,7 @@ namespace OpenSim.Framework.Monitoring
279 lock (m_samples) 290 lock (m_samples)
280 { 291 {
281 // m_log.DebugFormat( 292 // m_log.DebugFormat(
282 // "[STAT]: Samples for {0} are {1}", 293 // "[STAT]: Samples for {0} are {1}",
283 // Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray())); 294 // Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray()));
284 295
285 foreach (double s in m_samples) 296 foreach (double s in m_samples)
@@ -315,12 +326,12 @@ namespace OpenSim.Framework.Monitoring
315 if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) 326 if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime))
316 { 327 {
317 sb.AppendFormat( 328 sb.AppendFormat(
318 ", {0:0.##}{1}/s, {2:0.##}{3}/s", 329 ", {0:0.##}{1}/s, {2:0.##}{3}/s",
319 lastChangeOverTime, 330 lastChangeOverTime,
320 string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName), 331 string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName),
321 averageChangeOverTime, 332 averageChangeOverTime,
322 string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName)); 333 string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName));
323 } 334 }
324 } 335 }
325 } 336 }
326} \ No newline at end of file 337}
diff --git a/OpenSim/Framework/Monitoring/StatsLogger.cs b/OpenSim/Framework/Monitoring/StatsLogger.cs
index 15a37aa..b719af9 100644
--- a/OpenSim/Framework/Monitoring/StatsLogger.cs
+++ b/OpenSim/Framework/Monitoring/StatsLogger.cs
@@ -99,13 +99,13 @@ namespace OpenSim.Framework.Monitoring
99 } 99 }
100 100
101 string path = cmd[2]; 101 string path = cmd[2];
102 102
103 using (StreamWriter sw = new StreamWriter(path, true)) 103 using (StreamWriter sw = new StreamWriter(path, true))
104 { 104 {
105 foreach (string line in GetReport()) 105 foreach (string line in GetReport())
106 sw.WriteLine(line); 106 sw.WriteLine(line);
107 } 107 }
108 108
109 MainConsole.Instance.OutputFormat("Stats saved to file {0}", path); 109 MainConsole.Instance.OutputFormat("Stats saved to file {0}", path);
110 } 110 }
111 111
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
index 3136ee8..a6b341f 100644
--- a/OpenSim/Framework/Monitoring/StatsManager.cs
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -47,6 +47,8 @@ namespace OpenSim.Framework.Monitoring
47 // Subcommand used to list other stats. 47 // Subcommand used to list other stats.
48 public const string ListSubCommand = "list"; 48 public const string ListSubCommand = "list";
49 49
50 public static string StatsPassword { get; set; }
51
50 // All subcommands 52 // All subcommands
51 public static HashSet<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand }; 53 public static HashSet<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand };
52 54
@@ -80,8 +82,7 @@ namespace OpenSim.Framework.Monitoring
80 + "'all' will show all statistics.\n" 82 + "'all' will show all statistics.\n"
81 + "A <category> name will show statistics from that category.\n" 83 + "A <category> name will show statistics from that category.\n"
82 + "A <category>.<container> name will show statistics from that category in that container.\n" 84 + "A <category>.<container> name will show statistics from that category in that container.\n"
83 + "More than one name can be given separated by spaces.\n" 85 + "More than one name can be given separated by spaces.\n",
84 + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
85 HandleShowStatsCommand); 86 HandleShowStatsCommand);
86 87
87 console.Commands.AddCommand( 88 console.Commands.AddCommand(
@@ -91,7 +92,6 @@ namespace OpenSim.Framework.Monitoring
91 "show stats [list|all|(<category>[.<container>])+", 92 "show stats [list|all|(<category>[.<container>])+",
92 "Alias for 'stats show' command", 93 "Alias for 'stats show' command",
93 HandleShowStatsCommand); 94 HandleShowStatsCommand);
94
95 StatsLogger.RegisterConsoleCommands(console); 95 StatsLogger.RegisterConsoleCommands(console);
96 } 96 }
97 97
@@ -262,33 +262,36 @@ namespace OpenSim.Framework.Monitoring
262 { 262 {
263 OSDMap map = new OSDMap(); 263 OSDMap map = new OSDMap();
264 264
265 foreach (string catName in RegisteredStats.Keys) 265 lock (RegisteredStats)
266 { 266 {
267 // Do this category if null spec, "all" subcommand or category name matches passed parameter. 267 foreach (string catName in RegisteredStats.Keys)
268 // Skip category if none of the above.
269 if (!(String.IsNullOrEmpty(pCategoryName) || pCategoryName == AllSubCommand || pCategoryName == catName))
270 continue;
271
272 OSDMap contMap = new OSDMap();
273 foreach (string contName in RegisteredStats[catName].Keys)
274 { 268 {
275 if (!(string.IsNullOrEmpty(pContainerName) || pContainerName == AllSubCommand || pContainerName == contName)) 269 // Do this category if null spec, "all" subcommand or category name matches passed parameter.
270 // Skip category if none of the above.
271 if (!(String.IsNullOrEmpty(pCategoryName) || pCategoryName == AllSubCommand || pCategoryName == catName))
276 continue; 272 continue;
277
278 OSDMap statMap = new OSDMap();
279 273
280 SortedDictionary<string, Stat> theStats = RegisteredStats[catName][contName]; 274 OSDMap contMap = new OSDMap();
281 foreach (string statName in theStats.Keys) 275 foreach (string contName in RegisteredStats[catName].Keys)
282 { 276 {
283 if (!(String.IsNullOrEmpty(pStatName) || pStatName == AllSubCommand || pStatName == statName)) 277 if (!(string.IsNullOrEmpty(pContainerName) || pContainerName == AllSubCommand || pContainerName == contName))
284 continue; 278 continue;
285 279
286 statMap.Add(statName, theStats[statName].ToOSDMap()); 280 OSDMap statMap = new OSDMap();
287 } 281
282 SortedDictionary<string, Stat> theStats = RegisteredStats[catName][contName];
283 foreach (string statName in theStats.Keys)
284 {
285 if (!(String.IsNullOrEmpty(pStatName) || pStatName == AllSubCommand || pStatName == statName))
286 continue;
287
288 statMap.Add(statName, theStats[statName].ToBriefOSDMap());
289 }
288 290
289 contMap.Add(contName, statMap); 291 contMap.Add(contName, statMap);
292 }
293 map.Add(catName, contMap);
290 } 294 }
291 map.Add(catName, contMap);
292 } 295 }
293 296
294 return map; 297 return map;
@@ -301,6 +304,17 @@ namespace OpenSim.Framework.Monitoring
301 int response_code = 200; 304 int response_code = 200;
302 string contenttype = "text/json"; 305 string contenttype = "text/json";
303 306
307 if (StatsPassword != String.Empty && (!request.ContainsKey("pass") || request["pass"].ToString() != StatsPassword))
308 {
309 responsedata["int_response_code"] = response_code;
310 responsedata["content_type"] = "text/plain";
311 responsedata["keepalive"] = false;
312 responsedata["str_response_string"] = "Access denied";
313 responsedata["access_control_allow_origin"] = "*";
314
315 return responsedata;
316 }
317
304 string pCategoryName = StatsManager.AllSubCommand; 318 string pCategoryName = StatsManager.AllSubCommand;
305 string pContainerName = StatsManager.AllSubCommand; 319 string pContainerName = StatsManager.AllSubCommand;
306 string pStatName = StatsManager.AllSubCommand; 320 string pStatName = StatsManager.AllSubCommand;
@@ -358,8 +372,8 @@ namespace OpenSim.Framework.Monitoring
358 /// <returns></returns> 372 /// <returns></returns>
359 public static bool RegisterStat(Stat stat) 373 public static bool RegisterStat(Stat stat)
360 { 374 {
361 SortedDictionary<string, SortedDictionary<string, Stat>> category = null, newCategory; 375 SortedDictionary<string, SortedDictionary<string, Stat>> category = null;
362 SortedDictionary<string, Stat> container = null, newContainer; 376 SortedDictionary<string, Stat> container = null;
363 377
364 lock (RegisteredStats) 378 lock (RegisteredStats)
365 { 379 {
@@ -369,22 +383,15 @@ namespace OpenSim.Framework.Monitoring
369 if (TryGetStatParents(stat, out category, out container)) 383 if (TryGetStatParents(stat, out category, out container))
370 return false; 384 return false;
371 385
372 // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. 386 if (container == null)
373 // This means that we don't need to lock or copy them on iteration, which will be a much more 387 container = new SortedDictionary<string, Stat>();
374 // common operation after startup.
375 if (container != null)
376 newContainer = new SortedDictionary<string, Stat>(container);
377 else
378 newContainer = new SortedDictionary<string, Stat>();
379 388
380 if (category != null) 389 if (category == null)
381 newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>(category); 390 category = new SortedDictionary<string, SortedDictionary<string, Stat>>();
382 else
383 newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>();
384 391
385 newContainer[stat.ShortName] = stat; 392 container[stat.ShortName] = stat;
386 newCategory[stat.Container] = newContainer; 393 category[stat.Container] = container;
387 RegisteredStats[stat.Category] = newCategory; 394 RegisteredStats[stat.Category] = category;
388 } 395 }
389 396
390 return true; 397 return true;
@@ -397,23 +404,24 @@ namespace OpenSim.Framework.Monitoring
397 /// <returns></returns> 404 /// <returns></returns>
398 public static bool DeregisterStat(Stat stat) 405 public static bool DeregisterStat(Stat stat)
399 { 406 {
400 SortedDictionary<string, SortedDictionary<string, Stat>> category = null, newCategory; 407 SortedDictionary<string, SortedDictionary<string, Stat>> category = null;
401 SortedDictionary<string, Stat> container = null, newContainer; 408 SortedDictionary<string, Stat> container = null;
402 409
403 lock (RegisteredStats) 410 lock (RegisteredStats)
404 { 411 {
405 if (!TryGetStatParents(stat, out category, out container)) 412 if (!TryGetStatParents(stat, out category, out container))
406 return false; 413 return false;
407 414
408 newContainer = new SortedDictionary<string, Stat>(container); 415 if(container != null)
409 newContainer.Remove(stat.ShortName); 416 {
410 417 container.Remove(stat.ShortName);
411 newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>(category); 418 if(category != null && container.Count == 0)
412 newCategory.Remove(stat.Container); 419 {
413 420 category.Remove(stat.Container);
414 newCategory[stat.Container] = newContainer; 421 if(category.Count == 0)
415 RegisteredStats[stat.Category] = newCategory; 422 RegisteredStats.Remove(stat.Category);
416 423 }
424 }
417 return true; 425 return true;
418 } 426 }
419 } 427 }
@@ -554,4 +562,4 @@ namespace OpenSim.Framework.Monitoring
554 Debug, 562 Debug,
555 Info 563 Info
556 } 564 }
557} \ No newline at end of file 565}
diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs
index a644fa5..9cac451 100644
--- a/OpenSim/Framework/Monitoring/Watchdog.cs
+++ b/OpenSim/Framework/Monitoring/Watchdog.cs
@@ -96,7 +96,7 @@ namespace OpenSim.Framework.Monitoring
96 FirstTick = Environment.TickCount & Int32.MaxValue; 96 FirstTick = Environment.TickCount & Int32.MaxValue;
97 LastTick = FirstTick; 97 LastTick = FirstTick;
98 98
99 Stat 99 Stat
100 = new Stat( 100 = new Stat(
101 name, 101 name,
102 string.Format("Last update of thread {0}", name), 102 string.Format("Last update of thread {0}", name),
@@ -180,6 +180,30 @@ namespace OpenSim.Framework.Monitoring
180 m_watchdogTimer.Elapsed += WatchdogTimerElapsed; 180 m_watchdogTimer.Elapsed += WatchdogTimerElapsed;
181 } 181 }
182 182
183 public static void Stop()
184 {
185 if(m_threads == null)
186 return;
187
188 lock(m_threads)
189 {
190 m_enabled = false;
191 if(m_watchdogTimer != null)
192 {
193 m_watchdogTimer.Dispose();
194 m_watchdogTimer = null;
195 }
196
197 foreach(ThreadWatchdogInfo twi in m_threads.Values)
198 {
199 Thread t = twi.Thread;
200 if(t.IsAlive)
201 t.Abort();
202 }
203 m_threads.Clear();
204 }
205 }
206
183 /// <summary> 207 /// <summary>
184 /// Add a thread to the watchdog tracker. 208 /// Add a thread to the watchdog tracker.
185 /// </summary> 209 /// </summary>
@@ -230,14 +254,12 @@ namespace OpenSim.Framework.Monitoring
230 254
231 twi.Cleanup(); 255 twi.Cleanup();
232 m_threads.Remove(threadID); 256 m_threads.Remove(threadID);
233
234 return true; 257 return true;
235 } 258 }
236 else 259 else
237 { 260 {
238 m_log.WarnFormat( 261 m_log.WarnFormat(
239 "[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID); 262 "[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID);
240
241 return false; 263 return false;
242 } 264 }
243 } 265 }
@@ -317,6 +339,8 @@ namespace OpenSim.Framework.Monitoring
317 /// <param name="e"></param> 339 /// <param name="e"></param>
318 private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) 340 private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
319 { 341 {
342 if(!m_enabled)
343 return;
320 int now = Environment.TickCount & Int32.MaxValue; 344 int now = Environment.TickCount & Int32.MaxValue;
321 int msElapsed = now - LastWatchdogThreadTick; 345 int msElapsed = now - LastWatchdogThreadTick;
322 346
@@ -332,27 +356,36 @@ namespace OpenSim.Framework.Monitoring
332 if (callback != null) 356 if (callback != null)
333 { 357 {
334 List<ThreadWatchdogInfo> callbackInfos = null; 358 List<ThreadWatchdogInfo> callbackInfos = null;
359 List<ThreadWatchdogInfo> threadsToRemove = null;
360
361 const ThreadState thgone = ThreadState.Stopped;
335 362
336 lock (m_threads) 363 lock (m_threads)
337 { 364 {
338 foreach (ThreadWatchdogInfo threadInfo in m_threads.Values) 365 foreach(ThreadWatchdogInfo threadInfo in m_threads.Values)
339 { 366 {
340 if (threadInfo.Thread.ThreadState == ThreadState.Stopped) 367 if(!m_enabled)
368 return;
369 if((threadInfo.Thread.ThreadState & thgone) != 0)
341 { 370 {
342 RemoveThread(threadInfo.Thread.ManagedThreadId); 371 if(threadsToRemove == null)
372 threadsToRemove = new List<ThreadWatchdogInfo>();
343 373
344 if (callbackInfos == null) 374 threadsToRemove.Add(threadInfo);
375/*
376 if(callbackInfos == null)
345 callbackInfos = new List<ThreadWatchdogInfo>(); 377 callbackInfos = new List<ThreadWatchdogInfo>();
346 378
347 callbackInfos.Add(threadInfo); 379 callbackInfos.Add(threadInfo);
380*/
348 } 381 }
349 else if (!threadInfo.IsTimedOut && now - threadInfo.LastTick >= threadInfo.Timeout) 382 else if(!threadInfo.IsTimedOut && now - threadInfo.LastTick >= threadInfo.Timeout)
350 { 383 {
351 threadInfo.IsTimedOut = true; 384 threadInfo.IsTimedOut = true;
352 385
353 if (threadInfo.AlarmIfTimeout) 386 if(threadInfo.AlarmIfTimeout)
354 { 387 {
355 if (callbackInfos == null) 388 if(callbackInfos == null)
356 callbackInfos = new List<ThreadWatchdogInfo>(); 389 callbackInfos = new List<ThreadWatchdogInfo>();
357 390
358 // Send a copy of the watchdog info to prevent race conditions where the watchdog 391 // Send a copy of the watchdog info to prevent race conditions where the watchdog
@@ -361,9 +394,13 @@ namespace OpenSim.Framework.Monitoring
361 } 394 }
362 } 395 }
363 } 396 }
397
398 if(threadsToRemove != null)
399 foreach(ThreadWatchdogInfo twi in threadsToRemove)
400 RemoveThread(twi.Thread.ManagedThreadId);
364 } 401 }
365 402
366 if (callbackInfos != null) 403 if(callbackInfos != null)
367 foreach (ThreadWatchdogInfo callbackInfo in callbackInfos) 404 foreach (ThreadWatchdogInfo callbackInfo in callbackInfos)
368 callback(callbackInfo); 405 callback(callbackInfo);
369 } 406 }
@@ -377,4 +414,4 @@ namespace OpenSim.Framework.Monitoring
377 m_watchdogTimer.Start(); 414 m_watchdogTimer.Start();
378 } 415 }
379 } 416 }
380} \ No newline at end of file 417}
diff --git a/OpenSim/Framework/Monitoring/WorkManager.cs b/OpenSim/Framework/Monitoring/WorkManager.cs
index d1a74ce..5d9b185 100644
--- a/OpenSim/Framework/Monitoring/WorkManager.cs
+++ b/OpenSim/Framework/Monitoring/WorkManager.cs
@@ -36,16 +36,16 @@ namespace OpenSim.Framework.Monitoring
36 /// Manages various work items in the simulator. 36 /// Manages various work items in the simulator.
37 /// </summary> 37 /// </summary>
38 /// <remarks> 38 /// <remarks>
39 /// Currently, here work can be started 39 /// Currently, here work can be started
40 /// * As a long-running and monitored thread. 40 /// * As a long-running and monitored thread.
41 /// * In a thread that will never timeout but where the job is expected to eventually complete. 41 /// * In a thread that will never timeout but where the job is expected to eventually complete.
42 /// * In a threadpool thread that will timeout if it takes a very long time to complete (> 10 mins). 42 /// * In a threadpool thread that will timeout if it takes a very long time to complete (> 10 mins).
43 /// * As a job which will be run in a single-threaded job engine. Such jobs must not incorporate delays (sleeps, 43 /// * As a job which will be run in a single-threaded job engine. Such jobs must not incorporate delays (sleeps,
44 /// network waits, etc.). 44 /// network waits, etc.).
45 /// 45 ///
46 /// This is an evolving approach to better manage the work that OpenSimulator is asked to do from a very diverse 46 /// This is an evolving approach to better manage the work that OpenSimulator is asked to do from a very diverse
47 /// range of sources (client actions, incoming network, outgoing network calls, etc.). 47 /// range of sources (client actions, incoming network, outgoing network calls, etc.).
48 /// 48 ///
49 /// Util.FireAndForget is still available to insert jobs in the threadpool, though this is equivalent to 49 /// Util.FireAndForget is still available to insert jobs in the threadpool, though this is equivalent to
50 /// WorkManager.RunInThreadPool(). 50 /// WorkManager.RunInThreadPool().
51 /// </remarks> 51 /// </remarks>
@@ -57,7 +57,7 @@ namespace OpenSim.Framework.Monitoring
57 57
58 static WorkManager() 58 static WorkManager()
59 { 59 {
60 JobEngine = new JobEngine("Non-blocking non-critical job engine", "JOB ENGINE"); 60 JobEngine = new JobEngine("Non-blocking non-critical job engine", "JOB ENGINE", 30000);
61 61
62 StatsManager.RegisterStat( 62 StatsManager.RegisterStat(
63 new Stat( 63 new Stat(
@@ -82,6 +82,12 @@ namespace OpenSim.Framework.Monitoring
82 HandleControlCommand); 82 HandleControlCommand);
83 } 83 }
84 84
85 public static void Stop()
86 {
87 JobEngine.Stop();
88 Watchdog.Stop();
89 }
90
85 /// <summary> 91 /// <summary>
86 /// Start a new long-lived thread. 92 /// Start a new long-lived thread.
87 /// </summary> 93 /// </summary>
@@ -121,6 +127,7 @@ namespace OpenSim.Framework.Monitoring
121 Thread thread = new Thread(start); 127 Thread thread = new Thread(start);
122 thread.Priority = priority; 128 thread.Priority = priority;
123 thread.IsBackground = isBackground; 129 thread.IsBackground = isBackground;
130 thread.Name = name;
124 131
125 Watchdog.ThreadWatchdogInfo twi 132 Watchdog.ThreadWatchdogInfo twi
126 = new Watchdog.ThreadWatchdogInfo(thread, timeout, name) 133 = new Watchdog.ThreadWatchdogInfo(thread, timeout, name)
@@ -129,7 +136,6 @@ namespace OpenSim.Framework.Monitoring
129 Watchdog.AddThread(twi, name, log:log); 136 Watchdog.AddThread(twi, name, log:log);
130 137
131 thread.Start(); 138 thread.Start();
132 thread.Name = name;
133 139
134 return thread; 140 return thread;
135 } 141 }
@@ -143,7 +149,7 @@ namespace OpenSim.Framework.Monitoring
143 /// <param name="name">Name of the thread</param> 149 /// <param name="name">Name of the thread</param>
144 public static void RunInThread(WaitCallback callback, object obj, string name, bool log = false) 150 public static void RunInThread(WaitCallback callback, object obj, string name, bool log = false)
145 { 151 {
146 if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest) 152 if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
147 { 153 {
148 Culture.SetCurrentCulture(); 154 Culture.SetCurrentCulture();
149 callback(obj); 155 callback(obj);
@@ -168,7 +174,7 @@ namespace OpenSim.Framework.Monitoring
168 } 174 }
169 175
170 /// <summary> 176 /// <summary>
171 /// Run the callback via a threadpool thread. 177 /// Run the callback via a threadpool thread.
172 /// </summary> 178 /// </summary>
173 /// <remarks> 179 /// <remarks>
174 /// Such jobs may run after some delay but must always complete. 180 /// Such jobs may run after some delay but must always complete.
@@ -176,9 +182,9 @@ namespace OpenSim.Framework.Monitoring
176 /// <param name="callback"></param> 182 /// <param name="callback"></param>
177 /// <param name="obj"></param> 183 /// <param name="obj"></param>
178 /// <param name="name">The name of the job. This is used in monitoring and debugging.</param> 184 /// <param name="name">The name of the job. This is used in monitoring and debugging.</param>
179 public static void RunInThreadPool(System.Threading.WaitCallback callback, object obj, string name) 185 public static void RunInThreadPool(System.Threading.WaitCallback callback, object obj, string name, bool timeout = true)
180 { 186 {
181 Util.FireAndForget(callback, obj, name); 187 Util.FireAndForget(callback, obj, name, timeout);
182 } 188 }
183 189
184 /// <summary> 190 /// <summary>
@@ -187,17 +193,17 @@ namespace OpenSim.Framework.Monitoring
187 /// <remarks> 193 /// <remarks>
188 /// This differs from direct scheduling (e.g. Util.FireAndForget) in that a job can be run in the job 194 /// This differs from direct scheduling (e.g. Util.FireAndForget) in that a job can be run in the job
189 /// engine if it is running, where all jobs are currently performed in sequence on a single thread. This is 195 /// engine if it is running, where all jobs are currently performed in sequence on a single thread. This is
190 /// to prevent observed overload and server freeze problems when there are hundreds of connections which all attempt to 196 /// to prevent observed overload and server freeze problems when there are hundreds of connections which all attempt to
191 /// perform work at once (e.g. in conference situations). With lower numbers of connections, the small 197 /// perform work at once (e.g. in conference situations). With lower numbers of connections, the small
192 /// delay in performing jobs in sequence rather than concurrently has not been notiecable in testing, though a future more 198 /// delay in performing jobs in sequence rather than concurrently has not been notiecable in testing, though a future more
193 /// sophisticated implementation could perform jobs concurrently when the server is under low load. 199 /// sophisticated implementation could perform jobs concurrently when the server is under low load.
194 /// 200 ///
195 /// However, be advised that some callers of this function rely on all jobs being performed in sequence if any 201 /// However, be advised that some callers of this function rely on all jobs being performed in sequence if any
196 /// jobs are performed in sequence (i.e. if jobengine is active or not). Therefore, expanding the jobengine 202 /// jobs are performed in sequence (i.e. if jobengine is active or not). Therefore, expanding the jobengine
197 /// beyond a single thread will require considerable thought. 203 /// beyond a single thread will require considerable thought.
198 /// 204 ///
199 /// Also, any jobs submitted must be guaranteed to complete within a reasonable timeframe (e.g. they cannot 205 /// Also, any jobs submitted must be guaranteed to complete within a reasonable timeframe (e.g. they cannot
200 /// incorporate a network delay with a long timeout). At the moment, work that could suffer such issues 206 /// incorporate a network delay with a long timeout). At the moment, work that could suffer such issues
201 /// should still be run directly with RunInThread(), Util.FireAndForget(), etc. This is another area where 207 /// should still be run directly with RunInThread(), Util.FireAndForget(), etc. This is another area where
202 /// the job engine could be improved and so CPU utilization improved by better management of concurrency within 208 /// the job engine could be improved and so CPU utilization improved by better management of concurrency within
203 /// OpenSimulator. 209 /// OpenSimulator.
@@ -211,10 +217,10 @@ namespace OpenSim.Framework.Monitoring
211 /// <param name="log">If set to true then extra logging is performed.</param> 217 /// <param name="log">If set to true then extra logging is performed.</param>
212 public static void RunJob( 218 public static void RunJob(
213 string jobType, WaitCallback callback, object obj, string name, 219 string jobType, WaitCallback callback, object obj, string name,
214 bool canRunInThisThread = false, bool mustNotTimeout = false, 220 bool canRunInThisThread = false, bool mustNotTimeout = false,
215 bool log = false) 221 bool log = false)
216 { 222 {
217 if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest) 223 if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest)
218 { 224 {
219 Culture.SetCurrentCulture(); 225 Culture.SetCurrentCulture();
220 callback(obj); 226 callback(obj);
@@ -225,10 +231,8 @@ namespace OpenSim.Framework.Monitoring
225 JobEngine.QueueJob(name, () => callback(obj)); 231 JobEngine.QueueJob(name, () => callback(obj));
226 else if (canRunInThisThread) 232 else if (canRunInThisThread)
227 callback(obj); 233 callback(obj);
228 else if (mustNotTimeout)
229 RunInThread(callback, obj, name, log);
230 else 234 else
231 Util.FireAndForget(callback, obj, name); 235 Util.FireAndForget(callback, obj, name, !mustNotTimeout);
232 } 236 }
233 237
234 private static void HandleControlCommand(string module, string[] args) 238 private static void HandleControlCommand(string module, string[] args)
@@ -272,16 +276,16 @@ namespace OpenSim.Framework.Monitoring
272 MainConsole.Instance.Output("Usage: debug jobengine log <level>"); 276 MainConsole.Instance.Output("Usage: debug jobengine log <level>");
273 return; 277 return;
274 } 278 }
275 279
276 // int logLevel; 280 // int logLevel;
277 int logLevel = int.Parse(args[3]); 281 int logLevel = int.Parse(args[3]);
278 // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel)) 282 // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel))
279 // { 283 // {
280 JobEngine.LogLevel = logLevel; 284 JobEngine.LogLevel = logLevel;
281 MainConsole.Instance.OutputFormat("Set debug log level to {0}", JobEngine.LogLevel); 285 MainConsole.Instance.OutputFormat("Set debug log level to {0}", JobEngine.LogLevel);
282 // } 286 // }
283 } 287 }
284 else 288 else
285 { 289 {
286 MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand); 290 MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand);
287 } 291 }