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.cs368
1 files changed, 241 insertions, 127 deletions
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
index 2a01fc4..cc783aa 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>
@@ -799,35 +839,66 @@ namespace OpenSim.Region.ScriptEngine.XEngine
799 int colon = firstline.IndexOf(':'); 839 int colon = firstline.IndexOf(':');
800 if (firstline.Length > 2 && firstline.Substring(0, 2) == "//" && colon != -1) 840 if (firstline.Length > 2 && firstline.Substring(0, 2) == "//" && colon != -1)
801 { 841 {
802 string engineName = firstline.Substring(2, colon-2); 842 string engineName = firstline.Substring(2, colon - 2);
803 843
804 if (names.Contains(engineName)) 844 if (names.Contains(engineName))
805 { 845 {
806 engine = engineName; 846 engine = engineName;
807 script = "//" + script.Substring(script.IndexOf(':')+1); 847 script = "//" + script.Substring(colon + 1);
808 } 848 }
809 else 849 else
810 { 850 {
811 if (engine == ScriptEngineName) 851 if (engine == ScriptEngineName)
812 { 852 {
813 SceneObjectPart part = 853 // If we are falling back on XEngine as the default engine, then only complain to the user
814 m_Scene.GetSceneObjectPart( 854 // if a script language has been explicitly set and it's one that we recognize or there are
815 localID); 855 // no non-whitespace characters after the colon.
816 856 //
817 TaskInventoryItem item = 857 // If the script is
818 part.Inventory.GetInventoryItem(itemID); 858 // explicitly not allowed or the script is not in LSL then the user will be informed by a later compiler message.
819 859 //
820 ScenePresence presence = 860 // If the colon ends the line then we'll risk the false positive as this is more likely
821 m_Scene.GetScenePresence( 861 // to signal a real scriptengine line where the user wants to use the default compile language.
822 item.OwnerID); 862 //
823 863 // This avoids the overwhelming number of false positives where we're in this code because
824 if (presence != null) 864 // there's a colon in a comment in the first line of a script for entirely
865 // unrelated reasons (e.g. vim settings).
866 //
867 // TODO: A better fix would be to deprecate simple : detection and look for some less likely
868 // string to begin the comment (like #! in unix shell scripts).
869 bool warnRunningInXEngine = false;
870 string restOfFirstLine = firstline.Substring(colon + 1);
871
872 // FIXME: These are hardcoded because they are currently hardcoded in Compiler.cs
873 if (restOfFirstLine.StartsWith("c#")
874 || restOfFirstLine.StartsWith("vb")
875 || restOfFirstLine.StartsWith("lsl")
876 || restOfFirstLine.StartsWith("js")
877 || restOfFirstLine.StartsWith("yp")
878 || restOfFirstLine.Length == 0)
879 warnRunningInXEngine = true;
880
881 if (warnRunningInXEngine)
825 { 882 {
826 presence.ControllingClient.SendAgentAlertMessage( 883 SceneObjectPart part =
827 "Selected engine unavailable. "+ 884 m_Scene.GetSceneObjectPart(
828 "Running script on "+ 885 localID);
829 ScriptEngineName, 886
830 false); 887 TaskInventoryItem item =
888 part.Inventory.GetInventoryItem(itemID);
889
890 ScenePresence presence =
891 m_Scene.GetScenePresence(
892 item.OwnerID);
893
894 if (presence != null)
895 {
896 presence.ControllingClient.SendAgentAlertMessage(
897 "Selected engine unavailable. "+
898 "Running script on "+
899 ScriptEngineName,
900 false);
901 }
831 } 902 }
832 } 903 }
833 } 904 }
@@ -884,20 +955,31 @@ namespace OpenSim.Region.ScriptEngine.XEngine
884 { 955 {
885 if (m_InitialStartup) 956 if (m_InitialStartup)
886 { 957 {
887 m_InitialStartup = false; 958 // This delay exists to stop mono problems where script compilation and startup would stop the sim
959 // working properly for the session.
888 System.Threading.Thread.Sleep(15000); 960 System.Threading.Thread.Sleep(15000);
961 }
889 962
890 if (m_CompileQueue.Count == 0) 963 object[] o;
964
965 int scriptsStarted = 0;
966
967 while (m_CompileQueue.Dequeue(out o))
968 {
969 if (DoOnRezScript(o))
891 { 970 {
892 // No scripts on region, so won't get triggered later 971 scriptsStarted++;
893 // by the queue becoming empty so we trigger it here 972
894 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(0, String.Empty); 973 if (m_InitialStartup)
974 if (scriptsStarted % 50 == 0)
975 m_log.InfoFormat(
976 "[XEngine]: Started {0} scripts in {1}", scriptsStarted, m_Scene.RegionInfo.RegionName);
895 } 977 }
896 } 978 }
897 979
898 object[] o; 980 if (m_InitialStartup)
899 while (m_CompileQueue.Dequeue(out o)) 981 m_log.InfoFormat(
900 DoOnRezScript(o); 982 "[XEngine]: Completed starting {0} scripts on {1}", scriptsStarted, m_Scene.RegionInfo.RegionName);
901 983
902 // NOTE: Despite having a lockless queue, this lock is required 984 // NOTE: Despite having a lockless queue, this lock is required
903 // to make sure there is never no compile thread while there 985 // to make sure there is never no compile thread while there
@@ -905,12 +987,13 @@ namespace OpenSim.Region.ScriptEngine.XEngine
905 // due to a race condition 987 // due to a race condition
906 // 988 //
907 lock (m_CompileQueue) 989 lock (m_CompileQueue)
908 {
909 m_CurrentCompile = null; 990 m_CurrentCompile = null;
910 } 991
911 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(m_ScriptFailCount, 992 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(m_ScriptFailCount,
912 m_ScriptErrorMessage); 993 m_ScriptErrorMessage);
994
913 m_ScriptFailCount = 0; 995 m_ScriptFailCount = 0;
996 m_InitialStartup = false;
914 997
915 return null; 998 return null;
916 } 999 }
@@ -1093,11 +1176,18 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1093 1176
1094 AppDomain sandbox; 1177 AppDomain sandbox;
1095 if (m_AppDomainLoading) 1178 if (m_AppDomainLoading)
1179 {
1096 sandbox = AppDomain.CreateDomain( 1180 sandbox = AppDomain.CreateDomain(
1097 m_Scene.RegionInfo.RegionID.ToString(), 1181 m_Scene.RegionInfo.RegionID.ToString(),
1098 evidence, appSetup); 1182 evidence, appSetup);
1183 m_AppDomains[appDomain].AssemblyResolve +=
1184 new ResolveEventHandler(
1185 AssemblyResolver.OnAssemblyResolve);
1186 }
1099 else 1187 else
1188 {
1100 sandbox = AppDomain.CurrentDomain; 1189 sandbox = AppDomain.CurrentDomain;
1190 }
1101 1191
1102 //PolicyLevel sandboxPolicy = PolicyLevel.CreateAppDomainLevel(); 1192 //PolicyLevel sandboxPolicy = PolicyLevel.CreateAppDomainLevel();
1103 //AllMembershipCondition sandboxMembershipCondition = new AllMembershipCondition(); 1193 //AllMembershipCondition sandboxMembershipCondition = new AllMembershipCondition();
@@ -1109,9 +1199,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1109 1199
1110 m_AppDomains[appDomain] = sandbox; 1200 m_AppDomains[appDomain] = sandbox;
1111 1201
1112 m_AppDomains[appDomain].AssemblyResolve +=
1113 new ResolveEventHandler(
1114 AssemblyResolver.OnAssemblyResolve);
1115 m_DomainScripts[appDomain] = new List<UUID>(); 1202 m_DomainScripts[appDomain] = new List<UUID>();
1116 } 1203 }
1117 catch (Exception e) 1204 catch (Exception e)
@@ -1396,25 +1483,24 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1396 return false; 1483 return false;
1397 1484
1398 uuids = m_PrimObjects[localID]; 1485 uuids = m_PrimObjects[localID];
1399
1400 1486
1401 foreach (UUID itemID in uuids) 1487 foreach (UUID itemID in uuids)
1402 {
1403 IScriptInstance instance = null;
1404 try
1405 { 1488 {
1406 if (m_Scripts.ContainsKey(itemID)) 1489 IScriptInstance instance = null;
1407 instance = m_Scripts[itemID]; 1490 try
1408 } 1491 {
1409 catch { /* ignore race conditions */ } 1492 if (m_Scripts.ContainsKey(itemID))
1410 1493 instance = m_Scripts[itemID];
1411 if (instance != null) 1494 }
1412 { 1495 catch { /* ignore race conditions */ }
1413 instance.PostEvent(p); 1496
1414 result = true; 1497 if (instance != null)
1498 {
1499 instance.PostEvent(p);
1500 result = true;
1501 }
1415 } 1502 }
1416 } 1503 }
1417 }
1418 1504
1419 return result; 1505 return result;
1420 } 1506 }
@@ -1539,6 +1625,13 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1539 } 1625 }
1540 } 1626 }
1541 1627
1628 public void SetRunEnable(UUID instanceID, bool enable)
1629 {
1630 IScriptInstance instance = GetInstance(instanceID);
1631 if (instance != null)
1632 instance.Run = enable;
1633 }
1634
1542 public bool GetScriptState(UUID itemID) 1635 public bool GetScriptState(UUID itemID)
1543 { 1636 {
1544 IScriptInstance instance = GetInstance(itemID); 1637 IScriptInstance instance = GetInstance(itemID);
@@ -1573,7 +1666,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1573 { 1666 {
1574 IScriptInstance instance = GetInstance(itemID); 1667 IScriptInstance instance = GetInstance(itemID);
1575 if (instance != null) 1668 if (instance != null)
1576 instance.Stop(0); 1669 {
1670 // Give the script some time to finish processing its last event. Simply aborting the script thread can
1671 // cause issues on mono 2.6, 2.10 and possibly later where locks are not released properly on abort.
1672 instance.Stop(1000);
1673 }
1577 } 1674 }
1578 1675
1579 public DetectParams GetDetectParams(UUID itemID, int idx) 1676 public DetectParams GetDetectParams(UUID itemID, int idx)
@@ -1749,14 +1846,15 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1749 FileMode.Open, FileAccess.Read)) 1846 FileMode.Open, FileAccess.Read))
1750 { 1847 {
1751 tfs.Read(tdata, 0, tdata.Length); 1848 tfs.Read(tdata, 0, tdata.Length);
1752 tfs.Close();
1753 } 1849 }
1754 1850
1755 assem = new System.Text.ASCIIEncoding().GetString(tdata); 1851 assem = new System.Text.ASCIIEncoding().GetString(tdata);
1756 } 1852 }
1757 catch (Exception e) 1853 catch (Exception e)
1758 { 1854 {
1759 m_log.DebugFormat("[XEngine]: Unable to open script textfile {0}, reason: {1}", assemName+".text", e.Message); 1855 m_log.ErrorFormat(
1856 "[XEngine]: Unable to open script textfile {0}{1}, reason: {2}",
1857 assemName, ".text", e.Message);
1760 } 1858 }
1761 } 1859 }
1762 } 1860 }
@@ -1773,16 +1871,15 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1773 using (FileStream fs = File.Open(assemName, FileMode.Open, FileAccess.Read)) 1871 using (FileStream fs = File.Open(assemName, FileMode.Open, FileAccess.Read))
1774 { 1872 {
1775 fs.Read(data, 0, data.Length); 1873 fs.Read(data, 0, data.Length);
1776 fs.Close();
1777 } 1874 }
1778 1875
1779 assem = System.Convert.ToBase64String(data); 1876 assem = System.Convert.ToBase64String(data);
1780 } 1877 }
1781 catch (Exception e) 1878 catch (Exception e)
1782 { 1879 {
1783 m_log.DebugFormat("[XEngine]: Unable to open script assembly {0}, reason: {1}", assemName, e.Message); 1880 m_log.ErrorFormat(
1881 "[XEngine]: Unable to open script assembly {0}, reason: {1}", assemName, e.Message);
1784 } 1882 }
1785
1786 } 1883 }
1787 } 1884 }
1788 1885
@@ -1795,9 +1892,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1795 using (StreamReader msr = new StreamReader(mfs)) 1892 using (StreamReader msr = new StreamReader(mfs))
1796 { 1893 {
1797 map = msr.ReadToEnd(); 1894 map = msr.ReadToEnd();
1798 msr.Close();
1799 } 1895 }
1800 mfs.Close();
1801 } 1896 }
1802 } 1897 }
1803 1898
@@ -1833,6 +1928,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1833 1928
1834 public bool SetXMLState(UUID itemID, string xml) 1929 public bool SetXMLState(UUID itemID, string xml)
1835 { 1930 {
1931// m_log.DebugFormat("[XEngine]: Writing state for script item with ID {0}", itemID);
1932
1836 if (xml == String.Empty) 1933 if (xml == String.Empty)
1837 return false; 1934 return false;
1838 1935
@@ -1893,31 +1990,61 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1893 { 1990 {
1894 using (FileStream fs = File.Create(path)) 1991 using (FileStream fs = File.Create(path))
1895 { 1992 {
1993// m_log.DebugFormat("[XEngine]: Writing assembly file {0}", path);
1994
1896 fs.Write(filedata, 0, filedata.Length); 1995 fs.Write(filedata, 0, filedata.Length);
1897 fs.Close();
1898 } 1996 }
1899 } 1997 }
1900 catch (IOException ex) 1998 catch (IOException ex)
1901 { 1999 {
1902 // if there already exists a file at that location, it may be locked. 2000 // if there already exists a file at that location, it may be locked.
1903 m_log.ErrorFormat("[XEngine]: File {0} already exists! {1}", path, ex.Message); 2001 m_log.ErrorFormat("[XEngine]: Error whilst writing assembly file {0}, {1}", path, ex.Message);
1904 } 2002 }
2003
2004 string textpath = path + ".text";
1905 try 2005 try
1906 { 2006 {
1907 using (FileStream fs = File.Create(path + ".text")) 2007 using (FileStream fs = File.Create(textpath))
1908 { 2008 {
1909 using (StreamWriter sw = new StreamWriter(fs)) 2009 using (StreamWriter sw = new StreamWriter(fs))
1910 { 2010 {
2011// m_log.DebugFormat("[XEngine]: Writing .text file {0}", textpath);
2012
1911 sw.Write(base64); 2013 sw.Write(base64);
1912 sw.Close();
1913 } 2014 }
1914 fs.Close();
1915 } 2015 }
1916 } 2016 }
1917 catch (IOException ex) 2017 catch (IOException ex)
1918 { 2018 {
1919 // if there already exists a file at that location, it may be locked. 2019 // if there already exists a file at that location, it may be locked.
1920 m_log.ErrorFormat("[XEngine]: File {0} already exists! {1}", path, ex.Message); 2020 m_log.ErrorFormat("[XEngine]: Error whilst writing .text file {0}, {1}", textpath, ex.Message);
2021 }
2022 }
2023
2024 XmlNodeList mapL = rootE.GetElementsByTagName("LineMap");
2025 if (mapL.Count > 0)
2026 {
2027 XmlElement mapE = (XmlElement)mapL[0];
2028
2029 string mappath = Path.Combine(m_ScriptEnginesPath, World.RegionInfo.RegionID.ToString());
2030 mappath = Path.Combine(mappath, mapE.GetAttribute("Filename"));
2031
2032 try
2033 {
2034 using (FileStream mfs = File.Create(mappath))
2035 {
2036 using (StreamWriter msw = new StreamWriter(mfs))
2037 {
2038 // m_log.DebugFormat("[XEngine]: Writing linemap file {0}", mappath);
2039
2040 msw.Write(mapE.InnerText);
2041 }
2042 }
2043 }
2044 catch (IOException ex)
2045 {
2046 // if there already exists a file at that location, it may be locked.
2047 m_log.ErrorFormat("[XEngine]: Linemap file {0} already exists! {1}", mappath, ex.Message);
1921 } 2048 }
1922 } 2049 }
1923 } 2050 }
@@ -1931,43 +2058,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1931 { 2058 {
1932 using (StreamWriter ssw = new StreamWriter(sfs)) 2059 using (StreamWriter ssw = new StreamWriter(sfs))
1933 { 2060 {
2061// m_log.DebugFormat("[XEngine]: Writing state file {0}", statepath);
2062
1934 ssw.Write(stateE.OuterXml); 2063 ssw.Write(stateE.OuterXml);
1935 ssw.Close();
1936 } 2064 }
1937 sfs.Close();
1938 } 2065 }
1939 } 2066 }
1940 catch (IOException ex) 2067 catch (IOException ex)
1941 { 2068 {
1942 // if there already exists a file at that location, it may be locked. 2069 // if there already exists a file at that location, it may be locked.
1943 m_log.ErrorFormat("[XEngine]: File {0} already exists! {1}", statepath, ex.Message); 2070 m_log.ErrorFormat("[XEngine]: Error whilst writing state file {0}, {1}", statepath, ex.Message);
1944 }
1945
1946 XmlNodeList mapL = rootE.GetElementsByTagName("LineMap");
1947 if (mapL.Count > 0)
1948 {
1949 XmlElement mapE = (XmlElement)mapL[0];
1950
1951 string mappath = Path.Combine(m_ScriptEnginesPath, World.RegionInfo.RegionID.ToString());
1952 mappath = Path.Combine(mappath, mapE.GetAttribute("Filename"));
1953
1954 try
1955 {
1956 using (FileStream mfs = File.Create(mappath))
1957 {
1958 using (StreamWriter msw = new StreamWriter(mfs))
1959 {
1960 msw.Write(mapE.InnerText);
1961 msw.Close();
1962 }
1963 mfs.Close();
1964 }
1965 }
1966 catch (IOException ex)
1967 {
1968 // if there already exists a file at that location, it may be locked.
1969 m_log.ErrorFormat("[XEngine]: File {0} already exists! {1}", statepath, ex.Message);
1970 }
1971 } 2071 }
1972 2072
1973 return true; 2073 return true;
@@ -2001,45 +2101,59 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2001 if (!topScripts.ContainsKey(si.LocalID)) 2101 if (!topScripts.ContainsKey(si.LocalID))
2002 topScripts[si.RootLocalID] = 0; 2102 topScripts[si.RootLocalID] = 0;
2003 2103
2004// long ticksElapsed = tickNow - si.MeasurementPeriodTickStart; 2104 topScripts[si.RootLocalID] += CalculateAdjustedExectionTime(si, tickNow);
2005// float framesElapsed = ticksElapsed / (18.1818 * TimeSpan.TicksPerMillisecond); 2105 }
2006 2106 }
2007 // Execution time of the script adjusted by it's measurement period to make scripts started at
2008 // different times comparable.
2009// float adjustedExecutionTime
2010// = (float)si.MeasurementPeriodExecutionTime
2011// / ((float)(tickNow - si.MeasurementPeriodTickStart) / ScriptInstance.MaxMeasurementPeriod)
2012// / TimeSpan.TicksPerMillisecond;
2013
2014 long ticksElapsed = tickNow - si.MeasurementPeriodTickStart;
2015
2016 // Avoid divide by zerp
2017 if (ticksElapsed == 0)
2018 ticksElapsed = 1;
2019 2107
2020 // Scale execution time to the ideal 55 fps frame time for these reasons. 2108 return topScripts;
2021 // 2109 }
2022 // 1) XEngine does not execute scripts per frame, unlike other script engines. Hence, there is no
2023 // 'script execution time per frame', which is the original purpose of this value.
2024 //
2025 // 2) Giving the raw execution times is misleading since scripts start at different times, making
2026 // it impossible to compare scripts.
2027 //
2028 // 3) Scaling the raw execution time to the time that the script has been running is better but
2029 // is still misleading since a script that has just been rezzed may appear to have been running
2030 // for much longer.
2031 //
2032 // 4) Hence, we scale execution time to an idealised frame time (55 fps). This is also not perfect
2033 // since the figure does not represent actual execution time and very hard running scripts will
2034 // never exceed 18ms (though this is a very high number for script execution so is a warning sign).
2035 float adjustedExecutionTime
2036 = ((float)si.MeasurementPeriodExecutionTime / ticksElapsed) * 18.1818f;
2037 2110
2038 topScripts[si.RootLocalID] += adjustedExecutionTime; 2111 public float GetScriptExecutionTime(List<UUID> itemIDs)
2112 {
2113 if (itemIDs == null|| itemIDs.Count == 0)
2114 {
2115 return 0.0f;
2116 }
2117 float time = 0.0f;
2118 long tickNow = Util.EnvironmentTickCount();
2119 IScriptInstance si;
2120 // Calculate the time for all scripts that this engine is executing
2121 // Ignore any others
2122 foreach (UUID id in itemIDs)
2123 {
2124 si = GetInstance(id);
2125 if (si != null && si.Running)
2126 {
2127 time += CalculateAdjustedExectionTime(si, tickNow);
2039 } 2128 }
2040 } 2129 }
2130 return time;
2131 }
2041 2132
2042 return topScripts; 2133 private float CalculateAdjustedExectionTime(IScriptInstance si, long tickNow)
2134 {
2135 long ticksElapsed = tickNow - si.MeasurementPeriodTickStart;
2136
2137 // Avoid divide by zero
2138 if (ticksElapsed == 0)
2139 ticksElapsed = 1;
2140
2141 // Scale execution time to the ideal 55 fps frame time for these reasons.
2142 //
2143 // 1) XEngine does not execute scripts per frame, unlike other script engines. Hence, there is no
2144 // 'script execution time per frame', which is the original purpose of this value.
2145 //
2146 // 2) Giving the raw execution times is misleading since scripts start at different times, making
2147 // it impossible to compare scripts.
2148 //
2149 // 3) Scaling the raw execution time to the time that the script has been running is better but
2150 // is still misleading since a script that has just been rezzed may appear to have been running
2151 // for much longer.
2152 //
2153 // 4) Hence, we scale execution time to an idealised frame time (55 fps). This is also not perfect
2154 // since the figure does not represent actual execution time and very hard running scripts will
2155 // never exceed 18ms (though this is a very high number for script execution so is a warning sign).
2156 return ((float)si.MeasurementPeriodExecutionTime / ticksElapsed) * 18.1818f;
2043 } 2157 }
2044 2158
2045 public void SuspendScript(UUID itemID) 2159 public void SuspendScript(UUID itemID)