opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Timers;
31 using OpenMetaverse.Packets;
32 using OpenSim.Framework;
33 using OpenSim.Framework.Monitoring;
34 using OpenSim.Region.Framework.Interfaces;
35  
36 namespace OpenSim.Region.Framework.Scenes
37 {
38 /// <summary>
39 /// Collect statistics from the scene to send to the client and for access by other monitoring tools.
40 /// </summary>
41 /// <remarks>
42 /// FIXME: This should be a monitoring region module
43 /// </remarks>
44 public class SimStatsReporter
45 {
46 private static readonly log4net.ILog m_log
47 = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
48  
49 public const string LastReportedObjectUpdateStatName = "LastReportedObjectUpdates";
50 public const string SlowFramesStatName = "SlowFrames";
51  
52 public delegate void SendStatResult(SimStats stats);
53  
54 public delegate void YourStatsAreWrong();
55  
56 public event SendStatResult OnSendStatsResult;
57  
58 public event YourStatsAreWrong OnStatsIncorrect;
59  
60 private SendStatResult handlerSendStatResult;
61  
62 private YourStatsAreWrong handlerStatsIncorrect;
63  
64 /// <summary>
65 /// These are the IDs of stats sent in the StatsPacket to the viewer.
66 /// </summary>
67 /// <remarks>
68 /// Some of these are not relevant to OpenSimulator since it is architected differently to other simulators
69 /// (e.g. script instructions aren't executed as part of the frame loop so 'script time' is tricky).
70 /// </remarks>
71 public enum Stats : uint
72 {
73 TimeDilation = 0,
74 SimFPS = 1,
75 PhysicsFPS = 2,
76 AgentUpdates = 3,
77 FrameMS = 4,
78 NetMS = 5,
79 OtherMS = 6,
80 PhysicsMS = 7,
81 AgentMS = 8,
82 ImageMS = 9,
83 ScriptMS = 10,
84 TotalPrim = 11,
85 ActivePrim = 12,
86 Agents = 13,
87 ChildAgents = 14,
88 ActiveScripts = 15,
89 ScriptLinesPerSecond = 16,
90 InPacketsPerSecond = 17,
91 OutPacketsPerSecond = 18,
92 PendingDownloads = 19,
93 PendingUploads = 20,
94 VirtualSizeKb = 21,
95 ResidentSizeKb = 22,
96 PendingLocalUploads = 23,
97 UnAckedBytes = 24,
98 PhysicsPinnedTasks = 25,
99 PhysicsLodTasks = 26,
100 SimPhysicsStepMs = 27,
101 SimPhysicsShapeMs = 28,
102 SimPhysicsOtherMs = 29,
103 SimPhysicsMemory = 30,
104 ScriptEps = 31,
105 SimSpareMs = 32,
106 SimSleepMs = 33,
107 SimIoPumpTime = 34
108 }
109  
110 /// <summary>
111 /// This is for llGetRegionFPS
112 /// </summary>
113 public float LastReportedSimFPS
114 {
115 get { return lastReportedSimFPS; }
116 }
117  
118 /// <summary>
119 /// Number of object updates performed in the last stats cycle
120 /// </summary>
121 /// <remarks>
122 /// This isn't sent out to the client but it is very useful data to detect whether viewers are being sent a
123 /// large number of object updates.
124 /// </remarks>
125 public float LastReportedObjectUpdates { get; private set; }
126  
127 public float[] LastReportedSimStats
128 {
129 get { return lastReportedSimStats; }
130 }
131  
132 /// <summary>
133 /// Number of frames that have taken longer to process than Scene.MIN_FRAME_TIME
134 /// </summary>
135 public Stat SlowFramesStat { get; private set; }
136  
137 /// <summary>
138 /// The threshold at which we log a slow frame.
139 /// </summary>
140 public int SlowFramesStatReportThreshold { get; private set; }
141  
142 /// <summary>
143 /// Extra sim statistics that are used by monitors but not sent to the client.
144 /// </summary>
145 /// <value>
146 /// The keys are the stat names.
147 /// </value>
148 private Dictionary<string, float> m_lastReportedExtraSimStats = new Dictionary<string, float>();
149  
150 // Sending a stats update every 3 seconds-
151 private int m_statsUpdatesEveryMS = 3000;
152 private float m_statsUpdateFactor;
153 private float m_timeDilation;
154 private int m_fps;
155  
156 /// <summary>
157 /// Number of the last frame on which we processed a stats udpate.
158 /// </summary>
159 private uint m_lastUpdateFrame;
160  
161 /// <summary>
162 /// Our nominal fps target, as expected in fps stats when a sim is running normally.
163 /// </summary>
164 private float m_nominalReportedFps = 55;
165  
166 /// <summary>
167 /// Parameter to adjust reported scene fps
168 /// </summary>
169 /// <remarks>
170 /// Our scene loop runs slower than other server implementations, apparantly because we work somewhat differently.
171 /// However, we will still report an FPS that's closer to what people are used to seeing. A lower FPS might
172 /// affect clients and monitoring scripts/software.
173 /// </remarks>
174 private float m_reportedFpsCorrectionFactor = 5;
175  
176 // saved last reported value so there is something available for llGetRegionFPS
177 private float lastReportedSimFPS;
178 private float[] lastReportedSimStats = new float[22];
179 private float m_pfps;
180  
181 /// <summary>
182 /// Number of agent updates requested in this stats cycle
183 /// </summary>
184 private int m_agentUpdates;
185  
186 /// <summary>
187 /// Number of object updates requested in this stats cycle
188 /// </summary>
189 private int m_objectUpdates;
190  
191 private int m_frameMS;
192 private int m_spareMS;
193 private int m_netMS;
194 private int m_agentMS;
195 private int m_physicsMS;
196 private int m_imageMS;
197 private int m_otherMS;
198  
199 //Ckrinke: (3-21-08) Comment out to remove a compiler warning. Bring back into play when needed.
200 //Ckrinke private int m_scriptMS = 0;
201  
202 private int m_rootAgents;
203 private int m_childAgents;
204 private int m_numPrim;
205 private int m_inPacketsPerSecond;
206 private int m_outPacketsPerSecond;
207 private int m_activePrim;
208 private int m_unAckedBytes;
209 private int m_pendingDownloads;
210 private int m_pendingUploads = 0; // FIXME: Not currently filled in
211 private int m_activeScripts;
212 private int m_scriptLinesPerSecond;
213  
214 private int m_objectCapacity = 45000;
215  
216 private Scene m_scene;
217  
218 private RegionInfo ReportingRegion;
219  
220 private Timer m_report = new Timer();
221  
222 private IEstateModule estateModule;
223  
224 public SimStatsReporter(Scene scene)
225 {
226 m_scene = scene;
227 m_reportedFpsCorrectionFactor = scene.MinFrameTime * m_nominalReportedFps;
228 m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000);
229 ReportingRegion = scene.RegionInfo;
230  
231 m_objectCapacity = scene.RegionInfo.ObjectCapacity;
232 m_report.AutoReset = true;
233 m_report.Interval = m_statsUpdatesEveryMS;
234 m_report.Elapsed += TriggerStatsHeartbeat;
235 m_report.Enabled = true;
236  
237 if (StatsManager.SimExtraStats != null)
238 OnSendStatsResult += StatsManager.SimExtraStats.ReceiveClassicSimStatsPacket;
239  
240 /// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit
241 /// longer than ideal (which in itself is a concern).
242 SlowFramesStatReportThreshold = (int)Math.Ceiling(m_scene.MinFrameTime * 1000 * 1.2);
243  
244 SlowFramesStat
245 = new Stat(
246 "SlowFrames",
247 "Slow Frames",
248 "Number of frames where frame time has been significantly longer than the desired frame time.",
249 " frames",
250 "scene",
251 m_scene.Name,
252 StatType.Push,
253 null,
254 StatVerbosity.Info);
255  
256 StatsManager.RegisterStat(SlowFramesStat);
257 }
258  
259 public void Close()
260 {
261 m_report.Elapsed -= TriggerStatsHeartbeat;
262 m_report.Close();
263 }
264  
265 /// <summary>
266 /// Sets the number of milliseconds between stat updates.
267 /// </summary>
268 /// <param name='ms'></param>
269 public void SetUpdateMS(int ms)
270 {
271 m_statsUpdatesEveryMS = ms;
272 m_statsUpdateFactor = (float)(m_statsUpdatesEveryMS / 1000);
273 m_report.Interval = m_statsUpdatesEveryMS;
274 }
275  
276 private void TriggerStatsHeartbeat(object sender, EventArgs args)
277 {
278 try
279 {
280 statsHeartBeat(sender, args);
281 }
282 catch (Exception e)
283 {
284 m_log.Warn(string.Format(
285 "[SIM STATS REPORTER] Update for {0} failed with exception ",
286 m_scene.RegionInfo.RegionName), e);
287 }
288 }
289  
290 private void statsHeartBeat(object sender, EventArgs e)
291 {
292 if (!m_scene.Active)
293 return;
294  
295 SimStatsPacket.StatBlock[] sb = new SimStatsPacket.StatBlock[22];
296 SimStatsPacket.RegionBlock rb = new SimStatsPacket.RegionBlock();
297  
298 // Know what's not thread safe in Mono... modifying timers.
299 // m_log.Debug("Firing Stats Heart Beat");
300 lock (m_report)
301 {
302 uint regionFlags = 0;
303  
304 try
305 {
306 if (estateModule == null)
307 estateModule = m_scene.RequestModuleInterface<IEstateModule>();
308 regionFlags = estateModule != null ? estateModule.GetRegionFlags() : (uint) 0;
309 }
310 catch (Exception)
311 {
312 // leave region flags at 0
313 }
314  
315 #region various statistic googly moogly
316  
317 // We're going to lie about the FPS because we've been lying since 2008. The actual FPS is currently
318 // locked at a maximum of 11. Maybe at some point this can change so that we're not lying.
319 int reportedFPS = (int)(m_fps * m_reportedFpsCorrectionFactor);
320  
321 // save the reported value so there is something available for llGetRegionFPS
322 lastReportedSimFPS = reportedFPS / m_statsUpdateFactor;
323  
324 float physfps = ((m_pfps / 1000));
325  
326 //if (physfps > 600)
327 //physfps = physfps - (physfps - 600);
328  
329 if (physfps < 0)
330 physfps = 0;
331  
332 #endregion
333  
334 m_rootAgents = m_scene.SceneGraph.GetRootAgentCount();
335 m_childAgents = m_scene.SceneGraph.GetChildAgentCount();
336 m_numPrim = m_scene.SceneGraph.GetTotalObjectsCount();
337 m_activePrim = m_scene.SceneGraph.GetActiveObjectsCount();
338 m_activeScripts = m_scene.SceneGraph.GetActiveScriptsCount();
339  
340 // FIXME: Checking for stat sanity is a complex approach. What we really need to do is fix the code
341 // so that stat numbers are always consistent.
342 CheckStatSanity();
343  
344 //Our time dilation is 0.91 when we're running a full speed,
345 // therefore to make sure we get an appropriate range,
346 // we have to factor in our error. (0.10f * statsUpdateFactor)
347 // multiplies the fix for the error times the amount of times it'll occur a second
348 // / 10 divides the value by the number of times the sim heartbeat runs (10fps)
349 // Then we divide the whole amount by the amount of seconds pass in between stats updates.
350  
351 // 'statsUpdateFactor' is how often stats packets are sent in seconds. Used below to change
352 // values to X-per-second values.
353  
354 uint thisFrame = m_scene.Frame;
355 float framesUpdated = (float)(thisFrame - m_lastUpdateFrame) * m_reportedFpsCorrectionFactor;
356 m_lastUpdateFrame = thisFrame;
357  
358 // Avoid div-by-zero if somehow we've not updated any frames.
359 if (framesUpdated == 0)
360 framesUpdated = 1;
361  
362 for (int i = 0; i < 22; i++)
363 {
364 sb[i] = new SimStatsPacket.StatBlock();
365 }
366  
367 sb[0].StatID = (uint) Stats.TimeDilation;
368 sb[0].StatValue = (Single.IsNaN(m_timeDilation)) ? 0.1f : m_timeDilation ; //((((m_timeDilation + (0.10f * statsUpdateFactor)) /10) / statsUpdateFactor));
369  
370 sb[1].StatID = (uint) Stats.SimFPS;
371 sb[1].StatValue = reportedFPS / m_statsUpdateFactor;
372  
373 sb[2].StatID = (uint) Stats.PhysicsFPS;
374 sb[2].StatValue = physfps / m_statsUpdateFactor;
375  
376 sb[3].StatID = (uint) Stats.AgentUpdates;
377 sb[3].StatValue = (m_agentUpdates / m_statsUpdateFactor);
378  
379 sb[4].StatID = (uint) Stats.Agents;
380 sb[4].StatValue = m_rootAgents;
381  
382 sb[5].StatID = (uint) Stats.ChildAgents;
383 sb[5].StatValue = m_childAgents;
384  
385 sb[6].StatID = (uint) Stats.TotalPrim;
386 sb[6].StatValue = m_numPrim;
387  
388 sb[7].StatID = (uint) Stats.ActivePrim;
389 sb[7].StatValue = m_activePrim;
390  
391 sb[8].StatID = (uint)Stats.FrameMS;
392 sb[8].StatValue = m_frameMS / framesUpdated;
393  
394 sb[9].StatID = (uint)Stats.NetMS;
395 sb[9].StatValue = m_netMS / framesUpdated;
396  
397 sb[10].StatID = (uint)Stats.PhysicsMS;
398 sb[10].StatValue = m_physicsMS / framesUpdated;
399  
400 sb[11].StatID = (uint)Stats.ImageMS ;
401 sb[11].StatValue = m_imageMS / framesUpdated;
402  
403 sb[12].StatID = (uint)Stats.OtherMS;
404 sb[12].StatValue = m_otherMS / framesUpdated;
405  
406 sb[13].StatID = (uint)Stats.InPacketsPerSecond;
407 sb[13].StatValue = (m_inPacketsPerSecond / m_statsUpdateFactor);
408  
409 sb[14].StatID = (uint)Stats.OutPacketsPerSecond;
410 sb[14].StatValue = (m_outPacketsPerSecond / m_statsUpdateFactor);
411  
412 sb[15].StatID = (uint)Stats.UnAckedBytes;
413 sb[15].StatValue = m_unAckedBytes;
414  
415 sb[16].StatID = (uint)Stats.AgentMS;
416 sb[16].StatValue = m_agentMS / framesUpdated;
417  
418 sb[17].StatID = (uint)Stats.PendingDownloads;
419 sb[17].StatValue = m_pendingDownloads;
420  
421 sb[18].StatID = (uint)Stats.PendingUploads;
422 sb[18].StatValue = m_pendingUploads;
423  
424 sb[19].StatID = (uint)Stats.ActiveScripts;
425 sb[19].StatValue = m_activeScripts;
426  
427 sb[20].StatID = (uint)Stats.ScriptLinesPerSecond;
428 sb[20].StatValue = m_scriptLinesPerSecond / m_statsUpdateFactor;
429  
430 sb[21].StatID = (uint)Stats.SimSpareMs;
431 sb[21].StatValue = m_spareMS / framesUpdated;
432  
433 for (int i = 0; i < 22; i++)
434 {
435 lastReportedSimStats[i] = sb[i].StatValue;
436 }
437  
438 SimStats simStats
439 = new SimStats(
440 ReportingRegion.RegionLocX, ReportingRegion.RegionLocY, regionFlags, (uint)m_objectCapacity,
441 rb, sb, m_scene.RegionInfo.originRegionID);
442  
443 handlerSendStatResult = OnSendStatsResult;
444 if (handlerSendStatResult != null)
445 {
446 handlerSendStatResult(simStats);
447 }
448  
449 // Extra statistics that aren't currently sent to clients
450 lock (m_lastReportedExtraSimStats)
451 {
452 m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / m_statsUpdateFactor;
453 m_lastReportedExtraSimStats[SlowFramesStat.ShortName] = (float)SlowFramesStat.Value;
454  
455 Dictionary<string, float> physicsStats = m_scene.PhysicsScene.GetStats();
456  
457 if (physicsStats != null)
458 {
459 foreach (KeyValuePair<string, float> tuple in physicsStats)
460 {
461 // FIXME: An extremely dirty hack to divide MS stats per frame rather than per second
462 // Need to change things so that stats source can indicate whether they are per second or
463 // per frame.
464 if (tuple.Key.EndsWith("MS"))
465 m_lastReportedExtraSimStats[tuple.Key] = tuple.Value / framesUpdated;
466 else
467 m_lastReportedExtraSimStats[tuple.Key] = tuple.Value / m_statsUpdateFactor;
468 }
469 }
470 }
471  
472 ResetValues();
473 }
474 }
475  
476 private void ResetValues()
477 {
478 m_timeDilation = 0;
479 m_fps = 0;
480 m_pfps = 0;
481 m_agentUpdates = 0;
482 m_objectUpdates = 0;
483 //m_inPacketsPerSecond = 0;
484 //m_outPacketsPerSecond = 0;
485 m_unAckedBytes = 0;
486 m_scriptLinesPerSecond = 0;
487  
488 m_frameMS = 0;
489 m_agentMS = 0;
490 m_netMS = 0;
491 m_physicsMS = 0;
492 m_imageMS = 0;
493 m_otherMS = 0;
494 m_spareMS = 0;
495  
496 //Ckrinke This variable is not used, so comment to remove compiler warning until it is used.
497 //Ckrinke m_scriptMS = 0;
498 }
499  
500 # region methods called from Scene
501 // The majority of these functions are additive
502 // so that you can easily change the amount of
503 // seconds in between sim stats updates
504  
505 public void AddTimeDilation(float td)
506 {
507 //float tdsetting = td;
508 //if (tdsetting > 1.0f)
509 //tdsetting = (tdsetting - (tdsetting - 0.91f));
510  
511 //if (tdsetting < 0)
512 //tdsetting = 0.0f;
513 m_timeDilation = td;
514 }
515  
516 internal void CheckStatSanity()
517 {
518 if (m_rootAgents < 0 || m_childAgents < 0)
519 {
520 handlerStatsIncorrect = OnStatsIncorrect;
521 if (handlerStatsIncorrect != null)
522 {
523 handlerStatsIncorrect();
524 }
525 }
526 if (m_rootAgents == 0 && m_childAgents == 0)
527 {
528 m_unAckedBytes = 0;
529 }
530 }
531  
532 public void AddFPS(int frames)
533 {
534 m_fps += frames;
535 }
536  
537 public void AddPhysicsFPS(float frames)
538 {
539 m_pfps += frames;
540 }
541  
542 public void AddObjectUpdates(int numUpdates)
543 {
544 m_objectUpdates += numUpdates;
545 }
546  
547 public void AddAgentUpdates(int numUpdates)
548 {
549 m_agentUpdates += numUpdates;
550 }
551  
552 public void AddInPackets(int numPackets)
553 {
554 m_inPacketsPerSecond = numPackets;
555 }
556  
557 public void AddOutPackets(int numPackets)
558 {
559 m_outPacketsPerSecond = numPackets;
560 }
561  
562 public void AddunAckedBytes(int numBytes)
563 {
564 m_unAckedBytes += numBytes;
565 if (m_unAckedBytes < 0) m_unAckedBytes = 0;
566 }
567  
568 public void addFrameMS(int ms)
569 {
570 m_frameMS += ms;
571  
572 // At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit
573 // longer than ideal due to the inaccuracy of the Sleep in Scene.Update() (which in itself is a concern).
574 if (ms > SlowFramesStatReportThreshold)
575 SlowFramesStat.Value++;
576 }
577  
578 public void AddSpareMS(int ms)
579 {
580 m_spareMS += ms;
581 }
582  
583 public void addNetMS(int ms)
584 {
585 m_netMS += ms;
586 }
587  
588 public void addAgentMS(int ms)
589 {
590 m_agentMS += ms;
591 }
592  
593 public void addPhysicsMS(int ms)
594 {
595 m_physicsMS += ms;
596 }
597  
598 public void addImageMS(int ms)
599 {
600 m_imageMS += ms;
601 }
602  
603 public void addOtherMS(int ms)
604 {
605 m_otherMS += ms;
606 }
607  
608 public void AddPendingDownloads(int count)
609 {
610 m_pendingDownloads += count;
611  
612 if (m_pendingDownloads < 0)
613 m_pendingDownloads = 0;
614  
615 //m_log.InfoFormat("[stats]: Adding {0} to pending downloads to make {1}", count, m_pendingDownloads);
616 }
617  
618 public void addScriptLines(int count)
619 {
620 m_scriptLinesPerSecond += count;
621 }
622  
623 public void AddPacketsStats(int inPackets, int outPackets, int unAckedBytes)
624 {
625 AddInPackets(inPackets);
626 AddOutPackets(outPackets);
627 AddunAckedBytes(unAckedBytes);
628 }
629  
630 #endregion
631  
632 public Dictionary<string, float> GetExtraSimStats()
633 {
634 lock (m_lastReportedExtraSimStats)
635 return new Dictionary<string, float>(m_lastReportedExtraSimStats);
636 }
637 }
638 }