diff --git a/OpenSim/Framework/Statistics/BaseStatsCollector.cs b/OpenSim/Framework/Statistics/BaseStatsCollector.cs index a1841c568a..c9e57ce7b3 100644 --- a/OpenSim/Framework/Statistics/BaseStatsCollector.cs +++ b/OpenSim/Framework/Statistics/BaseStatsCollector.cs @@ -26,8 +26,8 @@ */ using System; +using System.Diagnostics; using System.Text; - using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -46,8 +46,12 @@ namespace OpenSim.Framework.Statistics sb.Append(Environment.NewLine); sb.Append( string.Format( - "Allocated to OpenSim : {0} MB" + Environment.NewLine, + "Allocated to OpenSim objects: {0} MB\n", Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0))); + sb.Append( + string.Format( + "Process memory : {0} MB\n", + Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0))); return sb.ToString(); } diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index efa4a7ba87..31fa101e50 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -81,12 +81,15 @@ namespace OpenSim.Framework private static uint nextXferID = 5000; private static Random randomClass = new Random(); + // Get a list of invalid file characters (OS dependent) private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]"; private static string regexInvalidPathChars = "[" + new String(Path.GetInvalidPathChars()) + "]"; private static object XferLock = new object(); - /// Thread pool used for Util.FireAndForget if - /// FireAndForgetMethod.SmartThreadPool is used + + /// + /// Thread pool used for Util.FireAndForget if FireAndForgetMethod.SmartThreadPool is used + /// private static SmartThreadPool m_ThreadPool; // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC. diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 6fba2493d4..e955a58527 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -503,7 +503,11 @@ namespace OpenSim string currentCommand; while ((currentCommand = readFile.ReadLine()) != null) { - if (currentCommand != String.Empty) + currentCommand = currentCommand.Trim(); + if (!(currentCommand == "" + || currentCommand.StartsWith(";") + || currentCommand.StartsWith("//") + || currentCommand.StartsWith("#"))) { m_log.Info("[COMMANDFILE]: Running '" + currentCommand + "'"); m_console.RunCommand(currentCommand); diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index c303d6db1c..61d604ff3d 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -26,8 +26,10 @@ */ using System; +using System.Collections; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Security; using log4net; @@ -45,8 +47,6 @@ namespace OpenSim.Region.CoreModules.World.Estate { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private delegate void LookupUUIDS(List uuidLst); - public Scene Scene { get; private set; } public IUserManagement UserManager { get; private set; } @@ -876,98 +876,77 @@ namespace OpenSim.Region.CoreModules.World.Estate if (!Scene.Permissions.CanIssueEstateCommand(remoteClient.AgentId, false)) return; - Dictionary SceneData = new Dictionary(); + Dictionary sceneData = null; List uuidNameLookupList = new List(); if (reportType == 1) { - SceneData = Scene.PhysicsScene.GetTopColliders(); + sceneData = Scene.PhysicsScene.GetTopColliders(); } else if (reportType == 0) { - SceneData = Scene.SceneGraph.GetTopScripts(); + IScriptModule scriptModule = Scene.RequestModuleInterface(); + + if (scriptModule != null) + sceneData = scriptModule.GetObjectScriptsExecutionTimes(); } List SceneReport = new List(); - lock (SceneData) + if (sceneData != null) { - foreach (uint obj in SceneData.Keys) + var sortedSceneData + = sceneData.Select( + item => new { Measurement = item.Value, Part = Scene.GetSceneObjectPart(item.Key) }); + + sortedSceneData.OrderBy(item => item.Measurement); + + int items = 0; + + foreach (var entry in sortedSceneData) { - SceneObjectPart prt = Scene.GetSceneObjectPart(obj); - if (prt != null) + // The object may have been deleted since we received the data. + if (entry.Part == null) + continue; + + // Don't show scripts that haven't executed or where execution time is below one microsecond in + // order to produce a more readable report. + if (entry.Measurement < 0.001) + continue; + + items++; + SceneObjectGroup so = entry.Part.ParentGroup; + + LandStatReportItem lsri = new LandStatReportItem(); + lsri.LocationX = so.AbsolutePosition.X; + lsri.LocationY = so.AbsolutePosition.Y; + lsri.LocationZ = so.AbsolutePosition.Z; + lsri.Score = entry.Measurement; + lsri.TaskID = so.UUID; + lsri.TaskLocalID = so.LocalId; + lsri.TaskName = entry.Part.Name; + lsri.OwnerName = UserManager.GetUserName(so.OwnerID); + + if (filter.Length != 0) { - SceneObjectGroup sog = prt.ParentGroup; - LandStatReportItem lsri = new LandStatReportItem(); - lsri.LocationX = sog.AbsolutePosition.X; - lsri.LocationY = sog.AbsolutePosition.Y; - lsri.LocationZ = sog.AbsolutePosition.Z; - lsri.Score = SceneData[obj]; - lsri.TaskID = sog.UUID; - lsri.TaskLocalID = sog.LocalId; - lsri.TaskName = sog.GetPartName(obj); - lsri.OwnerName = "waiting"; - lock (uuidNameLookupList) - uuidNameLookupList.Add(sog.OwnerID); - - if (filter.Length != 0) + if ((lsri.OwnerName.Contains(filter) || lsri.TaskName.Contains(filter))) { - if ((lsri.OwnerName.Contains(filter) || lsri.TaskName.Contains(filter))) - { - } - else - { - continue; - } } - - SceneReport.Add(lsri); + else + { + continue; + } } + + SceneReport.Add(lsri); + + if (items >= 100) + break; } } remoteClient.SendLandStatReply(reportType, requestFlags, (uint)SceneReport.Count,SceneReport.ToArray()); - - if (uuidNameLookupList.Count > 0) - LookupUUID(uuidNameLookupList); } - private static void LookupUUIDSCompleted(IAsyncResult iar) - { - LookupUUIDS icon = (LookupUUIDS)iar.AsyncState; - icon.EndInvoke(iar); - } - - private void LookupUUID(List uuidLst) - { - LookupUUIDS d = LookupUUIDsAsync; - - d.BeginInvoke(uuidLst, - LookupUUIDSCompleted, - d); - } - - private void LookupUUIDsAsync(List uuidLst) - { - UUID[] uuidarr; - - lock (uuidLst) - { - uuidarr = uuidLst.ToArray(); - } - - for (int i = 0; i < uuidarr.Length; i++) - { - // string lookupname = m_scene.CommsManager.UUIDNameRequestString(uuidarr[i]); - - IUserManagement userManager = Scene.RequestModuleInterface(); - if (userManager != null) - userManager.GetUserName(uuidarr[i]); - - // we drop it. It gets cached though... so we're ready for the next request. - // diva commnent 11/21/2010: uh?!? wft? - // justincc comment 21/01/2011: A side effect of userManager.GetUserName() I presume. - } - } #endregion #region Outgoing Packets diff --git a/OpenSim/Region/Framework/Interfaces/IScriptModule.cs b/OpenSim/Region/Framework/Interfaces/IScriptModule.cs index 18c45dde6e..9cab2e185b 100644 --- a/OpenSim/Region/Framework/Interfaces/IScriptModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IScriptModule.cs @@ -27,6 +27,7 @@ using System; using System.Collections; +using System.Collections.Generic; using OpenMetaverse; namespace OpenSim.Region.Framework.Interfaces @@ -74,5 +75,14 @@ namespace OpenSim.Region.Framework.Interfaces /// Starts the processing threads. /// void StartProcessing(); + + /// + /// Get the execution times of all scripts in each object. + /// + /// + /// A dictionary where the key is the root object ID of a linkset + /// and the value is a representative execution time in milliseconds of all scripts in that linkset. + /// + Dictionary GetObjectScriptsExecutionTimes(); } -} +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index bc3400a7a5..5c542d65a2 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -733,6 +733,7 @@ namespace OpenSim.Region.Framework.Scenes #endregion #region Get Methods + /// /// Get the controlling client for the given avatar, if there is one. /// @@ -1074,36 +1075,6 @@ namespace OpenSim.Region.Framework.Scenes return Entities.GetEntities(); } - public Dictionary GetTopScripts() - { - Dictionary topScripts = new Dictionary(); - - EntityBase[] EntityList = GetEntities(); - int limit = 0; - foreach (EntityBase ent in EntityList) - { - if (ent is SceneObjectGroup) - { - SceneObjectGroup grp = (SceneObjectGroup)ent; - if ((grp.RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0) - { - if (grp.scriptScore >= 0.01) - { - topScripts.Add(grp.LocalId, grp.scriptScore); - limit++; - if (limit >= 100) - { - break; - } - } - grp.scriptScore = 0; - } - } - } - - return topScripts; - } - #endregion #region Other Methods diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 878476e5c9..afb5ccfae5 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -229,8 +229,6 @@ namespace OpenSim.Region.Framework.Scenes get { return RootPart.VolumeDetectActive; } } - public float scriptScore; - private Vector3 lastPhysGroupPos; private Quaternion lastPhysGroupRot; @@ -1184,12 +1182,7 @@ namespace OpenSim.Region.Framework.Scenes public void AddScriptLPS(int count) { - if (scriptScore + count >= float.MaxValue - count) - scriptScore = 0; - - scriptScore += (float)count; - SceneGraph d = m_scene.SceneGraph; - d.AddToScriptLPS(count); + m_scene.SceneGraph.AddToScriptLPS(count); } public void AddActiveScriptCount(int count) diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs index 8762642903..b04f6b6624 100644 --- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs @@ -78,12 +78,38 @@ namespace OpenSim.Region.ScriptEngine.Interfaces /// string State { get; set; } + /// + /// Time the script was last started + /// + DateTime TimeStarted { get; } + + /// + /// Tick the last measurement period was started. + /// + long MeasurementPeriodTickStart { get; } + + /// + /// Ticks spent executing in the last measurement period. + /// + long MeasurementPeriodExecutionTime { get; } + IScriptEngine Engine { get; } UUID AppDomain { get; set; } string PrimName { get; } string ScriptName { get; } UUID ItemID { get; } UUID ObjectID { get; } + + /// + /// UUID of the root object for the linkset that the script is in. + /// + UUID RootObjectID { get; } + + /// + /// Local id of the root object for the linkset that the script is in. + /// + uint RootLocalID { get; } + uint LocalID { get; } UUID AssetID { get; } Queue EventQueue { get; } diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index 968351b450..6e367421c0 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs @@ -164,6 +164,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance public uint LocalID { get; private set; } + public UUID RootObjectID { get; private set; } + + public uint RootLocalID { get; private set; } + public UUID AssetID { get; private set; } public Queue EventQueue { get; private set; } @@ -172,6 +176,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance public TaskInventoryItem ScriptTask { get; private set; } + public DateTime TimeStarted { get; private set; } + + public long MeasurementPeriodTickStart { get; private set; } + + public long MeasurementPeriodExecutionTime { get; private set; } + + public static readonly long MaxMeasurementPeriod = 30 * TimeSpan.TicksPerMinute; + public void ClearQueue() { m_TimerQueued = false; @@ -190,6 +202,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance Engine = engine; LocalID = part.LocalId; ObjectID = part.UUID; + RootLocalID = part.ParentGroup.LocalId; + RootObjectID = part.ParentGroup.UUID; ItemID = itemID; AssetID = assetID; PrimName = primName; @@ -458,6 +472,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance Running = true; + TimeStarted = DateTime.Now; + MeasurementPeriodTickStart = Util.EnvironmentTickCount(); + MeasurementPeriodExecutionTime = 0; + if (EventQueue.Count > 0) { if (m_CurrentWorkItem == null) @@ -710,8 +728,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance m_EventStart = DateTime.Now; m_InEvent = true; + int start = Util.EnvironmentTickCount(); + + // Reset the measurement period when we reach the end of the current one. + if (start - MeasurementPeriodTickStart > MaxMeasurementPeriod) + MeasurementPeriodTickStart = start; + m_Script.ExecuteEvent(State, data.EventName, data.Params); + MeasurementPeriodExecutionTime += Util.EnvironmentTickCount() - start; + m_InEvent = false; m_CurrentEvent = String.Empty; @@ -720,7 +746,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance // This will be the very first event we deliver // (state_entry) in default state // - SaveState(m_Assembly); m_SaveState = false; diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index 105d97da47..3697f78c83 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -1083,7 +1083,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine if (!m_PrimObjects[localID].Contains(itemID)) m_PrimObjects[localID].Add(itemID); - } if (!m_Assemblies.ContainsKey(assetID)) @@ -1891,6 +1890,59 @@ namespace OpenSim.Region.ScriptEngine.XEngine } } + public Dictionary GetObjectScriptsExecutionTimes() + { + long tickNow = Util.EnvironmentTickCount(); + Dictionary topScripts = new Dictionary(); + + lock (m_Scripts) + { + foreach (IScriptInstance si in m_Scripts.Values) + { + if (!topScripts.ContainsKey(si.LocalID)) + topScripts[si.RootLocalID] = 0; + +// long ticksElapsed = tickNow - si.MeasurementPeriodTickStart; +// float framesElapsed = ticksElapsed / (18.1818 * TimeSpan.TicksPerMillisecond); + + // Execution time of the script adjusted by it's measurement period to make scripts started at + // different times comparable. +// float adjustedExecutionTime +// = (float)si.MeasurementPeriodExecutionTime +// / ((float)(tickNow - si.MeasurementPeriodTickStart) / ScriptInstance.MaxMeasurementPeriod) +// / TimeSpan.TicksPerMillisecond; + + long ticksElapsed = tickNow - si.MeasurementPeriodTickStart; + + // Avoid divide by zerp + if (ticksElapsed == 0) + ticksElapsed = 1; + + // Scale execution time to the ideal 55 fps frame time for these reasons. + // + // 1) XEngine does not execute scripts per frame, unlike other script engines. Hence, there is no + // 'script execution time per frame', which is the original purpose of this value. + // + // 2) Giving the raw execution times is misleading since scripts start at different times, making + // it impossible to compare scripts. + // + // 3) Scaling the raw execution time to the time that the script has been running is better but + // is still misleading since a script that has just been rezzed may appear to have been running + // for much longer. + // + // 4) Hence, we scale execution time to an idealised frame time (55 fps). This is also not perfect + // since the figure does not represent actual execution time and very hard running scripts will + // never exceed 18ms (though this is a very high number for script execution so is a warning sign). + float adjustedExecutionTime + = ((float)si.MeasurementPeriodExecutionTime / ticksElapsed) * 18.1818f; + + topScripts[si.RootLocalID] += adjustedExecutionTime; + } + } + + return topScripts; + } + public void SuspendScript(UUID itemID) { // m_log.DebugFormat("[XEngine]: Received request to suspend script with ID {0}", itemID); diff --git a/bin/ScriptEngines/Default.lsl b/bin/ScriptEngines/Default.lsl deleted file mode 100644 index ade855d98f..0000000000 --- a/bin/ScriptEngines/Default.lsl +++ /dev/null @@ -1,104 +0,0 @@ -// autogenerated by generate_default_lsl.rb -integer touch_count = 0; - -default { - touch_start(integer total_number) { - float angle45 = PI/4.0; // 45 degrees - float angle30 = PI/6.0; // 30 degrees - float sqrt2 = llSqrt(2.0); - if((llFabs(-1.5) != 1.5) || (llFabs(10.4) != 10.4)) { - llShout(0, "Houston, we have a big problem! llFabs() does not work! Need it for other tests!"); - } - llSetText("This is a text by llSetText", <1,0,0>, 1); - llWhisper(0, "llWhispering a few random numbers between 0 and 100: " + llFrand(100) + "," + llFrand(100) + "," + llFrand(100) + "," + llFrand(100)); - llShout(0, "llShouting the unix time: " + llGetUnixTime() + ", and region corner: " + llGetRegionCorner()); - llShout(1, "Shouting a random number between 0 and 100 on the channel#1: " + llFrand(100)); - if (llAbs(-1) != 1) { - llSay(0, "Assert failed: llAbs(-1) != 1"); - } - if (llAbs(10) != 10) { - llSay(0, "Assert failed: llAbs(10) != 10"); - } - if (llFabs((llCos(angle45) - sqrt2/2.0) - 0) > 0.000001) { - llSay(0, "Assert failed: (llCos(angle45) - sqrt2/2.0) differs from 0 by more than 0.000001"); - llSay(0, " --> The actual result: " + (llCos(angle45) - sqrt2/2.0)); - } - if (llFabs((llSin(angle30) - 0.5) - 0) > 0.000001) { - llSay(0, "Assert failed: (llSin(angle30) - 0.5) differs from 0 by more than 0.000001"); - llSay(0, " --> The actual result: " + (llSin(angle30) - 0.5)); - } - if (llFabs((llAtan2(1, 1)*4 - PI) - 0) > 0.000001) { - llSay(0, "Assert failed: (llAtan2(1, 1)*4 - PI) differs from 0 by more than 0.000001"); - llSay(0, " --> The actual result: " + (llAtan2(1, 1)*4 - PI)); - } - if (llFabs((llTan(PI)) - 0) > 0.000001) { - llSay(0, "Assert failed: (llTan(PI)) differs from 0 by more than 0.000001"); - llSay(0, " --> The actual result: " + (llTan(PI))); - } - if (llFloor(2.4) != 2) { - llSay(0, "Assert failed: llFloor(2.4) != 2"); - } - if (llCeil(2.4) != 3) { - llSay(0, "Assert failed: llCeil(2.4) != 3"); - } - if (llRound(2.4) != 2) { - llSay(0, "Assert failed: llRound(2.4) != 2"); - } - if (llFloor(2.5) != 2) { - llSay(0, "Assert failed: llFloor(2.5) != 2"); - } - if (llCeil(2.5) != 3) { - llSay(0, "Assert failed: llCeil(2.5) != 3"); - } - if (llRound(2.5) != 3) { - llSay(0, "Assert failed: llRound(2.5) != 3"); - } - if (llFloor(2.51) != 2) { - llSay(0, "Assert failed: llFloor(2.51) != 2"); - } - if (llCeil(2.51) != 3) { - llSay(0, "Assert failed: llCeil(2.51) != 3"); - } - if (llRound(2.51) != 3) { - llSay(0, "Assert failed: llRound(2.51) != 3"); - } - if (llFloor(3.49) != 3) { - llSay(0, "Assert failed: llFloor(3.49) != 3"); - } - if (llCeil(3.49) != 4) { - llSay(0, "Assert failed: llCeil(3.49) != 4"); - } - if (llRound(3.49) != 3) { - llSay(0, "Assert failed: llRound(3.49) != 3"); - } - if (llFloor(3.5000001) != 3) { - llSay(0, "Assert failed: llFloor(3.5000001) != 3"); - } - if (llCeil(3.5000001) != 4) { - llSay(0, "Assert failed: llCeil(3.5000001) != 4"); - } - if (llRound(3.5000001) != 4) { - llSay(0, "Assert failed: llRound(3.5000001) != 4"); - } - if (llFloor(3.51) != 3) { - llSay(0, "Assert failed: llFloor(3.51) != 3"); - } - if (llCeil(3.51) != 4) { - llSay(0, "Assert failed: llCeil(3.51) != 4"); - } - if (llRound(3.51) != 4) { - llSay(0, "Assert failed: llRound(3.51) != 4"); - } - if ((llFabs(0-llPow(2, 16))) != 65536) { - llSay(0, "Assert failed: (llFabs(0-llPow(2, 16))) != 65536"); - } - if (llMD5String("Hello, Avatar!",0) != "112abd47ceaae1c05a826828650434a6") { - llSay(0, "Assert failed: llMD5String('Hello, Avatar!',0) != '112abd47ceaae1c05a826828650434a6'"); - } - if (llModPow(2, 16, 37) != 9) { - llSay(0, "Assert failed: llModPow(2, 16, 37) != 9"); - } - touch_count++; - llSay(0, "Object was touched. Touch count: " + touch_count); - } -} diff --git a/bin/shutdown_commands.txt b/bin/shutdown_commands.txt index ec76ec276d..4397749d23 100644 --- a/bin/shutdown_commands.txt +++ b/bin/shutdown_commands.txt @@ -1 +1,3 @@ -backup +; You can place simulator console commands here to execute when the simulator is shut down +; e.g. show stats +; Lines starting with ; are comments diff --git a/bin/startup_commands.txt b/bin/startup_commands.txt new file mode 100644 index 0000000000..1abfa64c87 --- /dev/null +++ b/bin/startup_commands.txt @@ -0,0 +1,3 @@ +; You can place region console commands here to execute once the simulator has finished starting up +; e.g. show stats +; Lines start with ; are comments. diff --git a/bin/startup_commands.txt.example b/bin/startup_commands.txt.example deleted file mode 100644 index 3dba4b378d..0000000000 --- a/bin/startup_commands.txt.example +++ /dev/null @@ -1,4 +0,0 @@ -terrain load-tile f32 islandterrain_1024x512.raw 512 1024 1000 1000 -terrain multiply 0.1 -terrain add 5 -