changeset 22220:20380d1d41f2

Truffle/Instrumentation: second merge of instrumentation code into the TruffleVM framework - deprecate Node.isInstrumentable() and Node.createWrapperNode(), now Instrumenter methods - Instrumenter uses new Accessor methods to delegate implementation to TruffleLanguage implementations - rework many instrumentation-related tests
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Thu, 17 Sep 2015 21:23:57 -0700
parents 1c0f490984d5
children c92d6117696f
files truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestingLanguage.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/ToolTestingLanguage.java truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java
diffstat 25 files changed, 1016 insertions(+), 353 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java	Thu Sep 17 21:23:57 2015 -0700
@@ -31,6 +31,7 @@
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.Node;
@@ -69,6 +70,16 @@
     }
 
     @Override
+    protected boolean isInstrumentable(Node node) {
+        return false;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
+        return null;
+    }
+
+    @Override
     protected ASTProber getDefaultASTProber() {
         return null;
     }
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java	Thu Sep 17 21:23:57 2015 -0700
@@ -32,6 +32,7 @@
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.Node;
@@ -87,6 +88,16 @@
         }
 
         @Override
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
         protected ASTProber getDefaultASTProber() {
             return null;
         }
@@ -159,6 +170,16 @@
         }
 
         @Override
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
         protected ASTProber getDefaultASTProber() {
             return null;
         }
@@ -227,6 +248,16 @@
         }
 
         @Override
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
         protected ASTProber getDefaultASTProber() {
             return null;
         }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java	Thu Sep 17 21:23:57 2015 -0700
@@ -31,6 +31,7 @@
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.Node;
@@ -72,6 +73,16 @@
         return null;
     }
 
+    @Override
+    protected boolean isInstrumentable(Node node) {
+        return false;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
+        return null;
+    }
+
     @SuppressWarnings("deprecation")
     @Override
     protected void enableASTProbing(ASTProber astProber) {
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java	Thu Sep 17 21:23:57 2015 -0700
@@ -22,23 +22,27 @@
  */
 package com.oracle.truffle.api.test.instrument;
 
+import static com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.ADD_TAG;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
 
 import org.junit.Test;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleRuntime;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
 import com.oracle.truffle.api.instrument.Instrument;
 import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ProbeListener;
+import com.oracle.truffle.api.instrument.SyntaxTag;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
+import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdvancedInstrumentCounterRoot;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
+import com.oracle.truffle.api.vm.TruffleVM;
 
 /**
  * Tests the kind of instrumentation where a client can provide an AST fragment to be
@@ -47,24 +51,36 @@
 public class AdvancedInstrumentTest {
 
     @Test
-    public void testAdvancedInstrumentListener() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
-        final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter();
+    public void testAdvancedInstrumentListener() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+
+        final TruffleVM vm = TruffleVM.newVM().build();
+        final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        final Source source = Source.fromText("testAdvancedInstrumentListener text", "testAdvancedInstrumentListener").withMimeType("text/x-instTest");
+
+        final Probe[] addNodeProbe = new Probe[1];
+        instrumenter.addProbeListener(new ProbeListener() {
+
+            public void startASTProbing(Source s) {
+            }
 
-        // 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, instrumenter);
-        final CallTarget callTarget1 = runtime.createCallTarget(rootNode);
+            public void newProbeInserted(Probe p) {
+            }
 
-        // Ensure it executes correctly
-        assertEquals(13, callTarget1.call());
+            public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
+                if (tag == ADD_TAG) {
+                    assertNull("only one add node", addNodeProbe[0]);
+                    addNodeProbe[0] = probe;
+                }
+            }
 
-        // Probe the addition node
-        final Probe probe = instrumenter.probe(addNode);
+            public void endASTProbing(Source s) {
+            }
 
-        assertEquals(13, callTarget1.call());
+        });
+        assertEquals(vm.eval(source).get(), 13);
+        assertNotNull("Add node should be probed", addNodeProbe[0]);
 
         // Attach a null factory; it never actually attaches a node.
         final Instrument instrument = Instrument.create(null, new AdvancedInstrumentRootFactory() {
@@ -73,23 +89,21 @@
                 return null;
             }
         }, null, "test AdvancedInstrument");
-        probe.attach(instrument);
+        addNodeProbe[0].attach(instrument);
 
-        assertEquals(13, callTarget1.call());
+        assertEquals(vm.eval(source).get(), 13);
 
         final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot();
 
         // Attach a factory that splices an execution counter into the AST.
-        probe.attach(Instrument.create(null, new AdvancedInstrumentRootFactory() {
+        addNodeProbe[0].attach(Instrument.create(null, new AdvancedInstrumentRootFactory() {
 
             public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) {
                 return counter;
             }
         }, null, "test AdvancedInstrument"));
         assertEquals(0, counter.getCount());
-
-        assertEquals(13, callTarget1.call());
-
+        assertEquals(vm.eval(source).get(), 13);
         assertEquals(1, counter.getCount());
     }
 }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Thu Sep 17 21:23:57 2015 -0700
@@ -22,16 +22,23 @@
  */
 package com.oracle.truffle.api.test.instrument;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleRuntime;
+import static com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.ADD_TAG;
+import static com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.VALUE_TAG;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.Instrument;
 import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeException;
-import com.oracle.truffle.api.instrument.ProbeFailure.Reason;
+import com.oracle.truffle.api.instrument.ProbeListener;
 import com.oracle.truffle.api.instrument.ProbeNode;
 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.SimpleInstrumentListener;
@@ -42,21 +49,11 @@
 import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeVisitor;
-import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode;
 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
-
-import java.util.Iterator;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import org.junit.Test;
+import com.oracle.truffle.api.vm.TruffleVM;
 
 /**
  * <h3>AST Instrumentation</h3>
@@ -78,133 +75,83 @@
  */
 public class InstrumentationTest {
 
-    private static final SyntaxTag ADD_TAG = new SyntaxTag() {
-
-        @Override
-        public String name() {
-            return "Addition";
-        }
-
-        @Override
-        public String getDescription() {
-            return "Test Language Addition Node";
-        }
-    };
-
-    private static final SyntaxTag VALUE_TAG = new SyntaxTag() {
-
-        @Override
-        public String name() {
-            return "Value";
-        }
+    @Test
+    public void testProbing() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+        final TruffleVM vm = TruffleVM.newVM().build();
+        final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        final Source source = Source.fromText("testProbing text", "testProbing").withMimeType("text/x-instTest");
 
-        @Override
-        public String getDescription() {
-            return "Test Language Value Node";
-        }
-    };
-
-    @Test
-    public void testInstrumentationStructure() throws IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchFieldException {
+        final Probe[] probes = new Probe[3];
+        instrumenter.addProbeListener(new ProbeListener() {
 
-        final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter();
-        // 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);
+            public void startASTProbing(Source s) {
+            }
 
-        try {
-            instrumenter.probe(addNode);
-        } catch (ProbeException e) {
-            assertEquals(e.getFailure().getReason(), Reason.NO_PARENT);
-        }
-        final TestRootNode rootNode = new TestRootNode(addNode, instrumenter);
-
-        // 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);
+            public void newProbeInserted(Probe probe) {
+            }
 
-        // Check the tree structure
-        assertEquals(addNode, leftValueNode.getParent());
-        assertEquals(addNode, rightValueNode.getParent());
-        Iterator<Node> iterator = addNode.getChildren().iterator();
-        assertEquals(leftValueNode, iterator.next());
-        assertEquals(rightValueNode, iterator.next());
-        assertFalse(iterator.hasNext());
-        assertEquals(rootNode, addNode.getParent());
-        iterator = rootNode.getChildren().iterator();
-        assertEquals(addNode, iterator.next());
-        assertFalse(iterator.hasNext());
-
-        // Ensure it executes correctly
-        assertEquals(13, callTarget1.call());
-
-        // Probe the addition node
-        instrumenter.probe(addNode);
-
-        // Check the modified tree structure
-        assertEquals(addNode, leftValueNode.getParent());
-        assertEquals(addNode, rightValueNode.getParent());
-        iterator = addNode.getChildren().iterator();
-        assertEquals(leftValueNode, iterator.next());
-        assertEquals(rightValueNode, iterator.next());
-        assertFalse(iterator.hasNext());
+            public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
+                if (tag == ADD_TAG) {
+                    assertEquals(probes[0], null);
+                    probes[0] = probe;
+                } else if (tag == VALUE_TAG) {
+                    if (probes[1] == null) {
+                        probes[1] = probe;
+                    } else if (probes[2] == null) {
+                        probes[2] = probe;
+                    } else {
+                        fail("Should only be three probes");
+                    }
+                }
+            }
 
-        // Ensure there's a WrapperNode correctly inserted into the AST
-        iterator = rootNode.getChildren().iterator();
-        Node wrapperNode = iterator.next();
-        assertTrue(wrapperNode instanceof TestLanguageWrapperNode);
-        assertFalse(iterator.hasNext());
-        assertEquals(rootNode, wrapperNode.getParent());
+            public void endASTProbing(Source s) {
+            }
 
-        // Check that the WrapperNode has both the probe and the wrapped node as children
-        iterator = wrapperNode.getChildren().iterator();
-        assertEquals(addNode, iterator.next());
-        ProbeNode probeNode = (ProbeNode) iterator.next();
-        assertTrue(probeNode.getProbe() != null);
-        assertFalse(iterator.hasNext());
+        });
+        assertEquals(vm.eval(source).get(), 13);
+        assertNotNull("Add node should be probed", probes[0]);
+        assertNotNull("Value nodes should be probed", probes[1]);
+        assertNotNull("Value nodes should be probed", probes[2]);
+        // Check instrumentation with the simplest kind of counters.
+        // They should all be removed when the check is finished.
+        checkCounters(probes[0], vm, source, new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter());
 
-        // Check that you can't probe the WrapperNodes
-        TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode;
-        try {
-            instrumenter.probe(wrapper);
-            fail();
-        } catch (ProbeException e) {
-            assertEquals(e.getFailure().getReason(), Reason.WRAPPER_NODE);
-        }
+        // Now try with the more complex flavor of listener
+        checkCounters(probes[0], vm, source, new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter());
 
-        // Check that the "probed" AST still executes correctly
-        assertEquals(13, callTarget1.call());
     }
 
     @Test
-    public void testListeners() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
-
-        final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter();
+    public void testTagging() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
 
-        // 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, instrumenter);
+        final TruffleVM vm = TruffleVM.newVM().build();
+        final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        final Source source = Source.fromText("testTagging text", "testTagging").withMimeType("text/x-instTest");
+
+        // Applies appropriate tags
+        final TestASTProber astProber = new TestASTProber(instrumenter);
+        instrumenter.registerASTProber(astProber);
 
-        // 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);
-        // Probe the addition node
-        final Probe probe = instrumenter.probe(addNode);
+        // Listens for probes and tags being added
+        final TestProbeListener probeListener = new TestProbeListener();
+        instrumenter.addProbeListener(probeListener);
+
+        assertEquals(13, vm.eval(source).get());
 
-        // Check instrumentation with the simplest kind of counters.
-        // They should all be removed when the check is finished.
-        checkCounters(probe, callTarget, rootNode, new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter());
+        // Check that the prober added probes to the tree
+        assertEquals(probeListener.probeCount, 3);
+        assertEquals(probeListener.tagCount, 3);
 
-        // Now try with the more complex flavor of listener
-        checkCounters(probe, callTarget, rootNode, new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter());
+        assertEquals(instrumenter.findProbesTaggedAs(InstrumentationTestingLanguage.ADD_TAG).size(), 1);
+        assertEquals(instrumenter.findProbesTaggedAs(VALUE_TAG).size(), 2);
     }
 
-    private static void checkCounters(Probe probe, CallTarget callTarget, RootNode rootNode, TestCounter counterA, TestCounter counterB, TestCounter counterC) {
+    private static void checkCounters(Probe probe, TruffleVM vm, Source source, TestCounter counterA, TestCounter counterB, TestCounter counterC) throws IOException {
 
         // Attach a counting instrument to the probe
         counterA.attach(probe);
@@ -213,7 +160,7 @@
         counterB.attach(probe);
 
         // Run it again and check that the two instruments are working
-        assertEquals(13, callTarget.call());
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
         assertEquals(counterB.enterCount(), 1);
@@ -223,143 +170,47 @@
         counterA.dispose();
 
         // Run it again and check that instrument B is still working but not A
-        assertEquals(13, callTarget.call());
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
         assertEquals(counterB.enterCount(), 2);
         assertEquals(counterB.leaveCount(), 2);
 
-        // Simulate a split by cloning the AST
-        final CallTarget callTarget2 = Truffle.getRuntime().createCallTarget((TestRootNode) rootNode.copy());
-        // Run the clone and check that instrument B is still working but not A
-        assertEquals(13, callTarget2.call());
+        // Attach a second instrument to the probe
+        counterC.attach(probe);
+
+        // Run the original and check that instruments B,C working but not A
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
         assertEquals(counterB.enterCount(), 3);
         assertEquals(counterB.leaveCount(), 3);
-
-        // Run the original and check that instrument B is still working but not A
-        assertEquals(13, callTarget2.call());
-        assertEquals(counterA.enterCount(), 1);
-        assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 4);
-        assertEquals(counterB.leaveCount(), 4);
-
-        // Attach a second instrument to the probe
-        counterC.attach(probe);
-
-        // Run the original and check that instruments B,C working but not A
-        assertEquals(13, callTarget.call());
-        assertEquals(counterA.enterCount(), 1);
-        assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 5);
-        assertEquals(counterB.leaveCount(), 5);
         assertEquals(counterC.enterCount(), 1);
         assertEquals(counterC.leaveCount(), 1);
 
-        // Run the clone and check that instruments B,C working but not A
-        assertEquals(13, callTarget2.call());
-        assertEquals(counterA.enterCount(), 1);
-        assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 6);
-        assertEquals(counterB.leaveCount(), 6);
-        assertEquals(counterC.enterCount(), 2);
-        assertEquals(counterC.leaveCount(), 2);
-
         // Remove instrumentC
         counterC.dispose();
 
         // Run the original and check that instrument B working but not A,C
-        assertEquals(13, callTarget.call());
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 7);
-        assertEquals(counterB.leaveCount(), 7);
-        assertEquals(counterC.enterCount(), 2);
-        assertEquals(counterC.leaveCount(), 2);
-
-        // Run the clone and check that instrument B working but not A,C
-        assertEquals(13, callTarget2.call());
-        assertEquals(counterA.enterCount(), 1);
-        assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 8);
-        assertEquals(counterB.leaveCount(), 8);
-        assertEquals(counterC.enterCount(), 2);
-        assertEquals(counterC.leaveCount(), 2);
+        assertEquals(counterB.enterCount(), 4);
+        assertEquals(counterB.leaveCount(), 4);
+        assertEquals(counterC.enterCount(), 1);
+        assertEquals(counterC.leaveCount(), 1);
 
         // Remove instrumentB
         counterB.dispose();
 
-        // Run both the original and clone, check that no instruments working
-        assertEquals(13, callTarget.call());
-        assertEquals(13, callTarget2.call());
+        // Check that no instruments working
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 8);
-        assertEquals(counterB.leaveCount(), 8);
-        assertEquals(counterC.enterCount(), 2);
-        assertEquals(counterC.leaveCount(), 2);
-    }
-
-    @Test
-    public void testTagging() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
-        final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter();
-
-        // Applies appropriate tags
-        final TestASTProber astProber = new TestASTProber(instrumenter);
-        instrumenter.registerASTProber(astProber);
-
-        // Listens for probes and tags being added
-        final TestProbeListener probeListener = new TestProbeListener();
-        instrumenter.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, instrumenter);
-
-        final CallTarget callTarget = runtime.createCallTarget(rootNode);
-
-        // Check that the prober added probes to the tree
-        assertEquals(probeListener.probeCount, 3);
-        assertEquals(probeListener.tagCount, 3);
-
-        assertEquals(instrumenter.findProbesTaggedAs(ADD_TAG).size(), 1);
-        assertEquals(instrumenter.findProbesTaggedAs(VALUE_TAG).size(), 2);
-
-        // Check that it executes correctly
-        assertEquals(13, callTarget.call());
-
-        // Dynamically attach a counter for all executions of all Addition nodes
-        for (Probe probe : instrumenter.findProbesTaggedAs(ADD_TAG)) {
-            additionCounter.attachCounter(probe);
-        }
-        // Dynamically attach a counter for all executions of all Value nodes
-        for (Probe probe : instrumenter.findProbesTaggedAs(VALUE_TAG)) {
-            valueCounter.attachCounter(probe);
-        }
-
-        // Counters initialized at 0
-        assertEquals(additionCounter.count, 0);
-        assertEquals(valueCounter.count, 0);
-
-        // Execute again
-        assertEquals(13, callTarget.call());
-
-        // There are two value nodes in the AST, but only one addition node
-        assertEquals(additionCounter.count, 1);
-        assertEquals(valueCounter.count, 2);
-
-        instrumenter.unregisterASTProber(astProber);
+        assertEquals(counterB.enterCount(), 4);
+        assertEquals(counterB.leaveCount(), 4);
+        assertEquals(counterC.enterCount(), 1);
+        assertEquals(counterC.leaveCount(), 1);
     }
 
     private interface TestCounter {
@@ -537,30 +388,6 @@
         }
     }
 
-    /**
-     * 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 count = 0;
-
-        public void attachCounter(Probe probe) {
-
-            // 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 DefaultSimpleInstrumentListener() {
-
-                @Override
-                public void enter(Probe p) {
-                    count++;
-                }
-            }, "Instrumentation Test MultiCounter"));
-        }
-    }
-
     private static final class TestProbeListener extends DefaultProbeListener {
 
         public int probeCount = 0;
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Thu Sep 17 21:23:57 2015 -0700
@@ -38,7 +38,6 @@
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.test.TestingLanguage;
 import com.oracle.truffle.api.vm.TruffleVM;
 
 /**
@@ -57,14 +56,16 @@
     abstract static class TestLanguageNode extends Node {
         public abstract Object execute(VirtualFrame vFrame);
 
+        @SuppressWarnings("deprecation")
         @Override
         public boolean isInstrumentable() {
             return true;
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public WrapperNode createWrapperNode() {
-            return new TestLanguageWrapperNode(this);
+            throw new UnsupportedOperationException();
         }
     }
 
@@ -159,6 +160,40 @@
      * 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.
      */
+    static class InstrumentationTestRootNode 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 InstrumentationTestRootNode(TestLanguageNode body) {
+            super(InstrumentationTestingLanguage.class, null, null);
+            this.body = body;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return body.execute(vFrame);
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return true;
+        }
+
+        @Override
+        public void applyInstrumentation() {
+            super.applyInstrumentation(body);
+        }
+    }
+
+    /**
+     * 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.
+     */
     static class TestRootNode extends RootNode {
         @Child private TestLanguageNode body;
 
@@ -170,7 +205,7 @@
          * tests run in the same environment.
          */
         public TestRootNode(TestLanguageNode body, Instrumenter instrumenter) {
-            super(TestingLanguage.class, null, null);
+            super(InstrumentationTestingLanguage.class, null, null);
             this.instrumenter = instrumenter;
             this.body = body;
         }
@@ -216,4 +251,5 @@
             return null;
         }
     }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestingLanguage.java	Thu Sep 17 21:23:57 2015 -0700
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2014, 2015, 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.api.test.instrument;
+
+import java.io.IOException;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.TruffleRuntime;
+import com.oracle.truffle.api.debug.DebugSupportProvider;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.SyntaxTag;
+import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeVisitor;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.InstrumentationTestRootNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
+
+@TruffleLanguage.Registration(name = "instrumentationTestLanguage", version = "0", mimeType = "text/x-instTest")
+public final class InstrumentationTestingLanguage extends TruffleLanguage<Object> {
+
+    public static final InstrumentationTestingLanguage INSTANCE = new InstrumentationTestingLanguage();
+
+    public static final SyntaxTag ADD_TAG = new SyntaxTag() {
+
+        @Override
+        public String name() {
+            return "Addition";
+        }
+
+        @Override
+        public String getDescription() {
+            return "Test Language Addition Node";
+        }
+    };
+
+    public static final SyntaxTag VALUE_TAG = new SyntaxTag() {
+
+        @Override
+        public String name() {
+            return "Value";
+        }
+
+        @Override
+        public String getDescription() {
+            return "Test Language Value Node";
+        }
+    };
+
+    private final ASTProber prober = new TestASTProber();
+
+    private InstrumentationTestingLanguage() {
+    }
+
+    @Override
+    protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException {
+        final TestValueNode leftValueNode = new TestValueNode(6);
+        final TestValueNode rightValueNode = new TestValueNode(7);
+        final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
+        final InstrumentationTestRootNode rootNode = new InstrumentationTestRootNode(addNode);
+        final TruffleRuntime runtime = Truffle.getRuntime();
+        final CallTarget callTarget = runtime.createCallTarget(rootNode);
+        return callTarget;
+    }
+
+    @Override
+    protected Object findExportedSymbol(Object context, String globalName, boolean onlyExplicit) {
+        return null;
+    }
+
+    @Override
+    protected Object getLanguageGlobal(Object context) {
+        return null;
+    }
+
+    @Override
+    protected boolean isObjectOfLanguage(Object object) {
+        return false;
+    }
+
+    @Override
+    protected Visualizer getVisualizer() {
+        return null;
+    }
+
+    @Override
+    protected ASTProber getDefaultASTProber() {
+        return prober;
+    }
+
+    @Override
+    protected boolean isInstrumentable(Node node) {
+        return node instanceof TestAdditionNode || node instanceof TestValueNode;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
+        if (isInstrumentable(node)) {
+            return new TestLanguageWrapperNode((TestLanguageNode) node);
+        }
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void enableASTProbing(ASTProber astProber) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+        return null;
+    }
+
+    @Override
+    protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected ToolSupportProvider getToolSupport() {
+        throw new UnsupportedOperationException();
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected DebugSupportProvider getDebugSupport() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Object createContext(Env env) {
+        return null;
+    }
+
+    static final class TestASTProber implements ASTProber {
+
+        public void probeAST(final Instrumenter instrumenter, Node startNode) {
+            startNode.accept(new NodeVisitor() {
+
+                @Override
+                public boolean visit(Node node) {
+                    if (node instanceof TestLanguageNode) {
+
+                        final TestLanguageNode testNode = (TestLanguageNode) node;
+
+                        if (node instanceof TestValueNode) {
+                            instrumenter.probe(testNode).tagAs(VALUE_TAG, null);
+
+                        } else if (node instanceof TestAdditionNode) {
+                            instrumenter.probe(testNode).tagAs(ADD_TAG, null);
+
+                        }
+                    }
+                    return true;
+                }
+            });
+        }
+    }
+
+}
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Thu Sep 17 21:23:57 2015 -0700
@@ -47,6 +47,7 @@
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.Node;
@@ -185,6 +186,16 @@
         }
 
         @Override
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
         protected ASTProber getDefaultASTProber() {
             return null;
         }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java	Thu Sep 17 21:23:57 2015 -0700
@@ -47,6 +47,7 @@
 import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.instrument.ProbeNode;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
@@ -79,7 +80,7 @@
             }
         }).build();
 
-        Source source = Source.fromText("any text", "any text").withMimeType("application/x-abstrlang");
+        Source source = Source.fromText("accessProbeForAbstractLanguage text", "accessProbeForAbstractLanguage").withMimeType("application/x-abstrlang");
 
         vm.eval(source);
 
@@ -131,14 +132,16 @@
             return getRootNode().getSourceSection();
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public boolean isInstrumentable() {
             return true;
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public ProbeNode.WrapperNode createWrapperNode() {
-            return new ANodeWrapper(this);
+            throw new UnsupportedOperationException();
         }
 
         Object constant() {
@@ -181,7 +184,7 @@
     }
 
     @TruffleLanguage.Registration(mimeType = "application/x-abstrlang", name = "AbstrLang", version = "0.1")
-    public static final class TestLanguage extends AbstractLanguage implements DebugSupportProvider {
+    public static final class TestLanguage extends AbstractLanguage {
         public static final TestLanguage INSTANCE = new TestLanguage();
 
         private final ASTProber prober = new ASTProber() {
@@ -254,6 +257,16 @@
         }
 
         @Override
+        protected boolean isInstrumentable(Node node) {
+            return node instanceof ANode;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return node instanceof ANode ? new ANodeWrapper((ANode) node) : null;
+        }
+
+        @Override
         protected ASTProber getDefaultASTProber() {
             return prober;
         }
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java	Thu Sep 17 21:23:57 2015 -0700
@@ -141,7 +141,7 @@
         this.handlers = handlers;
         this.initThread = Thread.currentThread();
         this.globals = new HashMap<>(globals);
-        this.instrumenter = SPI.createInstrumenter();
+        this.instrumenter = SPI.createInstrumenter(this);
         Map<String, Language> map = new HashMap<>();
         for (Map.Entry<String, LanguageCache> en : LanguageCache.languages().entrySet()) {
             map.put(en.getKey(), new Language(en.getValue()));
@@ -895,8 +895,8 @@
         }
 
         @Override
-        protected Instrumenter createInstrumenter() {
-            return super.createInstrumenter();
+        protected Instrumenter createInstrumenter(Object vm) {
+            return super.createInstrumenter(vm);
         }
 
         @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Thu Sep 17 21:23:57 2015 -0700
@@ -44,6 +44,8 @@
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
 import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.Node;
@@ -208,6 +210,29 @@
     protected abstract void enableASTProbing(ASTProber astProber);
 
     /**
+     * Returns {@code true} for a node can be "instrumented" by
+     * {@linkplain Instrumenter#probe(Node) probing}.
+     * <p>
+     * <b>Note:</b> instrumentation requires a appropriate {@link WrapperNode}
+     *
+     * @see Node#createWrapperNode()
+     */
+    protected abstract boolean isInstrumentable(Node node);
+
+    /**
+     * For nodes in this language that are {@linkplain #isInstrumentable() instrumentable}, this
+     * method returns an {@linkplain Node AST node} that:
+     * <ol>
+     * <li>implements {@link WrapperNode};</li>
+     * <li>has the node argument as it's child; and</li>
+     * <li>whose type is safe for replacement of the node in the parent.</li>
+     * </ol>
+     *
+     * @return an appropriately typed {@link WrapperNode}
+     */
+    protected abstract WrapperNode createWrapperNode(Node node);
+
+    /**
      * Gets the current specification for AST instrumentation for the language.
      */
     protected abstract ASTProber getDefaultASTProber();
@@ -426,6 +451,16 @@
         }
 
         @Override
+        protected boolean isInstrumentable(Node node, TruffleLanguage language) {
+            return language.isInstrumentable(node);
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node, TruffleLanguage language) {
+            return language.createWrapperNode(node);
+        }
+
+        @Override
         protected ASTProber getDefaultASTProber(TruffleLanguage language) {
             return language.getDefaultASTProber();
         }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Thu Sep 17 21:23:57 2015 -0700
@@ -44,6 +44,7 @@
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
 import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.Node;
@@ -104,6 +105,16 @@
             }
 
             @Override
+            protected boolean isInstrumentable(Node node) {
+                return false;
+            }
+
+            @Override
+            protected WrapperNode createWrapperNode(Node node) {
+                return null;
+            }
+
+            @Override
             protected ASTProber getDefaultASTProber() {
                 return null;
             }
@@ -210,6 +221,28 @@
         throw new UnsupportedOperationException();
     }
 
+    protected boolean isInstrumentable(Object vm, Node node) {
+        final RootNode rootNode = node.getRootNode();
+        Class<? extends TruffleLanguage> languageClazz = findLanguage(rootNode);
+        TruffleLanguage language = findLanguageImpl(vm, languageClazz);
+        return isInstrumentable(node, language);
+    }
+
+    protected boolean isInstrumentable(Node node, TruffleLanguage language) {
+        return API.isInstrumentable(node, language);
+    }
+
+    protected WrapperNode createWrapperNode(Object vm, Node node) {
+        final RootNode rootNode = node.getRootNode();
+        Class<? extends TruffleLanguage> languageClazz = findLanguage(rootNode);
+        TruffleLanguage language = findLanguageImpl(vm, languageClazz);
+        return createWrapperNode(node, language);
+    }
+
+    protected WrapperNode createWrapperNode(Node node, TruffleLanguage language) {
+        return API.createWrapperNode(node, language);
+    }
+
     protected ASTProber getDefaultASTProber(TruffleLanguage language) {
         return API.getDefaultASTProber(language);
     }
@@ -269,8 +302,8 @@
         return SPI.getInstrumenter(vm);
     }
 
-    protected Instrumenter createInstrumenter() {
-        return INSTRUMENT.createInstrumenter();
+    protected Instrumenter createInstrumenter(Object vm) {
+        return INSTRUMENT.createInstrumenter(vm);
     }
 
     private static Reference<Object> previousVM = new WeakReference<>(null);
@@ -304,7 +337,7 @@
         return oneVM;
     }
 
-    @SuppressWarnings({"unchecked", "rawtypes"})
+    @SuppressWarnings({"unchecked"})
     static <C> C findContext(Class<? extends TruffleLanguage> type) {
         Env env = SPI.findLanguage(CURRENT_VM.get(), type);
         return (C) API.findContext(env);
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Thu Sep 17 21:23:57 2015 -0700
@@ -37,7 +37,7 @@
 
     /**
      * Walk the AST starting at a node and enable instrumentation at selected nodes by attaching
-     * {@linkplain Probe Probes} to them. Ignore {@linkplain Node#isInstrumentable()
+     * {@linkplain Probe Probes} to them. Ignore {@linkplain Instrumenter#isInstrumentable(Node)
      * non-instrumentable} nodes.
      */
     void probeAST(Instrumenter instrumenter, Node node);
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Thu Sep 17 21:23:57 2015 -0700
@@ -605,6 +605,7 @@
             this.nextInstrumentNode = nextNode;
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public boolean isInstrumentable() {
             return false;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java	Thu Sep 17 21:23:57 2015 -0700
@@ -68,6 +68,8 @@
         return visitor.source;
     }
 
+    private final Object vm;
+
     private final Set<ASTProber> astProbers = Collections.synchronizedSet(new LinkedHashSet<ASTProber>());
 
     private final List<ProbeListener> probeListeners = new ArrayList<>();
@@ -103,7 +105,33 @@
         }
     }
 
-    Instrumenter() {
+    Instrumenter(Object vm) {
+        this.vm = vm;
+    }
+
+    /**
+     * Returns {@code true} if the node can be "instrumented" by {@linkplain #probe(Node) probing}.
+     * <p>
+     * <b>Note:</b> instrumentation requires a appropriate {@linkplain #createWrapperNode(Node)
+     * WrapperNode}.
+     */
+    public boolean isInstrumentable(Node node) {
+        return ACCESSOR.isInstrumentable(vm, node);
+    }
+
+    /**
+     * For nodes that are {@linkplain #isInstrumentable() instrumentable}, this method must return
+     * an {@linkplain Node AST node} that:
+     * <ol>
+     * <li>implements {@link WrapperNode}</li>
+     * <li>has the node argument as it's child, and</li>
+     * <li>whose type is safe for replacement of the node in the parent.</li>
+     * </ol>
+     *
+     * @return an appropriately typed {@link WrapperNode}
+     */
+    public WrapperNode createWrapperNode(Node node) {
+        return ACCESSOR.createWrapperNode(vm, node);
     }
 
     /**
@@ -142,12 +170,12 @@
             return wrapper.getProbe();
         }
 
-        if (!(node.isInstrumentable())) {
+        if (!isInstrumentable(node)) {
             throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, node, null);
         }
 
         // Create a new wrapper/probe with this node as its child.
-        final WrapperNode wrapper = node.createWrapperNode();
+        final WrapperNode wrapper = createWrapperNode(node);
 
         if (wrapper == null || !(wrapper instanceof Node)) {
             throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, node, wrapper);
@@ -327,8 +355,18 @@
     static final class AccessorInstrument extends Accessor {
 
         @Override
-        protected Instrumenter createInstrumenter() {
-            return new Instrumenter();
+        protected Instrumenter createInstrumenter(Object vm) {
+            return new Instrumenter(vm);
+        }
+
+        @Override
+        protected boolean isInstrumentable(Object vm, Node node) {
+            return super.isInstrumentable(vm, node);
+        }
+
+        @Override
+        public WrapperNode createWrapperNode(Object vm, Node node) {
+            return super.createWrapperNode(vm, node);
         }
 
         @SuppressWarnings("rawtypes")
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Thu Sep 17 21:23:57 2015 -0700
@@ -53,7 +53,8 @@
     /**
      * A node that can be inserted into a Truffle AST, and which enables {@linkplain Instrument
      * instrumentation} at a particular Guest Language (GL) node. Implementations must extend
-     * {@link Node} and should override {@link Node#isInstrumentable()} to return {@code false}.
+     * {@link Node} and should ensure that {@link TruffleLanguage#isInstrumentable(Node)} returns
+     * {@code false}.
      * <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
@@ -121,6 +122,7 @@
      */
     @Child protected AbstractInstrumentNode firstInstrumentNode;
 
+    @SuppressWarnings("deprecation")
     @Override
     public boolean isInstrumentable() {
         return false;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Thu Sep 17 21:23:57 2015 -0700
@@ -41,8 +41,6 @@
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.TruffleOptions;
 import com.oracle.truffle.api.impl.Accessor;
-import com.oracle.truffle.api.instrument.Instrument;
-import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.utilities.JSONHelper;
@@ -418,30 +416,17 @@
     }
 
     /**
-     * Any node for which this is {@code true} 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.
-     * <p>
-     * <b>Note:</b> instrumentation requires a appropriate {@link WrapperNode}, which must be
-     * provided by {@link #createWrapperNode()}.
-     *
-     * @see Instrument
+     * @see TruffleLanguage
      */
+    @Deprecated
     public boolean isInstrumentable() {
         return false;
     }
 
     /**
-     * For any node that {@link #isInstrumentable()}, this method must return a {@link Node} that:
-     * <ol>
-     * <li>implements {@link WrapperNode}</li>
-     * <li>has {@code this} as it's child, and</li>
-     * <li>whose type is safe for replacement of {@code this} in the parent.</li>
-     * </ol>
-     *
-     * @return an appropriately typed {@link WrapperNode} if {@link #isInstrumentable()}.
+     * @see TruffleLanguage
      */
+    @Deprecated
     public WrapperNode createWrapperNode() {
         return null;
     }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Thu Sep 17 21:23:57 2015 -0700
@@ -60,6 +60,7 @@
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.GraphPrintVisitor;
@@ -75,7 +76,9 @@
 import com.oracle.truffle.sl.builtins.SLNanoTimeBuiltin;
 import com.oracle.truffle.sl.builtins.SLPrintlnBuiltin;
 import com.oracle.truffle.sl.builtins.SLReadlnBuiltin;
+import com.oracle.truffle.sl.nodes.SLExpressionNode;
 import com.oracle.truffle.sl.nodes.SLRootNode;
+import com.oracle.truffle.sl.nodes.SLStatementNode;
 import com.oracle.truffle.sl.nodes.SLTypes;
 import com.oracle.truffle.sl.nodes.call.SLDispatchNode;
 import com.oracle.truffle.sl.nodes.call.SLInvokeNode;
@@ -99,7 +102,9 @@
 import com.oracle.truffle.sl.nodes.expression.SLStringLiteralNode;
 import com.oracle.truffle.sl.nodes.expression.SLSubNode;
 import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer;
+import com.oracle.truffle.sl.nodes.instrument.SLExpressionWrapperNode;
 import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber;
+import com.oracle.truffle.sl.nodes.instrument.SLStatementWrapperNode;
 import com.oracle.truffle.sl.nodes.local.SLReadLocalVariableNode;
 import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode;
 import com.oracle.truffle.sl.parser.Parser;
@@ -185,7 +190,7 @@
  */
 
 /*
- *
+ * 
  * <p> <b>Tools:</b><br> The use of some of Truffle's support for developer tools (based on the
  * Truffle Instrumentation Framework) are demonstrated in this file, for example: <ul> <li>a
  * {@linkplain NodeExecCounter counter for node executions}, tabulated by node type; and</li> <li>a
@@ -483,6 +488,22 @@
     }
 
     @Override
+    protected boolean isInstrumentable(Node node) {
+        return node instanceof SLStatementNode;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
+        if (node instanceof SLExpressionNode) {
+            return new SLExpressionWrapperNode((SLExpressionNode) node);
+        }
+        if (node instanceof SLStatementNode) {
+            return new SLStatementWrapperNode((SLStatementNode) node);
+        }
+        return null;
+    }
+
+    @Override
     protected ASTProber getDefaultASTProber() {
         return astProber;
     }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Thu Sep 17 21:23:57 2015 -0700
@@ -46,7 +46,6 @@
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.nodes.UnexpectedResultException;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.sl.nodes.instrument.SLExpressionWrapperNode;
 import com.oracle.truffle.sl.runtime.SLFunction;
 
 /**
@@ -97,12 +96,12 @@
 
     @Override
     public boolean isInstrumentable() {
-        return true;
+        throw new UnsupportedOperationException();
     }
 
     @Override
     public WrapperNode createWrapperNode() {
-        return new SLExpressionWrapperNode(this);
+        throw new UnsupportedOperationException();
     }
 
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Thu Sep 17 21:23:57 2015 -0700
@@ -40,13 +40,13 @@
  */
 package com.oracle.truffle.sl.nodes;
 
+import java.io.File;
+
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.sl.nodes.instrument.SLStatementWrapperNode;
-import java.io.File;
 
 /**
  * The base class of all Truffle nodes for SL. All nodes (even expressions) can be used as
@@ -102,14 +102,16 @@
         }
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public boolean isInstrumentable() {
         return true;
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public WrapperNode createWrapperNode() {
-        return new SLStatementWrapperNode(this);
+        throw new UnsupportedOperationException();
     }
 
 }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java	Thu Sep 17 21:23:57 2015 -0700
@@ -25,11 +25,16 @@
 package com.oracle.truffle.tools.test;
 
 import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode;
+import static com.oracle.truffle.tools.test.ToolTestingLanguage.VALUE_TAG;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Ignore;
 import org.junit.Test;
 
 import com.oracle.truffle.api.instrument.Instrumenter;
@@ -37,13 +42,21 @@
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.TruffleVM;
 import com.oracle.truffle.tools.CoverageTracker;
 
 public class CoverageTrackerTest {
 
+    @SuppressWarnings("unused") private static com.oracle.truffle.tools.test.ToolTestingLanguage DUMMY = null;
+    @SuppressWarnings("unused") private static ToolTestingLanguage INSTANCE = ToolTestingLanguage.INSTANCE;
+
     @Test
     public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
-        final Instrumenter instrumenter = TestNodes.createInstrumenter();
+        final TruffleVM vm = TruffleVM.newVM().build();
+        final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
         final CoverageTracker tool = new CoverageTracker();
         assertEquals(tool.getCounts().entrySet().size(), 0);
         tool.install(instrumenter);
@@ -59,27 +72,33 @@
     }
 
     @Test
-    public void testToolCreatedTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+    public void testToolCreatedTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+        final TruffleVM vm = TruffleVM.newVM().build();
+        final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        final CoverageTracker tool = new CoverageTracker(VALUE_TAG);
+        tool.install(instrumenter);
+        final Source source = Source.fromText("testToolCreatedTooLate text", "testToolCreatedTooLate").withMimeType("text/x-toolTest");
+        assertEquals(vm.eval(source).get(), 13);
+        assertTrue(tool.getCounts().isEmpty());
+        tool.dispose();
+    }
+
+    @Ignore
+    @Test
+    public void testToolInstalledcTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
         final Instrumenter instrumenter = TestNodes.createInstrumenter();
-        final RootNode expr13rootNode = TestNodes.createExpr13TestRootNode(instrumenter);
         final CoverageTracker tool = new CoverageTracker();
+        final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter);
+
         tool.install(instrumenter);
         assertEquals(13, expr13rootNode.execute(null));
         assertTrue(tool.getCounts().isEmpty());
         tool.dispose();
     }
 
-    @Test
-    public void testToolInstalledcTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
-        final Instrumenter instrumenter = TestNodes.createInstrumenter();
-        final CoverageTracker tool = new CoverageTracker();
-        final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter);
-        tool.install(instrumenter);
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
-        tool.dispose();
-    }
-
+    @Ignore
     @Test
     public void testCountingCoverage() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
         final Instrumenter instrumenter = TestNodes.createInstrumenter();
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java	Thu Sep 17 21:23:57 2015 -0700
@@ -106,11 +106,13 @@
             super(srcSection);
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public boolean isInstrumentable() {
             return true;
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public WrapperNode createWrapperNode() {
             return new TestWrapperNode(this);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/ToolTestingLanguage.java	Thu Sep 17 21:23:57 2015 -0700
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2014, 2015, 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.tools.test;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.TruffleRuntime;
+import com.oracle.truffle.api.debug.DebugSupportProvider;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.KillException;
+import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ProbeNode;
+import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.SyntaxTag;
+import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.api.nodes.NodeVisitor;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
+
+@TruffleLanguage.Registration(name = "toolTestLanguage", version = "0", mimeType = "text/x-toolTest")
+public final class ToolTestingLanguage extends TruffleLanguage<Object> {
+
+    public static final ToolTestingLanguage INSTANCE = new ToolTestingLanguage();
+
+    static final SyntaxTag ADD_TAG = new SyntaxTag() {
+
+        @Override
+        public String name() {
+            return "Addition";
+        }
+
+        @Override
+        public String getDescription() {
+            return "Test Language Addition Node";
+        }
+    };
+
+    static final SyntaxTag VALUE_TAG = new SyntaxTag() {
+
+        @Override
+        public String name() {
+            return "Value";
+        }
+
+        @Override
+        public String getDescription() {
+            return "Test Language Value Node";
+        }
+    };
+
+    private final ASTProber prober = new TestASTProber();
+
+    private ToolTestingLanguage() {
+    }
+
+    @Override
+    protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException {
+        final TestValueNode leftValueNode = new TestValueNode(6);
+        final TestValueNode rightValueNode = new TestValueNode(7);
+        final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
+        final InstrumentationTestRootNode rootNode = new InstrumentationTestRootNode(addNode);
+        final TruffleRuntime runtime = Truffle.getRuntime();
+        final CallTarget callTarget = runtime.createCallTarget(rootNode);
+        return callTarget;
+    }
+
+    @Override
+    protected Object findExportedSymbol(Object context, String globalName, boolean onlyExplicit) {
+        return null;
+    }
+
+    @Override
+    protected Object getLanguageGlobal(Object context) {
+        return null;
+    }
+
+    @Override
+    protected boolean isObjectOfLanguage(Object object) {
+        return false;
+    }
+
+    @Override
+    protected Visualizer getVisualizer() {
+        return null;
+    }
+
+    @Override
+    protected ASTProber getDefaultASTProber() {
+        return prober;
+    }
+
+    @Override
+    protected boolean isInstrumentable(Node node) {
+        return node instanceof TestAdditionNode || node instanceof TestValueNode;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
+        if (isInstrumentable(node)) {
+            return new TestLanguageWrapperNode((TestLanguageNode) node);
+        }
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void enableASTProbing(ASTProber astProber) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+        return null;
+    }
+
+    @Override
+    protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected ToolSupportProvider getToolSupport() {
+        throw new UnsupportedOperationException();
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected DebugSupportProvider getDebugSupport() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Object createContext(Env env) {
+        return null;
+    }
+
+    static final class TestASTProber implements ASTProber {
+
+        public void probeAST(final Instrumenter instrumenter, Node startNode) {
+            startNode.accept(new NodeVisitor() {
+
+                @Override
+                public boolean visit(Node node) {
+                    if (node instanceof TestLanguageNode) {
+
+                        final TestLanguageNode testNode = (TestLanguageNode) node;
+
+                        if (node instanceof TestValueNode) {
+                            instrumenter.probe(testNode).tagAs(VALUE_TAG, null);
+
+                        } else if (node instanceof TestAdditionNode) {
+                            instrumenter.probe(testNode).tagAs(ADD_TAG, null);
+
+                        }
+                    }
+                    return true;
+                }
+            });
+        }
+    }
+
+    abstract static class TestLanguageNode extends Node {
+        public abstract Object execute(VirtualFrame vFrame);
+
+        @SuppressWarnings("deprecation")
+        @Override
+        public boolean isInstrumentable() {
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        public WrapperNode createWrapperNode() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    @NodeInfo(cost = NodeCost.NONE)
+    static class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode {
+        @Child private TestLanguageNode child;
+        @Child private ProbeNode probeNode;
+
+        public TestLanguageWrapperNode(TestLanguageNode child) {
+            assert !(child instanceof TestLanguageWrapperNode);
+            this.child = child;
+        }
+
+        @Override
+        public String instrumentationInfo() {
+            return "Wrapper node for testing";
+        }
+
+        @Override
+        public boolean isInstrumentable() {
+            return false;
+        }
+
+        @Override
+        public void insertProbe(ProbeNode newProbeNode) {
+            this.probeNode = newProbeNode;
+        }
+
+        @Override
+        public Probe getProbe() {
+            return probeNode.getProbe();
+        }
+
+        @Override
+        public Node getChild() {
+            return child;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            probeNode.enter(child, vFrame);
+            Object result;
+            try {
+                result = child.execute(vFrame);
+                probeNode.returnValue(child, vFrame, result);
+            } catch (KillException e) {
+                throw (e);
+            } catch (Exception e) {
+                probeNode.returnExceptional(child, vFrame, e);
+                throw (e);
+            }
+            return result;
+        }
+    }
+
+    /**
+     * A simple node for our test language to store a value.
+     */
+    static class TestValueNode extends TestLanguageNode {
+        private final int value;
+
+        public TestValueNode(int value) {
+            this.value = value;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return new Integer(this.value);
+        }
+    }
+
+    /**
+     * A node for our test language that adds up two {@link TestValueNode}s.
+     */
+    static 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 vFrame) {
+            return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue());
+        }
+    }
+
+    /**
+     * 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.
+     */
+    static class InstrumentationTestRootNode 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 InstrumentationTestRootNode(TestLanguageNode body) {
+            super(ToolTestingLanguage.class, null, null);
+            this.body = body;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return body.execute(vFrame);
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return true;
+        }
+
+        @Override
+        public void applyInstrumentation() {
+            super.applyInstrumentation(body);
+        }
+    }
+
+    /**
+     * 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.
+     */
+    static class TestRootNode extends RootNode {
+        @Child private TestLanguageNode body;
+
+        final Instrumenter instrumenter;
+
+        /**
+         * 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, Instrumenter instrumenter) {
+            super(ToolTestingLanguage.class, null, null);
+            this.instrumenter = instrumenter;
+            this.body = body;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return body.execute(vFrame);
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return true;
+        }
+
+        @Override
+        public void applyInstrumentation() {
+            Method method;
+            try {
+                method = Instrumenter.class.getDeclaredMethod("applyInstrumentation", Node.class);
+                method.setAccessible(true);
+                method.invoke(instrumenter, body);
+            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                throw new RuntimeException("InstrumentationTestNodes");
+            }
+        }
+    }
+
+}
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Thu Sep 17 21:23:57 2015 -0700
@@ -64,7 +64,7 @@
  * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented
  * to produce the event {@link SimpleInstrumentListener#enter(Probe)};</li>
  * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which
- * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li>
+ * {@link Instrumenter#isInstrumentable(Node)}{@code == true};</li>
  * <li>Execution calls are tabulated only at nodes present in the AST when originally created;
  * dynamically added nodes will not be instrumented.</li>
  * </ul>
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java	Wed Sep 16 15:36:22 2015 -0700
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java	Thu Sep 17 21:23:57 2015 -0700
@@ -68,7 +68,7 @@
  * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented
  * to produce the event {@link StandardInstrumentListener#enter(Probe, Node, VirtualFrame)};</li>
  * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which
- * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li>
+ * {@linkplain Instrumenter#isInstrumentable(Node) isInstrumentable() == true};</li>
  * <li>Execution calls are tabulated only at nodes present in the AST when originally created;
  * dynamically added nodes will not be instrumented.</li>
  * </ul>
@@ -299,7 +299,7 @@
 
                 public boolean visit(Node node) {
 
-                    if (node.isInstrumentable()) {
+                    if (instrumenter.isInstrumentable(node)) {
                         try {
                             final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter");
                             instruments.add(instrument);