diff options
author | Justin Clark-Casey (justincc) | 2012-03-16 00:34:30 +0000 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2012-03-16 00:34:30 +0000 |
commit | a4b01ef38a735ffe70b402061871a9c99f2757ed (patch) | |
tree | b60dcc1cbfaeb0cb0184f4b9619a7e95afe0a4de | |
parent | Merge branch 'master' of ssh://opensimulator.org/var/git/opensim (diff) | |
download | opensim-SC-a4b01ef38a735ffe70b402061871a9c99f2757ed.zip opensim-SC-a4b01ef38a735ffe70b402061871a9c99f2757ed.tar.gz opensim-SC-a4b01ef38a735ffe70b402061871a9c99f2757ed.tar.bz2 opensim-SC-a4b01ef38a735ffe70b402061871a9c99f2757ed.tar.xz |
Replace script-lines-per-second with the script execution time scaled by its measurement period and an idealised frame time.
The previous lines-per-second measurement used for top scripts report was inaccurate, since lines executed does not reflect time taken to execute.
Also, every fetch of the report would reset all the numbers limiting its usefulness and we weren't even guaranteed to see the top 100.
The actual measurement value should be script execution time per frame but XEngine does not work this way.
Therefore, we use actual script execution time scaled by the measurement period and an idealised frame time.
This is still not ideal but gives reasonable results and allows scripts to be compared.
This commit moves script execution time calculations from SceneGraph into IScriptModule implementations.
7 files changed, 145 insertions, 70 deletions
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index c303d6d..d363b15 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs | |||
@@ -26,8 +26,10 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | ||
29 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
30 | using System.IO; | 31 | using System.IO; |
32 | using System.Linq; | ||
31 | using System.Reflection; | 33 | using System.Reflection; |
32 | using System.Security; | 34 | using System.Security; |
33 | using log4net; | 35 | using log4net; |
@@ -876,52 +878,67 @@ namespace OpenSim.Region.CoreModules.World.Estate | |||
876 | if (!Scene.Permissions.CanIssueEstateCommand(remoteClient.AgentId, false)) | 878 | if (!Scene.Permissions.CanIssueEstateCommand(remoteClient.AgentId, false)) |
877 | return; | 879 | return; |
878 | 880 | ||
879 | Dictionary<uint, float> SceneData = new Dictionary<uint,float>(); | 881 | Dictionary<uint, float> sceneData = null; |
880 | List<UUID> uuidNameLookupList = new List<UUID>(); | 882 | List<UUID> uuidNameLookupList = new List<UUID>(); |
881 | 883 | ||
882 | if (reportType == 1) | 884 | if (reportType == 1) |
883 | { | 885 | { |
884 | SceneData = Scene.PhysicsScene.GetTopColliders(); | 886 | sceneData = Scene.PhysicsScene.GetTopColliders(); |
885 | } | 887 | } |
886 | else if (reportType == 0) | 888 | else if (reportType == 0) |
887 | { | 889 | { |
888 | SceneData = Scene.SceneGraph.GetTopScripts(); | 890 | IScriptModule scriptModule = Scene.RequestModuleInterface<IScriptModule>(); |
891 | |||
892 | if (scriptModule != null) | ||
893 | sceneData = scriptModule.GetObjectScriptsExecutionTimes(); | ||
889 | } | 894 | } |
890 | 895 | ||
891 | List<LandStatReportItem> SceneReport = new List<LandStatReportItem>(); | 896 | List<LandStatReportItem> SceneReport = new List<LandStatReportItem>(); |
892 | lock (SceneData) | 897 | if (sceneData != null) |
893 | { | 898 | { |
894 | foreach (uint obj in SceneData.Keys) | 899 | var sortedSceneData |
900 | = sceneData.Select( | ||
901 | item => new { Measurement = item.Value, Part = Scene.GetSceneObjectPart(item.Key) }); | ||
902 | |||
903 | sortedSceneData.OrderBy(item => item.Measurement); | ||
904 | |||
905 | int items = 0; | ||
906 | |||
907 | foreach (var entry in sortedSceneData) | ||
895 | { | 908 | { |
896 | SceneObjectPart prt = Scene.GetSceneObjectPart(obj); | 909 | if (entry.Part == null) |
897 | if (prt != null) | 910 | continue; |
911 | |||
912 | items++; | ||
913 | SceneObjectGroup so = entry.Part.ParentGroup; | ||
914 | |||
915 | LandStatReportItem lsri = new LandStatReportItem(); | ||
916 | lsri.LocationX = so.AbsolutePosition.X; | ||
917 | lsri.LocationY = so.AbsolutePosition.Y; | ||
918 | lsri.LocationZ = so.AbsolutePosition.Z; | ||
919 | lsri.Score = entry.Measurement; | ||
920 | lsri.TaskID = so.UUID; | ||
921 | lsri.TaskLocalID = so.LocalId; | ||
922 | lsri.TaskName = entry.Part.Name; | ||
923 | lsri.OwnerName = "waiting"; | ||
924 | lock (uuidNameLookupList) | ||
925 | uuidNameLookupList.Add(so.OwnerID); | ||
926 | |||
927 | if (filter.Length != 0) | ||
898 | { | 928 | { |
899 | SceneObjectGroup sog = prt.ParentGroup; | 929 | if ((lsri.OwnerName.Contains(filter) || lsri.TaskName.Contains(filter))) |
900 | LandStatReportItem lsri = new LandStatReportItem(); | ||
901 | lsri.LocationX = sog.AbsolutePosition.X; | ||
902 | lsri.LocationY = sog.AbsolutePosition.Y; | ||
903 | lsri.LocationZ = sog.AbsolutePosition.Z; | ||
904 | lsri.Score = SceneData[obj]; | ||
905 | lsri.TaskID = sog.UUID; | ||
906 | lsri.TaskLocalID = sog.LocalId; | ||
907 | lsri.TaskName = sog.GetPartName(obj); | ||
908 | lsri.OwnerName = "waiting"; | ||
909 | lock (uuidNameLookupList) | ||
910 | uuidNameLookupList.Add(sog.OwnerID); | ||
911 | |||
912 | if (filter.Length != 0) | ||
913 | { | 930 | { |
914 | if ((lsri.OwnerName.Contains(filter) || lsri.TaskName.Contains(filter))) | ||
915 | { | ||
916 | } | ||
917 | else | ||
918 | { | ||
919 | continue; | ||
920 | } | ||
921 | } | 931 | } |
922 | 932 | else | |
923 | SceneReport.Add(lsri); | 933 | { |
934 | continue; | ||
935 | } | ||
924 | } | 936 | } |
937 | |||
938 | SceneReport.Add(lsri); | ||
939 | |||
940 | if (items >= 100) | ||
941 | break; | ||
925 | } | 942 | } |
926 | } | 943 | } |
927 | 944 | ||
diff --git a/OpenSim/Region/Framework/Interfaces/IScriptModule.cs b/OpenSim/Region/Framework/Interfaces/IScriptModule.cs index 18c45dd..9fb4a25 100644 --- a/OpenSim/Region/Framework/Interfaces/IScriptModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IScriptModule.cs | |||
@@ -27,6 +27,7 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | 29 | using System.Collections; |
30 | using System.Collections.Generic; | ||
30 | using OpenMetaverse; | 31 | using OpenMetaverse; |
31 | 32 | ||
32 | namespace OpenSim.Region.Framework.Interfaces | 33 | namespace OpenSim.Region.Framework.Interfaces |
@@ -74,5 +75,11 @@ namespace OpenSim.Region.Framework.Interfaces | |||
74 | /// Starts the processing threads. | 75 | /// Starts the processing threads. |
75 | /// </summary> | 76 | /// </summary> |
76 | void StartProcessing(); | 77 | void StartProcessing(); |
78 | |||
79 | /// <summary> | ||
80 | /// Get the execution times of all scripts in each object. | ||
81 | /// </summary> | ||
82 | /// <returns>A dictionary where the key is a local object ID and the value is an execution time in milliseconds.</returns> | ||
83 | Dictionary<uint, float> GetObjectScriptsExecutionTimes(); | ||
77 | } | 84 | } |
78 | } | 85 | } \ No newline at end of file |
diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index bc3400a..5c542d6 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs | |||
@@ -733,6 +733,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
733 | #endregion | 733 | #endregion |
734 | 734 | ||
735 | #region Get Methods | 735 | #region Get Methods |
736 | |||
736 | /// <summary> | 737 | /// <summary> |
737 | /// Get the controlling client for the given avatar, if there is one. | 738 | /// Get the controlling client for the given avatar, if there is one. |
738 | /// | 739 | /// |
@@ -1074,36 +1075,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
1074 | return Entities.GetEntities(); | 1075 | return Entities.GetEntities(); |
1075 | } | 1076 | } |
1076 | 1077 | ||
1077 | public Dictionary<uint, float> GetTopScripts() | ||
1078 | { | ||
1079 | Dictionary<uint, float> topScripts = new Dictionary<uint, float>(); | ||
1080 | |||
1081 | EntityBase[] EntityList = GetEntities(); | ||
1082 | int limit = 0; | ||
1083 | foreach (EntityBase ent in EntityList) | ||
1084 | { | ||
1085 | if (ent is SceneObjectGroup) | ||
1086 | { | ||
1087 | SceneObjectGroup grp = (SceneObjectGroup)ent; | ||
1088 | if ((grp.RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0) | ||
1089 | { | ||
1090 | if (grp.scriptScore >= 0.01) | ||
1091 | { | ||
1092 | topScripts.Add(grp.LocalId, grp.scriptScore); | ||
1093 | limit++; | ||
1094 | if (limit >= 100) | ||
1095 | { | ||
1096 | break; | ||
1097 | } | ||
1098 | } | ||
1099 | grp.scriptScore = 0; | ||
1100 | } | ||
1101 | } | ||
1102 | } | ||
1103 | |||
1104 | return topScripts; | ||
1105 | } | ||
1106 | |||
1107 | #endregion | 1078 | #endregion |
1108 | 1079 | ||
1109 | #region Other Methods | 1080 | #region Other Methods |
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 878476e..afb5ccf 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs | |||
@@ -229,8 +229,6 @@ namespace OpenSim.Region.Framework.Scenes | |||
229 | get { return RootPart.VolumeDetectActive; } | 229 | get { return RootPart.VolumeDetectActive; } |
230 | } | 230 | } |
231 | 231 | ||
232 | public float scriptScore; | ||
233 | |||
234 | private Vector3 lastPhysGroupPos; | 232 | private Vector3 lastPhysGroupPos; |
235 | private Quaternion lastPhysGroupRot; | 233 | private Quaternion lastPhysGroupRot; |
236 | 234 | ||
@@ -1184,12 +1182,7 @@ namespace OpenSim.Region.Framework.Scenes | |||
1184 | 1182 | ||
1185 | public void AddScriptLPS(int count) | 1183 | public void AddScriptLPS(int count) |
1186 | { | 1184 | { |
1187 | if (scriptScore + count >= float.MaxValue - count) | 1185 | m_scene.SceneGraph.AddToScriptLPS(count); |
1188 | scriptScore = 0; | ||
1189 | |||
1190 | scriptScore += (float)count; | ||
1191 | SceneGraph d = m_scene.SceneGraph; | ||
1192 | d.AddToScriptLPS(count); | ||
1193 | } | 1186 | } |
1194 | 1187 | ||
1195 | public void AddActiveScriptCount(int count) | 1188 | public void AddActiveScriptCount(int count) |
diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs index 8762642..11f54a2 100644 --- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs | |||
@@ -78,6 +78,21 @@ namespace OpenSim.Region.ScriptEngine.Interfaces | |||
78 | /// </summary> | 78 | /// </summary> |
79 | string State { get; set; } | 79 | string State { get; set; } |
80 | 80 | ||
81 | /// <summary> | ||
82 | /// Time the script was last started | ||
83 | /// </summary> | ||
84 | DateTime TimeStarted { get; } | ||
85 | |||
86 | /// <summary> | ||
87 | /// Tick the last measurement period was started. | ||
88 | /// </summary> | ||
89 | long MeasurementPeriodTickStart { get; } | ||
90 | |||
91 | /// <summary> | ||
92 | /// Ticks spent executing in the last measurement period. | ||
93 | /// </summary> | ||
94 | long MeasurementPeriodExecutionTime { get; } | ||
95 | |||
81 | IScriptEngine Engine { get; } | 96 | IScriptEngine Engine { get; } |
82 | UUID AppDomain { get; set; } | 97 | UUID AppDomain { get; set; } |
83 | string PrimName { get; } | 98 | string PrimName { get; } |
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index 968351b..b177287 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs | |||
@@ -172,6 +172,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance | |||
172 | 172 | ||
173 | public TaskInventoryItem ScriptTask { get; private set; } | 173 | public TaskInventoryItem ScriptTask { get; private set; } |
174 | 174 | ||
175 | public DateTime TimeStarted { get; private set; } | ||
176 | |||
177 | public long MeasurementPeriodTickStart { get; private set; } | ||
178 | |||
179 | public long MeasurementPeriodExecutionTime { get; private set; } | ||
180 | |||
181 | public static readonly long MaxMeasurementPeriod = 30 * TimeSpan.TicksPerMinute; | ||
182 | |||
175 | public void ClearQueue() | 183 | public void ClearQueue() |
176 | { | 184 | { |
177 | m_TimerQueued = false; | 185 | m_TimerQueued = false; |
@@ -458,6 +466,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance | |||
458 | 466 | ||
459 | Running = true; | 467 | Running = true; |
460 | 468 | ||
469 | TimeStarted = DateTime.Now; | ||
470 | MeasurementPeriodTickStart = Util.EnvironmentTickCount(); | ||
471 | MeasurementPeriodExecutionTime = 0; | ||
472 | |||
461 | if (EventQueue.Count > 0) | 473 | if (EventQueue.Count > 0) |
462 | { | 474 | { |
463 | if (m_CurrentWorkItem == null) | 475 | if (m_CurrentWorkItem == null) |
@@ -710,8 +722,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance | |||
710 | m_EventStart = DateTime.Now; | 722 | m_EventStart = DateTime.Now; |
711 | m_InEvent = true; | 723 | m_InEvent = true; |
712 | 724 | ||
725 | int start = Util.EnvironmentTickCount(); | ||
726 | |||
727 | // Reset the measurement period when we reach the end of the current one. | ||
728 | if (start - MeasurementPeriodTickStart > MaxMeasurementPeriod) | ||
729 | MeasurementPeriodTickStart = start; | ||
730 | |||
713 | m_Script.ExecuteEvent(State, data.EventName, data.Params); | 731 | m_Script.ExecuteEvent(State, data.EventName, data.Params); |
714 | 732 | ||
733 | MeasurementPeriodExecutionTime += Util.EnvironmentTickCount() - start; | ||
734 | |||
715 | m_InEvent = false; | 735 | m_InEvent = false; |
716 | m_CurrentEvent = String.Empty; | 736 | m_CurrentEvent = String.Empty; |
717 | 737 | ||
@@ -720,7 +740,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance | |||
720 | // This will be the very first event we deliver | 740 | // This will be the very first event we deliver |
721 | // (state_entry) in default state | 741 | // (state_entry) in default state |
722 | // | 742 | // |
723 | |||
724 | SaveState(m_Assembly); | 743 | SaveState(m_Assembly); |
725 | 744 | ||
726 | m_SaveState = false; | 745 | m_SaveState = false; |
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index 105d97d..bddb1b9 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs | |||
@@ -1891,6 +1891,59 @@ namespace OpenSim.Region.ScriptEngine.XEngine | |||
1891 | } | 1891 | } |
1892 | } | 1892 | } |
1893 | 1893 | ||
1894 | public Dictionary<uint, float> GetObjectScriptsExecutionTimes() | ||
1895 | { | ||
1896 | long tickNow = Util.EnvironmentTickCount(); | ||
1897 | Dictionary<uint, float> topScripts = new Dictionary<uint, float>(); | ||
1898 | |||
1899 | lock (m_Scripts) | ||
1900 | { | ||
1901 | foreach (IScriptInstance si in m_Scripts.Values) | ||
1902 | { | ||
1903 | if (!topScripts.ContainsKey(si.LocalID)) | ||
1904 | topScripts[si.LocalID] = 0; | ||
1905 | |||
1906 | // long ticksElapsed = tickNow - si.MeasurementPeriodTickStart; | ||
1907 | // float framesElapsed = ticksElapsed / (18.1818 * TimeSpan.TicksPerMillisecond); | ||
1908 | |||
1909 | // Execution time of the script adjusted by it's measurement period to make scripts started at | ||
1910 | // different times comparable. | ||
1911 | // float adjustedExecutionTime | ||
1912 | // = (float)si.MeasurementPeriodExecutionTime | ||
1913 | // / ((float)(tickNow - si.MeasurementPeriodTickStart) / ScriptInstance.MaxMeasurementPeriod) | ||
1914 | // / TimeSpan.TicksPerMillisecond; | ||
1915 | |||
1916 | long ticksElapsed = tickNow - si.MeasurementPeriodTickStart; | ||
1917 | |||
1918 | // Avoid divide by zerp | ||
1919 | if (ticksElapsed == 0) | ||
1920 | ticksElapsed = 1; | ||
1921 | |||
1922 | // Scale execution time to the ideal 55 fps frame time for these reasons. | ||
1923 | // | ||
1924 | // 1) XEngine does not execute scripts per frame, unlike other script engines. Hence, there is no | ||
1925 | // 'script execution time per frame', which is the original purpose of this value. | ||
1926 | // | ||
1927 | // 2) Giving the raw execution times is misleading since scripts start at different times, making | ||
1928 | // it impossible to compare scripts. | ||
1929 | // | ||
1930 | // 3) Scaling the raw execution time to the time that the script has been running is better but | ||
1931 | // is still misleading since a script that has just been rezzed may appear to have been running | ||
1932 | // for much longer. | ||
1933 | // | ||
1934 | // 4) Hence, we scale execution time to an idealised frame time (55 fps). This is also not perfect | ||
1935 | // since the figure does not represent actual execution time and very hard running scripts will | ||
1936 | // never exceed 18ms (though this is a very high number for script execution so is a warning sign). | ||
1937 | float adjustedExecutionTime | ||
1938 | = ((float)si.MeasurementPeriodExecutionTime / ticksElapsed) * 18.1818f; | ||
1939 | |||
1940 | topScripts[si.LocalID] += adjustedExecutionTime; | ||
1941 | } | ||
1942 | } | ||
1943 | |||
1944 | return topScripts; | ||
1945 | } | ||
1946 | |||
1894 | public void SuspendScript(UUID itemID) | 1947 | public void SuspendScript(UUID itemID) |
1895 | { | 1948 | { |
1896 | // m_log.DebugFormat("[XEngine]: Received request to suspend script with ID {0}", itemID); | 1949 | // m_log.DebugFormat("[XEngine]: Received request to suspend script with ID {0}", itemID); |