# HG changeset patch # User Michael Van De Vanter # Date 1416787643 28800 # Node ID e3c95cbbb50cd73f83f9737145623d5eb25ee2e8 # Parent e97e1f07a3d62406451a85d47a24953ad9069f16 Truffle Instrumentation: major API revision, based around the Probe and Instrument classes; add Instrumentable API for language implementors, with most details automated; reimplemented to handle AST splitting automatically; more JUnit tests. diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java Sun Nov 23 16:07:23 2014 -0800 @@ -89,6 +89,7 @@ this.rootNode = rootNode; this.compilationPolicy = compilationPolicy; this.rootNode.adoptChildren(); + this.rootNode.applyInstrumentation(); this.rootNode.setCallTarget(this); this.uninitializedRootNode = sourceCallTarget == null ? cloneRootNode(rootNode) : sourceCallTarget.uninitializedRootNode; if (TruffleCallTargetProfiling.getValue()) { diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InstrumentationTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InstrumentationTest.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InstrumentationTest.java Sun Nov 23 16:07:23 2014 -0800 @@ -22,6 +22,7 @@ */ package com.oracle.truffle.api.test; +import java.lang.reflect.*; import java.util.*; import org.junit.*; @@ -29,6 +30,10 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.Probe.ProbeListener; +import com.oracle.truffle.api.instrument.ProbeNode.Instrumentable; +import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.impl.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; @@ -36,13 +41,13 @@ *

AST Instrumentation

* * Instrumentation allows the insertion into Truffle ASTs language-specific instances of - * {@link Wrapper} that propagate {@link ExecutionEvents} through a {@link Probe} to any instances - * of {@link Instrument} that might be attached to the particular probe by tools. + * {@link WrapperNode} that propagate execution events through a {@link Probe} to any instances of + * {@link Instrument} that might be attached to the particular probe by tools. *
    *
  1. Creates a simple add AST
  2. *
  3. Verifies its structure
  4. - *
  5. "Probes" the add node by adding a {@link Wrapper} and associated {@link Probe}
  6. - *
  7. Attaches a simple {@link Instrument} to the node via its {@link Probe}
  8. + *
  9. "Probes" the add node by adding a {@link WrapperNode} and associated {@link Probe}
  10. + *
  11. Attaches a simple {@link Instrument} to the node via the Probe's {@link ProbeNode}
  12. *
  13. Verifies the structure of the probed AST
  14. *
  15. Verifies the execution of the probed AST
  16. *
  17. Verifies the results observed by the instrument.
  18. @@ -52,245 +57,696 @@ */ public class InstrumentationTest { + private static final SyntaxTag ADD_TAG = new SyntaxTag() { + + public String name() { + return "Addition"; + } + + public String getDescription() { + return "Test Language Addition Node"; + } + }; + + private static final SyntaxTag VALUE_TAG = new SyntaxTag() { + + public String name() { + return "Value"; + } + + public String getDescription() { + return "Test Language Value Node"; + } + }; + @Test - public void test() { - // Build a tree - TruffleRuntime runtime = Truffle.getRuntime(); - TestChildNode leftChild = new TestChildNode(); - TestChildNode rightChild = new TestChildNode(); - TestSourceSection sourceSection = new TestSourceSection(); - TestAddNode addNode = new TestAddNode(leftChild, rightChild, sourceSection); - TestRootNode rootNode = new TestRootNode(addNode); + public void testBasicInstrumentation() { + // Create a simple addition AST + final TruffleRuntime runtime = Truffle.getRuntime(); + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final TestRootNode rootNode = new TestRootNode(addNode); + + // Creating a call target sets the parent pointers in this tree and is necessary prior to + // checking any parent/child relationships + final CallTarget callTarget1 = runtime.createCallTarget(rootNode); + + // Check the tree structure + Assert.assertEquals(addNode, leftValueNode.getParent()); + Assert.assertEquals(addNode, rightValueNode.getParent()); + Iterator iterator = addNode.getChildren().iterator(); + Assert.assertEquals(leftValueNode, iterator.next()); + Assert.assertEquals(rightValueNode, iterator.next()); + Assert.assertFalse(iterator.hasNext()); + Assert.assertEquals(rootNode, addNode.getParent()); + iterator = rootNode.getChildren().iterator(); + Assert.assertEquals(addNode, iterator.next()); + Assert.assertFalse(iterator.hasNext()); + + // Ensure it executes correctly + Assert.assertEquals(13, callTarget1.call()); + + // Probe the addition node + final Probe probe = addNode.probe(); + + // Check the modified tree structure + Assert.assertEquals(addNode, leftValueNode.getParent()); + Assert.assertEquals(addNode, rightValueNode.getParent()); + iterator = addNode.getChildren().iterator(); + Assert.assertEquals(leftValueNode, iterator.next()); + Assert.assertEquals(rightValueNode, iterator.next()); + Assert.assertFalse(iterator.hasNext()); + + // Ensure there's a WrapperNode correctly inserted into the AST + iterator = rootNode.getChildren().iterator(); + Node wrapperNode = iterator.next(); + Assert.assertTrue(wrapperNode instanceof TestLanguageWrapperNode); + Assert.assertFalse(iterator.hasNext()); + Assert.assertEquals(rootNode, wrapperNode.getParent()); + + // Check that the WrapperNode has both the probe and the wrapped node as children + iterator = wrapperNode.getChildren().iterator(); + Assert.assertEquals(addNode, iterator.next()); + ProbeNode probeNode = (ProbeNode) iterator.next(); + Assert.assertTrue(probeNode.getProbe() != null); + Assert.assertFalse(iterator.hasNext()); + + // Check that you can't probe the WrapperNodes + TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode; + try { + wrapper.probe(); + Assert.fail(); + } catch (IllegalStateException e) { + } + + // Check that the "probed" AST still executes correctly + Assert.assertEquals(13, callTarget1.call()); + + // Attach a counting instrument to the probe + final TestCounter counterA = new TestCounter(); + counterA.attach(probe); + + // Attach a second counting instrument to the probe + final TestCounter counterB = new TestCounter(); + counterB.attach(probe); + + // Run it again and check that the two instruments are working + Assert.assertEquals(13, callTarget1.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 1); + Assert.assertEquals(counterB.leaveCount, 1); + + // Remove counterA and check the "instrument chain" + counterA.dispose(); + iterator = probeNode.getChildren().iterator(); + + // Run it again and check that instrument B is still working but not A + Assert.assertEquals(13, callTarget1.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 2); + Assert.assertEquals(counterB.leaveCount, 2); + + // Simulate a split by cloning the AST + final CallTarget callTarget2 = runtime.createCallTarget((TestRootNode) rootNode.copy()); + // Run the clone and check that instrument B is still working but not A + Assert.assertEquals(13, callTarget2.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 3); + Assert.assertEquals(counterB.leaveCount, 3); + + // Run the original and check that instrument B is still working but not A + Assert.assertEquals(13, callTarget2.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 4); + Assert.assertEquals(counterB.leaveCount, 4); + + // Attach a second instrument to the probe + final TestCounter counterC = new TestCounter(); + counterC.attach(probe); + + // Run the original and check that instruments B,C working but not A + Assert.assertEquals(13, callTarget1.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 5); + Assert.assertEquals(counterB.leaveCount, 5); + Assert.assertEquals(counterC.enterCount, 1); + Assert.assertEquals(counterC.leaveCount, 1); + + // Run the clone and check that instruments B,C working but not A + Assert.assertEquals(13, callTarget2.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 6); + Assert.assertEquals(counterB.leaveCount, 6); + Assert.assertEquals(counterC.enterCount, 2); + Assert.assertEquals(counterC.leaveCount, 2); + + // Remove instrumentC + counterC.dispose(); - // Have to create a call target before checking parent/child relationships - CallTarget target = runtime.createCallTarget(rootNode); + // Run the original and check that instrument B working but not A,C + Assert.assertEquals(13, callTarget1.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 7); + Assert.assertEquals(counterB.leaveCount, 7); + Assert.assertEquals(counterC.enterCount, 2); + Assert.assertEquals(counterC.leaveCount, 2); + + // Run the clone and check that instrument B working but not A,C + Assert.assertEquals(13, callTarget2.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 8); + Assert.assertEquals(counterB.leaveCount, 8); + Assert.assertEquals(counterC.enterCount, 2); + Assert.assertEquals(counterC.leaveCount, 2); + + // Remove instrumentB + counterB.dispose(); + + // Run both the original and clone, check that no instruments working + Assert.assertEquals(13, callTarget1.call()); + Assert.assertEquals(13, callTarget2.call()); + Assert.assertEquals(counterA.enterCount, 1); + Assert.assertEquals(counterA.leaveCount, 1); + Assert.assertEquals(counterB.enterCount, 8); + Assert.assertEquals(counterB.leaveCount, 8); + Assert.assertEquals(counterC.enterCount, 2); + Assert.assertEquals(counterC.leaveCount, 2); + } + + @Test + public void testTagging() { + + // Applies appropriate tags + final TestASTProber astProber = new TestASTProber(); + Probe.registerASTProber(astProber); + + // Listens for probes and tags being added + final TestProbeListener probeListener = new TestProbeListener(); + Probe.addProbeListener(probeListener); + + // Counts all entries to all instances of addition nodes + final TestMultiCounter additionCounter = new TestMultiCounter(); + + // Counts all entries to all instances of value nodes + final TestMultiCounter valueCounter = new TestMultiCounter(); + + // Create a simple addition AST + final TruffleRuntime runtime = Truffle.getRuntime(); + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + + final TestRootNode rootNode = new TestRootNode(addNode); + + final CallTarget callTarget = runtime.createCallTarget(rootNode); + + // Check that the prober added probes to the tree + Assert.assertEquals(probeListener.probeCount, 3); + Assert.assertEquals(probeListener.tagCount, 3); + + Assert.assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1); + Assert.assertEquals(Probe.findProbesTaggedAs(VALUE_TAG).size(), 2); + + // Check that it executes correctly + Assert.assertEquals(13, callTarget.call()); + + // Dynamically attach a counter for all executions of all Addition nodes + for (Probe probe : Probe.findProbesTaggedAs(ADD_TAG)) { + additionCounter.attachCounter(probe); + } + // Dynamically attach a counter for all executions of all Value nodes + for (Probe probe : Probe.findProbesTaggedAs(VALUE_TAG)) { + valueCounter.attachCounter(probe); + } + + // Counters initialized at 0 + Assert.assertEquals(additionCounter.count, 0); + Assert.assertEquals(valueCounter.count, 0); + + // Execute again + Assert.assertEquals(13, callTarget.call()); + + // There are two value nodes in the AST, but only one addition node + Assert.assertEquals(additionCounter.count, 1); + Assert.assertEquals(valueCounter.count, 2); + + Probe.unregisterASTProber(astProber); + + } + + @Test + public void testProbeLite() { + + // Use the "lite-probing" option, limited to a single pass of + // probing and a single Instrument at each probed node. This + // particular test uses a shared event receiver at every + // lite-probed node. + final TestEventReceiver receiver = new TestEventReceiver(); + + TestASTLiteProber astLiteProber = new TestASTLiteProber(receiver); + Probe.registerASTProber(astLiteProber); + + // Create a simple addition AST + final TruffleRuntime runtime = Truffle.getRuntime(); + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final TestRootNode rootNode = new TestRootNode(addNode); + + // Creating a call target sets the parent pointers in this tree and is necessary prior to + // checking any parent/child relationships + final CallTarget callTarget = runtime.createCallTarget(rootNode); + + // Check that the instrument is working as expected. + Assert.assertEquals(0, receiver.counter); + callTarget.call(); + Assert.assertEquals(2, receiver.counter); + + // Check that you can't probe a node that's already received a probeLite() call + try { + leftValueNode.probe(); + Assert.fail(); + } catch (IllegalStateException e) { + } + + try { + rightValueNode.probe(); + Assert.fail(); + } catch (IllegalStateException e) { + } // Check tree structure - Assert.assertEquals(addNode, leftChild.getParent()); - Assert.assertEquals(addNode, rightChild.getParent()); + Assert.assertTrue(leftValueNode.getParent() instanceof TestLanguageWrapperNode); + Assert.assertTrue(rightValueNode.getParent() instanceof TestLanguageWrapperNode); + TestLanguageWrapperNode leftWrapper = (TestLanguageWrapperNode) leftValueNode.getParent(); + TestLanguageWrapperNode rightWrapper = (TestLanguageWrapperNode) rightValueNode.getParent(); + Assert.assertEquals(addNode, leftWrapper.getParent()); + Assert.assertEquals(addNode, rightWrapper.getParent()); Iterator iterator = addNode.getChildren().iterator(); - Assert.assertEquals(leftChild, iterator.next()); - Assert.assertEquals(rightChild, iterator.next()); + Assert.assertEquals(leftWrapper, iterator.next()); + Assert.assertEquals(rightWrapper, iterator.next()); Assert.assertFalse(iterator.hasNext()); Assert.assertEquals(rootNode, addNode.getParent()); iterator = rootNode.getChildren().iterator(); Assert.assertEquals(addNode, iterator.next()); Assert.assertFalse(iterator.hasNext()); - Object result = target.call(); - Assert.assertEquals(42, result); - // Create another call target, this time with the "probed" add node - TestExecutionContext context = new TestExecutionContext(); - TestWrapper wrapper = new TestWrapper(addNode, context); - rootNode = new TestRootNode(wrapper); - target = runtime.createCallTarget(rootNode); - - // Check the new tree structure - Assert.assertEquals(addNode, leftChild.getParent()); - Assert.assertEquals(addNode, rightChild.getParent()); - iterator = addNode.getChildren().iterator(); - Assert.assertEquals(leftChild, iterator.next()); - Assert.assertEquals(rightChild, iterator.next()); - Assert.assertFalse(iterator.hasNext()); - Assert.assertEquals(wrapper, addNode.getParent()); - iterator = wrapper.getChildren().iterator(); - Assert.assertEquals(addNode, iterator.next()); - Assert.assertFalse(iterator.hasNext()); - Assert.assertEquals(rootNode, wrapper.getParent()); - iterator = rootNode.getChildren().iterator(); - Assert.assertEquals(wrapper, iterator.next()); - Assert.assertFalse(iterator.hasNext()); - result = target.call(); - Assert.assertEquals(42, result); + // Check that you can't get a probe on the wrappers because they were "lite-probed" + try { + leftWrapper.getProbe(); + Assert.fail(); + } catch (IllegalStateException e) { + } + try { + rightWrapper.getProbe(); + Assert.fail(); + } catch (IllegalStateException e) { + } - // Create some instruments - final TestInstrument instrumentA = new TestInstrument(); - final TestInstrument instrumentB = new TestInstrument(); - - wrapper.getProbe().addInstrument(instrumentA); - - result = target.call(); - Assert.assertEquals(instrumentA.numInstrumentEnter, 1); - Assert.assertEquals(instrumentA.numInstrumentLeave, 1); - Assert.assertEquals(instrumentB.numInstrumentEnter, 0); - Assert.assertEquals(instrumentB.numInstrumentLeave, 0); - Assert.assertEquals(42, result); - - wrapper.getProbe().addInstrument(instrumentB); + // Check that you can't probe the wrappers + try { + leftWrapper.probe(); + Assert.fail(); + } catch (IllegalStateException e) { + } + try { + rightWrapper.probe(); + Assert.fail(); + } catch (IllegalStateException e) { + } + try { + leftWrapper.probeLite(null); + Assert.fail(); + } catch (IllegalStateException e) { + } + try { + rightWrapper.probeLite(null); + Assert.fail(); + } catch (IllegalStateException e) { + } - result = target.call(); - Assert.assertEquals(instrumentA.numInstrumentEnter, 2); - Assert.assertEquals(instrumentA.numInstrumentLeave, 2); - Assert.assertEquals(instrumentB.numInstrumentEnter, 1); - Assert.assertEquals(instrumentB.numInstrumentLeave, 1); - Assert.assertEquals(42, result); + // Use reflection to check that each WrapperNode has a ProbeLiteNode with a + // SimpleEventReceiver + try { + Field probeNodeField = leftWrapper.getClass().getDeclaredField("probeNode"); + + // cheat: probeNode is private, so we change it's accessibility at runtime + probeNodeField.setAccessible(true); + ProbeNode probeNode = (ProbeNode) probeNodeField.get(leftWrapper); + + // hack: Since ProbeLiteNode is not visible, we do a string compare here + Assert.assertTrue(probeNode.getClass().toString().endsWith("ProbeLiteNode")); - wrapper.getProbe().removeInstrument(instrumentA); + // Now we do the same to check the type of the eventReceiver in ProbeLiteNode + Field eventReceiverField = probeNode.getClass().getDeclaredField("eventReceiver"); + eventReceiverField.setAccessible(true); + TruffleEventReceiver eventReceiver = (TruffleEventReceiver) eventReceiverField.get(probeNode); + Assert.assertTrue(eventReceiver instanceof SimpleEventReceiver); - result = target.call(); - Assert.assertEquals(instrumentA.numInstrumentEnter, 2); - Assert.assertEquals(instrumentA.numInstrumentLeave, 2); - Assert.assertEquals(instrumentB.numInstrumentEnter, 2); - Assert.assertEquals(instrumentB.numInstrumentLeave, 2); - Assert.assertEquals(42, result); + // Reset accessibility + probeNodeField.setAccessible(false); + eventReceiverField.setAccessible(false); + + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + Assert.fail(); + } + + Probe.unregisterASTProber(astLiteProber); } - private class TestRootNode extends RootNode { - @Child private RootNode child; + /** + * A guest language usually has a single language-specific subclass of {@link Node} from which + * all other nodes in the guest language subclass. By making this node {@link Instrumentable}, + * we allow all nodes of the guest language to have {@link Probe}s attach to them. + * + */ + private abstract class TestLanguageNode extends Node implements Instrumentable { + public abstract Object execute(VirtualFrame frame); + + public Probe probe() { + Node parent = getParent(); - public TestRootNode(RootNode child) { - super(null); - this.child = child; + if (parent == null) { + throw new IllegalStateException("Cannot call probe() on a node without a parent."); + } + + if (parent instanceof TestLanguageWrapperNode) { + return ((TestLanguageWrapperNode) parent).getProbe(); + } + + // Create a new wrapper/probe with this node as its child. + final TestLanguageWrapperNode wrapper = new TestLanguageWrapperNode(this); + + // Connect it to a Probe + final Probe probe = ProbeNode.insertProbe(wrapper); + + // Replace this node in the AST with the wrapper + this.replace(wrapper); + + return probe; } - @Override - public Object execute(VirtualFrame frame) { - return child.execute(frame); - } - } + public void probeLite(TruffleEventReceiver eventReceiver) { + Node parent = getParent(); - private class TestAddNode extends RootNode { - - @Child private TestChildNode left; - @Child private TestChildNode right; + if (parent == null) { + throw new IllegalStateException("Cannot call probeLite() on a node without a parent"); + } - public TestAddNode(TestChildNode left, TestChildNode right, TestSourceSection sourceSection) { - super(sourceSection); - this.left = left; - this.right = right; - } + if (parent instanceof TestLanguageWrapperNode) { + throw new IllegalStateException("Cannot call probeLite() on a node that already has a wrapper."); + } - @Override - public Object execute(VirtualFrame frame) { - return left.execute() + right.execute(); - } - } + final TestLanguageWrapperNode wrapper = new TestLanguageWrapperNode(this); + ProbeNode.insertProbeLite(wrapper, eventReceiver); - private class TestChildNode extends Node { - - public TestChildNode() { - super(null); - } - - public int execute() { - return 21; + this.replace(wrapper); } } /** * The wrapper node class is usually language-specific and inherits from the language-specific - * subclass of {@link Node}, not {@RootNode}. + * subclass of {@link Node}, in this case, {@link TestLanguageNode}. */ - private class TestWrapper extends RootNode implements Wrapper { - @Child private RootNode child; - private Probe probe; + @NodeInfo(cost = NodeCost.NONE) + private class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode { + @Child private TestLanguageNode child; + @Child private ProbeNode probeNode; - public TestWrapper(RootNode child, ExecutionContext context) { - this.child = insert(child); - this.probe = context.createProbe(child.getSourceSection()); + public TestLanguageWrapperNode(TestLanguageNode child) { + assert !(child instanceof TestLanguageWrapperNode); + this.child = child; } + public String instrumentationInfo() { + return "Wrapper node for testing"; + } + + public void insertProbe(ProbeNode newProbeNode) { + this.probeNode = newProbeNode; + } + + public Probe getProbe() { + try { + return probeNode.getProbe(); + } catch (IllegalStateException e) { + throw new IllegalStateException("Cannot call getProbe() on a wrapper that has no probe"); + } + } + + @Override public Node getChild() { return child; } - public Probe getProbe() { - return probe; + @Override + public Object execute(VirtualFrame frame) { + probeNode.enter(child, frame); + Object result; + + try { + result = child.execute(frame); + probeNode.returnValue(child, frame, result); + } catch (KillException e) { + throw (e); + } catch (Exception e) { + probeNode.returnExceptional(child, frame, e); + throw (e); + } + + return result; + } + + @Override + public Probe probe() { + throw new IllegalStateException("Cannot call probe() on a wrapper."); + } + + @Override + public void probeLite(TruffleEventReceiver eventReceiver) { + throw new IllegalStateException("Cannot call probeLite() on a wrapper."); + } + } + + /** + * A simple node for our test language to store a value. + */ + private class TestValueNode extends TestLanguageNode { + private final int value; + + public TestValueNode(int value) { + this.value = value; } @Override public Object execute(VirtualFrame frame) { - probe.enter(child, frame); - Object result; + return new Integer(this.value); + } + } + + /** + * A node for our test language that adds up two {@link TestValueNode}s. + */ + private class TestAdditionNode extends TestLanguageNode { + @Child private TestLanguageNode leftChild; + @Child private TestLanguageNode rightChild; + + public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild) { + this.leftChild = insert(leftChild); + this.rightChild = insert(rightChild); + } + + @Override + public Object execute(VirtualFrame frame) { + return new Integer(((Integer) leftChild.execute(frame)).intValue() + ((Integer) rightChild.execute(frame)).intValue()); + } + } - try { - result = child.execute(frame); - probe.leave(child, frame, result); - } catch (Exception e) { - probe.leaveExceptional(child, frame, e); - throw (e); - } - return result; + /** + * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST + * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle + * completes an AST. The root nodes serves as our entry point into a program. + */ + private class TestRootNode extends RootNode { + @Child private TestLanguageNode body; + + /** + * This constructor emulates the global machinery that applies registered probers to every + * newly created AST. Global registry is not used, since that would interfere with other + * tests run in the same environment. + */ + public TestRootNode(TestLanguageNode body) { + super(null); + this.body = body; + } + + @Override + public Object execute(VirtualFrame frame) { + return body.execute(frame); + } + + @Override + public boolean isCloningAllowed() { + return true; + } + + @Override + public void applyInstrumentation() { + Probe.applyASTProbers(body); } } /** - * An "empty" description of the source code that might correspond to a particular AST node. The - * instrumentation framework tracks probes that have been inserted by their source location, - * using this as a key. + * A counter for the number of times execution enters and leaves a probed AST node. */ - private class TestSourceSection implements SourceSection { + private class TestCounter { - public Source getSource() { - return null; - } + public int enterCount = 0; + public int leaveCount = 0; + public final Instrument instrument; + + public TestCounter() { + instrument = Instrument.create(new SimpleEventReceiver() { - public int getStartLine() { - return 0; - } + @Override + public void enter(Node node, VirtualFrame frame) { + enterCount++; + } - public LineLocation getLineLocation() { - return null; - } - - public int getStartColumn() { - return 0; + @Override + public void returnAny(Node node, VirtualFrame frame) { + leaveCount++; + } + }, "Instrumentation Test Counter"); } - public int getCharIndex() { - return 0; - } - - public int getCharLength() { - return 0; + public void attach(Probe probe) { + probe.attach(instrument); } - public int getCharEndIndex() { - return 0; - } - - public String getIdentifier() { - return null; - } - - public String getCode() { - return null; - } - - public String getShortDescription() { - return null; + public void dispose() { + instrument.dispose(); } } - private class TestExecutionContext extends ExecutionContext { + /** + * Tags selected nodes on newly constructed ASTs. + */ + private static final class TestASTProber implements NodeVisitor, ASTProber { + + public boolean visit(Node node) { + if (node instanceof TestLanguageNode) { + + final TestLanguageNode testNode = (TestLanguageNode) node; + + if (node instanceof TestValueNode) { + testNode.probe().tagAs(VALUE_TAG, null); + + } else if (node instanceof TestAdditionNode) { + testNode.probe().tagAs(ADD_TAG, null); + + } + } + return true; + } + + public void probeAST(Node node) { + node.accept(this); + } + } + + /** + * "lite-probes" every value node with a shared event receiver. + */ + private static final class TestASTLiteProber implements NodeVisitor, ASTProber { + private final TruffleEventReceiver eventReceiver; + + public TestASTLiteProber(SimpleEventReceiver simpleEventReceiver) { + this.eventReceiver = simpleEventReceiver; + } + + public boolean visit(Node node) { + if (node instanceof TestValueNode) { + final TestLanguageNode testNode = (TestValueNode) node; + testNode.probeLite(eventReceiver); + } + return true; + } + + public void probeAST(Node node) { + node.accept(this); + } + } + + /** + * Counts the number of "enter" events at probed nodes. + * + */ + static final class TestEventReceiver extends SimpleEventReceiver { + + public int counter = 0; @Override - public String getLanguageShortName() { - return "test"; - } - - @Override - protected void setSourceCallback(SourceCallback sourceCallback) { + public void enter(Node node, VirtualFrame frame) { + counter++; } } - private class TestInstrument extends Instrument { + /** + * A counter that can count executions at multiple nodes; it attaches a separate instrument at + * each Probe, but keeps a total count. + */ + private static final class TestMultiCounter { - public int numInstrumentEnter = 0; - public int numInstrumentLeave = 0; + public int count = 0; + + public void attachCounter(Probe probe) { - @Override - public void enter(Node astNode, VirtualFrame frame) { - numInstrumentEnter++; - } + // Attach a new instrument for every Probe + // where we want to count executions. + // it will get copied when ASTs cloned, so + // keep the count in this outer class. + probe.attach(Instrument.create(new SimpleEventReceiver() { - @Override - public void leave(Node astNode, VirtualFrame frame, Object result) { - numInstrumentLeave++; + @Override + public void enter(Node node, VirtualFrame frame) { + count++; + } + }, "Instrumentation Test MultiCounter")); } } + private static final class TestProbeListener implements ProbeListener { + + public int probeCount = 0; + public int tagCount = 0; + + public void newProbeInserted(Probe probe) { + probeCount++; + } + + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + tagCount++; + } + + public void startASTProbing(Source source) { + } + + public void endASTProbing(Source source) { + } + + } + } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/NodeUtilTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/NodeUtilTest.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/NodeUtilTest.java Sun Nov 23 16:07:23 2014 -0800 @@ -22,11 +22,12 @@ */ package com.oracle.truffle.api.test.nodes; +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + import java.util.*; import org.junit.*; -import static org.junit.Assert.*; -import static org.hamcrest.CoreMatchers.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.nodes.*; diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Sun Nov 23 16:07:23 2014 -0800 @@ -24,12 +24,9 @@ */ package com.oracle.truffle.api; -import java.util.*; - import com.oracle.truffle.api.impl.*; import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.instrument.impl.*; -import com.oracle.truffle.api.source.*; /** * Access to information and basic services in the runtime context for a Truffle-implemented guest @@ -39,108 +36,12 @@ */ public abstract class ExecutionContext { - private final ProbeManager probeManager = new ProbeManager(); - private final List sourceListeners = new ArrayList<>(); private Visualizer visualizer = new DefaultVisualizer(); protected ExecutionContext() { } /** - * Sets up the {@link SourceCallback} for this execution context. - */ - public void initialize() { - setSourceCallback(new SourceCallback() { - - public void startLoading(Source source) { - for (SourceListener listener : sourceListeners) { - listener.loadStarting(source); - } - } - - public void endLoading(Source source) { - for (SourceListener listener : sourceListeners) { - listener.loadEnding(source); - } - } - }); - } - - /** - * Registers a tool interested in being notified about the loading of {@link Source}s. - */ - public final void addSourceListener(SourceListener listener) { - assert listener != null; - sourceListeners.add(listener); - } - - /** - * Unregisters a tool interested in being notified about the loading of {@link Source}s. - */ - public final void removeSourceListener(SourceListener removeListener) { - final List listeners = new ArrayList<>(sourceListeners); - for (SourceListener listener : listeners) { - if (listener == removeListener) { - sourceListeners.remove(listener); - } - } - } - - /** - * Registers a tool interested in being notified about the insertion of a newly created - * {@link Probe} into a Truffle AST. - */ - public final void addProbeListener(ProbeListener listener) { - probeManager.addProbeListener(listener); - } - - /** - * Unregisters a tool interested in being notified about the insertion of a newly created - * {@link Probe} into a Truffle AST. - */ - public final void removeProbeListener(ProbeListener listener) { - probeManager.removeProbeListener(listener); - } - - /** - * Return a newly created, untagged, {@link Probe} associated with a particular source section, - * with no requirement that the association be unique. - * - * @return a probe associated with an extent of guest language source code. - */ - public final Probe createProbe(SourceSection source) { - return probeManager.createProbe(source); - } - - /** - * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty - * collection if no probes found. - */ - public final Collection findProbesTaggedAs(SyntaxTag tag) { - return probeManager.findProbesTaggedAs(tag); - } - - /** - * Sets a trap that will make a callback at any AST location where a existing probe holds a - * specified tag; only one trap may be set at a time. - * - * @throws IllegalStateException if a trap is already set - */ - public final void setTagTrap(SyntaxTagTrap trap) throws IllegalStateException { - // TODO (mlvdv) consider allowing multiple traps (without inhibiting Truffle inlining) - probeManager.setTagTrap(trap); - } - - /** - * Clears a trap that will halt execution; only one trap may be set at a time. - * - * @throws IllegalStateException if no trap is set. - */ - public final void clearTagTrap() { - probeManager.clearTagTrap(); - } - - /** * Access to information visualization services for the specific language. */ public final Visualizer getVisualizer() { @@ -162,11 +63,6 @@ public abstract String getLanguageShortName(); /** - * Establishes source event reporting. - */ - protected abstract void setSourceCallback(SourceCallback sourceCallback); - - /** * Get compiler options specific to this ExecutionContext. */ public CompilerOptions getCompilerOptions() { diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java Sun Nov 23 16:07:23 2014 -0800 @@ -39,6 +39,7 @@ public DefaultCallTarget(RootNode function) { this.rootNode = function; this.rootNode.adoptChildren(); + this.rootNode.applyInstrumentation(); this.rootNode.setCallTarget(this); } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java Sun Nov 23 16:07:23 2014 -0800 @@ -162,4 +162,5 @@ } return new DefaultLoopNode(repeating); } + } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTNodeProber.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTNodeProber.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument; - -import com.oracle.truffle.api.nodes.*; - -/** - * Methods for inserting a {@link Probe} at a Truffle AST node. - *

    - * This interface is guest language agnostic, but current extensions are language-specific. This - * will be revisited. - *

    - * Disclaimer: experimental interface under development. Really! - */ -public interface ASTNodeProber { - - /** - * Optionally applies instrumentation at a Truffle AST node, depending on guest - * language characteristics and use-case policy. - *

      - *
    • if no instrumentation is to be applied, returns the AST node unmodified;
    • - *
    • if an AST node is to be instrumented, then returns a newly created {@link Wrapper} that - * decorates the AST node and notifies an associated {@link Probe} of all - * {@link ExecutionEvents} at the wrapped AST node.
    • - *
    • if the argument is itself a {@link Wrapper}, i.e. if the AST node at this site has - * already been wrapped, then the wrapper is returned (with the possible addition of a - * {@linkplain SyntaxTag tag}).
    • - *
    - * - * @param astNode an AST node to which instrumentation might be applied - * @param tag an optional category directing how the node, if instrumented, should be perceived - * by tool users - * @param args additional arguments for instrumentation specific to a particular guest language - * @return if no instrumentation should be applied or if the node is a {@link Wrapper} then the - * unmodified node; otherwise a newly created {@link Wrapper} (whose child - * {@code astNode}) with an associated {@link Probe} . - */ - - Node probeAs(Node astNode, SyntaxTag tag, Object... args); -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Sun Nov 23 16:07:23 2014 -0800 @@ -27,40 +27,18 @@ import com.oracle.truffle.api.nodes.*; /** - * Implementation of a policy for instrumenting Truffle ASTs with {@link Probe}s at - * particular nodes by inserting node {@link Wrapper}s. - *

    - * Multiple "node probers" can be added, typically by different tools; the "combined prober" will - * apply all of them. - *

    - * The current implementation is provisional and does not completely encapsulate everything that - * needs to be implemented for a particular use-case or set of use-cases. In particular, the AST - * building code for each language implementation must have hand-coded applications of node probing - * methods at the desired locations. For the duration of this approach, this must be done for any - * node that any client tool wishes to probe. - *

    - * A better approach will be to implement such policies as a Truffle {@link NodeVisitor}, but that - * is not possible at this time. - *

    - * Disclaimer: experimental interface under development. + * Enables instrumentation by attaching {@linkplain Probe Probes} to some nodes in a (newly created, + * not yet executed) AST. + * + * @see Probe + * @see Probe#addProbeListener(com.oracle.truffle.api.instrument.Probe.ProbeListener) */ public interface ASTProber { - // TODO (mlvdv) This is a provisional interface, more of a marker really - // TODO (mlvdv) AST probing should eventually be done with visitors. + /** + * Walk the AST starting at a node and enable instrumentation at selected nodes by attaching + * {@linkplain Probe Probes} to them. + */ + void probeAST(Node node); - /** - * Adds a specification for adding probes at particular kinds of nodes. - * - * @param nodeProber - * @throws IllegalArgumentException if the prober is not applicable to the guest language - * implementation. - */ - void addNodeProber(ASTNodeProber nodeProber) throws IllegalArgumentException; - - /** - * Gets a (possibly guest language-specific) {@link ASTNodeProber} that will apply all that have - * been added; {@code null} if no instrumentation in AST. - */ - ASTNodeProber getCombinedNodeProber(); } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ExecutionEvents.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ExecutionEvents.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; - -/** - * Normal events at each Truffle AST {@link Node} that occur during guest language execution, and - * which the {@link Probe} associated with that node, if any, reports to every {@link Instrument} - * attached to the {@link Probe}. - *

    - * Disclaimer: experimental interface under development. - */ -public interface ExecutionEvents { - - /** - * Notifies that an AST node's execute method has just been entered. Callers should assure that - * a matching call to {@link #leave(Node, VirtualFrame, Object)} always follows. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame being passed to the execute method - */ - void enter(Node astNode, VirtualFrame frame); - - /** - * Notifies that an AST Node's void-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - */ - void leave(Node astNode, VirtualFrame frame); - - /** - * Notifies that an AST Node's boolean-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, boolean result); - - /** - * Notifies that an AST Node's byte-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, byte result); - - /** - * Notifies that an AST Node's short-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, short result); - - /** - * Notifies that an AST Node's integer-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, int result); - - /** - * Notifies that an AST Node's long-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, long result); - - /** - * Notifies that an AST Node's float-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, float result); - - /** - * Notifies that an AST Node's double-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, double result); - - /** - * Notifies that an AST Node's char-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, char result); - - /** - * Notifies that an AST Node's object-valued execute method is about to exit. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param result The result of the call to the execute method. - */ - void leave(Node astNode, VirtualFrame frame, Object result); - - /** - * Notifies that an AST Node's execute method is about to leave under exceptional conditions, - * returning no value. - *

    - * Callers should assure (via {@code try/finally}) that a matching call to this method always - * follows a call to {@link #enter(Node, VirtualFrame)}. - * - * @param astNode The AST node on which the execute method is being called - * @param frame The frame that was passed to the execute method - * @param e the exception associated with the unusual return - */ - void leaveExceptional(Node astNode, VirtualFrame frame, Exception e); - -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Sun Nov 23 16:07:23 2014 -0800 @@ -24,113 +24,297 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.impl.*; import com.oracle.truffle.api.nodes.*; +// TODO (mlvdv) migrate some of this to external documentation. /** - * A receiver of Truffle AST runtime {@link ExecutionEvents}, propagated from the {@link Probe} to - * which the instrument is attached, for the benefit of associated tools. + * A dynamically added/removed binding between a {@link Probe}, which provides notification of + * {@linkplain TruffleEventReceiver execution events} taking place at a {@link Node} in a Guest + * Language (GL) Truffle AST, and a {@linkplain TruffleEventReceiver receiver}, which consumes + * notifications on behalf of an external tool. *

    - * Guidelines for implementing {@link Instrument}s, with particular attention to minimize runtime - * performance overhead: + *

    Summary: How to "instrument" an AST location:

    *
      - *
    1. Extend this abstract class and override only the {@linkplain ExecutionEvents event handling - * methods} for which intervention is needed.
    2. - *
    3. Instruments are Truffle {@link Node}s and should be coded as much as possible in the desired - * Truffle style, documented more thoroughly elsewhere.
    4. - *
    5. Maintain as little state as possible.
    6. - *
    7. If state is necessary, make object fields {@code final} if at all possible.
    8. - *
    9. If non-final object-valued state is necessary, annotate it as {@link CompilationFinal} and - * call {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.
    10. - *
    11. Never store a {@link Frame} value in a field.
    12. - *
    13. Minimize computation in standard execution paths.
    14. - *
    15. If runtime calls must be made back to a tool, construct the instrument with a callback stored - * in a {@code final} field.
    16. - *
    17. Tool methods called by the instrument should be annotated as {@link TruffleBoundary} to - * prevent them from being inlined into fast execution paths.
    18. - *
    19. If computation in the execution path is needed, and if performance is important, then the - * computation is best expressed as a guest language AST and evaluated using standard Truffle - * mechanisms so that standard Truffle optimizations can be applied.
    20. + *
    21. Create an implementation of {@link TruffleEventReceiver} that responds to events on behalf of + * a tool.
    22. + *
    23. Create an Instrument via factory method {@link Instrument#create(TruffleEventReceiver)}.
    24. + *
    25. "Attach" the Instrument to a Probe via {@link Probe#attach(Instrument)}, at which point event + * notifications begin to arrive at the receiver.
    26. + *
    27. When no longer needed, "detach" the Instrument via {@link Instrument#dispose()}, at which + * point event notifications to the receiver cease, and the Instrument becomes unusable.
    28. + *
    + *

    + *

    Options for creating receivers:

    + *

    + *

      + *
    1. Implement the interface {@link TruffleEventReceiver}. The event handling methods account for + * both the entry into an AST node (about to call) and several possible kinds of exit from an AST + * node (just returned).
    2. + *
    3. Extend {@link DefaultEventReceiver}, which provides a no-op implementation of every + * {@link TruffleEventReceiver} method; override the methods of interest.
    4. + *
    5. Extend {@link SimpleEventReceiver}, where return values are ignored so only two methods (for + * "enter" and "return") will notify all events.
    6. *
    *

    - * Guidelines for attachment to a {@link Probe}: - *

      - *
    1. An Instrument instance must only attached to a single {@link Probe}, each of which is - * associated uniquely with a specific syntactic unit of a guest language program, and thus - * (initially) to a specific {@linkplain Node Truffle AST node}.
    2. - *
    3. When the AST containing such a node is copied at runtime, the {@link Probe} will be shared by - * every copy, and so the Instrument will receive events corresponding to the intended syntactic - * unit of code, independent of which AST copy is being executed.
    4. - *
    + *

    General guidelines for receiver implementation:

    + *

    + * When an Instrument is attached to a Probe, the receiver effectively becomes part of the executing + * GL program; performance can be affected by the receiver's implementation. + *

      + *
    • Do not store {@link Frame} or {@link Node} references in fields.
    • + *
    • Prefer {@code final} fields and (where performance is important) short methods.
    • + *
    • If needed, pass along the {@link VirtualFrame} reference from an event notification as far as + * possible through code that is expected to be inlined, since this incurs no runtime overhead. When + * access to frame data is needed, substitute a more expensive {@linkplain Frame#materialize() + * materialized} representation of the frame.
    • + *
    • If a receiver calls back to its tool during event handling, and if performance is an issue, + * then this should be through a final "callback" field in the instrument, and the called methods + * should be minimal.
    • + *
    • On the other hand, implementations should prevent Truffle from inlining beyond a reasonable + * point with the method annotation {@link TruffleBoundary}.
    • + *
    • The implicit "outer" pointer in a non-static inner class is a useful way to implement + * callbacks to owner tools.
    • + *
    • Primitive-valued return events are boxed for notification, but Truffle will eliminate the + * boxing if they are cast back to their primitive values quickly (in particular before crossing any + * {@link TruffleBoundary} annotations). + *
    + *

    + *

    Allowing for AST cloning:

    + *

    + * Truffle routinely clones ASTs, which has consequences for receiver implementation. + *

      + *
    • Even though a {@link Probe} is uniquely associated with a particular location in the + * executing Guest Language program, execution events at that location will in general be + * implemented by different {@link Node} instances, i.e. clones of the originally probed + * node.
    • + *
    • Because of cloning the {@link Node} supplied with notifications to a particular + * receiver will vary, but because they all represent the same GL program location the events should + * be treated as equivalent for most purposes.
    • + *
    + *

    + *

    Access to execution state:

    *

    - * Guidelines for handling {@link ExecutionEvents}: - *

      - *
    1. Separate event methods are defined for each kind of possible return: object-valued, - * primitive-valued, void-valued, and exceptional.
    2. - *
    3. Override "leave*" primitive methods if the language implementation returns primitives and the - * instrument should avoid boxing them.
    4. - *
    5. On the other hand, if boxing all primitives for instrumentation is desired, it is only - * necessary to override the object-valued return methods, since the default implementation of each - * primitive-valued return method is to box the value and forward it to the object-valued return - * method.
    6. - *
    - * + *
      + *
    • Event notification arguments provide primary access to the GL program's execution states: + *
        + *
      • {@link Node}: the concrete node (in one of the AST's clones) from which the event originated. + *
      • + *
      • {@link VirtualFrame}: the current execution frame. + *
      + *
    • Some global information is available, for example the execution + * {@linkplain TruffleRuntime#iterateFrames(FrameInstanceVisitor) stack}.
    • + *
    • Additional information needed by a receiver could be stored when created, preferably + * {@code final} of course. For example, a reference to the {@link Probe} to which the receiver's + * Instrument has been attached would give access to its corresponding + * {@linkplain Probe#getProbedSourceSection() source location} or to the collection of + * {@linkplain SyntaxTag tags} currently applied to the Probe.
    • + *
    + *

    + *

    Activating and deactivating Instruments:

    + *

    + * Instruments are single-use: + *

      + *
    • An instrument becomes active only when attached to a Probe via + * {@link Probe#attach(Instrument)}, and it may only be attached to a single Probe. It is a runtime + * error to attempt attaching a previously attached instrument.
    • + *
    • Attaching an instrument modifies every existing clone of the AST to which it is being + * attached, which can trigger deoptimization.
    • + *
    • The method {@link Instrument#dispose()} makes an instrument inactive by removing it from the + * Probe to which it was attached and rendering it permanently inert.
    • + *
    • Disposal removes the implementation of an instrument from all ASTs to which it was attached, + * which can trigger deoptimization.
    • + *
    + *

    + *

    Sharing receivers:

    + *

    + * Although an Instrument may only be attached to a single Probe, a receiver can be shared among + * multiple Instruments. This can be useful for observing events that might happen at different + * locations in a single AST, for example all assignments to a particular variable. In this case a + * new Instrument would be created and attached at each assignment node, but all the Instruments + * would be created with the same receiver. *

    * Disclaimer: experimental; under development. * * @see Probe - * @see ASTNodeProber + * @see TruffleEventReceiver */ -public abstract class Instrument extends InstrumentationNode { +public final class Instrument { - protected Instrument() { + /** + * Creates an instrument that will route execution events to a receiver. + * + * @param receiver a receiver for event generated by the instrument + * @param instrumentInfo optional description of the instrument's role + * @return a new instrument, ready for attachment at a probe + */ + public static Instrument create(TruffleEventReceiver receiver, String instrumentInfo) { + return new Instrument(receiver, instrumentInfo); } - public void enter(Node astNode, VirtualFrame frame) { + /** + * Creates an instrument that will route execution events to a receiver. + */ + public static Instrument create(TruffleEventReceiver receiver) { + return new Instrument(receiver, null); } - public void leave(Node astNode, VirtualFrame frame) { - } + /** + * Tool-supplied receiver of events. + */ + private final TruffleEventReceiver toolEventreceiver; + + /** + * Optional documentation, mainly for debugging. + */ + private final String instrumentInfo; - public void leave(Node astNode, VirtualFrame frame, boolean result) { - leave(astNode, frame, (Object) result); + /** + * Has this instrument been disposed? stays true once set. + */ + private boolean isDisposed = false; + + private Probe probe = null; + + private Instrument(TruffleEventReceiver receiver, String instrumentInfo) { + this.toolEventreceiver = receiver; + this.instrumentInfo = instrumentInfo; } - public void leave(Node astNode, VirtualFrame frame, byte result) { - leave(astNode, frame, (Object) result); + /** + * Removes this instrument (and any clones) from the probe to which it attached and renders the + * instrument inert. + * + * @throws IllegalStateException if this instrument has already been disposed + */ + public void dispose() throws IllegalStateException { + if (isDisposed) { + throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt"); + } + if (probe != null) { + // It's attached + probe.disposeInstrument(this); + } + this.isDisposed = true; } - public void leave(Node astNode, VirtualFrame frame, short result) { - leave(astNode, frame, (Object) result); + Probe getProbe() { + return probe; + } + + void setAttachedTo(Probe probe) { + this.probe = probe; + } + + /** + * Has this instrument been disposed and rendered unusable? + */ + boolean isDisposed() { + return isDisposed; + } + + InstrumentNode addToChain(InstrumentNode nextNode) { + return new InstrumentNode(nextNode); } - public void leave(Node astNode, VirtualFrame frame, int result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, long result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, char result) { - leave(astNode, frame, (Object) result); + /** + * Removes this instrument from an instrument chain. + */ + InstrumentNode removeFromChain(InstrumentNode instrumentNode) { + boolean found = false; + if (instrumentNode != null) { + if (instrumentNode.getInstrument() == this) { + // Found the match at the head of the chain + return instrumentNode.nextInstrument; + } + // Match not at the head of the chain; remove it. + found = instrumentNode.removeFromChain(Instrument.this); + } + if (!found) { + throw new IllegalStateException("Couldn't find instrument node to remove: " + this); + } + return instrumentNode; } - public void leave(Node astNode, VirtualFrame frame, float result) { - leave(astNode, frame, (Object) result); - } + @NodeInfo(cost = NodeCost.NONE) + final class InstrumentNode extends Node implements TruffleEventReceiver, InstrumentationNode { + + @Child private InstrumentNode nextInstrument; + + private InstrumentNode(InstrumentNode nextNode) { + this.nextInstrument = nextNode; + } + + /** + * Gets the instrument that created this node. + */ + private Instrument getInstrument() { + return Instrument.this; + } - public void leave(Node astNode, VirtualFrame frame, double result) { - leave(astNode, frame, (Object) result); - } + /** + * Removes the node from this chain that was added by a particular instrument, assuming that + * the head of the chain is not the one to be replaced. This is awkward, but is required + * because {@link Node#replace(Node)} won't take a {@code null} argument. This doesn't work + * for the tail of the list, which would be replacing itself with null. So the replacement + * must be directed the parent of the node being removed. + */ + private boolean removeFromChain(Instrument instrument) { + assert getInstrument() != instrument; + if (nextInstrument == null) { + return false; + } + if (nextInstrument.getInstrument() == instrument) { + // Next is the one to remove + if (nextInstrument.nextInstrument == null) { + // Next is at the tail; just forget + nextInstrument = null; + } else { + // Replace next with its successor + nextInstrument.replace(nextInstrument.nextInstrument); + } + return true; + } + return nextInstrument.removeFromChain(instrument); + } - public void leave(Node astNode, VirtualFrame frame, Object result) { - } + public void enter(Node node, VirtualFrame frame) { + Instrument.this.toolEventreceiver.enter(node, frame); + if (nextInstrument != null) { + nextInstrument.enter(node, frame); + } + } + + public void returnVoid(Node node, VirtualFrame frame) { + Instrument.this.toolEventreceiver.returnVoid(node, frame); + if (nextInstrument != null) { + nextInstrument.returnVoid(node, frame); + } + } - public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + public void returnValue(Node node, VirtualFrame frame, Object result) { + Instrument.this.toolEventreceiver.returnValue(node, frame, result); + if (nextInstrument != null) { + nextInstrument.returnValue(node, frame, result); + } + } + + public void returnExceptional(Node node, VirtualFrame frame, Exception exception) { + Instrument.this.toolEventreceiver.returnExceptional(node, frame, exception); + if (nextInstrument != null) { + nextInstrument.returnExceptional(node, frame, exception); + } + } + + public String instrumentationInfo() { + if (Instrument.this.instrumentInfo != null) { + return Instrument.this.instrumentInfo; + } + return toolEventreceiver.getClass().getSimpleName(); + } } } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumentable.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumentable.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument; - -/** - * Any Truffle node implementing this interface can be "instrumented" by installing a {@link Probe} - * that intercepts {@link ExecutionEvents} at the node and routes them to any {@link Instrument}s - * that have been attached to the {@link Probe}. Only one {@link Probe} may be installed at each - * node; subsequent calls return the one already installed. - */ -public interface Instrumentable { - - /** - * Enables "instrumentation" of this Truffle node by tools, where this node is presumed to be - * part (and not the root of) of a well-formed Truffle AST that is not being executed. The AST - * may be modified. - * - * @return The probe that was created. - */ - Probe probe(); -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument; + +import com.oracle.truffle.api.nodes.*; + +/** + * A marker interface for Truffle {@linkplain Node nodes} that support Instrumentation and + * are should not be part of any Guest Language execution semantics. + */ +public interface InstrumentationNode { + + /** + * A short description of the particular role played by the node, intended to support debugging. + */ + String instrumentationInfo(); +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Sun Nov 23 16:07:23 2014 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,63 +24,389 @@ */ package com.oracle.truffle.api.instrument; +import java.lang.ref.*; +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.instrument.ProbeNode.Instrumentable; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.utilities.*; +//TODO (mlvdv) migrate some of this to external documentation. /** - * A collector of {@link ExecutionEvents} at a specific site (node) in a Truffle AST (generated by a - * {@link Wrapper} inserted into the AST) for the purpose of instrumentation. For probes - * associated with programmer-facing tools, there should be no more than one probe associated with a - * particular piece of source code syntax (i.e. a {@link SourceSection}). + * A binding between a particular location in the Truffle AST representation of a running Guest + * Language (GL) program (i.e. a {@link Node}) and a dynamically managed collection of "attached" + * {@linkplain Instrument instrumentation} for use by external tools. *

    - * Any {@linkplain SyntaxTag tags} associated with a particular piece of source code syntax are - * managed by the probe. + * The effect of a binding is to intercept {@linkplain TruffleEventReceiver execution events} at the + * node and notify each attached {@link Instrument} before execution is allowed to resume. + *

    + * A Probe is "inserted" into a GL node via a call to {@link Instrumentable#probe()}; a GL node must + * implement {@link Instrumentable} in order to support instrumentation. No more than one Probe can + * be inserted at a node. *

    - * When ASTs are copied, it is presumed that the probe for a site is shared by all AST nodes - * representing that site. + * The "probing" of a Truffle AST must be done after it is complete (i.e. with parent pointers + * correctly assigned), but before any executions. This is done by creating an instance of + * {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}, after which it + * will be automatically applied to newly created ASTs. + *

    + * Each Probe may also have assigned to it one or more {@link SyntaxTag}s, for example identifying a + * node as a {@linkplain StandardSyntaxTag#STATEMENT STATEMENT}. Tags can be queried by tools to + * configure behavior relevant to each probed node. *

    - * A probe holds zero or more {@link Instrument}s, which can be added and removed dynamically. - * Events reported to a probe are propagated to every attached instrument; the order is undefined. - *

    - * Probe methods must be amenable to Truffle/Graal inlining on the assumption that the collection of - * attached instruments seldom changes. The assumption is invalidated when instruments are added or - * removed, but some instruments may change their internal state in such a way that the assumption - * should also be invalidated. + * Instrumentation is implemented by modifying ASTs, both by inserting nodes into each AST at probed + * locations and by attaching additional nodes that implement dynamically attached instruments. + * Attached instrumentation code become, in effect, part of the GL program, and is subject to the + * same levels of optimization as other GL code. This implementation accounts properly for the fact + * that Truffle frequently clones ASTs, along with any attached instrumentation nodes. A + * Probe, along with attached Instruments, represents a logical binding with a source code + * location, producing event notifications that are (mostly) independent of which AST clone is + * executing. * * @see Instrument - * @see Wrapper + * @see Instrumentable + * @see ASTProber */ -public interface Probe extends ExecutionEvents, SyntaxTagged, NodeInterface { +public final class Probe implements SyntaxTagged { + + /** + * An observer of events related to {@link Probe}s: creating and tagging. + */ + public interface ProbeListener { + + /** + * Notifies that all registered {@link ASTProber}s are about to be applied to a newly + * constructed AST. + * + * @param source source code from which the AST was constructed + */ + void startASTProbing(Source source); + + /** + * Notifies that a {@link Probe} has been newly attached to an AST via + * {@link Instrumentable#probe()}. + *

    + * There can be no more than one {@link Probe} at a node; this notification will only be + * delivered the first time {@linkplain Instrumentable#probe() probe()} is called at a + * particular AST node. There will also be no notification when the AST to which the Probe + * is attached is cloned. + */ + void newProbeInserted(Probe probe); + + /** + * Notifies that a {@link SyntaxTag} has been newly added to the set of tags associated with + * a {@link Probe} via {@link Probe#tagAs(SyntaxTag, Object)}. + *

    + * The {@linkplain SyntaxTag tags} at a {@link Probe} are a set; this notification + * will only be delivered the first time a particular {@linkplain SyntaxTag tag} is added at + * a {@link Probe}. + *

    + * An optional value supplied with {@linkplain Probe#tagAs(SyntaxTag, Object) + * tagAs(SyntaxTag, Object)} is reported to all listeners, but not stored. As a consequence, + * the optional value will have no effect at all if the tag had already been added. + * + * @param probe where a tag has been added + * @param tag the tag that has been newly added (subsequent additions of the tag are + * unreported). + * @param tagValue an optional value associated with the tag for the purposes of reporting. + */ + void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue); + + /** + * Notifies that the application of all registered {@link ASTProber}s to a newly constructed + * AST has completed. + * + * @param source source code from which the AST was constructed + */ + void endASTProbing(Source source); + + } + + private static final List astProbers = new ArrayList<>(); + + private static final List probeListeners = new ArrayList<>(); + + /** + * All Probes that have been created. + */ + private static final List> probes = new ArrayList<>(); + + private static final class FindSourceVisitor implements NodeVisitor { + + Source source = null; + + public boolean visit(Node node) { + final SourceSection sourceSection = node.getSourceSection(); + if (sourceSection != null) { + source = sourceSection.getSource(); + return false; + } + return true; + } + } + + /** + * Walks an AST, looking for the first node with an assigned {@link SourceSection} and returning + * the {@link Source}. + */ + private static Source findSource(Node node) { + final FindSourceVisitor visitor = new FindSourceVisitor(); + node.accept(visitor); + return visitor.source; + } + + /** + * The tag trap is a global setting; it only affects {@linkplain Probe probes} with the + * {@linkplain SyntaxTag tag} specified . + */ + private static SyntaxTagTrap globalTagTrap = null; + + /** + * Enables instrumentation at selected nodes in all subsequently constructed ASTs. + */ + public static void registerASTProber(ASTProber prober) { + astProbers.add(prober); + } + + public static void unregisterASTProber(ASTProber prober) { + astProbers.remove(prober); + } + + /** + * Enables instrumentation in a newly created AST by applying all registered instances of + * {@link ASTProber}. + */ + public static void applyASTProbers(Node node) { + + final Source source = findSource(node); + + for (ProbeListener listener : probeListeners) { + listener.startASTProbing(source); + } + for (ASTProber prober : astProbers) { + prober.probeAST(node); + } + for (ProbeListener listener : probeListeners) { + listener.endASTProbing(source); + } + } + + /** + * Adds a {@link ProbeListener} to receive events. + */ + public static void addProbeListener(ProbeListener listener) { + assert listener != null; + probeListeners.add(listener); + } + + /** + * Removes a {@link ProbeListener}. Ignored if listener not found. + */ + public static void removeProbeListener(ProbeListener listener) { + probeListeners.remove(listener); + } /** - * Get the {@link SourceSection} in some Truffle AST associated with this probe. + * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection if + * the specified tag is {@code null}. * - * @return The source associated with this probe. + * @return A collection of probes containing the given tag. */ - SourceSection getSourceLocation(); + public static Collection findProbesTaggedAs(SyntaxTag tag) { + final List taggedProbes = new ArrayList<>(); + for (WeakReference ref : probes) { + Probe probe = ref.get(); + if (probe != null) { + if (tag == null || probe.isTaggedAs(tag)) { + taggedProbes.add(ref.get()); + } + } + } + return taggedProbes; + } + + /** + * Sets the current "tag trap". This causes a callback to be triggered whenever execution + * reaches a {@link Probe} (either existing or subsequently created) with the specified tag. + * There can only be one tag trap set at a time. + * + * @param newTagTrap The {@link SyntaxTagTrap} to set. + * @throws IllegalStateException if a trap is currently set. + */ + public static void setTagTrap(SyntaxTagTrap newTagTrap) throws IllegalStateException { + assert newTagTrap != null; + if (globalTagTrap != null) { + throw new IllegalStateException("trap already set"); + } + globalTagTrap = newTagTrap; + + final SyntaxTag newTag = newTagTrap.getTag(); + for (WeakReference ref : probes) { + final Probe probe = ref.get(); + if (probe != null && probe.tags.contains(newTag)) { + probe.trapActive = true; + probe.probeStateUnchanged.invalidate(); + } + } + } /** - * Mark this probe as belonging to some tool-related category that can be used to guide tool - * behavior at an associated AST node. For example, a debugger might add the tag - * {@link StandardSyntaxTag#STATEMENT} as a way of configuring where execution should stop when - * stepping. + * Clears the current {@link SyntaxTagTrap}. + * + * @throws IllegalStateException if no trap is currently set. */ - void tagAs(SyntaxTag tag); + public static void clearTagTrap() { + if (globalTagTrap == null) { + throw new IllegalStateException("no trap set"); + } + globalTagTrap = null; + + for (WeakReference ref : probes) { + final Probe probe = ref.get(); + if (probe != null && probe.trapActive) { + probe.trapActive = false; + probe.probeStateUnchanged.invalidate(); + } + } + } + + private final SourceSection sourceSection; + private final ArrayList tags = new ArrayList<>(); + private final List> probeNodeClones = new ArrayList<>(); + private final CyclicAssumption probeStateUnchanged = new CyclicAssumption("Probe state unchanged"); + + /** + * {@code true} iff the global trap is set and this probe has the matching tag. + */ + private boolean trapActive = false; + + /** + * @see Instrumentable#probe() + */ + Probe(ProbeNode probeNode, SourceSection sourceSection) { + this.sourceSection = sourceSection; + probes.add(new WeakReference<>(this)); + registerProbeNodeClone(probeNode); + for (ProbeListener listener : probeListeners) { + listener.newProbeInserted(this); + } + } + + public boolean isTaggedAs(SyntaxTag tag) { + assert tag != null; + return tags.contains(tag); + } + + public Collection getSyntaxTags() { + return Collections.unmodifiableCollection(tags); + } /** - * Adds an {@link Instrument} to this probe's collection. No check is made to see if the same - * instrument has already been added. + * Adds a {@linkplain SyntaxTag tag} to the set of tags associated with this {@link Probe}; + * {@code no-op} if already in the set. + */ + public void tagAs(SyntaxTag tag, Object tagValue) { + assert tag != null; + if (!tags.contains(tag)) { + tags.add(tag); + for (ProbeListener listener : probeListeners) { + listener.probeTaggedAs(this, tag, tagValue); + } + if (globalTagTrap != null && tag == globalTagTrap.getTag()) { + this.trapActive = true; + } + probeStateUnchanged.invalidate(); + } + } + + /** + * Adds instrumentation at this Probe. * - * @param newInstrument The instrument to add to this probe. + * @param instrument an instrument not yet attached to a probe + * @throws IllegalStateException if the instrument has ever been attached before */ - void addInstrument(Instrument newInstrument); + public void attach(Instrument instrument) throws IllegalStateException { + if (instrument.isDisposed()) { + throw new IllegalStateException("Attempt to attach disposed instrument"); + } + if (instrument.getProbe() != null) { + throw new IllegalStateException("Attampt to attach an already attached instrument"); + } + instrument.setAttachedTo(this); + for (WeakReference ref : probeNodeClones) { + final ProbeNode probeNode = ref.get(); + if (probeNode != null) { + probeNode.addInstrument(instrument); + } + } + probeStateUnchanged.invalidate(); + } + + /** + * Gets the {@link SourceSection} associated with the Guest Language AST node being + * instrumented, possibly {@code null}. + */ + public SourceSection getProbedSourceSection() { + return sourceSection; + } + + public String getShortDescription() { + final String location = sourceSection == null ? "" : sourceSection.getShortDescription(); + return "Probe@" + location + getTagsDescription(); + } /** - * Removes the given instrument from the probe's collection. + * Receives notification that a new clone of the instrument chain associated with this + * {@link Probe} has been created as a side-effect of AST cloning. + */ + void registerProbeNodeClone(ProbeNode probeNode) { + probeNodeClones.add(new WeakReference<>(probeNode)); + } + + /** + * Gets the currently active {@linkplain SyntaxTagTrap tagTrap}; {@code null} if not set. + */ + SyntaxTagTrap getTrap() { + return trapActive ? globalTagTrap : null; + } + + /** + * Gets the {@link Assumption} that the instrumentation-related state of this {@link Probe} has + * not changed since this method was last called. + */ + Assumption getUnchangedAssumption() { + return probeStateUnchanged.getAssumption(); + } + + /** + * Internal method for removing and rendering inert a specific instrument previously attached at + * this Probe. * - * @param oldInstrument The instrument to remove from this probe. - * @throws RuntimeException if no matching instrument has been attached. + * @param instrument an instrument already attached + * @throws IllegalStateException if instrument not attached at this Probe + * @see Instrument#dispose() */ - void removeInstrument(Instrument oldInstrument) throws RuntimeException; + void disposeInstrument(Instrument instrument) throws IllegalStateException { + for (WeakReference ref : probeNodeClones) { + final ProbeNode probeNode = ref.get(); + if (probeNode != null) { + probeNode.removeInstrument(instrument); + } + } + probeStateUnchanged.invalidate(); + } + private String getTagsDescription() { + final StringBuilder sb = new StringBuilder(); + sb.append("["); + String prefix = ""; + for (SyntaxTag tag : tags) { + sb.append(prefix); + prefix = ","; + sb.append(tag.toString()); + } + sb.append("]"); + return sb.toString(); + } } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument; - -import com.oracle.truffle.api.source.*; - -/** - * Client for receiving events relate to {@link Probe} management. Does not report AST copying. - *

    - * Disclaimer: experimental interface under development. - * - * @See Instrumentation - */ -public interface ProbeListener { - - /** - * Notifies that a newly created (untagged) {@link Probe} has been inserted into a Truffle AST. - * There will be no notification when an existing {@link Probe} is shared by an AST copy. - */ - void newProbeInserted(SourceSection source, Probe probe); - - /** - * Notifies that a (fully constructed) {@link Probe} has been tagged. A subsequent marking with - * the same tag is idempotent and generates no notification. - */ - void probeTaggedAs(Probe probe, SyntaxTag tag); - -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.Instrument.InstrumentNode; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.source.*; + +/** + * Implementation interfaces and classes for attaching {@link Probe}s to {@link WrapperNode}s. + */ +public abstract class ProbeNode extends Node implements TruffleEventReceiver, InstrumentationNode { + + /** + * Any Truffle node implementing this interface can be "instrumented" by installing a + * {@link Probe} that intercepts execution events at the node and routes them to any + * {@link Instrument}s that have been attached to the {@link Probe}. Only one {@link Probe} may + * be installed at each node; subsequent calls return the one already installed. + * + * @see Instrument + */ + public interface Instrumentable { + + /** + * Enables "instrumentation" of a Guest Language Truffle node, where the node is presumed to + * be part of a well-formed Truffle AST that is not being executed. The AST may be modified + * as a side effect. + *

    + * This interface is not intended to be visible as part of the API for tools + * (instrumentation clients). + * + * @return a (possibly newly created) {@link Probe} associated with this node. + */ + Probe probe(); + + /** + * Enables a one-time, unchangeable "instrumentation" of a Guest Language Truffle node, + * where the node is presumed to be part of a well-formed Truffle AST that is not being + * executed. The AST may be modified as a side-effect. Unlike {@link #probe()}, once + * {@link #probeLite(TruffleEventReceiver)} is called at a node, no additional probing can + * be added and no additional instrumentation can be attached. + *

    + * This interface is not intended to be visible as part of the API for tools + * (instrumentation clients). + * + * @param eventReceiver The {@link TruffleEventReceiver} for the single "instrument" being + * attached to this node. + */ + void probeLite(TruffleEventReceiver eventReceiver); + } + + /** + * A node that can be inserted into a Truffle AST, and which enables instrumentation at + * a particular Guest Language (GL) node. + *

    + * The implementation must be GL-specific. A wrapper decorates a GL AST node (the + * wrapper's child) by acting as a transparent proxy with respect to the GL's + * execution semantics. + *

    + * Instrumentation at the wrapped node is implemented by an instance of {@link ProbeNode} + * attached as a second child of the {@link WrapperNode}. + *

    + * A wrapper is obliged to notify its attached {@link ProbeNode} when execution events occur at + * the wrapped AST node during program execution. + *

    + * When a GL AST is cloned, the {@link WrapperNode}, its {@link ProbeNode} and any + * {@linkplain Instrument instrumentation} are also cloned; they are in effect part of the GL + * AST. An instance of {@link Probe} represents abstractly the instrumentation at a particular + * location in a GL AST; it tracks all the copies of the Wrapper and attached instrumentation, + * and acts as a single point of access for tools. + *

    + * This interface is not intended to be visible as part of the API for tools (instrumentation + * clients). + *

    + * Implementation guidelines: + *

      + *
    1. Each GL implementation should include a WrapperNode implementation; usually only one is + * needed.
    2. + *
    3. The wrapper type should descend from the GL-specific node class.
    4. + *
    5. Must have a field: {@code @Child private Node child;}
    6. + *
    7. Must have a field: {@code @Child private ProbeNode probeNode;}
    8. + *
    9. The wrapper must act as a proxy for its child, which means implementing every + * possible execute- method that gets called on guest language AST node types by their + * parents, and passing along each call to its child.
    10. + *
    11. Method {@code Probe getProbe()} should be implemented as {@code probeNode.getProbe();} + *
    12. Method {@code insertProbe(ProbeNode)} should be implemented as + * {@code this.probeNode=insert(newProbeNode);}
    13. + *
    14. Most importantly, Wrappers must be implemented so that Truffle optimization will reduce + * their runtime overhead to zero when there are no attached {@link Instrument}s.
    15. + *
    + *

    + * Disclaimer: experimental interface under development. + * + * @see Instrument + */ + public interface WrapperNode extends InstrumentationNode { + + /** + * Gets the node being "wrapped", i.e. the AST node for which + * {@linkplain TruffleEventReceiver execution events} will be reported through the + * Instrumentation Framework. + */ + Node getChild(); + + /** + * Gets the {@link Probe} responsible for installing this wrapper; none if the wrapper + * installed via {@linkplain Instrumentable#probeLite(TruffleEventReceiver) "lite-Probing"}. + */ + Probe getProbe(); + + /** + * Implementation support for completing a newly created wrapper node. + */ + void insertProbe(ProbeNode probeNode); + + } + + /** + * Create a new {@link Probe} associated with, and attached to, a Guest Language specific + * instance of {@link WrapperNode}. + */ + public static Probe insertProbe(WrapperNode wrapper) { + final SourceSection sourceSection = wrapper.getChild().getSourceSection(); + final ProbeFullNode probeFullNode = new ProbeFullNode(); // private constructor + final Probe probe = new Probe(probeFullNode, sourceSection); // package private access + probeFullNode.setProbe(probe); + wrapper.insertProbe(probeFullNode); + return probe; + } + + /** + * Creates a new {@link ProbeLiteNode} associated with, and attached to, a Guest Language + * specific instance of {@link WrapperNode}. + */ + public static void insertProbeLite(WrapperNode wrapper, TruffleEventReceiver eventReceiver) { + final ProbeLiteNode probeLiteNode = new ProbeLiteNode(eventReceiver); + wrapper.insertProbe(probeLiteNode); + } + + /** + * @return the {@link Probe} permanently associated with this {@link ProbeNode}. + * + * @throws IllegalStateException if this location was "lite-Probed" + */ + public abstract Probe getProbe() throws IllegalStateException; + + /** + * Adds an {@link InstrumentNode} to this chain. + * + * @throws IllegalStateException if at a "lite-Probed" location. + */ + abstract void addInstrument(Instrument instrument); + + /** + * Removes an instrument from this chain of instruments. + * + * @throws IllegalStateException if at a "lite-Probed" location. + * @throws RuntimeException if no matching instrument is found, + */ + abstract void removeInstrument(Instrument instrument); + + /** + * Implementation class & interfaces for enabling the attachment of {@linkplain Probe Probes} to + * Truffle ASTs. + *

    + * Head of a chain of nodes acting on behalf of {@linkplain Instrument instruments}, attached to + * a Guest Language (GL) AST as a child of a GL-specific {@link WrapperNode} node. + *

    + * When Truffle clones an AST, the chain, including all attached {@linkplain Instrument + * instruments} will be cloned along with the {@link WrapperNode} to which it is attached. An + * instance of {@link Probe} represents abstractly the instrumentation at a particular location + * in a GL AST, tracks the clones of the chain, and keeps the instrumentation attached to the + * clones consistent. + */ + @NodeInfo(cost = NodeCost.NONE) + private static final class ProbeFullNode extends ProbeNode { + + /** + * First {@link InstrumentNode} node in chain; {@code null} of no instruments in chain. + */ + @Child protected InstrumentNode firstInstrument; + + // Never changed once set. + @CompilationFinal private Probe probe = null; + + /** + * An assumption that the state of the {@link Probe} with which this chain is associated has + * not changed since the last time checking such an assumption failed and a reference to a + * new assumption (associated with a new state of the {@link Probe} was retrieved. + */ + private Assumption probeUnchangedAssumption; + + private ProbeFullNode() { + this.firstInstrument = null; + } + + @Override + public Probe getProbe() throws IllegalStateException { + return probe; + } + + @Override + public Node copy() { + Node node = super.copy(); + probe.registerProbeNodeClone((ProbeNode) node); + return node; + } + + private void setProbe(Probe probe) { + this.probe = probe; + this.probeUnchangedAssumption = probe.getUnchangedAssumption(); + } + + private void checkProbeUnchangedAssumption() { + try { + probeUnchangedAssumption.check(); + } catch (InvalidAssumptionException ex) { + // Failure creates an implicit deoptimization + // Get the assumption associated with the new probe state + this.probeUnchangedAssumption = probe.getUnchangedAssumption(); + } + } + + @Override + @TruffleBoundary + void addInstrument(Instrument instrument) { + assert instrument.getProbe() == probe; + // The existing chain of nodes may be empty + // Attach the modified chain. + firstInstrument = insert(instrument.addToChain(firstInstrument)); + } + + @Override + @TruffleBoundary + void removeInstrument(Instrument instrument) { + assert instrument.getProbe() == probe; + final InstrumentNode modifiedChain = instrument.removeFromChain(firstInstrument); + if (modifiedChain == null) { + firstInstrument = null; + } else { + firstInstrument = insert(modifiedChain); + } + } + + public void enter(Node node, VirtualFrame frame) { + final SyntaxTagTrap trap = probe.getTrap(); + if (trap != null) { + checkProbeUnchangedAssumption(); + trap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), frame.materialize()); + } + if (firstInstrument != null) { + checkProbeUnchangedAssumption(); + firstInstrument.enter(node, frame); + } + } + + public void returnVoid(Node node, VirtualFrame frame) { + if (firstInstrument != null) { + checkProbeUnchangedAssumption(); + firstInstrument.returnVoid(node, frame); + } + } + + public void returnValue(Node node, VirtualFrame frame, Object result) { + if (firstInstrument != null) { + checkProbeUnchangedAssumption(); + firstInstrument.returnValue(node, frame, result); + } + } + + public void returnExceptional(Node node, VirtualFrame frame, Exception exception) { + if (firstInstrument != null) { + checkProbeUnchangedAssumption(); + firstInstrument.returnExceptional(node, frame, exception); + } + } + + public String instrumentationInfo() { + return "Standard probe"; + } + + } + + /** + * Implementation of a probe that only ever has a single "instrument" associated with it. No + * {@link Instrument} is ever created; instead this method simply delegates the various enter + * and return events to a {@link TruffleEventReceiver} passed in during construction. + */ + @NodeInfo(cost = NodeCost.NONE) + private static final class ProbeLiteNode extends ProbeNode { + + private final TruffleEventReceiver eventReceiver; + + private ProbeLiteNode(TruffleEventReceiver eventReceiver) { + this.eventReceiver = eventReceiver; + } + + @Override + public Probe getProbe() throws IllegalStateException { + throw new IllegalStateException("\"lite-Probed\" nodes have no explicit Probe"); + } + + @Override + @TruffleBoundary + void addInstrument(Instrument instrument) { + throw new IllegalStateException("Instruments may not be added at a \"lite-probed\" location"); + } + + @Override + @TruffleBoundary + void removeInstrument(Instrument instrument) { + throw new IllegalStateException("Instruments may not be removed at a \"lite-probed\" location"); + } + + public void enter(Node node, VirtualFrame frame) { + eventReceiver.enter(node, frame); + } + + public void returnVoid(Node node, VirtualFrame frame) { + eventReceiver.returnVoid(node, frame); + } + + public void returnValue(Node node, VirtualFrame frame, Object result) { + eventReceiver.returnValue(node, frame, result); + } + + public void returnExceptional(Node node, VirtualFrame frame, Exception exception) { + eventReceiver.returnExceptional(node, frame, exception); + } + + public String instrumentationInfo() { + return "\"Lite\" probe"; + } + + } +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceCallback.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceCallback.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument; - -import com.oracle.truffle.api.source.*; - -/** - * Instrumentation callback for guest language source-related events. - */ -public interface SourceCallback { - - void startLoading(Source source); - - void endLoading(Source source); - - /** - * Inert (singleton) implementation of {@link SourceCallback}. - */ - SourceCallback NULL = new SourceCallback() { - - public void startLoading(Source source) { - } - - public void endLoading(Source source) { - } - - }; -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceListener.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceListener.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument; - -import com.oracle.truffle.api.source.*; - -/** - * A client of the instrumentation framework that requests event notifications from the language - * engine when sources are loaded. - *

    - * Disclaimer: experimental interface under development. - */ -public interface SourceListener { - - /** - * The guest language runtime is starting to load a source. Care should be taken to ensure that - * under any circumstance there is always a following call to {@link #loadEnding(Source)} with - * the same argument. - */ - void loadStarting(Source source); - - /** - * The guest language runtime has finished loading a source. Care should be taken to ensure that - * under any circumstance there is always a prior call to {@link #loadStarting(Source)} with the - * same argument. - */ - void loadEnding(Source source); - -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardSyntaxTag.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardSyntaxTag.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardSyntaxTag.java Sun Nov 23 16:07:23 2014 -0800 @@ -32,9 +32,8 @@ * (for example for mostly expression-oriented languages) or even for specific languages. *

    * Disclaimer: experimental interface under development. - * + * * @see Probe - * @see Wrapper */ public enum StandardSyntaxTag implements SyntaxTag { diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTag.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTag.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTag.java Sun Nov 23 16:07:23 2014 -0800 @@ -38,7 +38,6 @@ * Disclaimer: experimental interface under development. * * @see Probe - * @see Wrapper * @see StandardSyntaxTag */ public interface SyntaxTag { diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTagged.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTagged.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTagged.java Sun Nov 23 16:07:23 2014 -0800 @@ -24,14 +24,13 @@ */ package com.oracle.truffle.api.instrument; +import java.util.*; + /** * Information about a guest language program element in a Truffle AST that can be marked as * belonging to 0 or more {@linkplain SyntaxTag tags}. *

    * Disclaimer: experimental interface under development. - * - * @see Probe - * @see Wrapper */ public interface SyntaxTagged { @@ -44,6 +43,6 @@ /** * In which user-sensible categories has this node been tagged (empty set if none). */ - Iterable getSyntaxTags(); + Collection getSyntaxTags(); } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/TruffleEventReceiver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/TruffleEventReceiver.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.nodes.*; + +/** + * A receiver of Truffle AST runtime execution events that can collect information and possibly + * intervene on behalf of an external tool. + */ +public interface TruffleEventReceiver { + + /** + * Receive notification that an AST node's execute method is about to be called. + */ + void enter(Node node, VirtualFrame frame); + + /** + * Receive notification that an AST Node's {@code void}-valued execute method has just returned. + */ + void returnVoid(Node node, VirtualFrame frame); + + /** + * Receive notification that an AST Node'sexecute method has just returned a value (boxed if + * primitive). + */ + void returnValue(Node node, VirtualFrame frame, Object result); + + /** + * Receive notification that an AST Node's execute method has just thrown an exception. + */ + void returnExceptional(Node node, VirtualFrame frame, Exception exception); + +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Wrapper.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Wrapper.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument; - -import com.oracle.truffle.api.nodes.*; - -/** - * A node that can be inserted into a Truffle AST in order to attach instrumentation at a - * particular node. - *

    - * A wrapper decorates an AST node (its child) by acting as a transparent - * proxy for the child with respect to Truffle execution semantics. - *

    - * A wrapper is also expected to notify its associated {@link Probe} when {@link ExecutionEvents} - * occur at the wrapper during program execution. - *

    - * The wrapper's {@link Probe} is shared by every copy of the wrapper made when the AST is copied. - *

    - * Wrapper implementation guidelines: - *

      - *
    1. Every guest language implementation should include a Wrapper implementation; usually only one - * is needed.
    2. - *
    3. The Wrapper type should descend from the language-specific node class of the guest - * language.
    4. - *
    5. The Wrapper must have a single {@code @Child private Node child} field.
    6. - *
    7. The Wrapper must act as a proxy for its child, which means implementing every - * possible execute- method that gets called on guest language AST node types by their - * parents, and passing along each call to its child.
    8. - *
    9. The Wrapper must have a single {@code private final Probe probe} to which an optional probe - * can be attached during node construction.
    10. - *
    11. Wrapper methods must also notify its attached {@link Probe}, if any, in terms of standard - * {@link ExecutionEvents}.
    12. - *
    13. Most importantly, Wrappers must be implemented so that Truffle optimization will reduce their - * runtime overhead to zero when there is no probe attached or when a probe has no attached - * {@link Instrument}s.
    14. - *
    - *

    - * Disclaimer: experimental interface under development. - * - * @see Probe - * @see ExecutionEvents - */ -public interface Wrapper { - - /** - * Gets the AST node being instrumented, which should never be an instance of {@link Wrapper}. - */ - Node getChild(); - - /** - * Gets the {@link Probe} to which events occurring at this wrapper's child are propagated. - */ - Probe getProbe(); - -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java Sun Nov 23 16:07:23 2014 -0800 @@ -58,7 +58,7 @@ return printTreeToString(node, maxDepth, null); } - private static void printTree(PrintWriter p, Node node, int maxDepth, Node markNode, int level) { + protected void printTree(PrintWriter p, Node node, int maxDepth, Node markNode, int level) { if (node == null) { p.print("null"); return; @@ -68,15 +68,13 @@ p.print("("); - final SourceSection src = node.getSourceSection(); - if (src != null) { - if (!(src instanceof NullSourceSection)) { - p.print(src.getSource().getName() + ":" + src.getStartLine()); - } + if (node instanceof InstrumentationNode) { + p.print(instrumentInfo((InstrumentationNode) node)); } - if (node instanceof SyntaxTagged) { - printSyntaxTagged(p, (SyntaxTagged) node); - } + + p.print(sourceInfo(node)); + + p.print(NodeUtil.printSyntaxTags(node)); ArrayList childFields = new ArrayList<>(); @@ -124,18 +122,7 @@ } } - private static void printSyntaxTagged(PrintWriter p, final SyntaxTagged taggedNode) { - p.print("["); - String prefix = ""; - for (SyntaxTag tag : taggedNode.getSyntaxTags()) { - p.print(prefix); - prefix = ","; - p.print(tag.toString()); - } - p.print("]"); - } - - private static void printChildren(PrintWriter p, int maxDepth, Node markNode, int level, NodeField field, Object value) { + protected void printChildren(PrintWriter p, int maxDepth, Node markNode, int level, NodeField field, Object value) { printNewLine(p, level); p.print(field.getName()); Node[] children = (Node[]) value; @@ -149,7 +136,7 @@ p.print("]"); } - private static void printChild(PrintWriter p, int maxDepth, Node markNode, int level, NodeField field, Object value) { + protected void printChild(PrintWriter p, int maxDepth, Node markNode, int level, NodeField field, Object value) { final Node valueNode = (Node) value; printNewLine(p, level, valueNode == markNode); p.print(field.getName()); @@ -157,7 +144,7 @@ printTree(p, valueNode, maxDepth, markNode, level + 1); } - private static void printNewLine(PrintWriter p, int level, boolean mark) { + protected static void printNewLine(PrintWriter p, int level, boolean mark) { p.println(); for (int i = 0; i < level; i++) { if (mark && i == 0) { @@ -168,12 +155,30 @@ } } - private static void printNewLine(PrintWriter p, int level) { + protected static void printNewLine(PrintWriter p, int level) { printNewLine(p, level, false); } - private static String nodeName(Node node) { + protected static String nodeName(Node node) { return node.getClass().getSimpleName(); } + protected static String sourceInfo(Node node) { + final SourceSection src = node.getSourceSection(); + if (src != null) { + if (src instanceof NullSourceSection) { + final NullSourceSection nullSection = (NullSourceSection) src; + return nullSection.getShortDescription(); + } else { + return src.getSource().getName() + ":" + src.getStartLine(); + } + } + return ""; + } + + protected static String instrumentInfo(InstrumentationNode node) { + final String info = node.instrumentationInfo(); + return info == null ? "" : info; + } + } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultEventReceiver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultEventReceiver.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument.impl; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.*; + +/** + * A receiver for AST {@linkplain TruffleEventReceiver execution events} that provides a no-op + * implementation of every event. + */ +public class DefaultEventReceiver implements TruffleEventReceiver { + + public void enter(Node node, VirtualFrame frame) { + } + + public void returnVoid(Node node, VirtualFrame frame) { + } + + public void returnValue(Node node, VirtualFrame frame, Object result) { + } + + public void returnExceptional(Node node, VirtualFrame frame, Exception exception) { + } + +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,568 +0,0 @@ -/* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument.impl; - -import java.util.*; - -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.source.*; - -/** - * Abstract implementation of Truffle {@link Node}s used as AST {@link Probe}s and - * {@link Instrument}s. A {@link Probe} manages its attached {@link Instrument}s by appending them - * to a chain through which {@link ExecutionEvents} are propagated. - */ -public abstract class InstrumentationNode extends Node implements ExecutionEvents { - - interface ProbeCallback { - void newTagAdded(ProbeImpl probe, SyntaxTag tag); - } - - static ProbeImpl createProbe(SourceSection source, ProbeCallback probeCallback) { - return new ProbeImpl(source, probeCallback); - } - - /** - * Next instrumentation node in chain. - */ - @Child protected InstrumentationNode next; - - protected InstrumentationNode() { - } - - /** - * Gets the {@link Probe} to which this instrument is attached; {@code null} if not attached. - */ - protected Probe getProbe() { - final InstrumentationNode parent = (InstrumentationNode) getParent(); - return parent == null ? null : parent.getProbe(); - } - - /** - * Add an instrument to the end of this instrument chain. - */ - private void internalAddInstrument(Instrument newInstrument) { - if (next == null) { - this.next = insert(newInstrument); - } else { - next.internalAddInstrument(newInstrument); - } - } - - /** - * Remove an instrument from this instrument chain. If no matching instrument is found, a - * {@link RuntimeException} is thrown. - * - * @param oldInstrument The {@link Instrument} to remove. - */ - private void internalRemoveInstrument(Instrument oldInstrument) { - if (next == null) { - throw new RuntimeException("Couldn't find probe to remove: " + oldInstrument); - } else if (next == oldInstrument) { - if (oldInstrument.next == null) { - this.next = null; - } else { - this.next = insert(oldInstrument.next); - oldInstrument.next = null; - } - } else { - next.internalRemoveInstrument(oldInstrument); - } - } - - /** - * Reports to the instance of {@link Probe} holding this instrument, if any, that some essential - * state has changed that requires deoptimization. - */ - @CompilerDirectives.TruffleBoundary - protected void notifyProbeChanged(Instrument instrument) { - Probe probe = getProbe(); - if (probe != null) { - final ProbeImpl probeImpl = (ProbeImpl) probe; - probeImpl.notifyProbeChanged(instrument); - } - } - - /** - * Informs the instrument that execution is just about to enter an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalEnter(Node, VirtualFrame)} to inform all instruments in the chain. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of entry - */ - protected void internalEnter(Node astNode, VirtualFrame frame) { - enter(astNode, frame); - if (next != null) { - next.internalEnter(astNode, frame); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalEnter(Node, VirtualFrame)} to inform all instruments in the chain. In this - * case, there is no return value. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - */ - protected void internalLeave(Node astNode, VirtualFrame frame) { - leave(astNode, frame); - if (next != null) { - next.internalLeave(astNode, frame); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, boolean)} to inform all instruments in the chain. - * In this case, a boolean value was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The boolean result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, boolean result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, byte)} to inform all instruments in the chain. In - * this case, a byte value was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The byte result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, byte result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, short)} to inform all instruments in the chain. In - * this case, a short value was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The short result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, short result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, int)} to inform all instruments in the chain. In - * this case, a int value was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The int result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, int result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, long)} to inform all instruments in the chain. In - * this case, a long value was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The long result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, long result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, char)} to inform all instruments in the chain. In - * this case, a char value was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The char result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, char result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, float)} to inform all instruments in the chain. In - * this case, a float value was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The float result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, float result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, double)} to inform all instruments in the chain. In - * this case, a double value was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The double result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, double result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeave(Node, VirtualFrame, Object)} to inform all instruments in the chain. In - * this case, an Object was returned. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param result The Object result - */ - protected void internalLeave(Node astNode, VirtualFrame frame, Object result) { - leave(astNode, frame, result); - if (next != null) { - next.internalLeave(astNode, frame, result); - } - } - - /** - * Informs the instrument that execution has just returned from an AST node with which this - * instrumentation node is associated. This will continue to call - * {@link #internalLeaveExceptional(Node, VirtualFrame, Exception)} to inform all instruments in - * the chain. In this case, a exception (sometimes containing a value) was thrown. - * - * @param astNode The {@link Node} that was entered - * @param frame The {@link VirtualFrame} at the time of exit - * @param e The exception - */ - protected void internalLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) { - leaveExceptional(astNode, frame, null); - if (next != null) { - next.internalLeaveExceptional(astNode, frame, e); - } - } - - /** - * Holder of a chain of {@linkplain InstrumentationNode instruments}: manages the - * {@link Assumption} that no {@link Instrument}s have been added or removed and that none of - * the attached instruments have changed state in a way that would require deopt. - *

    - * An instance is intended to be shared by every clone of the AST node with which it is - * originally attached, so it holds no parent pointer. - *

    - * Each probe is associated with a {@link SourceSection}, not necessarily uniquely, although - * such a policy could be enforced for some uses. - *

    - * Each {@link Probe} be categorized by one or more {@linkplain SyntaxTag tags}, signifying - * information useful for instrumentation about its AST location(s). - */ - static final class ProbeImpl extends InstrumentationNode implements Probe { - - private final ProbeCallback probeCallback; - - // TODO (mlvdv) assumption model broken - @CompilerDirectives.CompilationFinal private Assumption probeUnchanged; - - @CompilerDirectives.CompilationFinal private SyntaxTagTrap trap = null; - - /** - * The collection of tags for this instrumentation node. - */ - private final ArrayList tags = new ArrayList<>(); - - /** - * The region of source code associated with this probe. Note that this is distinct from - * {@link Node#getSourceSection()}, which is {@code null} for all instances of - * {@link InstrumentationNode} since they have no corresponding source of their own. - */ - private final SourceSection probedSourceSection; - - /** - * Constructor. - * - * @param probedSourceSection The {@link SourceSection} associated with this probe. - * @param probeCallback The {@link ProbeCallback} to inform when tags have been added to - * this probe. - */ - private ProbeImpl(SourceSection probedSourceSection, ProbeCallback probeCallback) { - this.probeCallback = probeCallback; - this.probedSourceSection = probedSourceSection; - this.probeUnchanged = Truffle.getRuntime().createAssumption(); - this.next = null; - } - - /** - * Returns the {@link SourceSection} associated with this probe. - */ - public SourceSection getSourceLocation() { - return probedSourceSection; - } - - /** - * Tags this probe with the given {@link SyntaxTag}. If the tag already exists, the tag is - * not added. - * - * @param tag The tag to add to this probe. - */ - @TruffleBoundary - public void tagAs(SyntaxTag tag) { - assert tag != null; - if (!tags.contains(tag)) { - tags.add(tag); - probeCallback.newTagAdded(this, tag); - } - } - - /** - * Checks if this probe has been tagged with the given tag. - * - * @param tag The {@link SyntaxTag} to check for. - * @return True if this probe has the given tag, false otherwise. - */ - public boolean isTaggedAs(SyntaxTag tag) { - assert tag != null; - return tags.contains(tag); - } - - /** - * Returns an iterable collection of all syntax tags on this probe. - * - * @return A collection of {@link SyntaxTag}s. - */ - public Iterable getSyntaxTags() { - return tags; - } - - /** - * Adds the given {@link Instrument} to this probe's chain of instruments. This method does - * not check to see if the same instrument has already been added. - * - * @param instrument The instrument to add to this probe. - */ - @TruffleBoundary - public void addInstrument(Instrument instrument) { - probeUnchanged.invalidate(); - super.internalAddInstrument(instrument); - probeUnchanged = Truffle.getRuntime().createAssumption(); - } - - /** - * Removes the given instrument from the chain of instruments. If no matching instrument is - * found, a {@link RuntimeException} is thrown. - * - * @param instrument The instrument to remove from this probe. - */ - @TruffleBoundary - public void removeInstrument(Instrument instrument) { - probeUnchanged.invalidate(); - super.internalRemoveInstrument(instrument); - probeUnchanged = Truffle.getRuntime().createAssumption(); - } - - /** - * Returns this probe. - */ - @Override - protected Probe getProbe() { - return this; - } - - @Override - @TruffleBoundary - protected void notifyProbeChanged(Instrument instrument) { - probeUnchanged.invalidate(); - probeUnchanged = Truffle.getRuntime().createAssumption(); - } - - @TruffleBoundary - void setTrap(SyntaxTagTrap trap) { - assert trap == null || isTaggedAs(trap.getTag()); - probeUnchanged.invalidate(); - this.trap = trap; - probeUnchanged = Truffle.getRuntime().createAssumption(); - } - - public void enter(Node astNode, VirtualFrame frame) { - if (trap != null || next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - if (trap != null) { - trap.tagTrappedAt(astNode, frame.materialize()); - } - if (next != null) { - next.internalEnter(astNode, frame); - } - } - } - - public void leave(Node astNode, VirtualFrame frame) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame); - } - } - - public void leave(Node astNode, VirtualFrame frame, boolean result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leave(Node astNode, VirtualFrame frame, byte result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leave(Node astNode, VirtualFrame frame, short result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leave(Node astNode, VirtualFrame frame, int result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leave(Node astNode, VirtualFrame frame, long result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leave(Node astNode, VirtualFrame frame, char result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leave(Node astNode, VirtualFrame frame, float result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leave(Node astNode, VirtualFrame frame, double result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leave(Node astNode, VirtualFrame frame, Object result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeaveExceptional(astNode, frame, e); - } - } - - } - -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineLocationToProbeCollectionMap.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineLocationToProbeCollectionMap.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument.impl; - -import java.io.*; -import java.util.*; - -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.source.*; - -/** - * A mapping from {@link LineLocation} (a line number in a specific piece of {@link Source} code) to - * a collection of {@link Probe}s whose associated {@link SourceSection} starts on that line. - */ -public class LineLocationToProbeCollectionMap implements ProbeListener { - - private static final boolean TRACE = false; - private static final PrintStream OUT = System.out; - - /** - * Map: Source line ==> probes associated with source sections starting on the line. - */ - private final Map> lineToProbesMap = new HashMap<>(); - - public LineLocationToProbeCollectionMap() { - } - - public void newProbeInserted(SourceSection source, Probe probe) { - if (source != null && !(source instanceof NullSourceSection)) { - final LineLocation lineLocation = source.getLineLocation(); - if (TRACE) { - OUT.println("LineLocationToProbeCollectionMap: adding " + lineLocation + " Probe=" + probe); - } - this.addProbeToLine(lineLocation, probe); - } - } - - public void probeTaggedAs(Probe probe, SyntaxTag tag) { - // This map ignores tags - } - - /** - * Returns the {@link Probe}, if any, associated with source that starts on a specified line; if - * there are more than one, return the one with the first starting character location. - */ - public Probe findLineProbe(LineLocation lineLocation) { - Probe probe = null; - final Collection probes = getProbesAtLine(lineLocation); - for (Probe probeOnLine : probes) { - if (probe == null) { - probe = probeOnLine; - } else if (probeOnLine.getSourceLocation().getCharIndex() < probe.getSourceLocation().getCharIndex()) { - probe = probeOnLine; - } - } - return probe; - } - - /** - * Records creation of a probe whose associated source starts on the given line. - *

    - * If the line already exists in the internal {@link #lineToProbesMap}, this probe will be added - * to the existing collection. If no line already exists in the internal map, then a new key is - * added along with a new collection containing the probe. - *

    - * This class requires that each added line/probe pair hasn't been previously added. However, - * attaching the same probe to a new line location is allowed. - * - * @param line The {@link LineLocation} to attach the probe to. - * @param probe The {@link Probe} to attach for that line location. - */ - protected void addProbeToLine(LineLocation line, Probe probe) { - - if (!lineToProbesMap.containsKey(line)) { - // Key does not exist, add new probe list - final ArrayList newProbeList = new ArrayList<>(2); - newProbeList.add(probe); - lineToProbesMap.put(line, newProbeList); - } else { - // Probe list exists, add to existing - final Collection existingProbeList = lineToProbesMap.get(line); - assert !existingProbeList.contains(probe); - existingProbeList.add(probe); - } - } - - /** - * - * Returns a collection of {@link Probe}s whose associated source begins at the given - * {@link LineLocation}, an empty list if none. - * - * @param line The line to check. - * @return A collection of probes at the given line. - */ - public Collection getProbesAtLine(LineLocation line) { - Collection probeList = lineToProbesMap.get(line); - - if (probeList == null) { - return Collections.emptyList(); - } - return probeList; - } - - /** - * Convenience method to get probes according to a int line number. Returns a collection of - * {@link Probe}s at the given line number, an empty list if none. - * - * @param lineNumber The line number to check. - * @return A collection of probes at the given line. - */ - public Collection getProbesAtLineNumber(int lineNumber) { - - final Set keySet = lineToProbesMap.keySet(); - if (keySet.size() == 0) { - return Collections.emptyList(); - } - - ArrayList probes = new ArrayList<>(); - for (LineLocation line : keySet) { - if (line.getLineNumber() == lineNumber) { - probes.addAll(lineToProbesMap.get(line)); - } - } - - return probes; - } - - public void forget(Source source) { - final Set mappedLines = lineToProbesMap.keySet(); - if (mappedLines.size() > 0) { - List forgetLines = new ArrayList<>(); - for (LineLocation line : mappedLines) { - if (line.getSource().equals(source)) { - forgetLines.add(line); - } - } - for (LineLocation line : forgetLines) { - lineToProbesMap.remove(line); - } - } - } -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineLocationToSourceSectionCollectionMap.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineLocationToSourceSectionCollectionMap.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument.impl; - -import java.io.*; -import java.util.*; - -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.source.*; - -/** - * A mapping from {@link LineLocation} (a line number in a specific piece of {@link Source} code) to - * a collection of {@link SourceSection}s that exist on that line. This class assumes that all nodes - * are instrumented as it uses the {@link ProbeListener} interface to determine the source sections - * that exist in the file. - */ -public class LineLocationToSourceSectionCollectionMap implements ProbeListener { - - private static final boolean TRACE = false; - private static final PrintStream OUT = System.out; - - /** - * Map: Source line ==> source sections that exist on the line. - */ - private final Map> lineToSourceSectionsMap = new HashMap<>(); - - public LineLocationToSourceSectionCollectionMap() { - } - - public void newProbeInserted(SourceSection sourceSection, Probe probe) { - if (sourceSection != null && !(sourceSection instanceof NullSourceSection)) { - final LineLocation lineLocation = sourceSection.getLineLocation(); - if (TRACE) { - OUT.println("LineLocationToSourceSectionCollectionMap: adding " + lineLocation + " Probe=" + probe); - } - this.addSourceSectionToLine(lineLocation, sourceSection); - } - } - - public void probeTaggedAs(Probe probe, SyntaxTag tag) { - // This map ignores tags, but this subclasses can override this method to operate on tags. - } - - /** - * Adds a source section to the given line. - *

    - * If the line already exists in the internal {@link #lineToSourceSectionsMap}, this source - * section will be added to the existing collection. If no line already exists in the internal - * map, then a new key is added along with a new collection containing the source section. - *

    - * This class does not check if a source section has already been added to a line. - * - * @param line The {@link LineLocation} to attach the source section to. - * @param sourceSection The {@link SourceSection} to attach for that line location. - */ - protected void addSourceSectionToLine(LineLocation line, SourceSection sourceSection) { - if (!lineToSourceSectionsMap.containsKey(line)) { - // Key does not exist, add new source section list - final ArrayList newSourceSectionList = new ArrayList<>(2); - newSourceSectionList.add(sourceSection); - lineToSourceSectionsMap.put(line, newSourceSectionList); - } else { - // Source section list exists, add to existing - final Collection existingSourceSectionList = lineToSourceSectionsMap.get(line); - existingSourceSectionList.add(sourceSection); - } - } - - /** - * Returns a collection of {@link SourceSection}s at the given {@link LineLocation}, an empty - * list if none. - * - * @param line The line to check. - * @return the source sections at the given line. - */ - public Collection getSourceSectionsAtLine(LineLocation line) { - Collection sourceSectionList = lineToSourceSectionsMap.get(line); - - if (sourceSectionList == null) { - return Collections.emptyList(); - } - return sourceSectionList; - } - - /** - * Convenience method to get source sections according to a int line number. Returns a - * collection of {@link SourceSection}s at the given line number. If there are no source - * sections at that line, an empty list is returned. - * - * @param lineNumber The line number to check. - * @return A collection of source sections at the given line. - */ - public Collection getSourceSectionsAtLineNumber(int lineNumber) { - - final Set keySet = lineToSourceSectionsMap.keySet(); - if (keySet.size() == 0) { - return Collections.emptyList(); - } - - final ArrayList sourceSections = new ArrayList<>(); - for (LineLocation line : keySet) { - if (line.getLineNumber() == lineNumber) { - sourceSections.addAll(lineToSourceSectionsMap.get(line)); - } - } - - return sourceSections; - } -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineToProbesMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineToProbesMap.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument.impl; + +import java.io.*; +import java.util.*; + +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.Probe.ProbeListener; +import com.oracle.truffle.api.source.*; + +/** + * A mapping from {@link LineLocation} (a line number in a specific piece of {@link Source} code) to + * a collection of {@link Probe}s whose associated {@link SourceSection} starts on that line. + */ +public class LineToProbesMap implements ProbeListener { + + private static final boolean TRACE = false; + private static final PrintStream OUT = System.out; + + private static void trace(String msg) { + OUT.println("LineToProbesMap: " + msg); + } + + /** + * Map: Source line ==> probes associated with source sections starting on the line. + */ + private final Map> lineToProbesMap = new HashMap<>(); + + public LineToProbesMap() { + } + + public void startASTProbing(Source source) { + } + + public void newProbeInserted(Probe probe) { + final SourceSection sourceSection = probe.getProbedSourceSection(); + if (sourceSection != null && !(sourceSection instanceof NullSourceSection)) { + final LineLocation lineLocation = sourceSection.getLineLocation(); + if (TRACE) { + trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription()); + } + this.addProbeToLine(lineLocation, probe); + } + } + + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + // This map ignores tags + } + + public void endASTProbing(Source source) { + } + + /** + * Returns the {@link Probe}, if any, associated with source that starts on a specified line; if + * there are more than one, return the one with the first starting character location. + */ + public Probe findLineProbe(LineLocation lineLocation) { + Probe probe = null; + final Collection probes = getProbesAtLine(lineLocation); + for (Probe probesOnLine : probes) { + if (probe == null) { + probe = probesOnLine; + } else if (probesOnLine.getProbedSourceSection().getCharIndex() < probe.getProbedSourceSection().getCharIndex()) { + probe = probesOnLine; + } + } + return probe; + } + + /** + * Records creation of a probe whose associated source starts on the given line. + *

    + * If the line already exists in the internal {@link #lineToProbesMap}, this probe will be added + * to the existing collection. If no line already exists in the internal map, then a new key is + * added along with a new collection containing the probe. + *

    + * This class requires that each added line/probe pair hasn't been previously added. However, + * attaching the same probe to a new line location is allowed. + * + * @param line The {@link LineLocation} to attach the probe to. + * @param probe The {@link Probe} to attach for that line location. + */ + protected void addProbeToLine(LineLocation line, Probe probe) { + + if (!lineToProbesMap.containsKey(line)) { + // Key does not exist, add new probe list + final ArrayList newProbeList = new ArrayList<>(2); + newProbeList.add(probe); + lineToProbesMap.put(line, newProbeList); + } else { + // Probe list exists, add to existing + final Collection existingProbeList = lineToProbesMap.get(line); + assert !existingProbeList.contains(probe); + existingProbeList.add(probe); + } + } + + /** + * + * Returns a collection of {@link Probe}s whose associated source begins at the given + * {@link LineLocation}, an empty list if none. + * + * @param line The line to check. + * @return A collection of probes at the given line. + */ + public Collection getProbesAtLine(LineLocation line) { + Collection probesList = lineToProbesMap.get(line); + + if (probesList == null) { + return Collections.emptyList(); + } + return probesList; + } + + /** + * Convenience method to get probes according to a int line number. Returns a collection of + * {@link Probe}s at the given line number, an empty list if none. + * + * @param lineNumber The line number to check. + * @return A collection of probes at the given line. + */ + public Collection getProbesAtLineNumber(int lineNumber) { + + final Set keySet = lineToProbesMap.keySet(); + if (keySet.size() == 0) { + return Collections.emptyList(); + } + + ArrayList probes = new ArrayList<>(); + for (LineLocation line : keySet) { + if (line.getLineNumber() == lineNumber) { + probes.addAll(lineToProbesMap.get(line)); + } + } + + return probes; + } + + public void forget(Source source) { + final Set mappedLines = lineToProbesMap.keySet(); + if (mappedLines.size() > 0) { + List forgetLines = new ArrayList<>(); + for (LineLocation line : mappedLines) { + if (line.getSource().equals(source)) { + forgetLines.add(line); + } + } + for (LineLocation line : forgetLines) { + lineToProbesMap.remove(line); + } + } + } +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineToSourceSectionMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineToSourceSectionMap.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument.impl; + +import java.io.*; +import java.util.*; + +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.Probe.*; +import com.oracle.truffle.api.source.*; + +/** + * A mapping from {@link LineLocation} (a line number in a specific piece of {@link Source} code) to + * a collection of {@link SourceSection}s that exist on that line. This class assumes that all nodes + * are instrumented as it uses the {@link ProbeListener} interface to determine the source sections + * that exist in the file. + */ +public class LineToSourceSectionMap implements ProbeListener { + + private static final boolean TRACE = false; + private static final PrintStream OUT = System.out; + + private static void trace(String msg) { + OUT.println("LineToSourceSectionMap: " + msg); + } + + /** + * Map: Source line ==> source sections that exist on the line. + */ + private final Map> lineToSourceSectionsMap = new HashMap<>(); + + public LineToSourceSectionMap() { + } + + public void startASTProbing(Source source) { + } + + public void newProbeInserted(Probe probe) { + final SourceSection sourceSection = probe.getProbedSourceSection(); + if (sourceSection != null && !(sourceSection instanceof NullSourceSection)) { + final LineLocation lineLocation = sourceSection.getLineLocation(); + if (TRACE) { + trace("NEW " + lineLocation.getShortDescription() + " Probe=" + probe); + } + this.addSourceSectionToLine(lineLocation, sourceSection); + } + } + + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + // This map ignores tags, but this subclasses can override this method to operate on tags. + } + + public void endASTProbing(Source source) { + } + + /** + * Adds a source section to the given line. + *

    + * If the line already exists in the internal {@link #lineToSourceSectionsMap}, this source + * section will be added to the existing collection. If no line already exists in the internal + * map, then a new key is added along with a new collection containing the source section. + *

    + * This class does not check if a source section has already been added to a line. + * + * @param line The {@link LineLocation} to attach the source section to. + * @param sourceSection The {@link SourceSection} to attach for that line location. + */ + protected void addSourceSectionToLine(LineLocation line, SourceSection sourceSection) { + if (!lineToSourceSectionsMap.containsKey(line)) { + // Key does not exist, add new source section list + final ArrayList newSourceSectionList = new ArrayList<>(2); + newSourceSectionList.add(sourceSection); + lineToSourceSectionsMap.put(line, newSourceSectionList); + } else { + // Source section list exists, add to existing + final Collection existingSourceSectionList = lineToSourceSectionsMap.get(line); + existingSourceSectionList.add(sourceSection); + } + } + + /** + * Returns a collection of {@link SourceSection}s at the given {@link LineLocation}, an empty + * list if none. + * + * @param line The line to check. + * @return the source sections at the given line. + */ + public Collection getSourceSectionsAtLine(LineLocation line) { + Collection sourceSectionList = lineToSourceSectionsMap.get(line); + + if (sourceSectionList == null) { + return Collections.emptyList(); + } + return sourceSectionList; + } + + /** + * Convenience method to get source sections according to a int line number. Returns a + * collection of {@link SourceSection}s at the given line number. If there are no source + * sections at that line, an empty list is returned. + * + * @param lineNumber The line number to check. + * @return A collection of source sections at the given line. + */ + public Collection getSourceSectionsAtLineNumber(int lineNumber) { + + final Set keySet = lineToSourceSectionsMap.keySet(); + if (keySet.size() == 0) { + return Collections.emptyList(); + } + + final ArrayList sourceSections = new ArrayList<>(); + for (LineLocation line : keySet) { + if (line.getLineNumber() == lineNumber) { + sourceSections.addAll(lineToSourceSectionsMap.get(line)); + } + } + + return sourceSections; + } +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/ProbeManager.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/ProbeManager.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,164 +0,0 @@ -/* - * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.api.instrument.impl; - -import java.util.*; - -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeCallback; -import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeImpl; -import com.oracle.truffle.api.source.*; - -/** - * Factory and services for AST {@link Probe}s. - */ -public final class ProbeManager { - - private final List probeListeners = new ArrayList<>(); - - private final List allProbes = new ArrayList<>(); - - /** - * Called when a {@link #tagTrap} is activated in a Probe. - */ - private final ProbeCallback probeCallback; - - /** - * When non-null, "enter" events with matching tags will trigger a callback. - */ - private SyntaxTagTrap tagTrap = null; - - public ProbeManager() { - this.probeCallback = new ProbeCallback() { - /** - * Receives (from the {@link Probe} implementation) and distributes notification that a - * {@link Probe} has acquired a new {@linkplain SyntaxTag tag}. - */ - public void newTagAdded(ProbeImpl probe, SyntaxTag tag) { - for (ProbeListener listener : probeListeners) { - listener.probeTaggedAs(probe, tag); - } - if (tagTrap != null && tag == tagTrap.getTag()) { - probe.setTrap(tagTrap); - } - } - }; - } - - /** - * Adds a {@link ProbeListener} to receive events. - */ - public void addProbeListener(ProbeListener listener) { - assert listener != null; - probeListeners.add(listener); - } - - /** - * Removes a {@link ProbeListener}. Ignored if listener not found. - */ - public void removeProbeListener(ProbeListener removeListener) { - final List listeners = new ArrayList<>(probeListeners); - for (ProbeListener listener : listeners) { - if (listener == removeListener) { - probeListeners.remove(listener); - } - } - } - - /** - * Creates a new {@link Probe} associated with a {@link SourceSection} of code corresponding to - * a Truffle AST node. - */ - public Probe createProbe(SourceSection source) { - assert source != null; - - ProbeImpl probe = InstrumentationNode.createProbe(source, probeCallback); - allProbes.add(probe); - - for (ProbeListener listener : probeListeners) { - listener.newProbeInserted(source, probe); - } - - return probe; - } - - /** - * Returns the subset of all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole - * collection if the specified tag is {@code null}. - * - * @return A collection of probes containing the given tag. - */ - public Collection findProbesTaggedAs(SyntaxTag tag) { - final List probes = new ArrayList<>(); - for (Probe probe : allProbes) { - if (tag == null || probe.isTaggedAs(tag)) { - probes.add(probe); - } - } - return probes; - } - - /** - * Sets the current "tag trap", which will cause a callback to be triggered whenever execution - * reaches a Probe (existing or subsequently created) with the specified tag. There can only be - * one tag trap set at a time. - *

    - * - * @param tagTrap The {@link SyntaxTagTrap} to set. - * @throws IllegalStateException if a trap is currently set. - */ - public void setTagTrap(SyntaxTagTrap tagTrap) throws IllegalStateException { - assert tagTrap != null; - if (this.tagTrap != null) { - throw new IllegalStateException("trap already set"); - } - this.tagTrap = tagTrap; - - SyntaxTag tag = tagTrap.getTag(); - for (ProbeImpl probe : allProbes) { - if (probe.isTaggedAs(tag)) { - probe.setTrap(tagTrap); - } - } - } - - /** - * Clears the current {@link SyntaxTagTrap}. - * - * @throws IllegalStateException if no trap is currently set. - */ - public void clearTagTrap() { - if (this.tagTrap == null) { - throw new IllegalStateException("no trap set"); - } - for (ProbeImpl probe : allProbes) { - if (probe.isTaggedAs(tagTrap.getTag())) { - probe.setTrap(null); - } - } - tagTrap = null; - } - -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleEventReceiver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleEventReceiver.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument.impl; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.*; + +/** + * An abstract receiver for AST {@linkplain TruffleEventReceiver execution events} that ignores + * return values and supports handling all events by overriding only two methods: + *

      + *
    • {@link #enter(Node, VirtualFrame)}, and
    • + *
    • {@link #returnAny(Node, VirtualFrame)}.
    • + *
    + */ +public abstract class SimpleEventReceiver implements TruffleEventReceiver { + + public void enter(Node node, VirtualFrame frame) { + } + + /** + * Receive notification that one of an AST Node's execute methods has just returned by any + * means: with or without a return value (ignored) or via exception (ignored). + * + * @param node + * @param frame + */ + public void returnAny(Node node, VirtualFrame frame) { + } + + public final void returnVoid(Node node, VirtualFrame frame) { + returnAny(node, frame); + } + + public final void returnValue(Node node, VirtualFrame frame, Object result) { + returnAny(node, frame); + } + + public final void returnExceptional(Node node, VirtualFrame frame, Exception e) { + returnAny(node, frame); + } + +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Sun Nov 23 16:07:23 2014 -0800 @@ -33,6 +33,8 @@ import sun.misc.*; import com.oracle.truffle.api.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.nodes.Node.Children; import com.oracle.truffle.api.source.*; @@ -769,6 +771,9 @@ } sb.append(" (" + node.getClass().getSimpleName() + ") "); + + sb.append(printSyntaxTags(node)); + sb.append(displaySourceAttribution(node)); p.println(sb.toString()); @@ -798,6 +803,34 @@ } /** + * Returns a string listing the {@linkplain SyntaxTag syntax tags}, if any, associated with a + * node: + *
      + *
    • "[{@linkplain StandardSyntaxTag#STATEMENT STATEMENT}, + * {@linkplain StandardSyntaxTag#ASSIGNMENT ASSIGNMENT}]" if tags have been applied;
    • + *
    • "[]" if the node supports tags, but none are present; and
    • + *
    • "" if the node does not support tags.
    • + *
    + */ + public static String printSyntaxTags(final Object node) { + if (node instanceof WrapperNode) { + final Probe probe = ((WrapperNode) node).getProbe(); + final Collection syntaxTags = probe.getSyntaxTags(); + final StringBuilder sb = new StringBuilder(); + String prefix = ""; + sb.append("["); + for (SyntaxTag tag : syntaxTags) { + sb.append(prefix); + prefix = ","; + sb.append(tag.toString()); + } + sb.append("]"); + return sb.toString(); + } + return ""; + } + + /** * Prints a human readable form of a {@link Node} AST to the given {@link PrintStream}. This * print method does not check for cycles in the node structure. * diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Sun Nov 23 16:07:23 2014 -0800 @@ -24,10 +24,11 @@ */ package com.oracle.truffle.api.nodes; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.impl.*; +import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.source.*; /** @@ -115,7 +116,7 @@ * stack) without prior knowledge of the language it has come from. * * Used for instance to determine the language of a RootNode: - * + * *
          * 
          * rootNode.getExecutionContext().getLanguageShortName();
    @@ -140,4 +141,19 @@
             }
         }
     
    +    /**
    +     * Apply all registered instances of {@link ASTProber} to the AST, if any, held by this root
    +     * node. This can only be done once the AST is complete, notably once all parent pointers are
    +     * correctly assigned. But it also must be done before any AST cloning or execution.
    +     * 

    + * If this is not done, then the AST will not be subject to debugging or any other + * instrumentation-supported tooling. + *

    + * Implementations should ensure that instrumentation is never applied more than once to an AST, + * as this is not guaranteed to be error-free. + * + * @see Probe#registerASTProber(com.oracle.truffle.api.instrument.ASTProber) + */ + public void applyInstrumentation() { + } } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java Sun Nov 23 16:07:23 2014 -0800 @@ -38,4 +38,6 @@ */ int getLineNumber(); + String getShortDescription(); + } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Sun Nov 23 16:07:23 2014 -0800 @@ -904,8 +904,13 @@ } @Override + public String getShortDescription() { + return source.getShortName() + ":" + line; + } + + @Override public String toString() { - return "SourceLine [" + source.getName() + ", " + line + "]"; + return "Line[" + getShortDescription() + "]"; } @Override diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java --- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java Sun Nov 23 16:07:23 2014 -0800 @@ -40,6 +40,7 @@ import com.oracle.truffle.api.source.*; import com.oracle.truffle.sl.*; import com.oracle.truffle.sl.builtins.*; +import com.oracle.truffle.sl.factory.*; import com.oracle.truffle.sl.runtime.*; import com.oracle.truffle.sl.test.SLTestRunner.TestCase; @@ -166,7 +167,7 @@ ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream printer = new PrintStream(out); try { - SLContext context = new SLContext(new BufferedReader(new StringReader(repeat(testCase.testInput, repeats))), printer); + SLContext context = SLContextFactory.create(new BufferedReader(new StringReader(repeat(testCase.testInput, repeats))), printer); for (NodeFactory builtin : builtins) { context.installBuiltin(builtin); } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java --- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Sun Nov 23 16:07:23 2014 -0800 @@ -36,10 +36,14 @@ import org.junit.runners.*; import org.junit.runners.model.*; -import com.oracle.truffle.api.*; +import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.impl.*; +import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; +import com.oracle.truffle.sl.factory.*; import com.oracle.truffle.sl.nodes.instrument.*; +import com.oracle.truffle.sl.nodes.local.*; import com.oracle.truffle.sl.parser.*; import com.oracle.truffle.sl.runtime.*; import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase; @@ -85,10 +89,14 @@ public SLInstrumentTestRunner(Class testClass) throws InitializationError { super(testClass); + final SLStandardASTProber prober = new SLStandardASTProber(); + Probe.registerASTProber(prober); try { testCases = createTests(testClass); } catch (IOException e) { throw new InitializationError(e); + } finally { + Probe.unregisterASTProber(prober); } } @@ -197,33 +205,21 @@ ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream printer = new PrintStream(out); + final ASTProber prober = new SLStandardASTProber(); + Probe.registerASTProber(prober); try { // We use the name of the file to determine what visitor to attach to it. if (testCase.baseName.endsWith(ASSIGNMENT_VALUE_SUFFIX)) { // Set up the execution context for Simple and register our two listeners - slContext = new SLContext(new BufferedReader(new StringReader(testCase.testInput)), printer); + slContext = SLContextFactory.create(new BufferedReader(new StringReader(testCase.testInput)), printer); final Source source = Source.fromText(readAllLines(testCase.path), testCase.sourceName); Parser.parseSL(slContext, source); - List functionList = slContext.getFunctionRegistry().getFunctions(); - // Since only functions can be global in SL, this guarantees that we instrument - // everything of interest. Parsing must occur before accepting the visitors since - // the visitor which creates our instrumentation points expects a complete AST. - - for (SLFunction function : functionList) { - RootCallTarget rootCallTarget = function.getCallTarget(); - if (rootCallTarget != null) { - rootCallTarget.getRootNode().accept(new SLInstrumenter()); - } - } - - // We iterate over all tags the SLInsturmenter tagged as assignments and attach our - // test instrument to those. - for (Probe probe : slContext.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) { - if (probe.isTaggedAs(StandardSyntaxTag.ASSIGNMENT)) { - probe.addInstrument(new SLPrintAssigmentValueInstrument(printer)); - } + // Attach an instrument to every probe tagged as an assignment + for (Probe probe : Probe.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) { + SLPrintAssigmentValueReciever slPrintAssigmentValueReceiver = new SLPrintAssigmentValueReciever(printer); + probe.attach(Instrument.create(slPrintAssigmentValueReceiver, "SL print assignment value")); } SLFunction main = slContext.getFunctionRegistry().lookup("main"); @@ -237,6 +233,7 @@ } catch (Throwable ex) { notifier.fireTestFailure(new Failure(testCase.name, ex)); } finally { + Probe.unregisterASTProber(prober); notifier.fireTestFinished(testCase.name); } @@ -272,4 +269,25 @@ return "Filter contains " + pattern; } } + + /** + * This sample instrument receiver provides prints the value of an assignment (after the + * assignment is complete) to the {@link PrintStream} specified in the constructor. This + * instrument can only be attached to a wrapped {@link SLWriteLocalVariableNode}, but provides + * no guards to protect it from being attached elsewhere. + */ + public final class SLPrintAssigmentValueReciever extends DefaultEventReceiver { + + private PrintStream output; + + public SLPrintAssigmentValueReciever(PrintStream output) { + this.output = output; + } + + @Override + public void returnValue(Node node, VirtualFrame frame, Object result) { + output.println(result); + } + } + } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLPrintAssigmentValueInstrument.java --- a/graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLPrintAssigmentValueInstrument.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.sl.test.instrument; - -import java.io.*; - -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.sl.nodes.local.*; - -/** - * This sample instrument provides prints the value of an assignment (after the assignment is - * complete) to the {@link PrintStream} specified in the constructor. This instrument can only be - * attached to a wrapped {@link SLWriteLocalVariableNode}, but provides no guards to protect it from - * being attached elsewhere. - */ -public final class SLPrintAssigmentValueInstrument extends Instrument { - - private PrintStream output; - - public SLPrintAssigmentValueInstrument(PrintStream output) { - this.output = output; - } - - @Override - public void leave(Node astNode, VirtualFrame frame, Object result) { - output.println(result); - } -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java Sun Nov 23 16:07:23 2014 -0800 @@ -27,10 +27,10 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; -import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.sl.builtins.*; +import com.oracle.truffle.sl.factory.*; import com.oracle.truffle.sl.nodes.*; import com.oracle.truffle.sl.nodes.call.*; import com.oracle.truffle.sl.nodes.controlflow.*; @@ -119,7 +119,7 @@ */ public static void main(String[] args) throws IOException { - SLContext context = new SLContext(new BufferedReader(new InputStreamReader(System.in)), System.out); + SLContext context = SLContextFactory.create(new BufferedReader(new InputStreamReader(System.in)), System.out); Source source; if (args.length == 0) { @@ -146,16 +146,9 @@ // logOutput.println("Source = " + source.getCode()); } - final SourceCallback sourceCallback = context.getSourceCallback(); + /* Parse the SL source file. */ + Parser.parseSL(context, source); - /* Parse the SL source file. */ - if (sourceCallback != null) { - sourceCallback.startLoading(source); - } - Parser.parseSL(context, source); - if (sourceCallback != null) { - sourceCallback.endLoading(source); - } /* Lookup our main entry point, which is per definition always named "main". */ SLFunction main = context.getFunctionRegistry().lookup("main"); if (main.getCallTarget() == null) { diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/factory/SLContextFactory.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/factory/SLContextFactory.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/factory/SLContextFactory.java Sun Nov 23 16:07:23 2014 -0800 @@ -30,12 +30,14 @@ public final class SLContextFactory { private SLContextFactory() { - } public static SLContext create() { - final SLContext slContext = new SLContext(new BufferedReader(new InputStreamReader(System.in)), System.out); - slContext.initialize(); + return create(new BufferedReader(new InputStreamReader(System.in)), System.out); + } + + public static SLContext create(BufferedReader input, PrintStream output) { + final SLContext slContext = new SLContext(input, output); slContext.setVisualizer(new SLDefaultVisualizer()); return slContext; } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Sun Nov 23 16:07:23 2014 -0800 @@ -95,19 +95,39 @@ Node parent = getParent(); if (parent == null) { - throw new IllegalStateException("Cannot probe a node without a parent"); + throw new IllegalStateException("Cannot call probe() a node without a parent."); } - if (parent instanceof SLExpressionWrapper) { - return ((SLExpressionWrapper) parent).getProbe(); + if (parent instanceof SLExpressionWrapperNode) { + return ((SLExpressionWrapperNode) parent).getProbe(); } // Create a new wrapper/probe with this node as its child. - final SLExpressionWrapper wrapper = new SLExpressionWrapper(getRootNodeSLContext(this), this); + final SLExpressionWrapperNode wrapper = new SLExpressionWrapperNode(this); + + // Connect it to a Probe + final Probe probe = ProbeNode.insertProbe(wrapper); // Replace this node in the AST with the wrapper this.replace(wrapper); - return wrapper.getProbe(); + return probe; + } + + @Override + public void probeLite(TruffleEventReceiver eventReceiver) { + Node parent = getParent(); + + if (parent == null) { + throw new IllegalStateException("Cannot call probeLite() on a node without a parent."); + } + + if (parent instanceof SLExpressionWrapperNode) { + throw new IllegalStateException("Cannot call probeLite() on a node that already has a wrapper."); + } + final SLExpressionWrapperNode wrapper = new SLExpressionWrapperNode(this); + ProbeNode.insertProbeLite(wrapper, eventReceiver); + + this.replace(wrapper); } } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Sun Nov 23 16:07:23 2014 -0800 @@ -22,9 +22,10 @@ */ package com.oracle.truffle.sl.nodes; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.*; -import com.oracle.truffle.api.CompilerDirectives.*; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.sl.builtins.*; import com.oracle.truffle.sl.nodes.controlflow.*; @@ -80,6 +81,11 @@ } @Override + public void applyInstrumentation() { + Probe.applyASTProbers(bodyNode); + } + + @Override public String toString() { return "root " + name; } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Sun Nov 23 16:07:23 2014 -0800 @@ -26,10 +26,10 @@ import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.ProbeNode.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.sl.nodes.instrument.*; -import com.oracle.truffle.sl.runtime.*; /** * The base class of all Truffle nodes for SL. All nodes (even expressions) can be used as @@ -90,28 +90,40 @@ Node parent = getParent(); if (parent == null) { - throw new IllegalStateException("Cannot probe a node without a parent"); + throw new IllegalStateException("Cannot call probe() on a node without a parent."); } - if (parent instanceof SLStatementWrapper) { - return ((SLStatementWrapper) parent).getProbe(); + if (parent instanceof SLStatementWrapperNode) { + return ((SLStatementWrapperNode) parent).getProbe(); } // Create a new wrapper/probe with this node as its child. - final SLStatementWrapper wrapper = new SLStatementWrapper(getRootNodeSLContext(this), this); + final SLStatementWrapperNode wrapper = new SLStatementWrapperNode(this); + + // Connect it to a Probe + final Probe probe = ProbeNode.insertProbe(wrapper); // Replace this node in the AST with the wrapper this.replace(wrapper); - return wrapper.getProbe(); + return probe; } - protected SLContext getRootNodeSLContext(Node node) { - assert node != null; + @Override + public void probeLite(TruffleEventReceiver eventReceiver) { + Node parent = getParent(); + + if (parent == null) { + throw new IllegalStateException("Cannot call probeLite() on a node without a parent"); + } - if (node instanceof SLRootNode) { - return ((SLRootNode) node).getSLContext(); + if (parent instanceof SLStatementWrapperNode) { + throw new IllegalStateException("Cannot call probeLite() on a node that already has a wrapper."); } - return getRootNodeSLContext(node.getParent()); + + final SLStatementWrapperNode wrapper = new SLStatementWrapperNode(this); + ProbeNode.insertProbeLite(wrapper, eventReceiver); + + this.replace(wrapper); } } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLASTPrinter.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLASTPrinter.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLASTPrinter.java Sun Nov 23 16:07:23 2014 -0800 @@ -26,37 +26,23 @@ import java.util.*; import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.impl.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.nodes.NodeUtil.NodeClass; import com.oracle.truffle.api.nodes.NodeUtil.NodeField; import com.oracle.truffle.api.nodes.NodeUtil.NodeFieldKind; -import com.oracle.truffle.api.source.*; /** * SLASTPrinter is used to print for SL's internal Truffle AST. This is used by * {@link SLDefaultVisualizer} to provide a means of displaying the internal Truffle AST */ -public final class SLASTPrinter implements ASTPrinter { +public final class SLASTPrinter extends DefaultASTPrinter { + public SLASTPrinter() { } - public void printTree(PrintWriter p, Node node, int maxDepth, Node markNode) { - printTree(p, node, maxDepth, markNode, 1); - p.println(); - p.flush(); - } - - public String printTreeToString(Node node, int maxDepth, Node markNode) { - StringWriter out = new StringWriter(); - printTree(new PrintWriter(out), node, maxDepth, markNode); - return out.toString(); - } - - public String printTreeToString(Node node, int maxDepth) { - return printTreeToString(node, maxDepth, null); - } - - private static void printTree(PrintWriter p, Node node, int maxDepth, Node markNode, int level) { + @Override + protected void printTree(PrintWriter p, Node node, int maxDepth, Node markNode, int level) { if (node == null) { p.print("null"); return; @@ -64,29 +50,15 @@ p.print(nodeName(node)); - String sep = ""; p.print("("); - final SourceSection src = node.getSourceSection(); - if (src != null) { - if (!(src instanceof NullSourceSection)) { - p.print(src.getSource().getName() + ":" + src.getStartLine()); - } else if (src instanceof NullSourceSection) { - final NullSourceSection nullSection = (NullSourceSection) src; - p.print(nullSection.getShortDescription()); - } + if (node instanceof InstrumentationNode) { + p.print(instrumentInfo((InstrumentationNode) node)); } - if (node instanceof SyntaxTagged) { - final SyntaxTagged taggedNode = (SyntaxTagged) node; - p.print("["); - String prefix = ""; - for (SyntaxTag tag : taggedNode.getSyntaxTags()) { - p.print(prefix); - prefix = ","; - p.print(tag.toString()); - } - p.print("]"); - } + + p.print(sourceInfo(node)); + + p.print(NodeUtil.printSyntaxTags(node)); ArrayList childFields = new ArrayList<>(); @@ -120,23 +92,9 @@ p.print(field.getName()); p.print(" = null "); } else if (field.getKind() == NodeFieldKind.CHILD) { - final Node valueNode = (Node) value; - printNewLine(p, level, valueNode == markNode); - p.print(field.getName()); - p.print(" = "); - printTree(p, valueNode, maxDepth, markNode, level + 1); + printChild(p, maxDepth, markNode, level, field, value); } else if (field.getKind() == NodeFieldKind.CHILDREN) { - printNewLine(p, level); - p.print(field.getName()); - Node[] children = (Node[]) value; - p.print(" = ["); - sep = ""; - for (Node child : children) { - p.print(sep); - sep = ", "; - printTree(p, child, maxDepth, markNode, level + 1); - } - p.print("]"); + printChildren(p, maxDepth, markNode, level, field, value); } else { printNewLine(p, level); p.print(field.getName()); @@ -148,22 +106,4 @@ } } - private static void printNewLine(PrintWriter p, int level, boolean mark) { - p.println(); - for (int i = 0; i < level; i++) { - if (mark && i == 0) { - p.print(" -->"); - } else { - p.print(" "); - } - } - } - - private static void printNewLine(PrintWriter p, int level) { - printNewLine(p, level, false); - } - - private static String nodeName(Node node) { - return node.getClass().getSimpleName(); - } } diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapper.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapper.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +0,0 @@ -/* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.sl.nodes.instrument; - -import java.math.*; - -import com.oracle.truffle.api.CompilerDirectives.*; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.sl.nodes.*; -import com.oracle.truffle.sl.runtime.*; - -/** - * A Truffle node that can be inserted into a Simple AST (assumed not to have executed yet) to - * enable "instrumentation" of an {@link SLExpressionNode}. Tools wishing to interact with AST - * execution may attach {@link Instrument}s to the {@link Probe} uniquely associated with the - * wrapper, and to which this wrapper routes {@link ExecutionEvents}. - */ - -public final class SLExpressionWrapper extends SLExpressionNode implements Wrapper { - @Child private SLExpressionNode child; - - private final Probe probe; - - /** - * Constructor. - * - * @param context The current Simple execution context - * @param child The {@link SLExpressionNode} that this wrapper is wrapping - */ - public SLExpressionWrapper(SLContext context, SLExpressionNode child) { - super(child.getSourceSection()); - assert !(child instanceof SLExpressionWrapper); - this.probe = context.createProbe(child.getSourceSection()); - this.child = child; - // The child should only be inserted after a replace, so we defer inserting the child to the - // creator of the wrapper. - } - - @Override - public SLExpressionNode getNonWrapperNode() { - return child; - } - - @Override - public Node getChild() { - return child; - } - - @Override - public Probe getProbe() { - return probe; - } - - @TruffleBoundary - public void tagAs(SyntaxTag tag) { - probe.tagAs(tag); - } - - @Override - public Object executeGeneric(VirtualFrame frame) { - this.tagAs(StandardSyntaxTag.STATEMENT); - probe.enter(child, frame); - Object result; - - try { - result = child.executeGeneric(frame); - probe.leave(child, frame, result); - } catch (Exception e) { - probe.leaveExceptional(child, frame, e); - throw (e); - } - return result; - } - - @Override - public long executeLong(VirtualFrame frame) throws UnexpectedResultException { - this.tagAs(StandardSyntaxTag.STATEMENT); - return SLTypesGen.SLTYPES.expectLong(executeGeneric(frame)); - } - - @Override - public BigInteger executeBigInteger(VirtualFrame frame) throws UnexpectedResultException { - this.tagAs(StandardSyntaxTag.STATEMENT); - return SLTypesGen.SLTYPES.expectBigInteger(executeGeneric(frame)); - } - - @Override - public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException { - this.tagAs(StandardSyntaxTag.STATEMENT); - return SLTypesGen.SLTYPES.expectBoolean(executeGeneric(frame)); - } - - @Override - public String executeString(VirtualFrame frame) throws UnexpectedResultException { - this.tagAs(StandardSyntaxTag.STATEMENT); - return SLTypesGen.SLTYPES.expectString(executeGeneric(frame)); - } - - @Override - public SLFunction executeFunction(VirtualFrame frame) throws UnexpectedResultException { - this.tagAs(StandardSyntaxTag.STATEMENT); - probe.enter(child, frame); - SLFunction result; - - try { - result = child.executeFunction(frame); - probe.leave(child, frame, result); - } catch (Exception e) { - probe.leaveExceptional(child, frame, e); - throw (e); - } - return result; - } - - @Override - public SLNull executeNull(VirtualFrame frame) throws UnexpectedResultException { - return SLTypesGen.SLTYPES.expectSLNull(executeGeneric(frame)); - } - -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.sl.nodes.instrument; + +import java.math.*; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.sl.nodes.*; +import com.oracle.truffle.sl.runtime.*; + +/** + * A Truffle node that can be inserted into a Simple AST (assumed not to have executed yet) to + * enable "instrumentation" of an {@link SLExpressionNode}. Tools wishing to interact with AST + * execution may attach {@link Instrument}s to the {@link Probe} uniquely associated with the + * wrapper, and to which this wrapper routes execution events. + */ +@NodeInfo(cost = NodeCost.NONE) +public final class SLExpressionWrapperNode extends SLExpressionNode implements WrapperNode { + @Child private SLExpressionNode child; + @Child private ProbeNode probeNode; + + /** + * Constructor. + * + * @param child The {@link SLExpressionNode} that this wrapper is wrapping + */ + public SLExpressionWrapperNode(SLExpressionNode child) { + super(child.getSourceSection()); + assert !(child instanceof SLExpressionWrapperNode); + this.child = child; + } + + public String instrumentationInfo() { + return "Wrapper node for SL Expressions"; + } + + @Override + public SLExpressionNode getNonWrapperNode() { + return child; + } + + public void insertProbe(ProbeNode newProbeNode) { + this.probeNode = newProbeNode; + } + + public Probe getProbe() { + try { + return probeNode.getProbe(); + } catch (IllegalStateException e) { + throw new IllegalStateException("A lite-Probed wrapper has no explicit Probe"); + } + } + + public Node getChild() { + return child; + } + + @Override + public Object executeGeneric(VirtualFrame frame) { + + probeNode.enter(child, frame); + Object result; + + try { + result = child.executeGeneric(frame); + probeNode.returnValue(child, frame, result); + } catch (Exception e) { + probeNode.returnExceptional(child, frame, e); + throw (e); + } + return result; + } + + @Override + public long executeLong(VirtualFrame frame) throws UnexpectedResultException { + return SLTypesGen.SLTYPES.expectLong(executeGeneric(frame)); + } + + @Override + public BigInteger executeBigInteger(VirtualFrame frame) throws UnexpectedResultException { + return SLTypesGen.SLTYPES.expectBigInteger(executeGeneric(frame)); + } + + @Override + public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException { + return SLTypesGen.SLTYPES.expectBoolean(executeGeneric(frame)); + } + + @Override + public String executeString(VirtualFrame frame) throws UnexpectedResultException { + return SLTypesGen.SLTYPES.expectString(executeGeneric(frame)); + } + + @Override + public SLFunction executeFunction(VirtualFrame frame) throws UnexpectedResultException { + probeNode.enter(child, frame); + SLFunction result; + + try { + result = child.executeFunction(frame); + probeNode.returnValue(child, frame, result); + } catch (Exception e) { + probeNode.returnExceptional(child, frame, e); + throw (e); + } + return result; + } + + @Override + public SLNull executeNull(VirtualFrame frame) throws UnexpectedResultException { + return SLTypesGen.SLTYPES.expectSLNull(executeGeneric(frame)); + } + + @Override + public Probe probe() { + throw new IllegalStateException("Cannot call probe() on a wrapper."); + } + + @Override + public void probeLite(TruffleEventReceiver eventReceiver) { + throw new IllegalStateException("Cannot call probeLite() on a wrapper."); + } +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLInstrumenter.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLInstrumenter.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.sl.nodes.instrument; - -import static com.oracle.truffle.api.instrument.StandardSyntaxTag.*; - -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.sl.nodes.*; -import com.oracle.truffle.sl.nodes.controlflow.*; -import com.oracle.truffle.sl.nodes.local.*; - -/** - * A visitor which traverses a completely parsed Simple AST (presumed not yet executed) and - * instruments some of them. - */ -public class SLInstrumenter implements NodeVisitor { - - public SLInstrumenter() { - } - - /** - * Instruments and tags all relevant {@link SLStatementNode}s and {@link SLExpressionNode}s. - * Currently, only SLStatementNodes that are not SLExpressionNodes are tagged as statements. - */ - public boolean visit(Node node) { - // We have to distinguish between SLExpressionNode and SLStatementNode since some of the - // generated factories have methods that require SLExpressionNodes as parameters. Since - // SLExpressionNodes are a subclass of SLStatementNode, we check if something is an - // SLExpressionNode first. - if (node instanceof SLExpressionNode && node.getParent() != null) { - SLExpressionNode expressionNode = (SLExpressionNode) node; - if (expressionNode.getSourceSection() != null) { - Probe probe = expressionNode.probe(); - // probe.tagAs(STATEMENT); - - if (node instanceof SLWriteLocalVariableNode) { - probe.tagAs(ASSIGNMENT); - } - } - } else if (node instanceof SLStatementNode && node.getParent() != null) { - - SLStatementNode statementNode = (SLStatementNode) node; - if (statementNode.getSourceSection() != null) { - Probe probe = statementNode.probe(); - probe.tagAs(STATEMENT); - - if (node instanceof SLWhileNode) { - probe.tagAs(START_LOOP); - } - } - } - - return true; - } -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.sl.nodes.instrument; + +import static com.oracle.truffle.api.instrument.StandardSyntaxTag.*; + +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.sl.nodes.*; +import com.oracle.truffle.sl.nodes.controlflow.*; +import com.oracle.truffle.sl.nodes.local.*; + +/** + * A visitor which traverses a completely parsed Simple AST (presumed not yet executed) and enables + * instrumentation at a few standard kinds of nodes. + */ +public class SLStandardASTProber implements NodeVisitor, ASTProber { + + /** + * {@inheritDoc} + *

    + * Instruments and tags all relevant {@link SLStatementNode}s and {@link SLExpressionNode}s. + * Currently, only SLStatementNodes that are not SLExpressionNodes are tagged as statements. + */ + public boolean visit(Node node) { + + if (node instanceof SLStatementNode) { + + // We have to distinguish between SLExpressionNode and SLStatementNode since some of the + // generated factories have methods that require SLExpressionNodes as parameters. Since + // SLExpressionNodes are a subclass of SLStatementNode, we check if something is an + // SLExpressionNode first. + if (node instanceof SLExpressionNode && node.getParent() != null) { + SLExpressionNode expressionNode = (SLExpressionNode) node; + if (expressionNode.getSourceSection() != null) { + Probe probe = expressionNode.probe(); + + if (node instanceof SLWriteLocalVariableNode) { + probe.tagAs(STATEMENT, null); + probe.tagAs(ASSIGNMENT, null); + } + } + } else if (node instanceof SLStatementNode && node.getParent() != null) { + + SLStatementNode statementNode = (SLStatementNode) node; + if (statementNode.getSourceSection() != null) { + Probe probe = statementNode.probe(); + probe.tagAs(STATEMENT, null); + + if (node instanceof SLWhileNode) { + probe.tagAs(START_LOOP, null); + } + } + } + } + return true; + } + + public void probeAST(Node node) { + node.accept(this); + } +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapper.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapper.java Fri Nov 21 13:16:02 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.truffle.sl.nodes.instrument; - -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.sl.nodes.*; -import com.oracle.truffle.sl.runtime.*; - -/** - * A Truffle node that can be inserted into a Simple AST (assumed not to have executed yet) to - * enable "instrumentation" of a {@link SLStatementNode}. Tools wishing to interact with AST - * execution may attach {@link Instrument}s to the {@link Probe} uniquely associated with the - * wrapper, and to which this wrapper routes {@link ExecutionEvents}. - */ -public final class SLStatementWrapper extends SLStatementNode implements Wrapper { - - @Child private SLStatementNode child; - - private final Probe probe; - - public SLStatementWrapper(SLContext context, SLStatementNode child) { - super(child.getSourceSection()); - assert !(child instanceof SLStatementWrapper); - this.probe = context.createProbe(child.getSourceSection()); - this.child = child; - } - - @Override - public SLStatementNode getNonWrapperNode() { - return child; - } - - public Node getChild() { - return child; - } - - public Probe getProbe() { - return probe; - } - - @TruffleBoundary - public void tagAs(SyntaxTag tag) { - probe.tagAs(tag); - } - - @Override - public void executeVoid(VirtualFrame frame) { - this.tagAs(StandardSyntaxTag.STATEMENT); - probe.enter(child, frame); - - try { - child.executeVoid(frame); - probe.leave(child, frame); - } catch (KillException e) { - throw (e); - } catch (Exception e) { - probe.leaveExceptional(child, frame, e); - throw (e); - } - } - -} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java Sun Nov 23 16:07:23 2014 -0800 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.sl.nodes.instrument; + +import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.sl.nodes.*; + +/** + * A Truffle node that can be inserted into a Simple AST (assumed not to have executed yet) to + * enable "instrumentation" of a {@link SLStatementNode}. Tools wishing to interact with AST + * execution may attach {@link Instrument}s to the {@link Probe} uniquely associated with the + * wrapper, and to which this wrapper routes execution events. + */ +@NodeInfo(cost = NodeCost.NONE) +public final class SLStatementWrapperNode extends SLStatementNode implements WrapperNode { + + @Child private SLStatementNode child; + @Child private ProbeNode probeNode; + + public SLStatementWrapperNode(SLStatementNode child) { + super(child.getSourceSection()); + assert !(child instanceof SLStatementWrapperNode); + this.child = child; + } + + public String instrumentationInfo() { + return "Wrapper node for SL Statements"; + } + + @Override + public SLStatementNode getNonWrapperNode() { + return child; + } + + public void insertProbe(ProbeNode newProbeNode) { + this.probeNode = newProbeNode; + } + + public Probe getProbe() { + try { + return probeNode.getProbe(); + } catch (IllegalStateException e) { + throw new IllegalStateException("A lite-Probed wrapper has no explicit Probe"); + } + } + + @Override + public Node getChild() { + return child; + } + + @Override + public void executeVoid(VirtualFrame frame) { + probeNode.enter(child, frame); + + try { + child.executeVoid(frame); + probeNode.returnVoid(child, frame); + } catch (KillException e) { + throw (e); + } catch (Exception e) { + probeNode.returnExceptional(child, frame, e); + throw (e); + } + } + + @Override + public Probe probe() { + throw new IllegalStateException("Cannot call probe() on a wrapper."); + } + + @Override + public void probeLite(TruffleEventReceiver eventReceiver) { + throw new IllegalStateException("Cannot call probeLite() on a wrapper."); + } +} diff -r e97e1f07a3d6 -r e3c95cbbb50c graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Fri Nov 21 13:16:02 2014 +0100 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java Sun Nov 23 16:07:23 2014 -0800 @@ -23,19 +23,16 @@ package com.oracle.truffle.sl.runtime; import java.io.*; -import java.util.*; import com.oracle.truffle.api.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.object.*; import com.oracle.truffle.api.source.*; import com.oracle.truffle.sl.*; import com.oracle.truffle.sl.builtins.*; import com.oracle.truffle.sl.nodes.*; -import com.oracle.truffle.sl.nodes.instrument.*; import com.oracle.truffle.sl.nodes.local.*; import com.oracle.truffle.sl.parser.*; @@ -56,7 +53,6 @@ private final PrintStream output; private final SLFunctionRegistry functionRegistry; private final Shape emptyShape; - private SourceCallback sourceCallback = null; public SLContext(BufferedReader input, PrintStream output) { this.input = input; @@ -72,11 +68,6 @@ return "Simple"; } - @Override - public void setSourceCallback(SourceCallback sourceCallback) { - this.sourceCallback = sourceCallback; - } - /** * Returns the default input, i.e., the source for the {@link SLReadlnBuiltin}. To allow unit * testing, we do not use {@link System#in} directly. @@ -100,10 +91,6 @@ return functionRegistry; } - public SourceCallback getSourceCallback() { - return sourceCallback; - } - /** * Adds all builtin functions to the {@link SLFunctionRegistry}. This method lists all * {@link SLBuiltinNode builtin implementation classes}. @@ -158,28 +145,7 @@ * @param source The {@link Source} to execute. */ public void executeMain(Source source) { - - if (sourceCallback != null) { - sourceCallback.startLoading(source); - } - Parser.parseSL(this, source); - - List functionList = getFunctionRegistry().getFunctions(); - - // Since only functions can be global in SL, this guarantees that we instrument - // everything of interest. Parsing must occur before accepting the visitors since - // the visitor which creates our instrumentation points expects a complete AST. - - for (SLFunction function : functionList) { - RootCallTarget rootCallTarget = function.getCallTarget(); - rootCallTarget.getRootNode().accept(new SLInstrumenter()); - } - - if (sourceCallback != null) { - sourceCallback.endLoading(source); - } - SLFunction main = getFunctionRegistry().lookup("main"); if (main.getCallTarget() == null) { throw new SLException("No function main() defined in SL source file.");