aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/XEngine/XEngine.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/XEngine.cs362
1 files changed, 236 insertions, 126 deletions
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
index 1e0f01f..7364b19 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
@@ -63,6 +63,14 @@ namespace OpenSim.Region.ScriptEngine.XEngine
63 { 63 {
64 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 64 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
65 65
66 /// <summary>
67 /// Control the printing of certain debug messages.
68 /// </summary>
69 /// <remarks>
70 /// If DebugLevel >= 1, then we log every time that a script is started.
71 /// </remarks>
72// public int DebugLevel { get; set; }
73
66 private SmartThreadPool m_ThreadPool; 74 private SmartThreadPool m_ThreadPool;
67 private int m_MaxScriptQueue; 75 private int m_MaxScriptQueue;
68 private Scene m_Scene; 76 private Scene m_Scene;
@@ -284,9 +292,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
284 AppDomain.CurrentDomain.AssemblyResolve += 292 AppDomain.CurrentDomain.AssemblyResolve +=
285 OnAssemblyResolve; 293 OnAssemblyResolve;
286 294
287 m_log.InfoFormat("[XEngine] Initializing scripts in region {0}",
288 scene.RegionInfo.RegionName);
289 m_Scene = scene; 295 m_Scene = scene;
296 m_log.InfoFormat("[XEngine]: Initializing scripts in region {0}", m_Scene.RegionInfo.RegionName);
290 297
291 m_MinThreads = m_ScriptConfig.GetInt("MinThreads", 2); 298 m_MinThreads = m_ScriptConfig.GetInt("MinThreads", 2);
292 m_MaxThreads = m_ScriptConfig.GetInt("MaxThreads", 100); 299 m_MaxThreads = m_ScriptConfig.GetInt("MaxThreads", 100);
@@ -389,9 +396,42 @@ namespace OpenSim.Region.ScriptEngine.XEngine
389 "Starts all stopped scripts." 396 "Starts all stopped scripts."
390 + "If a <script-item-uuid> is given then only that script will be started. Otherwise, all suitable scripts are started.", 397 + "If a <script-item-uuid> is given then only that script will be started. Otherwise, all suitable scripts are started.",
391 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript)); 398 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript));
399
400// MainConsole.Instance.Commands.AddCommand(
401// "Debug", false, "debug xengine", "debug xengine [<level>]",
402// "Turn on detailed xengine debugging.",
403// "If level <= 0, then no extra logging is done.\n"
404// + "If level >= 1, then we log every time that a script is started.",
405// HandleDebugLevelCommand);
392 } 406 }
393 407
394 /// <summary> 408 /// <summary>
409 /// Change debug level
410 /// </summary>
411 /// <param name="module"></param>
412 /// <param name="args"></param>
413// private void HandleDebugLevelCommand(string module, string[] args)
414// {
415// if (args.Length == 3)
416// {
417// int newDebug;
418// if (int.TryParse(args[2], out newDebug))
419// {
420// DebugLevel = newDebug;
421// MainConsole.Instance.OutputFormat("Debug level set to {0}", newDebug);
422// }
423// }
424// else if (args.Length == 2)
425// {
426// MainConsole.Instance.OutputFormat("Current debug level is {0}", DebugLevel);
427// }
428// else
429// {
430// MainConsole.Instance.Output("Usage: debug xengine 0..1");
431// }
432// }
433
434 /// <summary>
395 /// Parse the raw item id into a script instance from the command params if it's present. 435 /// Parse the raw item id into a script instance from the command params if it's present.
396 /// </summary> 436 /// </summary>
397 /// <param name="cmdparams"></param> 437 /// <param name="cmdparams"></param>
@@ -795,35 +835,66 @@ namespace OpenSim.Region.ScriptEngine.XEngine
795 int colon = firstline.IndexOf(':'); 835 int colon = firstline.IndexOf(':');
796 if (firstline.Length > 2 && firstline.Substring(0, 2) == "//" && colon != -1) 836 if (firstline.Length > 2 && firstline.Substring(0, 2) == "//" && colon != -1)
797 { 837 {
798 string engineName = firstline.Substring(2, colon-2); 838 string engineName = firstline.Substring(2, colon - 2);
799 839
800 if (names.Contains(engineName)) 840 if (names.Contains(engineName))
801 { 841 {
802 engine = engineName; 842 engine = engineName;
803 script = "//" + script.Substring(script.IndexOf(':')+1); 843 script = "//" + script.Substring(colon + 1);
804 } 844 }
805 else 845 else
806 { 846 {
807 if (engine == ScriptEngineName) 847 if (engine == ScriptEngineName)
808 { 848 {
809 SceneObjectPart part = 849 // If we are falling back on XEngine as the default engine, then only complain to the user
810 m_Scene.GetSceneObjectPart( 850 // if a script language has been explicitly set and it's one that we recognize or there are
811 localID); 851 // no non-whitespace characters after the colon.
812 852 //
813 TaskInventoryItem item = 853 // If the script is
814 part.Inventory.GetInventoryItem(itemID); 854 // explicitly not allowed or the script is not in LSL then the user will be informed by a later compiler message.
815 855 //
816 ScenePresence presence = 856 // If the colon ends the line then we'll risk the false positive as this is more likely
817 m_Scene.GetScenePresence( 857 // to signal a real scriptengine line where the user wants to use the default compile language.
818 item.OwnerID); 858 //
819 859 // This avoids the overwhelming number of false positives where we're in this code because
820 if (presence != null) 860 // there's a colon in a comment in the first line of a script for entirely
861 // unrelated reasons (e.g. vim settings).
862 //
863 // TODO: A better fix would be to deprecate simple : detection and look for some less likely
864 // string to begin the comment (like #! in unix shell scripts).
865 bool warnRunningInXEngine = false;
866 string restOfFirstLine = firstline.Substring(colon + 1);
867
868 // FIXME: These are hardcoded because they are currently hardcoded in Compiler.cs
869 if (restOfFirstLine.StartsWith("c#")
870 || restOfFirstLine.StartsWith("vb")
871 || restOfFirstLine.StartsWith("lsl")
872 || restOfFirstLine.StartsWith("js")
873 || restOfFirstLine.StartsWith("yp")
874 || restOfFirstLine.Length == 0)
875 warnRunningInXEngine = true;
876
877 if (warnRunningInXEngine)
821 { 878 {
822 presence.ControllingClient.SendAgentAlertMessage( 879 SceneObjectPart part =
823 "Selected engine unavailable. "+ 880 m_Scene.GetSceneObjectPart(
824 "Running script on "+ 881 localID);
825 ScriptEngineName, 882
826 false); 883 TaskInventoryItem item =
884 part.Inventory.GetInventoryItem(itemID);
885
886 ScenePresence presence =
887 m_Scene.GetScenePresence(
888 item.OwnerID);
889
890 if (presence != null)
891 {
892 presence.ControllingClient.SendAgentAlertMessage(
893 "Selected engine unavailable. "+
894 "Running script on "+
895 ScriptEngineName,
896 false);
897 }
827 } 898 }
828 } 899 }
829 } 900 }
@@ -880,20 +951,31 @@ namespace OpenSim.Region.ScriptEngine.XEngine
880 { 951 {
881 if (m_InitialStartup) 952 if (m_InitialStartup)
882 { 953 {
883 m_InitialStartup = false; 954 // This delay exists to stop mono problems where script compilation and startup would stop the sim
955 // working properly for the session.
884 System.Threading.Thread.Sleep(15000); 956 System.Threading.Thread.Sleep(15000);
957 }
958
959 object[] o;
960
961 int scriptsStarted = 0;
885 962
886 if (m_CompileQueue.Count == 0) 963 while (m_CompileQueue.Dequeue(out o))
964 {
965 if (DoOnRezScript(o))
887 { 966 {
888 // No scripts on region, so won't get triggered later 967 scriptsStarted++;
889 // by the queue becoming empty so we trigger it here 968
890 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(0, String.Empty); 969 if (m_InitialStartup)
970 if (scriptsStarted % 50 == 0)
971 m_log.InfoFormat(
972 "[XEngine]: Started {0} scripts in {1}", scriptsStarted, m_Scene.RegionInfo.RegionName);
891 } 973 }
892 } 974 }
893 975
894 object[] o; 976 if (m_InitialStartup)
895 while (m_CompileQueue.Dequeue(out o)) 977 m_log.InfoFormat(
896 DoOnRezScript(o); 978 "[XEngine]: Completed starting {0} scripts on {1}", scriptsStarted, m_Scene.RegionInfo.RegionName);
897 979
898 // NOTE: Despite having a lockless queue, this lock is required 980 // NOTE: Despite having a lockless queue, this lock is required
899 // to make sure there is never no compile thread while there 981 // to make sure there is never no compile thread while there
@@ -901,12 +983,13 @@ namespace OpenSim.Region.ScriptEngine.XEngine
901 // due to a race condition 983 // due to a race condition
902 // 984 //
903 lock (m_CompileQueue) 985 lock (m_CompileQueue)
904 {
905 m_CurrentCompile = null; 986 m_CurrentCompile = null;
906 } 987
907 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(m_ScriptFailCount, 988 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(m_ScriptFailCount,
908 m_ScriptErrorMessage); 989 m_ScriptErrorMessage);
990
909 m_ScriptFailCount = 0; 991 m_ScriptFailCount = 0;
992 m_InitialStartup = false;
910 993
911 return null; 994 return null;
912 } 995 }
@@ -1089,11 +1172,18 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1089 1172
1090 AppDomain sandbox; 1173 AppDomain sandbox;
1091 if (m_AppDomainLoading) 1174 if (m_AppDomainLoading)
1175 {
1092 sandbox = AppDomain.CreateDomain( 1176 sandbox = AppDomain.CreateDomain(
1093 m_Scene.RegionInfo.RegionID.ToString(), 1177 m_Scene.RegionInfo.RegionID.ToString(),
1094 evidence, appSetup); 1178 evidence, appSetup);
1179 m_AppDomains[appDomain].AssemblyResolve +=
1180 new ResolveEventHandler(
1181 AssemblyResolver.OnAssemblyResolve);
1182 }
1095 else 1183 else
1184 {
1096 sandbox = AppDomain.CurrentDomain; 1185 sandbox = AppDomain.CurrentDomain;
1186 }
1097 1187
1098 //PolicyLevel sandboxPolicy = PolicyLevel.CreateAppDomainLevel(); 1188 //PolicyLevel sandboxPolicy = PolicyLevel.CreateAppDomainLevel();
1099 //AllMembershipCondition sandboxMembershipCondition = new AllMembershipCondition(); 1189 //AllMembershipCondition sandboxMembershipCondition = new AllMembershipCondition();
@@ -1105,9 +1195,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1105 1195
1106 m_AppDomains[appDomain] = sandbox; 1196 m_AppDomains[appDomain] = sandbox;
1107 1197
1108 m_AppDomains[appDomain].AssemblyResolve +=
1109 new ResolveEventHandler(
1110 AssemblyResolver.OnAssemblyResolve);
1111 m_DomainScripts[appDomain] = new List<UUID>(); 1198 m_DomainScripts[appDomain] = new List<UUID>();
1112 } 1199 }
1113 catch (Exception e) 1200 catch (Exception e)
@@ -1392,25 +1479,24 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1392 return false; 1479 return false;
1393 1480
1394 uuids = m_PrimObjects[localID]; 1481 uuids = m_PrimObjects[localID];
1395
1396 1482
1397 foreach (UUID itemID in uuids) 1483 foreach (UUID itemID in uuids)
1398 {
1399 IScriptInstance instance = null;
1400 try
1401 {
1402 if (m_Scripts.ContainsKey(itemID))
1403 instance = m_Scripts[itemID];
1404 }
1405 catch { /* ignore race conditions */ }
1406
1407 if (instance != null)
1408 { 1484 {
1409 instance.PostEvent(p); 1485 IScriptInstance instance = null;
1410 result = true; 1486 try
1487 {
1488 if (m_Scripts.ContainsKey(itemID))
1489 instance = m_Scripts[itemID];
1490 }
1491 catch { /* ignore race conditions */ }
1492
1493 if (instance != null)
1494 {
1495 instance.PostEvent(p);
1496 result = true;
1497 }
1411 } 1498 }
1412 } 1499 }
1413 }
1414 1500
1415 return result; 1501 return result;
1416 } 1502 }
@@ -1535,6 +1621,13 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1535 } 1621 }
1536 } 1622 }
1537 1623
1624 public void SetRunEnable(UUID instanceID, bool enable)
1625 {
1626 IScriptInstance instance = GetInstance(instanceID);
1627 if (instance != null)
1628 instance.Run = enable;
1629 }
1630
1538 public bool GetScriptState(UUID itemID) 1631 public bool GetScriptState(UUID itemID)
1539 { 1632 {
1540 IScriptInstance instance = GetInstance(itemID); 1633 IScriptInstance instance = GetInstance(itemID);
@@ -1745,14 +1838,15 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1745 FileMode.Open, FileAccess.Read)) 1838 FileMode.Open, FileAccess.Read))
1746 { 1839 {
1747 tfs.Read(tdata, 0, tdata.Length); 1840 tfs.Read(tdata, 0, tdata.Length);
1748 tfs.Close();
1749 } 1841 }
1750 1842
1751 assem = new System.Text.ASCIIEncoding().GetString(tdata); 1843 assem = new System.Text.ASCIIEncoding().GetString(tdata);
1752 } 1844 }
1753 catch (Exception e) 1845 catch (Exception e)
1754 { 1846 {
1755 m_log.DebugFormat("[XEngine]: Unable to open script textfile {0}, reason: {1}", assemName+".text", e.Message); 1847 m_log.ErrorFormat(
1848 "[XEngine]: Unable to open script textfile {0}{1}, reason: {2}",
1849 assemName, ".text", e.Message);
1756 } 1850 }
1757 } 1851 }
1758 } 1852 }
@@ -1769,16 +1863,15 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1769 using (FileStream fs = File.Open(assemName, FileMode.Open, FileAccess.Read)) 1863 using (FileStream fs = File.Open(assemName, FileMode.Open, FileAccess.Read))
1770 { 1864 {
1771 fs.Read(data, 0, data.Length); 1865 fs.Read(data, 0, data.Length);
1772 fs.Close();
1773 } 1866 }
1774 1867
1775 assem = System.Convert.ToBase64String(data); 1868 assem = System.Convert.ToBase64String(data);
1776 } 1869 }
1777 catch (Exception e) 1870 catch (Exception e)
1778 { 1871 {
1779 m_log.DebugFormat("[XEngine]: Unable to open script assembly {0}, reason: {1}", assemName, e.Message); 1872 m_log.ErrorFormat(
1873 "[XEngine]: Unable to open script assembly {0}, reason: {1}", assemName, e.Message);
1780 } 1874 }
1781
1782 } 1875 }
1783 } 1876 }
1784 1877
@@ -1791,9 +1884,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1791 using (StreamReader msr = new StreamReader(mfs)) 1884 using (StreamReader msr = new StreamReader(mfs))
1792 { 1885 {
1793 map = msr.ReadToEnd(); 1886 map = msr.ReadToEnd();
1794 msr.Close();
1795 } 1887 }
1796 mfs.Close();
1797 } 1888 }
1798 } 1889 }
1799 1890
@@ -1829,6 +1920,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1829 1920
1830 public bool SetXMLState(UUID itemID, string xml) 1921 public bool SetXMLState(UUID itemID, string xml)
1831 { 1922 {
1923// m_log.DebugFormat("[XEngine]: Writing state for script item with ID {0}", itemID);
1924
1832 if (xml == String.Empty) 1925 if (xml == String.Empty)
1833 return false; 1926 return false;
1834 1927
@@ -1889,31 +1982,61 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1889 { 1982 {
1890 using (FileStream fs = File.Create(path)) 1983 using (FileStream fs = File.Create(path))
1891 { 1984 {
1985// m_log.DebugFormat("[XEngine]: Writing assembly file {0}", path);
1986
1892 fs.Write(filedata, 0, filedata.Length); 1987 fs.Write(filedata, 0, filedata.Length);
1893 fs.Close();
1894 } 1988 }
1895 } 1989 }
1896 catch (IOException ex) 1990 catch (IOException ex)
1897 { 1991 {
1898 // if there already exists a file at that location, it may be locked. 1992 // if there already exists a file at that location, it may be locked.
1899 m_log.ErrorFormat("[XEngine]: File {0} already exists! {1}", path, ex.Message); 1993 m_log.ErrorFormat("[XEngine]: Error whilst writing assembly file {0}, {1}", path, ex.Message);
1900 } 1994 }
1995
1996 string textpath = path + ".text";
1901 try 1997 try
1902 { 1998 {
1903 using (FileStream fs = File.Create(path + ".text")) 1999 using (FileStream fs = File.Create(textpath))
1904 { 2000 {
1905 using (StreamWriter sw = new StreamWriter(fs)) 2001 using (StreamWriter sw = new StreamWriter(fs))
1906 { 2002 {
2003// m_log.DebugFormat("[XEngine]: Writing .text file {0}", textpath);
2004
1907 sw.Write(base64); 2005 sw.Write(base64);
1908 sw.Close();
1909 } 2006 }
1910 fs.Close();
1911 } 2007 }
1912 } 2008 }
1913 catch (IOException ex) 2009 catch (IOException ex)
1914 { 2010 {
1915 // if there already exists a file at that location, it may be locked. 2011 // if there already exists a file at that location, it may be locked.
1916 m_log.ErrorFormat("[XEngine]: File {0} already exists! {1}", path, ex.Message); 2012 m_log.ErrorFormat("[XEngine]: Error whilst writing .text file {0}, {1}", textpath, ex.Message);
2013 }
2014 }
2015
2016 XmlNodeList mapL = rootE.GetElementsByTagName("LineMap");
2017 if (mapL.Count > 0)
2018 {
2019 XmlElement mapE = (XmlElement)mapL[0];
2020
2021 string mappath = Path.Combine(m_ScriptEnginesPath, World.RegionInfo.RegionID.ToString());
2022 mappath = Path.Combine(mappath, mapE.GetAttribute("Filename"));
2023
2024 try
2025 {
2026 using (FileStream mfs = File.Create(mappath))
2027 {
2028 using (StreamWriter msw = new StreamWriter(mfs))
2029 {
2030 // m_log.DebugFormat("[XEngine]: Writing linemap file {0}", mappath);
2031
2032 msw.Write(mapE.InnerText);
2033 }
2034 }
2035 }
2036 catch (IOException ex)
2037 {
2038 // if there already exists a file at that location, it may be locked.
2039 m_log.ErrorFormat("[XEngine]: Linemap file {0} already exists! {1}", mappath, ex.Message);
1917 } 2040 }
1918 } 2041 }
1919 } 2042 }
@@ -1927,43 +2050,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1927 { 2050 {
1928 using (StreamWriter ssw = new StreamWriter(sfs)) 2051 using (StreamWriter ssw = new StreamWriter(sfs))
1929 { 2052 {
2053// m_log.DebugFormat("[XEngine]: Writing state file {0}", statepath);
2054
1930 ssw.Write(stateE.OuterXml); 2055 ssw.Write(stateE.OuterXml);
1931 ssw.Close();
1932 } 2056 }
1933 sfs.Close();
1934 } 2057 }
1935 } 2058 }
1936 catch (IOException ex) 2059 catch (IOException ex)
1937 { 2060 {
1938 // if there already exists a file at that location, it may be locked. 2061 // if there already exists a file at that location, it may be locked.
1939 m_log.ErrorFormat("[XEngine]: File {0} already exists! {1}", statepath, ex.Message); 2062 m_log.ErrorFormat("[XEngine]: Error whilst writing state file {0}, {1}", statepath, ex.Message);
1940 }
1941
1942 XmlNodeList mapL = rootE.GetElementsByTagName("LineMap");
1943 if (mapL.Count > 0)
1944 {
1945 XmlElement mapE = (XmlElement)mapL[0];
1946
1947 string mappath = Path.Combine(m_ScriptEnginesPath, World.RegionInfo.RegionID.ToString());
1948 mappath = Path.Combine(mappath, mapE.GetAttribute("Filename"));
1949
1950 try
1951 {
1952 using (FileStream mfs = File.Create(mappath))
1953 {
1954 using (StreamWriter msw = new StreamWriter(mfs))
1955 {
1956 msw.Write(mapE.InnerText);
1957 msw.Close();
1958 }
1959 mfs.Close();
1960 }
1961 }
1962 catch (IOException ex)
1963 {
1964 // if there already exists a file at that location, it may be locked.
1965 m_log.ErrorFormat("[XEngine]: File {0} already exists! {1}", statepath, ex.Message);
1966 }
1967 } 2063 }
1968 2064
1969 return true; 2065 return true;
@@ -1997,45 +2093,59 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1997 if (!topScripts.ContainsKey(si.LocalID)) 2093 if (!topScripts.ContainsKey(si.LocalID))
1998 topScripts[si.RootLocalID] = 0; 2094 topScripts[si.RootLocalID] = 0;
1999 2095
2000// long ticksElapsed = tickNow - si.MeasurementPeriodTickStart; 2096 topScripts[si.RootLocalID] += CalculateAdjustedExectionTime(si, tickNow);
2001// float framesElapsed = ticksElapsed / (18.1818 * TimeSpan.TicksPerMillisecond); 2097 }
2002 2098 }
2003 // Execution time of the script adjusted by it's measurement period to make scripts started at
2004 // different times comparable.
2005// float adjustedExecutionTime
2006// = (float)si.MeasurementPeriodExecutionTime
2007// / ((float)(tickNow - si.MeasurementPeriodTickStart) / ScriptInstance.MaxMeasurementPeriod)
2008// / TimeSpan.TicksPerMillisecond;
2009
2010 long ticksElapsed = tickNow - si.MeasurementPeriodTickStart;
2011
2012 // Avoid divide by zerp
2013 if (ticksElapsed == 0)
2014 ticksElapsed = 1;
2015 2099
2016 // Scale execution time to the ideal 55 fps frame time for these reasons. 2100 return topScripts;
2017 // 2101 }
2018 // 1) XEngine does not execute scripts per frame, unlike other script engines. Hence, there is no
2019 // 'script execution time per frame', which is the original purpose of this value.
2020 //
2021 // 2) Giving the raw execution times is misleading since scripts start at different times, making
2022 // it impossible to compare scripts.
2023 //
2024 // 3) Scaling the raw execution time to the time that the script has been running is better but
2025 // is still misleading since a script that has just been rezzed may appear to have been running
2026 // for much longer.
2027 //
2028 // 4) Hence, we scale execution time to an idealised frame time (55 fps). This is also not perfect
2029 // since the figure does not represent actual execution time and very hard running scripts will
2030 // never exceed 18ms (though this is a very high number for script execution so is a warning sign).
2031 float adjustedExecutionTime
2032 = ((float)si.MeasurementPeriodExecutionTime / ticksElapsed) * 18.1818f;
2033 2102
2034 topScripts[si.RootLocalID] += adjustedExecutionTime; 2103 public float GetScriptExecutionTime(List<UUID> itemIDs)
2104 {
2105 if (itemIDs == null|| itemIDs.Count == 0)
2106 {
2107 return 0.0f;
2108 }
2109 float time = 0.0f;
2110 long tickNow = Util.EnvironmentTickCount();
2111 IScriptInstance si;
2112 // Calculate the time for all scripts that this engine is executing
2113 // Ignore any others
2114 foreach (UUID id in itemIDs)
2115 {
2116 si = GetInstance(id);
2117 if (si != null && si.Running)
2118 {
2119 time += CalculateAdjustedExectionTime(si, tickNow);
2035 } 2120 }
2036 } 2121 }
2122 return time;
2123 }
2037 2124
2038 return topScripts; 2125 private float CalculateAdjustedExectionTime(IScriptInstance si, long tickNow)
2126 {
2127 long ticksElapsed = tickNow - si.MeasurementPeriodTickStart;
2128
2129 // Avoid divide by zero
2130 if (ticksElapsed == 0)
2131 ticksElapsed = 1;
2132
2133 // Scale execution time to the ideal 55 fps frame time for these reasons.
2134 //
2135 // 1) XEngine does not execute scripts per frame, unlike other script engines. Hence, there is no
2136 // 'script execution time per frame', which is the original purpose of this value.
2137 //
2138 // 2) Giving the raw execution times is misleading since scripts start at different times, making
2139 // it impossible to compare scripts.
2140 //
2141 // 3) Scaling the raw execution time to the time that the script has been running is better but
2142 // is still misleading since a script that has just been rezzed may appear to have been running
2143 // for much longer.
2144 //
2145 // 4) Hence, we scale execution time to an idealised frame time (55 fps). This is also not perfect
2146 // since the figure does not represent actual execution time and very hard running scripts will
2147 // never exceed 18ms (though this is a very high number for script execution so is a warning sign).
2148 return ((float)si.MeasurementPeriodExecutionTime / ticksElapsed) * 18.1818f;
2039 } 2149 }
2040 2150
2041 public void SuspendScript(UUID itemID) 2151 public void SuspendScript(UUID itemID)