mantis 8518: Yengine; we can't wait for GC (worse finalizers) to count released memory of some local variables, so add a pseudo free; fix memory account on timeslice rentry; change the folder for the debug IL files; fix memory usage on reset. This changes will only take effect on new compiles

0.9.1.0-post-fixes
UbitUmarov 2019-04-15 23:32:22 +01:00
parent cfd3923868
commit a83b7a292b
9 changed files with 178 additions and 80 deletions

View File

@ -197,7 +197,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
public CallLabel openCallLabel = null; // only one call label can be open at a time
// - the call label is open from the time of CallPre() until corresponding CallPost()
// - so no non-trivial pushes/pops etc allowed between a CallPre() and a CallPost()
public List<ScriptMyLocal> HeapLocals = new List<ScriptMyLocal>();
private ScriptMyILGen _ilGen;
public ScriptMyILGen ilGen
{
@ -1258,6 +1258,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
// resume at the correct spot.
actCallLabels.Clear();
allCallLabels.Clear();
HeapLocals.Clear();
openCallLabel = null;
// Alloc stack space for local vars.
@ -1398,19 +1399,17 @@ namespace OpenSim.Region.ScriptEngine.Yengine
_ilGen = collector.WriteOutAll();
collector = null;
// Output code to restore stack frame from stream.
// It jumps back to the call labels within the function body.
List<ScriptMyLocal> activeTemps = null;
if(!isTrivial)
if (!isTrivial)
{
// Build list of locals and temps active at all the call labels.
// Build list of locals and temps active at all the call labels.
activeTemps = new List<ScriptMyLocal>();
foreach(CallLabel cl in allCallLabels)
{
foreach(ScriptMyLocal lcl in cl.callLabel.whereAmI.localsReadBeforeWritten)
foreach (CallLabel cl in allCallLabels)
{
if(!activeTemps.Contains(lcl))
foreach(ScriptMyLocal lcl in cl.callLabel.whereAmI.localsReadBeforeWritten)
{
if(!activeTemps.Contains(lcl))
{
activeTemps.Add(lcl);
}
}
@ -1452,11 +1451,34 @@ namespace OpenSim.Region.ScriptEngine.Yengine
}
// Output the 'real' return opcode.
// push return value
ilGen.MarkLabel(retLabel);
if(!(curDeclFunc.retType is TokenTypeVoid))
if (!(curDeclFunc.retType is TokenTypeVoid))
{
ilGen.Emit(curDeclFunc, OpCodes.Ldloc, retValue);
}
// pseudo free memory usage
foreach (ScriptMyLocal sml in HeapLocals)
{
Type t = sml.type;
if (t == typeof(HeapTrackerList))
{
ilGen.Emit(curDeclFunc, OpCodes.Ldloc, sml);
HeapTrackerList.GenFree(curDeclFunc, ilGen);
}
else if (t == typeof(HeapTrackerString))
{
ilGen.Emit(curDeclFunc, OpCodes.Ldloc, sml);
HeapTrackerString.GenFree(curDeclFunc, ilGen);
}
else if (t == typeof(HeapTrackerObject))
{
ilGen.Emit(curDeclFunc, OpCodes.Ldloc, sml);
HeapTrackerObject.GenFree(curDeclFunc, ilGen);
}
}
ilGen.Emit(curDeclFunc, OpCodes.Ret);
retLabel = null;
retValue = null;
@ -1675,11 +1697,11 @@ namespace OpenSim.Region.ScriptEngine.Yengine
if(u != t)
{
if(t == typeof(HeapTrackerList))
HeapTrackerList.GenPop(curDeclFunc, ilGen);
HeapTrackerList.GenRestore(curDeclFunc, ilGen);
if(t == typeof(HeapTrackerObject))
HeapTrackerObject.GenPop(curDeclFunc, ilGen);
HeapTrackerObject.GenRestore(curDeclFunc, ilGen);
if(t == typeof(HeapTrackerString))
HeapTrackerString.GenPop(curDeclFunc, ilGen);
HeapTrackerString.GenRestore(curDeclFunc, ilGen);
}
else
{

View File

@ -2611,10 +2611,10 @@ namespace OpenSim.Region.ScriptEngine.Yengine
// everything required by any blocks it can branch to.
do
{
this.resolvedSomething = false;
this.resolveSequence++;
this.ResolveBlock((GraphNodeBlock)firstLin);
} while(this.resolvedSomething);
resolvedSomething = false;
resolveSequence++;
ResolveBlock((GraphNodeBlock)firstLin);
} while(resolvedSomething);
// Repeat the cutting loops as long as we keep finding stuff.
bool didSomething;
@ -2939,7 +2939,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
return;
// So we don't recurse forever on a backward branch.
currentBlock.hasBeenResolved = this.resolveSequence;
currentBlock.hasBeenResolved = resolveSequence;
// Assume we haven't written any locals yet.
List<ScriptMyLocal> localsWrittenSoFar = new List<ScriptMyLocal>();
@ -2975,7 +2975,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
!currentBlock.localsReadBeforeWritten.Contains(readByNextBlock))
{
currentBlock.localsReadBeforeWritten.Add(readByNextBlock);
this.resolvedSomething = true;
resolvedSomething = true;
}
}
}

View File

@ -1483,7 +1483,8 @@ namespace OpenSim.Region.ScriptEngine.Yengine
{
if(type.ToHeapTrackerType() != null)
{
this.localBuilder = scg.ilGen.DeclareLocal(type.ToHeapTrackerType(), name);
localBuilder = scg.ilGen.DeclareLocal(type.ToHeapTrackerType(), name);
scg.HeapLocals.Add(localBuilder);
scg.PushXMRInst();
scg.ilGen.Emit(type, OpCodes.Newobj, type.GetHeapTrackerCtor());
scg.ilGen.Emit(type, OpCodes.Stloc, localBuilder);
@ -1547,6 +1548,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
scg.ilGen.Emit(errorAt, OpCodes.Ldloc, localBuilder);
scg.ilGen.Emit(errorAt, OpCodes.Ldloc, htpop);
type.CallHeapTrackerPopMeth(errorAt, scg.ilGen);
scg.HeapLocals.Add(htpop);
}
else
{

View File

@ -130,7 +130,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
// Since we just wrote the .xmrobj file, maybe save disassembly.
if (m_Engine.m_ScriptDebugSaveIL)
{
string asmFileName = GetScriptFileName (m_ScriptObjCodeKey + ".yasm");
string asmFileName = GetScriptILFileName(m_ScriptObjCodeKey + ".yasm");
// m_log.Debug ("[YEngine]: MMRScriptCompileSaveILGen: saving to " + asmFileName);
asmFileWriter = File.CreateText (asmFileName);
}

View File

@ -125,7 +125,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
// Save new value in array, replacing one of same key if there.
// null means remove the value, ie, script did array[key] = undef.
if(value != null)
if (value != null)
{
dnary[key] = value;
}
@ -285,10 +285,9 @@ namespace OpenSim.Region.ScriptEngine.Yengine
public void RecvArrayObj(RecvArrayObjDelegate recvObj)
{
heapUse = inst.UpdateHeapUse(heapUse, EMPTYHEAP);
// Cause any enumeration to refill the array from the sorted dictionary.
// Since it is a sorted dictionary, any enumerations will be in the same
// order as on the sending side.
// Cause any enumeration to refill the array from the sorted dictionary.
// Since it is a sorted dictionary, any enumerations will be in the same
// order as on the sending side.
arrayValid = 0;
enumrValid = false;

View File

@ -58,11 +58,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
if(inst == null)
throw new ArgumentNullException("inst");
instance = inst;
}
~HeapTrackerBase()
{
usage = instance.UpdateHeapUse(usage, 0);
usage = 0;
}
}
@ -73,24 +69,33 @@ namespace OpenSim.Region.ScriptEngine.Yengine
{
private static FieldInfo listValueField = typeof(HeapTrackerList).GetField("value");
private static MethodInfo listSaveMethod = typeof(HeapTrackerList).GetMethod("Save");
private static MethodInfo listRestoreMethod = typeof(HeapTrackerList).GetMethod("Restore");
private static MethodInfo listFreeMethod = typeof(HeapTrackerList).GetMethod("Free");
public LSL_List value;
public HeapTrackerList(XMRInstAbstract inst) : base(inst) { }
public HeapTrackerList(XMRInstAbstract inst) : base(inst) {}
// generate CIL code to pop the value from the CIL stack
// generate CIL code to pop the value ie store in value
// input:
// 'this' pointer already pushed on CIL stack
// new value pushed on CIL stack
// new value
// output:
// 'this' pointer popped from stack
// new value popped from CIL stack
// heap usage updated
public static void GenPop(Token errorAt, ScriptMyILGen ilGen)
{
ilGen.Emit(errorAt, OpCodes.Call, listSaveMethod);
}
public static void GenRestore(Token errorAt, ScriptMyILGen ilGen)
{
ilGen.Emit(errorAt, OpCodes.Call, listRestoreMethod);
}
public static void GenFree(Token errorAt, ScriptMyILGen ilGen)
{
ilGen.Emit(errorAt, OpCodes.Call, listFreeMethod);
}
// generate CIL code to push the value on the CIL stack
// input:
// 'this' pointer already pushed on CIL stack
@ -106,23 +111,32 @@ namespace OpenSim.Region.ScriptEngine.Yengine
public void Save(LSL_List lis)
{
int newuse = Size(lis);
usage = instance.UpdateHeapUse(usage, newuse);
if (lis == null)
usage = instance.UpdateHeapUse(usage, 0);
else
usage = instance.UpdateHeapUse(usage, Size(lis));
value = lis;
}
public void Restore(LSL_List lis)
{
value = lis;
if (lis != null)
usage = Size(lis);
else
usage = 0;
}
public void Free()
{
usage = instance.UpdateHeapUse(usage, 0);
value = null;
instance = null;
}
//private static int counter = 5;
public static int Size(LSL_List lis)
{
// VS2017 in debug mode seems to have a problem running this statement quickly:
//SLOW: return (!typeof(LSL_List).IsValueType && (lis == null)) ? 0 : lis.Size;
//FAST: return 33;
//SLOW: return (lis == null) ? 0 : 99;
//FAST: return ++ counter;
// VS2017 in debug mode seems content to run this quickly though:
try
{
return lis.Size;
@ -141,6 +155,8 @@ namespace OpenSim.Region.ScriptEngine.Yengine
{
private static FieldInfo objectValueField = typeof(HeapTrackerObject).GetField("value");
private static MethodInfo objectSaveMethod = typeof(HeapTrackerObject).GetMethod("Save");
private static MethodInfo objectRestoreMethod = typeof(HeapTrackerObject).GetMethod("Restore");
private static MethodInfo objectFreeMethod = typeof(HeapTrackerObject).GetMethod("Free");
public const int HT_CHAR = 2;
public const int HT_DELE = 8;
@ -168,6 +184,16 @@ namespace OpenSim.Region.ScriptEngine.Yengine
ilGen.Emit(errorAt, OpCodes.Call, objectSaveMethod);
}
public static void GenRestore(Token errorAt, ScriptMyILGen ilGen)
{
ilGen.Emit(errorAt, OpCodes.Call, objectRestoreMethod);
}
public static void GenFree(Token errorAt, ScriptMyILGen ilGen)
{
ilGen.Emit(errorAt, OpCodes.Call, objectFreeMethod);
}
// generate CIL code to push the value on the CIL stack
// input:
// 'this' pointer already pushed on CIL stack
@ -188,6 +214,19 @@ namespace OpenSim.Region.ScriptEngine.Yengine
value = obj;
}
public void Restore(object obj)
{
value = obj;
usage = Size(obj);
}
public void Free()
{
usage = instance.UpdateHeapUse(usage, 0);
value = null;
instance = null;
}
// public so it can be used by XMRArray
public static int Size(object obj)
{
@ -204,8 +243,8 @@ namespace OpenSim.Region.ScriptEngine.Yengine
return HT_SING;
if(obj is int)
return HT_INT;
if(obj is LSL_Float)
return HT_SFLT;
if(obj is LSL_Float) // lsl floats are stupid doubles
return HT_DOUB;
if(obj is LSL_Integer)
return HT_INT;
if(obj is LSL_List)
@ -252,7 +291,9 @@ namespace OpenSim.Region.ScriptEngine.Yengine
public class HeapTrackerString: HeapTrackerBase
{
private static FieldInfo stringValueField = typeof(HeapTrackerString).GetField("value");
private static MethodInfo stringRestoreMethod = typeof(HeapTrackerString).GetMethod("Restore");
private static MethodInfo stringSaveMethod = typeof(HeapTrackerString).GetMethod("Save");
private static MethodInfo stringFreeMethod = typeof(HeapTrackerString).GetMethod("Free");
public string value;
@ -271,6 +312,16 @@ namespace OpenSim.Region.ScriptEngine.Yengine
ilGen.Emit(errorAt, OpCodes.Call, stringSaveMethod);
}
public static void GenRestore(Token errorAt, ScriptMyILGen ilGen)
{
ilGen.Emit(errorAt, OpCodes.Call, stringRestoreMethod);
}
public static void GenFree(Token errorAt, ScriptMyILGen ilGen)
{
ilGen.Emit(errorAt, OpCodes.Call, stringFreeMethod);
}
// generate CIL code to push the value on the CIL stack
// input:
// 'this' pointer already pushed on CIL stack
@ -291,6 +342,19 @@ namespace OpenSim.Region.ScriptEngine.Yengine
value = str;
}
public void Restore(string str)
{
value = str;
usage = Size(str);
}
public void Free()
{
usage = instance.UpdateHeapUse(usage, 0);
value = null;
instance = null;
}
public static int Size(string str)
{
return (str == null) ? 0 : str.Length * HeapTrackerObject.HT_CHAR;

View File

@ -84,17 +84,36 @@ namespace OpenSim.Region.ScriptEngine.Yengine
heapUse = instance.UpdateHeapUse(heapUse, 0);
}
public void Clear()
{
heapUse = 0;
if(iarArrays != null)
{
foreach(XMR_Array xa in iarArrays)
xa.__pub_clear();
}
if(iarChars != null)
iarChars = new char[iarChars.Length];
if (iarLists != null)
iarLists = new LSL_List[iarLists.Length];
if (iarObjects != null)
iarObjects = new object[iarObjects.Length];
if(iarStrings != null)
iarStrings = new string[iarStrings.Length];
}
public void AllocVarArrays(XMRInstArSizes ars)
{
ClearOldArrays();
int newuse = heapUse +
ars.iasChars* HeapTrackerObject.HT_CHAR +
ars.iasFloats * HeapTrackerObject.HT_SFLT +
ars.iasIntegers * HeapTrackerObject.HT_INT +
ars.iasRotations * HeapTrackerObject.HT_ROT +
ars.iasVectors * HeapTrackerObject.HT_VEC +
ars.iasSDTIntfObjs * HeapTrackerObject.HT_DELE;
heapUse = instance.UpdateHeapUse(heapUse,
ars.iasChars * HeapTrackerObject.HT_CHAR +
ars.iasFloats * HeapTrackerObject.HT_SFLT +
ars.iasIntegers * HeapTrackerObject.HT_INT +
ars.iasRotations * HeapTrackerObject.HT_ROT +
ars.iasVectors * HeapTrackerObject.HT_VEC +
ars.iasSDTIntfObjs * HeapTrackerObject.HT_DELE);
heapUse = instance.UpdateHeapUse(heapUse, newuse);
iarArrays = (ars.iasArrays > 0) ? new XMR_Array[ars.iasArrays] : noArrays;
iarChars = (ars.iasChars > 0) ? new char[ars.iasChars] : noChars;
@ -424,31 +443,13 @@ namespace OpenSim.Region.ScriptEngine.Yengine
\**************************************************/
protected int heapLimit;
private int heapUsed;
protected int heapUsed;
public virtual int UpdateHeapUse(int olduse, int newuse)
{
if(newuse <= olduse)
Interlocked.Add(ref heapUsed, newuse - olduse);
else
{
int newtotal, oldtotal;
do
{
oldtotal = Interlocked.Add(ref heapUsed, 0);
newtotal = oldtotal + newuse - olduse;
if(newtotal > heapLimit)
{
// System.GC.Collect ();
// System.GC.WaitForPendingFinalizers ();
oldtotal = Interlocked.Add(ref heapUsed, 0);
newtotal = oldtotal + newuse - olduse;
if(newtotal > heapLimit)
throw new OutOfHeapException(oldtotal, newtotal, heapLimit);
}
} while(Interlocked.CompareExchange(ref heapUsed, newtotal, oldtotal) != oldtotal);
}
int newtotal = Interlocked.Add(ref heapUsed, newuse - olduse);
if(newtotal > heapLimit)
throw new OutOfHeapException(newtotal + olduse - newuse, newtotal, heapLimit);
return newuse;
}

View File

@ -236,6 +236,13 @@ namespace OpenSim.Region.ScriptEngine.Yengine
return GetScriptFileName(m_ScriptBasePath, filename);
}
public string GetScriptILFileName(string filename)
{
string path = Path.Combine(m_ScriptBasePath, "DebugIL");
Directory.CreateDirectory(path);
return Path.Combine(path, filename);
}
public static string GetScriptFileName(string scriptBasePath, string filename)
{
// Get old path, ie, all files lumped in a single huge directory.

View File

@ -841,6 +841,9 @@ namespace OpenSim.Region.ScriptEngine.Yengine
m_SleepUntil = DateTime.MinValue; // not doing llSleep()
m_ResetCount++; // has been reset once more
heapUsed = 0;
glblVars.Clear();
// Tell next call to 'default state_entry()' to reset all global
// vars to their initial values.
doGblInit = true;
@ -848,7 +851,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine
// Throw away all its stack frames.
// If the script is resetting itself, there shouldn't be any stack frames.
// If the script is being reset by something else, we throw them away cuz we want to start from the beginning of an event handler.
stackFrames = null;
stackFrames = null;
// Set script to 'default' state and queue call to its
// 'state_entry()' event handler.