changeset 22232:526de3af756d

Merge with 0480c4873a4a8a71250c9570b702cea98ab1c7bc
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Mon, 21 Sep 2015 12:15:38 -0700
parents 59e022cee529 (diff) 0480c4873a4a (current diff)
children 1f19e3cada3d
files truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.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.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java
diffstat 66 files changed, 3247 insertions(+), 2235 deletions(-) [+]
line wrap: on
line diff
--- a/mx.truffle/suite.py	Mon Sep 21 13:11:41 2015 +0200
+++ b/mx.truffle/suite.py	Mon Sep 21 12:15:38 2015 -0700
@@ -193,6 +193,7 @@
           ],
       "checkstyle" : "com.oracle.truffle.api",
       "javaCompliance" : "1.7",
+      "annotationProcessors" : ["TRUFFLE_DSL_PROCESSOR"],
       "workingSets" : "Truffle,Tools",
     },
 
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import org.junit.Assert;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.ImplicitCast;
@@ -36,8 +39,6 @@
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.nodes.UnexpectedResultException;
-import org.junit.Assert;
-import org.junit.Test;
 
 public class ArrayTest {
 
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import org.junit.Assert;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.ImplicitCast;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
@@ -34,8 +37,6 @@
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import org.junit.Assert;
-import org.junit.Test;
 
 public class ImplicitCastTest {
 
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,6 +22,10 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.ImplicitCast;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -36,8 +40,6 @@
 import com.oracle.truffle.api.dsl.test.ShortCircuitTestFactory.VarArgsNodeFactory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ArgumentNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
 
 public class ShortCircuitTest {
 
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,13 +22,20 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import java.io.IOException;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.TruffleLanguage;
 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.WrapperNode;
 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.source.Source;
-import java.io.IOException;
 
 public final class TestingLanguage extends TruffleLanguage<Object> {
     public static final TestingLanguage INSTANCE = new TestingLanguage();
@@ -58,13 +65,51 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
+    protected Visualizer getVisualizer() {
+        return null;
+    }
+
+    @Override
+    protected boolean isInstrumentable(Node node) {
+        return false;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
         return null;
     }
 
     @Override
+    protected ASTProber getDefaultASTProber() {
+        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() {
-        return null;
+        throw new UnsupportedOperationException();
     }
 
     @Override
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,14 +22,21 @@
  */
 package com.oracle.truffle.api.dsl.test.processor;
 
+import java.io.IOException;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.debug.DebugSupportProvider;
 import com.oracle.truffle.api.dsl.test.ExpectError;
+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.WrapperNode;
 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.source.Source;
-import java.io.IOException;
 
 public class LanguageRegistrationTest {
 
@@ -76,13 +83,51 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
+        protected ASTProber getDefaultASTProber() {
             return null;
         }
 
         @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() {
-            return null;
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        protected void enableASTProbing(ASTProber astProber) {
+            throw new UnsupportedOperationException();
         }
 
         @Override
@@ -120,13 +165,51 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
+        protected ASTProber getDefaultASTProber() {
             return null;
         }
 
         @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() {
-            return null;
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        protected void enableASTProbing(ASTProber astProber) {
+            throw new UnsupportedOperationException();
         }
 
         @Override
@@ -160,13 +243,51 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
+        protected ASTProber getDefaultASTProber() {
             return null;
         }
 
         @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() {
-            return null;
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        protected void enableASTProbing(ASTProber astProber) {
+            throw new UnsupportedOperationException();
         }
 
         @Override
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,14 +22,15 @@
  */
 package com.oracle.truffle.api.test;
 
+import org.junit.Assert;
+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.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
-import org.junit.Assert;
-import org.junit.Test;
 
 /**
  * <h3>Passing Arguments</h3>
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,13 +22,20 @@
  */
 package com.oracle.truffle.api.test;
 
+import java.io.IOException;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.TruffleLanguage;
 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.WrapperNode;
 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.source.Source;
-import java.io.IOException;
 
 public final class TestingLanguage extends TruffleLanguage<Object> {
     public static final TestingLanguage INSTANCE = new TestingLanguage();
@@ -57,13 +64,51 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
+    protected Visualizer getVisualizer() {
+        return null;
+    }
+
+    @Override
+    protected ASTProber getDefaultASTProber() {
         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) {
+        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() {
-        return null;
+        throw new UnsupportedOperationException();
     }
 
     @Override
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,20 +22,26 @@
  */
 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 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.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 static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.InstrumentTestTag;
+import com.oracle.truffle.api.vm.TruffleVM;
 
 /**
  * Tests the kind of instrumentation where a client can provide an AST fragment to be
@@ -44,47 +50,60 @@
 public class AdvancedInstrumentTest {
 
     @Test
-    public void testAdvancedInstrumentListener() {
-        // 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 callTarget1 = runtime.createCallTarget(rootNode);
+    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) {
+            }
+
+            public void newProbeInserted(Probe p) {
+            }
 
-        // Ensure it executes correctly
-        assertEquals(13, callTarget1.call());
+            public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
+                if (tag == InstrumentTestTag.ADD_TAG) {
+                    assertNull("only one add node", addNodeProbe[0]);
+                    addNodeProbe[0] = probe;
+                }
+            }
 
-        // Probe the addition node
-        final Probe probe = addNode.probe();
+            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() {
+        // Attach a factory that never actually attaches a node.
+        final AdvancedInstrumentRootFactory rootFactory1 = new AdvancedInstrumentRootFactory() {
 
             public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) {
                 return null;
             }
-        }, null, "test AdvancedInstrument");
-        probe.attach(instrument);
+        };
+        instrumenter.attach(addNodeProbe[0], null, rootFactory1, null, "test AdvancedInstrument");
 
-        assertEquals(13, callTarget1.call());
-
-        final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot();
+        assertEquals(vm.eval(source).get(), 13);
 
         // Attach a factory that splices an execution counter into the AST.
-        probe.attach(Instrument.create(null, new AdvancedInstrumentRootFactory() {
+        final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot();
+        final AdvancedInstrumentRootFactory rootFactory2 = new AdvancedInstrumentRootFactory() {
 
             public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) {
                 return counter;
             }
-        }, null, "test AdvancedInstrument"));
-        assertEquals(0, counter.getCount());
+        };
+        instrumenter.attach(addNodeProbe[0], null, rootFactory2, null, "test AdvancedInstrument");
 
-        assertEquals(13, callTarget1.call());
-
+        assertEquals(0, counter.getCount());
+        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	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,17 +22,22 @@
  */
 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 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.ProbeNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.ProbeListener;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.instrument.SimpleInstrumentListener;
 import com.oracle.truffle.api.instrument.StandardInstrumentListener;
 import com.oracle.truffle.api.instrument.SyntaxTag;
@@ -41,18 +46,12 @@
 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.test.instrument.InstrumentationTestingLanguage.InstrumentTestTag;
+import com.oracle.truffle.api.vm.TruffleVM;
 
 /**
  * <h3>AST Instrumentation</h3>
@@ -60,143 +59,86 @@
  * Instrumentation allows the insertion into Truffle ASTs language-specific instances of
  * {@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 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>
- * </ol>
- * To do these tests, several required classes have been implemented in their most basic form, only
- * implementing the methods necessary for the tests to pass, with stubs elsewhere.
  */
 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";
-        }
-    };
+        final Probe[] probes = new Probe[3];
+        instrumenter.addProbeListener(new ProbeListener() {
 
-    @Test
-    public void testInstrumentationStructure() {
-        // 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 {
-            addNode.probe();
-        } catch (ProbeException e) {
-            assertEquals(e.getFailure().getReason(), Reason.NO_PARENT);
-        }
-        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);
+            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
-        addNode.probe();
-
-        // 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 == InstrumentTestTag.ADD_TAG) {
+                    assertEquals(probes[0], null);
+                    probes[0] = probe;
+                } else if (tag == InstrumentTestTag.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(instrumenter), new TestSimpleInstrumentCounter(instrumenter), new TestSimpleInstrumentCounter(instrumenter));
 
-        // Check that you can't probe the WrapperNodes
-        TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode;
-        try {
-            wrapper.probe();
-            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(instrumenter), new TestStandardInstrumentCounter(instrumenter), new TestStandardInstrumentCounter(instrumenter));
 
-        // Check that the "probed" AST still executes correctly
-        assertEquals(13, callTarget1.call());
     }
 
     @Test
-    public void testListeners() {
+    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);
+        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 = addNode.probe();
+        // 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(InstrumentTestTag.ADD_TAG).size(), 1);
+        assertEquals(instrumenter.findProbesTaggedAs(InstrumentTestTag.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);
@@ -205,7 +147,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);
@@ -215,141 +157,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() {
-        // 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
-        assertEquals(probeListener.probeCount, 3);
-        assertEquals(probeListener.tagCount, 3);
-
-        assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1);
-        assertEquals(Probe.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 : 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
-        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);
-
-        Probe.unregisterASTProber(astProber);
+        assertEquals(counterB.enterCount(), 4);
+        assertEquals(counterB.leaveCount(), 4);
+        assertEquals(counterC.enterCount(), 1);
+        assertEquals(counterC.leaveCount(), 1);
     }
 
     private interface TestCounter {
@@ -370,28 +218,11 @@
 
         public int enterCount = 0;
         public int leaveCount = 0;
-        public final Instrument instrument;
-
-        public TestSimpleInstrumentCounter() {
-            this.instrument = Instrument.create(new SimpleInstrumentListener() {
-
-                public void enter(Probe probe) {
-                    enterCount++;
-                }
+        public Instrumenter instrumenter;
+        private Instrument instrument;
 
-                public void returnVoid(Probe probe) {
-                    leaveCount++;
-                }
-
-                public void returnValue(Probe probe, Object result) {
-                    leaveCount++;
-                }
-
-                public void returnExceptional(Probe probe, Exception exception) {
-                    leaveCount++;
-                }
-
-            }, "Instrumentation Test Counter");
+        public TestSimpleInstrumentCounter(Instrumenter instrumenter) {
+            this.instrumenter = instrumenter;
         }
 
         @Override
@@ -406,7 +237,25 @@
 
         @Override
         public void attach(Probe probe) {
-            probe.attach(instrument);
+            instrument = instrumenter.attach(probe, new SimpleInstrumentListener() {
+
+                public void onEnter(Probe p) {
+                    enterCount++;
+                }
+
+                public void onReturnVoid(Probe p) {
+                    leaveCount++;
+                }
+
+                public void onReturnValue(Probe p, Object result) {
+                    leaveCount++;
+                }
+
+                public void onReturnExceptional(Probe p, Exception exception) {
+                    leaveCount++;
+                }
+
+            }, "Instrumentation Test Counter");
         }
 
         @Override
@@ -422,28 +271,11 @@
 
         public int enterCount = 0;
         public int leaveCount = 0;
-        public final Instrument instrument;
-
-        public TestStandardInstrumentCounter() {
-            this.instrument = Instrument.create(new StandardInstrumentListener() {
-
-                public void enter(Probe probe, Node node, VirtualFrame vFrame) {
-                    enterCount++;
-                }
+        public final Instrumenter instrumenter;
+        public Instrument instrument;
 
-                public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
-                    leaveCount++;
-                }
-
-                public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
-                    leaveCount++;
-                }
-
-                public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
-                    leaveCount++;
-                }
-
-            }, "Instrumentation Test Counter");
+        public TestStandardInstrumentCounter(Instrumenter instrumenter) {
+            this.instrumenter = instrumenter;
         }
 
         @Override
@@ -458,7 +290,25 @@
 
         @Override
         public void attach(Probe probe) {
-            probe.attach(instrument);
+            instrument = instrumenter.attach(probe, new StandardInstrumentListener() {
+
+                public void onEnter(Probe p, Node node, VirtualFrame vFrame) {
+                    enterCount++;
+                }
+
+                public void onReturnVoid(Probe p, Node node, VirtualFrame vFrame) {
+                    leaveCount++;
+                }
+
+                public void onReturnValue(Probe p, Node node, VirtualFrame vFrame, Object result) {
+                    leaveCount++;
+                }
+
+                public void onReturnExceptional(Probe p, Node node, VirtualFrame vFrame, Exception exception) {
+                    leaveCount++;
+                }
+
+            }, "Instrumentation Test Counter");
         }
 
         @Override
@@ -472,6 +322,12 @@
      */
     private static final class TestASTProber implements NodeVisitor, ASTProber {
 
+        private final Instrumenter instrumenter;
+
+        TestASTProber(Instrumenter instrumenter) {
+            this.instrumenter = instrumenter;
+        }
+
         @Override
         public boolean visit(Node node) {
             if (node instanceof TestLanguageNode) {
@@ -479,10 +335,10 @@
                 final TestLanguageNode testNode = (TestLanguageNode) node;
 
                 if (node instanceof TestValueNode) {
-                    testNode.probe().tagAs(VALUE_TAG, null);
+                    instrumenter.probe(testNode).tagAs(InstrumentTestTag.VALUE_TAG, null);
 
                 } else if (node instanceof TestAdditionNode) {
-                    testNode.probe().tagAs(ADD_TAG, null);
+                    instrumenter.probe(testNode).tagAs(InstrumentTestTag.ADD_TAG, null);
 
                 }
             }
@@ -490,7 +346,7 @@
         }
 
         @Override
-        public void probeAST(Node node) {
+        public void probeAST(Instrumenter inst, Node node) {
             node.accept(this);
         }
     }
@@ -503,7 +359,7 @@
         public int counter = 0;
 
         @Override
-        public void enter(Probe probe) {
+        public void onEnter(Probe probe) {
             counter++;
         }
     }
@@ -516,35 +372,11 @@
         public int counter = 0;
 
         @Override
-        public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+        public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
             counter++;
         }
     }
 
-    /**
-     * 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	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,42 +22,46 @@
  */
 package com.oracle.truffle.api.test.instrument;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot;
+import com.oracle.truffle.api.instrument.EventHandlerNode;
+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.WrapperNode;
 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.RootNode;
-import com.oracle.truffle.api.test.TestingLanguage;
+import com.oracle.truffle.api.vm.TruffleVM;
 
 /**
  * Tests instrumentation where a client can attach a node that gets attached into the AST.
  */
 class InstrumentationTestNodes {
 
+    static Instrumenter createInstrumenter() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        TruffleVM vm = TruffleVM.newVM().build();
+        final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrument = (Instrumenter) field.get(vm);
+        return instrument;
+    }
+
     abstract static class TestLanguageNode extends Node {
         public abstract Object execute(VirtualFrame vFrame);
 
-        @Override
-        public boolean isInstrumentable() {
-            return true;
-        }
-
-        @Override
-        public WrapperNode createWrapperNode() {
-            return new TestLanguageWrapperNode(this);
-        }
     }
 
     @NodeInfo(cost = NodeCost.NONE)
     static class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode {
         @Child private TestLanguageNode child;
-        @Child private ProbeNode probeNode;
+        @Child private EventHandlerNode eventHandlerNode;
 
         public TestLanguageWrapperNode(TestLanguageNode child) {
             assert !(child instanceof TestLanguageWrapperNode);
@@ -70,18 +74,13 @@
         }
 
         @Override
-        public boolean isInstrumentable() {
-            return false;
-        }
-
-        @Override
-        public void insertProbe(ProbeNode newProbeNode) {
-            this.probeNode = newProbeNode;
+        public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+            this.eventHandlerNode = eventHandler;
         }
 
         @Override
         public Probe getProbe() {
-            return probeNode.getProbe();
+            return eventHandlerNode.getProbe();
         }
 
         @Override
@@ -91,15 +90,15 @@
 
         @Override
         public Object execute(VirtualFrame vFrame) {
-            probeNode.enter(child, vFrame);
+            eventHandlerNode.enter(child, vFrame);
             Object result;
             try {
                 result = child.execute(vFrame);
-                probeNode.returnValue(child, vFrame, result);
+                eventHandlerNode.returnValue(child, vFrame, result);
             } catch (KillException e) {
                 throw (e);
             } catch (Exception e) {
-                probeNode.returnExceptional(child, vFrame, e);
+                eventHandlerNode.returnExceptional(child, vFrame, e);
                 throw (e);
             }
             return result;
@@ -145,7 +144,7 @@
      * 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 {
+    static class InstrumentationTestRootNode extends RootNode {
         @Child private TestLanguageNode body;
 
         /**
@@ -153,8 +152,8 @@
          * 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(TestingLanguage.class, null, null);
+        public InstrumentationTestRootNode(TestLanguageNode body) {
+            super(InstrumentationTestingLanguage.class, null, null);
             this.body = body;
         }
 
@@ -170,7 +169,51 @@
 
         @Override
         public void applyInstrumentation() {
-            Probe.applyASTProbers(body);
+            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(InstrumentationTestingLanguage.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");
+            }
         }
     }
 
@@ -192,4 +235,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	Mon Sep 21 12:15:38 2015 -0700
@@ -0,0 +1,190 @@
+/*
+ * 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.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();
+
+    static enum InstrumentTestTag implements SyntaxTag {
+
+        ADD_TAG("addition", "test language addition node"),
+
+        VALUE_TAG("value", "test language value node");
+
+        private final String name;
+        private final String description;
+
+        private InstrumentTestTag(String name, String description) {
+            this.name = name;
+            this.description = description;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+    }
+
+    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(InstrumentTestTag.VALUE_TAG, null);
+
+                        } else if (node instanceof TestAdditionNode) {
+                            instrumenter.probe(testNode).tagAs(InstrumentTestTag.ADD_TAG, null);
+
+                        }
+                    }
+                    return true;
+                }
+            });
+        }
+    }
+
+}
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -91,7 +91,8 @@
             w.write(text);
         }
 
-        Source s1 = Source.fromFileName(file.getPath());
+        // JDK8 default fails on OS X: https://bugs.openjdk.java.net/browse/JDK-8129632
+        Source s1 = Source.fromFileName(file.getPath()).withMimeType("text/x-java");
         assertEquals("Recognized as Java", "text/x-java", s1.getMimeType());
         Source s2 = s1.withMimeType("text/x-c");
         assertEquals("They have the same content", s1.getCode(), s2.getCode());
@@ -107,7 +108,8 @@
 
         String text = "// Hello";
 
-        Source s1 = Source.fromFileName(text, file.getPath());
+        // JDK8 default fails on OS X: https://bugs.openjdk.java.net/browse/JDK-8129632
+        Source s1 = Source.fromFileName(text, file.getPath()).withMimeType("text/x-java");
         assertEquals("Recognized as Java", "text/x-java", s1.getMimeType());
         Source s2 = s1.withMimeType("text/x-c");
         assertEquals("They have the same content", s1.getCode(), s2.getCode());
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,16 +22,10 @@
  */
 package com.oracle.truffle.api.test.vm;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.RootCallTarget;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.TruffleLanguage.Env;
-import com.oracle.truffle.api.debug.DebugSupportProvider;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
-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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.io.Reader;
 import java.util.Enumeration;
@@ -39,13 +33,28 @@
 import java.util.Map;
 import java.util.Properties;
 import java.util.concurrent.Executors;
+
 import org.junit.After;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.TruffleLanguage.Env;
+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.WrapperNode;
+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.RootNode;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.TruffleVM;
+
 public class ImplicitExplicitExportTest {
     private static Thread mainThread;
     private TruffleVM vm;
@@ -172,13 +181,51 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
+        protected ASTProber getDefaultASTProber() {
             return null;
         }
 
         @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() {
-            return null;
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        protected void enableASTProbing(ASTProber astProber) {
+            throw new UnsupportedOperationException();
         }
 
         private Object importExport(Source code) {
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -22,12 +22,20 @@
  */
 package com.oracle.truffle.api.test.vm;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.TruffleLanguage.Env;
 import com.oracle.truffle.api.debug.Breakpoint;
-import com.oracle.truffle.api.debug.DebugSupportException;
 import com.oracle.truffle.api.debug.DebugSupportProvider;
 import com.oracle.truffle.api.debug.Debugger;
 import com.oracle.truffle.api.debug.ExecutionEvent;
@@ -36,23 +44,20 @@
 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.EventHandlerNode;
+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.StandardSyntaxTag;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
 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.source.SourceSection;
 import com.oracle.truffle.api.vm.EventConsumer;
 import com.oracle.truffle.api.vm.TruffleVM;
-import java.io.IOException;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import org.junit.Test;
 
 /**
  * Bug report validating test.
@@ -75,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);
 
@@ -102,13 +107,17 @@
             super(AbstractLanguage.class, ss, null);
             node = new ANode(42);
             adoptChildren();
-            node.probe().tagAs(StandardSyntaxTag.STATEMENT, this);
         }
 
         @Override
         public Object execute(VirtualFrame frame) {
             return node.constant();
         }
+
+        @Override
+        public void applyInstrumentation() {
+            super.applyInstrumentation(node);
+        }
     }
 
     private static class ANode extends Node {
@@ -123,56 +132,64 @@
             return getRootNode().getSourceSection();
         }
 
-        @Override
-        public boolean isInstrumentable() {
-            return true;
+        Object constant() {
+            return constant;
+        }
+    }
+
+    private static class ANodeWrapper extends ANode implements WrapperNode {
+        @Child ANode child;
+        @Child private EventHandlerNode eventHandlerNode;
+
+        ANodeWrapper(ANode node) {
+            super(1);  // dummy
+            this.child = node;
         }
 
         @Override
-        public ProbeNode.WrapperNode createWrapperNode() {
-            class WN extends ANode implements ProbeNode.WrapperNode {
-                private ProbeNode probeNode;
-
-                public WN(int constant) {
-                    super(constant);
-                }
-
-                @Override
-                public Node getChild() {
-                    return ANode.this;
-                }
+        public Node getChild() {
+            return child;
+        }
 
-                @Override
-                public Probe getProbe() {
-                    return probeNode.getProbe();
-                }
-
-                @Override
-                public void insertProbe(ProbeNode pn) {
-                    this.probeNode = pn;
-                }
-
-                @Override
-                public String instrumentationInfo() {
-                    throw new UnsupportedOperationException();
-                }
-            }
-            return new WN(constant);
+        @Override
+        public Probe getProbe() {
+            return eventHandlerNode.getProbe();
         }
 
-        Object constant() {
-            return constant;
+        @Override
+        public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+            this.eventHandlerNode = eventHandler;
         }
 
+        @Override
+        public String instrumentationInfo() {
+            throw new UnsupportedOperationException();
+        }
     }
 
     private abstract static class AbstractLanguage extends TruffleLanguage<Object> {
     }
 
     @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() {
+
+            public void probeAST(final Instrumenter instrumenter, Node startNode) {
+                startNode.accept(new NodeVisitor() {
+
+                    public boolean visit(Node node) {
+
+                        if (node instanceof ANode) {
+                            instrumenter.probe(node).tagAs(StandardSyntaxTag.STATEMENT, null);
+                        }
+                        return true;
+                    }
+                });
+            }
+        };
+
         @Override
         protected Object createContext(Env env) {
             assertNull("Not defined symbol", env.importSymbol("unknown"));
@@ -199,23 +216,25 @@
             throw new UnsupportedOperationException();
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         protected ToolSupportProvider getToolSupport() {
             throw new UnsupportedOperationException();
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         protected DebugSupportProvider getDebugSupport() {
-            return this;
-        }
-
-        @Override
-        public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException {
             throw new UnsupportedOperationException();
         }
 
         @Override
-        public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException {
+        public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) {
             throw new InstrumentOKException();
         }
 
@@ -225,6 +244,22 @@
         }
 
         @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;
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
         public void enableASTProbing(ASTProber astProber) {
             throw new UnsupportedOperationException();
         }
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java	Mon Sep 21 12:15:38 2015 -0700
@@ -28,6 +28,7 @@
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.impl.Accessor;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
@@ -82,6 +83,11 @@
             Object tmp = ForeignAccess.execute(foreignAccess, frame, function, args);
             return convert.convert(frame, tmp);
         }
+
+        @Override
+        public void applyInstrumentation() {
+            SymbolInvokerImpl.ACCESSOR_INTEROP.applyInstrumentation(foreignAccess);
+        }
     }
 
     private static final class ConvertNode extends Node {
@@ -125,4 +131,14 @@
             return obj;
         }
     }
+
+    static final class AccessorInterop extends Accessor {
+
+        @Override
+        protected void applyInstrumentation(Node node) {
+            super.applyInstrumentation(node);
+        }
+    }
+
+    static final AccessorInterop ACCESSOR_INTEROP = new AccessorInterop();
 }
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,20 +24,6 @@
  */
 package com.oracle.truffle.api.vm;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.TruffleLanguage.Env;
-import com.oracle.truffle.api.TruffleLanguage.Registration;
-import com.oracle.truffle.api.debug.DebugSupportProvider;
-import com.oracle.truffle.api.debug.Debugger;
-import com.oracle.truffle.api.debug.ExecutionEvent;
-import com.oracle.truffle.api.debug.SuspendedEvent;
-import com.oracle.truffle.api.impl.Accessor;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
-import com.oracle.truffle.api.interop.TruffleObject;
-import com.oracle.truffle.api.interop.java.JavaInterop;
-import com.oracle.truffle.api.source.Source;
 import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
@@ -65,6 +51,23 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.TruffleLanguage.Env;
+import com.oracle.truffle.api.TruffleLanguage.Registration;
+import com.oracle.truffle.api.debug.DebugSupportProvider;
+import com.oracle.truffle.api.debug.Debugger;
+import com.oracle.truffle.api.debug.ExecutionEvent;
+import com.oracle.truffle.api.debug.SuspendedEvent;
+import com.oracle.truffle.api.impl.Accessor;
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.interop.TruffleObject;
+import com.oracle.truffle.api.interop.java.JavaInterop;
+import com.oracle.truffle.api.source.Source;
+
 /**
  * <em>Virtual machine</em> for Truffle based languages. Term virtual machine is a bit overloaded,
  * so don't think of <em>Java virtual machine</em> here - while we are running and using
@@ -109,6 +112,7 @@
     private final OutputStream out;
     private final EventConsumer<?>[] handlers;
     private final Map<String, Object> globals;
+    private final Instrumenter instrumenter;
     private Debugger debugger;
 
     /**
@@ -123,6 +127,7 @@
         this.handlers = null;
         this.globals = null;
         this.executor = null;
+        this.instrumenter = null;
     }
 
     /**
@@ -136,6 +141,7 @@
         this.handlers = handlers;
         this.initThread = Thread.currentThread();
         this.globals = new HashMap<>(globals);
+        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()));
@@ -463,7 +469,6 @@
         try (Closeable d = SPI.executionStart(this, fillIn, s)) {
             TruffleLanguage<?> langImpl = l.getImpl(true);
             fillLang[0] = langImpl;
-            TruffleVM.findDebuggerSupport(langImpl);
             if (debugger == null) {
                 debugger = fillIn[0];
             }
@@ -625,7 +630,7 @@
          * delegates to
          * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject)}
          * just handles primitive types as well.
-         * 
+         *
          * @param <T> the type of the view one wants to obtain
          * @param representation the class of the view interface (it has to be an interface)
          * @return instance of the view wrapping the object of this symbol
@@ -796,7 +801,14 @@
 
         TruffleLanguage<?> getImpl(boolean create) {
             getEnv(create);
-            return info.getImpl(false);
+            TruffleLanguage<?> impl = info.getImpl(false);
+            if (impl != null) {
+                ASTProber prober = SPI.getDefaultASTProber(impl);
+                if (prober != null) {
+                    instrumenter.registerASTProber(prober);
+                }
+            }
+            return impl;
         }
 
         TruffleLanguage.Env getEnv(boolean create) {
@@ -816,8 +828,7 @@
     // Accessor helper methods
     //
 
-    TruffleLanguage<?> findLanguage(Probe probe) {
-        Class<? extends TruffleLanguage> languageClazz = SPI.findLanguage(probe);
+    TruffleLanguage<?> findLanguage(Class<? extends TruffleLanguage> languageClazz) {
         for (Map.Entry<String, Language> entrySet : langs.entrySet()) {
             Language languageDescription = entrySet.getValue();
             final TruffleLanguage<?> impl = languageDescription.getImpl(false);
@@ -828,6 +839,10 @@
         throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs);
     }
 
+    TruffleLanguage<?> findLanguage(Probe probe) {
+        return findLanguage(SPI.findLanguage(probe));
+    }
+
     Env findEnv(Class<? extends TruffleLanguage> languageClazz) {
         for (Map.Entry<String, Language> entrySet : langs.entrySet()) {
             Language languageDescription = entrySet.getValue();
@@ -839,10 +854,6 @@
         throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs);
     }
 
-    static DebugSupportProvider findDebuggerSupport(TruffleLanguage<?> l) {
-        return SPI.getDebugSupport(l);
-    }
-
     private static class SPIAccessor extends Accessor {
         @Override
         public Object importSymbol(Object vmObj, TruffleLanguage<?> ownLang, String globalName) {
@@ -898,14 +909,32 @@
             return super.languageGlobal(env);
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         public ToolSupportProvider getToolSupport(TruffleLanguage<?> l) {
-            return super.getToolSupport(l);
+            throw new UnsupportedOperationException();
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
+        public DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
+            throw new UnsupportedOperationException();
         }
 
         @Override
-        public DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
-            return super.getDebugSupport(l);
+        protected Instrumenter createInstrumenter(Object vm) {
+            return super.createInstrumenter(vm);
+        }
+
+        @Override
+        protected ASTProber getDefaultASTProber(TruffleLanguage impl) {
+            return super.getDefaultASTProber(impl);
+        }
+
+        @Override
+        protected Instrumenter getInstrumenter(Object obj) {
+            final TruffleVM vm = (TruffleVM) obj;
+            return vm.instrumenter;
         }
 
         @Override
@@ -920,6 +949,12 @@
         }
 
         @Override
+        protected TruffleLanguage findLanguageImpl(Object obj, Class<? extends TruffleLanguage> languageClazz) {
+            final TruffleVM vm = (TruffleVM) obj;
+            return vm.findLanguage(languageClazz);
+        }
+
+        @Override
         protected Closeable executionStart(Object obj, Debugger[] fillIn, Source s) {
             TruffleVM vm = (TruffleVM) obj;
             return super.executionStart(vm, fillIn, s);
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,12 +24,6 @@
  */
 package com.oracle.truffle.api;
 
-import com.oracle.truffle.api.debug.DebugSupportProvider;
-import com.oracle.truffle.api.impl.Accessor;
-import com.oracle.truffle.api.impl.FindContextNode;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.source.Source;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -45,6 +39,21 @@
 import java.util.Map;
 import java.util.WeakHashMap;
 
+import com.oracle.truffle.api.debug.DebugSupportProvider;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.impl.Accessor;
+import com.oracle.truffle.api.impl.FindContextNode;
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.Source;
+
 /**
  * An entry point for everyone who wants to implement a Truffle based language. By providing an
  * implementation of this type and registering it using {@link Registration} annotation, your
@@ -183,11 +192,81 @@
      */
     protected abstract boolean isObjectOfLanguage(Object object);
 
+    @Deprecated
     protected abstract ToolSupportProvider getToolSupport();
 
+    @Deprecated
     protected abstract DebugSupportProvider getDebugSupport();
 
     /**
+     * Gets visualization services for language-specific information.
+     */
+    protected abstract Visualizer getVisualizer();
+
+    /**
+     * Enables AST probing on all subsequently created ASTs (sources parsed).
+     *
+     * @param astProber optional AST prober to enable; the default for the language used if
+     *            {@code null}
+     */
+    @Deprecated
+    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();
+
+    /**
+     * Runs source code in a halted execution context, or at top level.
+     *
+     * @param source the code to run
+     * @param node node where execution halted, {@code null} if no execution context
+     * @param mFrame frame where execution halted, {@code null} if no execution context
+     * @return result of running the code in the context, or at top level if no execution context.
+     * @throws IOException if the evaluation cannot be performed
+     */
+    protected abstract Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException;
+
+    /**
+     * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot}
+     * that, when executed, computes the result of a textual expression in the language; used to
+     * create an
+     * {@linkplain Instrumenter#attach(com.oracle.truffle.api.instrument.Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)}
+     * .
+     *
+     * @param expr a guest language expression
+     * @param resultListener optional listener for the result of each evaluation.
+     * @return a new factory
+     * @throws IOException if the factory cannot be created, for example if the expression is badly
+     *             formed.
+     */
+    protected abstract AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException;
+
+    /**
      * Allows a language implementor to create a node that can effectively lookup up the context
      * associated with current execution. The context is created by
      * {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)} method.
@@ -357,6 +436,14 @@
         }
 
         @Override
+        protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr,
+                        AdvancedInstrumentResultListener resultListener) throws IOException {
+
+            final TruffleLanguage language = findLanguageImpl(vm, languageClass);
+            return language.createAdvancedInstrumentRootFactory(expr, resultListener);
+        }
+
+        @Override
         protected Object findExportedSymbol(TruffleLanguage.Env env, String globalName, boolean onlyExplicit) {
             return env.langCtx.findExportedSymbol(globalName, onlyExplicit);
         }
@@ -382,10 +469,27 @@
         }
 
         @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();
+        }
+
+        @SuppressWarnings("deprecation")
+        @Override
         protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) {
             return l.getToolSupport();
         }
 
+        @SuppressWarnings("deprecation")
         @Override
         protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
             return l.getDebugSupport();
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java	Mon Sep 21 12:15:38 2015 -0700
@@ -28,7 +28,7 @@
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 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.ToolSupportProvider;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.Source;
@@ -52,7 +52,7 @@
      * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot}
      * that, when executed, computes the result of a textual expression in the language; used to
      * create an
-     * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+     * {@linkplain Instrumenter#attach(com.oracle.truffle.api.instrument.Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
      * Advanced Instrument}.
      *
      * @param expr a guest language expression
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,6 +24,13 @@
  */
 package com.oracle.truffle.api.debug;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
@@ -34,7 +41,7 @@
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 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.KillException;
 import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
@@ -43,12 +50,6 @@
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.LineLocation;
 import com.oracle.truffle.api.source.Source;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
 
 /**
  * Represents debugging related state of a {@link com.oracle.truffle.api.vm.TruffleVM}. Instance of
@@ -60,7 +61,7 @@
 public final class Debugger {
 
     private static final boolean TRACE = false;
-    private static final String TRACE_PREFIX = "DEBUG ENGINE: ";
+    private static final String TRACE_PREFIX = "Debugger: ";
 
     private static final PrintStream OUT = System.out;
 
@@ -73,6 +74,7 @@
         }
     }
 
+    private final Instrumenter instrumenter;
     private final Object vm;
     private Source lastSource;
 
@@ -92,6 +94,9 @@
         void addWarning(String warning);
     }
 
+    private final BreakpointCallback breakpointCallback;
+    private final WarningLog warningLog;
+
     /**
      * Implementation of line-oriented breakpoints.
      */
@@ -107,9 +112,9 @@
      */
     private DebugExecutionContext debugContext;
 
-    Debugger(Object vm) {
+    Debugger(Object vm, Instrumenter instrumenter) {
         this.vm = vm;
-
+        this.instrumenter = instrumenter;
         Source.setFileCaching(true);
 
         // Initialize execution context stack
@@ -117,7 +122,7 @@
         prepareContinue();
         debugContext.contextTrace("START EXEC DEFAULT");
 
-        final BreakpointCallback breakpointCallback = new BreakpointCallback() {
+        breakpointCallback = new BreakpointCallback() {
 
             @TruffleBoundary
             public void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason) {
@@ -125,7 +130,7 @@
             }
         };
 
-        final WarningLog warningLog = new WarningLog() {
+        warningLog = new WarningLog() {
 
             public void addWarning(String warning) {
                 assert debugContext != null;
@@ -137,10 +142,6 @@
         this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog);
     }
 
-    Object vm() {
-        return vm;
-    }
-
     /**
      * Sets a breakpoint to halt at a source line.
      *
@@ -269,11 +270,12 @@
         debugContext.setStrategy(new StepOver(stepCount));
     }
 
+    // TODO (mlvdv) used by the breakpoint factories; to be deprecated/replaced.
     /**
      * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot}
      * that, when executed, computes the result of a textual expression in the language; used to
      * create an
-     * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+     * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
      * Advanced Instrument}.
      *
      * @param expr a guest language expression
@@ -284,15 +286,12 @@
      */
     @SuppressWarnings("rawtypes")
     AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Probe probe, String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
-        try {
-            Class<? extends TruffleLanguage> langugageClass = ACCESSOR.findLanguage(probe);
-            TruffleLanguage.Env env = ACCESSOR.findLanguage(vm, langugageClass);
-            TruffleLanguage<?> l = ACCESSOR.findLanguage(env);
-            DebugSupportProvider dsp = ACCESSOR.getDebugSupport(l);
-            return dsp.createAdvancedInstrumentRootFactory(expr, resultListener);
-        } catch (DebugSupportException ex) {
-            throw new IOException(ex);
-        }
+        Class<? extends TruffleLanguage> languageClass = ACCESSOR.findLanguage(probe);
+        return ACCESSOR.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener);
+    }
+
+    Instrumenter getInstrumenter() {
+        return instrumenter;
     }
 
     /**
@@ -416,7 +415,7 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
+            instrumenter.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
                 @TruffleBoundary
                 @Override
                 public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
@@ -430,7 +429,7 @@
                     strategyTrace("RESUME BEFORE", "");
                 }
             });
-            Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
+            instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
                 @TruffleBoundary
                 @Override
                 public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
@@ -449,8 +448,8 @@
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
-            Probe.setAfterTagTrap(null);
+            instrumenter.setBeforeTagTrap(null);
+            instrumenter.setAfterTagTrap(null);
         }
     }
 
@@ -473,7 +472,7 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
+            instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
 
                 @TruffleBoundary
                 @Override
@@ -491,7 +490,7 @@
 
         @Override
         protected void unsetStrategy() {
-            Probe.setAfterTagTrap(null);
+            instrumenter.setAfterTagTrap(null);
         }
     }
 
@@ -517,7 +516,7 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
+            instrumenter.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
                 @TruffleBoundary
                 @Override
                 public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
@@ -542,7 +541,7 @@
                 }
             });
 
-            Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
+            instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
                 @TruffleBoundary
                 @Override
                 public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
@@ -563,8 +562,8 @@
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
-            Probe.setAfterTagTrap(null);
+            instrumenter.setBeforeTagTrap(null);
+            instrumenter.setAfterTagTrap(null);
         }
     }
 
@@ -592,7 +591,7 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
+            instrumenter.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
                 @TruffleBoundary
                 @Override
                 public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
@@ -613,7 +612,7 @@
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
+            instrumenter.setBeforeTagTrap(null);
         }
     }
 
@@ -813,7 +812,8 @@
         protected Closeable executionStart(Object vm, Debugger[] fillIn, Source s) {
             final Debugger d;
             if (fillIn[0] == null) {
-                d = fillIn[0] = new Debugger(vm);
+                final Instrumenter instrumenter = ACCESSOR.getInstrumenter(vm);
+                d = fillIn[0] = new Debugger(vm, instrumenter);
             } else {
                 d = fillIn[0];
             }
@@ -842,8 +842,14 @@
         }
 
         @Override
-        protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
-            return super.getDebugSupport(l);
+        protected Instrumenter getInstrumenter(Object vm) {
+            return super.getInstrumenter(vm);
+        }
+
+        @Override
+        protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr,
+                        AdvancedInstrumentResultListener resultListener) throws IOException {
+            return super.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener);
         }
 
         @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,20 +24,32 @@
  */
 package com.oracle.truffle.api.debug;
 
-import com.oracle.truffle.api.Assumption;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.Truffle;
 import static com.oracle.truffle.api.debug.Breakpoint.State.DISABLED;
 import static com.oracle.truffle.api.debug.Breakpoint.State.DISABLED_UNRESOLVED;
 import static com.oracle.truffle.api.debug.Breakpoint.State.DISPOSED;
 import static com.oracle.truffle.api.debug.Breakpoint.State.ENABLED;
 import static com.oracle.truffle.api.debug.Breakpoint.State.ENABLED_UNRESOLVED;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.debug.Debugger.BreakpointCallback;
 import com.oracle.truffle.api.debug.Debugger.WarningLog;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 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.StandardSyntaxTag;
 import com.oracle.truffle.api.instrument.SyntaxTag;
@@ -48,15 +60,6 @@
 import com.oracle.truffle.api.source.LineLocation;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.utilities.CyclicAssumption;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
 
 //TODO (mlvdv) some common functionality could be factored out of this and TagBreakpointSupport
 
@@ -83,7 +86,7 @@
     private static final boolean TRACE = false;
     private static final PrintStream OUT = System.out;
 
-    private static final String BREAKPOINT_NAME = "LINE BREAKPOINT";
+    private static final String BREAKPOINT_NAME = "Line Breakpoints";
 
     @TruffleBoundary
     private static void trace(String format, Object... args) {
@@ -106,6 +109,7 @@
         }
     };
 
+    private final Debugger debugger;
     private final BreakpointCallback breakpointCallback;
     private final WarningLog warningLog;
 
@@ -127,17 +131,17 @@
      */
     @CompilationFinal private boolean breakpointsActive = true;
     private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active");
-    private final Debugger debugger;
 
     LineBreakpointFactory(Debugger debugger, BreakpointCallback breakpointCallback, final WarningLog warningLog) {
         this.debugger = debugger;
         this.breakpointCallback = breakpointCallback;
         this.warningLog = warningLog;
 
+        final Instrumenter instrumenter = debugger.getInstrumenter();
         lineToProbesMap = new LineToProbesMap();
-        lineToProbesMap.install();
+        lineToProbesMap.install(instrumenter);
 
-        Probe.addProbeListener(new DefaultProbeListener() {
+        instrumenter.addProbeListener(new DefaultProbeListener() {
 
             @Override
             public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
@@ -378,12 +382,12 @@
                 throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME);
             }
             Instrument newInstrument = null;
+            final Instrumenter instrumenter = debugger.getInstrumenter();
             if (conditionExpr == null) {
-                newInstrument = Instrument.create(new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME);
+                newInstrument = instrumenter.attach(newProbe, new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME);
             } else {
-                newInstrument = Instrument.create(this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME);
+                newInstrument = instrumenter.attach(newProbe, this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME);
             }
-            newProbe.attach(newInstrument);
             instruments.add(newInstrument);
             changeState(isEnabled ? ENABLED : DISABLED);
         }
@@ -442,7 +446,7 @@
             }
         }
 
-        public void notifyResult(Node node, VirtualFrame vFrame, Object result) {
+        public void onExecution(Node node, VirtualFrame vFrame, Object result) {
             final boolean condition = (Boolean) result;
             if (TRACE) {
                 trace("breakpoint condition = %b  %s", condition, getShortDescription());
@@ -452,7 +456,7 @@
             }
         }
 
-        public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) {
+        public void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex) {
             addExceptionWarning(ex);
             if (TRACE) {
                 trace("breakpoint failure = %s  %s", ex, getShortDescription());
@@ -479,7 +483,7 @@
         private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener {
 
             @Override
-            public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+            public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
                 LineBreakpointImpl.this.nodeEnter(node, vFrame);
             }
         }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,13 +24,6 @@
  */
 package com.oracle.truffle.api.debug;
 
-import com.oracle.truffle.api.instrument.InstrumentationTool;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeListener;
-import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
-import com.oracle.truffle.api.source.LineLocation;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.source.SourceSection;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -38,11 +31,19 @@
 import java.util.HashMap;
 import java.util.Map;
 
+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.impl.DefaultProbeListener;
+import com.oracle.truffle.api.source.LineLocation;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+
 /**
  * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST,
  * indexed by {@link Source} and line number.
  */
-final class LineToProbesMap extends InstrumentationTool {
+final class LineToProbesMap extends Instrumenter.Tool {
 
     private static final boolean TRACE = false;
     private static final PrintStream OUT = System.out;
@@ -68,7 +69,11 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        final Instrumenter instrumenter = getInstrumenter();
+        for (Probe probe : instrumenter.findProbesTaggedAs(null)) {
+            addMapEntry(probe);
+        }
+        instrumenter.addProbeListener(probeListener);
         return true;
     }
 
@@ -79,7 +84,7 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
+        getInstrumenter().removeProbeListener(probeListener);
     }
 
     /**
@@ -115,21 +120,25 @@
 
         @Override
         public void newProbeInserted(Probe probe) {
-            final SourceSection sourceSection = probe.getProbedSourceSection();
-            if (sourceSection != null && sourceSection.getSource() != null) {
-                final LineLocation lineLocation = sourceSection.getLineLocation();
-                if (TRACE) {
-                    trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());
-                }
-                Collection<Probe> probes = lineToProbesMap.get(lineLocation);
-                if (probes == null) {
-                    probes = new ArrayList<>(2);
-                    lineToProbesMap.put(lineLocation, probes);
-                } else {
-                    assert !probes.contains(probe);
-                }
-                probes.add(probe);
+            addMapEntry(probe);
+        }
+    }
+
+    private void addMapEntry(Probe probe) {
+        final SourceSection sourceSection = probe.getProbedSourceSection();
+        if (sourceSection != null && sourceSection.getSource() != null) {
+            final LineLocation lineLocation = sourceSection.getLineLocation();
+            if (TRACE) {
+                trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());
             }
+            Collection<Probe> probes = lineToProbesMap.get(lineLocation);
+            if (probes == null) {
+                probes = new ArrayList<>(2);
+                lineToProbesMap.put(lineLocation, probes);
+            } else {
+                assert !probes.contains(probe);
+            }
+            probes.add(probe);
         }
     }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Mon Sep 21 12:15:38 2015 -0700
@@ -47,6 +47,7 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 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.SyntaxTag;
 import com.oracle.truffle.api.instrument.SyntaxTagTrap;
@@ -62,10 +63,10 @@
  * Support class for creating and managing "Tag Breakpoints". A Tag Breakpoint halts execution just
  * before reaching any node whose Probe carries a specified {@linkplain SyntaxTag Tag}.
  * <p>
- * The {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Tag Trap}, which is built directly into the
- * Instrumentation Framework, does the same thing more efficiently, but there may only be one Tag
- * Trap active at a time. Any number of tag breakpoints may coexist with the Tag Trap, but it would
- * be confusing to have a Tag Breakpoint set for the same Tag as the current Tag Trap.
+ * The {@linkplain Instrumenter#setBeforeTagTrap(SyntaxTagTrap) Tag Trap}, which is built directly
+ * into the Instrumentation Framework, does the same thing more efficiently, but there may only be
+ * one Tag Trap active at a time. Any number of tag breakpoints may coexist with the Tag Trap, but
+ * it would be confusing to have a Tag Breakpoint set for the same Tag as the current Tag Trap.
  * <p>
  * Notes:
  * <ol>
@@ -103,6 +104,7 @@
         }
     };
 
+    private final Debugger debugger;
     private final BreakpointCallback breakpointCallback;
     private final WarningLog warningLog;
 
@@ -117,14 +119,13 @@
      */
     @CompilationFinal private boolean breakpointsActive = true;
     private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active");
-    private final Debugger debugger;
 
     TagBreakpointFactory(Debugger debugger, BreakpointCallback breakpointCallback, final WarningLog warningLog) {
         this.debugger = debugger;
         this.breakpointCallback = breakpointCallback;
         this.warningLog = warningLog;
 
-        Probe.addProbeListener(new DefaultProbeListener() {
+        debugger.getInstrumenter().addProbeListener(new DefaultProbeListener() {
 
             @Override
             public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
@@ -195,7 +196,7 @@
 
             tagToBreakpoint.put(tag, breakpoint);
 
-            for (Probe probe : Probe.findProbesTaggedAs(tag)) {
+            for (Probe probe : debugger.getInstrumenter().findProbesTaggedAs(tag)) {
                 breakpoint.attach(probe);
             }
         } else {
@@ -341,12 +342,12 @@
                 throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME);
             }
             Instrument newInstrument = null;
+            final Instrumenter instrumenter = debugger.getInstrumenter();
             if (conditionExpr == null) {
-                newInstrument = Instrument.create(new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME);
+                newInstrument = instrumenter.attach(newProbe, new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME);
             } else {
-                newInstrument = Instrument.create(this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME);
+                instrumenter.attach(newProbe, this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME);
             }
-            newProbe.attach(newInstrument);
             instruments.add(newInstrument);
             changeState(isEnabled ? ENABLED : DISABLED);
         }
@@ -406,7 +407,7 @@
 
         }
 
-        public void notifyResult(Node node, VirtualFrame vFrame, Object result) {
+        public void onExecution(Node node, VirtualFrame vFrame, Object result) {
             final boolean condition = (Boolean) result;
             if (TRACE) {
                 trace("breakpoint condition = %b  %s", condition, getShortDescription());
@@ -416,7 +417,7 @@
             }
         }
 
-        public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) {
+        public void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex) {
             addExceptionWarning(ex);
             if (TRACE) {
                 trace("breakpoint failure = %s  %s", ex, getShortDescription());
@@ -443,7 +444,7 @@
         private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener {
 
             @Override
-            public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+            public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
                 TagBreakpointImpl.this.nodeEnter(node, vFrame);
             }
         }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,6 +24,13 @@
  */
 package com.oracle.truffle.api.impl;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+
 import com.oracle.truffle.api.Assumption;
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
@@ -31,26 +38,28 @@
 import com.oracle.truffle.api.TruffleLanguage.Env;
 import com.oracle.truffle.api.debug.DebugSupportProvider;
 import com.oracle.truffle.api.debug.Debugger;
+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.Probe;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.Source;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
 
 /**
- * Communication between TruffleVM and TruffleLanguage API/SPI.
+ * Communication between TruffleVM, TruffleLanguage API/SPI, and other services.
  */
 @SuppressWarnings("rawtypes")
 public abstract class Accessor {
     private static Accessor API;
     private static Accessor SPI;
     private static Accessor NODES;
+    private static Accessor INTEROP;
     private static Accessor INSTRUMENT;
     private static Accessor DEBUG;
     private static final ThreadLocal<Object> CURRENT_VM = new ThreadLocal<>();
@@ -72,11 +81,13 @@
                 return false;
             }
 
+            @SuppressWarnings("deprecation")
             @Override
             protected ToolSupportProvider getToolSupport() {
-                return null;
+                throw new UnsupportedOperationException();
             }
 
+            @SuppressWarnings("deprecation")
             @Override
             protected DebugSupportProvider getDebugSupport() {
                 return null;
@@ -91,12 +102,49 @@
             protected Object createContext(TruffleLanguage.Env env) {
                 return null;
             }
+
+            @Override
+            protected boolean isInstrumentable(Node node) {
+                return false;
+            }
+
+            @Override
+            protected WrapperNode createWrapperNode(Node node) {
+                return null;
+            }
+
+            @Override
+            protected ASTProber getDefaultASTProber() {
+                return null;
+            }
+
+            @Override
+            protected Visualizer getVisualizer() {
+                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;
+            }
         };
         lng.hashCode();
         new Node(null) {
         }.getRootNode();
 
         try {
+            Class.forName(Instrumenter.class.getName(), true, Instrumenter.class.getClassLoader());
             Class.forName(Debugger.class.getName(), true, Debugger.class.getClassLoader());
         } catch (ClassNotFoundException ex) {
             throw new IllegalStateException(ex);
@@ -114,6 +162,11 @@
                 throw new IllegalStateException();
             }
             NODES = this;
+        } else if (this.getClass().getSimpleName().endsWith("Interop")) {
+            if (INTEROP != null) {
+                throw new IllegalStateException();
+            }
+            INTEROP = this;
         } else if (this.getClass().getSimpleName().endsWith("Instrument")) {
             if (INSTRUMENT != null) {
                 throw new IllegalStateException();
@@ -152,12 +205,45 @@
         return API.languageGlobal(env);
     }
 
-    protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) {
-        return API.getToolSupport(l);
+    @Deprecated
+    protected ToolSupportProvider getToolSupport(@SuppressWarnings("unused") TruffleLanguage<?> l) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Deprecated
+    protected DebugSupportProvider getDebugSupport(@SuppressWarnings("unused") TruffleLanguage<?> l) {
+        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 DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
-        return API.getDebugSupport(l);
+    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);
+    }
+
+    protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr, AdvancedInstrumentResultListener resultListener)
+                    throws IOException {
+        return API.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener);
     }
 
     protected Class<? extends TruffleLanguage> findLanguage(RootNode n) {
@@ -184,6 +270,36 @@
         return SPI.findLanguage(vm, languageClass);
     }
 
+    protected TruffleLanguage findLanguageImpl(Object known, Class<? extends TruffleLanguage> languageClass) {
+        Object vm;
+        if (known == null) {
+            vm = CURRENT_VM.get();
+            if (vm == null) {
+                throw new IllegalStateException();
+            }
+        } else {
+            vm = known;
+        }
+        return SPI.findLanguageImpl(vm, languageClass);
+    }
+
+    protected Instrumenter getInstrumenter(Object known) {
+        Object vm;
+        if (known == null) {
+            vm = CURRENT_VM.get();
+            if (vm == null) {
+                throw new IllegalStateException();
+            }
+        } else {
+            vm = known;
+        }
+        return SPI.getInstrumenter(vm);
+    }
+
+    protected Instrumenter createInstrumenter(Object vm) {
+        return INSTRUMENT.createInstrumenter(vm);
+    }
+
     private static Reference<Object> previousVM = new WeakReference<>(null);
     private static Assumption oneVM = Truffle.getRuntime().createAssumption();
 
@@ -215,7 +331,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);
@@ -237,4 +353,8 @@
     protected TruffleLanguage<?> findLanguage(Env env) {
         return API.findLanguage(env);
     }
+
+    protected void applyInstrumentation(Node node) {
+        INSTRUMENT.applyInstrumentation(node);
+    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Mon Sep 21 12:15:38 2015 -0700
@@ -31,15 +31,15 @@
  * not yet executed) AST.
  *
  * @see Probe
- * @see Probe#addProbeListener(ProbeListener)
+ * @see Instrumenter#addProbeListener(ProbeListener)
  */
 public interface ASTProber {
 
     /**
      * 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(Node node);
+    void probeAST(Instrumenter instrumenter, Node node);
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentResultListener.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentResultListener.java	Mon Sep 21 12:15:38 2015 -0700
@@ -31,8 +31,11 @@
 /**
  * Listener for receiving the result a client-provided {@linkplain AdvancedInstrumentRoot AST
  * fragment}, when executed by a
- * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+ * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
  * Advanced Instrument}.
+ * <p>
+ * Notification is fully synchronous, so overrides have performance implications. Non-trivial
+ * methods should be coded with Truffle guidelines and cautions in mind.
  *
  * @see Instrument
  * @see AdvancedInstrumentRoot
@@ -43,7 +46,7 @@
     /**
      * Notifies listener that a client-provided {@linkplain AdvancedInstrumentRoot AST fragment} has
      * been executed by an
-     * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+     * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
      * Advanced Instrument} with the specified result, possibly {@code null}.
      * <p>
      * <strong>Note: </strong> Truffle will attempt to optimize implementations through partial
@@ -54,7 +57,7 @@
      * @param vFrame execution frame at the guest-language AST node
      * @param result the result of this AST fragment's execution
      */
-    void notifyResult(Node node, VirtualFrame vFrame, Object result);
+    void onExecution(Node node, VirtualFrame vFrame, Object result);
 
     /**
      * Notifies listener that execution of client-provided {@linkplain AdvancedInstrumentRoot AST
@@ -69,6 +72,6 @@
      * @param vFrame execution frame at the guest-language AST node
      * @param ex the exception
      */
-    void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex);
+    void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex);
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRoot.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRoot.java	Mon Sep 21 12:15:38 2015 -0700
@@ -30,7 +30,7 @@
 /**
  * Root of a client-provided AST fragment that can be executed efficiently, subject to full Truffle
  * optimization, by an
- * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+ * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
  * Advanced Instrument}.
  *
  * @see Instrument
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRootFactory.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRootFactory.java	Mon Sep 21 12:15:38 2015 -0700
@@ -29,7 +29,7 @@
 /**
  * Creator of {@linkplain AdvancedInstrumentRoot AST fragments} suitable for efficient execution,
  * subject to full Truffle optimization, by an
- * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+ * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
  * Advanced Instrument}.
  *
  * @see Instrument
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/EventHandlerNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -0,0 +1,64 @@
+/*
+ * 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.api.instrument;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+
+/**
+ * An Instrumentation-managed {@link Node} that synchronously propagates notification of AST
+ * Execution Events through the Instrumentation Framework.
+ */
+public abstract class EventHandlerNode extends Node implements InstrumentationNode {
+
+    protected EventHandlerNode() {
+    }
+
+    /**
+     * An AST node's execute method is about to be called.
+     */
+    public abstract void enter(Node node, VirtualFrame vFrame);
+
+    /**
+     * An AST Node's {@code void}-valued execute method has just returned.
+     */
+    public abstract void returnVoid(Node node, VirtualFrame vFrame);
+
+    /**
+     * An AST Node's execute method has just returned a value (boxed if primitive).
+     */
+    public abstract void returnValue(Node node, VirtualFrame vFrame, Object result);
+
+    /**
+     * An AST Node's execute method has just thrown an exception.
+     */
+    public abstract void returnExceptional(Node node, VirtualFrame vFrame, Exception exception);
+
+    /**
+     * Gets the {@link Probe} that manages this chain of event handling.
+     */
+    public abstract Probe getProbe();
+
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Mon Sep 21 12:15:38 2015 -0700
@@ -27,15 +27,11 @@
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
 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.source.SourceSection;
 
-// TODO (mlvdv) these statics should not be global.  Move them to some kind of context.
-// TODO (mlvdv) migrate factory (together with Probe)? break out nested classes?
-
 /**
  * A <em>binding</em> between:
  * <ol>
@@ -47,117 +43,13 @@
  * Client-oriented documentation for the use of Instruments is available online at <a
  * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https://
  * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a>
- * <p>
- * The implementation of Instruments is complicated by the requirement that Truffle be able to clone
- * ASTs at any time. In particular, any instrumentation-supporting Nodes that have been attached to
- * an AST must be cloned along with the AST: AST clones are not permitted to share Nodes.
- * <p>
- * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is encouraged
- * by providing the {@link SimpleInstrumentListener} for clients that need know nothing more than
- * the properties associated with a Probe: it's {@link SourceSection} and any associated instances
- * of {@link SyntaxTag}.
- * <p>
- * AST cloning is <em>not transparent</em> to clients that use the
- * {@link StandardInstrumentListener}, since those event methods identify the concrete Node instance
- * (and thus the AST instance) where the event takes place.
- * <p>
- * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4>
- * <p>
- * <ul>
- * <li>A new Instrument is created in permanent association with a client-provided
- * <em>listener.</em></li>
- *
- * <li>Multiple Instruments may share a single listener.</li>
- *
- * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to a
- * Probe, at which time the Instrument begins routing execution events from the Probe's AST location
- * to the Instrument's listener.</li>
- *
- * <li>Neither Instruments nor Probes are {@link Node}s.</li>
- *
- * <li>A Probe has a single source-based location in an AST, but manages a separate
- * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li>
- * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is cloned
- * along with the rest of the AST.</li>
- *
- * <li>When a new Instrument (for example an instance created by
- * {@link Instrument#create(com.oracle.truffle.api.instrument.SimpleInstrumentListener, java.lang.String)}
- * is attached to a Probe, the Instrument inserts a new instance of its private Node type into
- * <em>each of the instrument chains</em> managed by the Probe, i.e. one node instance per existing
- * clone of the AST.</li>
- *
- * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the
- * Instrument's private Node type will be cloned along with the rest of the the AST.</li>
- * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the
- * shared (outer) Instrument instance; that state includes a reference to the Instrument's listener.
- * </li>
- *
- * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, the
- * Instrument searches every instrument chain associated with the Probe and removes the instance of
- * its private Node type.</li>
- *
- * <li>Attaching and disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of the
- * AST.</li>
- *
- * </ul>
  *
  * @see Probe
- * @see TruffleEvents
+ * @see EventHandlerNode
  */
 public abstract class Instrument {
 
     /**
-     * Creates a <em>Simple Instrument</em>: this Instrument routes execution events to a
-     * client-provided listener.
-     *
-     * @param listener a listener for execution events
-     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
-     * @return a new instrument, ready for attachment at a probe
-     */
-    public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) {
-        return new SimpleInstrument(listener, instrumentInfo);
-    }
-
-    /**
-     * Creates a <em>Standard Instrument</em>: this Instrument routes execution events, together
-     * with access to Truffle execution state, to a client-provided listener.
-     *
-     * @param standardListener a listener for execution events and execution state
-     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
-     * @return a new instrument, ready for attachment at a probe
-     */
-    public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) {
-        return new StandardInstrument(standardListener, instrumentInfo);
-    }
-
-    /**
-     * Creates an <em>Advanced Instrument</em>: this Instrument executes efficiently, subject to
-     * full Truffle optimization, a client-provided AST fragment every time the Probed node is
-     * entered.
-     * <p>
-     * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework
-     * and reported to the listener; there is no other notification.
-     *
-     * @param resultListener optional client callback for results/failure notification
-     * @param rootFactory provider of AST fragments on behalf of the client
-     * @param requiredResultType optional requirement, any non-assignable result is reported to the
-     *            the listener, if any, as a failure
-     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
-     * @return a new instrument, ready for attachment at a probe
-     */
-    public static Instrument create(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
-        return new AdvancedInstrument(resultListener, rootFactory, requiredResultType, instrumentInfo);
-    }
-
-    // TODO (mlvdv) experimental
-    /**
-     * For implementation testing.
-     */
-    public static Instrument create(TruffleOptListener listener) {
-        return new TruffleOptInstrument(listener, null);
-    }
-
-    /**
      * Has this instrument been disposed? stays true once set.
      */
     private boolean isDisposed = false;
@@ -169,6 +61,61 @@
      */
     private final String instrumentInfo;
 
+    /**
+     * <h4>Implementation Notes</h4>
+     * <p>
+     * The implementation of Instruments is complicated by the requirement that Truffle be able to
+     * clone ASTs at any time. In particular, any instrumentation-supporting Nodes that have been
+     * attached to an AST must be cloned along with the AST: AST clones are not permitted to share
+     * Nodes.
+     * <p>
+     * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is
+     * encouraged by providing the {@link SimpleInstrumentListener} for clients that need know
+     * nothing more than the properties associated with a Probe: its {@link SourceSection} and any
+     * associated instances of {@link SyntaxTag}.
+     * <p>
+     * AST cloning is <em>not transparent</em> to clients that use the
+     * {@link StandardInstrumentListener}, since those event methods identify the concrete Node
+     * instance (and thus the AST instance) where the event takes place.
+     * <p>
+     * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4>
+     * <p>
+     * <ul>
+     * <li>A new Instrument is created in permanent association with a client-provided
+     * <em>listener.</em></li>
+     *
+     * <li>Multiple Instruments may share a single listener.</li>
+     *
+     * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to
+     * a Probe, at which time the Instrument begins routing execution events from the Probe's AST
+     * location to the Instrument's listener.</li>
+     *
+     * <li>Neither Instruments nor Probes are {@link Node}s.</li>
+     *
+     * <li>A Probe has a single source-based location in an AST, but manages a separate
+     * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li>
+     * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is
+     * cloned along with the rest of the AST.</li>
+     *
+     * <li>When a new Instrument is attached to a Probe, the Instrument inserts a new instance of
+     * its private Node type into <em>each of the instrument chains</em> managed by the Probe, i.e.
+     * one node instance per existing clone of the AST.</li>
+     *
+     * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the
+     * Instrument's private Node type will be cloned along with the rest of the the AST.</li>
+     * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the
+     * shared (outer) Instrument instance; that state includes a reference to the Instrument's
+     * listener.</li>
+     *
+     * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed},
+     * the Instrument searches every instrument chain associated with the Probe and removes the
+     * instance of its private Node type.</li>
+     *
+     * <li>Attaching or disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of
+     * the AST.</li>
+     *
+     * </ul>
+     */
     private Instrument(String instrumentInfo) {
         this.instrumentInfo = instrumentInfo;
     }
@@ -223,14 +170,14 @@
     /**
      * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}.
      */
-    private static final class SimpleInstrument extends Instrument {
+    static final class SimpleInstrument extends Instrument {
 
         /**
          * Tool-supplied listener for events.
          */
         private final SimpleInstrumentListener simpleListener;
 
-        private SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) {
+        SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) {
             super(instrumentInfo);
             this.simpleListener = simpleListener;
         }
@@ -267,34 +214,39 @@
                 super(nextNode);
             }
 
+            @Override
             public void enter(Node node, VirtualFrame vFrame) {
-                SimpleInstrument.this.simpleListener.enter(SimpleInstrument.this.probe);
+                SimpleInstrument.this.simpleListener.onEnter(SimpleInstrument.this.probe);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
+            @Override
             public void returnVoid(Node node, VirtualFrame vFrame) {
-                SimpleInstrument.this.simpleListener.returnVoid(SimpleInstrument.this.probe);
+                SimpleInstrument.this.simpleListener.onReturnVoid(SimpleInstrument.this.probe);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
+            @Override
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                SimpleInstrument.this.simpleListener.returnValue(SimpleInstrument.this.probe, result);
+                SimpleInstrument.this.simpleListener.onReturnValue(SimpleInstrument.this.probe, result);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
+            @Override
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                SimpleInstrument.this.simpleListener.returnExceptional(SimpleInstrument.this.probe, exception);
+                SimpleInstrument.this.simpleListener.onReturnExceptional(SimpleInstrument.this.probe, exception);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
+            @Override
             public String instrumentationInfo() {
                 final String info = getInstrumentInfo();
                 return info != null ? info : simpleListener.getClass().getSimpleName();
@@ -305,14 +257,14 @@
     /**
      * An instrument that propagates events to an instance of {@link StandardInstrumentListener}.
      */
-    private static final class StandardInstrument extends Instrument {
+    static final class StandardInstrument extends Instrument {
 
         /**
          * Tool-supplied listener for AST events.
          */
         private final StandardInstrumentListener standardListener;
 
-        private StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) {
+        StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) {
             super(instrumentInfo);
             this.standardListener = standardListener;
         }
@@ -349,34 +301,39 @@
                 super(nextNode);
             }
 
+            @Override
             public void enter(Node node, VirtualFrame vFrame) {
-                standardListener.enter(StandardInstrument.this.probe, node, vFrame);
+                standardListener.onEnter(StandardInstrument.this.probe, node, vFrame);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.enter(node, vFrame);
                 }
             }
 
+            @Override
             public void returnVoid(Node node, VirtualFrame vFrame) {
-                standardListener.returnVoid(StandardInstrument.this.probe, node, vFrame);
+                standardListener.onReturnVoid(StandardInstrument.this.probe, node, vFrame);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
+            @Override
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                standardListener.returnValue(StandardInstrument.this.probe, node, vFrame, result);
+                standardListener.onReturnValue(StandardInstrument.this.probe, node, vFrame, result);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
+            @Override
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                standardListener.returnExceptional(StandardInstrument.this.probe, node, vFrame, exception);
+                standardListener.onReturnExceptional(StandardInstrument.this.probe, node, vFrame, exception);
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
+            @Override
             public String instrumentationInfo() {
                 final String info = getInstrumentInfo();
                 return info != null ? info : standardListener.getClass().getSimpleName();
@@ -389,13 +346,13 @@
      * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle
      * AST with potential for full optimization.
      */
-    private static final class AdvancedInstrument extends Instrument {
+    static final class AdvancedInstrument extends Instrument {
 
         private final AdvancedInstrumentResultListener resultListener;
         private final AdvancedInstrumentRootFactory rootFactory;
         private final Class<?> requiredResultType;
 
-        private AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
+        AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
             super(instrumentInfo);
             this.resultListener = resultListener;
             this.rootFactory = rootFactory;
@@ -436,6 +393,7 @@
                 super(nextNode);
             }
 
+            @Override
             public void enter(Node node, VirtualFrame vFrame) {
                 if (instrumentRoot == null) {
                     try {
@@ -447,7 +405,7 @@
                         }
                     } catch (RuntimeException ex) {
                         if (resultListener != null) {
-                            resultListener.notifyFailure(node, vFrame, ex);
+                            resultListener.onFailure(node, vFrame, ex);
                         }
                     }
                 }
@@ -456,11 +414,11 @@
                         final Object result = instrumentRoot.executeRoot(node, vFrame);
                         if (resultListener != null) {
                             checkResultType(result);
-                            resultListener.notifyResult(node, vFrame, result);
+                            resultListener.onExecution(node, vFrame, result);
                         }
                     } catch (RuntimeException ex) {
                         if (resultListener != null) {
-                            resultListener.notifyFailure(node, vFrame, ex);
+                            resultListener.onFailure(node, vFrame, ex);
                         }
                     }
                 }
@@ -491,24 +449,28 @@
                 return new RuntimeException("Instrument result " + result.toString() + " not assignable to " + requiredResultType.getSimpleName());
             }
 
+            @Override
             public void returnVoid(Node node, VirtualFrame vFrame) {
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
+            @Override
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
+            @Override
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
+            @Override
             public String instrumentationInfo() {
                 final String info = getInstrumentInfo();
                 return info != null ? info : rootFactory.getClass().getSimpleName();
@@ -516,10 +478,12 @@
         }
     }
 
+    // TODO (mlvdv) experimental
     public interface TruffleOptListener {
         void notifyIsCompiled(boolean isCompiled);
     }
 
+    @SuppressWarnings("unused")
     private static final class TruffleOptInstrument extends Instrument {
 
         private final TruffleOptListener toolOptListener;
@@ -561,6 +525,7 @@
                 this.isCompiled = CompilerDirectives.inCompiledCode();
             }
 
+            @Override
             public void enter(Node node, VirtualFrame vFrame) {
                 if (this.isCompiled != CompilerDirectives.inCompiledCode()) {
                     this.isCompiled = CompilerDirectives.inCompiledCode();
@@ -571,24 +536,28 @@
                 }
             }
 
+            @Override
             public void returnVoid(Node node, VirtualFrame vFrame) {
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnVoid(node, vFrame);
                 }
             }
 
+            @Override
             public void returnValue(Node node, VirtualFrame vFrame, Object result) {
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnValue(node, vFrame, result);
                 }
             }
 
+            @Override
             public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
                 if (nextInstrumentNode != null) {
                     nextInstrumentNode.returnExceptional(node, vFrame, exception);
                 }
             }
 
+            @Override
             public String instrumentationInfo() {
                 final String info = getInstrumentInfo();
                 return info != null ? info : toolOptListener.getClass().getSimpleName();
@@ -597,7 +566,7 @@
     }
 
     @NodeInfo(cost = NodeCost.NONE)
-    abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode {
+    abstract class AbstractInstrumentNode extends EventHandlerNode {
 
         @Child protected AbstractInstrumentNode nextInstrumentNode;
 
@@ -606,8 +575,8 @@
         }
 
         @Override
-        public boolean isInstrumentable() {
-            return false;
+        public Probe getProbe() {
+            return probe;
         }
 
         /**
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationException.java	Mon Sep 21 13:11:41 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 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.api.instrument;
-
-public class InstrumentationException extends Exception {
-
-    public InstrumentationException(RuntimeException ex) {
-        super(ex);
-    }
-
-    private static final long serialVersionUID = 447857066220935502L;
-
-}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,7 +24,6 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 
 /**
@@ -39,30 +38,4 @@
      */
     String instrumentationInfo();
 
-    /**
-     * Events that propagate through the {@linkplain InstrumentationNode implementation nodes} of
-     * the Instrumentation Framework, not visible in this form to Instrumentation clients.
-     */
-    interface TruffleEvents {
-
-        /**
-         * An AST node's execute method is about to be called.
-         */
-        void enter(Node node, VirtualFrame vFrame);
-
-        /**
-         * An AST Node's {@code void}-valued execute method has just returned.
-         */
-        void returnVoid(Node node, VirtualFrame vFrame);
-
-        /**
-         * An AST Node's execute method has just returned a value (boxed if primitive).
-         */
-        void returnValue(Node node, VirtualFrame vFrame, Object result);
-
-        /**
-         * An AST Node's execute method has just thrown an exception.
-         */
-        void returnExceptional(Node node, VirtualFrame vFrame, Exception exception);
-    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java	Mon Sep 21 13:11:41 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-/*
- * Copyright (c) 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.api.instrument;
-
-/**
- * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language
- * program execution.
- * <p>
- * Tools share a common <em>life cycle</em>:
- * <ul>
- * <li>A newly created tool is inert until {@linkplain #install() installed}.</li>
- * <li>An installed tool becomes <em>enabled</em> and immediately begins installing
- * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from
- * those instruments</li>
- * <li>A tool may only be installed once.</li>
- * <li>It should be possible to install multiple instances of a tool, possibly (but not necessarily)
- * configured differently with respect to what data is being collected.</li>
- * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) enabled and disabled}
- * arbitrarily.</li>
- * <li>A disabled tool:
- * <ul>
- * <li>Collects no data;</li>
- * <li>Retains existing AST instrumentation;</li>
- * <li>Continues to instrument newly created ASTs; and</li>
- * <li>Retains previously collected data.</li>
- * </ul>
- * </li>
- * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool
- * installed but with all previously collected data removed.</li>
- * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not
- * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data persists.</li>
- * </ul>
- * <p>
- * Tool-specific methods that access data collected by the tool should:
- * <ul>
- * <li>Return modification-safe representations of the data; and</li>
- * <li>Not change the state of the data.</li>
- * </ul>
- * <b>Note:</b><br>
- * Tool installation is currently <em>global</em> to the Truffle Execution environment. When
- * language-agnostic management of individual execution environments is added to the platform,
- * installation will be (optionally) specific to a single execution environment.
- */
-public abstract class InstrumentationTool {
-    // TODO (mlvdv) still thinking about the most appropriate name for this class of tools
-
-    private enum ToolState {
-
-        /** Not yet installed, inert. */
-        UNINSTALLED,
-
-        /** Installed, collecting data. */
-        ENABLED,
-
-        /** Installed, not collecting data. */
-        DISABLED,
-
-        /** Was installed, but now removed, inactive, and no longer usable. */
-        DISPOSED;
-    }
-
-    private ToolState toolState = ToolState.UNINSTALLED;
-
-    protected InstrumentationTool() {
-    }
-
-    /**
-     * Connect the tool to some part of the Truffle runtime, and enable data collection to start.
-     * Instrumentation will only be added to subsequently created ASTs.
-     *
-     * @throws IllegalStateException if the tool has previously been installed.
-     */
-    public final void install() {
-        checkUninstalled();
-        if (internalInstall()) {
-            toolState = ToolState.ENABLED;
-        }
-    }
-
-    /**
-     * @return whether the tool is currently collecting data.
-     */
-    public final boolean isEnabled() {
-        return toolState == ToolState.ENABLED;
-    }
-
-    /**
-     * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not
-     * collecting data, but keeping data already collected).
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void setEnabled(boolean isEnabled) {
-        checkInstalled();
-        internalSetEnabled(isEnabled);
-        toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED;
-    }
-
-    /**
-     * Clears any data already collected, but otherwise does not change the state of the tool.
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void reset() {
-        checkInstalled();
-        internalReset();
-    }
-
-    /**
-     * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data already
-     * collected.
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void dispose() {
-        checkInstalled();
-        internalDispose();
-        toolState = ToolState.DISPOSED;
-    }
-
-    /**
-     * @return whether the installation succeeded.
-     */
-    protected abstract boolean internalInstall();
-
-    /**
-     * No subclass action required.
-     *
-     * @param isEnabled
-     */
-    protected void internalSetEnabled(boolean isEnabled) {
-    }
-
-    protected abstract void internalReset();
-
-    protected abstract void internalDispose();
-
-    /**
-     * Ensure that the tool is currently installed.
-     *
-     * @throws IllegalStateException
-     */
-    private void checkInstalled() throws IllegalStateException {
-        if (toolState == ToolState.UNINSTALLED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed");
-        }
-        if (toolState == ToolState.DISPOSED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed");
-        }
-    }
-
-    /**
-     * Ensure that the tool has not yet been installed.
-     *
-     * @throws IllegalStateException
-     */
-    private void checkUninstalled() {
-        if (toolState != ToolState.UNINSTALLED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed");
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java	Mon Sep 21 12:15:38 2015 -0700
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 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.api.instrument;
+
+import java.io.PrintStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.impl.Accessor;
+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.source.SourceSection;
+
+/**
+ * Access to instrumentation services in an instance of {@link TruffleVM}.
+ */
+public final class Instrumenter {
+
+    private static final boolean TRACE = false;
+    private static final String TRACE_PREFIX = "Instrumenter: ";
+    private static final PrintStream OUT = System.out;
+
+    private static void trace(String format, Object... args) {
+        if (TRACE) {
+            OUT.println(TRACE_PREFIX + String.format(format, args));
+        }
+    }
+
+    /**
+     * 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;
+    }
+
+    private enum ToolState {
+
+        /** Not yet installed, inert. */
+        UNINSTALLED,
+
+        /** Installed, collecting data. */
+        ENABLED,
+
+        /** Installed, not collecting data. */
+        DISABLED,
+
+        /** Was installed, but now removed, inactive, and no longer usable. */
+        DISPOSED;
+    }
+
+    /**
+     * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language
+     * program execution.
+     * <p>
+     * Tools share a common <em>life cycle</em>:
+     * <ul>
+     * <li>A newly created tool is inert until {@linkplain #install(Instrumenter) installed}.</li>
+     * <li>An installed tool becomes <em>enabled</em> and immediately begins installing
+     * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from
+     * those instruments</li>
+     * <li>A tool may only be installed once.</li>
+     * <li>It should be possible to install multiple instances of a tool, possibly (but not
+     * necessarily) configured differently with respect to what data is being collected.</li>
+     * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) enabled and disabled}
+     * arbitrarily.</li>
+     * <li>A disabled tool:
+     * <ul>
+     * <li>Collects no data;</li>
+     * <li>Retains existing AST instrumentation;</li>
+     * <li>Continues to instrument newly created ASTs; and</li>
+     * <li>Retains previously collected data.</li>
+     * </ul>
+     * </li>
+     * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool
+     * installed but with all previously collected data removed.</li>
+     * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not
+     * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data
+     * persists.</li>
+     * </ul>
+     * <p>
+     * Tool-specific methods that access data collected by the tool should:
+     * <ul>
+     * <li>Return modification-safe representations of the data; and</li>
+     * <li>Not change the state of the data.</li>
+     * </ul>
+     * <b>Note:</b><br>
+     * Tool installation is currently <em>global</em> to the Truffle Execution environment. When
+     * language-agnostic management of individual execution environments is added to the platform,
+     * installation will be (optionally) specific to a single execution environment.
+     */
+    public abstract static class Tool {
+        // TODO (mlvdv) still thinking about the most appropriate name for this class of tools
+
+        private ToolState toolState = ToolState.UNINSTALLED;
+
+        private Instrumenter instrumenter;
+
+        protected Tool() {
+        }
+
+        /**
+         * Connect the tool to some part of the Truffle runtime, and enable data collection to
+         * start. Instrumentation will only be added to subsequently created ASTs.
+         *
+         * @throws IllegalStateException if the tool has previously been installed.
+         */
+        public final void install(Instrumenter inst) {
+            checkUninstalled();
+            this.instrumenter = inst;
+
+            if (internalInstall()) {
+                toolState = ToolState.ENABLED;
+            }
+            instrumenter.tools.add(this);
+        }
+
+        /**
+         * @return whether the tool is currently collecting data.
+         */
+        public final boolean isEnabled() {
+            return toolState == ToolState.ENABLED;
+        }
+
+        /**
+         * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not
+         * collecting data, but keeping data already collected).
+         *
+         * @throws IllegalStateException if not yet installed or disposed.
+         */
+        public final void setEnabled(boolean isEnabled) {
+            checkInstalled();
+            internalSetEnabled(isEnabled);
+            toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED;
+        }
+
+        /**
+         * Clears any data already collected, but otherwise does not change the state of the tool.
+         *
+         * @throws IllegalStateException if not yet installed or disposed.
+         */
+        public final void reset() {
+            checkInstalled();
+            internalReset();
+        }
+
+        /**
+         * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data
+         * already collected.
+         *
+         * @throws IllegalStateException if not yet installed or disposed.
+         */
+        public final void dispose() {
+            checkInstalled();
+            internalDispose();
+            toolState = ToolState.DISPOSED;
+            instrumenter.tools.remove(this);
+        }
+
+        /**
+         * @return whether the installation succeeded.
+         */
+        protected abstract boolean internalInstall();
+
+        /**
+         * No subclass action required.
+         *
+         * @param isEnabled
+         */
+        protected void internalSetEnabled(boolean isEnabled) {
+        }
+
+        protected abstract void internalReset();
+
+        protected abstract void internalDispose();
+
+        protected final Instrumenter getInstrumenter() {
+            return instrumenter;
+        }
+
+        /**
+         * Ensure that the tool is currently installed.
+         *
+         * @throws IllegalStateException
+         */
+        private void checkInstalled() throws IllegalStateException {
+            if (toolState == ToolState.UNINSTALLED) {
+                throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed");
+            }
+            if (toolState == ToolState.DISPOSED) {
+                throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed");
+            }
+        }
+
+        /**
+         * Ensure that the tool has not yet been installed.
+         *
+         * @throws IllegalStateException
+         */
+        private void checkUninstalled() {
+            if (toolState != ToolState.UNINSTALLED) {
+                throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed");
+            }
+        }
+    }
+
+    private final Object vm;
+
+    /** Tools that have been created, but not yet disposed. */
+    private final Set<Tool> tools = Collections.synchronizedSet(new LinkedHashSet<Tool>());
+
+    private final Set<ASTProber> astProbers = Collections.synchronizedSet(new LinkedHashSet<ASTProber>());
+
+    private final List<ProbeListener> probeListeners = new ArrayList<>();
+
+    /**
+     * All Probes that have been created.
+     */
+    private final List<WeakReference<Probe>> probes = new ArrayList<>();
+
+    /**
+     * A global trap that triggers notification just before executing any Node that is Probed with a
+     * matching tag.
+     */
+    @CompilationFinal private SyntaxTagTrap beforeTagTrap = null;
+
+    /**
+     * A global trap that triggers notification just after executing any Node that is Probed with a
+     * matching tag.
+     */
+    @CompilationFinal private SyntaxTagTrap afterTagTrap = null;
+
+    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;
+        }
+    }
+
+    Instrumenter(Object vm) {
+        this.vm = vm;
+    }
+
+    /**
+     * Returns {@code true} if the AST 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 AST nodes that are {@linkplain #isInstrumentable() instrumentable}, returns a
+     * <em>wrapper node</em> that:
+     * <ol>
+     * <li>implements {@link WrapperNode}</li>
+     * <li>has the node as its single 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);
+    }
+
+    /**
+     * Prepares an AST node for {@linkplain Instrument instrumentation}, where the node is presumed
+     * to be part of a well-formed Truffle AST that has not yet been executed.
+     * <p>
+     * <em>Probing</em> a node is idempotent:
+     * <ul>
+     * <li>If the node has not been Probed, modifies the AST by first inserting a
+     * {@linkplain #createWrapperNode(Node) wrapper node} between the node and its parent and then
+     * returning the newly created Probe associated with the wrapper.</li>
+     * <li>If the node has been Probed, returns the Probe associated with its existing wrapper.</li>
+     * <li>No more than one {@link Probe} may be associated with a node, so a wrapper may not wrap
+     * another wrapper.</li>
+     * </ul>
+     * It is a runtime error to attempt Probing an AST node with no parent.
+     *
+     * @return a (possibly newly created) {@link Probe} associated with this node.
+     * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged
+     */
+    @SuppressWarnings("rawtypes")
+    public Probe probe(Node node) {
+
+        final Node parent = node.getParent();
+
+        if (node instanceof WrapperNode) {
+            throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, node, null);
+        }
+
+        if (parent == null) {
+            throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, node, null);
+        }
+
+        if (parent instanceof WrapperNode) {
+            final WrapperNode wrapper = (WrapperNode) parent;
+            if (TRACE) {
+                final Probe probe = wrapper.getProbe();
+                final SourceSection sourceSection = wrapper.getChild().getSourceSection();
+                final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription();
+                trace("PROBE FOUND %s %s %s", "Probe@", location, probe.getTagsDescription());
+            }
+            return wrapper.getProbe();
+        }
+
+        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 = createWrapperNode(node);
+
+        if (wrapper == null || !(wrapper instanceof Node)) {
+            throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, node, wrapper);
+        }
+
+        final Node wrapperNode = (Node) wrapper;
+
+        if (!node.isSafelyReplaceableBy(wrapperNode)) {
+            throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, node, wrapper);
+        }
+
+        final SourceSection sourceSection = wrapper.getChild().getSourceSection();
+        final ProbeNode probeNode = new ProbeNode();
+        Class<? extends TruffleLanguage> l = ACCESSOR.findLanguage(wrapper.getChild().getRootNode());
+        final Probe probe = new Probe(this, l, probeNode, sourceSection);
+        probes.add(new WeakReference<>(probe));
+        probeNode.probe = probe;  // package private access
+        wrapper.insertEventHandlerNode(probeNode);
+        node.replace(wrapperNode);
+        if (TRACE) {
+            final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription();
+            trace("PROBED %s %s %s", "Probe@", location, probe.getTagsDescription());
+        }
+        for (ProbeListener listener : probeListeners) {
+            listener.newProbeInserted(probe);
+        }
+        return probe;
+    }
+
+    /**
+     * 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 listener) {
+        probeListeners.remove(listener);
+    }
+
+    /**
+     * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of
+     * probes 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> 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;
+    }
+
+    // TODO (mlvdv) generalize to permit multiple "before traps" without a performance hit?
+    /**
+     * Sets the current "<em>before</em> tag trap"; there can be no more than one in effect.
+     * <ul>
+     * <li>The before-trap triggers a callback just <strong><em>before</em></strong> execution
+     * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created)
+     * with the specified {@link SyntaxTag}.</li>
+     * <li>Setting the before-trap to {@code null} clears an existing before-trap.</li>
+     * <li>Setting a non{@code -null} before-trap when one is already set clears the previously set
+     * before-trap.</li>
+     * </ul>
+     *
+     * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set.
+     */
+    public void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) {
+        beforeTagTrap = newBeforeTagTrap;
+        for (WeakReference<Probe> ref : probes) {
+            final Probe probe = ref.get();
+            if (probe != null) {
+                probe.notifyTrapsChanged();
+            }
+        }
+    }
+
+    // TODO (mlvdv) generalize to permit multiple "after traps" without a performance hit?
+    /**
+     * Sets the current "<em>after</em> tag trap"; there can be no more than one in effect.
+     * <ul>
+     * <li>The after-trap triggers a callback just <strong><em>after</em></strong> execution leaves
+     * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with
+     * the specified {@link SyntaxTag}.</li>
+     * <li>Setting the after-trap to {@code null} clears an existing after-trap.</li>
+     * <li>Setting a non{@code -null} after-trap when one is already set clears the previously set
+     * after-trap.</li>
+     * </ul>
+     *
+     * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set.
+     */
+    public void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) {
+        afterTagTrap = newAfterTagTrap;
+        for (WeakReference<Probe> ref : probes) {
+            final Probe probe = ref.get();
+            if (probe != null) {
+                probe.notifyTrapsChanged();
+            }
+        }
+    }
+
+    /**
+     * Enables instrumentation at selected nodes in all subsequently constructed ASTs. Ignored if
+     * the argument is already registered, runtime error if argument is {@code null}.
+     */
+    public void registerASTProber(ASTProber prober) {
+        if (prober == null) {
+            throw new IllegalArgumentException("Register non-null ASTProbers");
+        }
+        astProbers.add(prober);
+    }
+
+    public void unregisterASTProber(ASTProber prober) {
+        astProbers.remove(prober);
+    }
+
+    /**
+     * <em>Attaches</em> a {@link SimpleInstrumentListener listener} to a {@link Probe}, creating a
+     * <em>binding</em> called an {@link Instrument}. Until the Instrument is
+     * {@linkplain Instrument#dispose() disposed}, it routes synchronous notification of
+     * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to
+     * the listener.
+     *
+     * @param probe source of execution events
+     * @param listener receiver of execution events
+     * @param instrumentInfo optional documentation about the Instrument
+     * @return a handle for access to the binding
+     */
+    @SuppressWarnings("static-method")
+    public Instrument attach(Probe probe, SimpleInstrumentListener listener, String instrumentInfo) {
+        final Instrument instrument = new Instrument.SimpleInstrument(listener, instrumentInfo);
+        probe.attach(instrument);
+        return instrument;
+    }
+
+    /**
+     * <em>Attaches</em> a {@link StandardInstrumentListener listener} to a {@link Probe}, creating
+     * a <em>binding</em> called an {@link Instrument}. Until the Instrument is
+     * {@linkplain Instrument#dispose() disposed}, it routes synchronous notification of
+     * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to
+     * the listener.
+     *
+     * @param probe source of execution events
+     * @param listener receiver of execution events
+     * @param instrumentInfo optional documentation about the Instrument
+     * @return a handle for access to the binding
+     */
+    @SuppressWarnings("static-method")
+    public Instrument attach(Probe probe, StandardInstrumentListener listener, String instrumentInfo) {
+        final Instrument instrument = new Instrument.StandardInstrument(listener, instrumentInfo);
+        probe.attach(instrument);
+        return instrument;
+    }
+
+    /**
+     * <em>Attaches</em> a {@link AdvancedInstrumentResultListener listener} to a {@link Probe},
+     * creating a <em>binding</em> called an {@link Instrument}. Until the Instrument is
+     * {@linkplain Instrument#dispose() disposed}, it routes synchronous notification of
+     * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to
+     * the listener.
+     * <p>
+     * This Instrument executes efficiently, subject to full Truffle optimization, a client-provided
+     * AST fragment every time the Probed node is entered.
+     * <p>
+     * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework
+     * and reported to the listener; there is no other notification.
+     *
+     * @param probe probe source of execution events
+     * @param listener optional client callback for results/failure notification
+     * @param rootFactory provider of AST fragments on behalf of the client
+     * @param requiredResultType optional requirement, any non-assignable result is reported to the
+     *            the listener, if any, as a failure
+     * @param instrumentInfo instrumentInfo optional documentation about the Instrument
+     * @return a handle for access to the binding
+     */
+    @SuppressWarnings("static-method")
+    public Instrument attach(Probe probe, AdvancedInstrumentResultListener listener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
+        final Instrument instrument = new Instrument.AdvancedInstrument(listener, rootFactory, requiredResultType, instrumentInfo);
+        probe.attach(instrument);
+        return instrument;
+    }
+
+    @SuppressWarnings("unused")
+    void executionStarted(Source s) {
+    }
+
+    void executionEnded() {
+    }
+
+    void tagAdded(Probe probe, SyntaxTag tag, Object tagValue) {
+        for (ProbeListener listener : probeListeners) {
+            listener.probeTaggedAs(probe, tag, tagValue);
+        }
+    }
+
+    SyntaxTagTrap getBeforeTagTrap() {
+        return beforeTagTrap;
+    }
+
+    SyntaxTagTrap getAfterTagTrap() {
+        return afterTagTrap;
+    }
+
+    // TODO (mlvdv) build this in as a VM event?
+    /**
+     * Enables instrumentation in a newly created AST by applying all registered instances of
+     * {@link ASTProber}.
+     */
+    private void applyInstrumentation(Node node) {
+
+        String name = "<?>";
+        final Source source = findSource(node);
+        if (source != null) {
+            name = source.getShortName();
+        } else {
+            final SourceSection sourceSection = node.getEncapsulatingSourceSection();
+            if (sourceSection != null) {
+                name = sourceSection.getShortDescription();
+            }
+        }
+        trace("START %s", name);
+        for (ProbeListener listener : probeListeners) {
+            listener.startASTProbing(source);
+        }
+        for (ASTProber prober : astProbers) {
+            prober.probeAST(this, node);  // TODO (mlvdv)
+        }
+        for (ProbeListener listener : probeListeners) {
+            listener.endASTProbing(source);
+        }
+        trace("FINISHED %s", name);
+    }
+
+    static final class AccessorInstrument extends Accessor {
+
+        @Override
+        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")
+        @Override
+        protected Class<? extends TruffleLanguage> findLanguage(RootNode n) {
+            return super.findLanguage(n);
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected Class<? extends TruffleLanguage> findLanguage(Probe probe) {
+            return probe.getLanguage();
+        }
+
+        @Override
+        protected void applyInstrumentation(Node node) {
+            super.getInstrumenter(null).applyInstrumentation(node);
+        }
+    }
+
+    static final AccessorInstrument ACCESSOR = new AccessorInstrument();
+
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,18 +24,6 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import com.oracle.truffle.api.Assumption;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.impl.Accessor;
-import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
-import com.oracle.truffle.api.nodes.InvalidAssumptionException;
-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.source.SourceSection;
-import com.oracle.truffle.api.utilities.CyclicAssumption;
 import java.io.PrintStream;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -43,65 +31,32 @@
 import java.util.Collections;
 import java.util.List;
 
-//TODO (mlvdv) these statics should not be global.  Move them to some kind of context.
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.nodes.InvalidAssumptionException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.api.utilities.CyclicAssumption;
 
 /**
  * A <em>binding</em> between:
  * <ol>
- * <li>A program location in an executing Truffle AST (corresponding to a {@link SourceSection}),
- * and</li>
- * <li>A dynamically managed collection of "attached" {@linkplain Instrument Instruments} that
- * receive event notifications on behalf of external clients.</li>
+ * <li>A <em>guest language program location</em> in an executing Truffle AST (corresponding to a
+ * {@link SourceSection}), and</li>
+ * <li>A dynamically managed collection of <em>attached</em> {@linkplain Instrument Instruments}
+ * that receive event notifications from the Probe's AST location on behalf of external clients.</li>
  * </ol>
+ * <strong>Note</strong>:The relationship must be with an AST <em>location</em>, not a specific
+ * {@link Node}, because ASTs are routinely <em>cloned</em> at runtime. An AST <em>location</em> is
+ * best represented as the {@link SourceSection} from which the original AST Node was created.
  * <p>
  * Client-oriented documentation for the use of Probes is available online at <a
  * HREF="https://wiki.openjdk.java.net/display/Graal/Finding+Probes" >https://wiki.openjdk.java.
  * net/display/Graal/Finding+Probes</a>
  * <p>
- * <h4>Implementation notes:</h4>
- * <p>
- * <ul>
- * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a
- * particular {@link SourceSection}, even though:
- * <ul>
- * <li>that location is represented in an AST as a {@link Node}, which might be replaced through
- * optimizations such as specialization, and</li>
- * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by multiple
- * Nodes in multiple ASTs.</li>
- * </ul>
- * </li>
  *
- * <li>The effect of the binding is to intercept {@linkplain TruffleEvents execution events}
- * arriving at the "probed" AST Node and notify each attached {@link Instrument} before execution is
- * allowed to proceed to the child and again after execution completes.</li>
- *
- * <li>The method {@link Node#probe()} creates a Probe on an AST Node; redundant calls return the
- * same Probe.</li>
- *
- * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers
- * correctly assigned), but before any cloning or executions. This is done by creating an instance
- * of {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}. Once
- * registered, it will be applied automatically to every newly created AST.</li>
- *
- * <li>The "probing" of an AST Node is implemented by insertion of a {@link ProbeNode.WrapperNode}
- * into the AST (as new parent of the Node being probed), together with an associated
- * {@link ProbeNode} that routes execution events at the probed Node to all the
- * {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li>
- *
- * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well,
- * together with their attached instrument chains. Each Probe instance intercepts cloning events and
- * keeps track of all AST copies.</li>
- *
- * <li>All attached {@link InstrumentationNode}s effectively become part of the running program:
- * <ul>
- * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle optimization.</li>
- * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with any
- * Truffle optimizations.</li>
- * </ul>
- * </li>
- *
- * </ul>
- *
+ * @see Instrumenter
  * @see Instrument
  * @see ASTProber
  * @see ProbeListener
@@ -121,173 +76,7 @@
         }
     }
 
-    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<>();
-
-    /**
-     * A global trap that triggers notification just before executing any Node that is Probed with a
-     * matching tag.
-     */
-    @CompilationFinal private static SyntaxTagTrap beforeTagTrap = null;
-
-    /**
-     * A global trap that triggers notification just after executing any Node that is Probed with a
-     * matching tag.
-     */
-    @CompilationFinal private static SyntaxTagTrap afterTagTrap = null;
-
-    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;
-    }
-
-    /**
-     * 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) {
-
-        String name = "<?>";
-        final Source source = findSource(node);
-        if (source != null) {
-            name = source.getShortName();
-        } else {
-            final SourceSection sourceSection = node.getEncapsulatingSourceSection();
-            if (sourceSection != null) {
-                name = sourceSection.getShortDescription();
-            }
-        }
-        trace("START %s", name);
-        for (ProbeListener listener : probeListeners) {
-            listener.startASTProbing(source);
-        }
-        for (ASTProber prober : astProbers) {
-            prober.probeAST(node);
-        }
-        for (ProbeListener listener : probeListeners) {
-            listener.endASTProbing(source);
-        }
-        trace("FINISHED %s", name);
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of
-     * probes if the specified tag is {@code null}.
-     *
-     * @return A collection of probes containing the given tag.
-     */
-    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;
-    }
-
-    // TODO (mlvdv) generalize to permit multiple "before traps" without a performance hit?
-    /**
-     * Sets the current "<em>before</em> tag trap"; there can be no more than one in effect.
-     * <ul>
-     * <li>The before-trap triggers a callback just <strong><em>before</em></strong> execution
-     * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created)
-     * with the specified {@link SyntaxTag}.</li>
-     * <li>Setting the before-trap to {@code null} clears an existing before-trap.</li>
-     * <li>Setting a non{@code -null} before-trap when one is already set clears the previously set
-     * before-trap.</li>
-     * </ul>
-     *
-     * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set.
-     */
-    public static void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) {
-        beforeTagTrap = newBeforeTagTrap;
-        for (WeakReference<Probe> ref : probes) {
-            final Probe probe = ref.get();
-            if (probe != null) {
-                probe.notifyTrapsChanged();
-            }
-        }
-    }
-
-    // TODO (mlvdv) generalize to permit multiple "after traps" without a performance hit?
-    /**
-     * Sets the current "<em>after</em> tag trap"; there can be no more than one in effect.
-     * <ul>
-     * <li>The after-trap triggers a callback just <strong><em>after</em></strong> execution leaves
-     * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with
-     * the specified {@link SyntaxTag}.</li>
-     * <li>Setting the after-trap to {@code null} clears an existing after-trap.</li>
-     * <li>Setting a non{@code -null} after-trap when one is already set clears the previously set
-     * after-trap.</li>
-     * </ul>
-     *
-     * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set.
-     */
-    public static void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) {
-        afterTagTrap = newAfterTagTrap;
-        for (WeakReference<Probe> ref : probes) {
-            final Probe probe = ref.get();
-            if (probe != null) {
-                probe.notifyTrapsChanged();
-            }
-        }
-    }
-
+    private final Instrumenter instrumenter;
     private final SourceSection sourceSection;
     private final ArrayList<SyntaxTag> tags = new ArrayList<>();
     private final List<WeakReference<ProbeNode>> probeNodeClones = new ArrayList<>();
@@ -311,39 +100,60 @@
     @CompilationFinal private boolean isAfterTrapActive = false;
 
     /**
-     * Intended for use only by {@link ProbeNode}.
+     * Constructor for use only by {@link ProbeNode}.
+     * <p>
+     * <h4>Probe Implementation notes:</h4>
+     * <p>
+     * <ul>
+     * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a
+     * particular {@link SourceSection}, even though:
+     * <ul>
+     * <li>that location is represented in an AST as a {@link Node}, which might be replaced through
+     * optimizations such as specialization, and</li>
+     * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by
+     * multiple Nodes in multiple ASTs.</li>
+     * </ul>
+     * </li>
+     * <li>The effect of the binding is to intercept {@linkplain EventHandlerNode execution events}
+     * arriving at the "probed" AST Node and notify each attached {@link Instrument} before
+     * execution is allowed to proceed to the child and again after execution completes.</li>
+     *
+     * <li>The method {@link Instrumenter#probe(Node)} creates a Probe on an AST Node; redundant
+     * calls return the same Probe.</li>
+     *
+     * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent
+     * pointers correctly assigned), but before any cloning or executions. This is done by applying
+     * instances of {@link ASTProber} provided by each language implementation, combined with any
+     * instances registered by tools via {@link Instrumenter#registerASTProber(ASTProber)}. Once
+     * registered, these will be applied automatically to every newly created AST.</li>
+     *
+     * <li>The "probing" of an AST Node is implemented by insertion of a
+     * {@link ProbeNode.WrapperNode} into the AST (as new parent of the Node being probed), together
+     * with an associated {@link ProbeNode} that routes execution events at the probed Node to all
+     * the {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li>
+     *
+     * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well,
+     * together with their attached instrument chains. Each Probe instance intercepts cloning events
+     * and keeps track of all AST copies.</li>
+     *
+     * <li>All attached {@link InstrumentationNode}s effectively become part of the running program:
+     * <ul>
+     * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle
+     * optimization.</li>
+     * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with
+     * any Truffle optimizations.</li>
+     * </ul>
+     * </li>
+     * </ul>
      */
-    Probe(Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) {
+    Probe(Instrumenter instrumenter, Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) {
+        this.instrumenter = instrumenter;
         this.sourceSection = sourceSection;
-        probes.add(new WeakReference<>(this));
         registerProbeNodeClone(probeNode);
-        if (TRACE) {
-            final String location = this.sourceSection == null ? "<unknown>" : sourceSection.getShortDescription();
-            trace("ADDED %s %s %s", "Probe@", location, getTagsDescription());
-        }
-        for (ProbeListener listener : probeListeners) {
-            listener.newProbeInserted(this);
-        }
         this.language = l;
     }
 
     /**
-     * Is this node tagged as belonging to a particular human-sensible category of language
-     * constructs?
-     */
-    public boolean isTaggedAs(SyntaxTag tag) {
-        assert tag != null;
-        return tags.contains(tag);
-    }
-
-    /**
-     * In which user-sensible categories has this node been tagged (<em>empty set</em> if none).
-     */
-    public Collection<SyntaxTag> getSyntaxTags() {
-        return Collections.unmodifiableCollection(tags);
-    }
-
-    /**
      * Adds a {@linkplain SyntaxTag tag} to the set of tags associated with this {@link Probe};
      * {@code no-op} if already in the set.
      */
@@ -351,16 +161,16 @@
         assert tag != null;
         if (!tags.contains(tag)) {
             tags.add(tag);
-            for (ProbeListener listener : probeListeners) {
-                listener.probeTaggedAs(this, tag, tagValue);
-            }
+            instrumenter.tagAdded(this, tag, tagValue);
 
             // Update the status of this Probe with respect to global tag traps
             boolean tagTrapsChanged = false;
+            final SyntaxTagTrap beforeTagTrap = instrumenter.getBeforeTagTrap();
             if (beforeTagTrap != null && tag == beforeTagTrap.getTag()) {
                 this.isBeforeTrapActive = true;
                 tagTrapsChanged = true;
             }
+            final SyntaxTagTrap afterTagTrap = instrumenter.getAfterTagTrap();
             if (afterTagTrap != null && tag == afterTagTrap.getTag()) {
                 this.isAfterTrapActive = true;
                 tagTrapsChanged = true;
@@ -375,12 +185,29 @@
     }
 
     /**
+     * Is the <em>Probed node</em> tagged as belonging to a particular human-sensible category of
+     * language constructs?
+     */
+    public boolean isTaggedAs(SyntaxTag tag) {
+        assert tag != null;
+        return tags.contains(tag);
+    }
+
+    /**
+     * In which user-sensible categories has the <em>Probed node</em> been tagged (
+     * <em>empty set</em> if none).
+     */
+    public Collection<SyntaxTag> getSyntaxTags() {
+        return Collections.unmodifiableCollection(tags);
+    }
+
+    /**
      * Adds instrumentation at this Probe.
      *
      * @param instrument an instrument not yet attached to a probe
      * @throws IllegalStateException if the instrument has ever been attached before
      */
-    public void attach(Instrument instrument) throws IllegalStateException {
+    void attach(Instrument instrument) throws IllegalStateException {
         if (instrument.isDisposed()) {
             throw new IllegalStateException("Attempt to attach disposed instrument");
         }
@@ -398,8 +225,8 @@
     }
 
     /**
-     * Gets the {@link SourceSection} associated with the Guest Language AST node being
-     * instrumented, possibly {@code null}.
+     * Gets the {@link SourceSection} associated with the <en>Probed AST node</em>, possibly
+     * {@code null}.
      */
     public SourceSection getProbedSourceSection() {
         return sourceSection;
@@ -439,23 +266,27 @@
     /**
      * Gets the currently active <strong><em>before</em></strong> {@linkplain SyntaxTagTrap Tag
      * Trap} at this Probe. Non{@code -null} if the global
-     * {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Before Tag Trap} is set and if this Probe
-     * holds the {@link SyntaxTag} specified in the trap.
+     * {@linkplain Instrumenter#setBeforeTagTrap(SyntaxTagTrap) Before Tag Trap} is set and if this
+     * Probe holds the {@link SyntaxTag} specified in the trap.
      */
     SyntaxTagTrap getBeforeTrap() {
         checkProbeUnchanged();
-        return isBeforeTrapActive ? beforeTagTrap : null;
+        return isBeforeTrapActive ? instrumenter.getBeforeTagTrap() : null;
     }
 
     /**
      * Gets the currently active <strong><em>after</em></strong> {@linkplain SyntaxTagTrap Tag Trap}
      * at this Probe. Non{@code -null} if the global
-     * {@linkplain Probe#setAfterTagTrap(SyntaxTagTrap) After Tag Trap} is set and if this Probe
-     * holds the {@link SyntaxTag} specified in the trap.
+     * {@linkplain Instrumenter#setAfterTagTrap(SyntaxTagTrap) After Tag Trap} is set and if this
+     * Probe holds the {@link SyntaxTag} specified in the trap.
      */
     SyntaxTagTrap getAfterTrap() {
         checkProbeUnchanged();
-        return isAfterTrapActive ? afterTagTrap : null;
+        return isAfterTrapActive ? instrumenter.getAfterTagTrap() : null;
+    }
+
+    Class<? extends TruffleLanguage> getLanguage() {
+        return language;
     }
 
     /**
@@ -476,13 +307,15 @@
         probeStateUnchangedCyclic.invalidate();
     }
 
-    private void notifyTrapsChanged() {
+    void notifyTrapsChanged() {
+        final SyntaxTagTrap beforeTagTrap = instrumenter.getBeforeTagTrap();
         this.isBeforeTrapActive = beforeTagTrap != null && this.isTaggedAs(beforeTagTrap.getTag());
+        final SyntaxTagTrap afterTagTrap = instrumenter.getAfterTagTrap();
         this.isAfterTrapActive = afterTagTrap != null && this.isTaggedAs(afterTagTrap.getTag());
         invalidateProbeUnchanged();
     }
 
-    private String getTagsDescription() {
+    String getTagsDescription() {
         final StringBuilder sb = new StringBuilder();
         sb.append("[");
         String prefix = "";
@@ -494,18 +327,4 @@
         sb.append("]");
         return sb.toString();
     }
-
-    static final class AccessorInstrument extends Accessor {
-        @Override
-        protected Class<? extends TruffleLanguage> findLanguage(RootNode n) {
-            return super.findLanguage(n);
-        }
-
-        @Override
-        protected Class<? extends TruffleLanguage> findLanguage(Probe probe) {
-            return probe.language;
-        }
-    }
-
-    static final AccessorInstrument ACCESSOR = new AccessorInstrument();
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java	Mon Sep 21 12:15:38 2015 -0700
@@ -28,7 +28,8 @@
 import com.oracle.truffle.api.nodes.Node;
 
 /**
- * An exception thrown when {@link Node#probe()} fails because of an implementation failure.
+ * An exception thrown when {@link Instrumenter#probe(Node)} fails because of an implementation
+ * failure.
  * <p>
  * Language and tool implementations should ensure that clients of tools never see this exception.
  */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,7 +24,6 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeFieldAccessor;
 import com.oracle.truffle.api.nodes.NodeUtil;
@@ -78,7 +77,7 @@
     private final Object wrapper;
 
     /**
-     * Description of an internal failure of {@link Node#probe()}.
+     * Description of an internal failure of {@link Instrumenter#probe(Node)}.
      *
      * @param reason what caused the failure
      * @param parent the parent, if known, of the child being probed
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java	Mon Sep 21 12:15:38 2015 -0700
@@ -41,10 +41,11 @@
     void startASTProbing(Source source);
 
     /**
-     * Notifies that a {@link Probe} has been newly attached to an AST via {@link Node#probe()}.
+     * Notifies that a {@link Probe} has been newly attached to an AST via
+     * {@link Instrumenter#probe(Node)}.
      * <p>
      * There can be no more than one {@link Probe} at a node; this notification will only be
-     * delivered the first time {@linkplain Node#probe() probe()} is called at a particular AST
+     * delivered the first time {@linkplain Instrumenter#probe(Node)} is called at a particular AST
      * node. There will also be no notification when the AST to which the Probe is attached is
      * cloned.
      */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -26,14 +26,11 @@
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.Instrument.AbstractInstrumentNode;
-import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
 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.source.SourceSection;
 
 /**
  * Implementation class & interface for enabling the attachment of {@linkplain Probe Probes} to
@@ -50,85 +47,7 @@
  * clones consistent.
  */
 @NodeInfo(cost = NodeCost.NONE)
-public final class ProbeNode extends Node implements TruffleEvents, InstrumentationNode {
-
-    /**
-     * 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}.
-     * <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>
-     *
-     * @see Instrument
-     */
-    public interface WrapperNode extends InstrumentationNode {
-
-        /**
-         * Gets the node being "wrapped", i.e. the AST node for which
-         * {@linkplain InstrumentationNode.TruffleEvents execution events} will be reported through
-         * the Instrumentation Framework.
-         */
-        Node getChild();
-
-        /**
-         * Gets the {@link Probe} responsible for installing this wrapper.
-         */
-        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}.
-     */
-    @SuppressWarnings("rawtypes")
-    public static Probe insertProbe(WrapperNode wrapper) {
-        final SourceSection sourceSection = wrapper.getChild().getSourceSection();
-        final ProbeNode probeNode = new ProbeNode(); // private constructor
-        Class<? extends TruffleLanguage> l = Probe.ACCESSOR.findLanguage(wrapper.getChild().getRootNode());
-        probeNode.probe = new Probe(l, probeNode, sourceSection);  // package private access
-        wrapper.insertProbe(probeNode);
-        return probeNode.probe;
-    }
+final class ProbeNode extends EventHandlerNode {
 
     // Never changed once set.
     @CompilationFinal Probe probe = null;
@@ -138,11 +57,6 @@
     @Child protected AbstractInstrumentNode firstInstrumentNode;
 
     @Override
-    public boolean isInstrumentable() {
-        return false;
-    }
-
-    @Override
     public Node copy() {
         Node node = super.copy();
         probe.registerProbeNodeClone((ProbeNode) node);
@@ -152,10 +66,12 @@
     /**
      * @return the {@link Probe} permanently associated with this {@link ProbeNode}.
      */
+    @Override
     public Probe getProbe() {
         return probe;
     }
 
+    @Override
     public void enter(Node node, VirtualFrame vFrame) {
         this.probe.checkProbeUnchanged();
         final SyntaxTagTrap beforeTagTrap = probe.getBeforeTrap();
@@ -167,6 +83,7 @@
         }
     }
 
+    @Override
     public void returnVoid(Node node, VirtualFrame vFrame) {
         this.probe.checkProbeUnchanged();
         if (firstInstrumentNode != null) {
@@ -178,6 +95,7 @@
         }
     }
 
+    @Override
     public void returnValue(Node node, VirtualFrame vFrame, Object result) {
         this.probe.checkProbeUnchanged();
         if (firstInstrumentNode != null) {
@@ -189,6 +107,7 @@
         }
     }
 
+    @Override
     public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
         this.probe.checkProbeUnchanged();
         if (firstInstrumentNode != null) {
@@ -200,6 +119,7 @@
         }
     }
 
+    @Override
     public String instrumentationInfo() {
         return "Standard probe";
     }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java	Mon Sep 21 12:15:38 2015 -0700
@@ -27,7 +27,7 @@
 import com.oracle.truffle.api.source.SourceSection;
 
 /**
- * A receiver of Truffle execution events that can act on behalf of an external client.
+ * A receiver of Truffle AST execution events that can act on behalf of an external client.
  * <p>
  * The {@link Probe} instance provides access to the {@link SourceSection} associated with the
  * event, as well as any {@link SyntaxTag}s that have been applied at that program's location.
@@ -39,6 +39,9 @@
  * Clients are free, of course, to record additional information in the listener implementation that
  * carries additional information about the context and reason for the particular {@link Instrument}
  * that is to be created from the listener.
+ * <p>
+ * Notification is fully synchronous, so overrides have performance implications. Non-trivial
+ * methods should be coded with Truffle guidelines and cautions in mind.
  */
 public interface SimpleInstrumentListener {
 
@@ -47,7 +50,7 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void enter(Probe probe);
+    void onEnter(Probe probe);
 
     /**
      * Receive notification that a program location's {@code void}-valued execution has just
@@ -55,7 +58,7 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnVoid(Probe probe);
+    void onReturnVoid(Probe probe);
 
     /**
      * Receive notification that a program location's execution has just completed and returned a
@@ -63,12 +66,12 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnValue(Probe probe, Object result);
+    void onReturnValue(Probe probe, Object result);
 
     /**
      * Receive notification that a program location's execution has just thrown an exception.
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnExceptional(Probe probe, Exception exception);
+    void onReturnExceptional(Probe probe, Exception exception);
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java	Mon Sep 21 12:15:38 2015 -0700
@@ -29,10 +29,10 @@
 import com.oracle.truffle.api.source.SourceSection;
 
 /**
- * A receiver of Truffle execution events that can act on behalf of an external client.
+ * A receiver of Truffle AST execution events that can act on behalf of an external client.
  * <p>
  * The {@link Probe} argument provides access to the {@link SourceSection} associated with the
- * event, as well as any {@link SyntaxTag}s that have been applied at that AST node.
+ * event, as well as any {@link SyntaxTag}s that have been applied at that program's location.
  * <p>
  * This listener is designed for clients that also require access to the AST execution state at the
  * time of the event. Clients that do not require access to the AST execution state should use the
@@ -41,6 +41,9 @@
  * Clients are free, of course, to record additional information in the listener implementation that
  * carries additional information about the context and reason for the particular {@link Instrument}
  * that is to be created from the listener.
+ * <p>
+ * Notification is fully synchronous, so overrides have performance implications. Non-trivial
+ * methods should be coded with Truffle guidelines and cautions in mind.
  */
 public interface StandardInstrumentListener {
 
@@ -49,14 +52,14 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void enter(Probe probe, Node node, VirtualFrame vFrame);
+    void onEnter(Probe probe, Node node, VirtualFrame vFrame);
 
     /**
      * Receive notification that an AST Node's {@code void}-valued execute method has just returned.
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnVoid(Probe probe, Node node, VirtualFrame vFrame);
+    void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame);
 
     /**
      * Receive notification that an AST Node's execute method has just returned a value (boxed if
@@ -64,12 +67,12 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result);
+    void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result);
 
     /**
      * Receive notification that an AST Node's execute method has just thrown an exception.
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception);
+    void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/WrapperNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013, 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.api.instrument;
+
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.nodes.Node;
+
+/**
+ * A {@link Node} instance that must be inserted into a Truffle AST in order to enable
+ * {@linkplain Instrument instrumentation} at a particular Guest Language (GL) node. Implementations
+ * must 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 execution
+ * semantics.
+ * <p>
+ * Instrumentation at the wrapped node is implemented by an instance of {@link EventHandlerNode}
+ * attached as a second child of the {@link WrapperNode}.
+ * <p>
+ * A wrapper is obliged to notify its attached {@link EventHandlerNode} when execution events occur
+ * at the wrapped AST node during program execution.
+ * <p>
+ * When a GL AST is cloned, the {@link WrapperNode}, its {@link EventHandlerNode} 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>
+ * Implementation guidelines:
+ * <ol>
+ * <li>Each GL implementation must implement a WrapperNode implementation for each AST context in
+ * which Instrumentation is to be supported.</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 EventHandlerNode eventHandlerNode;}</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 eventHandlerNode.getProbe();}
+ * <li>Method {@code insertProbe(EventHandlerNode)} should be implemented as
+ * {@code this.eventHandlerNode=insert(eventHandlerNode);}</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>
+ *
+ * @see Instrument
+ */
+public interface WrapperNode extends InstrumentationNode {
+
+    /**
+     * Gets the node being "wrapped", i.e. the AST node for which
+     * {@linkplain InstrumentationNode.EventHandlerNode execution events} will be reported through
+     * the Instrumentation Framework.
+     */
+    Node getChild();
+
+    /**
+     * Gets the {@link Probe} responsible for installing this wrapper.
+     */
+    Probe getProbe();
+
+    /**
+     * Implementation support for completing a newly created wrapper node.
+     */
+    void insertEventHandlerNode(EventHandlerNode eventHandlerNode);
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Mon Sep 21 12:15:38 2015 -0700
@@ -26,7 +26,7 @@
 
 import com.oracle.truffle.api.instrument.ASTPrinter;
 import com.oracle.truffle.api.instrument.InstrumentationNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeClass;
 import com.oracle.truffle.api.nodes.NodeFieldAccessor;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java	Mon Sep 21 12:15:38 2015 -0700
@@ -32,15 +32,15 @@
  */
 public class DefaultSimpleInstrumentListener implements SimpleInstrumentListener {
 
-    public void enter(Probe probe) {
+    public void onEnter(Probe probe) {
     }
 
-    public void returnVoid(Probe probe) {
+    public void onReturnVoid(Probe probe) {
     }
 
-    public void returnValue(Probe probe, Object result) {
+    public void onReturnValue(Probe probe, Object result) {
     }
 
-    public void returnExceptional(Probe probe, Exception exception) {
+    public void onReturnExceptional(Probe probe, Exception exception) {
     }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java	Mon Sep 21 12:15:38 2015 -0700
@@ -35,16 +35,16 @@
  */
 public class DefaultStandardInstrumentListener implements StandardInstrumentListener {
 
-    public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+    public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
     }
 
-    public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
+    public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) {
     }
 
-    public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
+    public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
     }
 
-    public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
+    public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
     }
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,6 +24,15 @@
  */
 package com.oracle.truffle.api.nodes;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
@@ -32,22 +41,9 @@
 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.ProbeException;
-import com.oracle.truffle.api.instrument.ProbeFailure;
-import com.oracle.truffle.api.instrument.ProbeNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.utilities.JSONHelper;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.Callable;
 
 /**
  * Abstract base class for all Truffle nodes.
@@ -420,86 +416,22 @@
     }
 
     /**
-     * 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;
     }
 
     /**
-     * Enables {@linkplain Instrument instrumentation} of a node, where the node is presumed to be
-     * part of a well-formed Truffle AST that is not being executed. If this node has not already
-     * been probed, modifies the AST by inserting a {@linkplain WrapperNode wrapper node} between
-     * the node and its parent; the wrapper node must be provided by implementations of
-     * {@link #createWrapperNode()}. No more than one {@link Probe} may be associated with a node,
-     * so a {@linkplain WrapperNode wrapper} may not wrap another {@linkplain WrapperNode wrapper}.
-     *
-     * @return a (possibly newly created) {@link Probe} associated with this node.
-     * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged
-     */
-    public final Probe probe() {
-
-        if (this instanceof WrapperNode) {
-            throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, this, null);
-        }
-
-        if (parent == null) {
-            throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, this, null);
-        }
-
-        if (parent instanceof WrapperNode) {
-            return ((WrapperNode) parent).getProbe();
-        }
-
-        if (!isInstrumentable()) {
-            throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, this, null);
-        }
-
-        // Create a new wrapper/probe with this node as its child.
-        final WrapperNode wrapper = createWrapperNode();
-
-        if (wrapper == null || !(wrapper instanceof Node)) {
-            throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, this, wrapper);
-        }
-
-        final Node wrapperNode = (Node) wrapper;
-
-        if (!this.isSafelyReplaceableBy(wrapperNode)) {
-            throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, this, wrapper);
-        }
-
-        // Connect it to a Probe
-        final Probe probe = ProbeNode.insertProbe(wrapper);
-
-        // Replace this node in the AST with the wrapper
-        this.replace(wrapperNode);
-
-        return probe;
-    }
-
-    /**
      * Converts this node to a textual representation useful for debugging.
      */
     @Override
@@ -576,6 +508,10 @@
         return "";
     }
 
+    protected void applyInstrumentation(Node node) {
+        ACCESSOR.applyInstrumentation(node);
+    }
+
     private static final Object GIL = new Object();
 
     private static final ThreadLocal<Integer> IN_ATOMIC_BLOCK = new ThreadLocal<Integer>() {
@@ -605,8 +541,13 @@
         protected Class<? extends TruffleLanguage> findLanguage(RootNode n) {
             return n.language;
         }
+
+        @Override
+        protected void applyInstrumentation(Node node) {
+            super.applyInstrumentation(node);
+        }
     }
 
     // registers into Accessor.NODES
-    @SuppressWarnings("unused") private static final AccessorNodes ACCESSOR = new AccessorNodes();
+    private static final AccessorNodes ACCESSOR = new AccessorNodes();
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Mon Sep 21 12:15:38 2015 -0700
@@ -27,7 +27,7 @@
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.TruffleOptions;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
 import com.oracle.truffle.api.instrument.SyntaxTag;
 import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -36,7 +36,6 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.impl.DefaultCompilerOptions;
 import com.oracle.truffle.api.instrument.ASTProber;
-import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.source.SourceSection;
 
 /**
@@ -162,7 +161,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();
@@ -188,9 +187,10 @@
     }
 
     /**
-     * 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.
+     * Apply to the AST all instances of {@link ASTProber} specified for the language, 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.
@@ -198,7 +198,7 @@
      * 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)
+     * @see TruffleLanguage
      */
     public void applyInstrumentation() {
     }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,8 +24,6 @@
  */
 package com.oracle.truffle.api.source;
 
-import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.TruffleLanguage.Registration;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -54,6 +52,9 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.TruffleLanguage.Registration;
+
 /**
  * Representation of a guest language source code unit and its contents. Sources originate in
  * several ways:
@@ -425,7 +426,7 @@
 
     /**
      * The URL if the source is retrieved via URL.
-     * 
+     *
      * @return URL or <code>null</code>
      */
     public abstract URL getURL();
@@ -909,6 +910,8 @@
                 return "text/x-c";
             } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) {
                 return "application/x-r";
+            } else if (file.getName().endsWith(".js") || file.getName().endsWith(".JS")) {
+                return "application/javascript";
             } else {
                 try {
                     return Files.probeContentType(file.toPath());
@@ -997,6 +1000,8 @@
                 return "text/x-c";
             } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) {
                 return "application/x-r";
+            } else if (file.getName().endsWith(".js") || file.getName().endsWith(".JS")) {
+                return "application/javascript";
             } else {
                 try {
                     return Files.probeContentType(file.toPath());
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Mon Sep 21 12:15:38 2015 -0700
@@ -40,22 +40,12 @@
  */
 package com.oracle.truffle.sl.test.instrument;
 
-import com.oracle.truffle.api.instrument.ASTProber;
-import com.oracle.truffle.api.instrument.Instrument;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.StandardSyntaxTag;
-import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.vm.TruffleVM;
-import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber;
-import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode;
-import com.oracle.truffle.sl.test.SLTestRunner;
-import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.lang.reflect.Field;
 import java.nio.charset.Charset;
 import java.nio.file.FileSystems;
 import java.nio.file.FileVisitResult;
@@ -65,6 +55,7 @@
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.List;
+
 import org.junit.Assert;
 import org.junit.internal.TextListener;
 import org.junit.runner.Description;
@@ -77,6 +68,18 @@
 import org.junit.runners.ParentRunner;
 import org.junit.runners.model.InitializationError;
 
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.StandardSyntaxTag;
+import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.TruffleVM;
+import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber;
+import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode;
+import com.oracle.truffle.sl.test.SLTestRunner;
+import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase;
+
 /**
  * This class builds and executes the tests for instrumenting SL. Although much of this class is
  * written with future automation in mind, at the moment the tests that are created are hard-coded
@@ -94,7 +97,7 @@
 
     private static final String LF = System.getProperty("line.separator");
 
-    static class InstrumentTestCase {
+    static final class InstrumentTestCase {
         protected final Description name;
         protected final Path path;
         protected final String baseName;
@@ -115,16 +118,12 @@
 
     private final List<InstrumentTestCase> testCases;
 
-    public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError {
+    public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError, SecurityException, IllegalArgumentException {
         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);
         }
     }
 
@@ -236,20 +235,25 @@
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         PrintStream ps = 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
                 TruffleVM vm = TruffleVM.newVM().setIn(new ByteArrayInputStream(testCase.testInput.getBytes("UTF-8"))).setOut(out).build();
 
+                final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+                field.setAccessible(true);
+                final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+                instrumenter.registerASTProber(prober);
+
                 final String src = readAllLines(testCase.path);
                 vm.eval(Source.fromText(src, testCase.path.toString()).withMimeType("application/x-sl"));
 
                 // Attach an instrument to every probe tagged as an assignment
-                for (Probe probe : Probe.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) {
+                for (Probe probe : instrumenter.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) {
                     SLPrintAssigmentValueListener slPrintAssigmentValueListener = new SLPrintAssigmentValueListener(ps);
-                    probe.attach(Instrument.create(slPrintAssigmentValueListener, "SL print assignment value"));
+                    instrumenter.attach(probe, slPrintAssigmentValueListener, "SL print assignment value");
                 }
 
                 TruffleVM.Symbol main = vm.findGlobalSymbol("main");
@@ -263,13 +267,12 @@
         } catch (Throwable ex) {
             notifier.fireTestFailure(new Failure(testCase.name, ex));
         } finally {
-            Probe.unregisterASTProber(prober);
             notifier.fireTestFinished(testCase.name);
         }
 
     }
 
-    public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException {
+    public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException, SecurityException, IllegalArgumentException {
         JUnitCore core = new JUnitCore();
         core.addListener(new TextListener(System.out));
         SLInstrumentTestRunner suite = new SLInstrumentTestRunner(testClass);
@@ -314,7 +317,7 @@
         }
 
         @Override
-        public void returnValue(Probe probe, Object result) {
+        public void onReturnValue(Probe probe, Object result) {
             output.println(result);
         }
     }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Mon Sep 21 12:15:38 2015 -0700
@@ -40,11 +40,19 @@
  */
 package com.oracle.truffle.sl;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.debug.DebugSupportException;
 import com.oracle.truffle.api.debug.DebugSupportProvider;
 import com.oracle.truffle.api.dsl.NodeFactory;
 import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
@@ -52,7 +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.Probe;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.GraphPrintVisitor;
@@ -68,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;
@@ -92,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;
@@ -102,14 +114,6 @@
 import com.oracle.truffle.sl.runtime.SLFunction;
 import com.oracle.truffle.sl.runtime.SLFunctionRegistry;
 import com.oracle.truffle.sl.runtime.SLNull;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.math.BigInteger;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
 
 /**
  * SL is a simple language to demonstrate and showcase features of Truffle. The implementation is as
@@ -198,8 +202,7 @@
 public final class SLLanguage extends TruffleLanguage<SLContext> {
     private static List<NodeFactory<? extends SLBuiltinNode>> builtins = Collections.emptyList();
     private static Visualizer visualizer = new SLDefaultVisualizer();
-    private static ASTProber registeredASTProber; // non-null if prober already registered
-    private DebugSupportProvider debugSupport;
+    private ASTProber astProber = new SLStandardASTProber();
 
     private SLLanguage() {
     }
@@ -256,6 +259,7 @@
     /**
      * Temporary method during API evolution, supports debugger integration.
      */
+    @Deprecated
     public static void run(Source source) throws IOException {
         TruffleVM vm = TruffleVM.newVM().build();
         assert vm.getLanguages().containsKey("application/x-sl");
@@ -478,28 +482,72 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
-        return getDebugSupport();
+    protected Visualizer getVisualizer() {
+        if (visualizer == null) {
+            visualizer = new SLDefaultVisualizer();
+        }
+        return visualizer;
+    }
+
+    @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;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected void enableASTProbing(ASTProber prober) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+        throw new IllegalStateException("evalInContext not supported in this language");
+    }
+
+    @Override
+    protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
+        throw new IllegalStateException("createAdvancedInstrumentRootFactory not supported in this language");
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
+    protected ToolSupportProvider getToolSupport() {
+        return null;
+    }
+
+    @SuppressWarnings("deprecation")
+    @Override
     protected DebugSupportProvider getDebugSupport() {
-        if (debugSupport == null) {
-            debugSupport = new SLDebugProvider();
-        }
-        return debugSupport;
+        return null;
+    }
+
+    public Node createFindContextNode0() {
+        return createFindContextNode();
+    }
+
+    public SLContext findContext0(Node contextNode) {
+        return findContext(contextNode);
     }
 
     // TODO (mlvdv) remove the static hack when we no longer have the static demo variables
     private static void setupToolDemos() {
-        // if (statementCounts || coverage) {
-        // if (registeredASTProber == null) {
-        // final ASTProber newProber = new SLStandardASTProber();
-        // // This should be registered on the TruffleVM
-        // Probe.registerASTProber(newProber);
-        // registeredASTProber = newProber;
-        // }
-        // }
         // if (nodeExecCounts) {
         // nodeExecCounter = new NodeExecCounter();
         // nodeExecCounter.install();
@@ -531,46 +579,4 @@
         // }
     }
 
-    public Node createFindContextNode0() {
-        return createFindContextNode();
-    }
-
-    public SLContext findContext0(Node contextNode) {
-        return findContext(contextNode);
-    }
-
-    private final class SLDebugProvider implements DebugSupportProvider {
-
-        public SLDebugProvider() {
-            if (registeredASTProber == null) {
-                registeredASTProber = new SLStandardASTProber();
-                // This should be registered on the TruffleVM
-                Probe.registerASTProber(registeredASTProber);
-            }
-        }
-
-        public Visualizer getVisualizer() {
-            if (visualizer == null) {
-                visualizer = new SLDefaultVisualizer();
-            }
-            return visualizer;
-        }
-
-        public void enableASTProbing(ASTProber prober) {
-            if (prober != null) {
-                // This should be registered on the TruffleVM
-                Probe.registerASTProber(prober);
-            }
-        }
-
-        public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException {
-            throw new DebugSupportException("evalInContext not supported in this language");
-        }
-
-        public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException {
-            throw new DebugSupportException("createAdvancedInstrumentRootFactory not supported in this language");
-        }
-
-    }
-
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -42,11 +42,9 @@
 
 import com.oracle.truffle.api.dsl.TypeSystemReference;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 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;
 
 /**
@@ -94,15 +92,4 @@
     public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException {
         return SLTypesGen.expectBoolean(executeGeneric(frame));
     }
-
-    @Override
-    public boolean isInstrumentable() {
-        return true;
-    }
-
-    @Override
-    public WrapperNode createWrapperNode() {
-        return new SLExpressionWrapperNode(this);
-    }
-
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -43,7 +43,6 @@
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.sl.SLLanguage;
@@ -100,7 +99,7 @@
 
     @Override
     public void applyInstrumentation() {
-        Probe.applyASTProbers(bodyNode);
+        super.applyInstrumentation(bodyNode);
     }
 
     @Override
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -40,13 +40,12 @@
  */
 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
@@ -101,15 +100,4 @@
             return String.format("%s:%d%s", sourceName, startLine, estimated ? "~" : "");
         }
     }
-
-    @Override
-    public boolean isInstrumentable() {
-        return true;
-    }
-
-    @Override
-    public WrapperNode createWrapperNode() {
-        return new SLStatementWrapperNode(this);
-    }
-
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -41,11 +41,11 @@
 package com.oracle.truffle.sl.nodes.instrument;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrument.EventHandlerNode;
 import com.oracle.truffle.api.instrument.Instrument;
 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.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
@@ -63,7 +63,7 @@
 @NodeInfo(cost = NodeCost.NONE)
 public final class SLExpressionWrapperNode extends SLExpressionNode implements WrapperNode {
     @Child private SLExpressionNode child;
-    @Child private ProbeNode probeNode;
+    @Child private EventHandlerNode eventHandlerNode;
 
     /**
      * Constructor.
@@ -81,21 +81,16 @@
     }
 
     @Override
-    public boolean isInstrumentable() {
-        return false;
-    }
-
-    @Override
     public SLExpressionNode getNonWrapperNode() {
         return child;
     }
 
-    public void insertProbe(ProbeNode newProbeNode) {
-        this.probeNode = newProbeNode;
+    public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+        this.eventHandlerNode = eventHandler;
     }
 
     public Probe getProbe() {
-        return probeNode.getProbe();
+        return eventHandlerNode.getProbe();
     }
 
     public Node getChild() {
@@ -105,16 +100,16 @@
     @Override
     public Object executeGeneric(VirtualFrame vFrame) {
 
-        probeNode.enter(child, vFrame);
+        eventHandlerNode.enter(child, vFrame);
         Object result;
 
         try {
             result = child.executeGeneric(vFrame);
-            probeNode.returnValue(child, vFrame, result);
+            eventHandlerNode.returnValue(child, vFrame, result);
         } catch (KillException e) {
             throw (e);
         } catch (Exception e) {
-            probeNode.returnExceptional(child, vFrame, e);
+            eventHandlerNode.returnExceptional(child, vFrame, e);
             throw (e);
         }
         return result;
@@ -132,14 +127,14 @@
 
     @Override
     public SLFunction executeFunction(VirtualFrame vFrame) throws UnexpectedResultException {
-        probeNode.enter(child, vFrame);
+        eventHandlerNode.enter(child, vFrame);
         SLFunction result;
 
         try {
             result = child.executeFunction(vFrame);
-            probeNode.returnValue(child, vFrame, result);
+            eventHandlerNode.returnValue(child, vFrame, result);
         } catch (Exception e) {
-            probeNode.returnExceptional(child, vFrame, e);
+            eventHandlerNode.returnExceptional(child, vFrame, e);
             throw (e);
         }
         return result;
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java	Mon Sep 21 12:15:38 2015 -0700
@@ -40,12 +40,14 @@
  */
 package com.oracle.truffle.sl.nodes.instrument;
 
-import com.oracle.truffle.api.instrument.ASTProber;
-import com.oracle.truffle.api.instrument.InstrumentationNode;
-import com.oracle.truffle.api.instrument.Probe;
 import static com.oracle.truffle.api.instrument.StandardSyntaxTag.ASSIGNMENT;
 import static com.oracle.truffle.api.instrument.StandardSyntaxTag.START_LOOP;
 import static com.oracle.truffle.api.instrument.StandardSyntaxTag.STATEMENT;
+
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.InstrumentationNode;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeVisitor;
 import com.oracle.truffle.sl.nodes.SLExpressionNode;
@@ -57,39 +59,39 @@
  * 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 {
+public class SLStandardASTProber implements ASTProber {
+
+    public void probeAST(final Instrumenter instrumenter, Node startNode) {
+        startNode.accept(new NodeVisitor() {
 
-    /**
-     * {@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) {
+            /**
+             * 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 InstrumentationNode) && node instanceof SLStatementNode && node.getParent() != null && node.getSourceSection() != null) {
-            // All SL nodes are instrumentable, but treat expressions specially
+                if (!(node instanceof InstrumentationNode) && node instanceof SLStatementNode && node.getParent() != null && node.getSourceSection() != null) {
+                    // All SL nodes are instrumentable, but treat expressions specially
 
-            if (node instanceof SLExpressionNode) {
-                SLExpressionNode expressionNode = (SLExpressionNode) node;
-                Probe probe = expressionNode.probe();
-                if (node instanceof SLWriteLocalVariableNode) {
-                    probe.tagAs(STATEMENT, null);
-                    probe.tagAs(ASSIGNMENT, null);
+                    if (node instanceof SLExpressionNode) {
+                        SLExpressionNode expressionNode = (SLExpressionNode) node;
+                        final Probe probe = instrumenter.probe(expressionNode);
+                        if (node instanceof SLWriteLocalVariableNode) {
+                            probe.tagAs(STATEMENT, null);
+                            probe.tagAs(ASSIGNMENT, null);
+                        }
+                    } else {
+                        SLStatementNode statementNode = (SLStatementNode) node;
+                        final Probe probe = instrumenter.probe(statementNode);
+                        probe.tagAs(STATEMENT, null);
+                        if (node instanceof SLWhileNode) {
+                            probe.tagAs(START_LOOP, null);
+                        }
+                    }
                 }
-            } else {
-                SLStatementNode statementNode = (SLStatementNode) node;
-                Probe probe = statementNode.probe();
-                probe.tagAs(STATEMENT, null);
-                if (node instanceof SLWhileNode) {
-                    probe.tagAs(START_LOOP, null);
-                }
+                return true;
             }
-        }
-        return true;
-    }
-
-    public void probeAST(Node node) {
-        node.accept(this);
+        });
     }
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java	Mon Sep 21 12:15:38 2015 -0700
@@ -41,11 +41,11 @@
 package com.oracle.truffle.sl.nodes.instrument;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrument.EventHandlerNode;
 import com.oracle.truffle.api.instrument.Instrument;
 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.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
@@ -61,7 +61,7 @@
 public final class SLStatementWrapperNode extends SLStatementNode implements WrapperNode {
 
     @Child private SLStatementNode child;
-    @Child private ProbeNode probeNode;
+    @Child private EventHandlerNode eventHandlerNode;
 
     public SLStatementWrapperNode(SLStatementNode child) {
         super(child.getSourceSection());
@@ -74,21 +74,17 @@
     }
 
     @Override
-    public boolean isInstrumentable() {
-        return false;
-    }
-
-    @Override
     public SLStatementNode getNonWrapperNode() {
         return child;
     }
 
-    public void insertProbe(ProbeNode newProbeNode) {
-        this.probeNode = newProbeNode;
+    @Override
+    public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+        this.eventHandlerNode = eventHandler;
     }
 
     public Probe getProbe() {
-        return probeNode.getProbe();
+        return eventHandlerNode.getProbe();
     }
 
     @Override
@@ -98,15 +94,15 @@
 
     @Override
     public void executeVoid(VirtualFrame vFrame) {
-        probeNode.enter(child, vFrame);
+        eventHandlerNode.enter(child, vFrame);
 
         try {
             child.executeVoid(vFrame);
-            probeNode.returnVoid(child, vFrame);
+            eventHandlerNode.returnVoid(child, vFrame);
         } catch (KillException e) {
             throw (e);
         } catch (Exception e) {
-            probeNode.returnExceptional(child, vFrame, e);
+            eventHandlerNode.returnExceptional(child, vFrame, e);
             throw (e);
         }
     }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,25 +24,33 @@
  */
 package com.oracle.truffle.tools.test;
 
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.StandardSyntaxTag;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.TruffleVM;
 import com.oracle.truffle.tools.CoverageTracker;
-import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode;
-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 org.junit.Test;
+import com.oracle.truffle.tools.test.ToolTestUtil.ToolTestTag;
 
 public class CoverageTrackerTest {
 
     @Test
-    public void testNoExecution() {
+    public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        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();
+        tool.install(instrumenter);
         assertEquals(tool.getCounts().entrySet().size(), 0);
         tool.setEnabled(false);
         assertEquals(tool.getCounts().entrySet().size(), 0);
@@ -54,66 +62,55 @@
         assertEquals(tool.getCounts().entrySet().size(), 0);
     }
 
-    @Test
-    public void testToolCreatedTooLate() {
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        final CoverageTracker tool = new CoverageTracker();
-        tool.install();
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
-        tool.dispose();
-    }
-
-    @Test
-    public void testToolInstalledcTooLate() {
-        final CoverageTracker tool = new CoverageTracker();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        tool.install();
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
-        tool.dispose();
+    void checkCounts(Source source, CoverageTracker coverage, Long[] expectedCounts) {
+        final Map<Source, Long[]> countMap = coverage.getCounts();
+        assertEquals(countMap.size(), 1);
+        final Long[] resultCounts = countMap.get(source);
+        assertTrue(Arrays.equals(resultCounts, expectedCounts));
     }
 
     @Test
-    public void testCountingCoverage() {
-        final CoverageTracker tool = new CoverageTracker();
-        tool.install();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
+    public void testCountingCoverage() 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 = ToolTestUtil.createTestSource("testCountingCoverage");
 
-        // Not probed yet.
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
+        final CoverageTracker valueCoverage = new CoverageTracker(ToolTestTag.VALUE_TAG);
+        final CoverageTracker addCoverage = new CoverageTracker(ToolTestTag.ADD_TAG);
 
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
+        valueCoverage.install(instrumenter);
+        assertTrue(valueCoverage.getCounts().isEmpty());
+
+        assertEquals(vm.eval(source).get(), 13);
 
-        // Probed but not tagged yet.
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
+        checkCounts(source, valueCoverage, new Long[]{Long.valueOf(1), null, Long.valueOf(1), null});
 
-        probe.tagAs(StandardSyntaxTag.STATEMENT, "fake statement for testing");
+        addCoverage.install(instrumenter);
 
-        // Counting now; execute once
-        assertEquals(13, expr13rootNode.execute(null));
+        assertEquals(vm.eval(source).get(), 13);
 
-        final Long[] longs1 = tool.getCounts().get(addNode.getSourceSection().getSource());
-        assertNotNull(longs1);
-        assertEquals(longs1.length, 2);
-        assertNull(longs1[0]);  // Line 1 is empty (text lines are 1-based)
-        assertEquals(1L, longs1[1].longValue());  // Expression is on line 2
+        checkCounts(source, valueCoverage, new Long[]{Long.valueOf(2), null, Long.valueOf(2), null});
+        checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(1), null, null});
+
+        valueCoverage.setEnabled(false);
+        assertEquals(vm.eval(source).get(), 13);
 
-        // Execute 99 more times
-        for (int i = 0; i < 99; i++) {
-            assertEquals(13, expr13rootNode.execute(null));
-        }
+        checkCounts(source, valueCoverage, new Long[]{Long.valueOf(2), null, Long.valueOf(2), null});
+        checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(2), null, null});
+
+        valueCoverage.setEnabled(true);
+        assertEquals(vm.eval(source).get(), 13);
 
-        final Long[] longs100 = tool.getCounts().get(addNode.getSourceSection().getSource());
-        assertNotNull(longs100);
-        assertEquals(longs100.length, 2);
-        assertNull(longs100[0]);
-        assertEquals(100L, longs100[1].longValue());
+        checkCounts(source, valueCoverage, new Long[]{Long.valueOf(3), null, Long.valueOf(3), null});
+        checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(3), null, null});
+
+        valueCoverage.dispose();
+        assertEquals(vm.eval(source).get(), 13);
 
-        tool.dispose();
+        checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(4), null, null});
+
+        addCoverage.dispose();
     }
-
 }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,67 +24,97 @@
  */
 package com.oracle.truffle.tools.test;
 
+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.Test;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.source.LineLocation;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.TruffleVM;
 import com.oracle.truffle.tools.LineToProbesMap;
-import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode;
-import static com.oracle.truffle.tools.test.TestNodes.expr13Line1;
-import static com.oracle.truffle.tools.test.TestNodes.expr13Line2;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import org.junit.Test;
+import com.oracle.truffle.tools.test.ToolTestUtil.ToolTestTag;
 
 public class LineToProbesMapTest {
 
     @Test
-    public void testToolCreatedTooLate() {
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
-        final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
-        assertEquals(lineLocation, expr13Line2);
-
-        final LineToProbesMap tool = new LineToProbesMap();
-        tool.install();
-
-        assertNull(tool.findFirstProbe(expr13Line1));
-        assertNull(tool.findFirstProbe(expr13Line2));
-        tool.dispose();
+    public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        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 = ToolTestUtil.createTestSource("testNoExecution");
+        final LineToProbesMap probesMap = new LineToProbesMap();
+        probesMap.install(instrumenter);
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(1)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(2)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(3)));
+        probesMap.dispose();
     }
 
     @Test
-    public void testToolInstalledTooLate() {
-        final LineToProbesMap tool = new LineToProbesMap();
+    public void testMapping1() 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 = ToolTestUtil.createTestSource("testMapping1");
+        final LineToProbesMap probesMap = new LineToProbesMap();
+
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(1)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(2)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(3)));
 
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
-        final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
-        assertEquals(lineLocation, expr13Line2);
+        // Map installed before AST gets created
+        probesMap.install(instrumenter);
+        assertEquals(vm.eval(source).get(), 13);
 
-        tool.install();
+        final Probe probe1 = probesMap.findFirstProbe(source.createLineLocation(1));
+        assertNotNull(probe1);
+        assertTrue(probe1.isTaggedAs(ToolTestTag.VALUE_TAG));
+        final Probe probe2 = probesMap.findFirstProbe(source.createLineLocation(2));
+        assertNotNull(probe2);
+        assertTrue(probe2.isTaggedAs(ToolTestTag.ADD_TAG));
+        final Probe probe3 = probesMap.findFirstProbe(source.createLineLocation(3));
+        assertNotNull(probe3);
+        assertTrue(probe3.isTaggedAs(ToolTestTag.VALUE_TAG));
 
-        assertNull(tool.findFirstProbe(expr13Line1));
-        assertNull(tool.findFirstProbe(expr13Line2));
-        tool.dispose();
+        probesMap.dispose();
     }
 
     @Test
-    public void testMapping() {
-        final LineToProbesMap tool = new LineToProbesMap();
-        tool.install();
+    public void testMapping2() 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 = ToolTestUtil.createTestSource("testMapping2");
+        final LineToProbesMap probesMap = new LineToProbesMap();
+
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(1)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(2)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(3)));
 
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
-        final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
-        assertEquals(lineLocation, expr13Line2);
+        // Map installed after AST gets created
+        assertEquals(vm.eval(source).get(), 13);
+        probesMap.install(instrumenter);
 
-        assertNull(tool.findFirstProbe(expr13Line1));
-        assertEquals(tool.findFirstProbe(expr13Line2), probe);
-        tool.dispose();
+        final Probe probe1 = probesMap.findFirstProbe(source.createLineLocation(1));
+        assertNotNull(probe1);
+        assertTrue(probe1.isTaggedAs(ToolTestTag.VALUE_TAG));
+        final Probe probe2 = probesMap.findFirstProbe(source.createLineLocation(2));
+        assertNotNull(probe2);
+        assertTrue(probe2.isTaggedAs(ToolTestTag.ADD_TAG));
+        final Probe probe3 = probesMap.findFirstProbe(source.createLineLocation(3));
+        assertNotNull(probe3);
+        assertTrue(probe3.isTaggedAs(ToolTestTag.VALUE_TAG));
+
+        probesMap.dispose();
     }
-
 }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,29 +24,31 @@
  */
 package com.oracle.truffle.tools.test;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.StandardSyntaxTag;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.TruffleVM;
 import com.oracle.truffle.tools.NodeExecCounter;
 import com.oracle.truffle.tools.NodeExecCounter.NodeExecutionCount;
-import com.oracle.truffle.tools.test.TestNodes.TestAddNode;
-import com.oracle.truffle.tools.test.TestNodes.TestValueNode;
-import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestCallTarget;
-import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import org.junit.Test;
 
 public class NodeExecCounterTest {
 
     @Test
-    public void testNoExecution() {
+    public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final TruffleVM vm = TruffleVM.newVM().build();
+        final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
         final NodeExecCounter tool = new NodeExecCounter();
         assertEquals(tool.getCounts().length, 0);
-        tool.install();
+        tool.install(instrumenter);
         assertEquals(tool.getCounts().length, 0);
         tool.setEnabled(false);
         assertEquals(tool.getCounts().length, 0);
@@ -58,113 +60,45 @@
         assertEquals(tool.getCounts().length, 0);
     }
 
-    @Test
-    public void testToolCreatedTooLate() {
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
-        final NodeExecCounter tool = new NodeExecCounter();
-        tool.install();
-        assertEquals(13, expr13callTarget.call());
-        assertEquals(tool.getCounts().length, 0);
-        tool.dispose();
-    }
-
-    @Test
-    public void testToolInstalledcTooLate() {
-        final NodeExecCounter tool = new NodeExecCounter();
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
-        tool.install();
-        assertEquals(13, expr13callTarget.call());
-        assertEquals(tool.getCounts().length, 0);
-        tool.dispose();
+    void checkCounts(NodeExecCounter execTool, int addCount, int valueCount) {
+        NodeExecutionCount[] counts = execTool.getCounts();
+        assertEquals(counts.length, 2);
+        for (NodeExecutionCount counter : counts) {
+            if (counter.nodeClass() == ToolTestUtil.TestAdditionNode.class) {
+                assertEquals(counter.executionCount(), addCount);
+            } else if (counter.nodeClass() == ToolTestUtil.TestValueNode.class) {
+                assertEquals(counter.executionCount(), valueCount);
+            } else {
+                fail("correct classes counted");
+            }
+        }
     }
 
     @Test
-    public void testCountingAll() {
-        final NodeExecCounter tool = new NodeExecCounter();
-        tool.install();
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
-
-        // execute once
-        assertEquals(13, expr13callTarget.call());
-        final NodeExecutionCount[] count1 = tool.getCounts();
-        assertNotNull(count1);
-        assertEquals(count1.length, 2);
-        for (NodeExecutionCount count : count1) {
-            final Class<?> class1 = count.nodeClass();
-            final long executionCount = count.executionCount();
-            if (class1 == TestAddNode.class) {
-                assertEquals(executionCount, 1);
-            } else if (class1 == TestValueNode.class) {
-                assertEquals(executionCount, 2);
-            } else {
-                fail();
-            }
-        }
+    public void testCounting() 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 = ToolTestUtil.createTestSource("testCounting");
+        final NodeExecCounter execTool = new NodeExecCounter();
+        execTool.install(instrumenter);
 
-        // Execute 99 more times
-        for (int i = 0; i < 99; i++) {
-            assertEquals(13, expr13callTarget.call());
-        }
-        final NodeExecutionCount[] counts100 = tool.getCounts();
-        assertNotNull(counts100);
-        assertEquals(counts100.length, 2);
-        for (NodeExecutionCount count : counts100) {
-            final Class<?> class1 = count.nodeClass();
-            final long executionCount = count.executionCount();
-            if (class1 == TestAddNode.class) {
-                assertEquals(executionCount, 100);
-            } else if (class1 == TestValueNode.class) {
-                assertEquals(executionCount, 200);
-            } else {
-                fail();
-            }
-        }
+        assertEquals(execTool.getCounts().length, 0);
 
-        tool.dispose();
-    }
+        assertEquals(vm.eval(source).get(), 13);
 
-    @Test
-    public void testCountingTagged() {
-        final NodeExecCounter tool = new NodeExecCounter(StandardSyntaxTag.STATEMENT);
-        tool.install();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-
-        // Not probed yet.
-        assertEquals(13, expr13rootNode.execute(null));
-        assertEquals(tool.getCounts().length, 0);
-
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
-
-        // Probed but not tagged yet.
-        assertEquals(13, expr13rootNode.execute(null));
-        assertEquals(tool.getCounts().length, 0);
-
-        probe.tagAs(StandardSyntaxTag.STATEMENT, "fake statement for testing");
+        checkCounts(execTool, 1, 2);
 
-        // Counting now; execute once
-        assertEquals(13, expr13rootNode.execute(null));
-        final NodeExecutionCount[] counts1 = tool.getCounts();
-        assertNotNull(counts1);
-        assertEquals(counts1.length, 1);
-        final NodeExecutionCount count1 = counts1[0];
-        assertNotNull(count1);
-        assertEquals(count1.nodeClass(), addNode.getClass());
-        assertEquals(count1.executionCount(), 1);
+        for (int i = 0; i < 99; i++) {
+            assertEquals(vm.eval(source).get(), 13);
+        }
+        checkCounts(execTool, 100, 200);
 
-        // Execute 99 more times
-        for (int i = 0; i < 99; i++) {
-            assertEquals(13, expr13rootNode.execute(null));
-        }
+        execTool.setEnabled(false);
+        assertEquals(vm.eval(source).get(), 13);
+        checkCounts(execTool, 100, 200);
 
-        final NodeExecutionCount[] counts100 = tool.getCounts();
-        assertNotNull(counts100);
-        assertEquals(counts100.length, 1);
-        final NodeExecutionCount count100 = counts100[0];
-        assertNotNull(count100);
-        assertEquals(count100.nodeClass(), addNode.getClass());
-        assertEquals(count100.executionCount(), 100);
-
-        tool.dispose();
+        execTool.dispose();
     }
 }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java	Mon Sep 21 13:11:41 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,228 +0,0 @@
-/*
- * 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 com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.frame.VirtualFrame;
-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.nodes.Node;
-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.source.LineLocation;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.source.SourceSection;
-
-/**
- * Nodes and an {@linkplain CallTarget executable ASTs} for testing.
- */
-class TestNodes {
-
-    /**
-     * A fake source used for testing: empty line 1, expression on line 2.
-     */
-    public static final Source expr13Source = Source.fromText("\n6+7\n", "Test Source: expression on line 2 that evaluates to 13");
-    public static final LineLocation expr13Line1 = expr13Source.createLineLocation(1);
-    public static final LineLocation expr13Line2 = expr13Source.createLineLocation(2);
-
-    /**
-     * An executable addition expression that evaluates to 13.
-     */
-    static CallTarget createExpr13TestCallTarget() {
-        final RootNode rootNode = createExpr13TestRootNode();
-        return Truffle.getRuntime().createCallTarget(rootNode);
-    }
-
-    /**
-     * Root holding an addition expression that evaluates to 13.
-     */
-    static RootNode createExpr13TestRootNode() {
-        final TestLanguageNode ast = createExpr13AST();
-        final TestRootNode rootNode = new TestRootNode(ast);
-        rootNode.adoptChildren();
-        return rootNode;
-    }
-
-    /**
-     * Addition expression that evaluates to 13, with faked source attribution.
-     */
-    static TestLanguageNode createExpr13AST() {
-        final SourceSection leftSourceSection = expr13Source.createSection("left", 1, 1);
-        final TestValueNode leftValueNode = new TestValueNode(6, leftSourceSection);
-        final SourceSection rightSourceSection = expr13Source.createSection("right", 3, 1);
-        final TestValueNode rightValueNode = new TestValueNode(7, rightSourceSection);
-        final SourceSection exprSourceSection = expr13Source.createSection("expr", 1, 3);
-        return new TestAddNode(leftValueNode, rightValueNode, exprSourceSection);
-    }
-
-    abstract static class TestLanguageNode extends Node {
-        public abstract Object execute(VirtualFrame frame);
-
-        public TestLanguageNode() {
-        }
-
-        public TestLanguageNode(SourceSection srcSection) {
-            super(srcSection);
-        }
-
-        @Override
-        public boolean isInstrumentable() {
-            return true;
-        }
-
-        @Override
-        public WrapperNode createWrapperNode() {
-            return new TestWrapperNode(this);
-        }
-    }
-
-    @NodeInfo(cost = NodeCost.NONE)
-    static class TestWrapperNode extends TestLanguageNode implements WrapperNode {
-        @Child private TestLanguageNode child;
-        @Child private ProbeNode probeNode;
-
-        public TestWrapperNode(TestLanguageNode child) {
-            assert !(child instanceof TestWrapperNode);
-            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 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;
-        }
-    }
-
-    /**
-     * 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;
-
-        /**
-         * 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(TruffleLanguage.class, null, 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);
-        }
-    }
-
-    static class TestValueNode extends TestLanguageNode {
-        private final int value;
-
-        public TestValueNode(int value) {
-            this.value = value;
-        }
-
-        public TestValueNode(int value, SourceSection srcSection) {
-            super(srcSection);
-            this.value = value;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame) {
-            return new Integer(this.value);
-        }
-    }
-
-    static class TestAddNode extends TestLanguageNode {
-        @Child private TestLanguageNode leftChild;
-        @Child private TestLanguageNode rightChild;
-
-        public TestAddNode(TestValueNode leftChild, TestValueNode rightChild, SourceSection sourceSection) {
-            super(sourceSection);
-            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());
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/ToolTestUtil.java	Mon Sep 21 12:15:38 2015 -0700
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2015, 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.EventHandlerNode;
+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.SyntaxTag;
+import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
+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;
+import com.oracle.truffle.api.source.SourceSection;
+
+public class ToolTestUtil {
+
+    static final String MIME_TYPE = "text/x-toolTest";
+
+    static enum ToolTestTag implements SyntaxTag {
+
+        ADD_TAG("addition", "test language addition node"),
+
+        VALUE_TAG("value", "test language value node");
+
+        private final String name;
+        private final String description;
+
+        private ToolTestTag(String name, String description) {
+            this.name = name;
+            this.description = description;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+    }
+
+    static Source createTestSource(String description) {
+        return Source.fromText("6\n+\n7\n" + description, description).withMimeType(MIME_TYPE);
+    }
+
+    @TruffleLanguage.Registration(name = "toolTestLanguage", version = "0", mimeType = MIME_TYPE)
+    public static final class ToolTestLang extends TruffleLanguage<Object> {
+
+        public static final ToolTestLang INSTANCE = new ToolTestLang();
+
+        private final ASTProber prober = new TestASTProber();
+
+        private ToolTestLang() {
+        }
+
+        @Override
+        protected CallTarget parse(Source source, Node context, String... argumentNames) throws IOException {
+            final TestValueNode leftValueNode = new TestValueNode(6, source.createSection("6", 0, 1));
+            final TestValueNode rightValueNode = new TestValueNode(7, source.createSection("7", 4, 1));
+            final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode, source.createSection("+", 2, 1));
+            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 ToolTestWrapperNode((ToolTestLangNode) 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 ToolTestLangNode) {
+
+                        final ToolTestLangNode testNode = (ToolTestLangNode) node;
+
+                        if (node instanceof TestValueNode) {
+                            instrumenter.probe(testNode).tagAs(ToolTestTag.VALUE_TAG, null);
+
+                        } else if (node instanceof TestAdditionNode) {
+                            instrumenter.probe(testNode).tagAs(ToolTestTag.ADD_TAG, null);
+
+                        }
+                    }
+                    return true;
+                }
+            });
+        }
+    }
+
+    abstract static class ToolTestLangNode extends Node {
+        public abstract Object execute(VirtualFrame vFrame);
+
+        protected ToolTestLangNode(SourceSection ss) {
+            super(ss);
+        }
+    }
+
+    @NodeInfo(cost = NodeCost.NONE)
+    static class ToolTestWrapperNode extends ToolTestLangNode implements WrapperNode {
+        @Child private ToolTestLangNode child;
+        @Child private EventHandlerNode eventHandlerNode;
+
+        public ToolTestWrapperNode(ToolTestLangNode child) {
+            super(null);
+            assert !(child instanceof ToolTestWrapperNode);
+            this.child = child;
+        }
+
+        @Override
+        public String instrumentationInfo() {
+            return "Wrapper node for testing";
+        }
+
+        public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+            this.eventHandlerNode = eventHandler;
+        }
+
+        @Override
+        public Probe getProbe() {
+            return eventHandlerNode.getProbe();
+        }
+
+        @Override
+        public Node getChild() {
+            return child;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            eventHandlerNode.enter(child, vFrame);
+            Object result;
+            try {
+                result = child.execute(vFrame);
+                eventHandlerNode.returnValue(child, vFrame, result);
+            } catch (KillException e) {
+                throw (e);
+            } catch (Exception e) {
+                eventHandlerNode.returnExceptional(child, vFrame, e);
+                throw (e);
+            }
+            return result;
+        }
+    }
+
+    /**
+     * A simple node for our test language to store a value.
+     */
+    static class TestValueNode extends ToolTestLangNode {
+        private final int value;
+
+        public TestValueNode(int value, SourceSection s) {
+            super(s);
+            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 ToolTestLangNode {
+        @Child private ToolTestLangNode leftChild;
+        @Child private ToolTestLangNode rightChild;
+
+        public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild, SourceSection s) {
+            super(s);
+            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 ToolTestLangNode 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(ToolTestLangNode body) {
+            super(ToolTestLang.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 ToolTestLangNode 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(ToolTestLangNode body, Instrumenter instrumenter) {
+            super(ToolTestLang.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.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,21 +24,27 @@
  */
 package com.oracle.truffle.tools.test;
 
-import com.oracle.truffle.api.instrument.InstrumentationTool;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+
 import org.junit.Test;
 
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.vm.TruffleVM;
+
 /**
  * Test the basic life cycle properties shared by all instances of {@link InstrumentationTool}.
  */
 public class TruffleToolTest {
 
     @Test
-    public void testEmptyLifeCycle() {
+    public void testEmptyLifeCycle() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
         assertFalse(tool.isEnabled());
-        tool.install();
+        tool.install(instrumenter);
         assertTrue(tool.isEnabled());
         tool.reset();
         assertTrue(tool.isEnabled());
@@ -73,45 +79,58 @@
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyInstalled() {
+    public void testAlreadyInstalled() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
-        tool.install();
+        tool.install(instrumenter);
+        tool.install(instrumenter);
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyDisposed1() {
+    public void testAlreadyDisposed1() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        tool.install(instrumenter);
         tool.dispose();
-        tool.install();
+        tool.install(instrumenter);
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyDisposed2() {
+    public void testAlreadyDisposed2() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        tool.install(instrumenter);
         tool.dispose();
         tool.reset();
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyDisposed3() {
+    public void testAlreadyDisposed3() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        tool.install(instrumenter);
         tool.dispose();
         tool.setEnabled(true);
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyDisposed4() {
+    public void testAlreadyDisposed4() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        tool.install(instrumenter);
         tool.dispose();
         tool.dispose();
     }
 
-    private static final class DummyTruffleTool extends InstrumentationTool {
+    private static Instrumenter getInstrumenter() throws NoSuchFieldException, IllegalAccessException {
+        final TruffleVM vm = TruffleVM.newVM().build();
+        final Field field = TruffleVM.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        return instrumenter;
+    }
+
+    private static final class DummyTruffleTool extends Instrumenter.Tool {
 
         @Override
         protected boolean internalInstall() {
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,8 +24,17 @@
  */
 package com.oracle.truffle.tools;
 
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeSet;
+
 import com.oracle.truffle.api.instrument.Instrument;
-import com.oracle.truffle.api.instrument.InstrumentationTool;
+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.SimpleInstrumentListener;
@@ -37,14 +46,6 @@
 import com.oracle.truffle.api.source.LineLocation;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.TreeSet;
 
 /**
  * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes that
@@ -61,9 +62,9 @@
  * <p>
  * <ul>
  * <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>
+ * to produce the event {@link SimpleInstrumentListener#onEnter(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>
@@ -81,7 +82,7 @@
  * @see Instrument
  * @see SyntaxTag
  */
-public final class CoverageTracker extends InstrumentationTool {
+public final class CoverageTracker extends Instrumenter.Tool {
 
     /** Counting data. */
     private final Map<LineLocation, CoverageRecord> coverageMap = new HashMap<>();
@@ -115,7 +116,11 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        final Instrumenter instrumenter = getInstrumenter();
+        for (Probe probe : instrumenter.findProbesTaggedAs(countingTag)) {
+            addCoverageCounter(probe);
+        }
+        instrumenter.addProbeListener(probeListener);
         return true;
     }
 
@@ -126,7 +131,7 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
+        getInstrumenter().removeProbeListener(probeListener);
         for (Instrument instrument : instruments) {
             instrument.dispose();
         }
@@ -250,7 +255,7 @@
         }
 
         @Override
-        public void enter(Probe probe) {
+        public void onEnter(Probe probe) {
             if (isEnabled()) {
                 count++;
             }
@@ -273,34 +278,35 @@
         @Override
         public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
             if (countingTag == tag) {
-
-                final SourceSection srcSection = probe.getProbedSourceSection();
-                if (srcSection == null) {
-                    // TODO (mlvdv) report this?
-                    return;
-                }
-                // Get the source line where the
-                final LineLocation lineLocation = srcSection.getLineLocation();
-                CoverageRecord record = coverageMap.get(lineLocation);
-                if (record != null) {
-                    // Another node starts on same line; count only the first (textually)
-                    if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) {
-                        // Existing record, corresponds to code earlier on line
-                        return;
-                    } else {
-                        // Existing record, corresponds to code at a later position; replace it
-                        record.instrument.dispose();
-                    }
-                }
-
-                final CoverageRecord coverage = new CoverageRecord(srcSection);
-                final Instrument instrument = Instrument.create(coverage, CoverageTracker.class.getSimpleName());
-                coverage.instrument = instrument;
-                instruments.add(instrument);
-                probe.attach(instrument);
-                coverageMap.put(lineLocation, coverage);
+                addCoverageCounter(probe);
             }
         }
     }
 
+    private void addCoverageCounter(Probe probe) {
+        final SourceSection srcSection = probe.getProbedSourceSection();
+        if (srcSection == null) {
+            // TODO (mlvdv) report this?
+            return;
+        }
+        // Get the source line where the
+        final LineLocation lineLocation = srcSection.getLineLocation();
+        CoverageRecord record = coverageMap.get(lineLocation);
+        if (record != null) {
+            // Another node starts on same line; count only the first (textually)
+            if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) {
+                // Existing record, corresponds to code earlier on line
+                return;
+            } else {
+                // Existing record, corresponds to code at a later position; replace it
+                record.instrument.dispose();
+            }
+        }
+
+        final CoverageRecord coverageRecord = new CoverageRecord(srcSection);
+        final Instrument instrument = getInstrumenter().attach(probe, coverageRecord, CoverageTracker.class.getSimpleName());
+        coverageRecord.instrument = instrument;
+        instruments.add(instrument);
+        coverageMap.put(lineLocation, coverageRecord);
+    }
 }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,13 +24,6 @@
  */
 package com.oracle.truffle.tools;
 
-import com.oracle.truffle.api.instrument.InstrumentationTool;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeListener;
-import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
-import com.oracle.truffle.api.source.LineLocation;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.source.SourceSection;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -38,11 +31,19 @@
 import java.util.HashMap;
 import java.util.Map;
 
+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.impl.DefaultProbeListener;
+import com.oracle.truffle.api.source.LineLocation;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+
 /**
  * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST,
  * indexed by {@link Source} and line number.
  */
-public final class LineToProbesMap extends InstrumentationTool {
+public final class LineToProbesMap extends Instrumenter.Tool {
 
     private static final boolean TRACE = false;
     private static final PrintStream OUT = System.out;
@@ -68,7 +69,11 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        final Instrumenter instrumenter = getInstrumenter();
+        for (Probe probe : instrumenter.findProbesTaggedAs(null)) {
+            addMapEntry(probe);
+        }
+        instrumenter.addProbeListener(probeListener);
         return true;
     }
 
@@ -79,7 +84,7 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
+        getInstrumenter().removeProbeListener(probeListener);
     }
 
     /**
@@ -115,21 +120,25 @@
 
         @Override
         public void newProbeInserted(Probe probe) {
-            final SourceSection sourceSection = probe.getProbedSourceSection();
-            if (sourceSection != null && sourceSection.getSource() != null) {
-                final LineLocation lineLocation = sourceSection.getLineLocation();
-                if (TRACE) {
-                    trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());
-                }
-                Collection<Probe> probes = lineToProbesMap.get(lineLocation);
-                if (probes == null) {
-                    probes = new ArrayList<>(2);
-                    lineToProbesMap.put(lineLocation, probes);
-                } else {
-                    assert !probes.contains(probe);
-                }
-                probes.add(probe);
+            addMapEntry(probe);
+        }
+    }
+
+    private void addMapEntry(Probe probe) {
+        final SourceSection sourceSection = probe.getProbedSourceSection();
+        if (sourceSection != null && sourceSection.getSource() != null) {
+            final LineLocation lineLocation = sourceSection.getLineLocation();
+            if (TRACE) {
+                trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());
             }
+            Collection<Probe> probes = lineToProbesMap.get(lineLocation);
+            if (probes == null) {
+                probes = new ArrayList<>(2);
+                lineToProbesMap.put(lineLocation, probes);
+            } else {
+                assert !probes.contains(probe);
+            }
+            probes.add(probe);
         }
     }
 }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java	Mon Sep 21 13:11:41 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java	Mon Sep 21 12:15:38 2015 -0700
@@ -24,11 +24,21 @@
  */
 package com.oracle.truffle.tools;
 
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 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.InstrumentationTool;
+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;
@@ -40,15 +50,6 @@
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.Node.Child;
 import com.oracle.truffle.api.nodes.NodeVisitor;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes,
@@ -64,9 +65,9 @@
  * <p>
  * <ul>
  * <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>
+ * to produce the event {@link StandardInstrumentListener#onEnter(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>
@@ -95,7 +96,7 @@
  * @see SyntaxTag
  * @see ProbeFailure
  */
-public final class NodeExecCounter extends InstrumentationTool {
+public final class NodeExecCounter extends Instrumenter.Tool {
 
     /**
      * Execution count for AST nodes of a particular type.
@@ -112,13 +113,13 @@
      */
     private final StandardInstrumentListener instrumentListener = new DefaultStandardInstrumentListener() {
         @Override
-        public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+        public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
             if (isEnabled()) {
                 final Class<?> nodeClass = node.getClass();
                 /*
                  * Everything up to here is inlined by Truffle compilation. Delegate the next part
                  * to a method behind an inlining boundary.
-                 * 
+                 *
                  * Note that it is not permitted to pass a {@link VirtualFrame} across an inlining
                  * boundary; they are truly virtual in inlined code.
                  */
@@ -170,7 +171,7 @@
      * Create a per node-type execution counting tool for all nodes in subsequently created ASTs.
      */
     public NodeExecCounter() {
-        this.countingTag = null;
+        this(null);
     }
 
     /**
@@ -185,10 +186,10 @@
     protected boolean internalInstall() {
         if (countingTag == null) {
             astProber = new ExecCounterASTProber();
-            Probe.registerASTProber(astProber);
+            getInstrumenter().registerASTProber(astProber);
         } else {
             probeListener = new NodeExecCounterProbeListener();
-            Probe.addProbeListener(probeListener);
+            getInstrumenter().addProbeListener(probeListener);
         }
         return true;
     }
@@ -202,10 +203,10 @@
     @Override
     protected void internalDispose() {
         if (astProber != null) {
-            Probe.unregisterASTProber(astProber);
+            getInstrumenter().unregisterASTProber(astProber);
         }
         if (probeListener != null) {
-            Probe.removeProbeListener(probeListener);
+            getInstrumenter().removeProbeListener(probeListener);
         }
         for (Instrument instrument : instruments) {
             instrument.dispose();
@@ -289,24 +290,28 @@
     /**
      * A prober that attempts to probe and instrument every node.
      */
-    private class ExecCounterASTProber implements ASTProber, NodeVisitor {
+    private class ExecCounterASTProber implements ASTProber {
+
+        public void probeAST(final Instrumenter instrumenter, final Node startNode) {
 
-        public boolean visit(Node node) {
+            startNode.accept(new NodeVisitor() {
+
+                public boolean visit(Node node) {
+
+                    if (instrumenter.isInstrumentable(node)) {
+                        try {
 
-            if (node.isInstrumentable()) {
-                try {
-                    final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter");
-                    instruments.add(instrument);
-                    node.probe().attach(instrument);
-                } catch (ProbeException ex) {
-                    failures.add(ex.getFailure());
+                            final Probe probe = instrumenter.probe(node);
+                            final Instrument instrument = instrumenter.attach(probe, instrumentListener, "NodeExecCounter");
+                            instruments.add(instrument);
+                        } catch (ProbeException ex) {
+                            failures.add(ex.getFailure());
+                        }
+                    }
+                    return true;
                 }
-            }
-            return true;
-        }
 
-        public void probeAST(Node node) {
-            node.accept(this);
+            });
         }
     }
 
@@ -319,9 +324,8 @@
         @Override
         public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
             if (countingTag == tag) {
-                final Instrument instrument = Instrument.create(instrumentListener, NodeExecCounter.class.getSimpleName());
+                final Instrument instrument = getInstrumenter().attach(probe, instrumentListener, NodeExecCounter.class.getSimpleName());
                 instruments.add(instrument);
-                probe.attach(instrument);
             }
         }
     }