492 lines
19 KiB
C#
492 lines
19 KiB
C#
/*
|
|
* Copyright (c) Contributors, http://opensimulator.org/
|
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the OpenSimulator Project nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
using log4net;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
|
|
using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
|
|
using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
|
using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
|
|
using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
|
|
using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
|
|
using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
|
|
|
|
namespace OpenSim.Region.ScriptEngine.XMREngine
|
|
{
|
|
public partial class XMREngine {
|
|
|
|
private void XmrTestLs (string[] args, int indx)
|
|
{
|
|
bool flagFull = false;
|
|
bool flagQueues = false;
|
|
bool flagTopCPU = false;
|
|
int maxScripts = 0x7FFFFFFF;
|
|
int numScripts = 0;
|
|
string outName = null;
|
|
XMRInstance[] instances;
|
|
|
|
/*
|
|
* Decode command line options.
|
|
*/
|
|
for (int i = indx; i < args.Length; i ++) {
|
|
if (args[i] == "-full") {
|
|
flagFull = true;
|
|
continue;
|
|
}
|
|
if (args[i] == "-help") {
|
|
m_log.Info ("[XMREngine]: xmr ls -full -max=<number> -out=<filename> -queues -topcpu");
|
|
return;
|
|
}
|
|
if (args[i].StartsWith("-max=")) {
|
|
try {
|
|
maxScripts = Convert.ToInt32(args[i].Substring(5));
|
|
} catch (Exception e) {
|
|
m_log.Error("[XMREngine]: bad max " + args[i].Substring(5) + ": " + e.Message);
|
|
return;
|
|
}
|
|
continue;
|
|
}
|
|
if (args[i].StartsWith("-out=")) {
|
|
outName = args[i].Substring(5);
|
|
continue;
|
|
}
|
|
if (args[i] == "-queues") {
|
|
flagQueues = true;
|
|
continue;
|
|
}
|
|
if (args[i] == "-topcpu") {
|
|
flagTopCPU = true;
|
|
continue;
|
|
}
|
|
if (args[i][0] == '-') {
|
|
m_log.Error("[XMREngine]: unknown option " + args[i] + ", try 'xmr ls -help'");
|
|
return;
|
|
}
|
|
}
|
|
|
|
TextWriter outFile = null;
|
|
if (outName != null) {
|
|
try {
|
|
outFile = File.CreateText(outName);
|
|
} catch (Exception e) {
|
|
m_log.Error("[XMREngine]: error creating " + outName + ": " + e.Message);
|
|
return;
|
|
}
|
|
} else {
|
|
outFile = new LogInfoTextWriter(m_log);
|
|
}
|
|
|
|
try {
|
|
for (int i = 0; i < numThreadScriptWorkers; i ++) {
|
|
XMRScriptThread th = m_ScriptThreads[i];
|
|
outFile.WriteLine("Script thread ID: " + th.m_ScriptThreadTID);
|
|
long execTime = th.m_ScriptExecTime;
|
|
if (execTime < 0) {
|
|
execTime += (long)(DateTime.UtcNow - DateTime.MinValue).TotalMilliseconds;
|
|
}
|
|
outFile.WriteLine(" execution time: " + execTime + " mS");
|
|
outFile.WriteLine(" last ran at: " + th.m_LastRanAt.ToString());
|
|
XMRInstance rins = th.m_RunInstance;
|
|
if (rins != null) {
|
|
outFile.WriteLine(" running: " + rins.ItemID.ToString() + " " + rins.m_DescName);
|
|
if (flagFull) {
|
|
outFile.WriteLine (rins.RunTestLs (true));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scan instance list to find those that match selection criteria.
|
|
*/
|
|
if (!Monitor.TryEnter(m_InstancesDict, 100)) {
|
|
m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
|
|
return;
|
|
}
|
|
try
|
|
{
|
|
instances = new XMRInstance[m_InstancesDict.Count];
|
|
foreach (XMRInstance ins in m_InstancesDict.Values)
|
|
{
|
|
if (InstanceMatchesArgs(ins, args, indx)) {
|
|
instances[numScripts++] = ins;
|
|
}
|
|
}
|
|
} finally {
|
|
Monitor.Exit(m_InstancesDict);
|
|
}
|
|
|
|
/*
|
|
* Maybe sort by descending CPU time.
|
|
*/
|
|
if (flagTopCPU) {
|
|
Array.Sort<XMRInstance>(instances, CompareInstancesByCPUTime);
|
|
}
|
|
|
|
/*
|
|
* Print the entries.
|
|
*/
|
|
if (!flagFull) {
|
|
outFile.WriteLine(" ItemID" +
|
|
" CPU(ms)" +
|
|
" NumEvents" +
|
|
" Status " +
|
|
" World Position " +
|
|
" <Part>:<Item>");
|
|
}
|
|
for (int i = 0; (i < numScripts) && (i < maxScripts); i ++) {
|
|
outFile.WriteLine(instances[i].RunTestLs(flagFull));
|
|
}
|
|
|
|
/*
|
|
* Print number of scripts that match selection criteria,
|
|
* even if we were told to print fewer.
|
|
*/
|
|
outFile.WriteLine("total of {0} script(s)", numScripts);
|
|
|
|
/*
|
|
* If -queues given, print out queue contents too.
|
|
*/
|
|
if (flagQueues) {
|
|
LsQueue(outFile, "start", m_StartQueue, args, indx);
|
|
LsQueue(outFile, "sleep", m_SleepQueue, args, indx);
|
|
LsQueue(outFile, "yield", m_YieldQueue, args, indx);
|
|
}
|
|
} finally {
|
|
outFile.Close();
|
|
}
|
|
}
|
|
|
|
private void XmrTestPev (string[] args, int indx)
|
|
{
|
|
bool flagAll = false;
|
|
int numScripts = 0;
|
|
XMRInstance[] instances;
|
|
|
|
/*
|
|
* Decode command line options.
|
|
*/
|
|
int i, j;
|
|
List<string> selargs = new List<string> (args.Length);
|
|
MethodInfo[] eventmethods = typeof (IEventHandlers).GetMethods ();
|
|
MethodInfo eventmethod;
|
|
for (i = indx; i < args.Length; i ++) {
|
|
string arg = args[i];
|
|
if (arg == "-all") {
|
|
flagAll = true;
|
|
continue;
|
|
}
|
|
if (arg == "-help") {
|
|
m_log.Info ("[XMREngine]: xmr pev -all | <part-of-script-name> <event-name> <params...>");
|
|
return;
|
|
}
|
|
if (arg[0] == '-') {
|
|
m_log.Error ("[XMREngine]: unknown option " + arg + ", try 'xmr pev -help'");
|
|
return;
|
|
}
|
|
for (j = 0; j < eventmethods.Length; j ++) {
|
|
eventmethod = eventmethods[j];
|
|
if (eventmethod.Name == arg) goto gotevent;
|
|
}
|
|
selargs.Add (arg);
|
|
}
|
|
m_log.Error ("[XMREngine]: missing <event-name> <params...>, try 'xmr pev -help'");
|
|
return;
|
|
gotevent:
|
|
string eventname = eventmethod.Name;
|
|
StringBuilder sourcesb = new StringBuilder ();
|
|
while (++ i < args.Length) {
|
|
sourcesb.Append (' ');
|
|
sourcesb.Append (args[i]);
|
|
}
|
|
string sourcest = sourcesb.ToString ();
|
|
string sourcehash;
|
|
youveanerror = false;
|
|
Token t = TokenBegin.Construct ("", null, ErrorMsg, sourcest, out sourcehash);
|
|
if (youveanerror) return;
|
|
ParameterInfo[] paraminfos = eventmethod.GetParameters ();
|
|
object[] paramvalues = new object[paraminfos.Length];
|
|
i = 0;
|
|
while (!((t = t.nextToken) is TokenEnd)) {
|
|
if (i >= paramvalues.Length) {
|
|
ErrorMsg (t, "extra parameter(s)");
|
|
return;
|
|
}
|
|
paramvalues[i] = ParseParamValue (ref t);
|
|
if (paramvalues[i] == null) return;
|
|
i ++;
|
|
}
|
|
OpenSim.Region.ScriptEngine.Shared.EventParams eps =
|
|
new OpenSim.Region.ScriptEngine.Shared.EventParams (eventname, paramvalues, zeroDetectParams);
|
|
|
|
/*
|
|
* Scan instance list to find those that match selection criteria.
|
|
*/
|
|
if (!Monitor.TryEnter(m_InstancesDict, 100)) {
|
|
m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
instances = new XMRInstance[m_InstancesDict.Count];
|
|
foreach (XMRInstance ins in m_InstancesDict.Values) {
|
|
if (flagAll || InstanceMatchesArgs (ins, selargs.ToArray (), 0)) {
|
|
instances[numScripts++] = ins;
|
|
}
|
|
}
|
|
} finally {
|
|
Monitor.Exit(m_InstancesDict);
|
|
}
|
|
|
|
/*
|
|
* Post event to the matching instances.
|
|
*/
|
|
for (i = 0; i < numScripts; i ++) {
|
|
XMRInstance inst = instances[i];
|
|
m_log.Info ("[XMREngine]: post " + eventname + " to " + inst.m_DescName);
|
|
inst.PostEvent (eps);
|
|
}
|
|
}
|
|
|
|
private object ParseParamValue (ref Token token)
|
|
{
|
|
if (token is TokenFloat) {
|
|
return new LSL_Float (((TokenFloat)token).val);
|
|
}
|
|
if (token is TokenInt) {
|
|
return new LSL_Integer (((TokenInt)token).val);
|
|
}
|
|
if (token is TokenStr) {
|
|
return new LSL_String (((TokenStr)token).val);
|
|
}
|
|
if (token is TokenKwCmpLT) {
|
|
List<double> valuelist = new List<double> ();
|
|
while (!((token = token.nextToken) is TokenKwCmpGT)) {
|
|
if (!(token is TokenKwComma)) {
|
|
object value = ParseParamValue (ref token);
|
|
if (value == null) return null;
|
|
if (value is int) value = (double)(int)value;
|
|
if (!(value is double)) {
|
|
ErrorMsg (token, "must be float or integer constant");
|
|
return null;
|
|
}
|
|
valuelist.Add ((double)value);
|
|
} else if (token.prevToken is TokenKwComma) {
|
|
ErrorMsg (token, "missing constant");
|
|
return null;
|
|
}
|
|
}
|
|
double[] values = valuelist.ToArray ();
|
|
switch (values.Length) {
|
|
case 3: {
|
|
return new LSL_Vector (values[0], values[1], values[2]);
|
|
}
|
|
case 4: {
|
|
return new LSL_Rotation (values[0], values[1], values[2], values[3]);
|
|
}
|
|
default: {
|
|
ErrorMsg (token, "not rotation or vector");
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
if (token is TokenKwBrkOpen) {
|
|
List<object> valuelist = new List<object> ();
|
|
while (!((token = token.nextToken) is TokenKwBrkClose)) {
|
|
if (!(token is TokenKwComma)) {
|
|
object value = ParseParamValue (ref token);
|
|
if (value == null) return null;
|
|
valuelist.Add (value);
|
|
} else if (token.prevToken is TokenKwComma) {
|
|
ErrorMsg (token, "missing constant");
|
|
return null;
|
|
}
|
|
}
|
|
return new LSL_List (valuelist.ToArray ());
|
|
}
|
|
if (token is TokenName) {
|
|
FieldInfo field = typeof (OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass).GetField (((TokenName)token).val);
|
|
if ((field != null) && field.IsPublic && (field.IsLiteral || (field.IsStatic && field.IsInitOnly))) {
|
|
return field.GetValue (null);
|
|
}
|
|
}
|
|
ErrorMsg (token, "invalid constant");
|
|
return null;
|
|
}
|
|
|
|
private bool youveanerror;
|
|
private void ErrorMsg (Token token, string message)
|
|
{
|
|
youveanerror = true;
|
|
m_log.Info ("[XMREngine]: " + token.posn + " " + message);
|
|
}
|
|
|
|
private void XmrTestReset (string[] args, int indx)
|
|
{
|
|
bool flagAll = false;
|
|
int numScripts = 0;
|
|
XMRInstance[] instances;
|
|
|
|
if (args.Length <= indx) {
|
|
m_log.Error("[XMREngine]: must specify part of script name or -all for all scripts");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Decode command line options.
|
|
*/
|
|
for (int i = indx; i < args.Length; i ++) {
|
|
if (args[i] == "-all") {
|
|
flagAll = true;
|
|
continue;
|
|
}
|
|
if (args[i] == "-help") {
|
|
m_log.Info ("[XMREngine]: xmr reset -all | <part-of-script-name>");
|
|
return;
|
|
}
|
|
if (args[i][0] == '-') {
|
|
m_log.Error ("[XMREngine]: unknown option " + args[i] + ", try 'xmr reset -help'");
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scan instance list to find those that match selection criteria.
|
|
*/
|
|
if (!Monitor.TryEnter(m_InstancesDict, 100)) {
|
|
m_log.Error("[XMREngine]: deadlock m_LockedDict=" + m_LockedDict);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
instances = new XMRInstance[m_InstancesDict.Count];
|
|
foreach (XMRInstance ins in m_InstancesDict.Values) {
|
|
if (flagAll || InstanceMatchesArgs (ins, args, indx)) {
|
|
instances[numScripts++] = ins;
|
|
}
|
|
}
|
|
} finally {
|
|
Monitor.Exit(m_InstancesDict);
|
|
}
|
|
|
|
/*
|
|
* Reset the instances as if someone clicked their "Reset" button.
|
|
*/
|
|
for (int i = 0; i < numScripts; i ++) {
|
|
XMRInstance inst = instances[i];
|
|
m_log.Info ("[XMREngine]: resetting " + inst.m_DescName);
|
|
inst.Reset();
|
|
}
|
|
}
|
|
|
|
private static int CompareInstancesByCPUTime(XMRInstance a, XMRInstance b)
|
|
{
|
|
if (a == null) {
|
|
return (b == null) ? 0 : 1;
|
|
}
|
|
if (b == null) {
|
|
return -1;
|
|
}
|
|
if (b.m_CPUTime < a.m_CPUTime) return -1;
|
|
if (b.m_CPUTime > a.m_CPUTime) return 1;
|
|
return 0;
|
|
}
|
|
|
|
private void LsQueue(TextWriter outFile, string name, XMRInstQueue queue, string[] args, int indx)
|
|
{
|
|
outFile.WriteLine("Queue " + name + ":");
|
|
lock (queue) {
|
|
for (XMRInstance inst = queue.PeekHead(); inst != null; inst = inst.m_NextInst) {
|
|
try {
|
|
|
|
/*
|
|
* Try to print instance name.
|
|
*/
|
|
if (InstanceMatchesArgs(inst, args, indx)) {
|
|
outFile.WriteLine(" " + inst.ItemID.ToString() + " " + inst.m_DescName);
|
|
}
|
|
} catch (Exception e) {
|
|
|
|
/*
|
|
* Sometimes there are instances in the queue that are disposed.
|
|
*/
|
|
outFile.WriteLine(" " + inst.ItemID.ToString() + " " + inst.m_DescName + ": " + e.Message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool InstanceMatchesArgs(XMRInstance ins, string[] args, int indx)
|
|
{
|
|
bool hadSomethingToCompare = false;
|
|
|
|
for (int i = indx; i < args.Length; i ++)
|
|
{
|
|
if (args[i][0] != '-') {
|
|
hadSomethingToCompare = true;
|
|
if (ins.m_DescName.Contains(args[i])) return true;
|
|
if (ins.ItemID.ToString().Contains(args[i])) return true;
|
|
if (ins.AssetID.ToString().Contains(args[i])) return true;
|
|
}
|
|
}
|
|
return !hadSomethingToCompare;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Make m_log.Info look like a text writer.
|
|
*/
|
|
public class LogInfoTextWriter : TextWriter {
|
|
private StringBuilder sb = new StringBuilder();
|
|
private ILog m_log;
|
|
public LogInfoTextWriter (ILog m_log)
|
|
{
|
|
this.m_log = m_log;
|
|
}
|
|
public override void Write (char c)
|
|
{
|
|
if (c == '\n') {
|
|
m_log.Info("[XMREngine]: " + sb.ToString());
|
|
sb.Remove(0, sb.Length);
|
|
} else {
|
|
sb.Append(c);
|
|
}
|
|
}
|
|
public override void Close () { }
|
|
public override Encoding Encoding {
|
|
get {
|
|
return Encoding.UTF8;
|
|
}
|
|
}
|
|
}
|
|
}
|