diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 40c32145a1..b4a0cb915d 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -1415,82 +1415,195 @@ namespace OpenSim.Region.CoreModules.World.Estate private void HandleLandStatRequest(int parcelID, uint reportType, uint requestFlags, string filter, IClientAPI remoteClient) { - if (!Scene.Permissions.CanIssueEstateCommand(remoteClient.AgentId, false)) - return; - - Dictionary sceneData = null; - Dictionary bytesUsed = null; - Dictionary urlsInUse = null; - if (reportType == 1) { - sceneData = Scene.PhysicsScene.GetTopColliders(); + LandCollidersStatRequest(parcelID, requestFlags, filter, remoteClient); + return; } - else if (reportType == 0) + + if (reportType != 0) + remoteClient.SendLandStatReply(reportType, requestFlags, 0, new LandStatReportItem[0]); ; + + IScriptModule scriptModule = Scene.RequestModuleInterface(); + if (scriptModule == null) + remoteClient.SendLandStatReply(reportType, requestFlags, 0, new LandStatReportItem[0]); ; + + ICollection sceneData = scriptModule.GetTopObjectStats( + 0.001f, 1024, out float totaltime, out float totalmemory); + + if(sceneData == null || sceneData.Count == 0) + remoteClient.SendLandStatReply(0, requestFlags, 0, new LandStatReportItem[0]); + + Dictionary urlsInUse = null; + IUrlModule urlModule = Scene.RequestModuleInterface(); + if(urlModule != null) + urlsInUse = urlModule.GetUrlCountForHosts(); + + //reformat the name so we don't have to do it on every item + bool hasfilter = false; + if ((requestFlags & 0x0c) != 0 && !string.IsNullOrWhiteSpace(filter)) { - IScriptModule scriptModule = Scene.RequestModuleInterface(); - - if (scriptModule != null) + if ((requestFlags & 0x00000002) != 0) { - sceneData = scriptModule.GetObjectScriptsExecutionTimes(); - bytesUsed = scriptModule.GetObjectScriptsBytesUsed(); - } - - IUrlModule urlModule = Scene.RequestModuleInterface(); - if(urlModule != null) - { - urlsInUse = urlModule.GetUrlCountForHosts(); + int indx = filter.IndexOf('.'); + if(indx > 0) + { + string tmp = filter.Substring(0, indx); + if(indx < filter.Length - 1) + { + tmp += " " + filter.Substring(indx +1); + } + filter = tmp; + } } + filter.Trim(); + filter = filter.ToLower(); + hasfilter = filter.Length > 0; } - // hack: reformat the name so we don't have to do it on every item - if((requestFlags & 0x00000002) != 0) - { - filter = string.Join(" ", filter.Split(new[] { '.' }, 2)); - } - - filter = filter.ToLower(); + var sortedSceneData = sceneData.OrderByDescending(e => e.time); + int items = 0; List SceneReport = new List(); - if (sceneData != null) + foreach (var entry in sortedSceneData) { - var sortedSceneData - = sceneData.Select( - item => new { Measurement = item.Value, Part = Scene.GetSceneObjectPart(item.Key) }); + // The object may have been deleted since we received the data. + SceneObjectPart sop = Scene.GetSceneObjectPart(entry.localID); + if(sop == null) + continue; - sortedSceneData.OrderBy(item => item.Measurement); + SceneObjectGroup so = sop.ParentGroup; + if (so == null || so.IsDeleted || so.inTransit) + continue; - int items = 0; - - foreach (var entry in sortedSceneData) + int urls_used = 0; + if (urlsInUse != null) { - // The object may have been deleted since we received the data. - if (entry.Part == null) - continue; - - int bytes_used = 0; - if (bytesUsed.ContainsKey(entry.Part.LocalId)) - bytes_used = bytesUsed[entry.Part.LocalId]; - - int urls_used = 0; - if (urlsInUse.ContainsKey(entry.Part.UUID)) - urls_used = urlsInUse[entry.Part.UUID]; + urlsInUse.TryGetValue(sop.UUID, out urls_used); // Don't show scripts that haven't executed or where execution time is below one microsecond in // order to produce a more readable report. // Unless they are using URLs or using 1024 bytes or more. - if (entry.Measurement < 0.001 && bytes_used < 1024 && urls_used == 0) + if (entry.time < 0.001 && entry.memory < 1024 && urls_used == 0) + continue; + } + else if (entry.time < 0.001 && entry.memory < 1024) continue; - SceneObjectGroup so = entry.Part.ParentGroup; + ILandObject land = Scene.LandChannel.GetLandObject(so.AbsolutePosition); + if((requestFlags & 1) != 0 && land.LandData.LocalID != parcelID) + continue; - ILandObject land = Scene.LandChannel.GetLandObject(entry.Part.AbsolutePosition); + string owner_name = UserManager.GetUserName(so.OwnerID); + string task_name = so.Name; + string parcel_name = land != null ? land.LandData.Name : "unknown"; + + if (hasfilter) + { + if ((requestFlags & 0x00000002) != 0) + { + if (!owner_name.ToLower().Contains(filter)) + continue; + } + else if ((requestFlags & 0x00000004) != 0) + { + if (!task_name.ToLower().Contains(filter)) + continue; + } + else if ((requestFlags & 0x00000008) != 0) + { + if (!parcel_name.ToLower().Contains(filter)) + continue; + } + } + + LandStatReportItem lsri = new LandStatReportItem() + { + LocationX = so.AbsolutePosition.X, + LocationY = so.AbsolutePosition.Y, + LocationZ = so.AbsolutePosition.Z, + Score = entry.time, + TaskID = so.UUID, + TaskLocalID = so.LocalId, + TaskName = task_name, + OwnerName = owner_name, + OwnerID = so.OwnerID, + Bytes = entry.memory, + Urls = urls_used, + Time = Utils.DateTimeToUnixTime(sop.Rezzed), + Parcel = parcel_name + }; + + items++; + SceneReport.Add(lsri); + + if (items >= 100) + break; + } + + remoteClient.SendLandStatReply(0, requestFlags, (uint)SceneReport.Count,SceneReport.ToArray()); + } + + private void LandCollidersStatRequest(int parcelID, uint requestFlags, string filter, IClientAPI remoteClient) + { + if (!Scene.Permissions.CanIssueEstateCommand(remoteClient.AgentId, false)) + remoteClient.SendLandStatReply(1, requestFlags, 0, new LandStatReportItem[0]); + + Dictionary sceneData = Scene.PhysicsScene.GetTopColliders(); + + List SceneReport = new List(); + if (sceneData != null) + { + //reformat the name so we don't have to do it on every item + bool hasfilter = false; + if ((requestFlags & 0x0c) != 0 && !string.IsNullOrWhiteSpace(filter)) + { + if ((requestFlags & 0x00000002) != 0) + { + int indx = filter.IndexOf('.'); + if (indx > 0) + { + string tmp = filter.Substring(0, indx); + if (indx < filter.Length - 1) + { + tmp += " " + filter.Substring(indx + 1); + } + filter = tmp; + } + } + filter.Trim(); + filter = filter.ToLower(); + hasfilter = filter.Length > 0; + } + + int items = 0; + foreach (KeyValuePair kvp in sceneData) + { + if (kvp.Value < 0.001) + continue; + + // The object may have been deleted since we received the data. + SceneObjectPart sop = Scene.GetSceneObjectPart(kvp.Key); + if(sop == null) + continue; + + SceneObjectGroup so = sop.ParentGroup; + if (so == null || so.IsDeleted || so.inTransit) + 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. + // Unless they are using URLs or using 1024 bytes or more. + + ILandObject land = Scene.LandChannel.GetLandObject(so.AbsolutePosition); + if ((requestFlags & 1) != 0 && land.LandData.LocalID != parcelID) + continue; string owner_name = UserManager.GetUserName(so.OwnerID); - string task_name = entry.Part.Name; + string task_name = so.Name; string parcel_name = land != null ? land.LandData.Name : "unknown"; - if (filter.Length != 0 && requestFlags != 0) + if (hasfilter) { if ((requestFlags & 0x00000002) != 0) { @@ -1514,15 +1627,13 @@ namespace OpenSim.Region.CoreModules.World.Estate LocationX = so.AbsolutePosition.X, LocationY = so.AbsolutePosition.Y, LocationZ = so.AbsolutePosition.Z, - Score = entry.Measurement, + Score = kvp.Value, TaskID = so.UUID, TaskLocalID = so.LocalId, TaskName = task_name, OwnerName = owner_name, OwnerID = so.OwnerID, - Bytes = bytes_used, - Urls = urls_used, - Time = Utils.DateTimeToUnixTime(entry.Part.Rezzed), + Time = Utils.DateTimeToUnixTime(sop.Rezzed), Parcel = parcel_name }; @@ -1534,7 +1645,7 @@ namespace OpenSim.Region.CoreModules.World.Estate } } - remoteClient.SendLandStatReply(reportType, requestFlags, (uint)SceneReport.Count,SceneReport.ToArray()); + remoteClient.SendLandStatReply(1, requestFlags, (uint)SceneReport.Count, SceneReport.ToArray()); } #endregion diff --git a/OpenSim/Region/Framework/Interfaces/IScriptModule.cs b/OpenSim/Region/Framework/Interfaces/IScriptModule.cs index bbfdbf36b1..1993529275 100644 --- a/OpenSim/Region/Framework/Interfaces/IScriptModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IScriptModule.cs @@ -35,6 +35,13 @@ namespace OpenSim.Region.Framework.Interfaces public delegate void ScriptRemoved(UUID script); public delegate void ObjectRemoved(UUID prim); + public class ScriptTopStatsData + { + public uint localID; + public float time; + public int memory; + }; + public interface IScriptModule: INonSharedRegionModule { /// @@ -113,12 +120,13 @@ namespace OpenSim.Region.Framework.Interfaces Dictionary GetObjectScriptsExecutionTimes(); /// - /// Get the used memory of all scripts in each object. + /// Get the used memory and exec time of all scripts in each object. + /// if time or memory is higher than min values; /// /// /// A dictionary where the key is the root object ID of a linkset - /// and the value is a amount of bytes being used by the script + /// and the value is a class with amount of bytes being used by the script /// - Dictionary GetObjectScriptsBytesUsed(); + ICollection GetTopObjectStats(float mintime, int minmemory, out float totaltime, out float totalmemory); } } diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index d25ebd26db..7c74fd654c 100755 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -2411,7 +2411,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine { foreach (IScriptInstance si in m_Scripts.Values) { - if (!topScripts.ContainsKey(si.LocalID)) + if (!topScripts.ContainsKey(si.RootLocalID)) topScripts[si.RootLocalID] = 0; topScripts[si.RootLocalID] += GetExectionTime(si); @@ -2511,9 +2511,34 @@ namespace OpenSim.Region.ScriptEngine.XEngine } } - public Dictionary GetObjectScriptsBytesUsed() + public ICollection GetTopObjectStats(float mintime, int minmemory, out float totaltime, out float totalmemory) { - return new Dictionary(); + Dictionary topScripts = new Dictionary(); + totalmemory = 0; + totaltime = 0; + lock (m_Scripts) + { + foreach (IScriptInstance si in m_Scripts.Values) + { + float time = GetExectionTime(si); + totaltime += time; + if(time > mintime) + { + ScriptTopStatsData sd; + if (topScripts.TryGetValue(si.RootLocalID, out sd)) + sd.time += time; + else + { + sd = new ScriptTopStatsData(); + sd.localID = si.RootLocalID; + sd.time = time; + topScripts[si.RootLocalID] = sd; + } + } + } + } + return topScripts.Values; } + } } diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs b/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs index c823d18acb..a33188b903 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs @@ -1875,24 +1875,43 @@ namespace OpenSim.Region.ScriptEngine.Yengine } /** - * @brief Return a list of all script used bytes + * @brief Return a list of object script used bytes and time */ - public Dictionary GetObjectScriptsBytesUsed() + + public ICollection GetTopObjectStats(float mintime, int minmemory, out float totaltime, out float totalmemory) { - Dictionary topScripts = new Dictionary(); + Dictionary topScripts = new Dictionary(); + totalmemory = 0; + totaltime = 0; lock (m_InstancesDict) { foreach (XMRInstance instance in m_InstancesDict.Values) { uint rootLocalID = instance.m_Part.ParentGroup.LocalId; - int oldTotal; - if (!topScripts.TryGetValue(rootLocalID, out oldTotal)) - oldTotal = 0; - - topScripts[rootLocalID] = instance.xmrHeapUsed() + oldTotal; + float time = (float)instance.m_CPUTime; + totaltime += time; + int mem = instance.xmrHeapUsed(); + totalmemory += mem; + if (time > mintime || mem > minmemory) + { + ScriptTopStatsData sd; + if (topScripts.TryGetValue(rootLocalID, out sd)) + { + sd.time += time; + sd.memory += mem; + } + else + { + sd = new ScriptTopStatsData(); + sd.localID = rootLocalID; + sd.time = time; + sd.memory = mem; + topScripts[rootLocalID] = sd; + } + } } } - return topScripts; + return topScripts.Values; } /** diff --git a/OpenSim/Tests/Common/Mock/MockScriptEngine.cs b/OpenSim/Tests/Common/Mock/MockScriptEngine.cs index 2cc731a082..63c8f94774 100644 --- a/OpenSim/Tests/Common/Mock/MockScriptEngine.cs +++ b/OpenSim/Tests/Common/Mock/MockScriptEngine.cs @@ -274,9 +274,11 @@ namespace OpenSim.Tests.Common { } - public Dictionary GetObjectScriptsBytesUsed() + public ICollection GetTopObjectStats(float mintime, int minmemory, out float totaltime, out float totalmemory) { - return new Dictionary(); + totaltime = 0; + totalmemory = 0; + return null; } } }