/interpreter/tags/at2dist220411/src/edu/vub/at/trace/Tracer.java
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}