changeset 18485:e3c95cbbb50c

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.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Sun, 23 Nov 2014 16:07:23 -0800
parents e97e1f07a3d6
children b9a8b2885283
files graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InstrumentationTest.java graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/nodes/NodeUtilTest.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultTruffleRuntime.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTNodeProber.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ExecutionEvents.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumentable.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceCallback.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardSyntaxTag.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTag.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTagged.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/TruffleEventReceiver.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Wrapper.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultEventReceiver.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineLocationToProbeCollectionMap.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineLocationToSourceSectionCollectionMap.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineToProbesMap.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/LineToSourceSectionMap.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/ProbeManager.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SimpleEventReceiver.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/LineLocation.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTestRunner.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java graal/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLPrintAssigmentValueInstrument.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLMain.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/factory/SLContextFactory.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLASTPrinter.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapper.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLInstrumenter.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapper.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java
diffstat 51 files changed, 2693 insertions(+), 2507 deletions(-) [+]
line wrap: on
line diff
--- 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()) {
--- 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 @@
  * <h3>AST Instrumentation</h3>
  *
  * 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.
  * <ol>
  * <li>Creates a simple add AST</li>
  * <li>Verifies its structure</li>
- * <li>"Probes" the add node by adding a {@link Wrapper} and associated {@link Probe}</li>
- * <li>Attaches a simple {@link Instrument} to the node via its {@link Probe}</li>
+ * <li>"Probes" the add node by adding a {@link WrapperNode} and associated {@link Probe}</li>
+ * <li>Attaches a simple {@link Instrument} to the node via the Probe's {@link ProbeNode}</li>
  * <li>Verifies the structure of the probed AST</li>
  * <li>Verifies the execution of the probed AST</li>
  * <li>Verifies the results observed by the instrument.</li>
@@ -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<Node> 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<Node> 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) {
+        }
+
+    }
+
 }
--- 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.*;
--- 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<SourceListener> 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<SourceListener> 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<Probe> 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 <code>ExecutionContext</code>.
      */
     public CompilerOptions getCompilerOptions() {
--- 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);
     }
 
--- 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);
     }
+
 }
--- 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.
- * <p>
- * This interface is guest language agnostic, but current extensions are language-specific. This
- * will be revisited.
- * <p>
- * <strong>Disclaimer:</strong> experimental interface under development. Really!
- */
-public interface ASTNodeProber {
-
-    /**
-     * Optionally applies <em>instrumentation</em> at a Truffle AST node, depending on guest
-     * language characteristics and use-case policy.
-     * <ul>
-     * <li>if no instrumentation is to be applied, returns the AST node unmodified;</li>
-     * <li>if an AST node is to be instrumented, then returns a newly created {@link Wrapper} that
-     * <em>decorates</em> the AST node and notifies an associated {@link Probe} of all
-     * {@link ExecutionEvents} at the wrapped AST node.</li>
-     * <li>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}).</li>
-     * </ul>
-     *
-     * @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);
-}
--- 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 <em>instrumenting</em> Truffle ASTs with {@link Probe}s at
- * particular nodes by inserting node {@link Wrapper}s.
- * <p>
- * Multiple "node probers" can be added, typically by different tools; the "combined prober" will
- * apply all of them.
- * <p>
- * 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.
- * <p>
- * A better approach will be to implement such policies as a Truffle {@link NodeVisitor}, but that
- * is not possible at this time.
- * <p>
- * <strong>Disclaimer:</strong> 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();
 }
--- 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}.
- * <p>
- * <strong>Disclaimer:</strong> 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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);
-
-}
--- 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 <em>tools</em>.
+ * 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.
  * <p>
- * Guidelines for implementing {@link Instrument}s, with particular attention to minimize runtime
- * performance overhead:
+ * <h4>Summary: How to "instrument" an AST location:</h4>
  * <ol>
- * <li>Extend this abstract class and override only the {@linkplain ExecutionEvents event handling
- * methods} for which intervention is needed.</li>
- * <li>Instruments are Truffle {@link Node}s and should be coded as much as possible in the desired
- * <em>Truffle style</em>, documented more thoroughly elsewhere.</li>
- * <li>Maintain as little state as possible.</li>
- * <li>If state is necessary, make object fields {@code final} if at all possible.</li>
- * <li>If non-final object-valued state is necessary, annotate it as {@link CompilationFinal} and
- * call {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.</li>
- * <li>Never store a {@link Frame} value in a field.</li>
- * <li>Minimize computation in standard execution paths.</li>
- * <li>If runtime calls must be made back to a tool, construct the instrument with a callback stored
- * in a {@code final} field.</li>
- * <li>Tool methods called by the instrument should be annotated as {@link TruffleBoundary} to
- * prevent them from being inlined into fast execution paths.</li>
- * <li>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.</li>
+ * <li>Create an implementation of {@link TruffleEventReceiver} that responds to events on behalf of
+ * a tool.</li>
+ * <li>Create an Instrument via factory method {@link Instrument#create(TruffleEventReceiver)}.</li>
+ * <li>"Attach" the Instrument to a Probe via {@link Probe#attach(Instrument)}, at which point event
+ * notifications begin to arrive at the receiver.</li>
+ * <li>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.</li>
+ * </ol>
+ * <p>
+ * <h4>Options for creating receivers:</h4>
+ * <p>
+ * <ol>
+ * <li>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).</li>
+ * <li>Extend {@link DefaultEventReceiver}, which provides a no-op implementation of every
+ * {@link TruffleEventReceiver} method; override the methods of interest.</li>
+ * <li>Extend {@link SimpleEventReceiver}, where return values are ignored so only two methods (for
+ * "enter" and "return") will notify all events.</li>
  * </ol>
  * <p>
- * Guidelines for attachment to a {@link Probe}:
- * <ol>
- * <li>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}.</li>
- * <li>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.</li>
- * </ol>
+ * <h4>General guidelines for receiver implementation:</h4>
+ * <p>
+ * 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.
+ * <ul>
+ * <li>Do not store {@link Frame} or {@link Node} references in fields.</li>
+ * <li>Prefer {@code final} fields and (where performance is important) short methods.</li>
+ * <li>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.</li>
+ * <li>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.</li>
+ * <li>On the other hand, implementations should prevent Truffle from inlining beyond a reasonable
+ * point with the method annotation {@link TruffleBoundary}.</li>
+ * <li>The implicit "outer" pointer in a non-static inner class is a useful way to implement
+ * callbacks to owner tools.</li>
+ * <li>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).
+ * </ul>
+ * <p>
+ * <h4>Allowing for AST cloning:</h4>
+ * <p>
+ * Truffle routinely <em>clones</em> ASTs, which has consequences for receiver implementation.
+ * <ul>
+ * <li>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. <em>clones</em> of the originally probed
+ * node.</li>
+ * <li>Because of <em>cloning</em> 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.</li>
+ * </ul>
+ * <p>
+ * <h4>Access to execution state:</h4>
  * <p>
- * Guidelines for handling {@link ExecutionEvents}:
- * <ol>
- * <li>Separate event methods are defined for each kind of possible return: object-valued,
- * primitive-valued, void-valued, and exceptional.</li>
- * <li>Override "leave*" primitive methods if the language implementation returns primitives and the
- * instrument should avoid boxing them.</li>
- * <li>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.</li>
- * </ol>
- *
+ * <ul>
+ * <li>Event notification arguments provide primary access to the GL program's execution states:
+ * <ul>
+ * <li>{@link Node}: the concrete node (in one of the AST's clones) from which the event originated.
+ * </li>
+ * <li>{@link VirtualFrame}: the current execution frame.
+ * </ul>
+ * <li>Some global information is available, for example the execution
+ * {@linkplain TruffleRuntime#iterateFrames(FrameInstanceVisitor) stack}.</li>
+ * <li>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.</li>
+ * </ul>
+ * <p>
+ * <h4>Activating and deactivating Instruments:</h4>
+ * <p>
+ * Instruments are <em>single-use</em>:
+ * <ul>
+ * <li>An instrument becomes active only when <em>attached</em> 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.</li>
+ * <li>Attaching an instrument modifies every existing clone of the AST to which it is being
+ * attached, which can trigger deoptimization.</li>
+ * <li>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.</li>
+ * <li>Disposal removes the implementation of an instrument from all ASTs to which it was attached,
+ * which can trigger deoptimization.</li>
+ * </ul>
+ * <p>
+ * <h4>Sharing receivers:</h4>
+ * <p>
+ * 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.
  * <p>
  * <strong>Disclaimer:</strong> 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();
+        }
     }
 
 }
--- 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();
-}
--- /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 <em>Instrumentation</em> 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();
+}
--- 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 <em>instrumentation</em>. 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.
  * <p>
- * 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.
+ * <p>
+ * 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.
  * <p>
- * 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.
+ * <p>
+ * 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.
  * <p>
- * 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.
- * <p>
- * 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 <em>clones</em> ASTs, along with any attached instrumentation nodes. A
+ * Probe, along with attached Instruments, represents a <em>logical</em> 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()}.
+         * <p>
+         * 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)}.
+         * <p>
+         * The {@linkplain SyntaxTag tags} at a {@link Probe} are a <em>set</em>; this notification
+         * will only be delivered the first time a particular {@linkplain SyntaxTag tag} is added at
+         * a {@link Probe}.
+         * <p>
+         * 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<ASTProber> astProbers = new ArrayList<>();
+
+    private static final List<ProbeListener> probeListeners = new ArrayList<>();
+
+    /**
+     * All Probes that have been created.
+     */
+    private static final List<WeakReference<Probe>> 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<Probe> findProbesTaggedAs(SyntaxTag tag) {
+        final List<Probe> taggedProbes = new ArrayList<>();
+        for (WeakReference<Probe> 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<Probe> 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<Probe> 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<SyntaxTag> tags = new ArrayList<>();
+    private final List<WeakReference<ProbeNode>> 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<SyntaxTag> 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<ProbeNode> 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 ? "<unknown>" : 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<ProbeNode> 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();
+    }
 }
--- 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.
- * <p>
- * <strong>Disclaimer:</strong> 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);
-
-}
--- /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.
+         * <p>
+         * 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.
+         * <p>
+         * 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 <em>instrumentation</em> at
+     * a particular Guest Language (GL) node.
+     * <p>
+     * The implementation must be GL-specific. A wrapper <em>decorates</em> a GL AST node (the
+     * wrapper's <em>child</em>) by acting as a transparent <em>proxy</em> with respect to the GL's
+     * execution semantics.
+     * <p>
+     * Instrumentation at the wrapped node is implemented by an instance of {@link ProbeNode}
+     * attached as a second child of the {@link WrapperNode}.
+     * <p>
+     * A wrapper is obliged to notify its attached {@link ProbeNode} when execution events occur at
+     * the wrapped AST node during program execution.
+     * <p>
+     * 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.
+     * <p>
+     * This interface is not intended to be visible as part of the API for tools (instrumentation
+     * clients).
+     * <p>
+     * Implementation guidelines:
+     * <ol>
+     * <li>Each GL implementation should include a WrapperNode implementation; usually only one is
+     * needed.</li>
+     * <li>The wrapper type should descend from the <em>GL-specific node class</em>.</li>
+     * <li>Must have a field: {@code @Child private <GL>Node child;}</li>
+     * <li>Must have a field: {@code @Child private ProbeNode probeNode;}</li>
+     * <li>The wrapper must act as a <em>proxy</em> for its child, which means implementing every
+     * possible <em>execute-</em> method that gets called on guest language AST node types by their
+     * parents, and passing along each call to its child.</li>
+     * <li>Method {@code Probe getProbe()} should be implemented as {@code probeNode.getProbe();}
+     * <li>Method {@code insertProbe(ProbeNode)} should be implemented as
+     * {@code this.probeNode=insert(newProbeNode);}</li>
+     * <li>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.</li>
+     * </ol>
+     * <p>
+     * <strong>Disclaimer:</strong> 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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";
+        }
+
+    }
+}
--- 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) {
-        }
-
-    };
-}
--- 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.
- * <p>
- * <strong>Disclaimer:</strong> 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);
-
-}
--- 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.
  * <p>
  * <strong>Disclaimer:</strong> experimental interface under development.
- *
+ * 
  * @see Probe
- * @see Wrapper
  */
 public enum StandardSyntaxTag implements SyntaxTag {
 
--- 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 @@
  * <strong>Disclaimer:</strong> experimental interface under development.
  *
  * @see Probe
- * @see Wrapper
  * @see StandardSyntaxTag
  */
 public interface SyntaxTag {
--- 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}.
  * <p>
  * <strong>Disclaimer:</strong> 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 (<em>empty set</em> if none).
      */
-    Iterable<SyntaxTag> getSyntaxTags();
+    Collection<SyntaxTag> getSyntaxTags();
 
 }
--- /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);
+
+}
--- 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 <em>instrumentation</em> at a
- * particular node.
- * <p>
- * A wrapper <em>decorates</em> an AST node (its <em>child</em>) by acting as a transparent
- * <em>proxy</em> for the child with respect to Truffle execution semantics.
- * <p>
- * A wrapper is also expected to notify its associated {@link Probe} when {@link ExecutionEvents}
- * occur at the wrapper during program execution.
- * <p>
- * The wrapper's {@link Probe} is shared by every copy of the wrapper made when the AST is copied.
- * <p>
- * Wrapper implementation guidelines:
- * <ol>
- * <li>Every guest language implementation should include a Wrapper implementation; usually only one
- * is needed.</li>
- * <li>The Wrapper type should descend from the <em>language-specific node class</em> of the guest
- * language.</li>
- * <li>The Wrapper must have a single {@code @Child private <guestLanguage>Node child} field.</li>
- * <li>The Wrapper must act as a <em>proxy</em> for its child, which means implementing every
- * possible <em>execute-</em> method that gets called on guest language AST node types by their
- * parents, and passing along each call to its child.</li>
- * <li>The Wrapper must have a single {@code private final Probe probe} to which an optional probe
- * can be attached during node construction.</li>
- * <li>Wrapper methods must also notify its attached {@link Probe}, if any, in terms of standard
- * {@link ExecutionEvents}.</li>
- * <li>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.</li>
- * </ol>
- * <p>
- * <strong>Disclaimer:</strong> 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();
-
-}
--- 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<NodeField> 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;
+    }
+
 }
--- /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) {
+    }
+
+}
--- 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.
-     * <p>
-     * 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.
-     * <p>
-     * Each probe is associated with a {@link SourceSection}, not necessarily uniquely, although
-     * such a policy could be enforced for some uses.
-     * <p>
-     * 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<SyntaxTag> 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<SyntaxTag> 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);
-            }
-        }
-
-    }
-
-}
--- 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<LineLocation, Collection<Probe>> 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<Probe> 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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<Probe> newProbeList = new ArrayList<>(2);
-            newProbeList.add(probe);
-            lineToProbesMap.put(line, newProbeList);
-        } else {
-            // Probe list exists, add to existing
-            final Collection<Probe> 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<Probe> getProbesAtLine(LineLocation line) {
-        Collection<Probe> 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<Probe> getProbesAtLineNumber(int lineNumber) {
-
-        final Set<LineLocation> keySet = lineToProbesMap.keySet();
-        if (keySet.size() == 0) {
-            return Collections.emptyList();
-        }
-
-        ArrayList<Probe> 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<LineLocation> mappedLines = lineToProbesMap.keySet();
-        if (mappedLines.size() > 0) {
-            List<LineLocation> forgetLines = new ArrayList<>();
-            for (LineLocation line : mappedLines) {
-                if (line.getSource().equals(source)) {
-                    forgetLines.add(line);
-                }
-            }
-            for (LineLocation line : forgetLines) {
-                lineToProbesMap.remove(line);
-            }
-        }
-    }
-}
--- 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<LineLocation, Collection<SourceSection>> 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.
-     * <p>
-     * 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.
-     * <p>
-     * 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<SourceSection> newSourceSectionList = new ArrayList<>(2);
-            newSourceSectionList.add(sourceSection);
-            lineToSourceSectionsMap.put(line, newSourceSectionList);
-        } else {
-            // Source section list exists, add to existing
-            final Collection<SourceSection> 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<SourceSection> getSourceSectionsAtLine(LineLocation line) {
-        Collection<SourceSection> 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<SourceSection> getSourceSectionsAtLineNumber(int lineNumber) {
-
-        final Set<LineLocation> keySet = lineToSourceSectionsMap.keySet();
-        if (keySet.size() == 0) {
-            return Collections.emptyList();
-        }
-
-        final ArrayList<SourceSection> sourceSections = new ArrayList<>();
-        for (LineLocation line : keySet) {
-            if (line.getLineNumber() == lineNumber) {
-                sourceSections.addAll(lineToSourceSectionsMap.get(line));
-            }
-        }
-
-        return sourceSections;
-    }
-}
--- /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<LineLocation, Collection<Probe>> 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<Probe> 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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<Probe> newProbeList = new ArrayList<>(2);
+            newProbeList.add(probe);
+            lineToProbesMap.put(line, newProbeList);
+        } else {
+            // Probe list exists, add to existing
+            final Collection<Probe> 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<Probe> getProbesAtLine(LineLocation line) {
+        Collection<Probe> 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<Probe> getProbesAtLineNumber(int lineNumber) {
+
+        final Set<LineLocation> keySet = lineToProbesMap.keySet();
+        if (keySet.size() == 0) {
+            return Collections.emptyList();
+        }
+
+        ArrayList<Probe> 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<LineLocation> mappedLines = lineToProbesMap.keySet();
+        if (mappedLines.size() > 0) {
+            List<LineLocation> forgetLines = new ArrayList<>();
+            for (LineLocation line : mappedLines) {
+                if (line.getSource().equals(source)) {
+                    forgetLines.add(line);
+                }
+            }
+            for (LineLocation line : forgetLines) {
+                lineToProbesMap.remove(line);
+            }
+        }
+    }
+}
--- /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<LineLocation, Collection<SourceSection>> 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.
+     * <p>
+     * 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.
+     * <p>
+     * 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<SourceSection> newSourceSectionList = new ArrayList<>(2);
+            newSourceSectionList.add(sourceSection);
+            lineToSourceSectionsMap.put(line, newSourceSectionList);
+        } else {
+            // Source section list exists, add to existing
+            final Collection<SourceSection> 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<SourceSection> getSourceSectionsAtLine(LineLocation line) {
+        Collection<SourceSection> 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<SourceSection> getSourceSectionsAtLineNumber(int lineNumber) {
+
+        final Set<LineLocation> keySet = lineToSourceSectionsMap.keySet();
+        if (keySet.size() == 0) {
+            return Collections.emptyList();
+        }
+
+        final ArrayList<SourceSection> sourceSections = new ArrayList<>();
+        for (LineLocation line : keySet) {
+            if (line.getLineNumber() == lineNumber) {
+                sourceSections.addAll(lineToSourceSectionsMap.get(line));
+            }
+        }
+
+        return sourceSections;
+    }
+}
--- 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<ProbeListener> probeListeners = new ArrayList<>();
-
-    private final List<ProbeImpl> 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<ProbeListener> 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<Probe> findProbesTaggedAs(SyntaxTag tag) {
-        final List<Probe> 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.
-     * <p>
-     *
-     * @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;
-    }
-
-}
--- /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:
+ * <ul>
+ * <li>{@link #enter(Node, VirtualFrame)}, and</li>
+ * <li>{@link #returnAny(Node, VirtualFrame)}.</li>
+ * </ul>
+ */
+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);
+    }
+
+}
--- 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:
+     * <ul>
+     * <li>"[{@linkplain StandardSyntaxTag#STATEMENT STATEMENT},
+     * {@linkplain StandardSyntaxTag#ASSIGNMENT ASSIGNMENT}]" if tags have been applied;</li>
+     * <li>"[]" if the node supports tags, but none are present; and</li>
+     * <li>"" if the node does not support tags.</li>
+     * </ul>
+     */
+    public static String printSyntaxTags(final Object node) {
+        if (node instanceof WrapperNode) {
+            final Probe probe = ((WrapperNode) node).getProbe();
+            final Collection<SyntaxTag> 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.
      *
--- 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 <code>RootNode<code>:
-     *
+     * 
      * <pre>
      * <code>
      * 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.
+     * <p>
+     * If this is not done, then the AST will not be subject to debugging or any other
+     * instrumentation-supported tooling.
+     * <p>
+     * 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() {
+    }
 }
--- 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();
+
 }
--- 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
--- 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<? extends SLBuiltinNode> builtin : builtins) {
                 context.installBuiltin(builtin);
             }
--- 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<SLFunction> 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);
+        }
+    }
+
 }
--- 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);
-    }
-}
--- 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) {
--- 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;
     }
--- 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);
     }
 }
--- 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;
     }
--- 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);
     }
 }
--- 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<NodeField> 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();
-    }
 }
--- 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));
-    }
-
-}
--- /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.");
+    }
+}
--- 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;
-    }
-}
--- /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}
+     * <p>
+     * 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);
+    }
+}
--- 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);
-        }
-    }
-
-}
--- /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.");
+    }
+}
--- 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<SLFunction> 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.");