diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs new file mode 100644 index 0000000..ed33108 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCapture.cs | |||
@@ -0,0 +1,403 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Threading; | ||
30 | using System.IO; | ||
31 | using System.Xml; | ||
32 | using OpenSim.Region.ScriptEngine.Shared; | ||
33 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
34 | using log4net; | ||
35 | |||
36 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
37 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
38 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
39 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
40 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
41 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
42 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
43 | |||
44 | namespace OpenSim.Region.ScriptEngine.XMREngine | ||
45 | { | ||
46 | public partial class XMRInstance | ||
47 | { | ||
48 | /********************************************************************************\ | ||
49 | * The only method of interest to outside this module is GetExecutionState() * | ||
50 | * which captures the current state of the script into an XML document. * | ||
51 | * * | ||
52 | * The rest of this module contains support routines for GetExecutionState(). * | ||
53 | \********************************************************************************/ | ||
54 | |||
55 | /** | ||
56 | * @brief Create an XML element that gives the current state of the script. | ||
57 | * <ScriptState Engine="XMREngine" SourceHash=m_ObjCode.sourceHash Asset=m_Item.AssetID> | ||
58 | * <Snapshot>globalsandstackdump</Snapshot> | ||
59 | * <Running>m_Running</Running> | ||
60 | * <DetectArray ... | ||
61 | * <EventQueue ... | ||
62 | * <Permissions ... | ||
63 | * <Plugins /> | ||
64 | * </ScriptState> | ||
65 | * Updates the .state file while we're at it. | ||
66 | */ | ||
67 | public XmlElement GetExecutionState(XmlDocument doc) | ||
68 | { | ||
69 | // When we're detaching an attachment, we need to wait here. | ||
70 | |||
71 | // Change this to a 5 second timeout. If things do mess up, | ||
72 | // we don't want to be stuck forever. | ||
73 | // | ||
74 | m_DetachReady.WaitOne (5000, false); | ||
75 | |||
76 | XmlElement scriptStateN = doc.CreateElement("", "ScriptState", ""); | ||
77 | scriptStateN.SetAttribute("Engine", m_Engine.ScriptEngineName); | ||
78 | scriptStateN.SetAttribute("Asset", m_Item.AssetID.ToString()); | ||
79 | scriptStateN.SetAttribute ("SourceHash", m_ObjCode.sourceHash); | ||
80 | |||
81 | // Make sure we aren't executing part of the script so it stays | ||
82 | // stable. Setting suspendOnCheckRun tells CheckRun() to suspend | ||
83 | // and return out so RunOne() will release the lock asap. | ||
84 | suspendOnCheckRunHold = true; | ||
85 | lock (m_RunLock) | ||
86 | { | ||
87 | m_RunOnePhase = "GetExecutionState enter"; | ||
88 | CheckRunLockInvariants(true); | ||
89 | |||
90 | // Get copy of script globals and stack in relocateable form. | ||
91 | MemoryStream snapshotStream = new MemoryStream(); | ||
92 | MigrateOutEventHandler(snapshotStream); | ||
93 | Byte[] snapshotBytes = snapshotStream.ToArray(); | ||
94 | snapshotStream.Close(); | ||
95 | string snapshotString = Convert.ToBase64String(snapshotBytes); | ||
96 | XmlElement snapshotN = doc.CreateElement("", "Snapshot", ""); | ||
97 | snapshotN.AppendChild(doc.CreateTextNode(snapshotString)); | ||
98 | scriptStateN.AppendChild(snapshotN); | ||
99 | m_RunOnePhase = "GetExecutionState B"; CheckRunLockInvariants(true); | ||
100 | |||
101 | // "Running" says whether or not we are accepting new events. | ||
102 | XmlElement runningN = doc.CreateElement("", "Running", ""); | ||
103 | runningN.AppendChild(doc.CreateTextNode(m_Running.ToString())); | ||
104 | scriptStateN.AppendChild(runningN); | ||
105 | m_RunOnePhase = "GetExecutionState C"; CheckRunLockInvariants(true); | ||
106 | |||
107 | // "DoGblInit" says whether or not default:state_entry() will init global vars. | ||
108 | XmlElement doGblInitN = doc.CreateElement("", "DoGblInit", ""); | ||
109 | doGblInitN.AppendChild(doc.CreateTextNode(doGblInit.ToString())); | ||
110 | scriptStateN.AppendChild(doGblInitN); | ||
111 | m_RunOnePhase = "GetExecutionState D"; CheckRunLockInvariants(true); | ||
112 | |||
113 | // More misc data. | ||
114 | XmlNode permissionsN = doc.CreateElement("", "Permissions", ""); | ||
115 | scriptStateN.AppendChild(permissionsN); | ||
116 | |||
117 | XmlAttribute granterA = doc.CreateAttribute("", "granter", ""); | ||
118 | granterA.Value = m_Item.PermsGranter.ToString(); | ||
119 | permissionsN.Attributes.Append(granterA); | ||
120 | |||
121 | XmlAttribute maskA = doc.CreateAttribute("", "mask", ""); | ||
122 | maskA.Value = m_Item.PermsMask.ToString(); | ||
123 | permissionsN.Attributes.Append(maskA); | ||
124 | m_RunOnePhase = "GetExecutionState E"; CheckRunLockInvariants(true); | ||
125 | |||
126 | // "DetectParams" are returned by llDetected...() script functions | ||
127 | // for the currently active event, if any. | ||
128 | if (m_DetectParams != null) | ||
129 | { | ||
130 | XmlElement detParArrayN = doc.CreateElement("", "DetectArray", ""); | ||
131 | AppendXMLDetectArray(doc, detParArrayN, m_DetectParams); | ||
132 | scriptStateN.AppendChild(detParArrayN); | ||
133 | } | ||
134 | m_RunOnePhase = "GetExecutionState F"; CheckRunLockInvariants(true); | ||
135 | |||
136 | // Save any events we have in the queue. | ||
137 | // <EventQueue> | ||
138 | // <Event Name="..."> | ||
139 | // <param>...</param> ... | ||
140 | // <DetectParams>...</DetectParams> ... | ||
141 | // </Event> | ||
142 | // ... | ||
143 | // </EventQueue> | ||
144 | XmlElement queuedEventsN = doc.CreateElement("", "EventQueue", ""); | ||
145 | lock (m_QueueLock) | ||
146 | { | ||
147 | foreach (EventParams evt in m_EventQueue) | ||
148 | { | ||
149 | XmlElement singleEventN = doc.CreateElement("", "Event", ""); | ||
150 | singleEventN.SetAttribute("Name", evt.EventName); | ||
151 | AppendXMLObjectArray(doc, singleEventN, evt.Params, "param"); | ||
152 | AppendXMLDetectArray(doc, singleEventN, evt.DetectParams); | ||
153 | queuedEventsN.AppendChild(singleEventN); | ||
154 | } | ||
155 | } | ||
156 | scriptStateN.AppendChild(queuedEventsN); | ||
157 | m_RunOnePhase = "GetExecutionState G"; CheckRunLockInvariants(true); | ||
158 | |||
159 | // "Plugins" indicate enabled timers and listens, etc. | ||
160 | Object[] pluginData = | ||
161 | AsyncCommandManager.GetSerializationData(m_Engine, m_ItemID); | ||
162 | |||
163 | XmlNode plugins = doc.CreateElement("", "Plugins", ""); | ||
164 | AppendXMLObjectArray(doc, plugins, pluginData, "plugin"); | ||
165 | scriptStateN.AppendChild(plugins); | ||
166 | m_RunOnePhase = "GetExecutionState H"; CheckRunLockInvariants(true); | ||
167 | |||
168 | // Let script run again. | ||
169 | suspendOnCheckRunHold = false; | ||
170 | |||
171 | m_RunOnePhase = "GetExecutionState leave"; | ||
172 | CheckRunLockInvariants(true); | ||
173 | } | ||
174 | |||
175 | // scriptStateN represents the contents of the .state file so | ||
176 | // write the .state file while we are here. | ||
177 | FileStream fs = File.Create(m_StateFileName); | ||
178 | StreamWriter sw = new StreamWriter(fs); | ||
179 | sw.Write(scriptStateN.OuterXml); | ||
180 | sw.Close(); | ||
181 | fs.Close(); | ||
182 | |||
183 | return scriptStateN; | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * @brief Write script state to output stream. | ||
188 | * The script microthread is at same state on return, | ||
189 | * ie, either inactive or suspended inside CheckRun(). | ||
190 | * | ||
191 | * Input: | ||
192 | * stream = stream to write event handler state information to | ||
193 | */ | ||
194 | private void MigrateOutEventHandler (Stream stream) | ||
195 | { | ||
196 | moehexcep = null; | ||
197 | |||
198 | // do all the work in the MigrateOutEventHandlerThread() method below | ||
199 | moehstream = stream; | ||
200 | |||
201 | XMRScriptThread cst = m_Engine.CurrentScriptThread (); | ||
202 | if (cst != null) | ||
203 | { | ||
204 | |||
205 | // we might be getting called inside some LSL Api function | ||
206 | // so we are already in script thread and thus must do | ||
207 | // migration directly | ||
208 | MigrateOutEventHandlerThread (); | ||
209 | } | ||
210 | else | ||
211 | { | ||
212 | // some other thread, do migration via a script thread | ||
213 | m_Engine.QueueToTrunk(this.MigrateOutEventHandlerThread); | ||
214 | |||
215 | // wait for it to complete | ||
216 | lock (moehdone) | ||
217 | { | ||
218 | while (moehstream != null) | ||
219 | Monitor.Wait (moehdone); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | // maybe it threw up | ||
224 | if (moehexcep != null) | ||
225 | throw moehexcep; | ||
226 | } | ||
227 | |||
228 | private Exception moehexcep; | ||
229 | private object moehdone = new object (); | ||
230 | private Stream moehstream; | ||
231 | private void MigrateOutEventHandlerThread () | ||
232 | { | ||
233 | Exception except; | ||
234 | |||
235 | try | ||
236 | { | ||
237 | // Resume the microthread and it will throw a StackCaptureException() | ||
238 | // with the stack frames saved to this.stackFrames. | ||
239 | // Then write the saved stack frames to the output stream. | ||
240 | // | ||
241 | // There is a stack only if the event code is not None. | ||
242 | if (this.eventCode != ScriptEventCode.None) | ||
243 | { | ||
244 | // tell microthread to continue | ||
245 | // it should see captureStackFrames and throw StackCaptureException() | ||
246 | // ...generating XMRStackFrames as it unwinds | ||
247 | this.captureStackFrames = true; | ||
248 | // this.suspendOnCheckRunTemp = true; | ||
249 | except = this.microthread.ResumeEx (); | ||
250 | this.captureStackFrames = false; | ||
251 | |||
252 | if (except == null) | ||
253 | throw new Exception ("stack save did not complete"); | ||
254 | |||
255 | if (!(except is StackCaptureException)) | ||
256 | throw except; | ||
257 | } | ||
258 | |||
259 | // Write script state out, frames and all, to the stream. | ||
260 | // Does not change script state. | ||
261 | |||
262 | moehstream.WriteByte (migrationVersion); | ||
263 | moehstream.WriteByte ((byte)16); | ||
264 | this.MigrateOut (new BinaryWriter (moehstream)); | ||
265 | |||
266 | // Now restore script stack. | ||
267 | // Microthread will suspend inside CheckRun() when restore is complete. | ||
268 | if (this.eventCode != ScriptEventCode.None) | ||
269 | { | ||
270 | this.stackFramesRestored = false; | ||
271 | except = this.microthread.StartEx (); | ||
272 | |||
273 | if (except != null) | ||
274 | throw except; | ||
275 | |||
276 | if (!this.stackFramesRestored) | ||
277 | throw new Exception ("restore after save did not complete"); | ||
278 | |||
279 | } | ||
280 | } | ||
281 | catch (Exception e) | ||
282 | { | ||
283 | moehexcep = e; | ||
284 | } | ||
285 | finally | ||
286 | { | ||
287 | // make sure CheckRunLockInvariants() won't puque | ||
288 | if (this.microthread.Active () == 0) | ||
289 | this.eventCode = ScriptEventCode.None; | ||
290 | |||
291 | // wake the MigrateOutEventHandler() method above | ||
292 | lock (moehdone) | ||
293 | { | ||
294 | moehstream = null; | ||
295 | Monitor.Pulse (moehdone); | ||
296 | } | ||
297 | } | ||
298 | } | ||
299 | |||
300 | /** | ||
301 | * @brief Convert an DetectParams[] to corresponding XML. | ||
302 | * DetectParams[] holds the values retrievable by llDetected...() for | ||
303 | * a given event. | ||
304 | */ | ||
305 | private static void AppendXMLDetectArray(XmlDocument doc, XmlElement parent, DetectParams[] detect) | ||
306 | { | ||
307 | foreach (DetectParams d in detect) | ||
308 | { | ||
309 | XmlElement detectParamsN = GetXMLDetect(doc, d); | ||
310 | parent.AppendChild(detectParamsN); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | private static XmlElement GetXMLDetect(XmlDocument doc, DetectParams d) | ||
315 | { | ||
316 | XmlElement detectParamsN = doc.CreateElement("", "DetectParams", ""); | ||
317 | |||
318 | XmlAttribute d_key = doc.CreateAttribute("", "key", ""); | ||
319 | d_key.Value = d.Key.ToString(); | ||
320 | detectParamsN.Attributes.Append(d_key); | ||
321 | |||
322 | XmlAttribute pos = doc.CreateAttribute("", "pos", ""); | ||
323 | pos.Value = d.OffsetPos.ToString(); | ||
324 | detectParamsN.Attributes.Append(pos); | ||
325 | |||
326 | XmlAttribute d_linkNum = doc.CreateAttribute("", "linkNum", ""); | ||
327 | d_linkNum.Value = d.LinkNum.ToString(); | ||
328 | detectParamsN.Attributes.Append(d_linkNum); | ||
329 | |||
330 | XmlAttribute d_group = doc.CreateAttribute("", "group", ""); | ||
331 | d_group.Value = d.Group.ToString(); | ||
332 | detectParamsN.Attributes.Append(d_group); | ||
333 | |||
334 | XmlAttribute d_name = doc.CreateAttribute("", "name", ""); | ||
335 | d_name.Value = d.Name.ToString(); | ||
336 | detectParamsN.Attributes.Append(d_name); | ||
337 | |||
338 | XmlAttribute d_owner = doc.CreateAttribute("", "owner", ""); | ||
339 | d_owner.Value = d.Owner.ToString(); | ||
340 | detectParamsN.Attributes.Append(d_owner); | ||
341 | |||
342 | XmlAttribute d_position = doc.CreateAttribute("", "position", ""); | ||
343 | d_position.Value = d.Position.ToString(); | ||
344 | detectParamsN.Attributes.Append(d_position); | ||
345 | |||
346 | XmlAttribute d_rotation = doc.CreateAttribute("", "rotation", ""); | ||
347 | d_rotation.Value = d.Rotation.ToString(); | ||
348 | detectParamsN.Attributes.Append(d_rotation); | ||
349 | |||
350 | XmlAttribute d_type = doc.CreateAttribute("", "type", ""); | ||
351 | d_type.Value = d.Type.ToString(); | ||
352 | detectParamsN.Attributes.Append(d_type); | ||
353 | |||
354 | XmlAttribute d_velocity = doc.CreateAttribute("", "velocity", ""); | ||
355 | d_velocity.Value = d.Velocity.ToString(); | ||
356 | detectParamsN.Attributes.Append(d_velocity); | ||
357 | |||
358 | return detectParamsN; | ||
359 | } | ||
360 | |||
361 | /** | ||
362 | * @brief Append elements of an array of objects to an XML parent. | ||
363 | * @param doc = document the parent is part of | ||
364 | * @param parent = parent to append the items to | ||
365 | * @param array = array of objects | ||
366 | * @param tag = <tag ..>...</tag> for each element | ||
367 | */ | ||
368 | private static void AppendXMLObjectArray(XmlDocument doc, XmlNode parent, object[] array, string tag) | ||
369 | { | ||
370 | foreach (object o in array) | ||
371 | { | ||
372 | XmlElement element = GetXMLObject(doc, o, tag); | ||
373 | parent.AppendChild(element); | ||
374 | } | ||
375 | } | ||
376 | |||
377 | /** | ||
378 | * @brief Get and XML representation of an object. | ||
379 | * @param doc = document the tag will be put in | ||
380 | * @param o = object to be represented | ||
381 | * @param tag = <tag ...>...</tag> | ||
382 | */ | ||
383 | private static XmlElement GetXMLObject(XmlDocument doc, object o, string tag) | ||
384 | { | ||
385 | XmlAttribute typ = doc.CreateAttribute("", "type", ""); | ||
386 | XmlElement n = doc.CreateElement("", tag, ""); | ||
387 | |||
388 | if (o is LSL_List) | ||
389 | { | ||
390 | typ.Value = "list"; | ||
391 | n.Attributes.Append(typ); | ||
392 | AppendXMLObjectArray(doc, n, ((LSL_List)o).Data, "item"); | ||
393 | } | ||
394 | else | ||
395 | { | ||
396 | typ.Value = o.GetType().ToString(); | ||
397 | n.Attributes.Append(typ); | ||
398 | n.AppendChild(doc.CreateTextNode(o.ToString())); | ||
399 | } | ||
400 | return n; | ||
401 | } | ||
402 | } | ||
403 | } | ||