/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. */
  31. package edu.vub.at.trace;
  32. import edu.vub.at.actors.ATAsyncMessage;
  33. import edu.vub.at.actors.ATLetter;
  34. import edu.vub.at.eval.Evaluator;
  35. import edu.vub.at.eval.InvocationStack;
  36. import edu.vub.at.exceptions.InterpreterException;
  37. import edu.vub.at.objects.ATClosure;
  38. import edu.vub.at.objects.ATMessage;
  39. import edu.vub.at.objects.ATObject;
  40. import edu.vub.at.objects.ATTable;
  41. import edu.vub.at.objects.grammar.ATBegin;
  42. import edu.vub.at.objects.grammar.ATExpression;
  43. import edu.vub.at.objects.grammar.ATStatement;
  44. import edu.vub.at.objects.natives.NATNil;
  45. import edu.vub.at.parser.SourceLocation;
  46. import edu.vub.at.util.logging.Logging;
  47. import java.io.IOException;
  48. import java.io.Writer;
  49. import java.util.HashSet;
  50. import java.util.Set;
  51. /**
  52. * An instance of this class can be used to generate trace events
  53. * of sent and received messages. The tracelog emitted by this tracer
  54. * is in the JSON format defined by the Waterken server and can be
  55. * inspected by post-mortem distributed debuggers such as Causeway.
  56. */
  57. public class Tracer {
  58. private final JSONWriter outer;
  59. private final JSONWriter.ArrayWriter out;
  60. private final Set filteredSources;
  61. private final Marker mark;
  62. /**
  63. * Constructs a trace event generator.
  64. * @param stderr log event output stream
  65. * @param mark event counter
  66. */
  67. public Tracer(final Writer stderr, final Marker mark) throws IOException {
  68. outer = JSONWriter.make(stderr);
  69. out = outer.startArray();
  70. filteredSources = new HashSet();
  71. this.mark = mark;
  72. }
  73. /**
  74. * Closes the log.
  75. */
  76. public void close() {
  77. try {
  78. out.finish();
  79. } catch (IOException e) {
  80. Logging.EventLoop_LOG.warn("Unable to close Causeway log", e);
  81. }
  82. }
  83. /**
  84. * Filters out all stack traces from the given source file.
  85. */
  86. public void filter(String sourceFile) {
  87. filteredSources.add(sourceFile);
  88. }
  89. /**
  90. * Logs a comment.
  91. * @param text comment text
  92. *
  93. * { "class" : [ "org.ref_send.log.Comment", "org.ref_send.log.Event" ]
  94. * "anchor" : ...
  95. * "trace" : ...
  96. * "text" : text }
  97. */
  98. public void comment(final String text) {
  99. try {
  100. JSONWriter.ObjectWriter json = out.startElement().startObject();
  101. writeClassAndAnchor(json, "Comment", mark.apply());
  102. json.startMember("text").writeString(text);
  103. writeTrace(json, Tracer.traceHere(filteredSources));
  104. json.finish();
  105. } catch (IOException e) {
  106. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  107. }
  108. // stderr.apply(new Comment(mark.apply(),tracer.traceHere(),text));
  109. }
  110. /**
  111. * Logs an exception.
  112. * @param reason problem reason
  113. *
  114. * { "class" : [ "org.ref_send.log.Problem", "org.ref_send.log.Event" ]
  115. * "anchor" : ...
  116. * "trace" : ...
  117. * "text" : text
  118. * "reason" : reason }
  119. */
  120. public void problem(final InterpreterException reason) {
  121. try {
  122. JSONWriter.ObjectWriter json = out.startElement().startObject();
  123. writeClassAndAnchor(json, "Problem", mark.apply());
  124. json.startMember("text").writeString(Tracer.readException(reason));
  125. writeException(json.startMember("reason"), reason);
  126. writeTrace(json, Tracer.traceException(reason));
  127. json.finish();
  128. } catch (IOException e) {
  129. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  130. }
  131. //stderr.apply(new Problem(mark.apply(),
  132. // tracer.traceException(reason),
  133. // tracer.readException(reason), reason));
  134. }
  135. /**
  136. * Logs receipt of a message.
  137. * @param message message identifier
  138. * @param message a letter identifying an asynchronously sent AmbientTalk message
  139. *
  140. * { "class" : [ "org.ref_send.log.Got", "org.ref_send.log.Event" ]
  141. * "anchor" : ...
  142. * "trace" : ...
  143. * "message" : message }
  144. */
  145. public void got(final String message, final ATLetter letter) {
  146. try {
  147. JSONWriter.ObjectWriter json = out.startElement().startObject();
  148. writeClassAndAnchor(json, "Got", mark.apply());
  149. json.startMember("message").writeString(message);
  150. Tracer.traceAsyncMessage(letter).toJSON(json.startMember("trace"));
  151. json.finish();
  152. } catch (IOException e) {
  153. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  154. }
  155. //if (null != concrete && null != method &&
  156. // !Modifier.isStatic(method.getModifiers())){
  157. // try {
  158. // method = Reflection.method(concrete, method.getName(),
  159. // method.getParameterTypes());
  160. // } catch (final NoSuchMethodException e) {}
  161. //}
  162. //stderr.apply(new Got(mark.apply(),
  163. // null!=method ? tracer.traceMember(method) : null, message));
  164. }
  165. /**
  166. * Logs a message send.
  167. * @param message sent message identifier
  168. *
  169. * { "class" : [ "org.ref_send.log.Sent", "org.ref_send.log.Event" ]
  170. * "anchor" : ...
  171. * "trace" : ...
  172. * "message": message }
  173. */
  174. public void sent(final String message) {
  175. try {
  176. JSONWriter.ObjectWriter json = out.startElement().startObject();
  177. writeClassAndAnchor(json, "Sent", mark.apply());
  178. json.startMember("message").writeString(message);
  179. writeTrace(json, Tracer.traceHere(filteredSources));
  180. json.finish();
  181. } catch (IOException e) {
  182. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  183. }
  184. // stderr.apply(new Sent(mark.apply(),tracer.traceHere(),message));
  185. }
  186. /**
  187. * Logs sending of a return value.
  188. * @param message return message identifier
  189. *
  190. * { "class" : [ "org.ref_send.log.Returned", "org.ref_send.log.Sent", "org.ref_send.log.Event" ]
  191. * "anchor" : ...
  192. * "trace" : ...
  193. * "message": message }
  194. */
  195. public void returned(final String message) {
  196. try {
  197. JSONWriter.ObjectWriter json = out.startElement().startObject();
  198. writeClassAndAnchor(json, new String[] { "Returned", "Sent" }, mark.apply());
  199. json.startMember("message").writeString(message);
  200. writeTrace(json, Tracer.traceHere(filteredSources));
  201. json.finish();
  202. } catch (IOException e) {
  203. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  204. }
  205. // stderr.apply(new Returned(mark.apply(), null, message));
  206. }
  207. /**
  208. * Logs a conditional message send.
  209. * @param message message identifier
  210. * @param condition condition identifier
  211. *
  212. * { "class" : [ "org.ref_send.log.SentIf", "org.ref_send.log.Sent", "org.ref_send.log.Event" ]
  213. * "anchor" : ...
  214. * "trace" : ...
  215. * "message": message
  216. * "condition" : condition }
  217. */
  218. public void sentIf(final String message, final String condition) {
  219. try {
  220. JSONWriter.ObjectWriter json = out.startElement().startObject();
  221. writeClassAndAnchor(json, new String[] { "SentIf", "Sent" }, mark.apply());
  222. json.startMember("condition").writeString(condition);
  223. json.startMember("message").writeString(message);
  224. writeTrace(json, Tracer.traceHere(filteredSources));
  225. json.finish();
  226. } catch (IOException e) {
  227. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  228. }
  229. // stderr.apply(new SentIf(mark.apply(), tracer.traceHere(),
  230. // message, condition));
  231. }
  232. /**
  233. * Logs resolution of a promise.
  234. * @param condition condition identifier
  235. *
  236. * { "class" : [ "org.ref_send.log.Resolved", "org.ref_send.log.Event" ]
  237. * "anchor" : ...
  238. * "trace" : ...
  239. * "condition" : condition }
  240. */
  241. public void resolved(final String condition) {
  242. try {
  243. JSONWriter.ObjectWriter json = out.startElement().startObject();
  244. writeClassAndAnchor(json, new String[] { "Resolved" }, mark.apply());
  245. json.startMember("condition").writeString(condition);
  246. writeTrace(json, Tracer.traceHere(filteredSources));
  247. json.finish();
  248. } catch (IOException e) {
  249. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  250. }
  251. //stderr.apply(new Resolved(mark.apply(), tracer.traceHere(),
  252. // condition));
  253. }
  254. /**
  255. * Logs fulfillment of a promise.
  256. * @param condition condition identifier
  257. * @param fromReceiver optional receiver object that fulfilled the message
  258. * @param fromMessage optional message that was fulfilled
  259. * If fromReceiver and fromMessage are given, the last expr in the source of the method it denotes
  260. * is prepended to the tracelog
  261. *
  262. * { "class" : [ "org.ref_send.log.Fulfilled", "org.ref_send.log.Resolved", "org.ref_send.log.Event" ]
  263. * "anchor" : ...
  264. * "trace" : ...
  265. * "condition" : condition }
  266. */
  267. public void fulfilled(final String condition, final ATObject fromReceiver, final ATObject fromMessage) {
  268. try {
  269. JSONWriter.ObjectWriter json = out.startElement().startObject();
  270. writeClassAndAnchor(json, new String[] { "Fulfilled", "Resolved" }, mark.apply());
  271. json.startMember("condition").writeString(condition);
  272. writeTrace(json, (fromReceiver.equals(Evaluator.getNil())) ?
  273. Tracer.traceHere(filteredSources) :
  274. Tracer.traceHereStartingWith(fromReceiver, fromMessage.asMessage(), filteredSources));
  275. json.finish();
  276. } catch (Exception e) {
  277. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  278. }
  279. // stderr.apply(new Fulfilled(mark.apply(), tracer.traceHere(),
  280. // condition));
  281. }
  282. /**
  283. * Logs rejection of a promise.
  284. * @param condition condition identifier
  285. * @param fromReceiver an optional receiver object that rejected the message
  286. * @param fromMessage optional message that was rejected
  287. *
  288. * If fromReceiver and fromMessage are given, the last expr in the source of the method it denotes
  289. * is prepended to the tracelog
  290. *
  291. * { "class" : [ "org.ref_send.log.Rejected", "org.ref_send.log.Resolved", "org.ref_send.log.Event" ]
  292. * "anchor" : ...
  293. * "trace" : ...
  294. * "condition" : condition
  295. * "reason" : reason }
  296. */
  297. public void rejected(final String condition, final InterpreterException reason, final ATObject fromReceiver, final ATObject fromMessage) {
  298. try {
  299. JSONWriter.ObjectWriter json = out.startElement().startObject();
  300. writeClassAndAnchor(json, new String[] { "Rejected", "Resolved" }, mark.apply());
  301. json.startMember("condition").writeString(condition);
  302. writeException(json.startMember("reason"), reason);
  303. writeTrace(json, (fromReceiver.equals(Evaluator.getNil())) ?
  304. Tracer.traceHere(filteredSources) :
  305. Tracer.traceHereStartingWith(fromReceiver, fromMessage.asMessage(), filteredSources));
  306. json.finish();
  307. } catch (Exception e) {
  308. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  309. }
  310. // stderr.apply(new Rejected(mark.apply(), tracer.traceHere(),
  311. // condition, reason));
  312. }
  313. /**
  314. * Logs progress towards fulfillment of a promise.
  315. * @param condition condition identifier
  316. *
  317. * { "class" : [ "org.ref_send.log.Progressed", "org.ref_send.log.Resolved", "org.ref_send.log.Event" ]
  318. * "anchor" : ...
  319. * "trace" : ...
  320. * "condition" : condition }
  321. */
  322. public void progressed(final String condition) {
  323. try {
  324. JSONWriter.ObjectWriter json = out.startElement().startObject();
  325. writeClassAndAnchor(json, new String[] { "Progressed", "Resolved" }, mark.apply());
  326. json.startMember("condition").writeString(condition);
  327. writeTrace(json, Tracer.traceHere(filteredSources));
  328. json.finish();
  329. } catch (IOException e) {
  330. Logging.EventLoop_LOG.warn("Unable to log Causeway event", e);
  331. }
  332. // stderr.apply(new Progressed(mark.apply(), tracer.traceHere(),
  333. // condition));
  334. }
  335. /**
  336. * Gets the text message from an AmbientTalk exception.
  337. * @param e exception to extract message from
  338. */
  339. protected static String readException(final InterpreterException e) {
  340. return e.getMessage();
  341. }
  342. /**
  343. * Gets the stack trace for a given AmbientTalk exception.
  344. * @param e exception to trace
  345. */
  346. protected static Trace traceException(final InterpreterException e) {
  347. return e.getAmbientTalkStackTrace().generateTrace(new HashSet());
  348. }
  349. /**
  350. * Produces a trace consisting of only the given asynchronous message
  351. */
  352. protected static Trace traceAsyncMessage(final ATLetter letter) {
  353. String name = "unprintable message";
  354. ATClosure slot = null;
  355. SourceLocation loc = null;
  356. try {
  357. ATAsyncMessage msg = letter.base_message();
  358. name = msg.base_selector()+Evaluator.printAsList(msg.base_arguments()).javaValue;
  359. ATObject rcvr = letter.base_receiver();
  360. loc = rcvr.impl_getSourceOf(msg.base_selector());
  361. //slot = rcvr.meta_select(rcvr, msg.base_selector());
  362. //SourceLocation loc = (slot == null) ? null : slot.impl_getLocation();
  363. } catch (InterpreterException e) {}
  364. String source = null;
  365. int[][] span = null;
  366. if (loc != null) {
  367. source = loc.fileName;
  368. span = new int[][] { new int[] { loc.line, loc.column } };
  369. }
  370. return new Trace(new CallSite[] {
  371. new CallSite(name,
  372. source,
  373. span) } );
  374. }
  375. /**
  376. * Gets the current stack trace.
  377. */
  378. protected static Trace traceHere(java.util.Set sourceFilter) {
  379. return InvocationStack.captureInvocationStack().generateTrace(sourceFilter);
  380. }
  381. protected static Trace traceHereStartingWith(ATObject rcvr, ATMessage msg, java.util.Set sourceFilter) {
  382. String name = "unprintable message";
  383. ATClosure slot = null;
  384. SourceLocation loc = null;
  385. try {
  386. name = msg.base_selector()+Evaluator.printAsList(msg.base_arguments()).javaValue;
  387. slot = rcvr.meta_select(rcvr, msg.base_selector());
  388. loc = slot.base_method().base_bodyExpression().impl_getLocation();
  389. } catch (InterpreterException e) {}
  390. String source = null;
  391. int[][] span = null;
  392. if (loc != null) {
  393. source = loc.fileName;
  394. span = new int[][] { new int[] { loc.line, loc.column } };
  395. }
  396. Trace t = InvocationStack.captureInvocationStack().generateTrace(sourceFilter);
  397. // now prepend the callsite identified by loc to the runtime stack
  398. CallSite[] stack = t.calls;
  399. CallSite[] stackPlusLetter = new CallSite[stack.length+1];
  400. stackPlusLetter[0] = new CallSite(name, source, span);
  401. System.arraycopy(stack, 0, stackPlusLetter, 1, stack.length);
  402. return new Trace(stackPlusLetter);
  403. }
  404. private static void writeClassAndAnchor(JSONWriter.ObjectWriter json, String className, Anchor anchor) throws IOException {
  405. writeClassAndAnchor(json, new String[] { className }, anchor);
  406. }
  407. private static void writeClassAndAnchor(JSONWriter.ObjectWriter json, String[] classNames, Anchor anchor) throws IOException {
  408. JSONWriter.ArrayWriter classes = json.startMember("class").startArray();
  409. for (int i = 0; i < classNames.length; i++) {
  410. classes.startElement().writeString("org.ref_send.log."+classNames[i]);
  411. }
  412. classes.startElement().writeString("org.ref_send.log.Event");
  413. classes.finish();
  414. anchor.toJSON(json.startMember("anchor"));
  415. }
  416. /**
  417. * "anchor" : {
  418. * "number" : n,
  419. * "turn" : {
  420. * "loop" : "event-loop-name",
  421. * "number" : n
  422. * }
  423. * }
  424. *
  425. * "trace" : {
  426. * "calls" : [ {
  427. * "name" : "foo()",
  428. * "source" : "foo.at",
  429. * "span" : [ [ lineNo ] ]
  430. * } ]
  431. * }
  432. */
  433. private static void writeTrace(JSONWriter.ObjectWriter json, Trace trace) throws IOException {
  434. trace.toJSON(json.startMember("trace"));
  435. }
  436. /**
  437. * { "type" : "Exception"
  438. * "message" : "message"
  439. * "cause" : ... }
  440. */
  441. private static void writeException(JSONWriter json, Throwable t) throws IOException {
  442. JSONWriter.ObjectWriter exc = json.startObject();
  443. exc.startMember("type").writeString(t.getClass().getSimpleName());
  444. exc.startMember("message").writeString(t.getMessage());
  445. if (t.getCause() != null) {
  446. writeException(exc.startMember("cause"), t.getCause());
  447. };
  448. exc.finish();
  449. }
  450. }