changeset 22214:3aad794eec0e

Truffle/Instrumentation: first large merge of instrumentation code into the TruffleVM framework - introduce the Instrumenter class, held by the TruffleVM to support instrumentation services - reimplement Probe-realated services (formerly statics in Probe.java) to be provided by the Instrumenter- add new Accessors - change the TruffleVM startup sequence - change the APIs of the Debugger and many other classes
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Mon, 14 Sep 2015 22:59:51 -0700
parents 1d804d691dc7
children d3bdaa91bc82
files truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.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/debug/Debugger.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.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 truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java
diffstat 38 files changed, 1423 insertions(+), 683 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java	Mon Sep 14 22:59:51 2015 -0700
@@ -25,10 +25,18 @@
 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.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;
+import java.util.Collections;
+import java.util.List;
 
 public final class TestingLanguage extends TruffleLanguage<Object> {
     public static final TestingLanguage INSTANCE = new TestingLanguage();
@@ -58,13 +66,41 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
+    protected Visualizer getVisualizer() {
         return null;
     }
 
     @Override
+    protected List<ASTProber> getASTProbers() {
+        return Collections.emptyList();
+    }
+
+    @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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -22,14 +22,22 @@
  */
 package com.oracle.truffle.api.dsl.test.processor;
 
-import java.io.*;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
 
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.debug.*;
-import com.oracle.truffle.api.dsl.test.*;
-import com.oracle.truffle.api.instrument.*;
+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.ToolSupportProvider;
+import com.oracle.truffle.api.instrument.Visualizer;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.source.Source;
 
 public class LanguageRegistrationTest {
 
@@ -76,13 +84,41 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected List<ASTProber> getASTProbers() {
+            return Collections.emptyList();
+        }
+
+        @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 +156,41 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected List<ASTProber> getASTProbers() {
+            return Collections.emptyList();
+        }
+
+        @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 +224,41 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected List<ASTProber> getASTProbers() {
+            return Collections.emptyList();
+        }
+
+        @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/TestingLanguage.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java	Mon Sep 14 22:59:51 2015 -0700
@@ -25,10 +25,18 @@
 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.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;
+import java.util.Collections;
+import java.util.List;
 
 public final class TestingLanguage extends TruffleLanguage<Object> {
     public static final TestingLanguage INSTANCE = new TestingLanguage();
@@ -57,13 +65,41 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
+    protected Visualizer getVisualizer() {
         return null;
     }
 
     @Override
+    protected List<ASTProber> getASTProbers() {
+        return Collections.emptyList();
+    }
+
+    @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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -41,20 +41,22 @@
 public class AdvancedInstrumentTest {
 
     @Test
-    public void testAdvancedInstrumentListener() {
+    public void testAdvancedInstrumentListener() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter();
+
         // Create a simple addition AST
         final TruffleRuntime runtime = Truffle.getRuntime();
         final TestValueNode leftValueNode = new TestValueNode(6);
         final TestValueNode rightValueNode = new TestValueNode(7);
         final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
-        final TestRootNode rootNode = new TestRootNode(addNode);
+        final TestRootNode rootNode = new TestRootNode(addNode, instrumenter);
         final CallTarget callTarget1 = runtime.createCallTarget(rootNode);
 
         // Ensure it executes correctly
         assertEquals(13, callTarget1.call());
 
         // Probe the addition node
-        final Probe probe = addNode.probe();
+        final Probe probe = instrumenter.probe(addNode);
 
         assertEquals(13, callTarget1.call());
 
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -88,7 +88,9 @@
     };
 
     @Test
-    public void testInstrumentationStructure() {
+    public void testInstrumentationStructure() throws IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchFieldException {
+
+        final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter();
         // Create a simple addition AST
         final TruffleRuntime runtime = Truffle.getRuntime();
         final TestValueNode leftValueNode = new TestValueNode(6);
@@ -96,11 +98,11 @@
         final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
 
         try {
-            addNode.probe();
+            instrumenter.probe(addNode);
         } catch (ProbeException e) {
             assertEquals(e.getFailure().getReason(), Reason.NO_PARENT);
         }
-        final TestRootNode rootNode = new TestRootNode(addNode);
+        final TestRootNode rootNode = new TestRootNode(addNode, instrumenter);
 
         // Creating a call target sets the parent pointers in this tree and is necessary prior to
         // checking any parent/child relationships
@@ -122,7 +124,7 @@
         assertEquals(13, callTarget1.call());
 
         // Probe the addition node
-        addNode.probe();
+        instrumenter.probe(addNode);
 
         // Check the modified tree structure
         assertEquals(addNode, leftValueNode.getParent());
@@ -149,7 +151,7 @@
         // Check that you can't probe the WrapperNodes
         TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode;
         try {
-            wrapper.probe();
+            instrumenter.probe(wrapper);
             fail();
         } catch (ProbeException e) {
             assertEquals(e.getFailure().getReason(), Reason.WRAPPER_NODE);
@@ -160,20 +162,22 @@
     }
 
     @Test
-    public void testListeners() {
+    public void testListeners() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+
+        final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter();
 
         // Create a simple addition AST
         final TruffleRuntime runtime = Truffle.getRuntime();
         final TestValueNode leftValueNode = new TestValueNode(6);
         final TestValueNode rightValueNode = new TestValueNode(7);
         final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
-        final TestRootNode rootNode = new TestRootNode(addNode);
+        final TestRootNode rootNode = new TestRootNode(addNode, instrumenter);
 
         // Creating a call target sets the parent pointers in this tree and is necessary prior to
         // checking any parent/child relationships
         final CallTarget callTarget = runtime.createCallTarget(rootNode);
         // Probe the addition node
-        final Probe probe = addNode.probe();
+        final Probe probe = instrumenter.probe(addNode);
 
         // Check instrumentation with the simplest kind of counters.
         // They should all be removed when the check is finished.
@@ -281,14 +285,16 @@
     }
 
     @Test
-    public void testTagging() {
+    public void testTagging() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter();
+
         // Applies appropriate tags
-        final TestASTProber astProber = new TestASTProber();
-        Probe.registerASTProber(astProber);
+        final TestASTProber astProber = new TestASTProber(instrumenter);
+        instrumenter.registerASTProber(astProber);
 
         // Listens for probes and tags being added
         final TestProbeListener probeListener = new TestProbeListener();
-        Probe.addProbeListener(probeListener);
+        instrumenter.addProbeListener(probeListener);
 
         // Counts all entries to all instances of addition nodes
         final TestMultiCounter additionCounter = new TestMultiCounter();
@@ -302,7 +308,7 @@
         final TestValueNode rightValueNode = new TestValueNode(7);
         final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
 
-        final TestRootNode rootNode = new TestRootNode(addNode);
+        final TestRootNode rootNode = new TestRootNode(addNode, instrumenter);
 
         final CallTarget callTarget = runtime.createCallTarget(rootNode);
 
@@ -310,18 +316,18 @@
         assertEquals(probeListener.probeCount, 3);
         assertEquals(probeListener.tagCount, 3);
 
-        assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1);
-        assertEquals(Probe.findProbesTaggedAs(VALUE_TAG).size(), 2);
+        assertEquals(instrumenter.findProbesTaggedAs(ADD_TAG).size(), 1);
+        assertEquals(instrumenter.findProbesTaggedAs(VALUE_TAG).size(), 2);
 
         // Check that it executes correctly
         assertEquals(13, callTarget.call());
 
         // Dynamically attach a counter for all executions of all Addition nodes
-        for (Probe probe : Probe.findProbesTaggedAs(ADD_TAG)) {
+        for (Probe probe : instrumenter.findProbesTaggedAs(ADD_TAG)) {
             additionCounter.attachCounter(probe);
         }
         // Dynamically attach a counter for all executions of all Value nodes
-        for (Probe probe : Probe.findProbesTaggedAs(VALUE_TAG)) {
+        for (Probe probe : instrumenter.findProbesTaggedAs(VALUE_TAG)) {
             valueCounter.attachCounter(probe);
         }
 
@@ -336,7 +342,7 @@
         assertEquals(additionCounter.count, 1);
         assertEquals(valueCounter.count, 2);
 
-        Probe.unregisterASTProber(astProber);
+        instrumenter.unregisterASTProber(astProber);
     }
 
     private interface TestCounter {
@@ -459,6 +465,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) {
@@ -466,10 +478,10 @@
                 final TestLanguageNode testNode = (TestLanguageNode) node;
 
                 if (node instanceof TestValueNode) {
-                    testNode.probe().tagAs(VALUE_TAG, null);
+                    instrumenter.probe(testNode).tagAs(VALUE_TAG, null);
 
                 } else if (node instanceof TestAdditionNode) {
-                    testNode.probe().tagAs(ADD_TAG, null);
+                    instrumenter.probe(testNode).tagAs(ADD_TAG, null);
 
                 }
             }
@@ -477,7 +489,7 @@
         }
 
         @Override
-        public void probeAST(Node node) {
+        public void probeAST(Instrumenter inst, Node node) {
             node.accept(this);
         }
     }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Mon Sep 14 22:59:51 2015 -0700
@@ -22,18 +22,29 @@
  */
 package com.oracle.truffle.api.test.instrument;
 
+import java.lang.reflect.*;
+
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.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);
 
@@ -142,13 +153,16 @@
     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) {
+        public TestRootNode(TestLanguageNode body, Instrumenter instrumenter) {
             super(TestingLanguage.class, null, null);
+            this.instrumenter = instrumenter;
             this.body = body;
         }
 
@@ -164,7 +178,14 @@
 
         @Override
         public void applyInstrumentation() {
-            Probe.applyASTProbers(body);
+            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.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -22,19 +22,39 @@
  */
 package com.oracle.truffle.api.test.vm;
 
-import com.oracle.truffle.api.*;
+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.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.Executors;
+
+import org.junit.After;
+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.*;
-import com.oracle.truffle.api.instrument.*;
+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.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.*;
-import com.oracle.truffle.api.vm.*;
-import java.io.*;
-import java.util.*;
-import java.util.concurrent.Executors;
-import org.junit.*;
-import static org.junit.Assert.*;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.TruffleVM;
 
 public class ImplicitExplicitExportTest {
     private static Thread mainThread;
@@ -162,13 +182,41 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected List<ASTProber> getASTProbers() {
+            return Collections.emptyList();
+        }
+
+        @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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -22,25 +22,43 @@
  */
 package com.oracle.truffle.api.test.vm;
 
-import static org.junit.Assert.*;
+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.*;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 
-import com.oracle.truffle.api.*;
+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;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.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.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;
 
 /**
  * Bug report validating test.
@@ -90,13 +108,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 {
@@ -118,34 +140,7 @@
 
         @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;
-                }
-
-                @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);
+            return new ANodeWrapper(this);
         }
 
         Object constant() {
@@ -154,6 +149,36 @@
 
     }
 
+    private static class ANodeWrapper extends ANode implements ProbeNode.WrapperNode {
+        @Child ANode child;
+        private ProbeNode probeNode;
+
+        ANodeWrapper(ANode node) {
+            super(1);  // dummy
+            this.child = node;
+        }
+
+        @Override
+        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();
+        }
+    }
+
     private abstract static class AbstractLanguage extends TruffleLanguage<Object> {
     }
 
@@ -161,6 +186,22 @@
     public static final class TestLanguage extends AbstractLanguage implements DebugSupportProvider {
         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"));
@@ -187,23 +228,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();
         }
 
@@ -213,6 +256,12 @@
         }
 
         @Override
+        protected List<ASTProber> getASTProbers() {
+            return Arrays.asList(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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java	Mon Sep 14 22:59:51 2015 -0700
@@ -26,6 +26,7 @@
 
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.impl.Accessor;
 import com.oracle.truffle.api.interop.*;
 import com.oracle.truffle.api.nodes.*;
 
@@ -77,6 +78,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 {
@@ -120,4 +126,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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java	Mon Sep 14 22:59:51 2015 -0700
@@ -33,6 +33,7 @@
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.source.*;
+
 import java.io.*;
 import java.net.*;
 import java.nio.file.*;
@@ -85,6 +86,7 @@
     private final Writer out;
     private final EventConsumer<?>[] handlers;
     private final Map<String, Object> globals;
+    private final Instrumenter instrumenter;
     private Debugger debugger;
 
     /**
@@ -99,6 +101,7 @@
         this.handlers = null;
         this.globals = null;
         this.executor = null;
+        this.instrumenter = null;
     }
 
     /**
@@ -112,6 +115,7 @@
         this.handlers = handlers;
         this.initThread = Thread.currentThread();
         this.globals = new HashMap<>(globals);
+        this.instrumenter = SPI.createInstrumenter();
         this.langs = new HashMap<>();
         Enumeration<URL> en;
         try {
@@ -445,7 +449,6 @@
         try (Closeable d = SPI.executionStart(this, fillIn, s)) {
             TruffleLanguage<?> langImpl = l.getImpl();
             fillLang[0] = langImpl;
-            TruffleVM.findDebuggerSupport(langImpl);
             if (debugger == null) {
                 debugger = fillIn[0];
             }
@@ -607,7 +610,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
@@ -773,6 +776,10 @@
                 String n = props.getProperty(prefix + "className");
                 try {
                     impl = LanguageCache.find(n, loader());
+                    Instrumenter inst = TruffleVM.this.instrumenter;
+                    for (ASTProber prober : SPI.getASTProbers(TruffleVM.this, impl.getClass())) {
+                        inst.registerASTProber(prober);
+                    }
                     env = SPI.attachEnv(TruffleVM.this, impl, out, err, in);
                 } catch (Exception ex) {
                     throw new IllegalStateException("Cannot initialize " + getShortName() + " language with implementation " + n, ex);
@@ -798,6 +805,16 @@
     // Accessor helper methods
     //
 
+    TruffleLanguage<?> findLanguage(Class<? extends TruffleLanguage> languageClazz) {
+        for (Map.Entry<String, Language> entrySet : langs.entrySet()) {
+            Language languageDescription = entrySet.getValue();
+            if (languageClazz.isInstance(languageDescription.impl)) {
+                return languageDescription.impl;
+            }
+        }
+        throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs);
+    }
+
     TruffleLanguage<?> findLanguage(Probe probe) {
         Class<? extends TruffleLanguage> languageClazz = SPI.findLanguage(probe);
         for (Map.Entry<String, Language> entrySet : langs.entrySet()) {
@@ -819,10 +836,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) {
@@ -876,14 +889,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() {
+            return super.createInstrumenter();
+        }
+
+        @Override
+        protected List<ASTProber> getASTProbers(Object vm, Class<? extends TruffleLanguage> languageClass) {
+            return super.getASTProbers(vm, languageClass);
+        }
+
+        @Override
+        protected Instrumenter getInstrumenter(Object obj) {
+            final TruffleVM vm = (TruffleVM) obj;
+            return vm.instrumenter;
         }
 
         @Override
@@ -898,6 +929,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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Mon Sep 14 22:59:51 2015 -0700
@@ -24,17 +24,32 @@
  */
 package com.oracle.truffle.api;
 
-import com.oracle.truffle.api.debug.*;
-import com.oracle.truffle.api.impl.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.source.*;
-import java.io.*;
-import java.lang.annotation.*;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.util.Collections;
+import java.util.List;
 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.Instrument;
+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;
+
 /**
  * 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
@@ -173,11 +188,59 @@
      */
     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);
+
+    /**
+     * Gets the current specification for AST instrumentation for the language; <em>empty</em> if
+     * none.
+     */
+    protected abstract List<ASTProber> getASTProbers();
+
+    /**
+     * 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 Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+     * Advanced Instrument}.
+     *
+     * @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.
@@ -332,6 +395,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);
         }
@@ -357,10 +428,18 @@
         }
 
         @Override
+        protected List<ASTProber> getASTProbers(Object vm, Class<? extends TruffleLanguage> languageClass) {
+            TruffleLanguage impl = findLanguageImpl(vm, languageClass);
+            return impl.getASTProbers();
+        }
+
+        @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/Debugger.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Mon Sep 14 22:59:51 2015 -0700
@@ -45,7 +45,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;
 
@@ -58,6 +58,7 @@
         }
     }
 
+    private final Instrumenter instrumenter;
     private final Object vm;
     private Source lastSource;
 
@@ -77,6 +78,9 @@
         void addWarning(String warning);
     }
 
+    private final BreakpointCallback breakpointCallback;
+    private final WarningLog warningLog;
+
     /**
      * Implementation of line-oriented breakpoints.
      */
@@ -92,9 +96,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
@@ -102,7 +106,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) {
@@ -110,7 +114,7 @@
             }
         };
 
-        final WarningLog warningLog = new WarningLog() {
+        warningLog = new WarningLog() {
 
             public void addWarning(String warning) {
                 assert debugContext != null;
@@ -122,10 +126,6 @@
         this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog);
     }
 
-    Object vm() {
-        return vm;
-    }
-
     /**
      * Sets a breakpoint to halt at a source line.
      *
@@ -254,6 +254,7 @@
         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
@@ -269,15 +270,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;
     }
 
     /**
@@ -401,7 +399,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) {
@@ -415,7 +413,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) {
@@ -434,8 +432,8 @@
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
-            Probe.setAfterTagTrap(null);
+            instrumenter.setBeforeTagTrap(null);
+            instrumenter.setAfterTagTrap(null);
         }
     }
 
@@ -458,7 +456,7 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
+            instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
 
                 @TruffleBoundary
                 @Override
@@ -476,7 +474,7 @@
 
         @Override
         protected void unsetStrategy() {
-            Probe.setAfterTagTrap(null);
+            instrumenter.setAfterTagTrap(null);
         }
     }
 
@@ -502,7 +500,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) {
@@ -527,7 +525,7 @@
                 }
             });
 
-            Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
+            instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
                 @TruffleBoundary
                 @Override
                 public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
@@ -548,8 +546,8 @@
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
-            Probe.setAfterTagTrap(null);
+            instrumenter.setBeforeTagTrap(null);
+            instrumenter.setAfterTagTrap(null);
         }
     }
 
@@ -577,7 +575,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) {
@@ -598,7 +596,7 @@
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
+            instrumenter.setBeforeTagTrap(null);
         }
     }
 
@@ -798,7 +796,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];
             }
@@ -827,8 +826,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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Mon Sep 14 22:59:51 2015 -0700
@@ -67,7 +67,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) {
@@ -90,6 +90,7 @@
         }
     };
 
+    private final Debugger debugger;
     private final BreakpointCallback breakpointCallback;
     private final WarningLog warningLog;
 
@@ -111,17 +112,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) {
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java	Mon Sep 14 22:59:51 2015 -0700
@@ -61,7 +61,7 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        getInstrumenter().addProbeListener(probeListener);
         return true;
     }
 
@@ -72,7 +72,7 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
+        getInstrumenter().removeProbeListener(probeListener);
     }
 
     /**
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Mon Sep 14 22:59:51 2015 -0700
@@ -47,10 +47,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>
@@ -88,6 +88,7 @@
         }
     };
 
+    private final Debugger debugger;
     private final BreakpointCallback breakpointCallback;
     private final WarningLog warningLog;
 
@@ -102,14 +103,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) {
@@ -180,7 +180,7 @@
 
             tagToBreakpoint.put(tag, breakpoint);
 
-            for (Probe probe : Probe.findProbesTaggedAs(tag)) {
+            for (Probe probe : debugger.getInstrumenter().findProbesTaggedAs(tag)) {
                 breakpoint.attach(probe);
             }
         } else {
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Mon Sep 14 22:59:51 2015 -0700
@@ -24,26 +24,45 @@
  */
 package com.oracle.truffle.api.impl;
 
-import com.oracle.truffle.api.*;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.Collections;
+import java.util.List;
+
+import com.oracle.truffle.api.Assumption;
+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.*;
-import com.oracle.truffle.api.instrument.*;
+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.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.source.*;
-import java.io.*;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
+import com.oracle.truffle.api.source.Source;
 
 /**
- * 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 TOOL;
     private static Accessor DEBUG;
     private static final ThreadLocal<Object> CURRENT_VM = new ThreadLocal<>();
 
@@ -64,11 +83,13 @@
                 return false;
             }
 
+            @SuppressWarnings("deprecation")
             @Override
             protected ToolSupportProvider getToolSupport() {
-                return null;
+                throw new UnsupportedOperationException();
             }
 
+            @SuppressWarnings("deprecation")
             @Override
             protected DebugSupportProvider getDebugSupport() {
                 return null;
@@ -83,12 +104,39 @@
             protected Object createContext(TruffleLanguage.Env env) {
                 return null;
             }
+
+            @Override
+            protected List<ASTProber> getASTProbers() {
+                return Collections.emptyList();
+            }
+
+            @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);
@@ -106,6 +154,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();
@@ -116,6 +169,11 @@
                 throw new IllegalStateException();
             }
             DEBUG = this;
+        } else if (this.getClass().getSimpleName().endsWith("Tool")) {
+            if (TOOL != null) {
+                throw new IllegalStateException();
+            }
+            TOOL = this;
         } else {
             if (SPI != null) {
                 throw new IllegalStateException();
@@ -144,12 +202,23 @@
         return API.languageGlobal(env);
     }
 
-    protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) {
-        return API.getToolSupport(l);
+    @Deprecated
+    protected ToolSupportProvider getToolSupport(@SuppressWarnings("unused") TruffleLanguage<?> l) {
+        throw new UnsupportedOperationException();
     }
 
-    protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
-        return API.getDebugSupport(l);
+    @Deprecated
+    protected DebugSupportProvider getDebugSupport(@SuppressWarnings("unused") TruffleLanguage<?> l) {
+        throw new UnsupportedOperationException();
+    }
+
+    protected List<ASTProber> getASTProbers(Object vm, Class<? extends TruffleLanguage> languageClass) {
+        return API.getASTProbers(vm, languageClass);
+    }
+
+    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) {
@@ -176,6 +245,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() {
+        return INSTRUMENT.createInstrumenter();
+    }
+
     private static Reference<Object> previousVM = new WeakReference<>(null);
     private static Assumption oneVM = Truffle.getRuntime().createAssumption();
 
@@ -207,7 +306,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);
@@ -229,4 +328,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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Mon Sep 14 22:59:51 2015 -0700
@@ -31,7 +31,7 @@
  * not yet executed) AST.
  *
  * @see Probe
- * @see Probe#addProbeListener(ProbeListener)
+ * @see Instrumenter#addProbeListener(ProbeListener)
  */
 public interface ASTProber {
 
@@ -40,6 +40,6 @@
      * {@linkplain Probe Probes} to them. Ignore {@linkplain Node#isInstrumentable()
      * 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/InstrumentationTool.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java	Mon Sep 14 22:59:51 2015 -0700
@@ -24,13 +24,15 @@
  */
 package com.oracle.truffle.api.instrument;
 
+import com.oracle.truffle.api.impl.Accessor;
+
 /**
  * {@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>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>
@@ -83,6 +85,8 @@
 
     private ToolState toolState = ToolState.UNINSTALLED;
 
+    private Instrumenter instrumenter;
+
     protected InstrumentationTool() {
     }
 
@@ -92,8 +96,11 @@
      *
      * @throws IllegalStateException if the tool has previously been installed.
      */
-    public final void install() {
+    public final void install(Instrumenter inst) {
         checkUninstalled();
+        if (inst != null) {
+            this.instrumenter = inst;
+        }
         if (internalInstall()) {
             toolState = ToolState.ENABLED;
         }
@@ -157,6 +164,13 @@
 
     protected abstract void internalDispose();
 
+    protected final Instrumenter getInstrumenter() {
+        if (instrumenter == null) {
+            instrumenter = ACCESSOR.getInstrumenter(null);
+        }
+        return instrumenter;
+    }
+
     /**
      * Ensure that the tool is currently installed.
      *
@@ -182,4 +196,14 @@
         }
     }
 
+    static final class AccessorTool extends Accessor {
+
+        @Override
+        protected Instrumenter getInstrumenter(Object vm) {
+            return super.getInstrumenter(null);
+        }
+    }
+
+    static final AccessorTool ACCESSOR = new AccessorTool();
+
 }
--- /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 14 22:59:51 2015 -0700
@@ -0,0 +1,347 @@
+/*
+ * 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.List;
+
+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.ProbeNode.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;
+
+/**
+ * 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 final List<ASTProber> astProbers = new ArrayList<>();
+
+    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() {
+    }
+
+    /**
+     * 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 Node#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
+     */
+    @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 (!(node.isInstrumentable())) {
+            throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, node, null);
+        }
+
+        // Create a new wrapper/probe with this node as its child.
+        final WrapperNode wrapper = node.createWrapperNode();
+
+        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.insertProbe(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.
+     */
+    public void registerASTProber(ASTProber prober) {
+        astProbers.add(prober);
+    }
+
+    public void unregisterASTProber(ASTProber prober) {
+        astProbers.remove(prober);
+    }
+
+    @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;
+    }
+
+    /**
+     * 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() {
+            return new Instrumenter();
+        }
+
+        @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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Mon Sep 14 22:59:51 2015 -0700
@@ -24,19 +24,21 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import java.io.*;
-import java.lang.ref.*;
-import java.util.*;
+import java.io.PrintStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
 
-import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.Assumption;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.impl.Accessor;
+import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
-import com.oracle.truffle.api.nodes.*;
-import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.api.utilities.*;
-
-//TODO (mlvdv) these statics should not be global.  Move them to some kind of context.
+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:
@@ -68,13 +70,14 @@
  * 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 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 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>
+ * 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
@@ -114,173 +117,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<>();
@@ -306,17 +143,10 @@
     /**
      * Intended for use only by {@link ProbeNode}.
      */
-    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;
     }
 
@@ -344,16 +174,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;
@@ -432,23 +262,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;
     }
 
     /**
@@ -469,13 +303,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 = "";
@@ -487,18 +323,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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java	Mon Sep 14 22:59:51 2015 -0700
@@ -28,7 +28,8 @@
 import com.oracle.truffle.api.nodes.*;
 
 /**
- * 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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java	Mon Sep 14 22:59:51 2015 -0700
@@ -76,7 +76,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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java	Mon Sep 14 22:59:51 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 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Mon Sep 14 22:59:51 2015 -0700
@@ -26,12 +26,12 @@
 
 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.*;
+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.*;
-import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
 
 /**
  * Implementation class & interface for enabling the attachment of {@linkplain Probe Probes} to
@@ -114,20 +114,6 @@
         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;
-    }
-
     // Never changed once set.
     @CompilationFinal Probe probe = null;
     /**
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Mon Sep 14 22:59:51 2015 -0700
@@ -24,17 +24,28 @@
  */
 package com.oracle.truffle.api.nodes;
 
-import java.lang.annotation.*;
-import java.util.*;
-import java.util.concurrent.*;
+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.*;
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.ReplaceObserver;
+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.*;
+import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
-import com.oracle.truffle.api.source.*;
-import com.oracle.truffle.api.utilities.*;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.api.utilities.JSONHelper;
 
 /**
  * Abstract base class for all Truffle nodes.
@@ -436,57 +447,6 @@
     }
 
     /**
-     * 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
@@ -563,6 +523,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>() {
@@ -592,8 +556,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/RootNode.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Mon Sep 14 22:59:51 2015 -0700
@@ -180,9 +180,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.
@@ -190,7 +191,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.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Mon Sep 14 22:59:51 2015 -0700
@@ -40,27 +40,46 @@
  */
 package com.oracle.truffle.sl.test.instrument;
 
-import java.io.*;
-import java.nio.charset.*;
-import java.nio.file.*;
-import java.nio.file.attribute.*;
-import java.util.*;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.lang.reflect.Field;
+import java.nio.charset.Charset;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
 
-import org.junit.*;
-import org.junit.internal.*;
-import org.junit.runner.*;
-import org.junit.runner.manipulation.*;
-import org.junit.runner.notification.*;
-import org.junit.runners.*;
-import org.junit.runners.model.*;
+import org.junit.Assert;
+import org.junit.internal.TextListener;
+import org.junit.runner.Description;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Result;
+import org.junit.runner.manipulation.Filter;
+import org.junit.runner.manipulation.NoTestsRemainException;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.ParentRunner;
+import org.junit.runners.model.InitializationError;
 
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.instrument.impl.*;
+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.StandardSyntaxTag;
+import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener;
 import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.vm.*;
-import com.oracle.truffle.sl.nodes.instrument.*;
-import com.oracle.truffle.sl.nodes.local.*;
-import com.oracle.truffle.sl.test.*;
+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;
 
 /**
@@ -80,7 +99,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;
@@ -101,16 +120,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);
         }
     }
 
@@ -222,18 +237,23 @@
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         PrintWriter printer = new PrintWriter(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().stdIn(new BufferedReader(new StringReader(testCase.testInput))).stdOut(printer).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(printer);
                     probe.attach(Instrument.create(slPrintAssigmentValueListener, "SL print assignment value"));
                 }
@@ -250,13 +270,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);
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Mon Sep 14 22:59:51 2015 -0700
@@ -58,12 +58,14 @@
 import com.oracle.truffle.sl.nodes.local.*;
 import com.oracle.truffle.sl.parser.*;
 import com.oracle.truffle.sl.runtime.*;
+
 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.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -142,7 +144,7 @@
  */
 
 /*
- * 
+ *
  * <p> <b>Tools:</b><br> The use of some of Truffle's support for developer tools (based on the
  * Truffle Instrumentation Framework) are demonstrated in this file, for example: <ul> <li>a
  * {@linkplain NodeExecCounter counter for node executions}, tabulated by node type; and</li> <li>a
@@ -154,10 +156,11 @@
 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 List<ASTProber> astProbers;
 
     private SLLanguage() {
+        this.astProbers = new ArrayList<>();
+        astProbers.add(new SLStandardASTProber());
     }
 
     public static final SLLanguage INSTANCE = new SLLanguage();
@@ -210,6 +213,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");
@@ -432,28 +436,56 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
-        return getDebugSupport();
+    protected Visualizer getVisualizer() {
+        if (visualizer == null) {
+            visualizer = new SLDefaultVisualizer();
+        }
+        return visualizer;
+    }
+
+    @Override
+    protected List<ASTProber> getASTProbers() {
+        return astProbers;
+    }
+
+    @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();
@@ -485,46 +517,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/SLRootNode.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Mon Sep 14 22:59:51 2015 -0700
@@ -41,13 +41,14 @@
 package com.oracle.truffle.sl.nodes;
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.frame.FrameDescriptor;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.sl.SLLanguage;
-import com.oracle.truffle.sl.builtins.*;
-import com.oracle.truffle.sl.nodes.controlflow.*;
-import com.oracle.truffle.sl.runtime.*;
+import com.oracle.truffle.sl.builtins.SLBuiltinNode;
+import com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode;
+import com.oracle.truffle.sl.runtime.SLContext;
 
 /**
  * The root of all SL execution trees. It is a Truffle requirement that the tree root extends the
@@ -97,7 +98,7 @@
 
     @Override
     public void applyInstrumentation() {
-        Probe.applyASTProbers(bodyNode);
+        super.applyInstrumentation(bodyNode);
     }
 
     @Override
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java	Mon Sep 14 22:59:51 2015 -0700
@@ -52,39 +52,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.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -36,10 +36,11 @@
 public class CoverageTrackerTest {
 
     @Test
-    public void testNoExecution() {
+    public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         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);
@@ -52,37 +53,40 @@
     }
 
     @Test
-    public void testToolCreatedTooLate() {
-        final RootNode expr13rootNode = createExpr13TestRootNode();
+    public void testToolCreatedTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
+        final RootNode expr13rootNode = TestNodes.createExpr13TestRootNode(instrumenter);
         final CoverageTracker tool = new CoverageTracker();
-        tool.install();
+        tool.install(instrumenter);
         assertEquals(13, expr13rootNode.execute(null));
         assertTrue(tool.getCounts().isEmpty());
         tool.dispose();
     }
 
     @Test
-    public void testToolInstalledcTooLate() {
+    public void testToolInstalledcTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         final CoverageTracker tool = new CoverageTracker();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        tool.install();
+        final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter);
+        tool.install(instrumenter);
         assertEquals(13, expr13rootNode.execute(null));
         assertTrue(tool.getCounts().isEmpty());
         tool.dispose();
     }
 
     @Test
-    public void testCountingCoverage() {
+    public void testCountingCoverage() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         final CoverageTracker tool = new CoverageTracker();
-        tool.install();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
+        tool.install(instrumenter);
+        final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter);
 
         // Not probed yet.
         assertEquals(13, expr13rootNode.execute(null));
         assertTrue(tool.getCounts().isEmpty());
 
         final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
+        final Probe probe = instrumenter.probe(addNode);
 
         // Probed but not tagged yet.
         assertEquals(13, expr13rootNode.execute(null));
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -37,15 +37,16 @@
 public class LineToProbesMapTest {
 
     @Test
-    public void testToolCreatedTooLate() {
-        final RootNode expr13rootNode = createExpr13TestRootNode();
+    public void testToolCreatedTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
+        final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter);
         final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
+        final Probe probe = instrumenter.probe(addNode);
         final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
         assertEquals(lineLocation, expr13Line2);
 
         final LineToProbesMap tool = new LineToProbesMap();
-        tool.install();
+        tool.install(instrumenter);
 
         assertNull(tool.findFirstProbe(expr13Line1));
         assertNull(tool.findFirstProbe(expr13Line2));
@@ -53,16 +54,17 @@
     }
 
     @Test
-    public void testToolInstalledTooLate() {
+    public void testToolInstalledTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         final LineToProbesMap tool = new LineToProbesMap();
 
-        final RootNode expr13rootNode = createExpr13TestRootNode();
+        final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter);
         final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
+        final Probe probe = instrumenter.probe(addNode);
         final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
         assertEquals(lineLocation, expr13Line2);
 
-        tool.install();
+        tool.install(instrumenter);
 
         assertNull(tool.findFirstProbe(expr13Line1));
         assertNull(tool.findFirstProbe(expr13Line2));
@@ -70,13 +72,14 @@
     }
 
     @Test
-    public void testMapping() {
+    public void testMapping() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         final LineToProbesMap tool = new LineToProbesMap();
-        tool.install();
+        tool.install(instrumenter);
 
-        final RootNode expr13rootNode = createExpr13TestRootNode();
+        final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter);
         final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
+        final Probe probe = instrumenter.probe(addNode);
         final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
         assertEquals(lineLocation, expr13Line2);
 
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -40,10 +40,11 @@
 public class NodeExecCounterTest {
 
     @Test
-    public void testNoExecution() {
+    public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         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);
@@ -56,30 +57,33 @@
     }
 
     @Test
-    public void testToolCreatedTooLate() {
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
+    public void testToolCreatedTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
+        final CallTarget expr13callTarget = createExpr13TestCallTarget(instrumenter);
         final NodeExecCounter tool = new NodeExecCounter();
-        tool.install();
+        tool.install(instrumenter);
         assertEquals(13, expr13callTarget.call());
         assertEquals(tool.getCounts().length, 0);
         tool.dispose();
     }
 
     @Test
-    public void testToolInstalledcTooLate() {
+    public void testToolInstalledcTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         final NodeExecCounter tool = new NodeExecCounter();
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
-        tool.install();
+        final CallTarget expr13callTarget = createExpr13TestCallTarget(instrumenter);
+        tool.install(instrumenter);
         assertEquals(13, expr13callTarget.call());
         assertEquals(tool.getCounts().length, 0);
         tool.dispose();
     }
 
     @Test
-    public void testCountingAll() {
+    public void testCountingAll() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         final NodeExecCounter tool = new NodeExecCounter();
-        tool.install();
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
+        tool.install(instrumenter);
+        final CallTarget expr13callTarget = createExpr13TestCallTarget(instrumenter);
 
         // execute once
         assertEquals(13, expr13callTarget.call());
@@ -121,17 +125,18 @@
     }
 
     @Test
-    public void testCountingTagged() {
+    public void testCountingTagged() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         final NodeExecCounter tool = new NodeExecCounter(StandardSyntaxTag.STATEMENT);
-        tool.install();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
+        tool.install(instrumenter);
+        final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter);
 
         // 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();
+        final Probe probe = instrumenter.probe(addNode);
 
         // Probed but not tagged yet.
         assertEquals(13, expr13rootNode.execute(null));
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java	Mon Sep 14 22:59:51 2015 -0700
@@ -24,12 +24,15 @@
  */
 package com.oracle.truffle.tools.test;
 
+import java.lang.reflect.*;
+
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.nodes.*;
 import com.oracle.truffle.api.source.*;
+import com.oracle.truffle.api.vm.TruffleVM;
 
 /**
  * Nodes and an {@linkplain CallTarget executable ASTs} for testing.
@@ -46,21 +49,29 @@
     /**
      * An executable addition expression that evaluates to 13.
      */
-    static CallTarget createExpr13TestCallTarget() {
-        final RootNode rootNode = createExpr13TestRootNode();
+    static CallTarget createExpr13TestCallTarget(Instrumenter instrumenter) {
+        final RootNode rootNode = createExpr13TestRootNode(instrumenter);
         return Truffle.getRuntime().createCallTarget(rootNode);
     }
 
     /**
      * Root holding an addition expression that evaluates to 13.
      */
-    static RootNode createExpr13TestRootNode() {
+    static RootNode createExpr13TestRootNode(Instrumenter instrumenter) {
         final TestLanguageNode ast = createExpr13AST();
-        final TestRootNode rootNode = new TestRootNode(ast);
+        final TestRootNode rootNode = new TestRootNode(ast, instrumenter);
         rootNode.adoptChildren();
         return rootNode;
     }
 
+    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;
+    }
+
     /**
      * Addition expression that evaluates to 13, with faked source attribution.
      */
@@ -156,13 +167,16 @@
     static class TestRootNode extends RootNode {
         @Child private TestLanguageNode body;
 
+        private 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) {
+        public TestRootNode(TestLanguageNode body, Instrumenter instrumenter) {
             super(TruffleLanguage.class, null, null);
+            this.instrumenter = instrumenter;
             this.body = body;
         }
 
@@ -178,7 +192,14 @@
 
         @Override
         public void applyInstrumentation() {
-            Probe.applyASTProbers(body);
+            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("TestNodes");
+            }
         }
     }
 
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java	Mon Sep 14 22:59:51 2015 -0700
@@ -36,10 +36,11 @@
 public class TruffleToolTest {
 
     @Test
-    public void testEmptyLifeCycle() {
+    public void testEmptyLifeCycle() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
         assertFalse(tool.isEnabled());
-        tool.install();
+        tool.install(instrumenter);
         assertTrue(tool.isEnabled());
         tool.reset();
         assertTrue(tool.isEnabled());
@@ -74,40 +75,45 @@
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyInstalled() {
+    public void testAlreadyInstalled() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = TestNodes.createInstrumenter();
         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 = TestNodes.createInstrumenter();
         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 = TestNodes.createInstrumenter();
         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 = TestNodes.createInstrumenter();
         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 = TestNodes.createInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        tool.install(instrumenter);
         tool.dispose();
         tool.dispose();
     }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Mon Sep 14 22:59:51 2015 -0700
@@ -102,7 +102,7 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        getInstrumenter().addProbeListener(probeListener);
         return true;
     }
 
@@ -113,7 +113,7 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
+        getInstrumenter().removeProbeListener(probeListener);
         for (Instrument instrument : instruments) {
             instrument.dispose();
         }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java	Mon Sep 14 22:59:51 2015 -0700
@@ -61,7 +61,7 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        getInstrumenter().addProbeListener(probeListener);
         return true;
     }
 
@@ -72,7 +72,7 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
+        getInstrumenter().removeProbeListener(probeListener);
     }
 
     /**
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java	Mon Sep 07 17:54:35 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java	Mon Sep 14 22:59:51 2015 -0700
@@ -155,7 +155,7 @@
      * Create a per node-type execution counting tool for all nodes in subsequently created ASTs.
      */
     public NodeExecCounter() {
-        this.countingTag = null;
+        this(null);
     }
 
     /**
@@ -170,10 +170,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;
     }
@@ -187,10 +187,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();
@@ -274,24 +274,27 @@
     /**
      * 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 (node.isInstrumentable()) {
-                try {
-                    final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter");
-                    instruments.add(instrument);
-                    node.probe().attach(instrument);
-                } catch (ProbeException ex) {
-                    failures.add(ex.getFailure());
+                    if (node.isInstrumentable()) {
+                        try {
+                            final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter");
+                            instruments.add(instrument);
+                            instrumenter.probe(node).attach(instrument);
+                        } catch (ProbeException ex) {
+                            failures.add(ex.getFailure());
+                        }
+                    }
+                    return true;
                 }
-            }
-            return true;
-        }
 
-        public void probeAST(Node node) {
-            node.accept(this);
+            });
         }
     }