PageRenderTime 86ms CodeModel.GetById 31ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 1ms

/interpreter/tags/at2dist110511/src/edu/vub/at/trace/Tracer.java

http://ambienttalk.googlecode.com/
Java | 493 lines | 235 code | 43 blank | 215 comment | 7 complexity | e5e4bd5a853c773b936776526b215ed7 MD5 | raw file
  1/**
  2 * AmbientTalk/2 Project
  3 * (c) Software Languages Lab, 2006 - 2009
  4 * Authors: Ambient Group at SOFT
  5 * 
  6 * The source code in this file is based on source code from Tyler Close's
  7 * Waterken server, Copyright 2008 Waterken Inc. Waterken's code is published
  8 * under the MIT license.
  9 * 
 10 * Permission is hereby granted, free of charge, to any person
 11 * obtaining a copy of this software and associated documentation
 12 * files (the "Software"), to deal in the Software without
 13 * restriction, including without limitation the rights to use,
 14 * copy, modify, merge, publish, distribute, sublicense, and/or
 15 * sell copies of the Software, and to permit persons to whom the
 16 * Software is furnished to do so, subject to the following
 17 * conditions:
 18 *
 19 * The above copyright notice and this permission notice shall be
 20 * included in all copies or substantial portions of the Software.
 21 *
 22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 23 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 24 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 25 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 26 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 27 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 28 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 29 * OTHER DEALINGS IN THE SOFTWARE.
 30 */
 31package edu.vub.at.trace;
 32
 33import edu.vub.at.actors.ATAsyncMessage;
 34import edu.vub.at.actors.ATLetter;
 35import edu.vub.at.eval.Evaluator;
 36import edu.vub.at.eval.InvocationStack;
 37import edu.vub.at.exceptions.InterpreterException;
 38import edu.vub.at.objects.ATClosure;
 39import edu.vub.at.objects.ATMessage;
 40import edu.vub.at.objects.ATObject;
 41import edu.vub.at.objects.ATTable;
 42import edu.vub.at.objects.grammar.ATBegin;
 43import edu.vub.at.objects.grammar.ATExpression;
 44import edu.vub.at.objects.grammar.ATStatement;
 45import edu.vub.at.objects.natives.NATNil;
 46import edu.vub.at.parser.SourceLocation;
 47import edu.vub.at.util.logging.Logging;
 48
 49import java.io.IOException;
 50import java.io.Writer;
 51import java.util.HashSet;
 52import java.util.Set;
 53
 54/**
 55 * An instance of this class can be used to generate trace events
 56 * of sent and received messages. The tracelog emitted by this tracer
 57 * is in the JSON format defined by the Waterken server and can be
 58 * inspected by post-mortem distributed debuggers such as Causeway.
 59 */
 60public class Tracer {
 61	
 62    private final JSONWriter outer;
 63    private final JSONWriter.ArrayWriter out;
 64    private final Set filteredSources;
 65    private final Marker mark;
 66
 67    /**
 68     * Constructs a trace event generator.
 69     * @param stderr    log event output stream
 70     * @param mark      event counter
 71     */
 72	public Tracer(final Writer stderr, final Marker mark) throws IOException {
 73	    outer = JSONWriter.make(stderr);
 74	    out = outer.startArray();
 75	    filteredSources = new HashSet();
 76	    this.mark = mark;
 77	}
 78        
 79    /**
 80     * Closes the log.
 81     */
 82	public void close() {
 83    	try {
 84			out.finish();
 85		} catch (IOException e) {
 86			Logging.EventLoop_LOG.warn("Unable to close Causeway log", e);
 87		}
 88    }
 89    
 90    /**
 91     * Filters out all stack traces from the given source file.
 92     */
 93    public void filter(String sourceFile) {
 94    	filteredSources.add(sourceFile);
 95    }
 96    
 97    
 98    /**
 99     * Logs a comment.
100     * @param text  comment text
101     * 
102     * { "class"      : [ "org.ref_send.log.Comment", "org.ref_send.log.Event" ]
103     *   "anchor" : ...
104     *   "trace"  : ...
105     *   "text"   : text }
106     */
107    public void comment(final String text) {
108    	try {
109			JSONWriter.ObjectWriter json = out.startElement().startObject();
110			writeClassAndAnchor(json, "Comment", mark.apply());
111			json.startMember("text").writeString(text);
112			writeTrace(json, Tracer.traceHere(filteredSources));
113			json.finish();
114		} catch (IOException e) {
115			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
116		}
117        // stderr.apply(new Comment(mark.apply(),tracer.traceHere(),text));
118    }
119    
120    /**
121     * Logs an exception.
122     * @param reason    problem reason
123     * 
124     * { "class"      : [ "org.ref_send.log.Problem", "org.ref_send.log.Event" ]
125     *   "anchor" : ...
126     *   "trace"  : ...
127     *   "text"   : text
128     *   "reason" : reason }
129     */
130    public void problem(final InterpreterException reason) {
131    	try {
132			JSONWriter.ObjectWriter json = out.startElement().startObject();
133			writeClassAndAnchor(json, "Problem", mark.apply());
134			json.startMember("text").writeString(Tracer.readException(reason));
135			writeException(json.startMember("reason"), reason);
136			writeTrace(json, Tracer.traceException(reason));
137			json.finish();
138    	} catch (IOException e) {
139			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
140		}
141    	
142        //stderr.apply(new Problem(mark.apply(),
143        //                         tracer.traceException(reason),
144        //                         tracer.readException(reason), reason));
145    }
146    
147    /**
148     * Logs receipt of a message.
149     * @param message   message identifier
150     * @param message   a letter identifying an asynchronously sent AmbientTalk message
151     * 
152     * { "class"      : [ "org.ref_send.log.Got", "org.ref_send.log.Event" ]
153     *   "anchor" : ...
154     *   "trace"  : ...
155     *   "message" : message }
156     */
157    public void got(final String message, final ATLetter letter) {
158    	try {
159			JSONWriter.ObjectWriter json = out.startElement().startObject();
160			writeClassAndAnchor(json, "Got", mark.apply());
161			json.startMember("message").writeString(message);
162			Tracer.traceAsyncMessage(letter).toJSON(json.startMember("trace"));
163			json.finish();
164		} catch (IOException e) {
165			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
166		}
167    	
168        //if (null != concrete && null != method &&
169        //        !Modifier.isStatic(method.getModifiers())){
170        //    try {
171        //        method = Reflection.method(concrete, method.getName(),
172        //                                   method.getParameterTypes());
173        //    } catch (final NoSuchMethodException e) {}
174        //}
175        //stderr.apply(new Got(mark.apply(),
176        //    null!=method ? tracer.traceMember(method) : null, message));
177    }
178
179    
180    /**
181     * Logs a message send.
182     * @param message   sent message identifier
183     * 
184     * { "class"      : [ "org.ref_send.log.Sent", "org.ref_send.log.Event" ]
185     *   "anchor" : ...
186     *   "trace"  : ...
187     *   "message": message }
188     */
189    public void sent(final String message) {
190    	try {
191			JSONWriter.ObjectWriter json = out.startElement().startObject();
192			writeClassAndAnchor(json, "Sent", mark.apply());
193			json.startMember("message").writeString(message);
194			writeTrace(json, Tracer.traceHere(filteredSources));
195			json.finish();
196		} catch (IOException e) {
197			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
198		}
199    	
200        // stderr.apply(new Sent(mark.apply(),tracer.traceHere(),message));
201    }
202
203    /**
204     * Logs sending of a return value.
205     * @param message   return message identifier
206     * 
207     * { "class"  : [ "org.ref_send.log.Returned", "org.ref_send.log.Sent", "org.ref_send.log.Event" ]
208     *   "anchor" : ...
209     *   "trace"  : ...
210     *   "message": message }
211     */
212    public void returned(final String message) {
213    	try {
214			JSONWriter.ObjectWriter json = out.startElement().startObject();
215			writeClassAndAnchor(json, new String[] { "Returned", "Sent" }, mark.apply());
216			json.startMember("message").writeString(message);
217			writeTrace(json, Tracer.traceHere(filteredSources));
218			json.finish();
219		} catch (IOException e) {
220			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
221		}
222    	
223        // stderr.apply(new Returned(mark.apply(), null, message));
224    }
225
226    /**
227     * Logs a conditional message send.
228     * @param message   message identifier
229     * @param condition condition identifier
230     * 
231     * { "class"      : [ "org.ref_send.log.SentIf", "org.ref_send.log.Sent", "org.ref_send.log.Event" ]
232     *   "anchor" : ...
233     *   "trace"  : ...
234     *   "message": message
235     *   "condition" : condition }
236     */
237    public void sentIf(final String message, final String condition) {
238    	try {
239			JSONWriter.ObjectWriter json = out.startElement().startObject();
240			writeClassAndAnchor(json, new String[] { "SentIf", "Sent" }, mark.apply());
241			json.startMember("condition").writeString(condition);
242			json.startMember("message").writeString(message);
243			writeTrace(json, Tracer.traceHere(filteredSources));
244			json.finish();
245		} catch (IOException e) {
246			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
247		}
248        // stderr.apply(new SentIf(mark.apply(), tracer.traceHere(),
249        //                        message, condition));
250    }
251
252    /**
253     * Logs resolution of a promise.
254     * @param condition condition identifier
255     * 
256     * { "class"      : [ "org.ref_send.log.Resolved", "org.ref_send.log.Event" ]
257     *   "anchor" : ...
258     *   "trace"  : ...
259     *   "condition" : condition }
260     */
261    public void resolved(final String condition) {
262    	try {
263			JSONWriter.ObjectWriter json = out.startElement().startObject();
264			writeClassAndAnchor(json, new String[] { "Resolved" }, mark.apply());
265			json.startMember("condition").writeString(condition);
266			writeTrace(json, Tracer.traceHere(filteredSources));
267			json.finish();
268		} catch (IOException e) {
269			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
270		}
271    	
272        //stderr.apply(new Resolved(mark.apply(), tracer.traceHere(),
273        //                          condition));
274    }
275
276    /**
277     * Logs fulfillment of a promise.
278     * @param condition condition identifier
279     * @param fromReceiver optional receiver object that fulfilled the message
280     * @param fromMessage optional message that was fulfilled
281     * If fromReceiver and fromMessage are given, the last expr in the source of the method it denotes
282     * is prepended to the tracelog
283     * 
284     * { "class"      : [ "org.ref_send.log.Fulfilled", "org.ref_send.log.Resolved", "org.ref_send.log.Event" ]
285     *   "anchor" : ...
286     *   "trace"  : ...
287     *   "condition" : condition }
288     */
289    public void fulfilled(final String condition, final ATObject fromReceiver, final ATObject fromMessage) {
290    	try {
291			JSONWriter.ObjectWriter json = out.startElement().startObject();
292			writeClassAndAnchor(json, new String[] { "Fulfilled", "Resolved" }, mark.apply());
293			json.startMember("condition").writeString(condition);
294			writeTrace(json, (fromReceiver.equals(Evaluator.getNil())) ?
295				Tracer.traceHere(filteredSources) :
296				Tracer.traceHereStartingWith(fromReceiver, fromMessage.asMessage(), filteredSources));
297			json.finish();
298		} catch (Exception e) {
299			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
300		}
301    	
302        // stderr.apply(new Fulfilled(mark.apply(), tracer.traceHere(),
303        //                           condition));
304    }
305    
306    /**
307     * Logs rejection of a promise.
308     * @param condition condition identifier
309     * @param fromReceiver an optional receiver object that rejected the message
310     * @param fromMessage optional message that was rejected
311     * 
312     * If fromReceiver and fromMessage are given, the last expr in the source of the method it denotes
313     * is prepended to the tracelog
314     * 
315     * { "class"      : [ "org.ref_send.log.Rejected", "org.ref_send.log.Resolved", "org.ref_send.log.Event" ]
316     *   "anchor" : ...
317     *   "trace"  : ...
318     *   "condition" : condition
319     *   "reason" : reason }
320     */
321    public void rejected(final String condition, final InterpreterException reason, final ATObject fromReceiver, final ATObject fromMessage) {
322    	try {
323			JSONWriter.ObjectWriter json = out.startElement().startObject();
324			writeClassAndAnchor(json, new String[] { "Rejected", "Resolved" }, mark.apply());
325			json.startMember("condition").writeString(condition);
326			writeException(json.startMember("reason"), reason);
327			writeTrace(json, (fromReceiver.equals(Evaluator.getNil())) ?
328					Tracer.traceHere(filteredSources) :
329					Tracer.traceHereStartingWith(fromReceiver, fromMessage.asMessage(), filteredSources));
330			json.finish();
331		} catch (Exception e) {
332			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
333		}
334    	
335        // stderr.apply(new Rejected(mark.apply(), tracer.traceHere(),
336        //                          condition, reason));
337    }
338    
339    /**
340     * Logs progress towards fulfillment of a promise.
341     * @param condition condition identifier
342     * 
343     * { "class"      : [ "org.ref_send.log.Progressed", "org.ref_send.log.Resolved", "org.ref_send.log.Event" ]
344     *   "anchor" : ...
345     *   "trace"  : ...
346     *   "condition" : condition }
347     */
348    public void progressed(final String condition) {
349    	try {
350			JSONWriter.ObjectWriter json = out.startElement().startObject();
351			writeClassAndAnchor(json, new String[] { "Progressed", "Resolved" }, mark.apply());
352			json.startMember("condition").writeString(condition);
353			writeTrace(json, Tracer.traceHere(filteredSources));
354			json.finish();
355		} catch (IOException e) {
356			Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
357		}
358    	
359        // stderr.apply(new Progressed(mark.apply(), tracer.traceHere(),
360        //                            condition));
361    }
362    
363    /**
364     * Gets the text message from an AmbientTalk exception.
365     * @param e exception to extract message from
366     */
367    protected static String readException(final InterpreterException e) {
368    	return e.getMessage();
369    }  
370       
371    /**
372     * Gets the stack trace for a given AmbientTalk exception.
373     * @param e exception to trace
374     */
375    protected static Trace traceException(final InterpreterException e) {
376    	return e.getAmbientTalkStackTrace().generateTrace(new HashSet());
377    }
378    
379    /**
380     * Produces a trace consisting of only the given asynchronous message
381     */
382    protected static Trace traceAsyncMessage(final ATLetter letter) {
383    	
384		String name = "unprintable message";
385		ATClosure slot = null;
386		SourceLocation loc = null;
387		try {
388			ATAsyncMessage msg = letter.base_message();
389			name = msg.base_selector()+Evaluator.printAsList(msg.base_arguments()).javaValue;
390			ATObject rcvr = letter.base_receiver();
391			loc = rcvr.impl_getSourceOf(msg.base_selector());
392			
393			//slot = rcvr.meta_select(rcvr, msg.base_selector());
394			//SourceLocation loc = (slot == null) ? null : slot.impl_getLocation();
395		} catch (InterpreterException e) {}
396		
397		String source = null;
398		int[][] span = null;
399		if (loc != null) {
400          source = loc.fileName;
401		  span = new int[][] { new int[] { loc.line, loc.column } };
402		}
403		
404    	return new Trace(new CallSite[] {
405        		new CallSite(name,
406        				     source,
407        				     span) } );
408    }
409    
410    /**
411     * Gets the current stack trace.
412     */
413    protected static Trace traceHere(java.util.Set sourceFilter) {
414    	return InvocationStack.captureInvocationStack().generateTrace(sourceFilter);
415    }
416
417    
418    protected static Trace traceHereStartingWith(ATObject rcvr, ATMessage msg, java.util.Set sourceFilter) {
419		String name = "unprintable message";
420		ATClosure slot = null;
421		SourceLocation loc = null;
422		try {
423			name = msg.base_selector()+Evaluator.printAsList(msg.base_arguments()).javaValue;
424			slot = rcvr.meta_select(rcvr, msg.base_selector());
425			loc = slot.base_method().base_bodyExpression().impl_getLocation();	
426		} catch (InterpreterException e) {}
427		
428		String source = null;
429		int[][] span = null;
430		if (loc != null) {
431          source = loc.fileName;
432		  span = new int[][] { new int[] { loc.line, loc.column } };
433		}
434		
435		Trace t = InvocationStack.captureInvocationStack().generateTrace(sourceFilter);
436		// now prepend the callsite identified by loc to the runtime stack
437		CallSite[] stack = t.calls;
438		CallSite[] stackPlusLetter = new CallSite[stack.length+1];
439		stackPlusLetter[0] = new CallSite(name, source, span);
440		System.arraycopy(stack, 0, stackPlusLetter, 1, stack.length);
441    	return new Trace(stackPlusLetter);	
442    }
443
444    private static void writeClassAndAnchor(JSONWriter.ObjectWriter json, String className, Anchor anchor) throws IOException {
445    	writeClassAndAnchor(json, new String[] { className }, anchor);
446    }
447    
448    private static void writeClassAndAnchor(JSONWriter.ObjectWriter json, String[] classNames, Anchor anchor) throws IOException {
449    	JSONWriter.ArrayWriter classes = json.startMember("class").startArray();
450    	for (int i = 0; i < classNames.length; i++) {
451        	classes.startElement().writeString("org.ref_send.log."+classNames[i]);
452		}
453    	classes.startElement().writeString("org.ref_send.log.Event");
454    	classes.finish();
455    	anchor.toJSON(json.startMember("anchor"));
456    }
457    
458    /**
459     * "anchor" : {
460     *   "number" : n,
461     *   "turn" : {
462     *     "loop" : "event-loop-name",
463     *     "number" : n
464     *   }
465     * }
466     * 
467     * "trace" : {
468     *   "calls" : [ {
469     *      "name" : "foo()",
470     *      "source" : "foo.at",
471     *      "span" : [ [ lineNo ] ]
472     *    } ]
473     * }
474     */
475    private static void writeTrace(JSONWriter.ObjectWriter json, Trace trace) throws IOException {
476    	trace.toJSON(json.startMember("trace"));
477    }
478        
479    /**
480     * { "type" : "Exception"
481     *   "message" : "message"
482     *   "cause" : ... }
483     */
484    private static void writeException(JSONWriter json, Throwable t) throws IOException {
485    	JSONWriter.ObjectWriter exc = json.startObject();
486    	exc.startMember("type").writeString(t.getClass().getSimpleName());
487    	exc.startMember("message").writeString(t.getMessage());
488    	if (t.getCause() != null) {
489        	writeException(exc.startMember("cause"), t.getCause());            		
490    	};
491    	exc.finish();
492    }
493}