changeset 22266:0d36601f233e

Merge revised Instrumentation framework into the Polyglot API - Required language implementation support now in TruffleLanguage - Instrumentation services provided by the new Instrumenter class - Reduced public API exposure; communication with other components via Accessor - Several methods removed from the Node class - Many test rewritten or using a new "test mode" because of limited access to Engine services Merge with c66f520ad8562b906a878e9b3293aaf54270db90
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Wed, 30 Sep 2015 16:33:56 -0700
parents 3d9e283e2487 (current diff) c66f520ad856 (diff)
children ebcb4878b011
files mx.truffle/suite.py truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/AssumptionsTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BadLongOverflowSpecializationTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BinaryNodeTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CodeFormatTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteEvaluatedTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImportGuardsTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MergeSpecializationsTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsWithArgumentsTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NegatedGuardsTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildNoNameTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest2.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationFallthroughTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationGroupingTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestSerialization.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/UnsupportedSpecializationTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/Interop.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/utilities/InstrumentationTestMode.java truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/ClassInteropTest.java truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/InstrumentationTestMode.java truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropSpeedTest.java truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropTest.java truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/MethodMessageTest.java truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveArrayInteropTest.java truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveRawArrayInteropTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/CallTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildrenNodesTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FinalFieldTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameSlotTypeSpecializationTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InterfaceChildFieldTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReturnTypeSpecializationTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TruffleRuntimeTest.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/utilities/InstrumentationTestMode.java truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/GlobalSymbolTest.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/PolyglotEngine.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/Breakpoint.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportException.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.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/LineBreakpoint.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/TagBreakpoint.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/Instrument.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationException.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/SyntaxTagTrap.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolSupportProvider.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/NodeUtil.java truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLJavaInteropTest.java truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.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.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.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 148 files changed, 5311 insertions(+), 3426 deletions(-) [+]
line wrap: on
line diff
--- a/mx.truffle/suite.py	Tue Sep 29 18:04:11 2015 +0200
+++ b/mx.truffle/suite.py	Wed Sep 30 16:33:56 2015 -0700
@@ -84,6 +84,7 @@
       "sourceDirs" : ["src"],
       "dependencies" : [
         "com.oracle.truffle.dsl.processor",
+        "com.oracle.truffle.api.vm",
         "mx:JUNIT",
       ],
       "checkstyle" : "com.oracle.truffle.dsl.processor",
@@ -127,6 +128,7 @@
       "subDir" : "truffle",
       "sourceDirs" : ["src"],
       "dependencies" : [
+        "com.oracle.truffle.api.interop.java",
         "com.oracle.truffle.api.vm",
         "mx:JUNIT"
       ],
@@ -193,6 +195,7 @@
           ],
       "checkstyle" : "com.oracle.truffle.api",
       "javaCompliance" : "1.7",
+      "annotationProcessors" : ["TRUFFLE_DSL_PROCESSOR"],
       "workingSets" : "Truffle,Tools",
     },
 
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.ImplicitCast;
@@ -32,15 +37,24 @@
 import com.oracle.truffle.api.dsl.TypeSystem;
 import com.oracle.truffle.api.dsl.TypeSystemReference;
 import com.oracle.truffle.api.dsl.test.ArrayTestFactory.TestNode1NodeGen;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.nodes.UnexpectedResultException;
-import org.junit.Assert;
-import org.junit.Test;
 
 public class ArrayTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testNode1() {
         final TestNode1 node = TestNode1NodeGen.create(null);
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/AssumptionsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/AssumptionsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,18 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
+import static com.oracle.truffle.api.dsl.test.TestHelper.getNode;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.Assumption;
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
@@ -36,17 +48,21 @@
 import com.oracle.truffle.api.dsl.test.AssumptionsTestFactory.MethodTestFactory;
 import com.oracle.truffle.api.dsl.test.AssumptionsTestFactory.NodeFieldTest2Factory;
 import com.oracle.truffle.api.dsl.test.AssumptionsTestFactory.StaticFieldTestFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
-import static com.oracle.truffle.api.dsl.test.TestHelper.getNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import java.util.HashMap;
-import java.util.Map;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class AssumptionsTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testField() {
         CallTarget root = createCallTarget(FieldTestFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BadLongOverflowSpecializationTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BadLongOverflowSpecializationTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,18 +22,33 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.BadLongOverflowSpecializationTestFactory.ImplicitCastExclusionFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class BadLongOverflowSpecializationTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     /* Regression test for */
 
     @NodeChild
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BinaryNodeTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/BinaryNodeTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,20 +22,35 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.BinaryNodeTestFactory.AddNodeFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class BinaryNodeTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testAdd() {
         TestRootNode<AddNode> node = createRoot(AddNodeFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CachedTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,15 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.assertionsEnabled;
+import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -39,16 +48,22 @@
 import com.oracle.truffle.api.dsl.test.CachedTestFactory.TestGuardWithJustCachedParameterFactory;
 import com.oracle.truffle.api.dsl.test.CachedTestFactory.TestMultipleCachesFactory;
 import com.oracle.truffle.api.dsl.test.CachedTestFactory.UnboundCacheFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.assertionsEnabled;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 @SuppressWarnings("unused")
 public class CachedTest {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testUnboundCache() {
         CallTarget root = createCallTarget(UnboundCacheFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CodeFormatTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/CodeFormatTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,17 +22,31 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.CodeFormatTestFactory.LineWrappingTestFactory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import org.junit.Assert;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 /**
  * Tests the generated code compiles without warnings for unusual large guard names.
  */
 public class CodeFormatTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         Assert.assertEquals(42, TestHelper.createCallTarget(LineWrappingTestFactory.create()).call());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ContainsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ContainsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,18 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.array;
+import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -33,21 +45,24 @@
 import com.oracle.truffle.api.dsl.test.ContainsTestFactory.Contains4Factory;
 import com.oracle.truffle.api.dsl.test.ContainsTestFactory.PolymorphicToMonomorphic0Factory;
 import com.oracle.truffle.api.dsl.test.TestHelper.ExecutionListener;
-import static com.oracle.truffle.api.dsl.test.TestHelper.array;
-import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.nodes.NodeCost;
-import static org.hamcrest.CoreMatchers.is;
-import org.junit.Assert;
-import static org.junit.Assert.assertThat;
-import org.junit.Test;
 
 @SuppressWarnings("unused")
 public class ContainsTest {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     /*
      * Tests a simple monomorphic inclusion.
      */
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteEvaluatedTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ExecuteEvaluatedTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
@@ -43,14 +48,23 @@
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ChildrenNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.UnexpectedResultException;
-import org.junit.Assert;
-import org.junit.Test;
 
 public class ExecuteEvaluatedTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testSingleEvaluated() {
         ArgumentNode arg0 = new ArgumentNode(0);
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/FallbackTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,16 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.array;
+import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -30,23 +40,28 @@
 import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback2Factory;
 import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback3Factory;
 import com.oracle.truffle.api.dsl.test.FallbackTestFactory.Fallback4Factory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.array;
-import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeUtil;
-import org.junit.Assert;
-import org.junit.Test;
 
 public class FallbackTest {
 
     private static final Object UNKNOWN_OBJECT = new Object() {
     };
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testFallback1() {
         assertRuns(Fallback1Factory.getInstance(), //
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,9 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import org.junit.Assert;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.ImplicitCast;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
@@ -34,8 +37,6 @@
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import org.junit.Assert;
-import org.junit.Test;
 
 public class ImplicitCastTest {
 
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImportGuardsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImportGuardsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,17 +22,32 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.array;
+import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.ImportStatic;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.ImportGuardsTestFactory.ImportGuards6Factory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.array;
-import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class ImportGuardsTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @ImportStatic(Imports0.class)
     @NodeChild("a")
     static class ImportGuards0 extends ValueNode {
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/IntegerLiteralGuardsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/IntegerLiteralGuardsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,13 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -29,14 +36,22 @@
 import com.oracle.truffle.api.dsl.test.IntegerLiteralGuardsTestFactory.DecimalLiteralTestFactory;
 import com.oracle.truffle.api.dsl.test.IntegerLiteralGuardsTestFactory.HexLiteralTestFactory;
 import com.oracle.truffle.api.dsl.test.IntegerLiteralGuardsTestFactory.OctalLiteralTestFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 @SuppressWarnings("unused")
 public class IntegerLiteralGuardsTest {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testDecimalLiteral() {
         CallTarget root = createCallTarget(DecimalLiteralTestFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LazyClassLoadingTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,18 +22,34 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeFactory;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.LazyClassLoadingTestFactory.TestNodeFactory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import org.junit.Assert;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class LazyClassLoadingTest {
+
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         String factoryName = TestNodeFactory.class.getName();
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LimitTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/LimitTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,14 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -31,15 +39,22 @@
 import com.oracle.truffle.api.dsl.test.LimitTestFactory.DefaultLimit3TestFactory;
 import com.oracle.truffle.api.dsl.test.LimitTestFactory.LocalLimitTestFactory;
 import com.oracle.truffle.api.dsl.test.LimitTestFactory.MethodLimitTestFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 @SuppressWarnings("unused")
 public class LimitTest {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testDefaultLimit3() {
         CallTarget root = createCallTarget(DefaultLimit3TestFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MergeSpecializationsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MergeSpecializationsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,18 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeFactory;
@@ -30,21 +42,25 @@
 import com.oracle.truffle.api.dsl.internal.SpecializedNode;
 import com.oracle.truffle.api.dsl.test.MergeSpecializationsTestFactory.TestCachedNodeFactory;
 import com.oracle.truffle.api.dsl.test.MergeSpecializationsTestFactory.TestNodeFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.nodes.Node;
-import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import org.junit.Test;
 
 public class MergeSpecializationsTest {
 
     private static final int THREADS = 50;
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @NodeChild
     @SuppressWarnings("unused")
     abstract static class TestNode extends ValueNode {
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,15 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
+import static com.oracle.truffle.api.dsl.test.TestHelper.getNode;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -45,16 +54,22 @@
 import com.oracle.truffle.api.dsl.test.MethodGuardsTestFactory.GuardStaticFieldTestFactory;
 import com.oracle.truffle.api.dsl.test.MethodGuardsTestFactory.GuardStaticFinalFieldCompareTestFactory;
 import com.oracle.truffle.api.dsl.test.MethodGuardsTestFactory.GuardUnboundMethodTestFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
-import static com.oracle.truffle.api.dsl.test.TestHelper.getNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 @SuppressWarnings("unused")
 public class MethodGuardsTest {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testGuardEqual() {
         CallTarget root = createCallTarget(GuardEqualTestFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsWithArgumentsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/MethodGuardsWithArgumentsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,14 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -32,15 +40,22 @@
 import com.oracle.truffle.api.dsl.test.MethodGuardsWithArgumentsTestFactory.MArgumentsDouble2Factory;
 import com.oracle.truffle.api.dsl.test.MethodGuardsWithArgumentsTestFactory.MArgumentsDouble3Factory;
 import com.oracle.truffle.api.dsl.test.MethodGuardsWithArgumentsTestFactory.MArgumentsSingle2Factory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import org.junit.Assert;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class MethodGuardsWithArgumentsTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testMArguments0() {
         TestRootNode<MArguments0> root = createRoot(MArguments0Factory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NegatedGuardsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NegatedGuardsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,17 +22,32 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.api.dsl.test.NegatedGuardsTestFactory.NegatedGuardNodeFactory;
 import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
 import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.dsl.test.NegatedGuardsTestFactory.NegatedGuardNodeFactory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import org.junit.Assert;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class NegatedGuardsTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testGuardGlobal() {
         TestRootNode<NegatedGuardNode> root = createRoot(NegatedGuardNodeFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildNoNameTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildNoNameTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -29,12 +29,26 @@
 import com.oracle.truffle.api.dsl.test.NodeChildNoNameTestFactory.ThreeArgsNoNameFactory;
 import com.oracle.truffle.api.dsl.test.NodeChildNoNameTestFactory.TwoArgsNoNameFactory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.frame.VirtualFrame;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 public class NodeChildNoNameTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testOneArg() {
         ValueNode node = new ConstantNode();
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeChildTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,17 +22,32 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.IntFieldTestNodeFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class NodeChildTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testIntField() {
         assertEquals(42, createCallTarget(IntFieldTestNodeFactory.create(42)).call());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NodeFieldTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,13 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.NodeField;
 import com.oracle.truffle.api.dsl.NodeFields;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -32,13 +39,21 @@
 import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.RewriteTestNodeFactory;
 import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.StringFieldTestNodeFactory;
 import com.oracle.truffle.api.dsl.test.NodeFieldTestFactory.TestContainerFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class NodeFieldTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testIntField() {
         assertEquals(42, createCallTarget(IntFieldTestNodeFactory.create(42)).call());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullLiteralGuardsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullLiteralGuardsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,19 +22,34 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.NullLiteralGuardsTestFactory.CompareNotNullNodeFactory;
 import com.oracle.truffle.api.dsl.test.NullLiteralGuardsTestFactory.CompareObjectsNullNodeFactory;
 import com.oracle.truffle.api.dsl.test.NullLiteralGuardsTestFactory.CompareStringNullNodeFactory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createCallTarget;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ChildrenNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 @SuppressWarnings("unused")
 public class NullLiteralGuardsTest {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testCompareObjectsNull() {
         CallTarget root = createCallTarget(CompareObjectsNullNodeFactory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/NullTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,19 +22,34 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.NullTestFactory.NullTest1Factory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class NullTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testGuardInvocations() {
         TestRootNode<NullTest1> root = createRoot(NullTest1Factory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,19 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.array;
+import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
@@ -29,21 +42,23 @@
 import com.oracle.truffle.api.dsl.test.PolymorphicTestFactory.Polymorphic2Factory;
 import com.oracle.truffle.api.dsl.test.PolymorphicTestFactory.Polymorphic3Factory;
 import com.oracle.truffle.api.dsl.test.TestHelper.ExecutionListener;
-import static com.oracle.truffle.api.dsl.test.TestHelper.array;
-import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeUtil;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Set;
-import org.junit.Assert;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
 
 public class PolymorphicTest {
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
 
     private static void assertParent(Node expectedParent, Node child) {
         Node parent = child.getParent();
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest2.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/PolymorphicTest2.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,17 +22,32 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.BinaryNodeTest.BinaryNode;
 import com.oracle.truffle.api.dsl.test.PolymorphicTest2Factory.Polymorphic1Factory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.nodes.NodeCost;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
 
 public class PolymorphicTest2 {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testMultipleTypes() {
         /* Tests the unexpected polymorphic case. */
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,12 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.ImplicitCast;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -36,11 +42,20 @@
 import com.oracle.truffle.api.dsl.test.ShortCircuitTestFactory.VarArgsNodeFactory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ArgumentNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class ShortCircuitTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testSingleChild1() {
         ArgumentNode arg0 = new ArgumentNode(0);
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SourceSectionTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,22 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
+import static com.oracle.truffle.api.dsl.test.TestHelper.createRootPrefix;
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.sameInstance;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.theories.DataPoints;
+import org.junit.experimental.theories.Theories;
+import org.junit.experimental.theories.Theory;
+import org.junit.runner.RunWith;
+
 import com.oracle.truffle.api.dsl.CreateCast;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -29,29 +45,28 @@
 import com.oracle.truffle.api.dsl.internal.SpecializationNode;
 import com.oracle.truffle.api.dsl.test.SourceSectionTestFactory.SourceSection0Factory;
 import com.oracle.truffle.api.dsl.test.SourceSectionTestFactory.SourceSection1Factory;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRoot;
-import static com.oracle.truffle.api.dsl.test.TestHelper.createRootPrefix;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ArgumentNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.SourceSection;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.sameInstance;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertThat;
-import org.junit.Test;
-import org.junit.experimental.theories.DataPoints;
-import org.junit.experimental.theories.Theories;
-import org.junit.experimental.theories.Theory;
-import org.junit.runner.RunWith;
 
 @RunWith(Theories.class)
 public class SourceSectionTest {
 
     @DataPoints public static final int[] data = new int[]{1, 2, 3, 4};
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Theory
     public void testSourceSections(int value0, int value1, int value2) {
         TestRootNode<SourceSection0> root = createRoot(SourceSection0Factory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationFallthroughTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationFallthroughTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,14 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.array;
+import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
@@ -33,15 +41,22 @@
 import com.oracle.truffle.api.dsl.test.SpecializationFallthroughTestFactory.FallthroughTest4Factory;
 import com.oracle.truffle.api.dsl.test.SpecializationFallthroughTestFactory.FallthroughTest5Factory;
 import com.oracle.truffle.api.dsl.test.TestHelper.ExecutionListener;
-import static com.oracle.truffle.api.dsl.test.TestHelper.array;
-import static com.oracle.truffle.api.dsl.test.TestHelper.assertRuns;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
-import org.junit.Assert;
-import org.junit.Test;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 
 public class SpecializationFallthroughTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testFallthrough0() {
         assertRuns(FallthroughTest0Factory.getInstance(), //
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationGroupingTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/SpecializationGroupingTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,16 +22,20 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.test.SpecializationGroupingTestFactory.TestElseConnectionBug1Factory;
 import com.oracle.truffle.api.dsl.test.SpecializationGroupingTestFactory.TestElseConnectionBug2Factory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.SlowPathException;
-import org.junit.Assert;
-import org.junit.Test;
 
 /**
  * Tests execution counts of guards. While we do not make guarantees for guard invocation except for
@@ -40,6 +44,16 @@
  */
 public class SpecializationGroupingTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testElseConnectionBug1() {
         CallTarget target = TestHelper.createCallTarget(TestElseConnectionBug1Factory.create(new GenericInt()));
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestSerialization.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestSerialization.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,19 +22,34 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.Specialization;
-import static com.oracle.truffle.api.dsl.test.TestHelper.executeWith;
 import com.oracle.truffle.api.dsl.test.TestSerializationFactory.SerializedNodeFactory;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode;
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.nodes.NodeCost;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import org.junit.Test;
 
 public class TestSerialization {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testUpdateRoot() {
         /* Tests the unexpected polymorphic case. */
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,13 +22,17 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import java.io.IOException;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.debug.DebugSupportProvider;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.Source;
-import java.io.IOException;
 
 public final class TestingLanguage extends TruffleLanguage<Object> {
     public static final TestingLanguage INSTANCE = new TestingLanguage();
@@ -58,12 +62,27 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
+    protected Visualizer getVisualizer() {
         return null;
     }
 
     @Override
-    protected DebugSupportProvider getDebugSupport() {
+    protected boolean isInstrumentable(Node node) {
+        return false;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
+        return null;
+    }
+
+    @Override
+    protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+        return null;
+    }
+
+    @Override
+    protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
         return null;
     }
 
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/UnsupportedSpecializationTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/UnsupportedSpecializationTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,13 @@
  */
 package com.oracle.truffle.api.dsl.test;
 
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.dsl.NodeChild;
 import com.oracle.truffle.api.dsl.NodeChildren;
 import com.oracle.truffle.api.dsl.ShortCircuit;
@@ -31,14 +38,22 @@
 import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode;
 import com.oracle.truffle.api.dsl.test.UnsupportedSpecializationTestFactory.Unsupported1Factory;
 import com.oracle.truffle.api.dsl.test.UnsupportedSpecializationTestFactory.Unsupported2Factory;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeUtil;
-import java.util.List;
-import org.junit.Assert;
-import org.junit.Test;
 
 public class UnsupportedSpecializationTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testUnsupported1() {
         TestRootNode<Unsupported1> root = TestHelper.createRoot(Unsupported1Factory.getInstance());
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/FunctionCall.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/FunctionCall.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,19 +22,24 @@
  */
 package com.oracle.truffle.api.dsl.test.examples;
 
+import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments;
+import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createDummyTarget;
+import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
-import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments;
-import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createDummyTarget;
-import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget;
 import com.oracle.truffle.api.dsl.test.examples.FunctionCallFactory.FunctionCallNodeGen;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.nodes.IndirectCallNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
 
 /**
  * This example illustrates how {@link Cached} can be used to implement function calls that use
@@ -46,6 +51,16 @@
 @SuppressWarnings("unused")
 public class FunctionCall {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testFunctionCall() {
         assertEquals(2, FunctionCallNode.CACHE_SIZE);
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/Interop.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/Interop.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,17 +22,22 @@
  */
 package com.oracle.truffle.api.dsl.test.examples;
 
+import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments;
+import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget;
+import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
-import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments;
-import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget;
 import com.oracle.truffle.api.dsl.test.examples.InteropFactory.UseInteropNodeGen;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
 
 /**
  * This example aims to illustrate how the {@link Cached} annotation can be used to implement a
@@ -40,6 +45,16 @@
  */
 public class Interop {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testInterop() {
         UseInterop node = UseInteropNodeGen.create(createArguments(2));
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/MathPow.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/MathPow.java	Wed Sep 30 16:33:56 2015 -0700
@@ -25,11 +25,18 @@
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+
 import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments;
 import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget;
+
 import com.oracle.truffle.api.dsl.test.examples.MathPowFactory.MathPowNodeGen;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.nodes.Node;
+
 import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -42,6 +49,16 @@
 @SuppressWarnings("unused")
 public class MathPow extends Node {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testPow() {
         MathPowNode node = MathPowNodeGen.create(createArguments(2));
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/examples/RubyCall.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,17 @@
  */
 package com.oracle.truffle.api.dsl.test.examples;
 
+import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments;
+import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget;
+import static org.junit.Assert.assertEquals;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.Assumption;
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -29,21 +40,15 @@
 import com.oracle.truffle.api.dsl.ImportStatic;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.dsl.internal.SpecializedNode;
-import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createArguments;
-import static com.oracle.truffle.api.dsl.test.examples.ExampleNode.createTarget;
 import com.oracle.truffle.api.dsl.test.examples.RubyCallFactory.RubyDispatchNodeGen;
 import com.oracle.truffle.api.dsl.test.examples.RubyCallFactory.RubyHeadNodeGen;
 import com.oracle.truffle.api.dsl.test.examples.RubyCallFactory.RubyLookupNodeGen;
+import com.oracle.truffle.api.dsl.test.utilities.InstrumentationTestMode;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.DirectCallNode;
 import com.oracle.truffle.api.nodes.IndirectCallNode;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.Node.Child;
 import com.oracle.truffle.api.utilities.CyclicAssumption;
-import java.util.HashMap;
-import java.util.Map;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
 
 /**
  * This example illustrates a simplified version of a Ruby function call semantics (RubyHeadNode).
@@ -52,6 +57,16 @@
 @SuppressWarnings("unused")
 public class RubyCall {
 
+    @Before
+    public void before() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testCall() {
         RubyHeadNode node = RubyHeadNodeGen.create(createArguments(4));
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,14 +22,18 @@
  */
 package com.oracle.truffle.api.dsl.test.processor;
 
+import java.io.IOException;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.debug.DebugSupportProvider;
 import com.oracle.truffle.api.dsl.test.ExpectError;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.Source;
-import java.io.IOException;
 
 public class LanguageRegistrationTest {
 
@@ -76,12 +80,27 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
             return null;
         }
 
         @Override
-        protected DebugSupportProvider getDebugSupport() {
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
+        protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+            return null;
+        }
+
+        @Override
+        protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
             return null;
         }
 
@@ -120,12 +139,27 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
             return null;
         }
 
         @Override
-        protected DebugSupportProvider getDebugSupport() {
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
+        protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+            return null;
+        }
+
+        @Override
+        protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
             return null;
         }
 
@@ -160,12 +194,27 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
             return null;
         }
 
         @Override
-        protected DebugSupportProvider getDebugSupport() {
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
+        protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+            return null;
+        }
+
+        @Override
+        protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
             return null;
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/utilities/InstrumentationTestMode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ * 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.dsl.test.utilities;
+
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+
+public class InstrumentationTestMode {
+
+    public static void set(boolean enable) {
+
+        try {
+            final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+            final Field instrumenterField = vm.getClass().getDeclaredField("instrumenter");
+            instrumenterField.setAccessible(true);
+            final Object instrumenter = instrumenterField.get(vm);
+            final java.lang.reflect.Field testVMField = Instrumenter.class.getDeclaredField("testVM");
+            testVMField.setAccessible(true);
+            if (enable) {
+                testVMField.set(instrumenter, vm);
+            } else {
+                testVMField.set(instrumenter, null);
+            }
+        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
+            fail("Reflective access to Instrumenter for testing");
+        }
+
+    }
+}
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/ClassInteropTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/ClassInteropTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -27,10 +27,13 @@
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -51,6 +54,12 @@
     public void initObjects() {
         obj = JavaInterop.asTruffleObject(ClassInteropTest.class);
         xyp = JavaInterop.asJavaObject(XYPlus.class, obj);
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
     }
 
     @Test
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/InstrumentationTestMode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,55 @@
+/*
+ * 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.interop.java.test;
+
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+
+public class InstrumentationTestMode {
+
+    public static void set(boolean enable) {
+
+        try {
+            final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+            final Field instrumenterField = vm.getClass().getDeclaredField("instrumenter");
+            instrumenterField.setAccessible(true);
+            final Object instrumenter = instrumenterField.get(vm);
+            final java.lang.reflect.Field testVMField = Instrumenter.class.getDeclaredField("testVM");
+            testVMField.setAccessible(true);
+            if (enable) {
+                testVMField.set(instrumenter, vm);
+            } else {
+                testVMField.set(instrumenter, null);
+            }
+        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
+            fail("Reflective access to Instrumenter for testing");
+        }
+
+    }
+}
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropSpeedTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropSpeedTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,14 +24,17 @@
  */
 package com.oracle.truffle.api.interop.java.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Random;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
-import java.util.Random;
-import org.junit.AfterClass;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-import org.junit.BeforeClass;
-import org.junit.Test;
 
 public class JavaInteropSpeedTest {
     private static final int REPEAT = 10000;
@@ -41,6 +44,7 @@
 
     @BeforeClass
     public static void beforeTesting() {
+        InstrumentationTestMode.set(true);
         arr = initArray(REPEAT);
         for (int i = 0; i < 1000; i++) {
             JavaInteropSpeedTest t = new JavaInteropSpeedTest();
@@ -50,6 +54,11 @@
         }
     }
 
+    @AfterClass
+    public static void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     private int mmInOp;
     private int mmInJava;
 
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/JavaInteropTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,6 +24,17 @@
  */
 package com.oracle.truffle.api.interop.java.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
@@ -35,13 +46,6 @@
 import com.oracle.truffle.api.interop.java.MethodMessage;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
-import java.util.List;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import org.junit.Before;
-import org.junit.Test;
 
 public class JavaInteropTest {
     public int x;
@@ -67,6 +71,12 @@
     public void initObjects() {
         obj = JavaInterop.asTruffleObject(this);
         xyp = JavaInterop.asJavaObject(XYPlus.class, obj);
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
     }
 
     @Test
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/MethodMessageTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/MethodMessageTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,14 +24,18 @@
  */
 package com.oracle.truffle.api.interop.java.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.interop.Message;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.interop.java.MethodMessage;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import org.junit.Test;
 
 public class MethodMessageTest {
     interface MathWrap {
@@ -39,6 +43,16 @@
         MaxFunction max();
     }
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     interface MaxFunction {
         @MethodMessage(message = "IS_NULL")
         boolean isNull();
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveArrayInteropTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveArrayInteropTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -26,9 +26,13 @@
 
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
+
 import java.util.List;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -70,6 +74,12 @@
     public void initObjects() {
         obj = JavaInterop.asTruffleObject(this);
         interop = JavaInterop.asJavaObject(ExactMatchInterop.class, obj);
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
     }
 
     @Test
--- a/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveRawArrayInteropTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.interop.java.test/src/com/oracle/truffle/api/interop/java/test/PrimitiveRawArrayInteropTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -26,9 +26,13 @@
 
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
+
 import java.util.List;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
+
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
@@ -79,6 +83,12 @@
     public void initObjects() {
         obj = JavaInterop.asTruffleObject(this);
         interop = JavaInterop.asJavaObject(RawInterop.class, obj);
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
     }
 
     @Test
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,14 +22,18 @@
  */
 package com.oracle.truffle.api.test;
 
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleRuntime;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
-import org.junit.Assert;
-import org.junit.Test;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
 
 /**
  * <h3>Passing Arguments</h3>
@@ -54,6 +58,16 @@
  */
 public class ArgumentsTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/CallTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/CallTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,13 +22,17 @@
  */
 package com.oracle.truffle.api.test;
 
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleRuntime;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.RootNode;
-import org.junit.Assert;
-import org.junit.Test;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
 
 /**
  * <h3>Calling Another Tree</h3>
@@ -46,6 +50,16 @@
  */
 public class CallTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildNodeTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -29,8 +29,13 @@
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.Node.Child;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
 import java.util.Iterator;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -55,6 +60,16 @@
  */
 public class ChildNodeTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildrenNodesTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ChildrenNodesTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -28,8 +28,13 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
 import java.util.Iterator;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -51,6 +56,16 @@
  */
 public class ChildrenNodesTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FinalFieldTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FinalFieldTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -28,7 +28,11 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -54,6 +58,16 @@
  */
 public class FinalFieldTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameSlotTypeSpecializationTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameSlotTypeSpecializationTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -32,7 +32,11 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -51,6 +55,16 @@
  */
 public class FrameSlotTypeSpecializationTest {
 
+    @Before
+    public void setInstrumentationTestMode() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void unsetInstrumentationTestMode() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/FrameTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,11 @@
  */
 package com.oracle.truffle.api.test;
 
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleRuntime;
@@ -33,8 +38,7 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
-import org.junit.Assert;
-import org.junit.Test;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
 
 /**
  * <h3>Storing Values in Frame Slots</h3>
@@ -69,8 +73,18 @@
  */
 public class FrameTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
-    public void test() {
+    public void test() throws SecurityException, IllegalArgumentException {
         TruffleRuntime runtime = Truffle.getRuntime();
         FrameDescriptor frameDescriptor = new FrameDescriptor();
         String varName = "localVar";
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InterfaceChildFieldTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InterfaceChildFieldTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -30,8 +30,13 @@
 import com.oracle.truffle.api.nodes.NodeInterface;
 import com.oracle.truffle.api.nodes.NodeUtil;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
 import java.util.Iterator;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -39,6 +44,16 @@
  */
 public class InterfaceChildFieldTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testChild() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReplaceTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -28,9 +28,16 @@
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
 import java.util.Iterator;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
+
 import static org.junit.Assert.assertEquals;
+
 import org.junit.Test;
 
 /**
@@ -54,6 +61,16 @@
  */
 public class ReplaceTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReturnTypeSpecializationTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ReturnTypeSpecializationTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -33,7 +33,11 @@
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.nodes.UnexpectedResultException;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -51,6 +55,16 @@
  */
 public class ReturnTypeSpecializationTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -27,7 +27,11 @@
 import com.oracle.truffle.api.TruffleRuntime;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -50,6 +54,16 @@
  */
 public class RootNodeTest {
 
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void test() {
         TruffleRuntime runtime = Truffle.getRuntime();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,13 +22,17 @@
  */
 package com.oracle.truffle.api.test;
 
+import java.io.IOException;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.debug.DebugSupportProvider;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.Source;
-import java.io.IOException;
 
 public final class TestingLanguage extends TruffleLanguage<Object> {
     public static final TestingLanguage INSTANCE = new TestingLanguage();
@@ -57,12 +61,27 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
+    protected Visualizer getVisualizer() {
         return null;
     }
 
     @Override
-    protected DebugSupportProvider getDebugSupport() {
+    protected boolean isInstrumentable(Node node) {
+        return false;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
+        return null;
+    }
+
+    @Override
+    protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+        return null;
+    }
+
+    @Override
+    protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
         return null;
     }
 
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TruffleRuntimeTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TruffleRuntimeTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,6 +22,21 @@
  */
 package com.oracle.truffle.api.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleRuntime;
@@ -30,17 +45,7 @@
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertSame;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import org.junit.Before;
-import org.junit.Test;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
 
 /**
  * <h3>Accessing the Truffle Runtime</h3>
@@ -63,12 +68,18 @@
     private TruffleRuntime runtime;
 
     @Before
-    public void setUp() {
+    public void before() {
+        InstrumentationTestMode.set(true);
         this.runtime = Truffle.getRuntime();
     }
 
-    private static RootNode createTestRootNode() {
-        return new RootNode(TestingLanguage.class, null, null) {
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
+    private static RootNode createTestRootNode(SourceSection sourceSection) {
+        return new RootNode(TestingLanguage.class, sourceSection, null) {
             @Override
             public Object execute(VirtualFrame frame) {
                 return 42;
@@ -93,7 +104,7 @@
 
     @Test
     public void testCreateCallTarget() {
-        RootNode rootNode = createTestRootNode();
+        RootNode rootNode = createTestRootNode(null);
         RootCallTarget target = runtime.createCallTarget(rootNode);
         assertNotNull(target);
         assertEquals(target.call(), 42);
@@ -102,14 +113,14 @@
 
     @Test
     public void testGetCallTargets1() {
-        RootNode rootNode = createTestRootNode();
+        RootNode rootNode = createTestRootNode(null);
         RootCallTarget target = runtime.createCallTarget(rootNode);
         assertTrue(runtime.getCallTargets().contains(target));
     }
 
     @Test
     public void testGetCallTargets2() {
-        RootNode rootNode = createTestRootNode();
+        RootNode rootNode = createTestRootNode(null);
         RootCallTarget target1 = runtime.createCallTarget(rootNode);
         RootCallTarget target2 = runtime.createCallTarget(rootNode);
         assertTrue(runtime.getCallTargets().contains(target1));
@@ -127,10 +138,8 @@
         SourceSection sourceSection1 = source1.createSection("foo", 1);
         SourceSection sourceSection2 = source1.createSection("bar", 2);
 
-        RootNode rootNode1 = createTestRootNode();
-        rootNode1.assignSourceSection(sourceSection1);
-        RootNode rootNode2 = createTestRootNode();
-        rootNode2.assignSourceSection(sourceSection2);
+        RootNode rootNode1 = createTestRootNode(sourceSection1);
+        RootNode rootNode2 = createTestRootNode(sourceSection2);
         RootNode rootNode2Copy = NodeUtil.cloneNode(rootNode2);
 
         assertSame(rootNode2.getSourceSection(), rootNode2Copy.getSourceSection());
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,20 +22,26 @@
  */
 package com.oracle.truffle.api.test.instrument;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleRuntime;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
-import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.SyntaxTag;
+import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
+import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdvancedInstrumentCounterRoot;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
-import static org.junit.Assert.assertEquals;
-import org.junit.Test;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.InstrumentTestTag;
+import com.oracle.truffle.api.vm.PolyglotEngine;
 
 /**
  * Tests the kind of instrumentation where a client can provide an AST fragment to be
@@ -44,47 +50,53 @@
 public class AdvancedInstrumentTest {
 
     @Test
-    public void testAdvancedInstrumentListener() {
-        // Create a simple addition AST
-        final TruffleRuntime runtime = Truffle.getRuntime();
-        final TestValueNode leftValueNode = new TestValueNode(6);
-        final TestValueNode rightValueNode = new TestValueNode(7);
-        final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
-        final TestRootNode rootNode = new TestRootNode(addNode);
-        final CallTarget callTarget1 = runtime.createCallTarget(rootNode);
+    public void testAdvancedInstrumentListener() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+
+        instrumenter.registerASTProber(new InstrumentationTestingLanguage.TestASTProber());
+        final Source source = Source.fromText("testAdvancedInstrumentListener text", "testAdvancedInstrumentListener").withMimeType("text/x-instTest");
+
+        final Probe[] addNodeProbe = new Probe[1];
+        instrumenter.addProbeListener(new DefaultProbeListener() {
 
-        // Ensure it executes correctly
-        assertEquals(13, callTarget1.call());
+            @Override
+            public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
+                if (tag == InstrumentTestTag.ADD_TAG) {
+                    assertNull("only one add node", addNodeProbe[0]);
+                    addNodeProbe[0] = probe;
+                }
+            }
+        });
+        assertEquals(vm.eval(source).get(), 13);
+        assertNotNull("Add node should be probed", addNodeProbe[0]);
 
-        // Probe the addition node
-        final Probe probe = addNode.probe();
-
-        assertEquals(13, callTarget1.call());
-
-        // Attach a null factory; it never actually attaches a node.
-        final Instrument instrument = Instrument.create(null, new AdvancedInstrumentRootFactory() {
+        // Attach a factory that never actually attaches a node.
+        final AdvancedInstrumentRootFactory rootFactory1 = new AdvancedInstrumentRootFactory() {
 
             public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) {
                 return null;
             }
-        }, null, "test AdvancedInstrument");
-        probe.attach(instrument);
+        };
+        instrumenter.attach(addNodeProbe[0], null, rootFactory1, null, "test AdvancedInstrument");
 
-        assertEquals(13, callTarget1.call());
-
-        final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot();
+        assertEquals(vm.eval(source).get(), 13);
 
         // Attach a factory that splices an execution counter into the AST.
-        probe.attach(Instrument.create(null, new AdvancedInstrumentRootFactory() {
+        final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot();
+        final AdvancedInstrumentRootFactory rootFactory2 = new AdvancedInstrumentRootFactory() {
 
             public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) {
                 return counter;
             }
-        }, null, "test AdvancedInstrument"));
-        assertEquals(0, counter.getCount());
+        };
+        instrumenter.attach(addNodeProbe[0], null, rootFactory2, null, "test AdvancedInstrument");
 
-        assertEquals(13, callTarget1.call());
-
+        assertEquals(0, counter.getCount());
+        assertEquals(vm.eval(source).get(), 13);
         assertEquals(1, counter.getCount());
     }
 }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,181 +22,115 @@
  */
 package com.oracle.truffle.api.test.instrument;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleRuntime;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.ASTProber;
-import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeException;
-import com.oracle.truffle.api.instrument.ProbeFailure.Reason;
-import com.oracle.truffle.api.instrument.ProbeNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.ProbeInstrument;
 import com.oracle.truffle.api.instrument.SimpleInstrumentListener;
 import com.oracle.truffle.api.instrument.StandardInstrumentListener;
 import com.oracle.truffle.api.instrument.SyntaxTag;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
 import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener;
 import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeVisitor;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode;
-import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode;
 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
-import java.util.Iterator;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import org.junit.Test;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.InstrumentTestTag;
+import com.oracle.truffle.api.vm.PolyglotEngine;
 
 /**
  * <h3>AST Instrumentation</h3>
  *
  * Instrumentation allows the insertion into Truffle ASTs language-specific instances of
  * {@link WrapperNode} that propagate execution events through a {@link Probe} to any instances of
- * {@link Instrument} that might be attached to the particular probe by tools.
- * <ol>
- * <li>Creates a simple add AST</li>
- * <li>Verifies its structure</li>
- * <li>"Probes" the add node by adding a {@link WrapperNode} and associated {@link Probe}</li>
- * <li>Attaches a simple {@link Instrument} to the node via the Probe's {@link ProbeNode}</li>
- * <li>Verifies the structure of the probed AST</li>
- * <li>Verifies the execution of the probed AST</li>
- * <li>Verifies the results observed by the instrument.</li>
- * </ol>
- * To do these tests, several required classes have been implemented in their most basic form, only
- * implementing the methods necessary for the tests to pass, with stubs elsewhere.
+ * {@link ProbeInstrument} that might be attached to the particular probe by tools.
  */
 public class InstrumentationTest {
 
-    private static final SyntaxTag ADD_TAG = new SyntaxTag() {
-
-        @Override
-        public String name() {
-            return "Addition";
-        }
-
-        @Override
-        public String getDescription() {
-            return "Test Language Addition Node";
-        }
-    };
-
-    private static final SyntaxTag VALUE_TAG = new SyntaxTag() {
-
-        @Override
-        public String name() {
-            return "Value";
-        }
+    @Test
+    public void testProbing() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        instrumenter.registerASTProber(new TestASTProber(instrumenter));
+        final Source source = Source.fromText("testProbing text", "testProbing").withMimeType("text/x-instTest");
 
-        @Override
-        public String getDescription() {
-            return "Test Language Value Node";
-        }
-    };
-
-    @Test
-    public void testInstrumentationStructure() {
-        // 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);
-
-        try {
-            addNode.probe();
-        } catch (ProbeException e) {
-            assertEquals(e.getFailure().getReason(), Reason.NO_PARENT);
-        }
-        final TestRootNode rootNode = new TestRootNode(addNode);
-
-        // Creating a call target sets the parent pointers in this tree and is necessary prior to
-        // checking any parent/child relationships
-        final CallTarget callTarget1 = runtime.createCallTarget(rootNode);
+        final Probe[] probes = new Probe[3];
+        instrumenter.addProbeListener(new DefaultProbeListener() {
 
-        // Check the tree structure
-        assertEquals(addNode, leftValueNode.getParent());
-        assertEquals(addNode, rightValueNode.getParent());
-        Iterator<Node> iterator = addNode.getChildren().iterator();
-        assertEquals(leftValueNode, iterator.next());
-        assertEquals(rightValueNode, iterator.next());
-        assertFalse(iterator.hasNext());
-        assertEquals(rootNode, addNode.getParent());
-        iterator = rootNode.getChildren().iterator();
-        assertEquals(addNode, iterator.next());
-        assertFalse(iterator.hasNext());
-
-        // Ensure it executes correctly
-        assertEquals(13, callTarget1.call());
-
-        // Probe the addition node
-        addNode.probe();
-
-        // Check the modified tree structure
-        assertEquals(addNode, leftValueNode.getParent());
-        assertEquals(addNode, rightValueNode.getParent());
-        iterator = addNode.getChildren().iterator();
-        assertEquals(leftValueNode, iterator.next());
-        assertEquals(rightValueNode, iterator.next());
-        assertFalse(iterator.hasNext());
+            @Override
+            public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
+                if (tag == InstrumentTestTag.ADD_TAG) {
+                    assertEquals(probes[0], null);
+                    probes[0] = probe;
+                } else if (tag == InstrumentTestTag.VALUE_TAG) {
+                    if (probes[1] == null) {
+                        probes[1] = probe;
+                    } else if (probes[2] == null) {
+                        probes[2] = probe;
+                    } else {
+                        fail("Should only be three probes");
+                    }
+                }
+            }
+        });
+        assertEquals(vm.eval(source).get(), 13);
+        assertNotNull("Add node should be probed", probes[0]);
+        assertNotNull("Value nodes should be probed", probes[1]);
+        assertNotNull("Value nodes should be probed", probes[2]);
+        // Check instrumentation with the simplest kind of counters.
+        // They should all be removed when the check is finished.
+        checkCounters(probes[0], vm, source, new TestSimpleInstrumentCounter(instrumenter), new TestSimpleInstrumentCounter(instrumenter), new TestSimpleInstrumentCounter(instrumenter));
 
-        // Ensure there's a WrapperNode correctly inserted into the AST
-        iterator = rootNode.getChildren().iterator();
-        Node wrapperNode = iterator.next();
-        assertTrue(wrapperNode instanceof TestLanguageWrapperNode);
-        assertFalse(iterator.hasNext());
-        assertEquals(rootNode, wrapperNode.getParent());
+        // Now try with the more complex flavor of listener
+        checkCounters(probes[0], vm, source, new TestStandardInstrumentCounter(instrumenter), new TestStandardInstrumentCounter(instrumenter), new TestStandardInstrumentCounter(instrumenter));
 
-        // Check that the WrapperNode has both the probe and the wrapped node as children
-        iterator = wrapperNode.getChildren().iterator();
-        assertEquals(addNode, iterator.next());
-        ProbeNode probeNode = (ProbeNode) iterator.next();
-        assertTrue(probeNode.getProbe() != null);
-        assertFalse(iterator.hasNext());
-
-        // Check that you can't probe the WrapperNodes
-        TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode;
-        try {
-            wrapper.probe();
-            fail();
-        } catch (ProbeException e) {
-            assertEquals(e.getFailure().getReason(), Reason.WRAPPER_NODE);
-        }
-
-        // Check that the "probed" AST still executes correctly
-        assertEquals(13, callTarget1.call());
     }
 
     @Test
-    public void testListeners() {
+    public void testTagging() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
 
-        // Create a simple addition AST
-        final TruffleRuntime runtime = Truffle.getRuntime();
-        final TestValueNode leftValueNode = new TestValueNode(6);
-        final TestValueNode rightValueNode = new TestValueNode(7);
-        final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
-        final TestRootNode rootNode = new TestRootNode(addNode);
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        final Source source = Source.fromText("testTagging text", "testTagging").withMimeType("text/x-instTest");
+
+        // Applies appropriate tags
+        final TestASTProber astProber = new TestASTProber(instrumenter);
+        instrumenter.registerASTProber(astProber);
 
-        // Creating a call target sets the parent pointers in this tree and is necessary prior to
-        // checking any parent/child relationships
-        final CallTarget callTarget = runtime.createCallTarget(rootNode);
-        // Probe the addition node
-        final Probe probe = addNode.probe();
+        // Listens for probes and tags being added
+        final TestProbeListener probeListener = new TestProbeListener();
+        instrumenter.addProbeListener(probeListener);
+
+        assertEquals(13, vm.eval(source).get());
 
-        // Check instrumentation with the simplest kind of counters.
-        // They should all be removed when the check is finished.
-        checkCounters(probe, callTarget, rootNode, new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter());
+        // Check that the prober added probes to the tree
+        assertEquals(probeListener.probeCount, 3);
+        assertEquals(probeListener.tagCount, 3);
 
-        // Now try with the more complex flavor of listener
-        checkCounters(probe, callTarget, rootNode, new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter());
+        assertEquals(instrumenter.findProbesTaggedAs(InstrumentTestTag.ADD_TAG).size(), 1);
+        assertEquals(instrumenter.findProbesTaggedAs(InstrumentTestTag.VALUE_TAG).size(), 2);
     }
 
-    private static void checkCounters(Probe probe, CallTarget callTarget, RootNode rootNode, TestCounter counterA, TestCounter counterB, TestCounter counterC) {
+    private static void checkCounters(Probe probe, PolyglotEngine vm, Source source, TestCounter counterA, TestCounter counterB, TestCounter counterC) throws IOException {
 
         // Attach a counting instrument to the probe
         counterA.attach(probe);
@@ -205,7 +139,7 @@
         counterB.attach(probe);
 
         // Run it again and check that the two instruments are working
-        assertEquals(13, callTarget.call());
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
         assertEquals(counterB.enterCount(), 1);
@@ -215,141 +149,47 @@
         counterA.dispose();
 
         // Run it again and check that instrument B is still working but not A
-        assertEquals(13, callTarget.call());
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
         assertEquals(counterB.enterCount(), 2);
         assertEquals(counterB.leaveCount(), 2);
 
-        // Simulate a split by cloning the AST
-        final CallTarget callTarget2 = Truffle.getRuntime().createCallTarget((TestRootNode) rootNode.copy());
-        // Run the clone and check that instrument B is still working but not A
-        assertEquals(13, callTarget2.call());
+        // Attach a second instrument to the probe
+        counterC.attach(probe);
+
+        // Run the original and check that instruments B,C working but not A
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
         assertEquals(counterB.enterCount(), 3);
         assertEquals(counterB.leaveCount(), 3);
-
-        // Run the original and check that instrument B is still working but not A
-        assertEquals(13, callTarget2.call());
-        assertEquals(counterA.enterCount(), 1);
-        assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 4);
-        assertEquals(counterB.leaveCount(), 4);
-
-        // Attach a second instrument to the probe
-        counterC.attach(probe);
-
-        // Run the original and check that instruments B,C working but not A
-        assertEquals(13, callTarget.call());
-        assertEquals(counterA.enterCount(), 1);
-        assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 5);
-        assertEquals(counterB.leaveCount(), 5);
         assertEquals(counterC.enterCount(), 1);
         assertEquals(counterC.leaveCount(), 1);
 
-        // Run the clone and check that instruments B,C working but not A
-        assertEquals(13, callTarget2.call());
-        assertEquals(counterA.enterCount(), 1);
-        assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 6);
-        assertEquals(counterB.leaveCount(), 6);
-        assertEquals(counterC.enterCount(), 2);
-        assertEquals(counterC.leaveCount(), 2);
-
         // Remove instrumentC
         counterC.dispose();
 
         // Run the original and check that instrument B working but not A,C
-        assertEquals(13, callTarget.call());
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 7);
-        assertEquals(counterB.leaveCount(), 7);
-        assertEquals(counterC.enterCount(), 2);
-        assertEquals(counterC.leaveCount(), 2);
-
-        // Run the clone and check that instrument B working but not A,C
-        assertEquals(13, callTarget2.call());
-        assertEquals(counterA.enterCount(), 1);
-        assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 8);
-        assertEquals(counterB.leaveCount(), 8);
-        assertEquals(counterC.enterCount(), 2);
-        assertEquals(counterC.leaveCount(), 2);
+        assertEquals(counterB.enterCount(), 4);
+        assertEquals(counterB.leaveCount(), 4);
+        assertEquals(counterC.enterCount(), 1);
+        assertEquals(counterC.leaveCount(), 1);
 
         // Remove instrumentB
         counterB.dispose();
 
-        // Run both the original and clone, check that no instruments working
-        assertEquals(13, callTarget.call());
-        assertEquals(13, callTarget2.call());
+        // Check that no instruments working
+        assertEquals(13, vm.eval(source).get());
         assertEquals(counterA.enterCount(), 1);
         assertEquals(counterA.leaveCount(), 1);
-        assertEquals(counterB.enterCount(), 8);
-        assertEquals(counterB.leaveCount(), 8);
-        assertEquals(counterC.enterCount(), 2);
-        assertEquals(counterC.leaveCount(), 2);
-    }
-
-    @Test
-    public void testTagging() {
-        // Applies appropriate tags
-        final TestASTProber astProber = new TestASTProber();
-        Probe.registerASTProber(astProber);
-
-        // Listens for probes and tags being added
-        final TestProbeListener probeListener = new TestProbeListener();
-        Probe.addProbeListener(probeListener);
-
-        // Counts all entries to all instances of addition nodes
-        final TestMultiCounter additionCounter = new TestMultiCounter();
-
-        // Counts all entries to all instances of value nodes
-        final TestMultiCounter valueCounter = new TestMultiCounter();
-
-        // Create a simple addition AST
-        final TruffleRuntime runtime = Truffle.getRuntime();
-        final TestValueNode leftValueNode = new TestValueNode(6);
-        final TestValueNode rightValueNode = new TestValueNode(7);
-        final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
-
-        final TestRootNode rootNode = new TestRootNode(addNode);
-
-        final CallTarget callTarget = runtime.createCallTarget(rootNode);
-
-        // Check that the prober added probes to the tree
-        assertEquals(probeListener.probeCount, 3);
-        assertEquals(probeListener.tagCount, 3);
-
-        assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1);
-        assertEquals(Probe.findProbesTaggedAs(VALUE_TAG).size(), 2);
-
-        // Check that it executes correctly
-        assertEquals(13, callTarget.call());
-
-        // Dynamically attach a counter for all executions of all Addition nodes
-        for (Probe probe : Probe.findProbesTaggedAs(ADD_TAG)) {
-            additionCounter.attachCounter(probe);
-        }
-        // Dynamically attach a counter for all executions of all Value nodes
-        for (Probe probe : Probe.findProbesTaggedAs(VALUE_TAG)) {
-            valueCounter.attachCounter(probe);
-        }
-
-        // Counters initialized at 0
-        assertEquals(additionCounter.count, 0);
-        assertEquals(valueCounter.count, 0);
-
-        // Execute again
-        assertEquals(13, callTarget.call());
-
-        // There are two value nodes in the AST, but only one addition node
-        assertEquals(additionCounter.count, 1);
-        assertEquals(valueCounter.count, 2);
-
-        Probe.unregisterASTProber(astProber);
+        assertEquals(counterB.enterCount(), 4);
+        assertEquals(counterB.leaveCount(), 4);
+        assertEquals(counterC.enterCount(), 1);
+        assertEquals(counterC.leaveCount(), 1);
     }
 
     private interface TestCounter {
@@ -370,28 +210,11 @@
 
         public int enterCount = 0;
         public int leaveCount = 0;
-        public final Instrument instrument;
-
-        public TestSimpleInstrumentCounter() {
-            this.instrument = Instrument.create(new SimpleInstrumentListener() {
-
-                public void enter(Probe probe) {
-                    enterCount++;
-                }
+        public Instrumenter instrumenter;
+        private ProbeInstrument instrument;
 
-                public void returnVoid(Probe probe) {
-                    leaveCount++;
-                }
-
-                public void returnValue(Probe probe, Object result) {
-                    leaveCount++;
-                }
-
-                public void returnExceptional(Probe probe, Exception exception) {
-                    leaveCount++;
-                }
-
-            }, "Instrumentation Test Counter");
+        public TestSimpleInstrumentCounter(Instrumenter instrumenter) {
+            this.instrumenter = instrumenter;
         }
 
         @Override
@@ -406,7 +229,25 @@
 
         @Override
         public void attach(Probe probe) {
-            probe.attach(instrument);
+            instrument = instrumenter.attach(probe, new SimpleInstrumentListener() {
+
+                public void onEnter(Probe p) {
+                    enterCount++;
+                }
+
+                public void onReturnVoid(Probe p) {
+                    leaveCount++;
+                }
+
+                public void onReturnValue(Probe p, Object result) {
+                    leaveCount++;
+                }
+
+                public void onReturnExceptional(Probe p, Exception exception) {
+                    leaveCount++;
+                }
+
+            }, "Instrumentation Test Counter");
         }
 
         @Override
@@ -422,28 +263,11 @@
 
         public int enterCount = 0;
         public int leaveCount = 0;
-        public final Instrument instrument;
-
-        public TestStandardInstrumentCounter() {
-            this.instrument = Instrument.create(new StandardInstrumentListener() {
-
-                public void enter(Probe probe, Node node, VirtualFrame vFrame) {
-                    enterCount++;
-                }
+        public final Instrumenter instrumenter;
+        public ProbeInstrument instrument;
 
-                public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
-                    leaveCount++;
-                }
-
-                public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
-                    leaveCount++;
-                }
-
-                public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
-                    leaveCount++;
-                }
-
-            }, "Instrumentation Test Counter");
+        public TestStandardInstrumentCounter(Instrumenter instrumenter) {
+            this.instrumenter = instrumenter;
         }
 
         @Override
@@ -458,7 +282,25 @@
 
         @Override
         public void attach(Probe probe) {
-            probe.attach(instrument);
+            instrument = instrumenter.attach(probe, new StandardInstrumentListener() {
+
+                public void onEnter(Probe p, Node node, VirtualFrame vFrame) {
+                    enterCount++;
+                }
+
+                public void onReturnVoid(Probe p, Node node, VirtualFrame vFrame) {
+                    leaveCount++;
+                }
+
+                public void onReturnValue(Probe p, Node node, VirtualFrame vFrame, Object result) {
+                    leaveCount++;
+                }
+
+                public void onReturnExceptional(Probe p, Node node, VirtualFrame vFrame, Exception exception) {
+                    leaveCount++;
+                }
+
+            }, "Instrumentation Test Counter");
         }
 
         @Override
@@ -472,6 +314,12 @@
      */
     private static final class TestASTProber implements NodeVisitor, ASTProber {
 
+        private final Instrumenter instrumenter;
+
+        TestASTProber(Instrumenter instrumenter) {
+            this.instrumenter = instrumenter;
+        }
+
         @Override
         public boolean visit(Node node) {
             if (node instanceof TestLanguageNode) {
@@ -479,10 +327,10 @@
                 final TestLanguageNode testNode = (TestLanguageNode) node;
 
                 if (node instanceof TestValueNode) {
-                    testNode.probe().tagAs(VALUE_TAG, null);
+                    instrumenter.probe(testNode).tagAs(InstrumentTestTag.VALUE_TAG, null);
 
                 } else if (node instanceof TestAdditionNode) {
-                    testNode.probe().tagAs(ADD_TAG, null);
+                    instrumenter.probe(testNode).tagAs(InstrumentTestTag.ADD_TAG, null);
 
                 }
             }
@@ -490,8 +338,8 @@
         }
 
         @Override
-        public void probeAST(Node node) {
-            node.accept(this);
+        public void probeAST(Instrumenter inst, RootNode rootNode) {
+            rootNode.accept(this);
         }
     }
 
@@ -503,7 +351,7 @@
         public int counter = 0;
 
         @Override
-        public void enter(Probe probe) {
+        public void onEnter(Probe probe) {
             counter++;
         }
     }
@@ -516,35 +364,11 @@
         public int counter = 0;
 
         @Override
-        public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+        public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
             counter++;
         }
     }
 
-    /**
-     * A counter that can count executions at multiple nodes; it attaches a separate instrument at
-     * each Probe, but keeps a total count.
-     */
-    private static final class TestMultiCounter {
-
-        public int count = 0;
-
-        public void attachCounter(Probe probe) {
-
-            // Attach a new instrument for every Probe
-            // where we want to count executions.
-            // it will get copied when ASTs cloned, so
-            // keep the count in this outer class.
-            probe.attach(Instrument.create(new DefaultSimpleInstrumentListener() {
-
-                @Override
-                public void enter(Probe p) {
-                    count++;
-                }
-            }, "Instrumentation Test MultiCounter"));
-        }
-    }
-
     private static final class TestProbeListener extends DefaultProbeListener {
 
         public int probeCount = 0;
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java	Wed Sep 30 16:33:56 2015 -0700
@@ -25,15 +25,15 @@
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot;
+import com.oracle.truffle.api.instrument.EventHandlerNode;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.KillException;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.test.TestingLanguage;
 
 /**
  * Tests instrumentation where a client can attach a node that gets attached into the AST.
@@ -43,21 +43,12 @@
     abstract static class TestLanguageNode extends Node {
         public abstract Object execute(VirtualFrame vFrame);
 
-        @Override
-        public boolean isInstrumentable() {
-            return true;
-        }
-
-        @Override
-        public WrapperNode createWrapperNode() {
-            return new TestLanguageWrapperNode(this);
-        }
     }
 
     @NodeInfo(cost = NodeCost.NONE)
     static class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode {
         @Child private TestLanguageNode child;
-        @Child private ProbeNode probeNode;
+        @Child private EventHandlerNode eventHandlerNode;
 
         public TestLanguageWrapperNode(TestLanguageNode child) {
             assert !(child instanceof TestLanguageWrapperNode);
@@ -70,18 +61,13 @@
         }
 
         @Override
-        public boolean isInstrumentable() {
-            return false;
-        }
-
-        @Override
-        public void insertProbe(ProbeNode newProbeNode) {
-            this.probeNode = newProbeNode;
+        public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+            this.eventHandlerNode = eventHandler;
         }
 
         @Override
         public Probe getProbe() {
-            return probeNode.getProbe();
+            return eventHandlerNode.getProbe();
         }
 
         @Override
@@ -91,15 +77,15 @@
 
         @Override
         public Object execute(VirtualFrame vFrame) {
-            probeNode.enter(child, vFrame);
+            eventHandlerNode.enter(child, vFrame);
             Object result;
             try {
                 result = child.execute(vFrame);
-                probeNode.returnValue(child, vFrame, result);
+                eventHandlerNode.returnValue(child, vFrame, result);
             } catch (KillException e) {
                 throw (e);
             } catch (Exception e) {
-                probeNode.returnExceptional(child, vFrame, e);
+                eventHandlerNode.returnExceptional(child, vFrame, e);
                 throw (e);
             }
             return result;
@@ -145,7 +131,7 @@
      * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
      * completes an AST. The root nodes serves as our entry point into a program.
      */
-    static class TestRootNode extends RootNode {
+    static class InstrumentationTestRootNode extends RootNode {
         @Child private TestLanguageNode body;
 
         /**
@@ -153,8 +139,8 @@
          * newly created AST. Global registry is not used, since that would interfere with other
          * tests run in the same environment.
          */
-        public TestRootNode(TestLanguageNode body) {
-            super(TestingLanguage.class, null, null);
+        public InstrumentationTestRootNode(TestLanguageNode body) {
+            super(InstrumentationTestingLanguage.class, null, null);
             this.body = body;
         }
 
@@ -167,10 +153,37 @@
         public boolean isCloningAllowed() {
             return true;
         }
+    }
+
+    /**
+     * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST
+     * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
+     * completes an AST. The root nodes serves as our entry point into a program.
+     */
+    static class TestRootNode extends RootNode {
+        @Child private TestLanguageNode body;
+
+        final Instrumenter instrumenter;
+
+        /**
+         * This constructor emulates the global machinery that applies registered probers to every
+         * newly created AST. Global registry is not used, since that would interfere with other
+         * tests run in the same environment.
+         */
+        public TestRootNode(TestLanguageNode body, Instrumenter instrumenter) {
+            super(InstrumentationTestingLanguage.class, null, null);
+            this.instrumenter = instrumenter;
+            this.body = body;
+        }
 
         @Override
-        public void applyInstrumentation() {
-            Probe.applyASTProbers(body);
+        public Object execute(VirtualFrame vFrame) {
+            return body.execute(vFrame);
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return true;
         }
     }
 
@@ -192,4 +205,5 @@
             return null;
         }
     }
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestingLanguage.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.test.instrument;
+
+import java.io.IOException;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.TruffleRuntime;
+import com.oracle.truffle.api.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.SyntaxTag;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeVisitor;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.InstrumentationTestRootNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode;
+import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
+
+@TruffleLanguage.Registration(name = "instrumentationTestLanguage", version = "0", mimeType = "text/x-instTest")
+public final class InstrumentationTestingLanguage extends TruffleLanguage<Object> {
+
+    public static final InstrumentationTestingLanguage INSTANCE = new InstrumentationTestingLanguage();
+
+    static enum InstrumentTestTag implements SyntaxTag {
+
+        ADD_TAG("addition", "test language addition node"),
+
+        VALUE_TAG("value", "test language value node");
+
+        private final String name;
+        private final String description;
+
+        private InstrumentTestTag(String name, String description) {
+            this.name = name;
+            this.description = description;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+    }
+
+    private InstrumentationTestingLanguage() {
+    }
+
+    @Override
+    protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException {
+        final TestValueNode leftValueNode = new TestValueNode(6);
+        final TestValueNode rightValueNode = new TestValueNode(7);
+        final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
+        final InstrumentationTestRootNode rootNode = new InstrumentationTestRootNode(addNode);
+        final TruffleRuntime runtime = Truffle.getRuntime();
+        final CallTarget callTarget = runtime.createCallTarget(rootNode);
+        return callTarget;
+    }
+
+    @Override
+    protected Object findExportedSymbol(Object context, String globalName, boolean onlyExplicit) {
+        return null;
+    }
+
+    @Override
+    protected Object getLanguageGlobal(Object context) {
+        return null;
+    }
+
+    @Override
+    protected boolean isObjectOfLanguage(Object object) {
+        return false;
+    }
+
+    @Override
+    protected Visualizer getVisualizer() {
+        return null;
+    }
+
+    @Override
+    protected boolean isInstrumentable(Node node) {
+        return node instanceof TestAdditionNode || node instanceof TestValueNode;
+    }
+
+    @Override
+    protected WrapperNode createWrapperNode(Node node) {
+        if (isInstrumentable(node)) {
+            return new TestLanguageWrapperNode((TestLanguageNode) node);
+        }
+        return null;
+    }
+
+    @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;
+    }
+
+    @Override
+    protected Object createContext(Env env) {
+        return null;
+    }
+
+    static final class TestASTProber implements ASTProber {
+
+        public void probeAST(final Instrumenter instrumenter, RootNode startNode) {
+            startNode.accept(new NodeVisitor() {
+
+                @Override
+                public boolean visit(Node node) {
+                    if (node instanceof TestLanguageNode) {
+
+                        final TestLanguageNode testNode = (TestLanguageNode) node;
+
+                        if (node instanceof TestValueNode) {
+                            instrumenter.probe(testNode).tagAs(InstrumentTestTag.VALUE_TAG, null);
+
+                        } else if (node instanceof TestAdditionNode) {
+                            instrumenter.probe(testNode).tagAs(InstrumentTestTag.ADD_TAG, null);
+
+                        }
+                    }
+                    return true;
+                }
+            });
+        }
+    }
+
+}
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -91,7 +91,8 @@
             w.write(text);
         }
 
-        Source s1 = Source.fromFileName(file.getPath());
+        // JDK8 default fails on OS X: https://bugs.openjdk.java.net/browse/JDK-8129632
+        Source s1 = Source.fromFileName(file.getPath()).withMimeType("text/x-java");
         assertEquals("Recognized as Java", "text/x-java", s1.getMimeType());
         Source s2 = s1.withMimeType("text/x-c");
         assertEquals("They have the same content", s1.getCode(), s2.getCode());
@@ -107,7 +108,8 @@
 
         String text = "// Hello";
 
-        Source s1 = Source.fromFileName(text, file.getPath());
+        // JDK8 default fails on OS X: https://bugs.openjdk.java.net/browse/JDK-8129632
+        Source s1 = Source.fromFileName(text, file.getPath()).withMimeType("text/x-java");
         assertEquals("Recognized as Java", "text/x-java", s1.getMimeType());
         Source s2 = s1.withMimeType("text/x-c");
         assertEquals("They have the same content", s1.getCode(), s2.getCode());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/InstrumentationTestMode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.test.utilities;
+
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Field;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+
+public class InstrumentationTestMode {
+
+    public static void set(boolean enable) {
+
+        try {
+            final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+            final Field instrumenterField = vm.getClass().getDeclaredField("instrumenter");
+            instrumenterField.setAccessible(true);
+            final Object instrumenter = instrumenterField.get(vm);
+            final java.lang.reflect.Field testVMField = Instrumenter.class.getDeclaredField("testVM");
+            testVMField.setAccessible(true);
+            if (enable) {
+                testVMField.set(instrumenter, vm);
+            } else {
+                testVMField.set(instrumenter, null);
+            }
+        } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException ex) {
+            fail("Reflective access to Instrumenter for testing");
+        }
+
+    }
+}
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/GlobalSymbolTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/GlobalSymbolTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,18 +24,37 @@
 
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.test.utilities.InstrumentationTestMode;
+
 import static com.oracle.truffle.api.test.vm.ImplicitExplicitExportTest.L3;
+
 import com.oracle.truffle.api.vm.PolyglotEngine;
+
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.Executors;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 public class GlobalSymbolTest {
+
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void globalSymbolFoundByLanguage() throws IOException {
         PolyglotEngine vm = PolyglotEngine.buildNew().globalSymbol("ahoj", "42").executor(Executors.newSingleThreadExecutor()).build();
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,16 +22,10 @@
  */
 package com.oracle.truffle.api.test.vm;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.RootCallTarget;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.TruffleLanguage.Env;
-import com.oracle.truffle.api.debug.DebugSupportProvider;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.vm.PolyglotEngine;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.io.Reader;
 import java.util.Enumeration;
@@ -41,13 +35,25 @@
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.Executors;
+
 import org.junit.After;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertTrue;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.RootCallTarget;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.TruffleLanguage.Env;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+
 public class ImplicitExplicitExportTest {
     private static Thread mainThread;
     private PolyglotEngine vm;
@@ -185,12 +191,27 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        protected Visualizer getVisualizer() {
             return null;
         }
 
         @Override
-        protected DebugSupportProvider getDebugSupport() {
+        protected boolean isInstrumentable(Node node) {
+            return false;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return null;
+        }
+
+        @Override
+        protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+            return null;
+        }
+
+        @Override
+        protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
             return null;
         }
 
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -22,13 +22,21 @@
  */
 package com.oracle.truffle.api.test.vm;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.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.MaterializedFrame;
@@ -36,23 +44,19 @@
 import com.oracle.truffle.api.instrument.ASTProber;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.EventHandlerNode;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeNode;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeVisitor;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.vm.EventConsumer;
 import com.oracle.truffle.api.vm.PolyglotEngine;
-import java.io.IOException;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import org.junit.Test;
 
 /**
  * Bug report validating test.
@@ -65,8 +69,9 @@
  * Debugger instance simulates.
  */
 public class InitializationTest {
+
     @Test
-    public void accessProbeForAbstractLanguage() throws IOException {
+    public void accessProbeForAbstractLanguage() throws IOException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
         final Debugger[] arr = {null};
         PolyglotEngine vm = PolyglotEngine.buildNew().onEvent(new EventConsumer<ExecutionEvent>(ExecutionEvent.class) {
             @Override
@@ -75,7 +80,26 @@
             }
         }).build();
 
-        Source source = Source.fromText("any text", "any text").withMimeType("application/x-abstrlang");
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        instrumenter.registerASTProber(new ASTProber() {
+
+            public void probeAST(final Instrumenter inst, RootNode startNode) {
+                startNode.accept(new NodeVisitor() {
+
+                    public boolean visit(Node node) {
+
+                        if (node instanceof ANode) {
+                            inst.probe(node).tagAs(StandardSyntaxTag.STATEMENT, null);
+                        }
+                        return true;
+                    }
+                });
+            }
+        });
+
+        Source source = Source.fromText("accessProbeForAbstractLanguage text", "accessProbeForAbstractLanguage").withMimeType("application/x-abstrlang");
 
         vm.eval(source);
 
@@ -102,7 +126,6 @@
             super(AbstractLanguage.class, ss, null);
             node = new ANode(42);
             adoptChildren();
-            node.probe().tagAs(StandardSyntaxTag.STATEMENT, this);
         }
 
         @Override
@@ -123,54 +146,46 @@
             return getRootNode().getSourceSection();
         }
 
-        @Override
-        public boolean isInstrumentable() {
-            return true;
+        Object constant() {
+            return constant;
+        }
+    }
+
+    private static class ANodeWrapper extends ANode implements WrapperNode {
+        @Child ANode child;
+        @Child private EventHandlerNode eventHandlerNode;
+
+        ANodeWrapper(ANode node) {
+            super(1);  // dummy
+            this.child = node;
         }
 
         @Override
-        public ProbeNode.WrapperNode createWrapperNode() {
-            class WN extends ANode implements ProbeNode.WrapperNode {
-                private ProbeNode probeNode;
-
-                public WN(int constant) {
-                    super(constant);
-                }
-
-                @Override
-                public Node getChild() {
-                    return ANode.this;
-                }
+        public Node getChild() {
+            return child;
+        }
 
-                @Override
-                public Probe getProbe() {
-                    return probeNode.getProbe();
-                }
-
-                @Override
-                public void insertProbe(ProbeNode pn) {
-                    this.probeNode = pn;
-                }
-
-                @Override
-                public String instrumentationInfo() {
-                    throw new UnsupportedOperationException();
-                }
-            }
-            return new WN(constant);
+        @Override
+        public Probe getProbe() {
+            return eventHandlerNode.getProbe();
         }
 
-        Object constant() {
-            return constant;
+        @Override
+        public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+            this.eventHandlerNode = eventHandler;
         }
 
+        @Override
+        public String instrumentationInfo() {
+            throw new UnsupportedOperationException();
+        }
     }
 
     private abstract static class AbstractLanguage extends TruffleLanguage<Object> {
     }
 
     @TruffleLanguage.Registration(mimeType = "application/x-abstrlang", name = "AbstrLang", version = "0.1")
-    public static final class TestLanguage extends AbstractLanguage implements DebugSupportProvider {
+    public static final class TestLanguage extends AbstractLanguage {
         public static final TestLanguage INSTANCE = new TestLanguage();
 
         @Override
@@ -200,22 +215,12 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport() {
+        public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) {
             throw new UnsupportedOperationException();
         }
 
         @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 AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) {
             throw new InstrumentOKException();
         }
 
@@ -225,8 +230,13 @@
         }
 
         @Override
-        public void enableASTProbing(ASTProber astProber) {
-            throw new UnsupportedOperationException();
+        protected boolean isInstrumentable(Node node) {
+            return node instanceof ANode;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            return node instanceof ANode ? new ANodeWrapper((ANode) node) : null;
         }
     }
 
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/PolyglotEngine.java	Wed Sep 30 16:33:56 2015 -0700
@@ -54,14 +54,13 @@
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.TruffleLanguage.Env;
 import com.oracle.truffle.api.TruffleLanguage.Registration;
-import com.oracle.truffle.api.debug.DebugSupportProvider;
 import com.oracle.truffle.api.debug.Debugger;
 import com.oracle.truffle.api.debug.ExecutionEvent;
 import com.oracle.truffle.api.debug.SuspendedEvent;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.impl.Accessor;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.interop.ForeignAccess;
 import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.interop.java.JavaInterop;
@@ -114,7 +113,8 @@
     private final OutputStream out;
     private final EventConsumer<?>[] handlers;
     private final Map<String, Object> globals;
-    private Debugger debugger;
+    private final Instrumenter instrumenter;
+    private final Debugger debugger;
     private boolean disposed;
 
     /**
@@ -129,6 +129,8 @@
         this.handlers = null;
         this.globals = null;
         this.executor = null;
+        this.instrumenter = null;
+        this.debugger = null;
     }
 
     /**
@@ -142,6 +144,8 @@
         this.handlers = handlers;
         this.initThread = Thread.currentThread();
         this.globals = new HashMap<>(globals);
+        this.instrumenter = SPI.createInstrumenter(this);
+        this.debugger = SPI.createDebugger(this, this.instrumenter);
         Map<String, Language> map = new HashMap<>();
         /* We want to create a language instance but per LanguageCache and not per mime type. */
         Set<LanguageCache> uniqueCaches = new HashSet<>(LanguageCache.languages().values());
@@ -331,7 +335,6 @@
          *
          * @return new, isolated virtual machine with pre-registered languages
          */
-        @SuppressWarnings("deprecation")
         public PolyglotEngine build() {
             if (out == null) {
                 out = System.out;
@@ -348,7 +351,7 @@
                     command.run();
                 }
             };
-            return new TruffleVM(nonNullExecutor, globals, out, err, in, handlers.toArray(new EventConsumer[0]));
+            return new PolyglotEngine(nonNullExecutor, globals, out, err, in, handlers.toArray(new EventConsumer[0]));
         }
     }
 
@@ -492,14 +495,13 @@
     }
 
     private Value eval(final Language l, final Source s) throws IOException {
-        final Debugger[] fillIn = {debugger};
         final Object[] result = {null, null};
         final CountDownLatch ready = new CountDownLatch(1);
         final TruffleLanguage[] lang = {null};
         executor.execute(new Runnable() {
             @Override
             public void run() {
-                evalImpl(fillIn, lang, s, result, l, ready);
+                evalImpl(lang, s, result, l, ready);
             }
         });
         exceptionCheck(result);
@@ -515,14 +517,10 @@
     }
 
     @SuppressWarnings("try")
-    private void evalImpl(Debugger[] fillIn, TruffleLanguage<?>[] fillLang, Source s, Object[] result, Language l, CountDownLatch ready) {
-        try (Closeable d = SPI.executionStart(this, fillIn, s)) {
+    private void evalImpl(TruffleLanguage<?>[] fillLang, Source s, Object[] result, Language l, CountDownLatch ready) {
+        try (Closeable d = SPI.executionStart(this, debugger, s)) {
             TruffleLanguage<?> langImpl = l.getImpl(true);
             fillLang[0] = langImpl;
-            PolyglotEngine.findDebuggerSupport(langImpl);
-            if (debugger == null) {
-                debugger = fillIn[0];
-            }
             result[0] = SPI.eval(langImpl, s);
         } catch (IOException ex) {
             result[1] = ex;
@@ -533,15 +531,11 @@
 
     @SuppressWarnings("try")
     final Object invokeForeign(final Node foreignNode, final VirtualFrame frame, final TruffleObject receiver) throws IOException {
-        final Debugger[] fillIn = {debugger};
         final Object[] res = {null, null};
         executor.execute(new Runnable() {
             @Override
             public void run() {
-                try (final Closeable c = SPI.executionStart(PolyglotEngine.this, fillIn, null)) {
-                    if (debugger == null) {
-                        debugger = fillIn[0];
-                    }
+                try (final Closeable c = SPI.executionStart(PolyglotEngine.this, debugger, null)) {
                     res[0] = ForeignAccess.execute(foreignNode, frame, receiver, ForeignAccess.getArguments(frame).toArray());
                 } catch (IOException ex) {
                     res[1] = ex;
@@ -752,13 +746,12 @@
          */
         public Value invoke(final Object thiz, final Object... args) throws IOException {
             get();
-            final Debugger[] fillIn = {debugger};
             final CountDownLatch done = new CountDownLatch(1);
             final Object[] res = {null, null};
             executor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    invokeImpl(fillIn, thiz, args, res, done);
+                    invokeImpl(thiz, args, res, done);
                 }
             });
             exceptionCheck(res);
@@ -766,11 +759,8 @@
         }
 
         @SuppressWarnings("try")
-        private void invokeImpl(Debugger[] fillIn, Object thiz, Object[] args, Object[] res, CountDownLatch done) {
-            try (final Closeable c = SPI.executionStart(PolyglotEngine.this, fillIn, null)) {
-                if (debugger == null) {
-                    debugger = fillIn[0];
-                }
+        private void invokeImpl(Object thiz, Object[] args, Object[] res, CountDownLatch done) {
+            try (final Closeable c = SPI.executionStart(PolyglotEngine.this, debugger, null)) {
                 List<Object> arr = new ArrayList<>();
                 if (thiz == null) {
                     if (language != null) {
@@ -869,12 +859,13 @@
 
         TruffleLanguage<?> getImpl(boolean create) {
             getEnv(create);
-            return info.getImpl(false);
+            TruffleLanguage<?> impl = info.getImpl(false);
+            return impl;
         }
 
         TruffleLanguage.Env getEnv(boolean create) {
             if (env == null && create) {
-                env = SPI.attachEnv(PolyglotEngine.this, info.getImpl(true), out, err, in);
+                env = SPI.attachEnv(PolyglotEngine.this, info.getImpl(true), out, err, in, instrumenter);
             }
             return env;
         }
@@ -889,8 +880,7 @@
     // Accessor helper methods
     //
 
-    TruffleLanguage<?> findLanguage(Probe probe) {
-        Class<? extends TruffleLanguage> languageClazz = SPI.findLanguage(probe);
+    TruffleLanguage<?> findLanguage(Class<? extends TruffleLanguage> languageClazz) {
         for (Map.Entry<String, Language> entrySet : langs.entrySet()) {
             Language languageDescription = entrySet.getValue();
             final TruffleLanguage<?> impl = languageDescription.getImpl(false);
@@ -901,6 +891,10 @@
         throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs);
     }
 
+    TruffleLanguage<?> findLanguage(Probe probe) {
+        return findLanguage(SPI.findLanguage(probe));
+    }
+
     Env findEnv(Class<? extends TruffleLanguage> languageClazz) {
         for (Map.Entry<String, Language> entrySet : langs.entrySet()) {
             Language languageDescription = entrySet.getValue();
@@ -912,10 +906,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) {
@@ -951,9 +941,9 @@
         }
 
         @Override
-        protected Env attachEnv(Object obj, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn) {
+        protected Env attachEnv(Object obj, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn, Instrumenter instrumenter) {
             PolyglotEngine vm = (PolyglotEngine) obj;
-            return super.attachEnv(vm, language, stdOut, stdErr, stdIn);
+            return super.attachEnv(vm, language, stdOut, stdErr, stdIn, instrumenter);
         }
 
         @Override
@@ -972,13 +962,19 @@
         }
 
         @Override
-        public ToolSupportProvider getToolSupport(TruffleLanguage<?> l) {
-            return super.getToolSupport(l);
+        protected Instrumenter createInstrumenter(Object vm) {
+            return super.createInstrumenter(vm);
         }
 
         @Override
-        public DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
-            return super.getDebugSupport(l);
+        protected Debugger createDebugger(Object vm, Instrumenter instrumenter) {
+            return super.createDebugger(vm, instrumenter);
+        }
+
+        @Override
+        protected Instrumenter getInstrumenter(Object obj) {
+            final PolyglotEngine vm = (PolyglotEngine) obj;
+            return vm.instrumenter;
         }
 
         @Override
@@ -993,9 +989,15 @@
         }
 
         @Override
-        protected Closeable executionStart(Object obj, Debugger[] fillIn, Source s) {
+        protected TruffleLanguage findLanguageImpl(Object obj, Class<? extends TruffleLanguage> languageClazz) {
+            final PolyglotEngine vm = (PolyglotEngine) obj;
+            return vm.findLanguage(languageClazz);
+        }
+
+        @Override
+        protected Closeable executionStart(Object obj, Debugger debugger, Source s) {
             PolyglotEngine vm = (PolyglotEngine) obj;
-            return super.executionStart(vm, fillIn, s);
+            return super.executionStart(vm, debugger, s);
         }
 
         @Override
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,10 +24,6 @@
  */
 package com.oracle.truffle.api.vm;
 
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.debug.ExecutionEvent;
-import com.oracle.truffle.api.debug.SuspendedEvent;
-import com.oracle.truffle.api.source.Source;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -37,6 +33,11 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.debug.ExecutionEvent;
+import com.oracle.truffle.api.debug.SuspendedEvent;
+import com.oracle.truffle.api.source.Source;
+
 /**
  * @deprecated
  */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,12 +24,6 @@
  */
 package com.oracle.truffle.api;
 
-import com.oracle.truffle.api.debug.DebugSupportProvider;
-import com.oracle.truffle.api.impl.Accessor;
-import com.oracle.truffle.api.impl.FindContextNode;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.source.Source;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -45,6 +39,21 @@
 import java.util.Map;
 import java.util.WeakHashMap;
 
+import com.oracle.truffle.api.debug.Debugger;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.impl.Accessor;
+import com.oracle.truffle.api.impl.FindContextNode;
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.SyntaxTag;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.Source;
+
 /**
  * An entry point for everyone who wants to implement a Truffle based language. By providing an
  * implementation of this type and registering it using {@link Registration} annotation, your
@@ -52,6 +61,13 @@
  * polyglot execution engine} - all they will need to do is to include your JAR into their
  * application and all the Truffle goodies (multi-language support, multitenant hosting, debugging,
  * etc.) will be made available to them.
+ * <p>
+ * The use of {@linkplain Instrumenter Instrument-based services} requires that the language
+ * {@linkplain Instrumenter#registerASTProber(com.oracle.truffle.api.instrument.ASTProber) register}
+ * an instance of {@link ASTProber} suitable for the language implementation that can be applied to
+ * "mark up" each newly created AST with {@link SyntaxTag "tags"} that identify standard syntactic
+ * constructs in order to configure tool behavior. See also {@linkplain #createContext(Env)
+ * createContext(Env)}.
  *
  * @param <C> internal state of the language associated with every thread that is executing program
  *            {@link #parse(com.oracle.truffle.api.source.Source, com.oracle.truffle.api.nodes.Node, java.lang.String...)
@@ -67,18 +83,18 @@
 
     /**
      * The annotation to use to register your language to the
-     * {@link com.oracle.truffle.api.vm.TruffleVM Truffle} system. By annotating your implementation
-     * of {@link TruffleLanguage} by this annotation you are just a
+     * {@link com.oracle.truffle.api.vm.PolyglotEngine Truffle} system. By annotating your
+     * implementation of {@link TruffleLanguage} by this annotation you are just a
      * <em>one JAR drop to the class path</em> away from your users. Once they include your JAR in
      * their application, your language will be available to the
-     * {@link com.oracle.truffle.api.vm.TruffleVM Truffle virtual machine}.
+     * {@link com.oracle.truffle.api.vm.PolyglotEngine Truffle virtual machine}.
      */
     @Retention(RetentionPolicy.SOURCE)
     @Target(ElementType.TYPE)
     public @interface Registration {
         /**
          * Unique name of your language. This name will be exposed to users via the
-         * {@link com.oracle.truffle.api.vm.TruffleVM.Language#getName()} getter.
+         * {@link com.oracle.truffle.api.vm.PolyglotEngine.Language#getName()} getter.
          *
          * @return identifier of your language
          */
@@ -86,7 +102,7 @@
 
         /**
          * Unique string identifying the language version. This name will be exposed to users via
-         * the {@link com.oracle.truffle.api.vm.TruffleVM.Language#getVersion()} getter.
+         * the {@link com.oracle.truffle.api.vm.PolyglotEngine.Language#getVersion()} getter.
          *
          * @return version of your language
          */
@@ -95,7 +111,7 @@
         /**
          * List of MIME types associated with your language. Users will use them (directly or
          * indirectly) when
-         * {@link com.oracle.truffle.api.vm.TruffleVM#eval(com.oracle.truffle.api.source.Source)
+         * {@link com.oracle.truffle.api.vm.PolyglotEngine#eval(com.oracle.truffle.api.source.Source)
          * executing} their code snippets or their {@link Source files}.
          *
          * @return array of MIME types assigned to your language files
@@ -117,6 +133,15 @@
      * {@link Node findNode} and later {@link #findContext(com.oracle.truffle.api.nodes.Node)
      * findContext(findNode)} to get back your language context.
      *
+     * If it is expected that any {@linkplain Instrumenter Instrumentation Services} or tools that
+     * depend on those services (e.g. the {@link Debugger}, then part of the preparation in the new
+     * context is to
+     * {@linkplain Instrumenter#registerASTProber(com.oracle.truffle.api.instrument.ASTProber)
+     * register} a "default" {@link ASTProber} for the language implementation. Instrumentation
+     * requires that this be available to "mark up" each newly created AST with
+     * {@linkplain SyntaxTag "tags"} that identify standard syntactic constructs in order to
+     * configure tool behavior.
+     *
      * @param env the environment the language is supposed to operate in
      * @return internal data of the language in given environment
      */
@@ -152,7 +177,7 @@
      *         just parsed <code>code</code>
      * @throws IOException thrown when I/O or parsing goes wrong. Here-in thrown exception is
      *             propagate to the user who called one of <code>eval</code> methods of
-     *             {@link com.oracle.truffle.api.vm.TruffleVM}
+     *             {@link com.oracle.truffle.api.vm.PolyglotEngine}
      */
     protected abstract CallTarget parse(Source code, Node context, String... argumentNames) throws IOException;
 
@@ -201,9 +226,59 @@
      */
     protected abstract boolean isObjectOfLanguage(Object object);
 
-    protected abstract ToolSupportProvider getToolSupport();
+    /**
+     * Gets visualization services for language-specific information.
+     */
+    protected abstract Visualizer getVisualizer();
+
+    /**
+     * Returns {@code true} for a node can be "instrumented" by
+     * {@linkplain Instrumenter#probe(Node) probing}.
+     * <p>
+     * <b>Note:</b> instrumentation requires a appropriate {@link WrapperNode}
+     *
+     * @see WrapperNode
+     */
+    protected abstract boolean isInstrumentable(Node node);
 
-    protected abstract DebugSupportProvider getDebugSupport();
+    /**
+     * For nodes in this language that are <em>instrumentable</em>, this method returns an
+     * {@linkplain Node AST node} that:
+     * <ol>
+     * <li>implements {@link WrapperNode};</li>
+     * <li>has the node argument as it's child; and</li>
+     * <li>whose type is safe for replacement of the node in the parent.</li>
+     * </ol>
+     *
+     * @return an appropriately typed {@link WrapperNode}
+     */
+    protected abstract WrapperNode createWrapperNode(Node node);
+
+    /**
+     * Runs source code in a halted execution context, or at top level.
+     *
+     * @param source the code to run
+     * @param node node where execution halted, {@code null} if no execution context
+     * @param mFrame frame where execution halted, {@code null} if no execution context
+     * @return result of running the code in the context, or at top level if no execution context.
+     * @throws IOException if the evaluation cannot be performed
+     */
+    protected abstract Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException;
+
+    /**
+     * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot}
+     * that, when executed, computes the result of a textual expression in the language; used to
+     * create an
+     * {@linkplain Instrumenter#attach(com.oracle.truffle.api.instrument.Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)}
+     * .
+     *
+     * @param expr a guest language expression
+     * @param resultListener optional listener for the result of each evaluation.
+     * @return a new factory
+     * @throws IOException if the factory cannot be created, for example if the expression is badly
+     *             formed.
+     */
+    protected abstract AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException;
 
     /**
      * Allows a language implementor to create a node that can effectively lookup up the context
@@ -278,13 +353,15 @@
         private final InputStream in;
         private final OutputStream err;
         private final OutputStream out;
+        private final Instrumenter instrumenter;
 
-        Env(Object vm, TruffleLanguage<?> lang, OutputStream out, OutputStream err, InputStream in) {
+        Env(Object vm, TruffleLanguage<?> lang, OutputStream out, OutputStream err, InputStream in, Instrumenter instrumenter) {
             this.vm = vm;
             this.in = in;
             this.err = err;
             this.out = out;
             this.lang = lang;
+            this.instrumenter = instrumenter;
             this.langCtx = new LangCtx<>(lang, this);
         }
 
@@ -302,8 +379,8 @@
         }
 
         /**
-         * Input associated with {@link com.oracle.truffle.api.vm.TruffleVM} this language is being
-         * executed in.
+         * Input associated with {@link com.oracle.truffle.api.vm.PolyglotEngine} this language is
+         * being executed in.
          *
          * @return reader, never <code>null</code>
          */
@@ -317,8 +394,8 @@
         }
 
         /**
-         * Standard output writer for {@link com.oracle.truffle.api.vm.TruffleVM} this language is
-         * being executed in.
+         * Standard output writer for {@link com.oracle.truffle.api.vm.PolyglotEngine} this language
+         * is being executed in.
          *
          * @return writer, never <code>null</code>
          */
@@ -332,8 +409,8 @@
         }
 
         /**
-         * Standard error writer for {@link com.oracle.truffle.api.vm.TruffleVM} this language is
-         * being executed in.
+         * Standard error writer for {@link com.oracle.truffle.api.vm.PolyglotEngine} this language
+         * is being executed in.
          *
          * @return writer, never <code>null</code>
          */
@@ -345,6 +422,10 @@
         public Writer stdErr() {
             return new OutputStreamWriter(err);
         }
+
+        public Instrumenter instrumenter() {
+            return instrumenter;
+        }
     }
 
     private static final AccessAPI API = new AccessAPI();
@@ -352,8 +433,8 @@
     @SuppressWarnings("rawtypes")
     private static final class AccessAPI extends Accessor {
         @Override
-        protected Env attachEnv(Object vm, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn) {
-            Env env = new Env(vm, language, stdOut, stdErr, stdIn);
+        protected Env attachEnv(Object vm, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn, Instrumenter instrumenter) {
+            Env env = new Env(vm, language, stdOut, stdErr, stdIn, instrumenter);
             return env;
         }
 
@@ -382,6 +463,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);
         }
@@ -407,13 +496,13 @@
         }
 
         @Override
-        protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) {
-            return l.getToolSupport();
+        protected boolean isInstrumentable(Node node, TruffleLanguage language) {
+            return language.isInstrumentable(node);
         }
 
         @Override
-        protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
-            return l.getDebugSupport();
+        protected WrapperNode createWrapperNode(Node node, TruffleLanguage language) {
+            return language.createWrapperNode(node);
         }
 
         @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Breakpoint.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,10 +24,13 @@
  */
 package com.oracle.truffle.api.debug;
 
-import com.oracle.truffle.api.instrument.Instrument;
+import java.io.IOException;
+
+import javax.sound.midi.Instrument;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.source.Source;
-import java.io.IOException;
 
 /**
  * Breakpoint in a {@link com.oracle.truffle.api.vm.PolyglotEngine} with
@@ -115,8 +118,8 @@
     }
 
     /**
-     * Enables or disables this breakpoint's AST instrumentation. The breakpoint is enabled by
-     * default.
+     * Enables or disables this breakpoint's AST {@linkplain Instrumenter instrumentation}. The
+     * breakpoint is enabled by default.
      *
      * @param enabled <code>true</code> to activate the instrumentation, <code>false</code> to
      *            deactivate the instrumentation so that it has no effect.
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportException.java	Tue Sep 29 18:04:11 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.debug;
-
-public class DebugSupportException extends Exception {
-
-    private static final long serialVersionUID = 3039074861617372741L;
-
-    public DebugSupportException(String string) {
-        super(string);
-    }
-
-    public DebugSupportException(Exception ex) {
-        super(ex);
-    }
-
-}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java	Tue Sep 29 18:04:11 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.debug;
-
-import com.oracle.truffle.api.frame.MaterializedFrame;
-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.nodes.Node;
-import com.oracle.truffle.api.source.Source;
-
-/**
- * Access to language-specific information and execution services to enable debugging.
- */
-public interface DebugSupportProvider extends ToolSupportProvider {
-    /**
-     * 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 DebugSupportException if the evaluation cannot be performed
-     */
-    Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException;
-
-    /**
-     * 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 DebugSupportException if the factory cannot be created, for example if the expression
-     *             is badly formed.
-     */
-    AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException;
-}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,9 +24,17 @@
  */
 package com.oracle.truffle.api.debug;
 
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.frame.FrameInstance;
 import com.oracle.truffle.api.frame.FrameInstanceVisitor;
 import com.oracle.truffle.api.frame.MaterializedFrame;
@@ -34,21 +42,17 @@
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
-import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.KillException;
 import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.StandardAfterInstrumentListener;
+import com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
 import com.oracle.truffle.api.instrument.SyntaxTag;
-import com.oracle.truffle.api.instrument.SyntaxTagTrap;
+import com.oracle.truffle.api.instrument.TagInstrument;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.LineLocation;
 import com.oracle.truffle.api.source.Source;
-import java.io.Closeable;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
 
 /**
  * Represents debugging related state of a {@link com.oracle.truffle.api.vm.PolyglotEngine}.
@@ -60,7 +64,7 @@
 public final class Debugger {
 
     private static final boolean TRACE = false;
-    private static final String TRACE_PREFIX = "DEBUG ENGINE: ";
+    private static final String TRACE_PREFIX = "Debugger: ";
 
     private static final PrintStream OUT = System.out;
 
@@ -73,6 +77,7 @@
         }
     }
 
+    private final Instrumenter instrumenter;
     private final Object vm;
     private Source lastSource;
 
@@ -92,6 +97,9 @@
         void addWarning(String warning);
     }
 
+    private final BreakpointCallback breakpointCallback;
+    private final WarningLog warningLog;
+
     /**
      * Implementation of line-oriented breakpoints.
      */
@@ -107,9 +115,9 @@
      */
     private DebugExecutionContext debugContext;
 
-    Debugger(Object vm) {
+    Debugger(Object vm, Instrumenter instrumenter) {
         this.vm = vm;
-
+        this.instrumenter = instrumenter;
         Source.setFileCaching(true);
 
         // Initialize execution context stack
@@ -117,7 +125,7 @@
         prepareContinue();
         debugContext.contextTrace("START EXEC DEFAULT");
 
-        final BreakpointCallback breakpointCallback = new BreakpointCallback() {
+        breakpointCallback = new BreakpointCallback() {
 
             @TruffleBoundary
             public void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason) {
@@ -125,7 +133,7 @@
             }
         };
 
-        final WarningLog warningLog = new WarningLog() {
+        warningLog = new WarningLog() {
 
             public void addWarning(String warning) {
                 assert debugContext != null;
@@ -137,10 +145,6 @@
         this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog);
     }
 
-    Object vm() {
-        return vm;
-    }
-
     /**
      * Sets a breakpoint to halt at a source line.
      *
@@ -269,11 +273,12 @@
         debugContext.setStrategy(new StepOver(stepCount));
     }
 
+    // TODO (mlvdv) used by the breakpoint factories; to be deprecated/replaced.
     /**
      * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot}
      * that, when executed, computes the result of a textual expression in the language; used to
      * create an
-     * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+     * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
      * Advanced Instrument}.
      *
      * @param expr a guest language expression
@@ -284,15 +289,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;
     }
 
     /**
@@ -407,6 +409,8 @@
      * @see Debugger#prepareStepInto(int)
      */
     private final class StepInto extends StepStrategy {
+        private TagInstrument beforeTagInstrument;
+        private TagInstrument afterTagInstrument;
         private int unfinishedStepCount;
 
         StepInto(int stepCount) {
@@ -416,26 +420,40 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
+
+            beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() {
                 @TruffleBoundary
                 @Override
-                public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
+                public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
                     // HALT: just before statement
                     --unfinishedStepCount;
-                    strategyTrace("TRAP BEFORE", "unfinished steps=%d", unfinishedStepCount);
+                    strategyTrace("HALT BEFORE", "unfinished steps=%d", unfinishedStepCount);
                     // Should run in fast path
                     if (unfinishedStepCount <= 0) {
-                        halt(node, mFrame, true);
+                        halt(node, vFrame.materialize(), true);
                     }
                     strategyTrace("RESUME BEFORE", "");
                 }
-            });
-            Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
+            }, "Debugger StepInto");
+
+            afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() {
+
+                public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) {
+                    doHalt(node, vFrame.materialize());
+                }
+
+                public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
+                    doHalt(node, vFrame.materialize());
+                }
+
+                public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
+                    doHalt(node, vFrame.materialize());
+                }
+
                 @TruffleBoundary
-                @Override
-                public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
+                private void doHalt(Node node, MaterializedFrame mFrame) {
                     --unfinishedStepCount;
-                    strategyTrace(null, "TRAP AFTER unfinished steps=%d", unfinishedStepCount);
+                    strategyTrace(null, "HALT AFTER unfinished steps=%d", unfinishedStepCount);
                     if (currentStackDepth() < stackDepth) {
                         // HALT: just "stepped out"
                         if (unfinishedStepCount <= 0) {
@@ -444,13 +462,13 @@
                     }
                     strategyTrace("RESUME AFTER", "");
                 }
-            });
+            }, "Debugger StepInto");
         }
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
-            Probe.setAfterTagTrap(null);
+            beforeTagInstrument.dispose();
+            afterTagInstrument.dispose();
         }
     }
 
@@ -471,27 +489,41 @@
      */
     private final class StepOut extends StepStrategy {
 
+        private TagInstrument afterTagInstrument;
+
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
+
+            afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() {
+
+                public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) {
+                    doHalt(node, vFrame.materialize());
+                }
+
+                public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
+                    doHalt(node, vFrame.materialize());
+                }
+
+                public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
+                    doHalt(node, vFrame.materialize());
+                }
 
                 @TruffleBoundary
-                @Override
-                public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
+                private void doHalt(Node node, MaterializedFrame mFrame) {
                     // HALT:
                     final int currentStackDepth = currentStackDepth();
-                    strategyTrace("TRAP AFTER", "stackDepth: start=%d current=%d", stackDepth, currentStackDepth);
+                    strategyTrace("HALT AFTER", "stackDepth: start=%d current=%d", stackDepth, currentStackDepth);
                     if (currentStackDepth < stackDepth) {
                         halt(node, mFrame, false);
                     }
                     strategyTrace("RESUME AFTER", "");
                 }
-            });
+            }, "Debugger StepOut");
         }
 
         @Override
         protected void unsetStrategy() {
-            Probe.setAfterTagTrap(null);
+            afterTagInstrument.dispose();
         }
     }
 
@@ -509,6 +541,8 @@
      * </ul>
      */
     private final class StepOver extends StepStrategy {
+        private TagInstrument beforeTagInstrument;
+        private TagInstrument afterTagInstrument;
         private int unfinishedStepCount;
 
         StepOver(int stepCount) {
@@ -517,20 +551,21 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
+            beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() {
+
                 @TruffleBoundary
                 @Override
-                public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
+                public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
                     final int currentStackDepth = currentStackDepth();
                     if (currentStackDepth <= stackDepth) {
                         // HALT: stack depth unchanged or smaller; treat like StepInto
                         --unfinishedStepCount;
                         if (TRACE) {
-                            strategyTrace("TRAP BEFORE", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
+                            strategyTrace("HALT BEFORE", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
                         }
                         // Test should run in fast path
                         if (unfinishedStepCount <= 0) {
-                            halt(node, mFrame, true);
+                            halt(node, vFrame.materialize(), true);
                         }
                     } else {
                         // CONTINUE: Stack depth increased; don't count as a step
@@ -540,17 +575,29 @@
                     }
                     strategyTrace("RESUME BEFORE", "");
                 }
-            });
+            }, "Debugger StepOver");
+
+            afterTagInstrument = instrumenter.attach(CALL_TAG, new StandardAfterInstrumentListener() {
+
+                public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) {
+                    doHalt(node, vFrame.materialize());
+                }
 
-            Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) {
+                public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
+                    doHalt(node, vFrame.materialize());
+                }
+
+                public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
+                    doHalt(node, vFrame.materialize());
+                }
+
                 @TruffleBoundary
-                @Override
-                public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
+                private void doHalt(Node node, MaterializedFrame mFrame) {
                     final int currentStackDepth = currentStackDepth();
                     if (currentStackDepth < stackDepth) {
                         // HALT: just "stepped out"
                         --unfinishedStepCount;
-                        strategyTrace("TRAP AFTER", "unfinished steps=%d stackDepth: start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
+                        strategyTrace("HALT AFTER", "unfinished steps=%d stackDepth: start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
                         // Should run in fast path
                         if (unfinishedStepCount <= 0) {
                             halt(node, mFrame, false);
@@ -558,13 +605,13 @@
                         strategyTrace("RESUME AFTER", "");
                     }
                 }
-            });
+            }, "Debugger StepOver");
         }
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
-            Probe.setAfterTagTrap(null);
+            beforeTagInstrument.dispose();
+            afterTagInstrument.dispose();
         }
     }
 
@@ -582,6 +629,7 @@
      * </ul>
      */
     private final class StepOverNested extends StepStrategy {
+        private TagInstrument beforeTagInstrument;
         private int unfinishedStepCount;
         private final int startStackDepth;
 
@@ -592,28 +640,28 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) {
+            beforeTagInstrument = instrumenter.attach(STEPPING_TAG, new StandardBeforeInstrumentListener() {
                 @TruffleBoundary
                 @Override
-                public void tagTrappedAt(Node node, MaterializedFrame mFrame) {
+                public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
                     final int currentStackDepth = currentStackDepth();
                     if (currentStackDepth <= startStackDepth) {
                         // At original step depth (or smaller) after being nested
                         --unfinishedStepCount;
-                        strategyTrace("TRAP AFTER", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
+                        strategyTrace("HALT AFTER", "unfinished steps=%d stackDepth start=%d current=%d", unfinishedStepCount, stackDepth, currentStackDepth);
                         if (unfinishedStepCount <= 0) {
-                            halt(node, mFrame, false);
+                            halt(node, vFrame.materialize(), false);
                         }
                         // TODO (mlvdv) fixme for multiple steps
                         strategyTrace("RESUME BEFORE", "");
                     }
                 }
-            });
+            }, "Debuger StepOverNested");
         }
 
         @Override
         protected void unsetStrategy() {
-            Probe.setBeforeTagTrap(null);
+            beforeTagInstrument.dispose();
         }
     }
 
@@ -809,41 +857,32 @@
 
     @SuppressWarnings("rawtypes")
     private static final class AccessorDebug extends Accessor {
+
         @Override
-        protected Closeable executionStart(Object vm, Debugger[] fillIn, Source s) {
-            final Debugger d;
-            if (fillIn[0] == null) {
-                d = fillIn[0] = new Debugger(vm);
-            } else {
-                d = fillIn[0];
-            }
-            d.executionStarted(s);
+        protected Closeable executionStart(Object vm, final Debugger debugger, Source s) {
+            debugger.executionStarted(s);
             return new Closeable() {
                 @Override
                 public void close() throws IOException {
-                    d.executionEnded();
+                    debugger.executionEnded();
                 }
             };
         }
 
         @Override
+        protected Debugger createDebugger(Object vm, Instrumenter instrumenter) {
+            return new Debugger(vm, instrumenter);
+        }
+
+        @Override
         protected Class<? extends TruffleLanguage> findLanguage(Probe probe) {
             return super.findLanguage(probe);
         }
 
         @Override
-        protected TruffleLanguage.Env findLanguage(Object vm, Class<? extends TruffleLanguage> languageClass) {
-            return super.findLanguage(vm, languageClass);
-        }
-
-        @Override
-        protected TruffleLanguage<?> findLanguage(TruffleLanguage.Env env) {
-            return super.findLanguage(env);
-        }
-
-        @Override
-        protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
-            return super.getDebugSupport(l);
+        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/LineBreakpoint.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpoint.java	Wed Sep 30 16:33:56 2015 -0700
@@ -26,7 +26,6 @@
 
 import com.oracle.truffle.api.source.LineLocation;
 
-// TODO (mlvdv) generic?
 /**
  * A breakpoint associated with a {@linkplain LineLocation source line location}.
  *
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,20 +24,32 @@
  */
 package com.oracle.truffle.api.debug;
 
-import com.oracle.truffle.api.Assumption;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.Truffle;
 import static com.oracle.truffle.api.debug.Breakpoint.State.DISABLED;
 import static com.oracle.truffle.api.debug.Breakpoint.State.DISABLED_UNRESOLVED;
 import static com.oracle.truffle.api.debug.Breakpoint.State.DISPOSED;
 import static com.oracle.truffle.api.debug.Breakpoint.State.ENABLED;
 import static com.oracle.truffle.api.debug.Breakpoint.State.ENABLED_UNRESOLVED;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.debug.Debugger.BreakpointCallback;
 import com.oracle.truffle.api.debug.Debugger.WarningLog;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
-import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.ProbeInstrument;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
 import com.oracle.truffle.api.instrument.SyntaxTag;
@@ -48,15 +60,6 @@
 import com.oracle.truffle.api.source.LineLocation;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.utilities.CyclicAssumption;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
 
 //TODO (mlvdv) some common functionality could be factored out of this and TagBreakpointSupport
 
@@ -67,9 +70,9 @@
  * <ol>
  * <li>Line breakpoints can only be set at nodes tagged as {@link StandardSyntaxTag#STATEMENT}.</li>
  * <li>A newly created breakpoint looks for probes matching the location, attaches to them if found
- * by installing an {@link Instrument} that calls back to the breakpoint.</li>
- * <li>When Truffle "splits" or otherwise copies an AST, any attached {@link Instrument} will be
- * copied along with the rest of the AST and will call back to the same breakpoint.</li>
+ * by installing an {@link ProbeInstrument} that calls back to the breakpoint.</li>
+ * <li>When Truffle "splits" or otherwise copies an AST, any attached {@link ProbeInstrument} will
+ * be copied along with the rest of the AST and will call back to the same breakpoint.</li>
  * <li>When notification is received of a new Node being tagged as a statement, and if a
  * breakpoint's line location matches the Probe's line location, then the breakpoint will attach a
  * new Instrument at the probe to activate the breakpoint at that location.</li>
@@ -83,7 +86,7 @@
     private static final boolean TRACE = false;
     private static final PrintStream OUT = System.out;
 
-    private static final String BREAKPOINT_NAME = "LINE BREAKPOINT";
+    private static final String BREAKPOINT_NAME = "Line Breakpoints";
 
     @TruffleBoundary
     private static void trace(String format, Object... args) {
@@ -106,6 +109,7 @@
         }
     };
 
+    private final Debugger debugger;
     private final BreakpointCallback breakpointCallback;
     private final WarningLog warningLog;
 
@@ -127,17 +131,17 @@
      */
     @CompilationFinal private boolean breakpointsActive = true;
     private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active");
-    private final Debugger debugger;
 
     LineBreakpointFactory(Debugger debugger, BreakpointCallback breakpointCallback, final WarningLog warningLog) {
         this.debugger = debugger;
         this.breakpointCallback = breakpointCallback;
         this.warningLog = warningLog;
 
-        lineToProbesMap = new LineToProbesMap();
-        lineToProbesMap.install();
+        final Instrumenter instrumenter = debugger.getInstrumenter();
+        this.lineToProbesMap = new LineToProbesMap();
+        instrumenter.install(lineToProbesMap);
 
-        Probe.addProbeListener(new DefaultProbeListener() {
+        instrumenter.addProbeListener(new DefaultProbeListener() {
 
             @Override
             public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
@@ -288,7 +292,7 @@
          * The instrument(s) that this breakpoint currently has attached to a {@link Probe}:
          * {@code null} if not attached.
          */
-        private List<Instrument> instruments = new ArrayList<>();
+        private List<ProbeInstrument> instruments = new ArrayList<>();
 
         public LineBreakpointImpl(int ignoreCount, LineLocation lineLocation, boolean oneShot) {
             super(ENABLED_UNRESOLVED, ignoreCount, oneShot);
@@ -343,7 +347,7 @@
             if (this.conditionExpr != null || expr != null) {
                 // De-instrument the Probes instrumented by this breakpoint
                 final ArrayList<Probe> probes = new ArrayList<>();
-                for (Instrument instrument : instruments) {
+                for (ProbeInstrument instrument : instruments) {
                     probes.add(instrument.getProbe());
                     instrument.dispose();
                 }
@@ -365,7 +369,7 @@
         @Override
         public void dispose() {
             if (getState() != DISPOSED) {
-                for (Instrument instrument : instruments) {
+                for (ProbeInstrument instrument : instruments) {
                     instrument.dispose();
                 }
                 changeState(DISPOSED);
@@ -377,13 +381,13 @@
             if (getState() == DISPOSED) {
                 throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME);
             }
-            Instrument newInstrument = null;
+            ProbeInstrument newInstrument = null;
+            final Instrumenter instrumenter = debugger.getInstrumenter();
             if (conditionExpr == null) {
-                newInstrument = Instrument.create(new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME);
+                newInstrument = instrumenter.attach(newProbe, new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME);
             } else {
-                newInstrument = Instrument.create(this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME);
+                newInstrument = instrumenter.attach(newProbe, this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME);
             }
-            newProbe.attach(newInstrument);
             instruments.add(newInstrument);
             changeState(isEnabled ? ENABLED : DISABLED);
         }
@@ -442,7 +446,7 @@
             }
         }
 
-        public void notifyResult(Node node, VirtualFrame vFrame, Object result) {
+        public void onExecution(Node node, VirtualFrame vFrame, Object result) {
             final boolean condition = (Boolean) result;
             if (TRACE) {
                 trace("breakpoint condition = %b  %s", condition, getShortDescription());
@@ -452,7 +456,7 @@
             }
         }
 
-        public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) {
+        public void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex) {
             addExceptionWarning(ex);
             if (TRACE) {
                 trace("breakpoint failure = %s  %s", ex, getShortDescription());
@@ -479,7 +483,7 @@
         private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener {
 
             @Override
-            public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+            public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
                 LineBreakpointImpl.this.nodeEnter(node, vFrame);
             }
         }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,13 +24,6 @@
  */
 package com.oracle.truffle.api.debug;
 
-import com.oracle.truffle.api.instrument.InstrumentationTool;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeListener;
-import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
-import com.oracle.truffle.api.source.LineLocation;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.source.SourceSection;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -38,11 +31,19 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ProbeListener;
+import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
+import com.oracle.truffle.api.source.LineLocation;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+
 /**
- * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST,
- * indexed by {@link Source} and line number.
+ * An {@linkplain Instrumenter.Tool Instrumentation-based Tool} that builds a map of every
+ * {@link Probe} attached to some AST, indexed by {@link Source} and line number.
  */
-final class LineToProbesMap extends InstrumentationTool {
+final class LineToProbesMap extends Instrumenter.Tool {
 
     private static final boolean TRACE = false;
     private static final PrintStream OUT = System.out;
@@ -68,7 +69,11 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        final Instrumenter instrumenter = getInstrumenter();
+        for (Probe probe : instrumenter.findProbesTaggedAs(null)) {
+            addMapEntry(probe);
+        }
+        instrumenter.addProbeListener(probeListener);
         return true;
     }
 
@@ -79,7 +84,7 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
+        getInstrumenter().removeProbeListener(probeListener);
     }
 
     /**
@@ -115,21 +120,25 @@
 
         @Override
         public void newProbeInserted(Probe probe) {
-            final SourceSection sourceSection = probe.getProbedSourceSection();
-            if (sourceSection != null && sourceSection.getSource() != null) {
-                final LineLocation lineLocation = sourceSection.getLineLocation();
-                if (TRACE) {
-                    trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());
-                }
-                Collection<Probe> probes = lineToProbesMap.get(lineLocation);
-                if (probes == null) {
-                    probes = new ArrayList<>(2);
-                    lineToProbesMap.put(lineLocation, probes);
-                } else {
-                    assert !probes.contains(probe);
-                }
-                probes.add(probe);
+            addMapEntry(probe);
+        }
+    }
+
+    private void addMapEntry(Probe probe) {
+        final SourceSection sourceSection = probe.getProbedSourceSection();
+        if (sourceSection != null && sourceSection.getSource() != null) {
+            final LineLocation lineLocation = sourceSection.getLineLocation();
+            if (TRACE) {
+                trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());
             }
+            Collection<Probe> probes = lineToProbesMap.get(lineLocation);
+            if (probes == null) {
+                probes = new ArrayList<>(2);
+                lineToProbesMap.put(lineLocation, probes);
+            } else {
+                assert !probes.contains(probe);
+            }
+            probes.add(probe);
         }
     }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpoint.java	Wed Sep 30 16:33:56 2015 -0700
@@ -26,7 +26,6 @@
 
 import com.oracle.truffle.api.instrument.SyntaxTag;
 
-// TODO (mlvdv) generic?
 /**
  * A breakpoint associated with a {@link SyntaxTag}.
  *
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Wed Sep 30 16:33:56 2015 -0700
@@ -46,10 +46,10 @@
 import com.oracle.truffle.api.debug.Debugger.WarningLog;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
-import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ProbeInstrument;
 import com.oracle.truffle.api.instrument.SyntaxTag;
-import com.oracle.truffle.api.instrument.SyntaxTagTrap;
 import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
 import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener;
 import com.oracle.truffle.api.nodes.InvalidAssumptionException;
@@ -62,18 +62,20 @@
  * 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#attach(SyntaxTag, com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener, String)
+ * before TagInstrument}, does the same thing more efficiently, but there may only be one
+ * <em>before</em> TagInstrument active at a time. Any number of {@link TagBreakpoint}s may coexist
+ * with the <em>before</em> TagInstrument, but it would be confusing to have a {@link TagBreakpoint}
+ * set for the same Tag as the current <em>before</em> TagInstrument.
  * <p>
  * Notes:
  * <ol>
  * <li>Only one Tag Breakpoint can be active for a specific {@linkplain SyntaxTag Tag}.</li>
  * <li>A newly created breakpoint looks for probes matching the tag, attaches to them if found by
- * installing an {@link Instrument}.</li>
- * <li>When Truffle "splits" or otherwise copies an AST, any attached {@link Instrument} will be
- * copied along with the rest of the AST and will call back to the same breakpoint.</li>
+ * installing an {@link ProbeInstrument}.</li>
+ * <li>When Truffle "splits" or otherwise copies an AST, any attached {@link ProbeInstrument} will
+ * be copied along with the rest of the AST and will call back to the same breakpoint.</li>
  * <li>When notification is received that the breakpoint's Tag has been newly added to a Node, then
  * the breakpoint will attach a new Instrument at the probe to activate the breakpoint at that
  * location.</li>
@@ -103,6 +105,7 @@
         }
     };
 
+    private final Debugger debugger;
     private final BreakpointCallback breakpointCallback;
     private final WarningLog warningLog;
 
@@ -117,14 +120,13 @@
      */
     @CompilationFinal private boolean breakpointsActive = true;
     private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active");
-    private final Debugger debugger;
 
     TagBreakpointFactory(Debugger debugger, BreakpointCallback breakpointCallback, final WarningLog warningLog) {
         this.debugger = debugger;
         this.breakpointCallback = breakpointCallback;
         this.warningLog = warningLog;
 
-        Probe.addProbeListener(new DefaultProbeListener() {
+        debugger.getInstrumenter().addProbeListener(new DefaultProbeListener() {
 
             @Override
             public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
@@ -195,7 +197,7 @@
 
             tagToBreakpoint.put(tag, breakpoint);
 
-            for (Probe probe : Probe.findProbesTaggedAs(tag)) {
+            for (Probe probe : debugger.getInstrumenter().findProbesTaggedAs(tag)) {
                 breakpoint.attach(probe);
             }
         } else {
@@ -259,7 +261,7 @@
          * The instrument(s) that this breakpoint currently has attached to a {@link Probe}:
          * {@code null} if not attached.
          */
-        private List<Instrument> instruments = new ArrayList<>();
+        private List<ProbeInstrument> instruments = new ArrayList<>();
 
         private TagBreakpointImpl(int ignoreCount, SyntaxTag tag, boolean oneShot) {
             super(ENABLED, ignoreCount, oneShot);
@@ -306,7 +308,7 @@
             if (this.conditionExpr != null || expr != null) {
                 // De-instrument the Probes instrumented by this breakpoint
                 final ArrayList<Probe> probes = new ArrayList<>();
-                for (Instrument instrument : instruments) {
+                for (ProbeInstrument instrument : instruments) {
                     probes.add(instrument.getProbe());
                     instrument.dispose();
                 }
@@ -328,7 +330,7 @@
         @Override
         public void dispose() {
             if (getState() != DISPOSED) {
-                for (Instrument instrument : instruments) {
+                for (ProbeInstrument instrument : instruments) {
                     instrument.dispose();
                 }
                 changeState(DISPOSED);
@@ -340,13 +342,13 @@
             if (getState() == DISPOSED) {
                 throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME);
             }
-            Instrument newInstrument = null;
+            ProbeInstrument newInstrument = null;
+            final Instrumenter instrumenter = debugger.getInstrumenter();
             if (conditionExpr == null) {
-                newInstrument = Instrument.create(new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME);
+                newInstrument = instrumenter.attach(newProbe, new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME);
             } else {
-                newInstrument = Instrument.create(this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME);
+                instrumenter.attach(newProbe, this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME);
             }
-            newProbe.attach(newInstrument);
             instruments.add(newInstrument);
             changeState(isEnabled ? ENABLED : DISABLED);
         }
@@ -406,7 +408,7 @@
 
         }
 
-        public void notifyResult(Node node, VirtualFrame vFrame, Object result) {
+        public void onExecution(Node node, VirtualFrame vFrame, Object result) {
             final boolean condition = (Boolean) result;
             if (TRACE) {
                 trace("breakpoint condition = %b  %s", condition, getShortDescription());
@@ -416,7 +418,7 @@
             }
         }
 
-        public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) {
+        public void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex) {
             addExceptionWarning(ex);
             if (TRACE) {
                 trace("breakpoint failure = %s  %s", ex, getShortDescription());
@@ -443,7 +445,7 @@
         private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener {
 
             @Override
-            public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+            public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
                 TagBreakpointImpl.this.nodeEnter(node, vFrame);
             }
         }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,18 +24,6 @@
  */
 package com.oracle.truffle.api.impl;
 
-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.DebugSupportProvider;
-import com.oracle.truffle.api.debug.Debugger;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.source.Source;
 import java.io.Closeable;
 import java.io.IOException;
 import java.io.InputStream;
@@ -43,8 +31,26 @@
 import java.lang.ref.Reference;
 import java.lang.ref.WeakReference;
 
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.TruffleLanguage.Env;
+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.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
+
 /**
- * Communication between PolyglotEngine and TruffleLanguage API/SPI.
+ * Communication between PolyglotEngine, TruffleLanguage API/SPI, and other services.
  */
 @SuppressWarnings("rawtypes")
 public abstract class Accessor {
@@ -73,16 +79,6 @@
             }
 
             @Override
-            protected ToolSupportProvider getToolSupport() {
-                return null;
-            }
-
-            @Override
-            protected DebugSupportProvider getDebugSupport() {
-                return null;
-            }
-
-            @Override
             protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException {
                 throw new IOException();
             }
@@ -91,12 +87,38 @@
             protected Object createContext(TruffleLanguage.Env env) {
                 return null;
             }
+
+            @Override
+            protected boolean isInstrumentable(Node node) {
+                return false;
+            }
+
+            @Override
+            protected WrapperNode createWrapperNode(Node node) {
+                return null;
+            }
+
+            @Override
+            protected Visualizer getVisualizer() {
+                return null;
+            }
+
+            @Override
+            protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException {
+                return null;
+            }
+
+            @Override
+            protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException {
+                return null;
+            }
         };
         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);
@@ -132,8 +154,8 @@
         }
     }
 
-    protected Env attachEnv(Object vm, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn) {
-        return API.attachEnv(vm, language, stdOut, stdErr, stdIn);
+    protected Env attachEnv(Object vm, TruffleLanguage<?> language, OutputStream stdOut, OutputStream stdErr, InputStream stdIn, Instrumenter instrumenter) {
+        return API.attachEnv(vm, language, stdOut, stdErr, stdIn, instrumenter);
     }
 
     protected Object eval(TruffleLanguage<?> l, Source s) throws IOException {
@@ -152,12 +174,37 @@
         return API.languageGlobal(env);
     }
 
-    protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) {
-        return API.getToolSupport(l);
+    /**
+     * Provided by each {@linkplain TruffleLanguage language implementation}.
+     */
+    protected boolean isInstrumentable(Object vm, Node node) {
+        final RootNode rootNode = node.getRootNode();
+        Class<? extends TruffleLanguage> languageClazz = findLanguage(rootNode);
+        TruffleLanguage language = findLanguageImpl(vm, languageClazz);
+        return isInstrumentable(node, language);
+    }
+
+    protected boolean isInstrumentable(Node node, TruffleLanguage language) {
+        return API.isInstrumentable(node, language);
     }
 
-    protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) {
-        return API.getDebugSupport(l);
+    /**
+     * Provided by each {@linkplain TruffleLanguage language implementation}.
+     */
+    protected WrapperNode createWrapperNode(Object vm, Node node) {
+        final RootNode rootNode = node.getRootNode();
+        Class<? extends TruffleLanguage> languageClazz = findLanguage(rootNode);
+        TruffleLanguage language = findLanguageImpl(vm, languageClazz);
+        return createWrapperNode(node, language);
+    }
+
+    protected WrapperNode createWrapperNode(Node node, TruffleLanguage language) {
+        return API.createWrapperNode(node, language);
+    }
+
+    protected 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) {
@@ -173,7 +220,7 @@
         if (known == null) {
             vm = CURRENT_VM.get();
             if (vm == null) {
-                throw new IllegalStateException();
+                throw new IllegalStateException("Accessor.findLanguage access to vm");
             }
             if (languageClass == null) {
                 return null;
@@ -184,12 +231,46 @@
         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("Accessor.findLanguageImpl access to vm");
+            }
+        } 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("Accessor.getInstrumenter access to vm");
+            }
+        } else {
+            vm = known;
+        }
+        return SPI.getInstrumenter(vm);
+    }
+
+    protected Instrumenter createInstrumenter(Object vm) {
+        return INSTRUMENT.createInstrumenter(vm);
+    }
+
+    protected Debugger createDebugger(Object vm, Instrumenter instrumenter) {
+        return DEBUG.createDebugger(vm, instrumenter);
+    }
+
     private static Reference<Object> previousVM = new WeakReference<>(null);
     private static Assumption oneVM = Truffle.getRuntime().createAssumption();
 
-    protected Closeable executionStart(Object vm, Debugger[] fillIn, Source s) {
+    protected Closeable executionStart(Object vm, Debugger debugger, Source s) {
         vm.getClass();
-        final Closeable debugClose = DEBUG.executionStart(vm, fillIn, s);
+        final Closeable debugClose = DEBUG.executionStart(vm, debugger, s);
         final Object prev = CURRENT_VM.get();
         if (!(vm == previousVM.get())) {
             previousVM = new WeakReference<>(vm);
@@ -216,7 +297,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);
@@ -239,6 +320,11 @@
         return API.findLanguage(env);
     }
 
+    /** Applies all registered {@linkplain ASTProber probers} to the AST. */
+    protected void probeAST(RootNode rootNode) {
+        INSTRUMENT.probeAST(rootNode);
+    }
+
     protected void dispose(TruffleLanguage<?> impl, Env env) {
         API.dispose(impl, env);
     }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,22 +24,20 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
 
 /**
- * Enables instrumentation by attaching {@linkplain Probe Probes} to some nodes in a (newly created,
- * not yet executed) AST.
+ * Enables {@linkplain Instrumenter Instrumentation} by attaching {@linkplain Probe Probes} to some
+ * nodes in a (newly created, not yet executed) {@link RootNode}.
  *
- * @see Probe
- * @see Probe#addProbeListener(ProbeListener)
+ * @see Instrumenter#probe(com.oracle.truffle.api.nodes.Node)
  */
 public interface ASTProber {
 
     /**
-     * Walk the AST starting at a node and enable instrumentation at selected nodes by attaching
-     * {@linkplain Probe Probes} to them. Ignore {@linkplain Node#isInstrumentable()
-     * non-instrumentable} nodes.
+     * Walk the root's AST and enable instrumentation at selected nodes by attaching
+     * {@linkplain Probe Probes} to them.
      */
-    void probeAST(Node node);
+    void probeAST(Instrumenter instrumenter, RootNode rootNode);
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentResultListener.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentResultListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -31,10 +31,13 @@
 /**
  * Listener for receiving the result a client-provided {@linkplain AdvancedInstrumentRoot AST
  * fragment}, when executed by a
- * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+ * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
  * Advanced Instrument}.
+ * <p>
+ * Notification is fully synchronous, so overrides have performance implications. Non-trivial
+ * methods should be coded with Truffle guidelines and cautions in mind.
  *
- * @see Instrument
+ * @see ProbeInstrument
  * @see AdvancedInstrumentRoot
  * @see AdvancedInstrumentRootFactory
  */
@@ -43,7 +46,7 @@
     /**
      * Notifies listener that a client-provided {@linkplain AdvancedInstrumentRoot AST fragment} has
      * been executed by an
-     * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+     * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
      * Advanced Instrument} with the specified result, possibly {@code null}.
      * <p>
      * <strong>Note: </strong> Truffle will attempt to optimize implementations through partial
@@ -54,7 +57,7 @@
      * @param vFrame execution frame at the guest-language AST node
      * @param result the result of this AST fragment's execution
      */
-    void notifyResult(Node node, VirtualFrame vFrame, Object result);
+    void onExecution(Node node, VirtualFrame vFrame, Object result);
 
     /**
      * Notifies listener that execution of client-provided {@linkplain AdvancedInstrumentRoot AST
@@ -69,6 +72,6 @@
      * @param vFrame execution frame at the guest-language AST node
      * @param ex the exception
      */
-    void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex);
+    void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex);
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRoot.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRoot.java	Wed Sep 30 16:33:56 2015 -0700
@@ -30,17 +30,17 @@
 /**
  * Root of a client-provided AST fragment that can be executed efficiently, subject to full Truffle
  * optimization, by an
- * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+ * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
  * Advanced Instrument}.
  *
- * @see Instrument
+ * @see ProbeInstrument
  * @see AdvancedInstrumentRootFactory
  * @see AdvancedInstrumentResultListener
  */
 public abstract class AdvancedInstrumentRoot extends Node implements InstrumentationNode {
 
     /**
-     * Executes this AST fragment on behalf of a client {@link Instrument}, just before the
+     * Executes this AST fragment on behalf of a client {@link ProbeInstrument}, just before the
      * guest-language AST node to which the {@link Probe} holding the Instrument is executed.
      *
      * @param node the guest-language AST node to which the host Instrument's Probe is attached
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRootFactory.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRootFactory.java	Wed Sep 30 16:33:56 2015 -0700
@@ -29,17 +29,18 @@
 /**
  * Creator of {@linkplain AdvancedInstrumentRoot AST fragments} suitable for efficient execution,
  * subject to full Truffle optimization, by an
- * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
+ * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)
  * Advanced Instrument}.
  *
- * @see Instrument
+ * @see ProbeInstrument
  * @see AdvancedInstrumentRoot
  */
 public interface AdvancedInstrumentRootFactory {
 
     /**
      * Provider of {@linkplain AdvancedInstrumentRoot AST fragment} instances to be executed by the
-     * Instrumentation Framework at a {@linkplain Probe Probed} site in a guest-language AST.
+     * {@linkplain Instrumenter Instrumentation Framework} at a {@linkplain Probe Probed} site in a
+     * guest-language AST.
      * <p>
      * <strong>Notes:</strong>
      * <ul>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/EventHandlerNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.instrument;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+
+/**
+ * An Instrumentation-managed {@link Node} that synchronously propagates notification of AST
+ * Execution Events through the {@linkplain Instrumenter Instrumentation Framework} .
+ */
+public abstract class EventHandlerNode extends Node implements InstrumentationNode {
+
+    protected EventHandlerNode() {
+    }
+
+    /**
+     * An AST node's execute method is about to be called.
+     */
+    public abstract void enter(Node node, VirtualFrame vFrame);
+
+    /**
+     * An AST Node's {@code void}-valued execute method has just returned.
+     */
+    public abstract void returnVoid(Node node, VirtualFrame vFrame);
+
+    /**
+     * An AST Node's execute method has just returned a value (boxed if primitive).
+     */
+    public abstract void returnValue(Node node, VirtualFrame vFrame, Object result);
+
+    /**
+     * An AST Node's execute method has just thrown an exception.
+     */
+    public abstract void returnExceptional(Node node, VirtualFrame vFrame, Exception exception);
+
+    /**
+     * Gets the {@link Probe} that manages this chain of event handling.
+     */
+    public abstract Probe getProbe();
+
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,627 +24,34 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import com.oracle.truffle.api.CompilerDirectives;
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.NodeCost;
-import com.oracle.truffle.api.nodes.NodeInfo;
-import com.oracle.truffle.api.source.SourceSection;
-
-// TODO (mlvdv) these statics should not be global.  Move them to some kind of context.
-// TODO (mlvdv) migrate factory (together with Probe)? break out nested classes?
-
 /**
  * A <em>binding</em> between:
  * <ol>
- * <li>A {@link Probe}: a source of <em>execution events</em> taking place at a program location in
- * an executing Truffle AST, and</li>
+ * <li>Some source of <em>execution events</em> in an executing Truffle AST, and</li>
  * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client.
  * </ol>
  * <p>
  * Client-oriented documentation for the use of Instruments is available online at <a
  * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https://
  * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a>
- * <p>
- * The implementation of Instruments is complicated by the requirement that Truffle be able to clone
- * ASTs at any time. In particular, any instrumentation-supporting Nodes that have been attached to
- * an AST must be cloned along with the AST: AST clones are not permitted to share Nodes.
- * <p>
- * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is encouraged
- * by providing the {@link SimpleInstrumentListener} for clients that need know nothing more than
- * the properties associated with a Probe: it's {@link SourceSection} and any associated instances
- * of {@link SyntaxTag}.
- * <p>
- * AST cloning is <em>not transparent</em> to clients that use the
- * {@link StandardInstrumentListener}, since those event methods identify the concrete Node instance
- * (and thus the AST instance) where the event takes place.
- * <p>
- * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4>
- * <p>
- * <ul>
- * <li>A new Instrument is created in permanent association with a client-provided
- * <em>listener.</em></li>
  *
- * <li>Multiple Instruments may share a single listener.</li>
- *
- * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to a
- * Probe, at which time the Instrument begins routing execution events from the Probe's AST location
- * to the Instrument's listener.</li>
- *
- * <li>Neither Instruments nor Probes are {@link Node}s.</li>
- *
- * <li>A Probe has a single source-based location in an AST, but manages a separate
- * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li>
- * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is cloned
- * along with the rest of the AST.</li>
- *
- * <li>When a new Instrument (for example an instance created by
- * {@link Instrument#create(com.oracle.truffle.api.instrument.SimpleInstrumentListener, java.lang.String)}
- * is attached to a Probe, the Instrument inserts a new instance of its private Node type into
- * <em>each of the instrument chains</em> managed by the Probe, i.e. one node instance per existing
- * clone of the AST.</li>
- *
- * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the
- * Instrument's private Node type will be cloned along with the rest of the the AST.</li>
- * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the
- * shared (outer) Instrument instance; that state includes a reference to the Instrument's listener.
- * </li>
- *
- * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, the
- * Instrument searches every instrument chain associated with the Probe and removes the instance of
- * its private Node type.</li>
- *
- * <li>Attaching and disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of the
- * AST.</li>
- *
- * </ul>
- *
- * @see Probe
- * @see TruffleEvents
+ * @see Instrumenter
  */
 public abstract class Instrument {
 
-    /**
-     * Creates a <em>Simple Instrument</em>: this Instrument routes execution events to a
-     * client-provided listener.
-     *
-     * @param listener a listener for execution events
-     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
-     * @return a new instrument, ready for attachment at a probe
-     */
-    public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) {
-        return new SimpleInstrument(listener, instrumentInfo);
-    }
-
-    /**
-     * Creates a <em>Standard Instrument</em>: this Instrument routes execution events, together
-     * with access to Truffle execution state, to a client-provided listener.
-     *
-     * @param standardListener a listener for execution events and execution state
-     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
-     * @return a new instrument, ready for attachment at a probe
-     */
-    public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) {
-        return new StandardInstrument(standardListener, instrumentInfo);
-    }
-
-    /**
-     * Creates an <em>Advanced Instrument</em>: this Instrument executes efficiently, subject to
-     * full Truffle optimization, a client-provided AST fragment every time the Probed node is
-     * entered.
-     * <p>
-     * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework
-     * and reported to the listener; there is no other notification.
-     *
-     * @param resultListener optional client callback for results/failure notification
-     * @param rootFactory provider of AST fragments on behalf of the client
-     * @param requiredResultType optional requirement, any non-assignable result is reported to the
-     *            the listener, if any, as a failure
-     * @param instrumentInfo optional description of the instrument's role, intended for debugging.
-     * @return a new instrument, ready for attachment at a probe
-     */
-    public static Instrument create(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
-        return new AdvancedInstrument(resultListener, rootFactory, requiredResultType, instrumentInfo);
-    }
-
-    // TODO (mlvdv) experimental
-    /**
-     * For implementation testing.
-     */
-    public static Instrument create(TruffleOptListener listener) {
-        return new TruffleOptInstrument(listener, null);
-    }
-
-    /**
-     * Has this instrument been disposed? stays true once set.
-     */
-    private boolean isDisposed = false;
-
-    protected Probe probe = null;
-
-    /**
-     * Optional documentation, mainly for debugging.
-     */
-    private final String instrumentInfo;
-
-    private Instrument(String instrumentInfo) {
-        this.instrumentInfo = instrumentInfo;
-    }
-
-    /**
-     * Gets the {@link Probe} to which this Instrument is currently attached: {@code null} if not
-     * yet attached to a Probe or if this Instrument has been {@linkplain #dispose() disposed}.
-     */
-    public Probe getProbe() {
-        return probe;
-    }
-
-    /**
-     * Removes this Instrument from the Probe to which it attached and renders this Instrument
-     * inert.
-     *
-     * @throws IllegalStateException if this instrument has already been disposed
-     */
-    public void dispose() throws IllegalStateException {
-        if (isDisposed) {
-            throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt");
-        }
-        if (probe != null) {
-            // It's attached
-            probe.disposeInstrument(this);
-            probe = null;
-        }
-        this.isDisposed = true;
-    }
-
-    /**
-     * For internal implementation only.
-     */
-    void setAttachedTo(Probe probe) {
-        this.probe = probe;
-    }
-
-    /**
-     * Has this instrument been disposed and rendered unusable?
-     */
-    boolean isDisposed() {
-        return isDisposed;
-    }
-
-    abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode);
-
-    /**
-     * Removes this instrument from an instrument chain.
-     */
-    abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode);
-
-    /**
-     * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}.
-     */
-    private static final class SimpleInstrument extends Instrument {
-
-        /**
-         * Tool-supplied listener for events.
-         */
-        private final SimpleInstrumentListener simpleListener;
-
-        private SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) {
-            super(instrumentInfo);
-            this.simpleListener = simpleListener;
-        }
-
-        @Override
-        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
-            return new SimpleInstrumentNode(nextNode);
-        }
-
-        @Override
-        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
-            boolean found = false;
-            if (instrumentNode != null) {
-                if (instrumentNode.getInstrument() == this) {
-                    // Found the match at the head of the chain
-                    return instrumentNode.nextInstrumentNode;
-                }
-                // Match not at the head of the chain; remove it.
-                found = instrumentNode.removeFromChain(SimpleInstrument.this);
-            }
-            if (!found) {
-                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
-            }
-            return instrumentNode;
-        }
-
-        /**
-         * Node that implements a {@link SimpleInstrument} in a particular AST.
-         */
-        @NodeInfo(cost = NodeCost.NONE)
-        private final class SimpleInstrumentNode extends AbstractInstrumentNode {
-
-            private SimpleInstrumentNode(AbstractInstrumentNode nextNode) {
-                super(nextNode);
-            }
-
-            public void enter(Node node, VirtualFrame vFrame) {
-                SimpleInstrument.this.simpleListener.enter(SimpleInstrument.this.probe);
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.enter(node, vFrame);
-                }
-            }
-
-            public void returnVoid(Node node, VirtualFrame vFrame) {
-                SimpleInstrument.this.simpleListener.returnVoid(SimpleInstrument.this.probe);
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnVoid(node, vFrame);
-                }
-            }
-
-            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                SimpleInstrument.this.simpleListener.returnValue(SimpleInstrument.this.probe, result);
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnValue(node, vFrame, result);
-                }
-            }
-
-            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                SimpleInstrument.this.simpleListener.returnExceptional(SimpleInstrument.this.probe, exception);
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
-                }
-            }
-
-            public String instrumentationInfo() {
-                final String info = getInstrumentInfo();
-                return info != null ? info : simpleListener.getClass().getSimpleName();
-            }
-        }
+    protected Instrument() {
     }
 
     /**
-     * An instrument that propagates events to an instance of {@link StandardInstrumentListener}.
+     * Detaches this from its source of execution events and renders itself unusable.
+     *
+     * @throws IllegalStateException if this has already been disposed
      */
-    private static final class StandardInstrument extends Instrument {
-
-        /**
-         * Tool-supplied listener for AST events.
-         */
-        private final StandardInstrumentListener standardListener;
-
-        private StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) {
-            super(instrumentInfo);
-            this.standardListener = standardListener;
-        }
-
-        @Override
-        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
-            return new StandardInstrumentNode(nextNode);
-        }
-
-        @Override
-        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
-            boolean found = false;
-            if (instrumentNode != null) {
-                if (instrumentNode.getInstrument() == this) {
-                    // Found the match at the head of the chain
-                    return instrumentNode.nextInstrumentNode;
-                }
-                // Match not at the head of the chain; remove it.
-                found = instrumentNode.removeFromChain(StandardInstrument.this);
-            }
-            if (!found) {
-                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
-            }
-            return instrumentNode;
-        }
-
-        /**
-         * Node that implements a {@link StandardInstrument} in a particular AST.
-         */
-        @NodeInfo(cost = NodeCost.NONE)
-        private final class StandardInstrumentNode extends AbstractInstrumentNode {
-
-            private StandardInstrumentNode(AbstractInstrumentNode nextNode) {
-                super(nextNode);
-            }
-
-            public void enter(Node node, VirtualFrame vFrame) {
-                standardListener.enter(StandardInstrument.this.probe, node, vFrame);
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.enter(node, vFrame);
-                }
-            }
-
-            public void returnVoid(Node node, VirtualFrame vFrame) {
-                standardListener.returnVoid(StandardInstrument.this.probe, node, vFrame);
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnVoid(node, vFrame);
-                }
-            }
-
-            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                standardListener.returnValue(StandardInstrument.this.probe, node, vFrame, result);
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnValue(node, vFrame, result);
-                }
-            }
-
-            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                standardListener.returnExceptional(StandardInstrument.this.probe, node, vFrame, exception);
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
-                }
-            }
-
-            public String instrumentationInfo() {
-                final String info = getInstrumentInfo();
-                return info != null ? info : standardListener.getClass().getSimpleName();
-            }
-        }
-    }
+    public abstract void dispose() throws IllegalStateException;
 
     /**
-     * An instrument that allows clients to provide an AST fragment to be executed directly from
-     * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle
-     * AST with potential for full optimization.
+     * Has this been detached from its source of execution events.
      */
-    private static final class AdvancedInstrument extends Instrument {
-
-        private final AdvancedInstrumentResultListener resultListener;
-        private final AdvancedInstrumentRootFactory rootFactory;
-        private final Class<?> requiredResultType;
-
-        private AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
-            super(instrumentInfo);
-            this.resultListener = resultListener;
-            this.rootFactory = rootFactory;
-            this.requiredResultType = requiredResultType;
-        }
-
-        @Override
-        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
-            return new AdvancedInstrumentNode(nextNode);
-        }
-
-        @Override
-        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
-            boolean found = false;
-            if (instrumentNode != null) {
-                if (instrumentNode.getInstrument() == this) {
-                    // Found the match at the head of the chain
-                    return instrumentNode.nextInstrumentNode;
-                }
-                // Match not at the head of the chain; remove it.
-                found = instrumentNode.removeFromChain(AdvancedInstrument.this);
-            }
-            if (!found) {
-                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
-            }
-            return instrumentNode;
-        }
-
-        /**
-         * Node that implements a {@link AdvancedInstrument} in a particular AST.
-         */
-        @NodeInfo(cost = NodeCost.NONE)
-        private final class AdvancedInstrumentNode extends AbstractInstrumentNode {
-
-            @Child private AdvancedInstrumentRoot instrumentRoot;
-
-            private AdvancedInstrumentNode(AbstractInstrumentNode nextNode) {
-                super(nextNode);
-            }
-
-            public void enter(Node node, VirtualFrame vFrame) {
-                if (instrumentRoot == null) {
-                    try {
-                        final AdvancedInstrumentRoot newInstrumentRoot = AdvancedInstrument.this.rootFactory.createInstrumentRoot(AdvancedInstrument.this.probe, node);
-                        if (newInstrumentRoot != null) {
-                            instrumentRoot = newInstrumentRoot;
-                            adoptChildren();
-                            AdvancedInstrument.this.probe.invalidateProbeUnchanged();
-                        }
-                    } catch (RuntimeException ex) {
-                        if (resultListener != null) {
-                            resultListener.notifyFailure(node, vFrame, ex);
-                        }
-                    }
-                }
-                if (instrumentRoot != null) {
-                    try {
-                        final Object result = instrumentRoot.executeRoot(node, vFrame);
-                        if (resultListener != null) {
-                            checkResultType(result);
-                            resultListener.notifyResult(node, vFrame, result);
-                        }
-                    } catch (RuntimeException ex) {
-                        if (resultListener != null) {
-                            resultListener.notifyFailure(node, vFrame, ex);
-                        }
-                    }
-                }
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.enter(node, vFrame);
-                }
-            }
-
-            private void checkResultType(Object result) {
-                if (requiredResultType == null) {
-                    return;
-                }
-                if (result == null) {
-                    throw instrumentResultNull();
-                }
-                if (!(requiredResultType.isAssignableFrom(result.getClass()))) {
-                    throw instrumentResultWrongType(result);
-                }
-            }
-
-            @TruffleBoundary
-            private RuntimeException instrumentResultNull() {
-                return new RuntimeException("Instrument result null: " + requiredResultType.getSimpleName() + " is required");
-            }
-
-            @TruffleBoundary
-            private RuntimeException instrumentResultWrongType(Object result) {
-                return new RuntimeException("Instrument result " + result.toString() + " not assignable to " + requiredResultType.getSimpleName());
-            }
-
-            public void returnVoid(Node node, VirtualFrame vFrame) {
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnVoid(node, vFrame);
-                }
-            }
-
-            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnValue(node, vFrame, result);
-                }
-            }
-
-            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
-                }
-            }
-
-            public String instrumentationInfo() {
-                final String info = getInstrumentInfo();
-                return info != null ? info : rootFactory.getClass().getSimpleName();
-            }
-        }
-    }
+    public abstract boolean isDisposed();
 
-    public interface TruffleOptListener {
-        void notifyIsCompiled(boolean isCompiled);
-    }
-
-    private static final class TruffleOptInstrument extends Instrument {
-
-        private final TruffleOptListener toolOptListener;
-
-        private TruffleOptInstrument(TruffleOptListener listener, String instrumentInfo) {
-            super(instrumentInfo);
-            this.toolOptListener = listener;
-        }
-
-        @Override
-        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
-            return new TruffleOptInstrumentNode(nextNode);
-        }
-
-        @Override
-        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
-            boolean found = false;
-            if (instrumentNode != null) {
-                if (instrumentNode.getInstrument() == this) {
-                    // Found the match at the head of the chain
-                    return instrumentNode.nextInstrumentNode;
-                }
-                // Match not at the head of the chain; remove it.
-                found = instrumentNode.removeFromChain(TruffleOptInstrument.this);
-            }
-            if (!found) {
-                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
-            }
-            return instrumentNode;
-        }
-
-        @NodeInfo(cost = NodeCost.NONE)
-        private final class TruffleOptInstrumentNode extends AbstractInstrumentNode {
-
-            private boolean isCompiled;
-
-            private TruffleOptInstrumentNode(AbstractInstrumentNode nextNode) {
-                super(nextNode);
-                this.isCompiled = CompilerDirectives.inCompiledCode();
-            }
-
-            public void enter(Node node, VirtualFrame vFrame) {
-                if (this.isCompiled != CompilerDirectives.inCompiledCode()) {
-                    this.isCompiled = CompilerDirectives.inCompiledCode();
-                    TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled);
-                }
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.enter(node, vFrame);
-                }
-            }
-
-            public void returnVoid(Node node, VirtualFrame vFrame) {
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnVoid(node, vFrame);
-                }
-            }
-
-            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnValue(node, vFrame, result);
-                }
-            }
-
-            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-                if (nextInstrumentNode != null) {
-                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
-                }
-            }
-
-            public String instrumentationInfo() {
-                final String info = getInstrumentInfo();
-                return info != null ? info : toolOptListener.getClass().getSimpleName();
-            }
-        }
-    }
-
-    @NodeInfo(cost = NodeCost.NONE)
-    abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode {
-
-        @Child protected AbstractInstrumentNode nextInstrumentNode;
-
-        protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) {
-            this.nextInstrumentNode = nextNode;
-        }
-
-        @Override
-        public boolean isInstrumentable() {
-            return false;
-        }
-
-        /**
-         * Gets the instrument that created this node.
-         */
-        private Instrument getInstrument() {
-            return Instrument.this;
-        }
-
-        /**
-         * Removes the node from this chain that was added by a particular instrument, assuming that
-         * the head of the chain is not the one to be replaced. This is awkward, but is required
-         * because {@link Node#replace(Node)} won't take a {@code null} argument. This doesn't work
-         * for the tail of the list, which would be replacing itself with null. So the replacement
-         * must be directed the parent of the node being removed.
-         */
-        private boolean removeFromChain(Instrument instrument) {
-            assert getInstrument() != instrument;
-            if (nextInstrumentNode == null) {
-                return false;
-            }
-            if (nextInstrumentNode.getInstrument() == instrument) {
-                // Next is the one to remove
-                if (nextInstrumentNode.nextInstrumentNode == null) {
-                    // Next is at the tail; just forget
-                    nextInstrumentNode = null;
-                } else {
-                    // Replace next with its successor
-                    nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode);
-                }
-                return true;
-            }
-            return nextInstrumentNode.removeFromChain(instrument);
-        }
-
-        protected String getInstrumentInfo() {
-            return Instrument.this.instrumentInfo;
-        }
-    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationException.java	Tue Sep 29 18:04:11 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.instrument;
-
-public class InstrumentationException extends Exception {
-
-    public InstrumentationException(RuntimeException ex) {
-        super(ex);
-    }
-
-    private static final long serialVersionUID = 447857066220935502L;
-
-}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,13 +24,13 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.nodes.Node;
 
 /**
- * A marker interface for Truffle {@linkplain Node nodes} that internally implement the
- * <em>Instrumentation Framework</em>. Such nodes should not be part of any Guest Language execution
- * semantics, and should in general not be visible to ordinary Instrumentation clients.
+ * A marker interface for the Truffle {@linkplain Node Nodes} that internally implement the
+ * {@linkplain Instrumenter Instrumentation Framework} . Such nodes should not be part of any Guest
+ * Language execution semantics, and should in general not be visible to ordinary Instrumentation
+ * clients.
  */
 public interface InstrumentationNode {
 
@@ -39,30 +39,4 @@
      */
     String instrumentationInfo();
 
-    /**
-     * Events that propagate through the {@linkplain InstrumentationNode implementation nodes} of
-     * the Instrumentation Framework, not visible in this form to Instrumentation clients.
-     */
-    interface TruffleEvents {
-
-        /**
-         * An AST node's execute method is about to be called.
-         */
-        void enter(Node node, VirtualFrame vFrame);
-
-        /**
-         * An AST Node's {@code void}-valued execute method has just returned.
-         */
-        void returnVoid(Node node, VirtualFrame vFrame);
-
-        /**
-         * An AST Node's execute method has just returned a value (boxed if primitive).
-         */
-        void returnValue(Node node, VirtualFrame vFrame, Object result);
-
-        /**
-         * An AST Node's execute method has just thrown an exception.
-         */
-        void returnExceptional(Node node, VirtualFrame vFrame, Exception exception);
-    }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java	Tue Sep 29 18:04:11 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.instrument;
-
-/**
- * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language
- * program execution.
- * <p>
- * Tools share a common <em>life cycle</em>:
- * <ul>
- * <li>A newly created tool is inert until {@linkplain #install() installed}.</li>
- * <li>An installed tool becomes <em>enabled</em> and immediately begins installing
- * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from
- * those instruments</li>
- * <li>A tool may only be installed once.</li>
- * <li>It should be possible to install multiple instances of a tool, possibly (but not necessarily)
- * configured differently with respect to what data is being collected.</li>
- * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) enabled and disabled}
- * arbitrarily.</li>
- * <li>A disabled tool:
- * <ul>
- * <li>Collects no data;</li>
- * <li>Retains existing AST instrumentation;</li>
- * <li>Continues to instrument newly created ASTs; and</li>
- * <li>Retains previously collected data.</li>
- * </ul>
- * </li>
- * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool
- * installed but with all previously collected data removed.</li>
- * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not
- * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data persists.</li>
- * </ul>
- * <p>
- * Tool-specific methods that access data collected by the tool should:
- * <ul>
- * <li>Return modification-safe representations of the data; and</li>
- * <li>Not change the state of the data.</li>
- * </ul>
- * <b>Note:</b><br>
- * Tool installation is currently <em>global</em> to the Truffle Execution environment. When
- * language-agnostic management of individual execution environments is added to the platform,
- * installation will be (optionally) specific to a single execution environment.
- */
-public abstract class InstrumentationTool {
-    // TODO (mlvdv) still thinking about the most appropriate name for this class of tools
-
-    private enum ToolState {
-
-        /** Not yet installed, inert. */
-        UNINSTALLED,
-
-        /** Installed, collecting data. */
-        ENABLED,
-
-        /** Installed, not collecting data. */
-        DISABLED,
-
-        /** Was installed, but now removed, inactive, and no longer usable. */
-        DISPOSED;
-    }
-
-    private ToolState toolState = ToolState.UNINSTALLED;
-
-    protected InstrumentationTool() {
-    }
-
-    /**
-     * Connect the tool to some part of the Truffle runtime, and enable data collection to start.
-     * Instrumentation will only be added to subsequently created ASTs.
-     *
-     * @throws IllegalStateException if the tool has previously been installed.
-     */
-    public final void install() {
-        checkUninstalled();
-        if (internalInstall()) {
-            toolState = ToolState.ENABLED;
-        }
-    }
-
-    /**
-     * @return whether the tool is currently collecting data.
-     */
-    public final boolean isEnabled() {
-        return toolState == ToolState.ENABLED;
-    }
-
-    /**
-     * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not
-     * collecting data, but keeping data already collected).
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void setEnabled(boolean isEnabled) {
-        checkInstalled();
-        internalSetEnabled(isEnabled);
-        toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED;
-    }
-
-    /**
-     * Clears any data already collected, but otherwise does not change the state of the tool.
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void reset() {
-        checkInstalled();
-        internalReset();
-    }
-
-    /**
-     * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data already
-     * collected.
-     *
-     * @throws IllegalStateException if not yet installed or disposed.
-     */
-    public final void dispose() {
-        checkInstalled();
-        internalDispose();
-        toolState = ToolState.DISPOSED;
-    }
-
-    /**
-     * @return whether the installation succeeded.
-     */
-    protected abstract boolean internalInstall();
-
-    /**
-     * No subclass action required.
-     *
-     * @param isEnabled
-     */
-    protected void internalSetEnabled(boolean isEnabled) {
-    }
-
-    protected abstract void internalReset();
-
-    protected abstract void internalDispose();
-
-    /**
-     * Ensure that the tool is currently installed.
-     *
-     * @throws IllegalStateException
-     */
-    private void checkInstalled() throws IllegalStateException {
-        if (toolState == ToolState.UNINSTALLED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed");
-        }
-        if (toolState == ToolState.DISPOSED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed");
-        }
-    }
-
-    /**
-     * Ensure that the tool has not yet been installed.
-     *
-     * @throws IllegalStateException
-     */
-    private void checkUninstalled() {
-        if (toolState != ToolState.UNINSTALLED) {
-            throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed");
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,661 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.instrument;
+
+import java.io.PrintStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.impl.Accessor;
+import com.oracle.truffle.api.instrument.TagInstrument.AfterTagInstrument;
+import com.oracle.truffle.api.instrument.TagInstrument.BeforeTagInstrument;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+
+/**
+ * Client access to instrumentation services in a Truffle execution environment.
+ * <p>
+ * Services include:
+ * <ul>
+ * <li>A collection of {@linkplain Probe Probes}, each of which is {@linkplain #probe(Node) created}
+ * by clients in permanent association with a particular {@linkplain SourceSection source code
+ * location} in an AST executing in this environment. The Probe keeps tracks of all <em>clones</em>
+ * of the AST and ensures that any instrumentation <em>attached</em> to the Probe is put into effect
+ * in each AST clone at the {@link Node} that corresponds to the Probe's source code location.</li>
+ * <p>
+ * <li>A collection of {@linkplain ProbeListener listeners} that have registered to be notified
+ * whenever a new {@link Probe} is created in this environment and whenever a new {@link SyntaxTag}
+ * (or simply "tag") is newly added to an existing {@link Probe} in this environment.</li>
+ * <p>
+ * <li>The ability to {@linkplain #findProbesTaggedAs(SyntaxTag) enumerate} all existing
+ * {@linkplain Probe Probes} in this environment, optionally filtered to include only those to which
+ * a specific {@linkplain SyntaxTag tag} has been added.</li>
+ * <p>
+ * <li>The ability to <em>attach</em> a client-provided <em>event listener</em> to an existing
+ * {@link Probe} in this environment. The listener subsequently receives notification of execution
+ * events at the {@linkplain Node Nodes} corresponding to the Probe's {@linkplain SourceSection
+ * source code location}. The <em>attachment</em> also produces a {@link ProbeInstrument} that
+ * represents the binding, and which can be used to {@linkplain Instrument#dispose() detach} the
+ * listener from the Probe and stop event notification. A listener can be attached to any number of
+ * Probes, each time producing a new {@linkplain ProbeInstrument} that represents the binding.</li>
+ * <p>
+ * <li>The ability to <em>attach</em> a client-provided <em>event listener</em> to a specific
+ * {@linkplain SyntaxTag tag} for all {@linkplain Probe Probes} in the environment. A maximum of
+ * <em>one</em> listener may be attached to receive notification of "<em>before</em>" execution
+ * events (i.e. the flow of execution is just about to enter a {@link Node}), and a maximum of
+ * <em>one</em> listener may be attached to receive notification of "<em>after</em>" execution
+ * events. The <em>attachment</em> also produces a {@link TagInstrument} that represents the
+ * binding, and which can be used to {@linkplain Instrument#dispose() detach} the listener from the
+ * Probes and stop event notification. This mechanism is designed for much lower runtime overhead
+ * than other ways to accomplish the same thing, e.g. by attaching one listener individually to
+ * every Probe with the desired tag.</li>
+ * <p>
+ * <li>A collection of {@linkplain Tool Tools}, possibly client-provided, that can be
+ * {@linkplain #install(Tool) installed} for data collection, possibly providing their own services
+ * with the resulting information.</li>
+ * </ul>
+ */
+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));
+        }
+    }
+
+    private enum ToolState {
+
+        /** Not yet installed, inert. */
+        UNINSTALLED,
+
+        /** Installed, collecting data. */
+        ENABLED,
+
+        /** Installed, not collecting data. */
+        DISABLED,
+
+        /** Was installed, but now removed, inactive, and no longer usable. */
+        DISPOSED;
+    }
+
+    /**
+     * {@linkplain Instrumenter Instrumentation}-based collectors of data during Guest Language
+     * program execution.
+     * <p>
+     * Tools share a common <em>life cycle</em>:
+     * <ul>
+     * <li>A newly created tool is "UNINSTALLED"; it does nothing until
+     * {@linkplain Instrumenter#install(Tool) installed} .</li>
+     * <li>An installed tool becomes "ENABLED" and immediately begins attaching
+     * {@linkplain ProbeInstrument instrumentation} to ASTs and collecting execution data.</li>
+     * <li>A tool may only be installed once.</li>
+     * <li>It is possible to install multiple instances of a tool, possibly (but not necessarily)
+     * configured differently with respect to what data is being collected.</li>
+     * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) "ENABLED" and "DISABLED"}
+     * arbitrarily.</li>
+     * <li>A disabled tool:
+     * <ul>
+     * <li>Collects no data;</li>
+     * <li>Retains existing AST instrumentation;</li>
+     * <li>Continues to instrument newly created ASTs; and</li>
+     * <li>Retains previously collected data.</li>
+     * </ul>
+     * </li>
+     * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool
+     * installed but with all previously collected data removed.</li>
+     * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not
+     * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data
+     * persists.</li>
+     * </ul>
+     * <p>
+     * Tool-specific methods that access data collected by the tool should:
+     * <ul>
+     * <li>Return modification-safe representations of the data; and</li>
+     * <li>Not change the state of the data.</li>
+     * </ul>
+     */
+    public abstract static class Tool {
+        // TODO (mlvdv) still thinking about the most appropriate name for this class of tools
+
+        private ToolState toolState = ToolState.UNINSTALLED;
+
+        private Instrumenter instrumenter;
+
+        protected Tool() {
+        }
+
+        final void install(Instrumenter inst) {
+            checkUninstalled();
+            this.instrumenter = inst;
+
+            if (internalInstall()) {
+                toolState = ToolState.ENABLED;
+            }
+            instrumenter.tools.add(this);
+        }
+
+        /**
+         * @return whether the tool is currently collecting data.
+         */
+        public final boolean isEnabled() {
+            return toolState == ToolState.ENABLED;
+        }
+
+        /**
+         * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not
+         * collecting data, but keeping data already collected).
+         *
+         * @throws IllegalStateException if not yet installed or disposed.
+         */
+        public final void setEnabled(boolean isEnabled) {
+            checkInstalled();
+            internalSetEnabled(isEnabled);
+            toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED;
+        }
+
+        /**
+         * Clears any data already collected, but otherwise does not change the state of the tool.
+         *
+         * @throws IllegalStateException if not yet installed or disposed.
+         */
+        public final void reset() {
+            checkInstalled();
+            internalReset();
+        }
+
+        /**
+         * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data
+         * already collected.
+         *
+         * @throws IllegalStateException if not yet installed or disposed.
+         */
+        public final void dispose() {
+            checkInstalled();
+            internalDispose();
+            toolState = ToolState.DISPOSED;
+            instrumenter.tools.remove(this);
+        }
+
+        /**
+         * @return whether the installation succeeded.
+         */
+        protected abstract boolean internalInstall();
+
+        /**
+         * No subclass action required.
+         *
+         * @param isEnabled
+         */
+        protected void internalSetEnabled(boolean isEnabled) {
+        }
+
+        protected abstract void internalReset();
+
+        protected abstract void internalDispose();
+
+        protected final Instrumenter getInstrumenter() {
+            return instrumenter;
+        }
+
+        /**
+         * Ensure that the tool is currently installed.
+         *
+         * @throws IllegalStateException
+         */
+        private void checkInstalled() throws IllegalStateException {
+            if (toolState == ToolState.UNINSTALLED) {
+                throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed");
+            }
+            if (toolState == ToolState.DISPOSED) {
+                throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed");
+            }
+        }
+
+        /**
+         * Ensure that the tool has not yet been installed.
+         *
+         * @throws IllegalStateException
+         */
+        private void checkUninstalled() {
+            if (toolState != ToolState.UNINSTALLED) {
+                throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed");
+            }
+        }
+    }
+
+    private final Object vm;
+
+    /** Tools that have been created, but not yet disposed. */
+    Set<Tool> tools = new HashSet<>();
+
+    private final Set<ASTProber> astProbers = Collections.synchronizedSet(new LinkedHashSet<ASTProber>());
+
+    private final List<ProbeListener> probeListeners = new ArrayList<>();
+
+    /**
+     * All Probes that have been created.
+     */
+    private final List<WeakReference<Probe>> probes = new ArrayList<>();
+
+    /**
+     * A global instrument that triggers notification just before executing any Node that is Probed
+     * with a matching tag.
+     */
+    @CompilationFinal private BeforeTagInstrument beforeTagInstrument = null;
+
+    /**
+     * A global instrument that triggers notification just after executing any Node that is Probed
+     * with a matching tag.
+     */
+    @CompilationFinal private AfterTagInstrument afterTagInstrument = null;
+
+    Instrumenter(Object vm) {
+        this.vm = vm;
+    }
+
+    /**
+     * Prepares an AST node for {@linkplain ProbeInstrument instrumentation}, where the node is
+     * presumed to be part of a well-formed Truffle AST that has not yet been executed.
+     * <p>
+     * <em>Probing</em> a node is idempotent:
+     * <ul>
+     * <li>If the node has not been Probed, modifies the AST by first inserting a
+     * {@linkplain #createWrapperNode(Node) wrapper node} between the node and its parent and then
+     * returning the newly created Probe associated with the wrapper.</li>
+     * <li>If the node has been Probed, returns the Probe associated with its existing wrapper.</li>
+     * <li>No more than one {@link Probe} may be associated with a node, so a wrapper may not wrap
+     * another wrapper.</li>
+     * </ul>
+     * It is a runtime error to attempt Probing an AST node with no parent.
+     *
+     * @return a (possibly newly created) {@link Probe} associated with this node.
+     * @throws ProbeException (unchecked) when a Probe cannot be created, leaving the AST unchanged
+     */
+    @SuppressWarnings("rawtypes")
+    public Probe probe(Node node) {
+
+        final Node parent = node.getParent();
+
+        if (node instanceof WrapperNode) {
+            throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, node, null);
+        }
+
+        if (parent == null) {
+            throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, node, null);
+        }
+
+        if (parent instanceof WrapperNode) {
+            final WrapperNode wrapper = (WrapperNode) parent;
+            if (TRACE) {
+                final Probe probe = wrapper.getProbe();
+                final SourceSection sourceSection = wrapper.getChild().getSourceSection();
+                final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription();
+                trace("PROBE FOUND %s %s %s", "Probe@", location, probe.getTagsDescription());
+            }
+            return wrapper.getProbe();
+        }
+
+        if (!ACCESSOR.isInstrumentable(vm, node)) {
+            throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, node, null);
+        }
+
+        // Create a new wrapper/Probe with this node as its child.
+        final WrapperNode wrapper = createWrapperNode(node);
+
+        if (wrapper == null || !(wrapper instanceof Node)) {
+            throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, node, wrapper);
+        }
+
+        final Node wrapperNode = (Node) wrapper;
+
+        if (!node.isSafelyReplaceableBy(wrapperNode)) {
+            throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, node, wrapper);
+        }
+
+        final SourceSection sourceSection = wrapper.getChild().getSourceSection();
+        final ProbeNode probeNode = new ProbeNode();
+        Class<? extends TruffleLanguage> l = ACCESSOR.findLanguage(wrapper.getChild().getRootNode());
+        final Probe probe = new Probe(this, l, probeNode, sourceSection);
+        probes.add(new WeakReference<>(probe));
+        probeNode.probe = probe;  // package private access
+        wrapper.insertEventHandlerNode(probeNode);
+        node.replace(wrapperNode);
+        if (TRACE) {
+            final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription();
+            trace("PROBED %s %s %s", "Probe@", location, probe.getTagsDescription());
+        }
+        for (ProbeListener listener : probeListeners) {
+            listener.newProbeInserted(probe);
+        }
+        return probe;
+    }
+
+    /**
+     * Adds a {@link ProbeListener} to receive events.
+     */
+    public void addProbeListener(ProbeListener listener) {
+        assert listener != null;
+        probeListeners.add(listener);
+    }
+
+    /**
+     * Removes a {@link ProbeListener}. Ignored if listener not found.
+     */
+    public void removeProbeListener(ProbeListener listener) {
+        probeListeners.remove(listener);
+    }
+
+    /**
+     * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of
+     * probes if the specified tag is {@code null}.
+     *
+     * @return A collection of probes containing the given tag.
+     */
+    public Collection<Probe> findProbesTaggedAs(SyntaxTag tag) {
+        final List<Probe> taggedProbes = new ArrayList<>();
+        for (WeakReference<Probe> ref : probes) {
+            Probe probe = ref.get();
+            if (probe != null) {
+                if (tag == null || probe.isTaggedAs(tag)) {
+                    taggedProbes.add(ref.get());
+                }
+            }
+        }
+        return taggedProbes;
+    }
+
+    /**
+     * Enables instrumentation at selected nodes in all subsequently constructed ASTs. Ignored if
+     * the argument is already registered, runtime error if argument is {@code null}.
+     */
+    public void registerASTProber(ASTProber prober) {
+        if (prober == null) {
+            throw new IllegalArgumentException("Register non-null ASTProbers");
+        }
+        astProbers.add(prober);
+    }
+
+    public void unregisterASTProber(ASTProber prober) {
+        astProbers.remove(prober);
+    }
+
+    /**
+     * <em>Attaches</em> a {@link SimpleInstrumentListener listener} to a {@link Probe}, creating a
+     * <em>binding</em> called an {@link ProbeInstrument}. Until the Instrument is
+     * {@linkplain ProbeInstrument#dispose() disposed}, it routes synchronous notification of
+     * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to
+     * the listener.
+     *
+     * @param probe source of AST execution events
+     * @param listener receiver of execution events
+     * @param instrumentInfo optional documentation about the Instrument
+     * @return a handle for access to the binding
+     */
+    @SuppressWarnings("static-method")
+    public ProbeInstrument attach(Probe probe, SimpleInstrumentListener listener, String instrumentInfo) {
+        final ProbeInstrument instrument = new ProbeInstrument.SimpleInstrument(listener, instrumentInfo);
+        probe.attach(instrument);
+        return instrument;
+    }
+
+    /**
+     * <em>Attaches</em> a {@link StandardInstrumentListener listener} to a {@link Probe}, creating
+     * a <em>binding</em> called an {@link ProbeInstrument}. Until the Instrument is
+     * {@linkplain ProbeInstrument#dispose() disposed}, it routes synchronous notification of
+     * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to
+     * the listener.
+     *
+     * @param probe source of AST execution events
+     * @param listener receiver of execution events
+     * @param instrumentInfo optional documentation about the Instrument
+     * @return a handle for access to the binding
+     */
+    @SuppressWarnings("static-method")
+    public ProbeInstrument attach(Probe probe, StandardInstrumentListener listener, String instrumentInfo) {
+        final ProbeInstrument instrument = new ProbeInstrument.StandardInstrument(listener, instrumentInfo);
+        probe.attach(instrument);
+        return instrument;
+    }
+
+    /**
+     * <em>Attaches</em> a {@link AdvancedInstrumentResultListener listener} to a {@link Probe},
+     * creating a <em>binding</em> called an {@link ProbeInstrument}. Until the Instrument is
+     * {@linkplain ProbeInstrument#dispose() disposed}, it routes synchronous notification of
+     * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to
+     * the listener.
+     * <p>
+     * This Instrument executes efficiently, subject to full Truffle optimization, a client-provided
+     * AST fragment every time the Probed node is entered.
+     * <p>
+     * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework
+     * and reported to the listener; there is no other notification.
+     *
+     * @param probe source of AST execution events
+     * @param listener optional client callback for results/failure notification
+     * @param rootFactory provider of AST fragments on behalf of the client
+     * @param requiredResultType optional requirement, any non-assignable result is reported to the
+     *            the listener, if any, as a failure
+     * @param instrumentInfo instrumentInfo optional documentation about the Instrument
+     * @return a handle for access to the binding
+     */
+    @SuppressWarnings("static-method")
+    public ProbeInstrument attach(Probe probe, AdvancedInstrumentResultListener listener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
+        final ProbeInstrument instrument = new ProbeInstrument.AdvancedInstrument(listener, rootFactory, requiredResultType, instrumentInfo);
+        probe.attach(instrument);
+        return instrument;
+    }
+
+    // TODO (mlvdv) allow multiple <em>before</em> instruments without performance hit?
+    /**
+     * Sets the current "<em>before</em>" TagInstrument; there can be no more than one in effect.
+     * <ul>
+     * <li>The Instrument 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>Calling {@link TagInstrument#dispose()} removes the instrument.</li>
+     * </ul>
+     *
+     * @param tag identifies the nodes to be instrumented
+     * @param listener receiver of <em>before</em> execution events
+     * @param instrumentInfo optional, mainly for debugging.
+     * @return a newly created, active Instrument
+     * @throws IllegalStateException if called when a <em>before</em> Instrument is active.
+     */
+    public TagInstrument attach(SyntaxTag tag, StandardBeforeInstrumentListener listener, String instrumentInfo) {
+        if (beforeTagInstrument != null) {
+            throw new IllegalStateException("Only one 'before' TagInstrument at a time");
+        }
+        this.beforeTagInstrument = new TagInstrument.BeforeTagInstrument(this, tag, listener, instrumentInfo);
+        notifyTagInstrumentChange();
+        return beforeTagInstrument;
+    }
+
+    // TODO (mlvdv) allow multiple <em>after</em> instruments without performance hit?
+    /**
+     * Sets the current "<em>after</em>" TagInstrument; there can be no more than one in effect.
+     * <ul>
+     * <li>The Instrument triggers a callback just <strong><em>after</em></strong> execution reaches
+     * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with
+     * the specified {@link SyntaxTag}.</li>
+     * <li>Calling {@link TagInstrument#dispose()} removes the instrument.</li>
+     * </ul>
+     *
+     * @param tag identifies the nodes to be instrumented
+     * @param listener receiver of <em>after</em> execution events
+     * @param instrumentInfo optional, mainly for debugging.
+     * @return a newly created, active Instrument
+     * @throws IllegalStateException if called when a <em>after</em> Instrument is active.
+     */
+    public TagInstrument attach(SyntaxTag tag, StandardAfterInstrumentListener listener, String instrumentInfo) {
+        if (afterTagInstrument != null) {
+            throw new IllegalStateException("Only one 'afater' TagInstrument at a time");
+        }
+        this.afterTagInstrument = new TagInstrument.AfterTagInstrument(this, tag, listener, instrumentInfo);
+        notifyTagInstrumentChange();
+        return afterTagInstrument;
+    }
+
+    /**
+     * Connects the tool to some part of the Truffle runtime, and enable data collection to start.
+     *
+     * @return the tool
+     * @throws IllegalStateException if the tool has previously been installed or has been disposed.
+     */
+    public Tool install(Tool tool) {
+        tool.install(this);
+        return tool;
+    }
+
+    @SuppressWarnings("unused")
+    void executionStarted(Source s) {
+    }
+
+    void executionEnded() {
+    }
+
+    WrapperNode createWrapperNode(Node node) {
+        return ACCESSOR.createWrapperNode(vm, node);
+    }
+
+    void tagAdded(Probe probe, SyntaxTag tag, Object tagValue) {
+        for (ProbeListener listener : probeListeners) {
+            listener.probeTaggedAs(probe, tag, tagValue);
+        }
+    }
+
+    BeforeTagInstrument getBeforeTagInstrument() {
+        return beforeTagInstrument;
+    }
+
+    AfterTagInstrument getAfterTagInstrument() {
+        return afterTagInstrument;
+    }
+
+    void disposeBeforeTagInstrument() {
+        beforeTagInstrument = null;
+        notifyTagInstrumentChange();
+    }
+
+    void disposeAfterTagInstrument() {
+        afterTagInstrument = null;
+        notifyTagInstrumentChange();
+    }
+
+    private void notifyTagInstrumentChange() {
+        for (WeakReference<Probe> ref : probes) {
+            final Probe probe = ref.get();
+            if (probe != null) {
+                probe.notifyTagInstrumentsChanged();
+            }
+        }
+    }
+
+    /**
+     * Enables instrumentation in a newly created AST by applying all registered instances of
+     * {@link ASTProber}.
+     */
+    private void probeAST(RootNode rootNode) {
+        if (!astProbers.isEmpty()) {
+
+            String name = "<?>";
+            final SourceSection sourceSection = rootNode.getSourceSection();
+            if (sourceSection != null) {
+                final Source source = sourceSection.getSource();
+                if (source != null) {
+                    name = source.getShortName();
+                } else {
+                    name = sourceSection.getShortDescription();
+                }
+            }
+            trace("START %s", name);
+            for (ProbeListener listener : probeListeners) {
+                listener.startASTProbing(rootNode);
+            }
+            for (ASTProber prober : astProbers) {
+                prober.probeAST(this, rootNode);
+            }
+            for (ProbeListener listener : probeListeners) {
+                listener.endASTProbing(rootNode);
+            }
+            trace("FINISHED %s", name);
+        }
+    }
+
+    static final class AccessorInstrument extends Accessor {
+
+        @Override
+        protected Instrumenter createInstrumenter(Object vm) {
+            return new Instrumenter(vm);
+        }
+
+        @Override
+        protected boolean isInstrumentable(Object vm, Node node) {
+            return super.isInstrumentable(vm, node);
+        }
+
+        @Override
+        public WrapperNode createWrapperNode(Object vm, Node node) {
+            return super.createWrapperNode(vm, node);
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected Class<? extends TruffleLanguage> findLanguage(RootNode n) {
+            return super.findLanguage(n);
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected Class<? extends TruffleLanguage> findLanguage(Probe probe) {
+            return probe.getLanguage();
+        }
+
+        @Override
+        protected void probeAST(RootNode rootNode) {
+            // Normally null vm argument; can be reflectively set for testing
+            super.getInstrumenter(testVM).probeAST(rootNode);
+        }
+    }
+
+    static final AccessorInstrument ACCESSOR = new AccessorInstrument();
+
+    // Normally null; set for testing where the Accessor hasn't been fully initialized
+    private static Object testVM = null;
+
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,18 +24,6 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import com.oracle.truffle.api.Assumption;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.impl.Accessor;
-import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
-import com.oracle.truffle.api.nodes.InvalidAssumptionException;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.NodeVisitor;
-import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.api.utilities.CyclicAssumption;
 import java.io.PrintStream;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
@@ -43,66 +31,36 @@
 import java.util.Collections;
 import java.util.List;
 
-//TODO (mlvdv) these statics should not be global.  Move them to some kind of context.
+import com.oracle.truffle.api.Assumption;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.instrument.TagInstrument.AfterTagInstrument;
+import com.oracle.truffle.api.instrument.TagInstrument.BeforeTagInstrument;
+import com.oracle.truffle.api.nodes.InvalidAssumptionException;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.api.utilities.CyclicAssumption;
 
 /**
  * A <em>binding</em> between:
  * <ol>
- * <li>A program location in an executing Truffle AST (corresponding to a {@link SourceSection}),
- * and</li>
- * <li>A dynamically managed collection of "attached" {@linkplain Instrument Instruments} that
- * receive event notifications on behalf of external clients.</li>
+ * <li>a <em>guest language program location</em> in an executing Truffle AST (corresponding to a
+ * {@link SourceSection}), and</li>
+ * <li>a dynamically managed collection of <em>attached</em> {@linkplain Instrument Instruments}
+ * that receive event notifications on behalf of external clients.</li>
  * </ol>
+ * <strong>Note</strong>:The relationship for {@link ProbeInstrument} must be with an AST
+ * <em>location</em>, not a specific {@link Node}, because ASTs are routinely <em>cloned</em> at
+ * runtime. An AST <em>location</em> is best represented as the {@link SourceSection} from which the
+ * original AST Node was created.
  * <p>
  * Client-oriented documentation for the use of Probes is available online at <a
  * HREF="https://wiki.openjdk.java.net/display/Graal/Finding+Probes" >https://wiki.openjdk.java.
  * net/display/Graal/Finding+Probes</a>
  * <p>
- * <h4>Implementation notes:</h4>
- * <p>
- * <ul>
- * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a
- * particular {@link SourceSection}, even though:
- * <ul>
- * <li>that location is represented in an AST as a {@link Node}, which might be replaced through
- * optimizations such as specialization, and</li>
- * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by multiple
- * Nodes in multiple ASTs.</li>
- * </ul>
- * </li>
  *
- * <li>The effect of the binding is to intercept {@linkplain TruffleEvents execution events}
- * arriving at the "probed" AST Node and notify each attached {@link Instrument} before execution is
- * allowed to proceed to the child and again after execution completes.</li>
- *
- * <li>The method {@link Node#probe()} creates a Probe on an AST Node; redundant calls return the
- * same Probe.</li>
- *
- * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers
- * correctly assigned), but before any cloning or executions. This is done by creating an instance
- * of {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}. Once
- * registered, it will be applied automatically to every newly created AST.</li>
- *
- * <li>The "probing" of an AST Node is implemented by insertion of a {@link ProbeNode.WrapperNode}
- * into the AST (as new parent of the Node being probed), together with an associated
- * {@link ProbeNode} that routes execution events at the probed Node to all the
- * {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li>
- *
- * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well,
- * together with their attached instrument chains. Each Probe instance intercepts cloning events and
- * keeps track of all AST copies.</li>
- *
- * <li>All attached {@link InstrumentationNode}s effectively become part of the running program:
- * <ul>
- * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle optimization.</li>
- * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with any
- * Truffle optimizations.</li>
- * </ul>
- * </li>
- *
- * </ul>
- *
- * @see Instrument
+ * @see Instrumenter
+ * @see ProbeInstrument
  * @see ASTProber
  * @see ProbeListener
  * @see SyntaxTag
@@ -121,173 +79,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<>();
@@ -305,45 +97,67 @@
     @CompilationFinal private Assumption probeStateUnchangedAssumption = probeStateUnchangedCyclic.getAssumption();
 
     // Must invalidate whenever changed
-    @CompilationFinal private boolean isBeforeTrapActive = false;
+    @CompilationFinal private boolean isBeforeTagInstrumentActive = false;
 
     // Must invalidate whenever changed
-    @CompilationFinal private boolean isAfterTrapActive = false;
+    @CompilationFinal private boolean isAfterTagInstrumentActive = false;
 
     /**
-     * Intended for use only by {@link ProbeNode}.
+     * Constructor for use only by {@link ProbeNode}.
+     * <p>
+     * <h4>Probe Implementation notes:</h4>
+     * <p>
+     * <ul>
+     * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a
+     * particular {@link SourceSection}, even though:
+     * <ul>
+     * <li>that location is represented in an AST as a {@link Node}, which might be replaced through
+     * optimizations such as specialization, and</li>
+     * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by
+     * multiple Nodes in multiple ASTs.</li>
+     * </ul>
+     * </li>
+     * <li>The effect of the binding is to intercept {@linkplain EventHandlerNode execution events}
+     * arriving at the "probed" AST Node and notify each attached {@link ProbeInstrument} before
+     * execution is allowed to proceed to the child and again after execution completes.</li>
+     *
+     * <li>The method {@link Instrumenter#probe(Node)} creates a Probe on an AST Node; redundant
+     * calls return the same Probe.</li>
+     *
+     * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent
+     * pointers correctly assigned), but before any cloning or executions. This is done by applying
+     * instances of {@link ASTProber} provided by each language implementation, combined with any
+     * instances registered by tools via {@link Instrumenter#registerASTProber(ASTProber)}. Once
+     * registered, these will be applied automatically to every newly created AST.</li>
+     *
+     * <li>The "probing" of an AST Node is implemented by insertion of a
+     * {@link ProbeNode.WrapperNode} into the AST (as new parent of the Node being probed), together
+     * with an associated {@link ProbeNode} that routes execution events at the probed Node to all
+     * the {@linkplain ProbeInstrument Instruments} attached to the Probe's
+     * <em>instrument chain</em>.</li>
+     *
+     * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well,
+     * together with their attached instrument chains. Each Probe instance intercepts cloning events
+     * and keeps track of all AST copies.</li>
+     *
+     * <li>All attached {@link InstrumentationNode}s effectively become part of the running program:
+     * <ul>
+     * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle
+     * optimization.</li>
+     * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with
+     * any Truffle optimizations.</li>
+     * </ul>
+     * </li>
+     * </ul>
      */
-    Probe(Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) {
+    Probe(Instrumenter instrumenter, Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) {
+        this.instrumenter = instrumenter;
         this.sourceSection = sourceSection;
-        probes.add(new WeakReference<>(this));
         registerProbeNodeClone(probeNode);
-        if (TRACE) {
-            final String location = this.sourceSection == null ? "<unknown>" : sourceSection.getShortDescription();
-            trace("ADDED %s %s %s", "Probe@", location, getTagsDescription());
-        }
-        for (ProbeListener listener : probeListeners) {
-            listener.newProbeInserted(this);
-        }
         this.language = l;
     }
 
     /**
-     * Is this node tagged as belonging to a particular human-sensible category of language
-     * constructs?
-     */
-    public boolean isTaggedAs(SyntaxTag tag) {
-        assert tag != null;
-        return tags.contains(tag);
-    }
-
-    /**
-     * In which user-sensible categories has this node been tagged (<em>empty set</em> if none).
-     */
-    public Collection<SyntaxTag> getSyntaxTags() {
-        return Collections.unmodifiableCollection(tags);
-    }
-
-    /**
      * Adds a {@linkplain SyntaxTag tag} to the set of tags associated with this {@link Probe};
      * {@code no-op} if already in the set.
      */
@@ -351,21 +165,21 @@
         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;
-            if (beforeTagTrap != null && tag == beforeTagTrap.getTag()) {
-                this.isBeforeTrapActive = true;
-                tagTrapsChanged = true;
+            // Update the status of this Probe with respect to global TagInstruments
+            boolean tagInstrumentsChanged = false;
+            final BeforeTagInstrument beforeTagInstrument = instrumenter.getBeforeTagInstrument();
+            if (beforeTagInstrument != null && tag == beforeTagInstrument.getTag()) {
+                this.isBeforeTagInstrumentActive = true;
+                tagInstrumentsChanged = true;
             }
-            if (afterTagTrap != null && tag == afterTagTrap.getTag()) {
-                this.isAfterTrapActive = true;
-                tagTrapsChanged = true;
+            final AfterTagInstrument afterTagInstrument = instrumenter.getAfterTagInstrument();
+            if (afterTagInstrument != null && tag == afterTagInstrument.getTag()) {
+                this.isAfterTagInstrumentActive = true;
+                tagInstrumentsChanged = true;
             }
-            if (tagTrapsChanged) {
+            if (tagInstrumentsChanged) {
                 invalidateProbeUnchanged();
             }
             if (TRACE) {
@@ -375,12 +189,29 @@
     }
 
     /**
+     * Is the <em>Probed node</em> tagged as belonging to a particular human-sensible category of
+     * language constructs?
+     */
+    public boolean isTaggedAs(SyntaxTag tag) {
+        assert tag != null;
+        return tags.contains(tag);
+    }
+
+    /**
+     * In which user-sensible categories has the <em>Probed node</em> been tagged (
+     * <em>empty set</em> if none).
+     */
+    public Collection<SyntaxTag> getSyntaxTags() {
+        return Collections.unmodifiableCollection(tags);
+    }
+
+    /**
      * Adds instrumentation at this Probe.
      *
      * @param instrument an instrument not yet attached to a probe
      * @throws IllegalStateException if the instrument has ever been attached before
      */
-    public void attach(Instrument instrument) throws IllegalStateException {
+    void attach(ProbeInstrument instrument) throws IllegalStateException {
         if (instrument.isDisposed()) {
             throw new IllegalStateException("Attempt to attach disposed instrument");
         }
@@ -398,8 +229,8 @@
     }
 
     /**
-     * Gets the {@link SourceSection} associated with the Guest Language AST node being
-     * instrumented, possibly {@code null}.
+     * Gets the {@link SourceSection} associated with the <en>Probed AST node</em>, possibly
+     * {@code null}.
      */
     public SourceSection getProbedSourceSection() {
         return sourceSection;
@@ -416,9 +247,9 @@
      *
      * @param instrument an instrument already attached
      * @throws IllegalStateException if instrument not attached at this Probe
-     * @see Instrument#dispose()
+     * @see ProbeInstrument#dispose()
      */
-    void disposeInstrument(Instrument instrument) throws IllegalStateException {
+    void disposeInstrument(ProbeInstrument instrument) throws IllegalStateException {
         for (WeakReference<ProbeNode> ref : probeNodeClones) {
             final ProbeNode probeNode = ref.get();
             if (probeNode != null) {
@@ -437,25 +268,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.
+     * Gets the currently active {@linkplain BeforeTagInstrument} at this Probe. Non{@code -null} if
+     * the global {@linkplain BeforeTagInstrument} is set and if this Probe holds the
+     * {@link SyntaxTag} specified in the instrument.
      */
-    SyntaxTagTrap getBeforeTrap() {
+    BeforeTagInstrument getBeforeTagInstrument() {
         checkProbeUnchanged();
-        return isBeforeTrapActive ? beforeTagTrap : null;
+        return isBeforeTagInstrumentActive ? instrumenter.getBeforeTagInstrument() : 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.
+     * Gets the currently active {@linkplain AfterTagInstrument} at this Probe. Non{@code -null} if
+     * the global {@linkplain BeforeTagInstrument} is set and if this Probe holds the
+     * {@link SyntaxTag} specified in the instrument.
      */
-    SyntaxTagTrap getAfterTrap() {
+    AfterTagInstrument getAfterTagInstrument() {
         checkProbeUnchanged();
-        return isAfterTrapActive ? afterTagTrap : null;
+        return isAfterTagInstrumentActive ? instrumenter.getAfterTagInstrument() : null;
+    }
+
+    Class<? extends TruffleLanguage> getLanguage() {
+        return language;
     }
 
     /**
@@ -476,13 +309,15 @@
         probeStateUnchangedCyclic.invalidate();
     }
 
-    private void notifyTrapsChanged() {
-        this.isBeforeTrapActive = beforeTagTrap != null && this.isTaggedAs(beforeTagTrap.getTag());
-        this.isAfterTrapActive = afterTagTrap != null && this.isTaggedAs(afterTagTrap.getTag());
+    void notifyTagInstrumentsChanged() {
+        final BeforeTagInstrument beforeTagInstrument = instrumenter.getBeforeTagInstrument();
+        this.isBeforeTagInstrumentActive = beforeTagInstrument != null && this.isTaggedAs(beforeTagInstrument.getTag());
+        final AfterTagInstrument afterTagInstrument = instrumenter.getAfterTagInstrument();
+        this.isAfterTagInstrumentActive = afterTagInstrument != null && this.isTaggedAs(afterTagInstrument.getTag());
         invalidateProbeUnchanged();
     }
 
-    private String getTagsDescription() {
+    String getTagsDescription() {
         final StringBuilder sb = new StringBuilder();
         sb.append("[");
         String prefix = "";
@@ -494,18 +329,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	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java	Wed Sep 30 16:33:56 2015 -0700
@@ -28,7 +28,8 @@
 import com.oracle.truffle.api.nodes.Node;
 
 /**
- * An exception thrown when {@link Node#probe()} fails because of an implementation failure.
+ * An exception thrown when {@link Instrumenter#probe(Node)} fails because of an implementation
+ * failure.
  * <p>
  * Language and tool implementations should ensure that clients of tools never see this exception.
  */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,7 +24,6 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeFieldAccessor;
 import com.oracle.truffle.api.nodes.NodeUtil;
@@ -47,7 +46,7 @@
         WRAPPER_NODE("The node to be probed is a wrapper"),
 
         /**
-         * The node to be probed returned {@link Node#isInstrumentable()}{@code == false}.
+         * The node to be probed does not support {@linkplain Instrumenter Instrumentation} .
          */
         NOT_INSTRUMENTABLE("The node to be project is \"not instrumentable\""),
 
@@ -78,7 +77,7 @@
     private final Object wrapper;
 
     /**
-     * Description of an internal failure of {@link Node#probe()}.
+     * Description of an internal failure of {@link Instrumenter#probe(Node)}.
      *
      * @param reason what caused the failure
      * @param parent the parent, if known, of the child being probed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeInstrument.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,618 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.instrument;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.api.source.SourceSection;
+
+/**
+ * A <em>binding</em> between:
+ * <ol>
+ * <li>A {@link Probe}: a source of <em>execution events</em> taking place at a program location in
+ * an executing Truffle AST, and</li>
+ * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client.
+ * </ol>
+ * <p>
+ * Client-oriented documentation for the use of Instruments is available online at <a
+ * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https://
+ * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a>
+ *
+ * @see Probe
+ * @see Instrumenter
+ */
+public abstract class ProbeInstrument extends Instrument {
+
+    /**
+     * Optional documentation, mainly for debugging.
+     */
+    private final String instrumentInfo;
+
+    /**
+     * Has this instrument been disposed? stays true once set.
+     */
+    private boolean isDisposed = false;
+
+    protected Probe probe = null;
+
+    /**
+     * <h4>Implementation Notes</h4>
+     * <p>
+     * The implementation of Instruments is complicated by the requirement that Truffle be able to
+     * clone ASTs at any time. In particular, any instrumentation-supporting Nodes that have been
+     * attached to an AST must be cloned along with the AST: AST clones are not permitted to share
+     * Nodes.
+     * <p>
+     * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is
+     * encouraged by providing the {@link SimpleInstrumentListener} for clients that need know
+     * nothing more than the properties associated with a Probe: its {@link SourceSection} and any
+     * associated instances of {@link SyntaxTag}.
+     * <p>
+     * AST cloning is <em>not transparent</em> to clients that use the
+     * {@link StandardInstrumentListener}, since those event methods identify the concrete Node
+     * instance (and thus the AST instance) where the event takes place.
+     * <p>
+     * <h4>Implementation Notes: the Life Cycle of an {@link ProbeInstrument} at a {@link Probe}</h4>
+     * <p>
+     * <ul>
+     * <li>A new Instrument is created in permanent association with a client-provided
+     * <em>listener.</em></li>
+     *
+     * <li>Multiple Instruments may share a single listener.</li>
+     *
+     * <li>An Instrument does nothing until it is {@linkplain Probe#attach(ProbeInstrument)
+     * attached} to a Probe, at which time the Instrument begins routing execution events from the
+     * Probe's AST location to the Instrument's listener.</li>
+     *
+     * <li>Neither Instruments nor Probes are {@link Node}s.</li>
+     *
+     * <li>A Probe has a single source-based location in an AST, but manages a separate
+     * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li>
+     * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is
+     * cloned along with the rest of the AST.</li>
+     *
+     * <li>When a new Instrument is attached to a Probe, the Instrument inserts a new instance of
+     * its private Node type into <em>each of the instrument chains</em> managed by the Probe, i.e.
+     * one node instance per existing clone of the AST.</li>
+     *
+     * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the
+     * Instrument's private Node type will be cloned along with the rest of the the AST.</li>
+     * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the
+     * shared (outer) Instrument instance; that state includes a reference to the Instrument's
+     * listener.</li>
+     *
+     * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed},
+     * the Instrument searches every instrument chain associated with the Probe and removes the
+     * instance of its private Node type.</li>
+     *
+     * <li>Attaching or disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of
+     * the AST.</li>
+     *
+     * </ul>
+     */
+    private ProbeInstrument(String instrumentInfo) {
+        this.instrumentInfo = instrumentInfo;
+    }
+
+    /**
+     * Removes this Instrument from the Probe to which it attached and renders this Instrument
+     * inert.
+     *
+     * @throws IllegalStateException if this instrument has already been disposed
+     */
+    @Override
+    public void dispose() throws IllegalStateException {
+        if (isDisposed) {
+            throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt");
+        }
+        if (probe != null) {
+            // It's attached
+            probe.disposeInstrument(this);
+            probe = null;
+        }
+        this.isDisposed = true;
+    }
+
+    @Override
+    public boolean isDisposed() {
+        return isDisposed;
+    }
+
+    /**
+     * Gets the {@link Probe} to which this Instrument is currently attached: {@code null} if not
+     * yet attached to a Probe or if this Instrument has been {@linkplain #dispose() disposed}.
+     */
+    public Probe getProbe() {
+        return probe;
+    }
+
+    /**
+     * For internal implementation only.
+     */
+    void setAttachedTo(Probe probe) {
+        this.probe = probe;
+    }
+
+    abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode);
+
+    /**
+     * Removes this instrument from an instrument chain.
+     */
+    abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode);
+
+    /**
+     * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}.
+     */
+    static final class SimpleInstrument extends ProbeInstrument {
+
+        /**
+         * Tool-supplied listener for events.
+         */
+        private final SimpleInstrumentListener simpleListener;
+
+        SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.simpleListener = simpleListener;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new SimpleInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(SimpleInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        /**
+         * Node that implements a {@link SimpleInstrument} in a particular AST.
+         */
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class SimpleInstrumentNode extends AbstractInstrumentNode {
+
+            private SimpleInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+            }
+
+            @Override
+            public void enter(Node node, VirtualFrame vFrame) {
+                SimpleInstrument.this.simpleListener.onEnter(SimpleInstrument.this.probe);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            @Override
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                SimpleInstrument.this.simpleListener.onReturnVoid(SimpleInstrument.this.probe);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            @Override
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                SimpleInstrument.this.simpleListener.onReturnValue(SimpleInstrument.this.probe, result);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            @Override
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                SimpleInstrument.this.simpleListener.onReturnExceptional(SimpleInstrument.this.probe, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            @Override
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : simpleListener.getClass().getSimpleName();
+            }
+        }
+    }
+
+    /**
+     * An instrument that propagates events to an instance of {@link StandardInstrumentListener}.
+     */
+    static final class StandardInstrument extends ProbeInstrument {
+
+        /**
+         * Tool-supplied listener for AST events.
+         */
+        private final StandardInstrumentListener standardListener;
+
+        StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.standardListener = standardListener;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new StandardInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(StandardInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        /**
+         * Node that implements a {@link StandardInstrument} in a particular AST.
+         */
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class StandardInstrumentNode extends AbstractInstrumentNode {
+
+            private StandardInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+            }
+
+            @Override
+            public void enter(Node node, VirtualFrame vFrame) {
+                standardListener.onEnter(StandardInstrument.this.probe, node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            @Override
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                standardListener.onReturnVoid(StandardInstrument.this.probe, node, vFrame);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            @Override
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                standardListener.onReturnValue(StandardInstrument.this.probe, node, vFrame, result);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            @Override
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                standardListener.onReturnExceptional(StandardInstrument.this.probe, node, vFrame, exception);
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            @Override
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : standardListener.getClass().getSimpleName();
+            }
+        }
+    }
+
+    /**
+     * An instrument that allows clients to provide an AST fragment to be executed directly from
+     * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle
+     * AST with potential for full optimization.
+     */
+    static final class AdvancedInstrument extends ProbeInstrument {
+
+        private final AdvancedInstrumentResultListener resultListener;
+        private final AdvancedInstrumentRootFactory rootFactory;
+        private final Class<?> requiredResultType;
+
+        AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
+            super(instrumentInfo);
+            this.resultListener = resultListener;
+            this.rootFactory = rootFactory;
+            this.requiredResultType = requiredResultType;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new AdvancedInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(AdvancedInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        /**
+         * Node that implements a {@link AdvancedInstrument} in a particular AST.
+         */
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class AdvancedInstrumentNode extends AbstractInstrumentNode {
+
+            @Child private AdvancedInstrumentRoot instrumentRoot;
+
+            private AdvancedInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+            }
+
+            @Override
+            public void enter(Node node, VirtualFrame vFrame) {
+                if (instrumentRoot == null) {
+                    try {
+                        final AdvancedInstrumentRoot newInstrumentRoot = AdvancedInstrument.this.rootFactory.createInstrumentRoot(AdvancedInstrument.this.probe, node);
+                        if (newInstrumentRoot != null) {
+                            instrumentRoot = newInstrumentRoot;
+                            adoptChildren();
+                            AdvancedInstrument.this.probe.invalidateProbeUnchanged();
+                        }
+                    } catch (RuntimeException ex) {
+                        if (resultListener != null) {
+                            resultListener.onFailure(node, vFrame, ex);
+                        }
+                    }
+                }
+                if (instrumentRoot != null) {
+                    try {
+                        final Object result = instrumentRoot.executeRoot(node, vFrame);
+                        if (resultListener != null) {
+                            checkResultType(result);
+                            resultListener.onExecution(node, vFrame, result);
+                        }
+                    } catch (RuntimeException ex) {
+                        if (resultListener != null) {
+                            resultListener.onFailure(node, vFrame, ex);
+                        }
+                    }
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            private void checkResultType(Object result) {
+                if (requiredResultType == null) {
+                    return;
+                }
+                if (result == null) {
+                    throw instrumentResultNull();
+                }
+                if (!(requiredResultType.isAssignableFrom(result.getClass()))) {
+                    throw instrumentResultWrongType(result);
+                }
+            }
+
+            @TruffleBoundary
+            private RuntimeException instrumentResultNull() {
+                return new RuntimeException("Instrument result null: " + requiredResultType.getSimpleName() + " is required");
+            }
+
+            @TruffleBoundary
+            private RuntimeException instrumentResultWrongType(Object result) {
+                return new RuntimeException("Instrument result " + result.toString() + " not assignable to " + requiredResultType.getSimpleName());
+            }
+
+            @Override
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            @Override
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            @Override
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            @Override
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : rootFactory.getClass().getSimpleName();
+            }
+        }
+    }
+
+    // TODO (mlvdv) experimental
+    public interface TruffleOptListener {
+        void notifyIsCompiled(boolean isCompiled);
+    }
+
+    @SuppressWarnings("unused")
+    private static final class TruffleOptInstrument extends ProbeInstrument {
+
+        private final TruffleOptListener toolOptListener;
+
+        private TruffleOptInstrument(TruffleOptListener listener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.toolOptListener = listener;
+        }
+
+        @Override
+        AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
+            return new TruffleOptInstrumentNode(nextNode);
+        }
+
+        @Override
+        AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrumentNode;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(TruffleOptInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class TruffleOptInstrumentNode extends AbstractInstrumentNode {
+
+            private boolean isCompiled;
+
+            private TruffleOptInstrumentNode(AbstractInstrumentNode nextNode) {
+                super(nextNode);
+                this.isCompiled = CompilerDirectives.inCompiledCode();
+            }
+
+            @Override
+            public void enter(Node node, VirtualFrame vFrame) {
+                if (this.isCompiled != CompilerDirectives.inCompiledCode()) {
+                    this.isCompiled = CompilerDirectives.inCompiledCode();
+                    TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled);
+                }
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.enter(node, vFrame);
+                }
+            }
+
+            @Override
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnVoid(node, vFrame);
+                }
+            }
+
+            @Override
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnValue(node, vFrame, result);
+                }
+            }
+
+            @Override
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                if (nextInstrumentNode != null) {
+                    nextInstrumentNode.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            @Override
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : toolOptListener.getClass().getSimpleName();
+            }
+        }
+    }
+
+    @NodeInfo(cost = NodeCost.NONE)
+    abstract class AbstractInstrumentNode extends EventHandlerNode {
+
+        @Child protected AbstractInstrumentNode nextInstrumentNode;
+
+        protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) {
+            this.nextInstrumentNode = nextNode;
+        }
+
+        @Override
+        public Probe getProbe() {
+            return probe;
+        }
+
+        /**
+         * Gets the instrument that created this node.
+         */
+        private ProbeInstrument getInstrument() {
+            return ProbeInstrument.this;
+        }
+
+        /**
+         * Removes the node from this chain that was added by a particular instrument, assuming that
+         * the head of the chain is not the one to be replaced. This is awkward, but is required
+         * because {@link Node#replace(Node)} won't take a {@code null} argument. This doesn't work
+         * for the tail of the list, which would be replacing itself with null. So the replacement
+         * must be directed the parent of the node being removed.
+         */
+        private boolean removeFromChain(ProbeInstrument instrument) {
+            assert getInstrument() != instrument;
+            if (nextInstrumentNode == null) {
+                return false;
+            }
+            if (nextInstrumentNode.getInstrument() == instrument) {
+                // Next is the one to remove
+                if (nextInstrumentNode.nextInstrumentNode == null) {
+                    // Next is at the tail; just forget
+                    nextInstrumentNode = null;
+                } else {
+                    // Replace next with its successor
+                    nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode);
+                }
+                return true;
+            }
+            return nextInstrumentNode.removeFromChain(instrument);
+        }
+
+        protected String getInstrumentInfo() {
+            return ProbeInstrument.this.instrumentInfo;
+        }
+    }
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -25,10 +25,11 @@
 package com.oracle.truffle.api.instrument;
 
 import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.nodes.RootNode;
 
 /**
- * An observer of events related to {@link Probe}s: creating and tagging.
+ * An observer of events related to {@link Probe}s: creation of new Probes and the addition of new
+ * {@linkplain SyntaxTag SyntaxTags} to existing Probes.
  */
 public interface ProbeListener {
 
@@ -36,15 +37,16 @@
      * Notifies that all registered {@link ASTProber}s are about to be applied to a newly
      * constructed AST.
      *
-     * @param source source code from which the AST was constructed
+     * @param rootNode parent of the newly created AST
      */
-    void startASTProbing(Source source);
+    void startASTProbing(RootNode rootNode);
 
     /**
-     * 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.
      */
@@ -73,8 +75,8 @@
      * Notifies that the application of all registered {@link ASTProber}s to a newly constructed AST
      * has completed.
      *
-     * @param source source code from which the AST was constructed
+     * @param rootNode parent of the newly created AST
      */
-    void endASTProbing(Source source);
+    void endASTProbing(RootNode rootNode);
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -26,109 +26,30 @@
 
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.Instrument.AbstractInstrumentNode;
-import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
+import com.oracle.truffle.api.instrument.ProbeInstrument.AbstractInstrumentNode;
+import com.oracle.truffle.api.instrument.TagInstrument.AfterTagInstrument;
+import com.oracle.truffle.api.instrument.TagInstrument.BeforeTagInstrument;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
-import com.oracle.truffle.api.source.SourceSection;
 
 /**
  * Implementation class & interface for enabling the attachment of {@linkplain Probe Probes} to
- * Truffle ASTs.
+ * Truffle ASTs, part of the {@linkplain Instrumenter Instrumentation Framework}.
  * <p>
- * A {@link ProbeNode} is the head of a chain of nodes acting on behalf of {@linkplain Instrument
- * instruments}. It is attached to an AST as a child of a guest-language-specific
- * {@link WrapperNode} node.
+ * A {@link ProbeNode} is the head of a chain of nodes acting on behalf of
+ * {@linkplain ProbeInstrument instruments}. It is attached to an AST as a child of a
+ * guest-language-specific {@link WrapperNode} node.
  * <p>
- * When Truffle clones an AST, the chain, including all attached {@linkplain Instrument instruments}
- * will be cloned along with the {@link WrapperNode} to which it is attached. An instance of
- * {@link Probe} represents abstractly the instrumentation at a particular location in a Guest
- * Language AST, tracks the clones of the chain, and keeps the instrumentation attached to the
+ * When Truffle clones an AST, the chain, including all attached {@linkplain ProbeInstrument
+ * instruments} will be cloned along with the {@link WrapperNode} to which it is attached. An
+ * instance of {@link Probe} represents abstractly the instrumentation at a particular location in a
+ * Guest Language AST, tracks the clones of the chain, and keeps the instrumentation attached to the
  * clones consistent.
  */
 @NodeInfo(cost = NodeCost.NONE)
-public final class ProbeNode extends Node implements TruffleEvents, InstrumentationNode {
-
-    /**
-     * A node that can be inserted into a Truffle AST, and which enables {@linkplain Instrument
-     * instrumentation} at a particular Guest Language (GL) node. Implementations must extend
-     * {@link Node} and should override {@link Node#isInstrumentable()} to return {@code false}.
-     * <p>
-     * The implementation must be GL-specific. A wrapper <em>decorates</em> a GL AST node (the
-     * wrapper's <em>child</em>) by acting as a transparent <em>proxy</em> with respect to the GL's
-     * execution semantics.
-     * <p>
-     * Instrumentation at the wrapped node is implemented by an instance of {@link ProbeNode}
-     * attached as a second child of the {@link WrapperNode}.
-     * <p>
-     * A wrapper is obliged to notify its attached {@link ProbeNode} when execution events occur at
-     * the wrapped AST node during program execution.
-     * <p>
-     * When a GL AST is cloned, the {@link WrapperNode}, its {@link ProbeNode} and any
-     * {@linkplain Instrument instrumentation} are also cloned; they are in effect part of the GL
-     * AST. An instance of {@link Probe} represents abstractly the instrumentation at a particular
-     * location in a GL AST; it tracks all the copies of the Wrapper and attached instrumentation,
-     * and acts as a single point of access for tools.
-     * <p>
-     * This interface is not intended to be visible as part of the API for tools (instrumentation
-     * clients).
-     * <p>
-     * Implementation guidelines:
-     * <ol>
-     * <li>Each GL implementation should include a WrapperNode implementation; usually only one is
-     * needed.</li>
-     * <li>The wrapper type should descend from the <em>GL-specific node class</em>.</li>
-     * <li>Must have a field: {@code @Child private <GL>Node child;}</li>
-     * <li>Must have a field: {@code @Child private ProbeNode probeNode;}</li>
-     * <li>The wrapper must act as a <em>proxy</em> for its child, which means implementing every
-     * possible <em>execute-</em> method that gets called on guest language AST node types by their
-     * parents, and passing along each call to its child.</li>
-     * <li>Method {@code Probe getProbe()} should be implemented as {@code probeNode.getProbe();}
-     * <li>Method {@code insertProbe(ProbeNode)} should be implemented as
-     * {@code this.probeNode=insert(newProbeNode);}</li>
-     * <li>Most importantly, Wrappers must be implemented so that Truffle optimization will reduce
-     * their runtime overhead to zero when there are no attached {@link Instrument}s.</li>
-     * </ol>
-     * <p>
-     *
-     * @see Instrument
-     */
-    public interface WrapperNode extends InstrumentationNode {
-
-        /**
-         * Gets the node being "wrapped", i.e. the AST node for which
-         * {@linkplain InstrumentationNode.TruffleEvents execution events} will be reported through
-         * the Instrumentation Framework.
-         */
-        Node getChild();
-
-        /**
-         * Gets the {@link Probe} responsible for installing this wrapper.
-         */
-        Probe getProbe();
-
-        /**
-         * Implementation support for completing a newly created wrapper node.
-         */
-        void insertProbe(ProbeNode probeNode);
-    }
-
-    /**
-     * Create a new {@link Probe} associated with, and attached to, a Guest Language specific
-     * instance of {@link WrapperNode}.
-     */
-    @SuppressWarnings("rawtypes")
-    public static Probe insertProbe(WrapperNode wrapper) {
-        final SourceSection sourceSection = wrapper.getChild().getSourceSection();
-        final ProbeNode probeNode = new ProbeNode(); // private constructor
-        Class<? extends TruffleLanguage> l = Probe.ACCESSOR.findLanguage(wrapper.getChild().getRootNode());
-        probeNode.probe = new Probe(l, probeNode, sourceSection);  // package private access
-        wrapper.insertProbe(probeNode);
-        return probeNode.probe;
-    }
+final class ProbeNode extends EventHandlerNode {
 
     // Never changed once set.
     @CompilationFinal Probe probe = null;
@@ -138,11 +59,6 @@
     @Child protected AbstractInstrumentNode firstInstrumentNode;
 
     @Override
-    public boolean isInstrumentable() {
-        return false;
-    }
-
-    @Override
     public Node copy() {
         Node node = super.copy();
         probe.registerProbeNodeClone((ProbeNode) node);
@@ -152,54 +68,60 @@
     /**
      * @return the {@link Probe} permanently associated with this {@link ProbeNode}.
      */
+    @Override
     public Probe getProbe() {
         return probe;
     }
 
+    @Override
     public void enter(Node node, VirtualFrame vFrame) {
         this.probe.checkProbeUnchanged();
-        final SyntaxTagTrap beforeTagTrap = probe.getBeforeTrap();
-        if (beforeTagTrap != null) {
-            beforeTagTrap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize());
+        final BeforeTagInstrument beforeTagInstrument = probe.getBeforeTagInstrument();
+        if (beforeTagInstrument != null) {
+            beforeTagInstrument.getListener().onEnter(probe, ((WrapperNode) this.getParent()).getChild(), vFrame);
         }
         if (firstInstrumentNode != null) {
             firstInstrumentNode.enter(node, vFrame);
         }
     }
 
+    @Override
     public void returnVoid(Node node, VirtualFrame vFrame) {
         this.probe.checkProbeUnchanged();
         if (firstInstrumentNode != null) {
             firstInstrumentNode.returnVoid(node, vFrame);
         }
-        final SyntaxTagTrap afterTagTrap = probe.getAfterTrap();
-        if (afterTagTrap != null) {
-            afterTagTrap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize());
+        final AfterTagInstrument afterTagInstrument = probe.getAfterTagInstrument();
+        if (afterTagInstrument != null) {
+            afterTagInstrument.getListener().onReturnVoid(probe, ((WrapperNode) this.getParent()).getChild(), vFrame);
         }
     }
 
+    @Override
     public void returnValue(Node node, VirtualFrame vFrame, Object result) {
         this.probe.checkProbeUnchanged();
         if (firstInstrumentNode != null) {
             firstInstrumentNode.returnValue(node, vFrame, result);
         }
-        final SyntaxTagTrap afterTagTrap = probe.getAfterTrap();
-        if (afterTagTrap != null) {
-            afterTagTrap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize());
+        final AfterTagInstrument afterTagInstrument = probe.getAfterTagInstrument();
+        if (afterTagInstrument != null) {
+            afterTagInstrument.getListener().onReturnValue(probe, ((WrapperNode) this.getParent()).getChild(), vFrame, result);
         }
     }
 
+    @Override
     public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
         this.probe.checkProbeUnchanged();
         if (firstInstrumentNode != null) {
             firstInstrumentNode.returnExceptional(node, vFrame, exception);
         }
-        final SyntaxTagTrap afterTagTrap = probe.getAfterTrap();
-        if (afterTagTrap != null) {
-            afterTagTrap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), vFrame.materialize());
+        final AfterTagInstrument afterTagInstrument = probe.getAfterTagInstrument();
+        if (afterTagInstrument != null) {
+            afterTagInstrument.getListener().onReturnExceptional(probe, ((WrapperNode) this.getParent()).getChild(), vFrame, exception);
         }
     }
 
+    @Override
     public String instrumentationInfo() {
         return "Standard probe";
     }
@@ -215,7 +137,7 @@
      * Adds an {@link AbstractInstrumentNode} to this chain.
      */
     @TruffleBoundary
-    void addInstrument(Instrument instrument) {
+    void addInstrument(ProbeInstrument instrument) {
         assert instrument.getProbe() == probe;
         // The existing chain of nodes may be empty
         // Attach the modified chain.
@@ -228,7 +150,7 @@
      * @throws RuntimeException if no matching instrument is found,
      */
     @TruffleBoundary
-    void removeInstrument(Instrument instrument) {
+    void removeInstrument(ProbeInstrument instrument) {
         assert instrument.getProbe() == probe;
         final AbstractInstrumentNode modifiedChain = instrument.removeFromChain(firstInstrumentNode);
         if (modifiedChain == null) {
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -27,7 +27,8 @@
 import com.oracle.truffle.api.source.SourceSection;
 
 /**
- * A receiver of Truffle execution events that can act on behalf of an external client.
+ * A receiver of Truffle AST execution events (containing minimal information) that can act on
+ * behalf of an external client.
  * <p>
  * The {@link Probe} instance provides access to the {@link SourceSection} associated with the
  * event, as well as any {@link SyntaxTag}s that have been applied at that program's location.
@@ -37,8 +38,11 @@
  * execution state should use {@link StandardInstrumentListener}.
  * <p>
  * Clients are free, of course, to record additional information in the listener implementation that
- * carries additional information about the context and reason for the particular {@link Instrument}
- * that is to be created from the listener.
+ * carries additional information about the context and reason for the particular
+ * {@link ProbeInstrument} that is to be created from the listener.
+ * <p>
+ * Notification is fully synchronous, so overrides have performance implications. Non-trivial
+ * methods should be coded with Truffle guidelines and cautions in mind.
  */
 public interface SimpleInstrumentListener {
 
@@ -47,7 +51,7 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void enter(Probe probe);
+    void onEnter(Probe probe);
 
     /**
      * Receive notification that a program location's {@code void}-valued execution has just
@@ -55,7 +59,7 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnVoid(Probe probe);
+    void onReturnVoid(Probe probe);
 
     /**
      * Receive notification that a program location's execution has just completed and returned a
@@ -63,12 +67,12 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnValue(Probe probe, Object result);
+    void onReturnValue(Probe probe, Object result);
 
     /**
      * Receive notification that a program location's execution has just thrown an exception.
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnExceptional(Probe probe, Exception exception);
+    void onReturnExceptional(Probe probe, Exception exception);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardAfterInstrumentListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,72 @@
+/*
+ * 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 com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.SourceSection;
+
+/**
+ * A receiver of Truffle AST <em>after</em> execution events that can act on behalf of an external
+ * client.
+ * <p>
+ * The {@link Probe} argument provides access to the {@link SourceSection} associated with the
+ * event, as well as any {@link SyntaxTag}s that have been applied at that program's location.
+ * <p>
+ * This listener is designed for clients that also require access to the AST execution state at the
+ * time of the event. Clients that do not require access to the AST execution state should use the
+ * {@link SimpleInstrumentListener}.
+ * <p>
+ * Clients are free, of course, to record additional information in the listener implementation that
+ * carries additional information about the context and reason for the particular
+ * {@link ProbeInstrument} that is to be created from the listener.
+ * <p>
+ * Notification is fully synchronous, so overrides have performance implications. Non-trivial
+ * methods should be coded with Truffle guidelines and cautions in mind.
+ */
+public interface StandardAfterInstrumentListener {
+
+    /**
+     * Receive notification that an AST Node's {@code void}-valued execute method has just returned.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame);
+
+    /**
+     * Receive notification that an AST Node's execute method has just returned a value (boxed if
+     * primitive).
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result);
+
+    /**
+     * Receive notification that an AST Node's execute method has just thrown an exception.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardBeforeInstrumentListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,58 @@
+/*
+ * 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 com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.source.SourceSection;
+
+/**
+ * A receiver of Truffle AST <em>before</em> execution events that can act on behalf of an external
+ * client.
+ * <p>
+ * The {@link Probe} argument provides access to the {@link SourceSection} associated with the
+ * event, as well as any {@link SyntaxTag}s that have been applied at that program's location.
+ * <p>
+ * This listener is designed for clients that also require access to the AST execution state at the
+ * time of the event. Clients that do not require access to the AST execution state should use the
+ * {@link SimpleInstrumentListener}.
+ * <p>
+ * Clients are free, of course, to record additional information in the listener implementation that
+ * carries additional information about the context and reason for the particular
+ * {@link ProbeInstrument} that is to be created from the listener.
+ * <p>
+ * Notification is fully synchronous, so overrides have performance implications. Non-trivial
+ * methods should be coded with Truffle guidelines and cautions in mind.
+ */
+public interface StandardBeforeInstrumentListener {
+
+    /**
+     * Receive notification that an AST node's execute method is about to be called.
+     * <p>
+     * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
+     */
+    void onEnter(Probe probe, Node node, VirtualFrame vFrame);
+
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -29,18 +29,21 @@
 import com.oracle.truffle.api.source.SourceSection;
 
 /**
- * A receiver of Truffle execution events that can act on behalf of an external client.
+ * A receiver of Truffle AST execution events that can act on behalf of an external client.
  * <p>
  * The {@link Probe} argument provides access to the {@link SourceSection} associated with the
- * event, as well as any {@link SyntaxTag}s that have been applied at that AST node.
+ * event, as well as any {@link SyntaxTag}s that have been applied at that program's location.
  * <p>
  * This listener is designed for clients that also require access to the AST execution state at the
  * time of the event. Clients that do not require access to the AST execution state should use the
  * {@link SimpleInstrumentListener}.
  * <p>
  * Clients are free, of course, to record additional information in the listener implementation that
- * carries additional information about the context and reason for the particular {@link Instrument}
- * that is to be created from the listener.
+ * carries additional information about the context and reason for the particular
+ * {@link ProbeInstrument} that is to be created from the listener.
+ * <p>
+ * Notification is fully synchronous, so overrides have performance implications. Non-trivial
+ * methods should be coded with Truffle guidelines and cautions in mind.
  */
 public interface StandardInstrumentListener {
 
@@ -49,14 +52,14 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void enter(Probe probe, Node node, VirtualFrame vFrame);
+    void onEnter(Probe probe, Node node, VirtualFrame vFrame);
 
     /**
      * Receive notification that an AST Node's {@code void}-valued execute method has just returned.
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnVoid(Probe probe, Node node, VirtualFrame vFrame);
+    void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame);
 
     /**
      * Receive notification that an AST Node's execute method has just returned a value (boxed if
@@ -64,12 +67,12 @@
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result);
+    void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result);
 
     /**
      * Receive notification that an AST Node's execute method has just thrown an exception.
      * <p>
      * <strong>Synchronous</strong>: Truffle execution waits until the call returns.
      */
-    void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception);
+    void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception);
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTag.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTag.java	Wed Sep 30 16:33:56 2015 -0700
@@ -25,8 +25,9 @@
 package com.oracle.truffle.api.instrument;
 
 /**
- * Program element "tags", presumed to be singletons (best implemented as enums) that define
- * user-visible behavior for debugging and other simple tools. These categories should correspond to
+ * Program element "tags" such as <code>"STATEMENT"</code>, presumed to be singletons (best
+ * implemented as enums) that define user-visible behavior for debugging and other
+ * {@linkplain Instrumenter Instrumentation-based} tools. These categories should correspond to
  * program structures, for example "statement" and "assignment", that are meaningful
  * ("human-sensible") to guest language programmers.
  * <p>
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SyntaxTagTrap.java	Tue Sep 29 18:04:11 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.instrument;
-
-import com.oracle.truffle.api.frame.MaterializedFrame;
-import com.oracle.truffle.api.nodes.Node;
-
-/**
- * A trap that can be set to interrupt execution at probed nodes carrying a specific tag.
- *
- * @see Probe
- */
-public abstract class SyntaxTagTrap {
-
-    private final SyntaxTag tag;
-
-    protected SyntaxTagTrap(SyntaxTag tag) {
-        this.tag = tag;
-    }
-
-    public final SyntaxTag getTag() {
-        return tag;
-    }
-
-    /**
-     * Notifies that execution is halted at a node with the specified tag.
-     */
-    public abstract void tagTrappedAt(Node node, MaterializedFrame frame);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/TagInstrument.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,127 @@
+/*
+ * 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;
+
+/**
+ * A <em>binding</em> between:
+ * <ol>
+ * <li>A {@link SyntaxTag} that specifies nodes to act as source of <em>execution events</em> taking
+ * place at a program location in an executing Truffle AST, and</li>
+ * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client.
+ * </ol>
+ * <p>
+ * Client-oriented documentation for the use of Instruments is available online at <a
+ * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https://
+ * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a>
+ *
+ * @see SyntaxTag
+ * @see Instrumenter
+ */
+public abstract class TagInstrument extends Instrument {
+
+    /**
+     * Optional documentation, mainly for debugging.
+     */
+    @SuppressWarnings("unused") private final String instrumentInfo;
+
+    protected Instrumenter instrumenter;
+
+    /**
+     * Has this instrument been disposed? stays true once set.
+     */
+    protected boolean isDisposed = false;
+
+    private SyntaxTag tag = null;
+
+    protected TagInstrument(Instrumenter instrumenter, SyntaxTag tag, String instrumentInfo) {
+        this.instrumenter = instrumenter;
+        this.tag = tag;
+        this.instrumentInfo = instrumentInfo;
+    }
+
+    @Override
+    public void dispose() throws IllegalStateException {
+        if (isDisposed) {
+            throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt");
+        }
+        instrumenter.disposeAfterTagInstrument();
+        this.isDisposed = true;
+    }
+
+    @Override
+    public boolean isDisposed() {
+        return isDisposed;
+    }
+
+    public SyntaxTag getTag() {
+        return tag;
+    }
+
+    static final class BeforeTagInstrument extends TagInstrument {
+
+        private final StandardBeforeInstrumentListener listener;
+
+        BeforeTagInstrument(Instrumenter instrumenter, SyntaxTag tag, StandardBeforeInstrumentListener listener, String instrumentInfo) {
+            super(instrumenter, tag, instrumentInfo);
+            this.listener = listener;
+        }
+
+        StandardBeforeInstrumentListener getListener() {
+            return listener;
+        }
+
+        @Override
+        public void dispose() throws IllegalStateException {
+            if (isDisposed) {
+                throw new IllegalStateException("Disposed Instrument can not be disposed again");
+            }
+            instrumenter.disposeBeforeTagInstrument();
+            this.isDisposed = true;
+        }
+    }
+
+    static final class AfterTagInstrument extends TagInstrument {
+
+        private final StandardAfterInstrumentListener listener;
+
+        AfterTagInstrument(Instrumenter instrumenter, SyntaxTag tag, StandardAfterInstrumentListener listener, String instrumentInfo) {
+            super(instrumenter, tag, instrumentInfo);
+            this.listener = listener;
+        }
+
+        StandardAfterInstrumentListener getListener() {
+            return listener;
+        }
+
+        @Override
+        public void dispose() throws IllegalStateException {
+            if (isDisposed) {
+                throw new IllegalStateException("Disposed Instrument can not be disposed again");
+            }
+            instrumenter.disposeAfterTagInstrument();
+            this.isDisposed = true;
+        }
+    }
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ToolSupportProvider.java	Tue Sep 29 18:04:11 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.api.instrument;
-
-/**
- * Access to language-specific information and execution services for external tools.
- */
-public interface ToolSupportProvider {
-
-    /**
-     * Gets visualization services for language-specific information.
-     */
-    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}
-     */
-    void enableASTProbing(ASTProber astProber);
-
-}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java	Wed Sep 30 16:33:56 2015 -0700
@@ -29,9 +29,9 @@
 import com.oracle.truffle.api.nodes.Node;
 
 /**
- * Visualization services for the benefit of instrumentation-based tools, possibly specialized for
- * each guest language and possibly specialized for relevant information from the underlying Truffle
- * implementation.
+ * Visualization services for the benefit of {@linkplain Instrumenter Instrumentation-based} tools,
+ * possibly specialized for each guest language and possibly specialized for relevant information
+ * from the underlying Truffle implementation.
  * <p>
  * <strong>Disclaimer:</strong> experimental interface under development.
  */
@@ -61,7 +61,7 @@
 
     /**
      * Converts a value in the guest language to a display string. If
-     * 
+     *
      * @param trim if {@code > 0}, them limit size of String to either the value of trim or the
      *            number of characters in the first line, whichever is lower.
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/WrapperNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.api.instrument;
+
+import com.oracle.truffle.api.nodes.Node;
+
+/**
+ * A specialized {@link Node} instance that must be inserted into a Truffle AST in order to enable
+ * {@linkplain Instrumenter instrumentation} at a particular Guest Language (GL) node.
+ * <p>
+ * The implementation must be GL-specific. A wrapper <em>decorates</em> a GL AST node (the wrapper's
+ * <em>child</em>) by acting as a transparent <em>proxy</em> with respect to the GL's execution
+ * semantics.
+ * <p>
+ * Instrumentation at the wrapped node is implemented by an instance of {@link EventHandlerNode}
+ * attached as a second child of the {@link WrapperNode}.
+ * <p>
+ * A wrapper is obliged to notify its attached {@link EventHandlerNode} when execution events occur
+ * at the wrapped AST node during program execution.
+ * <p>
+ * When a GL AST is cloned, the {@link WrapperNode}, its {@link EventHandlerNode} and any
+ * {@linkplain ProbeInstrument instrumentation} are also cloned; they are in effect part of the GL
+ * AST. An instance of {@link Probe} represents abstractly the instrumentation at a particular
+ * location in a GL AST; it tracks all the copies of the Wrapper and attached instrumentation, and
+ * acts as a single point of access for tools.
+ * <p>
+ * Implementation guidelines:
+ * <ol>
+ * <li>Each GL implementation must implement a WrapperNode implementation for each AST context in
+ * which Instrumentation is to be supported.</li>
+ * <li>The wrapper type should descend from the <em>GL-specific node class</em>.</li>
+ * <li>Must have a field: {@code @Child private <GL>Node child;}</li>
+ * <li>Must have a field: {@code @Child private EventHandlerNode eventHandlerNode;}</li>
+ * <li>The wrapper must act as a <em>proxy</em> for its child, which means implementing every
+ * possible <em>execute-</em> method that gets called on guest language AST node types by their
+ * parents, and passing along each call to its child.</li>
+ * <li>Method {@code Probe getProbe()} should be implemented as {@code eventHandlerNode.getProbe();}
+ * <li>Method {@code insertProbe(EventHandlerNode)} should be implemented as
+ * {@code this.eventHandlerNode=insert(eventHandlerNode);}</li>
+ * <li>Most importantly, Wrappers must be implemented so that Truffle optimization will reduce their
+ * runtime overhead to zero when there are no attached {@link ProbeInstrument}s.</li>
+ * </ol>
+ * <p>
+ *
+ * @see ProbeInstrument
+ */
+public interface WrapperNode extends InstrumentationNode {
+
+    /**
+     * Gets the node being "wrapped", i.e. the AST node for which {@linkplain EventHandlerNode
+     * execution events} will be reported through the Instrumentation Framework.
+     */
+    Node getChild();
+
+    /**
+     * Gets the {@link Probe} responsible for installing this wrapper.
+     */
+    Probe getProbe();
+
+    /**
+     * Implementation support for completing a newly created wrapper node.
+     */
+    void insertEventHandlerNode(EventHandlerNode eventHandlerNode);
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Wed Sep 30 16:33:56 2015 -0700
@@ -26,7 +26,7 @@
 
 import com.oracle.truffle.api.instrument.ASTPrinter;
 import com.oracle.truffle.api.instrument.InstrumentationNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeClass;
 import com.oracle.truffle.api.nodes.NodeFieldAccessor;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultProbeListener.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultProbeListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -27,11 +27,11 @@
 import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.instrument.ProbeListener;
 import com.oracle.truffle.api.instrument.SyntaxTag;
-import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.nodes.RootNode;
 
 public abstract class DefaultProbeListener implements ProbeListener {
 
-    public void startASTProbing(Source source) {
+    public void startASTProbing(RootNode rootNode) {
     }
 
     public void newProbeInserted(Probe probe) {
@@ -40,7 +40,7 @@
     public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
     }
 
-    public void endASTProbing(Source source) {
+    public void endASTProbing(RootNode rootNode) {
     }
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -32,15 +32,15 @@
  */
 public class DefaultSimpleInstrumentListener implements SimpleInstrumentListener {
 
-    public void enter(Probe probe) {
+    public void onEnter(Probe probe) {
     }
 
-    public void returnVoid(Probe probe) {
+    public void onReturnVoid(Probe probe) {
     }
 
-    public void returnValue(Probe probe, Object result) {
+    public void onReturnValue(Probe probe, Object result) {
     }
 
-    public void returnExceptional(Probe probe, Exception exception) {
+    public void onReturnExceptional(Probe probe, Exception exception) {
     }
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java	Wed Sep 30 16:33:56 2015 -0700
@@ -35,16 +35,16 @@
  */
 public class DefaultStandardInstrumentListener implements StandardInstrumentListener {
 
-    public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+    public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
     }
 
-    public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
+    public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) {
     }
 
-    public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
+    public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
     }
 
-    public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
+    public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
     }
 
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,7 +24,171 @@
  */
 
 /**
- * Advanced node manipulation instruments.
+ * <h4>Truffle {@linkplain com.oracle.truffle.api.instrument.Instrumenter Instrumentation}: access to execution events for Debuggers and other tools.</h4>
+ * <p>
+ * This framework permits client
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter.Tool tools},
+ * either builtin or third-party, to observe with <em>very low overhead</em> the execution of a
+ * {@linkplain com.oracle.truffle.api.TruffleLanguage Truffle Language} program at the level of
+ * <em>AST execution events</em>:
+ * <ul>
+ * <li>a Truffle
+ * {@linkplain com.oracle.truffle.api.nodes.Node Node} is about to be executed, or</li>
+ * <li>a Truffle
+ * {@linkplain com.oracle.truffle.api.nodes.Node Node} execution has just completed.</li>
+ * </ul>
+ * The framework supports many kinds of tools, for example simple collectors of data such as the
+ * CoverageTracker.
+ * It also supports Truffle's built-in
+ * {@linkplain com.oracle.truffle.api.debug.Debugger debugging services}, as well as utilities
+ * that maintain {@linkplain com.oracle.truffle.api.debug.LineToProbesMap maps of source code locations}
+ * for other tools such as {@linkplain com.oracle.truffle.api.debug.Debugger debugging}.
+ *
+ * <h4>Instrumentation Services</h4>
+ * <p>
+ * API access to instrumentation services is provided by the
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter Instrumenter} that is part of
+ * the Truffle execution environment. These services fall into several categories:
+ *
+ * <ul>
+ *
+ * <li><strong>AST Markup: probing</strong>
+ * <ul>
+ * <li>Execution events can only be observed at AST locations that have been
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#probe(com.oracle.truffle.api.nodes.Node) probed},
+ * which results in the creation of a
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} that is permanently associated with a
+ * particular segment of source code, e.g. a "statement", that corresponds to an AST location.</li>
+ * <li>Probing is only supported at
+ * {@linkplain com.oracle.truffle.api.nodes.Node Nodes} where supported by specific language implementations.</li>
+ * <li>The relationship between a
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} and
+ * a source code location persists across Truffle <em>cloning</em> of ASTs, which is to say, a single
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} corresponds to (and keeps track of)
+ * the equivalent location in every clone of the original AST.</li>
+ * <li>Probing is specified by
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#registerASTProber(ASTProber) registering}
+ * an instance of
+ * {@linkplain com.oracle.truffle.api.instrument.ASTProber ASTProber}.
+ * A default prober provided by each {@linkplain com.oracle.truffle.api.TruffleLanguage TruffelLanguage}
+ * will be registered automatically.</li>
+ * <li>All AST probing must be complete before any AST execution has started.</li>
+ * </ul></li>
+ *
+ * <li><strong>AST Markup: tagging</strong>
+ * <ul>
+ * <li>Additional information can be added at any time to a
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} by
+ * {@linkplain com.oracle.truffle.api.instrument.Probe#tagAs(SyntaxTag, Object) adding} to its set of
+ * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tags}.</li>
+ *
+ * <li>{@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag Standard tags} should
+ * be used for common, language-agnostic concepts such as
+ * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATEMENT}, and this
+ * is usually done by each language's default
+ * {@linkplain com.oracle.truffle.api.instrument.ASTProber ASTProber} while
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probes} are being created.</li>
+ * <li>{@linkplain com.oracle.truffle.api.instrument.SyntaxTag Tags} serve to configure the behavior
+ * of clients during program execution.  For example, knowing which
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probes} are on
+ * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATEMENT nodes}
+ * informs both the CoverageTracker and the
+ * {@linkplain com.oracle.truffle.api.debug.Debugger debugger} while "stepping".</li>
+ * <li>{@linkplain com.oracle.truffle.api.instrument.SyntaxTag tags} can also be added at any
+ * time by any client for any purpose, including private
+ * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag Tags} about which other clients
+ * can learn nothing.</li>
+ * </ul></li>
+ *
+ * <li><strong>Markup Discovery: finding probes</strong>
+ * <ul>
+ * <li>Clients can be observe {@linkplain com.oracle.truffle.api.instrument.Probe Probes}
+ * being {@linkplain com.oracle.truffle.api.instrument.Instrumenter#probe(com.oracle.truffle.api.nodes.Node) created} and
+ * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag Tags} being
+ * {@linkplain com.oracle.truffle.api.instrument.Probe#tagAs(SyntaxTag, Object) added} by
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#addProbeListener(ProbeListener) registering} a
+ * {@linkplain com.oracle.truffle.api.instrument.ProbeListener ProbeListener}.</li>
+ * <li>Clients can also
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#findProbesTaggedAs(SyntaxTag) find} all existing
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probes}, possibly filtering the search by
+ * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tag}.</li>
+ * </ul></li>
+ *
+ * <li><strong>Observing Execution: event listening:</strong>
+ * <ul>
+ * <li>Clients can be notified of <em>AST execution events</em> by creating one of several kinds of
+ * <em>event listener</em> and <em>attaching</em> it to a
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe}. This creates an
+ * {@linkplain com.oracle.truffle.api.instrument.ProbeInstrument Instrument} that notifies the listener
+ * of every subsequent execution event at the AST location corresponding to the
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe}.</li>
+ * <li>An
+ * {@linkplain com.oracle.truffle.api.instrument.ProbeInstrument Instrument} can be
+ * {@linkplain com.oracle.truffle.api.instrument.ProbeInstrument#dispose() disposed}, at which time
+ * it is removed from service at every clone of the AST, incurs no further overhead, and is
+ * permanently unusable.</li>
+ * <li>Many clients need only implement a
+ * {@linkplain com.oracle.truffle.api.instrument.SimpleInstrumentListener SimpleInstrumentListener},
+ * whose notification methods provide only the instance of the
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} to which it was attached.  This
+ * provides access to the corresponding
+ * {@linkplain com.oracle.truffle.api.source.SourceSection source code location} and any
+ * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tags} that had been applied there.</li>
+ * <li>Clients that require deeper access to execution state implement a
+ * {@linkplain com.oracle.truffle.api.instrument.StandardInstrumentListener StandardInstrumentListener}
+ * whose notification methods provide access to the concrete
+ * {@linkplain com.oracle.truffle.api.nodes.Node AST location} and current
+ * {@linkplain com.oracle.truffle.api.frame.Frame stack frame}.</li>
+ * <li>Clients can also create an
+ * {@linkplain com.oracle.truffle.api.instrument.ProbeInstrument Instrument} (whose design is currently
+ * under revision) that supports (effectively) inserting a Truffle AST fragment into the AST
+ * location, where it will be executed subject to full Truffle optimization.</li>
+ * </ul></li>
+ *
+ * <li><strong>Wide-area Instrumentation: TagInstruments</strong>
+ * <ul>
+ * <li>A specialized form of Instrumentation is provided that efficiently attaches a single
+ * listener called a
+ * {@linkplain com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener StandardBeforeInstrumentListener} to every
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} containing a specified
+ * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tag}.</li>
+ * <li>One (but no more than one)
+ * {@linkplain com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener StandardBeforeInstrumentListener}
+ * may optionally be attached for notification <em>before</em> every <em>AST execution event</em> where the specified
+ * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tag} is present.</li>
+ * <li>One (but no more than one)
+ * {@linkplain com.oracle.truffle.api.instrument.StandardAfterInstrumentListener StandardAfterInstrumentListener}
+ * may optionally be attached for notification <em>after</em> every <em>AST execution event</em> where the specified
+ * {@linkplain com.oracle.truffle.api.instrument.SyntaxTag tag} is present.</li>
+ * <li>The
+ * {@linkplain com.oracle.truffle.api.instrument.TagInstrument TagInstrument} mechanism is independent
+ * of listeners that may be attached to
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probes}.  It is especially valuable for
+ * applications such as the debugger, where during "stepping" the program should be halted at
+ * any node tagged with
+ * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATEMENT}.</li>
+ * </ul></li>
+ *
+ * <li><strong>Data Gathering Utilities:  tools</strong>
+ * <ul>
+ * <li>The Instrumentation Framework supports extensible set of utilities that can be
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter#install(com.oracle.truffle.api.instrument.Instrumenter.Tool)
+ * installed}, at which time they start collecting information that can be queried at any time.</li>
+ * <li>Once installed, these
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter.Tool tools} can be dynamically
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter.Tool#setEnabled(boolean) disabled and re-enabled} and eventually
+ * {@linkplain com.oracle.truffle.api.instrument.Instrumenter.Tool#dispose() disposed} when no longer needed.</li>
+ * <li>A useful example is the
+ * {@linkplain com.oracle.truffle.api.debug.LineToProbesMap LineToProbesMap}, which incrementally builds and maintains a map of
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probes} indexed by
+ * {@linkplain com.oracle.truffle.api.source.LineLocation source code line}. Truffle
+ * {@linkplain com.oracle.truffle.api.debug.Debugger debugging services} depend heavily on this utility.</li>
+ * <li>The CoverageTracker maintains counts of execution events where a
+ * {@linkplain com.oracle.truffle.api.instrument.Probe Probe} has been tagged with
+ * {@linkplain com.oracle.truffle.api.instrument.StandardSyntaxTag#STATEMENT STATEMENT}, indexed by source code line.</li>
+ * </ul></li>
+ * </ul>
+ *
  */
 package com.oracle.truffle.api.instrument;
 
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,6 +24,15 @@
  */
 package com.oracle.truffle.api.nodes;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
@@ -32,22 +41,8 @@
 import com.oracle.truffle.api.TruffleLanguage;
 import com.oracle.truffle.api.TruffleOptions;
 import com.oracle.truffle.api.impl.Accessor;
-import com.oracle.truffle.api.instrument.Instrument;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeException;
-import com.oracle.truffle.api.instrument.ProbeFailure;
-import com.oracle.truffle.api.instrument.ProbeNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.api.utilities.JSONHelper;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.Callable;
 
 /**
  * Abstract base class for all Truffle nodes.
@@ -424,86 +419,6 @@
     }
 
     /**
-     * Any node for which this is {@code true} can be "instrumented" by installing a {@link Probe}
-     * that intercepts execution events at the node and routes them to any {@link Instrument}s that
-     * have been attached to the {@link Probe}. Only one {@link Probe} may be installed at each
-     * node; subsequent calls return the one already installed.
-     * <p>
-     * <b>Note:</b> instrumentation requires a appropriate {@link WrapperNode}, which must be
-     * provided by {@link #createWrapperNode()}.
-     *
-     * @see Instrument
-     */
-    public boolean isInstrumentable() {
-        return false;
-    }
-
-    /**
-     * For any node that {@link #isInstrumentable()}, this method must return a {@link Node} that:
-     * <ol>
-     * <li>implements {@link WrapperNode}</li>
-     * <li>has {@code this} as it's child, and</li>
-     * <li>whose type is safe for replacement of {@code this} in the parent.</li>
-     * </ol>
-     *
-     * @return an appropriately typed {@link WrapperNode} if {@link #isInstrumentable()}.
-     */
-    public WrapperNode createWrapperNode() {
-        return null;
-    }
-
-    /**
-     * Enables {@linkplain Instrument instrumentation} of a node, where the node is presumed to be
-     * part of a well-formed Truffle AST that is not being executed. If this node has not already
-     * been probed, modifies the AST by inserting a {@linkplain WrapperNode wrapper node} between
-     * the node and its parent; the wrapper node must be provided by implementations of
-     * {@link #createWrapperNode()}. No more than one {@link Probe} may be associated with a node,
-     * so a {@linkplain WrapperNode wrapper} may not wrap another {@linkplain WrapperNode wrapper}.
-     *
-     * @return a (possibly newly created) {@link Probe} associated with this node.
-     * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged
-     */
-    public final Probe probe() {
-
-        if (this instanceof WrapperNode) {
-            throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, this, null);
-        }
-
-        if (parent == null) {
-            throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, this, null);
-        }
-
-        if (parent instanceof WrapperNode) {
-            return ((WrapperNode) parent).getProbe();
-        }
-
-        if (!isInstrumentable()) {
-            throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, this, null);
-        }
-
-        // Create a new wrapper/probe with this node as its child.
-        final WrapperNode wrapper = createWrapperNode();
-
-        if (wrapper == null || !(wrapper instanceof Node)) {
-            throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, this, wrapper);
-        }
-
-        final Node wrapperNode = (Node) wrapper;
-
-        if (!this.isSafelyReplaceableBy(wrapperNode)) {
-            throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, this, wrapper);
-        }
-
-        // Connect it to a Probe
-        final Probe probe = ProbeNode.insertProbe(wrapper);
-
-        // Replace this node in the AST with the wrapper
-        this.replace(wrapperNode);
-
-        return probe;
-    }
-
-    /**
      * Converts this node to a textual representation useful for debugging.
      */
     @Override
@@ -603,14 +518,19 @@
         return true;
     }
 
-    private static final class AccessorNodes extends Accessor {
+    static final class AccessorNodes extends Accessor {
         @SuppressWarnings("rawtypes")
         @Override
         protected Class<? extends TruffleLanguage> findLanguage(RootNode n) {
             return n.language;
         }
+
+        @Override
+        protected void probeAST(RootNode rootNode) {
+            super.probeAST(rootNode);
+        }
     }
 
     // registers into Accessor.NODES
-    @SuppressWarnings("unused") private static final AccessorNodes ACCESSOR = new AccessorNodes();
+    static final AccessorNodes ACCESSOR = new AccessorNodes();
 }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java	Wed Sep 30 16:33:56 2015 -0700
@@ -42,9 +42,9 @@
 import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.TruffleOptions;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
 import com.oracle.truffle.api.instrument.SyntaxTag;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind;
 import com.oracle.truffle.api.source.SourceSection;
 
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -35,8 +35,6 @@
 import com.oracle.truffle.api.frame.FrameInstance;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.api.impl.DefaultCompilerOptions;
-import com.oracle.truffle.api.instrument.ASTProber;
-import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.source.SourceSection;
 
 /**
@@ -80,6 +78,11 @@
     /**
      * Creates new root node. Each {@link RootNode} is associated with a particular language - if
      * the root node represents a method it is assumed the method is written in such language.
+     * <p>
+     * <strong>Note:</strong> Although the {@link SourceSection} <em>can</em> be {@code null}, this
+     * is strongly discouraged for the purposes of testing/tracing/tooling. Please use
+     * {@link SourceSection#createUnavailable(String, String)} to create a descriptive instance with
+     * a language-specific <em>kind</em> such as "SL Builtin" and a <em>name</em> if possible.
      *
      * @param language the language of the node, <b>cannot be</b> <code>null</code>
      * @param sourceSection a part of source associated with this node, can be <code>null</code>
@@ -187,25 +190,22 @@
         }
     }
 
+    public final void applyInstrumentation() {
+        if (isInstrumentable()) {
+            Node.ACCESSOR.probeAST(this);
+        }
+    }
+
     /**
-     * Apply all registered instances of {@link ASTProber} to the AST, if any, held by this root
-     * node. This can only be done once the AST is complete, notably once all parent pointers are
-     * correctly assigned. But it also must be done before any AST cloning or execution.
-     * <p>
-     * If this is not done, then the AST will not be subject to debugging or any other
-     * instrumentation-supported tooling.
-     * <p>
-     * Implementations should ensure that instrumentation is never applied more than once to an AST,
-     * as this is not guaranteed to be error-free.
-     *
-     * @see Probe#registerASTProber(com.oracle.truffle.api.instrument.ASTProber)
+     * Does this contain AST content that it is possible to instrument.
      */
-    public void applyInstrumentation() {
+    protected boolean isInstrumentable() {
+        return true;
     }
 
     /**
      * Helper method to create a root node that always returns the same value. Certain operations
-     * (expecially {@link com.oracle.truffle.api.interop inter-operability} API) require return of
+     * (especially {@link com.oracle.truffle.api.interop inter-operability} API) require return of
      * stable {@link RootNode root nodes}. To simplify creation of such nodes, here is a factory
      * method that can create {@link RootNode} that returns always the same value.
      *
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,8 +24,6 @@
  */
 package com.oracle.truffle.api.source;
 
-import com.oracle.truffle.api.CompilerAsserts;
-import com.oracle.truffle.api.TruffleLanguage.Registration;
 import java.io.BufferedReader;
 import java.io.ByteArrayInputStream;
 import java.io.File;
@@ -54,6 +52,9 @@
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.oracle.truffle.api.CompilerAsserts;
+import com.oracle.truffle.api.TruffleLanguage.Registration;
+
 /**
  * Representation of a guest language source code unit and its contents. Sources originate in
  * several ways:
@@ -425,7 +426,7 @@
 
     /**
      * The URL if the source is retrieved via URL.
-     * 
+     *
      * @return URL or <code>null</code>
      */
     public abstract URL getURL();
@@ -909,6 +910,8 @@
                 return "text/x-c";
             } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) {
                 return "application/x-r";
+            } else if (file.getName().endsWith(".js") || file.getName().endsWith(".JS")) {
+                return "application/javascript";
             } else {
                 try {
                     return Files.probeContentType(file.toPath());
@@ -997,6 +1000,8 @@
                 return "text/x-c";
             } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) {
                 return "application/x-r";
+            } else if (file.getName().endsWith(".js") || file.getName().endsWith(".JS")) {
+                return "application/javascript";
             } else {
                 try {
                     return Files.probeContentType(file.toPath());
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceSection.java	Wed Sep 30 16:33:56 2015 -0700
@@ -98,7 +98,7 @@
 
     /**
      * Gets a representation of the first line of the section, suitable for a hash key.
-     * 
+     *
      * @return first line of the section
      */
     public LineLocation getLineLocation() {
@@ -175,7 +175,7 @@
      * @return the code as a String object
      */
     public String getCode() {
-        return getSource().getCode(charIndex, charLength);
+        return source == null ? "<unavailable>" : source.getCode(charIndex, charLength);
     }
 
     /**
@@ -262,6 +262,6 @@
      * @return source section which is mostly <em>empty</em>
      */
     public static SourceSection createUnavailable(String kind, String name) {
-        return new SourceSection(kind, null, name, -1, -1, -1, -1);
+        return new SourceSection(kind, null, name == null ? "<unknown>" : name, -1, -1, -1, -1);
     }
 }
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLJavaInteropTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLJavaInteropTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -44,11 +44,28 @@
 import com.oracle.truffle.api.interop.java.JavaInterop;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.sl.test.instrument.InstrumentationTestMode;
+
 import java.io.ByteArrayOutputStream;
+
 import static org.junit.Assert.assertEquals;
+
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 public class SLJavaInteropTest {
+
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void asFunction() throws Exception {
         String scriptText = "function main() {\n" + "    println(\"Called!\");\n" + "}\n";
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLTckTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -42,8 +42,13 @@
 
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.sl.test.instrument.InstrumentationTestMode;
 import com.oracle.truffle.tck.TruffleTCK;
+
 import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 
 /**
@@ -51,6 +56,17 @@
  *
  */
 public class SLTckTest extends TruffleTCK {
+
+    @Before
+    public void before() {
+        InstrumentationTestMode.set(true);
+    }
+
+    @After
+    public void after() {
+        InstrumentationTestMode.set(false);
+    }
+
     @Test
     public void testVerifyPresence() {
         PolyglotEngine vm = PolyglotEngine.buildNew().build();
@@ -176,4 +192,5 @@
     @Override
     public void testPrimitiveidentityFloat() throws Exception {
     }
+
 }
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java	Wed Sep 30 16:33:56 2015 -0700
@@ -40,22 +40,12 @@
  */
 package com.oracle.truffle.sl.test.instrument;
 
-import com.oracle.truffle.api.instrument.ASTProber;
-import com.oracle.truffle.api.instrument.Instrument;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.StandardSyntaxTag;
-import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.vm.PolyglotEngine;
-import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber;
-import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode;
-import com.oracle.truffle.sl.test.SLTestRunner;
-import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintStream;
+import java.lang.reflect.Field;
 import java.nio.charset.Charset;
 import java.nio.file.FileSystems;
 import java.nio.file.FileVisitResult;
@@ -65,6 +55,7 @@
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.List;
+
 import org.junit.Assert;
 import org.junit.internal.TextListener;
 import org.junit.runner.Description;
@@ -77,6 +68,18 @@
 import org.junit.runners.ParentRunner;
 import org.junit.runners.model.InitializationError;
 
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.StandardSyntaxTag;
+import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber;
+import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode;
+import com.oracle.truffle.sl.test.SLTestRunner;
+import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase;
+
 /**
  * This class builds and executes the tests for instrumenting SL. Although much of this class is
  * written with future automation in mind, at the moment the tests that are created are hard-coded
@@ -94,7 +97,7 @@
 
     private static final String LF = System.getProperty("line.separator");
 
-    static class InstrumentTestCase {
+    static final class InstrumentTestCase {
         protected final Description name;
         protected final Path path;
         protected final String baseName;
@@ -115,16 +118,12 @@
 
     private final List<InstrumentTestCase> testCases;
 
-    public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError {
+    public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError, SecurityException, IllegalArgumentException {
         super(testClass);
-        final SLStandardASTProber prober = new SLStandardASTProber();
-        Probe.registerASTProber(prober);
         try {
             testCases = createTests(testClass);
         } catch (IOException e) {
             throw new InitializationError(e);
-        } finally {
-            Probe.unregisterASTProber(prober);
         }
     }
 
@@ -236,20 +235,25 @@
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         PrintStream ps = new PrintStream(out);
         final ASTProber prober = new SLStandardASTProber();
-        Probe.registerASTProber(prober);
+
         try {
             // We use the name of the file to determine what visitor to attach to it.
             if (testCase.baseName.endsWith(ASSIGNMENT_VALUE_SUFFIX)) {
                 // Set up the execution context for Simple and register our two listeners
                 PolyglotEngine vm = PolyglotEngine.buildNew().setIn(new ByteArrayInputStream(testCase.testInput.getBytes("UTF-8"))).setOut(out).build();
 
+                final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+                field.setAccessible(true);
+                final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+                instrumenter.registerASTProber(prober);
+
                 final String src = readAllLines(testCase.path);
                 vm.eval(Source.fromText(src, testCase.path.toString()).withMimeType("application/x-sl"));
 
                 // Attach an instrument to every probe tagged as an assignment
-                for (Probe probe : Probe.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) {
+                for (Probe probe : instrumenter.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) {
                     SLPrintAssigmentValueListener slPrintAssigmentValueListener = new SLPrintAssigmentValueListener(ps);
-                    probe.attach(Instrument.create(slPrintAssigmentValueListener, "SL print assignment value"));
+                    instrumenter.attach(probe, slPrintAssigmentValueListener, "SL print assignment value");
                 }
 
                 PolyglotEngine.Value main = vm.findGlobalSymbol("main");
@@ -263,13 +267,12 @@
         } catch (Throwable ex) {
             notifier.fireTestFailure(new Failure(testCase.name, ex));
         } finally {
-            Probe.unregisterASTProber(prober);
             notifier.fireTestFinished(testCase.name);
         }
 
     }
 
-    public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException {
+    public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException, SecurityException, IllegalArgumentException {
         JUnitCore core = new JUnitCore();
         core.addListener(new TextListener(System.out));
         SLInstrumentTestRunner suite = new SLInstrumentTestRunner(testClass);
@@ -314,7 +317,7 @@
         }
 
         @Override
-        public void returnValue(Probe probe, Object result) {
+        public void onReturnValue(Probe probe, Object result) {
             output.println(result);
         }
     }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java	Wed Sep 30 16:33:56 2015 -0700
@@ -40,21 +40,26 @@
  */
 package com.oracle.truffle.sl;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.math.BigInteger;
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.List;
+
 import com.oracle.truffle.api.CallTarget;
 import com.oracle.truffle.api.RootCallTarget;
 import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.debug.DebugSupportException;
-import com.oracle.truffle.api.debug.DebugSupportProvider;
 import com.oracle.truffle.api.dsl.NodeFactory;
 import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
 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.Probe;
-import com.oracle.truffle.api.instrument.ToolSupportProvider;
 import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.GraphPrintVisitor;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeInfo;
@@ -68,7 +73,9 @@
 import com.oracle.truffle.sl.builtins.SLNanoTimeBuiltin;
 import com.oracle.truffle.sl.builtins.SLPrintlnBuiltin;
 import com.oracle.truffle.sl.builtins.SLReadlnBuiltin;
+import com.oracle.truffle.sl.nodes.SLExpressionNode;
 import com.oracle.truffle.sl.nodes.SLRootNode;
+import com.oracle.truffle.sl.nodes.SLStatementNode;
 import com.oracle.truffle.sl.nodes.SLTypes;
 import com.oracle.truffle.sl.nodes.call.SLDispatchNode;
 import com.oracle.truffle.sl.nodes.call.SLInvokeNode;
@@ -92,7 +99,9 @@
 import com.oracle.truffle.sl.nodes.expression.SLStringLiteralNode;
 import com.oracle.truffle.sl.nodes.expression.SLSubNode;
 import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer;
+import com.oracle.truffle.sl.nodes.instrument.SLExpressionWrapperNode;
 import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber;
+import com.oracle.truffle.sl.nodes.instrument.SLStatementWrapperNode;
 import com.oracle.truffle.sl.nodes.local.SLReadLocalVariableNode;
 import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode;
 import com.oracle.truffle.sl.parser.Parser;
@@ -102,14 +111,6 @@
 import com.oracle.truffle.sl.runtime.SLFunction;
 import com.oracle.truffle.sl.runtime.SLFunctionRegistry;
 import com.oracle.truffle.sl.runtime.SLNull;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.math.BigInteger;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
 
 /**
  * SL is a simple language to demonstrate and showcase features of Truffle. The implementation is as
@@ -186,20 +187,20 @@
  */
 
 /*
- * 
+ *
  * <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
- * simple {@linkplain CoverageTracker code coverage engine}.</li> </ul> In each case, the tool is
- * enabled if a corresponding local boolean variable in this file is set to {@code true}. Results
- * are printed at the end of the execution using each tool's <em>default printer</em>.
+ * Truffle {@linkplain Instrumenter 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 simple {@linkplain CoverageTracker code coverage engine}.</li> </ul> In each
+ * case, the tool is enabled if a corresponding local boolean variable in this file is set to {@code
+ * true}. Results are printed at the end of the execution using each tool's <em>default
+ * printer</em>.
  */
 @TruffleLanguage.Registration(name = "SL", version = "0.5", mimeType = "application/x-sl")
 public final class SLLanguage extends TruffleLanguage<SLContext> {
+    public static final String builtinKind = "SL builtin";
     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 SLLanguage() {
     }
@@ -214,6 +215,7 @@
         for (NodeFactory<? extends SLBuiltinNode> builtin : builtins) {
             context.installBuiltin(builtin, true);
         }
+        env.instrumenter().registerASTProber(new SLStandardASTProber());
         return context;
     }
 
@@ -256,6 +258,7 @@
     /**
      * Temporary method during API evolution, supports debugger integration.
      */
+    @Deprecated
     public static void run(Source source) throws IOException {
         PolyglotEngine vm = PolyglotEngine.buildNew().build();
         assert vm.getLanguages().containsKey("application/x-sl");
@@ -478,28 +481,49 @@
     }
 
     @Override
-    protected ToolSupportProvider getToolSupport() {
-        return getDebugSupport();
+    protected Visualizer getVisualizer() {
+        if (visualizer == null) {
+            visualizer = new SLDefaultVisualizer();
+        }
+        return visualizer;
+    }
+
+    @Override
+    protected boolean isInstrumentable(Node node) {
+        return node instanceof SLStatementNode;
     }
 
     @Override
-    protected DebugSupportProvider getDebugSupport() {
-        if (debugSupport == null) {
-            debugSupport = new SLDebugProvider();
+    protected WrapperNode createWrapperNode(Node node) {
+        if (node instanceof SLExpressionNode) {
+            return new SLExpressionWrapperNode((SLExpressionNode) node);
+        }
+        if (node instanceof SLStatementNode) {
+            return new SLStatementWrapperNode((SLStatementNode) node);
         }
-        return debugSupport;
+        return null;
+    }
+
+    @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");
+    }
+
+    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 PolyglotEngine
-        // Probe.registerASTProber(newProber);
-        // registeredASTProber = newProber;
-        // }
-        // }
         // if (nodeExecCounts) {
         // nodeExecCounter = new NodeExecCounter();
         // nodeExecCounter.install();
@@ -531,46 +555,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 PolyglotEngine
-                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 PolyglotEngine
-                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/builtins/SLAssertFalseBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertFalseBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -45,6 +45,7 @@
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.sl.SLAssertionError;
+import com.oracle.truffle.sl.SLLanguage;
 import com.oracle.truffle.sl.runtime.SLNull;
 
 /**
@@ -55,7 +56,7 @@
 public abstract class SLAssertFalseBuiltin extends SLBuiltinNode {
 
     public SLAssertFalseBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "assertFalse"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "assertFalse"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertTrueBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLAssertTrueBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -45,6 +45,7 @@
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.sl.SLAssertionError;
+import com.oracle.truffle.sl.SLLanguage;
 import com.oracle.truffle.sl.runtime.SLNull;
 
 /**
@@ -55,7 +56,7 @@
 public abstract class SLAssertTrueBuiltin extends SLBuiltinNode {
 
     public SLAssertTrueBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "assertTrue"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "assertTrue"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLDefineFunctionBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -45,6 +45,7 @@
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.sl.SLLanguage;
 import com.oracle.truffle.sl.parser.Parser;
 import com.oracle.truffle.sl.runtime.SLContext;
 
@@ -56,7 +57,7 @@
 public abstract class SLDefineFunctionBuiltin extends SLBuiltinNode {
 
     public SLDefineFunctionBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "defineFunction"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "defineFunction"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLHelloEqualsWorldBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLHelloEqualsWorldBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -49,6 +49,7 @@
 import com.oracle.truffle.api.frame.FrameSlot;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.sl.SLLanguage;
 
 /**
  * This builtin sets the variable named "hello" in the caller frame to the string "world".
@@ -57,7 +58,7 @@
 public abstract class SLHelloEqualsWorldBuiltin extends SLBuiltinNode {
 
     public SLHelloEqualsWorldBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "helloEqualsWorld"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "helloEqualsWorld"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNanoTimeBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNanoTimeBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -43,6 +43,7 @@
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.sl.SLLanguage;
 
 /**
  * Builtin function that returns the value of a high-resolution time, in nanoseconds.
@@ -51,7 +52,7 @@
 public abstract class SLNanoTimeBuiltin extends SLBuiltinNode {
 
     public SLNanoTimeBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "nanoTime"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "nanoTime"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLNewObjectBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -43,6 +43,7 @@
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.sl.SLLanguage;
 
 /**
  * Built-in function to create a new object. Objects in SL are simply made up of name/value pairs.
@@ -51,7 +52,7 @@
 public abstract class SLNewObjectBuiltin extends SLBuiltinNode {
 
     public SLNewObjectBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "new"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "new"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLPrintlnBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLPrintlnBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -44,7 +44,9 @@
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.sl.SLLanguage;
 import com.oracle.truffle.sl.runtime.SLContext;
+
 import java.io.PrintWriter;
 
 /**
@@ -60,7 +62,7 @@
 public abstract class SLPrintlnBuiltin extends SLBuiltinNode {
 
     public SLPrintlnBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "println"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "println"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLReadlnBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLReadlnBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -45,7 +45,9 @@
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.sl.SLException;
+import com.oracle.truffle.sl.SLLanguage;
 import com.oracle.truffle.sl.runtime.SLContext;
+
 import java.io.BufferedReader;
 import java.io.IOException;
 
@@ -56,7 +58,7 @@
 public abstract class SLReadlnBuiltin extends SLBuiltinNode {
 
     public SLReadlnBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "readln"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "readln"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/builtins/SLStackTraceBuiltin.java	Wed Sep 30 16:33:56 2015 -0700
@@ -54,6 +54,7 @@
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.api.source.SourceSection;
+import com.oracle.truffle.sl.SLLanguage;
 
 /**
  * Returns a string representation of the current stack. This includes the {@link CallTarget}s and
@@ -64,7 +65,7 @@
 public abstract class SLStackTraceBuiltin extends SLBuiltinNode {
 
     public SLStackTraceBuiltin() {
-        super(SourceSection.createUnavailable("SL builtin", "stacktrace"));
+        super(SourceSection.createUnavailable(SLLanguage.builtinKind, "stacktrace"));
     }
 
     @Specialization
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -42,11 +42,9 @@
 
 import com.oracle.truffle.api.dsl.TypeSystemReference;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.nodes.UnexpectedResultException;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.sl.nodes.instrument.SLExpressionWrapperNode;
 import com.oracle.truffle.sl.runtime.SLFunction;
 
 /**
@@ -94,15 +92,4 @@
     public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException {
         return SLTypesGen.expectBoolean(executeGeneric(frame));
     }
-
-    @Override
-    public boolean isInstrumentable() {
-        return true;
-    }
-
-    @Override
-    public WrapperNode createWrapperNode() {
-        return new SLExpressionWrapperNode(this);
-    }
-
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -43,9 +43,9 @@
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.sl.SLLanguage;
 import com.oracle.truffle.sl.builtins.SLBuiltinNode;
 import com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode;
@@ -69,8 +69,8 @@
     @CompilationFinal private boolean isCloningAllowed;
 
     @SuppressWarnings("unused")
-    public SLRootNode(SLContext ignore, FrameDescriptor frameDescriptor, SLExpressionNode bodyNode, String name) {
-        super(SLLanguage.class, null, frameDescriptor);
+    public SLRootNode(SLContext ignore, FrameDescriptor frameDescriptor, SLExpressionNode bodyNode, SourceSection sourceSection, String name) {
+        super(SLLanguage.class, sourceSection, frameDescriptor);
         this.bodyNode = bodyNode;
         this.name = name;
     }
@@ -89,21 +89,12 @@
         this.isCloningAllowed = isCloningAllowed;
     }
 
-    public SLExpressionNode getBodyNode() {
-        return bodyNode;
-    }
-
     @Override
     public boolean isCloningAllowed() {
         return isCloningAllowed;
     }
 
     @Override
-    public void applyInstrumentation() {
-        Probe.applyASTProbers(bodyNode);
-    }
-
-    @Override
     public String toString() {
         return "root " + name;
     }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -40,13 +40,12 @@
  */
 package com.oracle.truffle.sl.nodes;
 
+import java.io.File;
+
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeInfo;
 import com.oracle.truffle.api.source.SourceSection;
-import com.oracle.truffle.sl.nodes.instrument.SLStatementWrapperNode;
-import java.io.File;
 
 /**
  * The base class of all Truffle nodes for SL. All nodes (even expressions) can be used as
@@ -101,15 +100,4 @@
             return String.format("%s:%d%s", sourceName, startLine, estimated ? "~" : "");
         }
     }
-
-    @Override
-    public boolean isInstrumentable() {
-        return true;
-    }
-
-    @Override
-    public WrapperNode createWrapperNode() {
-        return new SLStatementWrapperNode(this);
-    }
-
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -41,11 +41,12 @@
 package com.oracle.truffle.sl.nodes.instrument;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.EventHandlerNode;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.KillException;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.ProbeInstrument;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
@@ -56,14 +57,14 @@
 
 /**
  * A Truffle node that can be inserted into a Simple AST (assumed not to have executed yet) to
- * enable "instrumentation" of an {@link SLExpressionNode}. Tools wishing to interact with AST
- * execution may attach {@link Instrument}s to the {@link Probe} uniquely associated with the
- * wrapper, and to which this wrapper routes execution events.
+ * enable {@linkplain Instrumenter Instrumentation} of an {@link SLExpressionNode}. Tools wishing to
+ * interact with AST execution may attach {@link ProbeInstrument}s to the {@link Probe} uniquely
+ * associated with the wrapper, and to which this wrapper routes execution events.
  */
 @NodeInfo(cost = NodeCost.NONE)
 public final class SLExpressionWrapperNode extends SLExpressionNode implements WrapperNode {
     @Child private SLExpressionNode child;
-    @Child private ProbeNode probeNode;
+    @Child private EventHandlerNode eventHandlerNode;
 
     /**
      * Constructor.
@@ -81,21 +82,16 @@
     }
 
     @Override
-    public boolean isInstrumentable() {
-        return false;
-    }
-
-    @Override
     public SLExpressionNode getNonWrapperNode() {
         return child;
     }
 
-    public void insertProbe(ProbeNode newProbeNode) {
-        this.probeNode = newProbeNode;
+    public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+        this.eventHandlerNode = eventHandler;
     }
 
     public Probe getProbe() {
-        return probeNode.getProbe();
+        return eventHandlerNode.getProbe();
     }
 
     public Node getChild() {
@@ -105,16 +101,16 @@
     @Override
     public Object executeGeneric(VirtualFrame vFrame) {
 
-        probeNode.enter(child, vFrame);
+        eventHandlerNode.enter(child, vFrame);
         Object result;
 
         try {
             result = child.executeGeneric(vFrame);
-            probeNode.returnValue(child, vFrame, result);
+            eventHandlerNode.returnValue(child, vFrame, result);
         } catch (KillException e) {
             throw (e);
         } catch (Exception e) {
-            probeNode.returnExceptional(child, vFrame, e);
+            eventHandlerNode.returnExceptional(child, vFrame, e);
             throw (e);
         }
         return result;
@@ -132,14 +128,14 @@
 
     @Override
     public SLFunction executeFunction(VirtualFrame vFrame) throws UnexpectedResultException {
-        probeNode.enter(child, vFrame);
+        eventHandlerNode.enter(child, vFrame);
         SLFunction result;
 
         try {
             result = child.executeFunction(vFrame);
-            probeNode.returnValue(child, vFrame, result);
+            eventHandlerNode.returnValue(child, vFrame, result);
         } catch (Exception e) {
-            probeNode.returnExceptional(child, vFrame, e);
+            eventHandlerNode.returnExceptional(child, vFrame, e);
             throw (e);
         }
         return result;
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java	Wed Sep 30 16:33:56 2015 -0700
@@ -40,14 +40,17 @@
  */
 package com.oracle.truffle.sl.nodes.instrument;
 
-import com.oracle.truffle.api.instrument.ASTProber;
-import com.oracle.truffle.api.instrument.InstrumentationNode;
-import com.oracle.truffle.api.instrument.Probe;
 import static com.oracle.truffle.api.instrument.StandardSyntaxTag.ASSIGNMENT;
 import static com.oracle.truffle.api.instrument.StandardSyntaxTag.START_LOOP;
 import static com.oracle.truffle.api.instrument.StandardSyntaxTag.STATEMENT;
+
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.InstrumentationNode;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.Probe;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeVisitor;
+import com.oracle.truffle.api.nodes.RootNode;
 import com.oracle.truffle.sl.nodes.SLExpressionNode;
 import com.oracle.truffle.sl.nodes.SLStatementNode;
 import com.oracle.truffle.sl.nodes.controlflow.SLWhileNode;
@@ -55,41 +58,41 @@
 
 /**
  * A visitor which traverses a completely parsed Simple AST (presumed not yet executed) and enables
- * instrumentation at a few standard kinds of nodes.
+ * {@linkplain Instrumenter 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, RootNode startNode) {
+        startNode.accept(new NodeVisitor() {
 
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Instruments and tags all relevant {@link SLStatementNode}s and {@link SLExpressionNode}s.
-     * Currently, only SLStatementNodes that are not SLExpressionNodes are tagged as statements.
-     */
-    public boolean visit(Node node) {
+            /**
+             * Instruments and tags all relevant {@link SLStatementNode}s and
+             * {@link SLExpressionNode}s. Currently, only SLStatementNodes that are not
+             * SLExpressionNodes are tagged as statements.
+             */
+            public boolean visit(Node node) {
 
-        if (!(node instanceof InstrumentationNode) && node instanceof SLStatementNode && node.getParent() != null && node.getSourceSection() != null) {
-            // All SL nodes are instrumentable, but treat expressions specially
+                if (!(node instanceof InstrumentationNode) && node instanceof SLStatementNode && node.getParent() != null && node.getSourceSection() != null) {
+                    // All SL nodes are instrumentable, but treat expressions specially
 
-            if (node instanceof SLExpressionNode) {
-                SLExpressionNode expressionNode = (SLExpressionNode) node;
-                Probe probe = expressionNode.probe();
-                if (node instanceof SLWriteLocalVariableNode) {
-                    probe.tagAs(STATEMENT, null);
-                    probe.tagAs(ASSIGNMENT, null);
+                    if (node instanceof SLExpressionNode) {
+                        SLExpressionNode expressionNode = (SLExpressionNode) node;
+                        final Probe probe = instrumenter.probe(expressionNode);
+                        if (node instanceof SLWriteLocalVariableNode) {
+                            probe.tagAs(STATEMENT, null);
+                            probe.tagAs(ASSIGNMENT, null);
+                        }
+                    } else {
+                        SLStatementNode statementNode = (SLStatementNode) node;
+                        final Probe probe = instrumenter.probe(statementNode);
+                        probe.tagAs(STATEMENT, null);
+                        if (node instanceof SLWhileNode) {
+                            probe.tagAs(START_LOOP, null);
+                        }
+                    }
                 }
-            } else {
-                SLStatementNode statementNode = (SLStatementNode) node;
-                Probe probe = statementNode.probe();
-                probe.tagAs(STATEMENT, null);
-                if (node instanceof SLWhileNode) {
-                    probe.tagAs(START_LOOP, null);
-                }
+                return true;
             }
-        }
-        return true;
-    }
-
-    public void probeAST(Node node) {
-        node.accept(this);
+        });
     }
 }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java	Wed Sep 30 16:33:56 2015 -0700
@@ -41,11 +41,12 @@
 package com.oracle.truffle.sl.nodes.instrument;
 
 import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.Instrument;
+import com.oracle.truffle.api.instrument.EventHandlerNode;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.KillException;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
+import com.oracle.truffle.api.instrument.ProbeInstrument;
+import com.oracle.truffle.api.instrument.WrapperNode;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.NodeCost;
 import com.oracle.truffle.api.nodes.NodeInfo;
@@ -53,15 +54,15 @@
 
 /**
  * A Truffle node that can be inserted into a Simple AST (assumed not to have executed yet) to
- * enable "instrumentation" of a {@link SLStatementNode}. Tools wishing to interact with AST
- * execution may attach {@link Instrument}s to the {@link Probe} uniquely associated with the
- * wrapper, and to which this wrapper routes execution events.
+ * enable {@linkplain Instrumenter Instrumentation} of a {@link SLStatementNode}. Tools wishing to
+ * interact with AST execution may attach {@link ProbeInstrument}s to the {@link Probe} uniquely
+ * associated with the wrapper, and to which this wrapper routes execution events.
  */
 @NodeInfo(cost = NodeCost.NONE)
 public final class SLStatementWrapperNode extends SLStatementNode implements WrapperNode {
 
     @Child private SLStatementNode child;
-    @Child private ProbeNode probeNode;
+    @Child private EventHandlerNode eventHandlerNode;
 
     public SLStatementWrapperNode(SLStatementNode child) {
         super(child.getSourceSection());
@@ -74,21 +75,17 @@
     }
 
     @Override
-    public boolean isInstrumentable() {
-        return false;
-    }
-
-    @Override
     public SLStatementNode getNonWrapperNode() {
         return child;
     }
 
-    public void insertProbe(ProbeNode newProbeNode) {
-        this.probeNode = newProbeNode;
+    @Override
+    public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+        this.eventHandlerNode = eventHandler;
     }
 
     public Probe getProbe() {
-        return probeNode.getProbe();
+        return eventHandlerNode.getProbe();
     }
 
     @Override
@@ -98,15 +95,15 @@
 
     @Override
     public void executeVoid(VirtualFrame vFrame) {
-        probeNode.enter(child, vFrame);
+        eventHandlerNode.enter(child, vFrame);
 
         try {
             child.executeVoid(vFrame);
-            probeNode.returnVoid(child, vFrame);
+            eventHandlerNode.returnVoid(child, vFrame);
         } catch (KillException e) {
             throw (e);
         } catch (Exception e) {
-            probeNode.returnExceptional(child, vFrame, e);
+            eventHandlerNode.returnExceptional(child, vFrame, e);
             throw (e);
         }
     }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java	Wed Sep 30 16:33:56 2015 -0700
@@ -40,8 +40,15 @@
  */
 package com.oracle.truffle.sl.parser;
 
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 import com.oracle.truffle.api.frame.FrameDescriptor;
 import com.oracle.truffle.api.frame.FrameSlot;
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
@@ -80,11 +87,6 @@
 import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode;
 import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNodeGen;
 import com.oracle.truffle.sl.runtime.SLContext;
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 /**
  * Helper class used by the SL {@link Parser} to create nodes. The code is factored out of the
@@ -166,8 +168,7 @@
         assert lexicalScope == null : "Wrong scoping of blocks in parser";
 
         final SLFunctionBodyNode functionBodyNode = new SLFunctionBodyNode(functionSrc, methodBlock);
-        final SLRootNode rootNode = new SLRootNode(this.context, frameDescriptor, functionBodyNode, functionName);
-        rootNode.assignSourceSection(functionSrc);
+        final SLRootNode rootNode = new SLRootNode(this.context, frameDescriptor, functionBodyNode, functionSrc, functionName);
 
         context.getFunctionRegistry().register(functionName, rootNode);
 
@@ -346,7 +347,8 @@
     /**
      * Returns a {@link SLReadLocalVariableNode} if this read is a local variable or a
      * {@link SLFunctionLiteralNode} if this read is global. In Simple, the only global names are
-     * functions. </br> There is currently no instrumentation for this node.
+     * functions. </br> There is currently no instrumentation{@linkplain Instrumenter
+     * Instrumentation} for this node.
      *
      * @param nameToken The name of the variable/function being read
      * @return either:
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/runtime/SLContext.java	Wed Sep 30 16:33:56 2015 -0700
@@ -48,6 +48,7 @@
 import com.oracle.truffle.api.object.Layout;
 import com.oracle.truffle.api.object.Shape;
 import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.sl.SLLanguage;
 import com.oracle.truffle.sl.builtins.SLAssertFalseBuiltinFactory;
 import com.oracle.truffle.sl.builtins.SLAssertTrueBuiltinFactory;
@@ -66,6 +67,7 @@
 import com.oracle.truffle.sl.nodes.local.SLReadArgumentNode;
 import com.oracle.truffle.sl.parser.Parser;
 import com.oracle.truffle.sl.parser.SLNodeFactory;
+
 import java.io.BufferedReader;
 import java.io.PrintWriter;
 import java.math.BigInteger;
@@ -170,8 +172,10 @@
         SLBuiltinNode builtinBodyNode = factory.createNode(argumentNodes, this);
         /* The name of the builtin function is specified via an annotation on the node class. */
         String name = lookupNodeInfo(builtinBodyNode.getClass()).shortName();
+
+        final SourceSection srcSection = SourceSection.createUnavailable(SLLanguage.builtinKind, name);
         /* Wrap the builtin in a RootNode. Truffle requires all AST to start with a RootNode. */
-        SLRootNode rootNode = new SLRootNode(this, new FrameDescriptor(), builtinBodyNode, name);
+        SLRootNode rootNode = new SLRootNode(this, new FrameDescriptor(), builtinBodyNode, srcSection, name);
 
         if (registerRootNodes) {
             /* Register the builtin function in our function registry. */
--- a/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.debug.shell/src/com/oracle/truffle/tools/debug/shell/server/REPLServerContext.java	Wed Sep 30 16:33:56 2015 -0700
@@ -84,10 +84,9 @@
     /**
      * @deprecated use {@link #engine()}.
      */
-    @SuppressWarnings("deprecation")
     @Deprecated
-    public com.oracle.truffle.api.vm.TruffleVM vm() {
-        return (com.oracle.truffle.api.vm.TruffleVM) engine();
+    public com.oracle.truffle.api.vm.PolyglotEngine vm() {
+        return engine();
     }
 
     protected abstract Debugger db();
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,25 +24,34 @@
  */
 package com.oracle.truffle.tools.test;
 
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.StandardSyntaxTag;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.tools.CoverageTracker;
-import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import org.junit.Test;
+import com.oracle.truffle.tools.test.ToolTestUtil.ToolTestTag;
 
 public class CoverageTrackerTest {
 
     @Test
-    public void testNoExecution() {
+    public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        instrumenter.registerASTProber(new ToolTestUtil.TestASTProber());
         final CoverageTracker tool = new CoverageTracker();
         assertEquals(tool.getCounts().entrySet().size(), 0);
-        tool.install();
+        instrumenter.install(tool);
         assertEquals(tool.getCounts().entrySet().size(), 0);
         tool.setEnabled(false);
         assertEquals(tool.getCounts().entrySet().size(), 0);
@@ -54,66 +63,56 @@
         assertEquals(tool.getCounts().entrySet().size(), 0);
     }
 
-    @Test
-    public void testToolCreatedTooLate() {
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        final CoverageTracker tool = new CoverageTracker();
-        tool.install();
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
-        tool.dispose();
-    }
-
-    @Test
-    public void testToolInstalledcTooLate() {
-        final CoverageTracker tool = new CoverageTracker();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        tool.install();
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
-        tool.dispose();
+    void checkCounts(Source source, CoverageTracker coverage, Long[] expectedCounts) {
+        final Map<Source, Long[]> countMap = coverage.getCounts();
+        assertEquals(countMap.size(), 1);
+        final Long[] resultCounts = countMap.get(source);
+        assertTrue(Arrays.equals(resultCounts, expectedCounts));
     }
 
     @Test
-    public void testCountingCoverage() {
-        final CoverageTracker tool = new CoverageTracker();
-        tool.install();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
+    public void testCountingCoverage() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        instrumenter.registerASTProber(new ToolTestUtil.TestASTProber());
+        final Source source = ToolTestUtil.createTestSource("testCountingCoverage");
 
-        // Not probed yet.
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
+        final CoverageTracker valueCoverage = new CoverageTracker(ToolTestTag.VALUE_TAG);
+        final CoverageTracker addCoverage = new CoverageTracker(ToolTestTag.ADD_TAG);
 
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
+        instrumenter.install(valueCoverage);
+        assertTrue(valueCoverage.getCounts().isEmpty());
+
+        assertEquals(vm.eval(source).get(), 13);
 
-        // Probed but not tagged yet.
-        assertEquals(13, expr13rootNode.execute(null));
-        assertTrue(tool.getCounts().isEmpty());
+        checkCounts(source, valueCoverage, new Long[]{Long.valueOf(1), null, Long.valueOf(1), null});
 
-        probe.tagAs(StandardSyntaxTag.STATEMENT, "fake statement for testing");
+        instrumenter.install(addCoverage);
 
-        // Counting now; execute once
-        assertEquals(13, expr13rootNode.execute(null));
+        assertEquals(vm.eval(source).get(), 13);
 
-        final Long[] longs1 = tool.getCounts().get(addNode.getSourceSection().getSource());
-        assertNotNull(longs1);
-        assertEquals(longs1.length, 2);
-        assertNull(longs1[0]);  // Line 1 is empty (text lines are 1-based)
-        assertEquals(1L, longs1[1].longValue());  // Expression is on line 2
+        checkCounts(source, valueCoverage, new Long[]{Long.valueOf(2), null, Long.valueOf(2), null});
+        checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(1), null, null});
+
+        valueCoverage.setEnabled(false);
+        assertEquals(vm.eval(source).get(), 13);
 
-        // Execute 99 more times
-        for (int i = 0; i < 99; i++) {
-            assertEquals(13, expr13rootNode.execute(null));
-        }
+        checkCounts(source, valueCoverage, new Long[]{Long.valueOf(2), null, Long.valueOf(2), null});
+        checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(2), null, null});
+
+        valueCoverage.setEnabled(true);
+        assertEquals(vm.eval(source).get(), 13);
 
-        final Long[] longs100 = tool.getCounts().get(addNode.getSourceSection().getSource());
-        assertNotNull(longs100);
-        assertEquals(longs100.length, 2);
-        assertNull(longs100[0]);
-        assertEquals(100L, longs100[1].longValue());
+        checkCounts(source, valueCoverage, new Long[]{Long.valueOf(3), null, Long.valueOf(3), null});
+        checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(3), null, null});
+
+        valueCoverage.dispose();
+        assertEquals(vm.eval(source).get(), 13);
 
-        tool.dispose();
+        checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(4), null, null});
+
+        addCoverage.dispose();
     }
-
 }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,67 +24,100 @@
  */
 package com.oracle.truffle.tools.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.source.LineLocation;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.tools.LineToProbesMap;
-import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode;
-import static com.oracle.truffle.tools.test.TestNodes.expr13Line1;
-import static com.oracle.truffle.tools.test.TestNodes.expr13Line2;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import org.junit.Test;
+import com.oracle.truffle.tools.test.ToolTestUtil.ToolTestTag;
 
 public class LineToProbesMapTest {
 
     @Test
-    public void testToolCreatedTooLate() {
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
-        final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
-        assertEquals(lineLocation, expr13Line2);
-
-        final LineToProbesMap tool = new LineToProbesMap();
-        tool.install();
-
-        assertNull(tool.findFirstProbe(expr13Line1));
-        assertNull(tool.findFirstProbe(expr13Line2));
-        tool.dispose();
+    public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        instrumenter.registerASTProber(new ToolTestUtil.TestASTProber());
+        final Source source = ToolTestUtil.createTestSource("testNoExecution");
+        final LineToProbesMap probesMap = new LineToProbesMap();
+        instrumenter.install(probesMap);
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(1)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(2)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(3)));
+        probesMap.dispose();
     }
 
     @Test
-    public void testToolInstalledTooLate() {
-        final LineToProbesMap tool = new LineToProbesMap();
+    public void testMapping1() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        instrumenter.registerASTProber(new ToolTestUtil.TestASTProber());
+        final Source source = ToolTestUtil.createTestSource("testMapping1");
+        final LineToProbesMap probesMap = new LineToProbesMap();
+
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(1)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(2)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(3)));
 
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
-        final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
-        assertEquals(lineLocation, expr13Line2);
+        // Map installed before AST gets created
+        instrumenter.install(probesMap);
+        assertEquals(vm.eval(source).get(), 13);
 
-        tool.install();
+        final Probe probe1 = probesMap.findFirstProbe(source.createLineLocation(1));
+        assertNotNull(probe1);
+        assertTrue(probe1.isTaggedAs(ToolTestTag.VALUE_TAG));
+        final Probe probe2 = probesMap.findFirstProbe(source.createLineLocation(2));
+        assertNotNull(probe2);
+        assertTrue(probe2.isTaggedAs(ToolTestTag.ADD_TAG));
+        final Probe probe3 = probesMap.findFirstProbe(source.createLineLocation(3));
+        assertNotNull(probe3);
+        assertTrue(probe3.isTaggedAs(ToolTestTag.VALUE_TAG));
 
-        assertNull(tool.findFirstProbe(expr13Line1));
-        assertNull(tool.findFirstProbe(expr13Line2));
-        tool.dispose();
+        probesMap.dispose();
     }
 
     @Test
-    public void testMapping() {
-        final LineToProbesMap tool = new LineToProbesMap();
-        tool.install();
+    public void testMapping2() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        instrumenter.registerASTProber(new ToolTestUtil.TestASTProber());
+        final Source source = ToolTestUtil.createTestSource("testMapping2");
+        final LineToProbesMap probesMap = new LineToProbesMap();
+
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(1)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(2)));
+        assertNull(probesMap.findFirstProbe(source.createLineLocation(3)));
 
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
-        final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation();
-        assertEquals(lineLocation, expr13Line2);
+        // Map installed after AST gets created
+        assertEquals(vm.eval(source).get(), 13);
+        instrumenter.install(probesMap);
 
-        assertNull(tool.findFirstProbe(expr13Line1));
-        assertEquals(tool.findFirstProbe(expr13Line2), probe);
-        tool.dispose();
+        final Probe probe1 = probesMap.findFirstProbe(source.createLineLocation(1));
+        assertNotNull(probe1);
+        assertTrue(probe1.isTaggedAs(ToolTestTag.VALUE_TAG));
+        final Probe probe2 = probesMap.findFirstProbe(source.createLineLocation(2));
+        assertNotNull(probe2);
+        assertTrue(probe2.isTaggedAs(ToolTestTag.ADD_TAG));
+        final Probe probe3 = probesMap.findFirstProbe(source.createLineLocation(3));
+        assertNotNull(probe3);
+        assertTrue(probe3.isTaggedAs(ToolTestTag.VALUE_TAG));
+
+        probesMap.dispose();
     }
-
 }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,29 +24,31 @@
  */
 package com.oracle.truffle.tools.test;
 
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.StandardSyntaxTag;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.RootNode;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.vm.PolyglotEngine;
 import com.oracle.truffle.tools.NodeExecCounter;
 import com.oracle.truffle.tools.NodeExecCounter.NodeExecutionCount;
-import com.oracle.truffle.tools.test.TestNodes.TestAddNode;
-import com.oracle.truffle.tools.test.TestNodes.TestValueNode;
-import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestCallTarget;
-import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-import org.junit.Test;
 
 public class NodeExecCounterTest {
 
     @Test
-    public void testNoExecution() {
+    public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
         final NodeExecCounter tool = new NodeExecCounter();
         assertEquals(tool.getCounts().length, 0);
-        tool.install();
+        instrumenter.install(tool);
         assertEquals(tool.getCounts().length, 0);
         tool.setEnabled(false);
         assertEquals(tool.getCounts().length, 0);
@@ -58,113 +60,45 @@
         assertEquals(tool.getCounts().length, 0);
     }
 
-    @Test
-    public void testToolCreatedTooLate() {
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
-        final NodeExecCounter tool = new NodeExecCounter();
-        tool.install();
-        assertEquals(13, expr13callTarget.call());
-        assertEquals(tool.getCounts().length, 0);
-        tool.dispose();
-    }
-
-    @Test
-    public void testToolInstalledcTooLate() {
-        final NodeExecCounter tool = new NodeExecCounter();
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
-        tool.install();
-        assertEquals(13, expr13callTarget.call());
-        assertEquals(tool.getCounts().length, 0);
-        tool.dispose();
+    void checkCounts(NodeExecCounter execTool, int addCount, int valueCount) {
+        NodeExecutionCount[] counts = execTool.getCounts();
+        assertEquals(counts.length, 2);
+        for (NodeExecutionCount counter : counts) {
+            if (counter.nodeClass() == ToolTestUtil.TestAdditionNode.class) {
+                assertEquals(counter.executionCount(), addCount);
+            } else if (counter.nodeClass() == ToolTestUtil.TestValueNode.class) {
+                assertEquals(counter.executionCount(), valueCount);
+            } else {
+                fail("correct classes counted");
+            }
+        }
     }
 
     @Test
-    public void testCountingAll() {
-        final NodeExecCounter tool = new NodeExecCounter();
-        tool.install();
-        final CallTarget expr13callTarget = createExpr13TestCallTarget();
-
-        // execute once
-        assertEquals(13, expr13callTarget.call());
-        final NodeExecutionCount[] count1 = tool.getCounts();
-        assertNotNull(count1);
-        assertEquals(count1.length, 2);
-        for (NodeExecutionCount count : count1) {
-            final Class<?> class1 = count.nodeClass();
-            final long executionCount = count.executionCount();
-            if (class1 == TestAddNode.class) {
-                assertEquals(executionCount, 1);
-            } else if (class1 == TestValueNode.class) {
-                assertEquals(executionCount, 2);
-            } else {
-                fail();
-            }
-        }
+    public void testCounting() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        final Source source = ToolTestUtil.createTestSource("testCounting");
+        final NodeExecCounter execTool = new NodeExecCounter();
+        instrumenter.install(execTool);
 
-        // Execute 99 more times
-        for (int i = 0; i < 99; i++) {
-            assertEquals(13, expr13callTarget.call());
-        }
-        final NodeExecutionCount[] counts100 = tool.getCounts();
-        assertNotNull(counts100);
-        assertEquals(counts100.length, 2);
-        for (NodeExecutionCount count : counts100) {
-            final Class<?> class1 = count.nodeClass();
-            final long executionCount = count.executionCount();
-            if (class1 == TestAddNode.class) {
-                assertEquals(executionCount, 100);
-            } else if (class1 == TestValueNode.class) {
-                assertEquals(executionCount, 200);
-            } else {
-                fail();
-            }
-        }
+        assertEquals(execTool.getCounts().length, 0);
 
-        tool.dispose();
-    }
+        assertEquals(vm.eval(source).get(), 13);
 
-    @Test
-    public void testCountingTagged() {
-        final NodeExecCounter tool = new NodeExecCounter(StandardSyntaxTag.STATEMENT);
-        tool.install();
-        final RootNode expr13rootNode = createExpr13TestRootNode();
-
-        // Not probed yet.
-        assertEquals(13, expr13rootNode.execute(null));
-        assertEquals(tool.getCounts().length, 0);
-
-        final Node addNode = expr13rootNode.getChildren().iterator().next();
-        final Probe probe = addNode.probe();
-
-        // Probed but not tagged yet.
-        assertEquals(13, expr13rootNode.execute(null));
-        assertEquals(tool.getCounts().length, 0);
-
-        probe.tagAs(StandardSyntaxTag.STATEMENT, "fake statement for testing");
+        checkCounts(execTool, 1, 2);
 
-        // Counting now; execute once
-        assertEquals(13, expr13rootNode.execute(null));
-        final NodeExecutionCount[] counts1 = tool.getCounts();
-        assertNotNull(counts1);
-        assertEquals(counts1.length, 1);
-        final NodeExecutionCount count1 = counts1[0];
-        assertNotNull(count1);
-        assertEquals(count1.nodeClass(), addNode.getClass());
-        assertEquals(count1.executionCount(), 1);
+        for (int i = 0; i < 99; i++) {
+            assertEquals(vm.eval(source).get(), 13);
+        }
+        checkCounts(execTool, 100, 200);
 
-        // Execute 99 more times
-        for (int i = 0; i < 99; i++) {
-            assertEquals(13, expr13rootNode.execute(null));
-        }
+        execTool.setEnabled(false);
+        assertEquals(vm.eval(source).get(), 13);
+        checkCounts(execTool, 100, 200);
 
-        final NodeExecutionCount[] counts100 = tool.getCounts();
-        assertNotNull(counts100);
-        assertEquals(counts100.length, 1);
-        final NodeExecutionCount count100 = counts100[0];
-        assertNotNull(count100);
-        assertEquals(count100.nodeClass(), addNode.getClass());
-        assertEquals(count100.executionCount(), 100);
-
-        tool.dispose();
+        execTool.dispose();
     }
 }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java	Tue Sep 29 18:04:11 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,228 +0,0 @@
-/*
- * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.truffle.tools.test;
-
-import com.oracle.truffle.api.CallTarget;
-import com.oracle.truffle.api.Truffle;
-import com.oracle.truffle.api.TruffleLanguage;
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.KillException;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeNode;
-import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.NodeCost;
-import com.oracle.truffle.api.nodes.NodeInfo;
-import com.oracle.truffle.api.nodes.RootNode;
-import com.oracle.truffle.api.source.LineLocation;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.source.SourceSection;
-
-/**
- * Nodes and an {@linkplain CallTarget executable ASTs} for testing.
- */
-class TestNodes {
-
-    /**
-     * A fake source used for testing: empty line 1, expression on line 2.
-     */
-    public static final Source expr13Source = Source.fromText("\n6+7\n", "Test Source: expression on line 2 that evaluates to 13");
-    public static final LineLocation expr13Line1 = expr13Source.createLineLocation(1);
-    public static final LineLocation expr13Line2 = expr13Source.createLineLocation(2);
-
-    /**
-     * An executable addition expression that evaluates to 13.
-     */
-    static CallTarget createExpr13TestCallTarget() {
-        final RootNode rootNode = createExpr13TestRootNode();
-        return Truffle.getRuntime().createCallTarget(rootNode);
-    }
-
-    /**
-     * Root holding an addition expression that evaluates to 13.
-     */
-    static RootNode createExpr13TestRootNode() {
-        final TestLanguageNode ast = createExpr13AST();
-        final TestRootNode rootNode = new TestRootNode(ast);
-        rootNode.adoptChildren();
-        return rootNode;
-    }
-
-    /**
-     * Addition expression that evaluates to 13, with faked source attribution.
-     */
-    static TestLanguageNode createExpr13AST() {
-        final SourceSection leftSourceSection = expr13Source.createSection("left", 1, 1);
-        final TestValueNode leftValueNode = new TestValueNode(6, leftSourceSection);
-        final SourceSection rightSourceSection = expr13Source.createSection("right", 3, 1);
-        final TestValueNode rightValueNode = new TestValueNode(7, rightSourceSection);
-        final SourceSection exprSourceSection = expr13Source.createSection("expr", 1, 3);
-        return new TestAddNode(leftValueNode, rightValueNode, exprSourceSection);
-    }
-
-    abstract static class TestLanguageNode extends Node {
-        public abstract Object execute(VirtualFrame frame);
-
-        public TestLanguageNode() {
-        }
-
-        public TestLanguageNode(SourceSection srcSection) {
-            super(srcSection);
-        }
-
-        @Override
-        public boolean isInstrumentable() {
-            return true;
-        }
-
-        @Override
-        public WrapperNode createWrapperNode() {
-            return new TestWrapperNode(this);
-        }
-    }
-
-    @NodeInfo(cost = NodeCost.NONE)
-    static class TestWrapperNode extends TestLanguageNode implements WrapperNode {
-        @Child private TestLanguageNode child;
-        @Child private ProbeNode probeNode;
-
-        public TestWrapperNode(TestLanguageNode child) {
-            assert !(child instanceof TestWrapperNode);
-            this.child = child;
-        }
-
-        @Override
-        public String instrumentationInfo() {
-            return "Wrapper node for testing";
-        }
-
-        @Override
-        public boolean isInstrumentable() {
-            return false;
-        }
-
-        @Override
-        public void insertProbe(ProbeNode newProbeNode) {
-            this.probeNode = newProbeNode;
-        }
-
-        @Override
-        public Probe getProbe() {
-            return probeNode.getProbe();
-        }
-
-        @Override
-        public Node getChild() {
-            return child;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame) {
-            probeNode.enter(child, frame);
-            Object result;
-
-            try {
-                result = child.execute(frame);
-                probeNode.returnValue(child, frame, result);
-            } catch (KillException e) {
-                throw (e);
-            } catch (Exception e) {
-                probeNode.returnExceptional(child, frame, e);
-                throw (e);
-            }
-
-            return result;
-        }
-    }
-
-    /**
-     * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST
-     * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
-     * completes an AST. The root nodes serves as our entry point into a program.
-     */
-    static class TestRootNode extends RootNode {
-        @Child private TestLanguageNode body;
-
-        /**
-         * This constructor emulates the global machinery that applies registered probers to every
-         * newly created AST. Global registry is not used, since that would interfere with other
-         * tests run in the same environment.
-         */
-        public TestRootNode(TestLanguageNode body) {
-            super(TruffleLanguage.class, null, null);
-            this.body = body;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame) {
-            return body.execute(frame);
-        }
-
-        @Override
-        public boolean isCloningAllowed() {
-            return true;
-        }
-
-        @Override
-        public void applyInstrumentation() {
-            Probe.applyASTProbers(body);
-        }
-    }
-
-    static class TestValueNode extends TestLanguageNode {
-        private final int value;
-
-        public TestValueNode(int value) {
-            this.value = value;
-        }
-
-        public TestValueNode(int value, SourceSection srcSection) {
-            super(srcSection);
-            this.value = value;
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame) {
-            return new Integer(this.value);
-        }
-    }
-
-    static class TestAddNode extends TestLanguageNode {
-        @Child private TestLanguageNode leftChild;
-        @Child private TestLanguageNode rightChild;
-
-        public TestAddNode(TestValueNode leftChild, TestValueNode rightChild, SourceSection sourceSection) {
-            super(sourceSection);
-            this.leftChild = insert(leftChild);
-            this.rightChild = insert(rightChild);
-        }
-
-        @Override
-        public Object execute(VirtualFrame frame) {
-            return new Integer(((Integer) leftChild.execute(frame)).intValue() + ((Integer) rightChild.execute(frame)).intValue());
-        }
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/ToolTestUtil.java	Wed Sep 30 16:33:56 2015 -0700
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2015, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.tools.test;
+
+import java.io.IOException;
+
+import com.oracle.truffle.api.CallTarget;
+import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleLanguage;
+import com.oracle.truffle.api.TruffleRuntime;
+import com.oracle.truffle.api.frame.MaterializedFrame;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener;
+import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory;
+import com.oracle.truffle.api.instrument.EventHandlerNode;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.KillException;
+import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.SyntaxTag;
+import com.oracle.truffle.api.instrument.Visualizer;
+import com.oracle.truffle.api.instrument.WrapperNode;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.NodeCost;
+import com.oracle.truffle.api.nodes.NodeInfo;
+import com.oracle.truffle.api.nodes.NodeVisitor;
+import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+
+public class ToolTestUtil {
+
+    static final String MIME_TYPE = "text/x-toolTest";
+
+    static enum ToolTestTag implements SyntaxTag {
+
+        ADD_TAG("addition", "test language addition node"),
+
+        VALUE_TAG("value", "test language value node");
+
+        private final String name;
+        private final String description;
+
+        private ToolTestTag(String name, String description) {
+            this.name = name;
+            this.description = description;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public String getDescription() {
+            return description;
+        }
+    }
+
+    static Source createTestSource(String description) {
+        return Source.fromText("6\n+\n7\n" + description, description).withMimeType(MIME_TYPE);
+    }
+
+    @TruffleLanguage.Registration(name = "toolTestLanguage", version = "0", mimeType = MIME_TYPE)
+    public static final class ToolTestLang extends TruffleLanguage<Object> {
+
+        public static final ToolTestLang INSTANCE = new ToolTestLang();
+
+        private ToolTestLang() {
+        }
+
+        @Override
+        protected CallTarget parse(Source source, Node context, String... argumentNames) throws IOException {
+            final TestValueNode leftValueNode = new TestValueNode(6, source.createSection("6", 0, 1));
+            final TestValueNode rightValueNode = new TestValueNode(7, source.createSection("7", 4, 1));
+            final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode, source.createSection("+", 2, 1));
+            final InstrumentationTestRootNode rootNode = new InstrumentationTestRootNode(addNode);
+            final TruffleRuntime runtime = Truffle.getRuntime();
+            final CallTarget callTarget = runtime.createCallTarget(rootNode);
+            return callTarget;
+        }
+
+        @Override
+        protected Object findExportedSymbol(Object context, String globalName, boolean onlyExplicit) {
+            return null;
+        }
+
+        @Override
+        protected Object getLanguageGlobal(Object context) {
+            return null;
+        }
+
+        @Override
+        protected boolean isObjectOfLanguage(Object object) {
+            return false;
+        }
+
+        @Override
+        protected Visualizer getVisualizer() {
+            return null;
+        }
+
+        @Override
+        protected boolean isInstrumentable(Node node) {
+            return node instanceof TestAdditionNode || node instanceof TestValueNode;
+        }
+
+        @Override
+        protected WrapperNode createWrapperNode(Node node) {
+            if (isInstrumentable(node)) {
+                return new ToolTestWrapperNode((ToolTestLangNode) node);
+            }
+            return null;
+        }
+
+        @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;
+        }
+
+        @Override
+        protected Object createContext(Env env) {
+            return null;
+        }
+    }
+
+    static final class TestASTProber implements ASTProber {
+
+        public void probeAST(final Instrumenter instrumenter, RootNode startNode) {
+            startNode.accept(new NodeVisitor() {
+
+                @Override
+                public boolean visit(Node node) {
+                    if (node instanceof ToolTestLangNode) {
+
+                        final ToolTestLangNode testNode = (ToolTestLangNode) node;
+
+                        if (node instanceof TestValueNode) {
+                            instrumenter.probe(testNode).tagAs(ToolTestTag.VALUE_TAG, null);
+
+                        } else if (node instanceof TestAdditionNode) {
+                            instrumenter.probe(testNode).tagAs(ToolTestTag.ADD_TAG, null);
+
+                        }
+                    }
+                    return true;
+                }
+            });
+        }
+    }
+
+    abstract static class ToolTestLangNode extends Node {
+        public abstract Object execute(VirtualFrame vFrame);
+
+        protected ToolTestLangNode(SourceSection ss) {
+            super(ss);
+        }
+    }
+
+    @NodeInfo(cost = NodeCost.NONE)
+    static class ToolTestWrapperNode extends ToolTestLangNode implements WrapperNode {
+        @Child private ToolTestLangNode child;
+        @Child private EventHandlerNode eventHandlerNode;
+
+        public ToolTestWrapperNode(ToolTestLangNode child) {
+            super(null);
+            assert !(child instanceof ToolTestWrapperNode);
+            this.child = child;
+        }
+
+        @Override
+        public String instrumentationInfo() {
+            return "Wrapper node for testing";
+        }
+
+        public void insertEventHandlerNode(EventHandlerNode eventHandler) {
+            this.eventHandlerNode = eventHandler;
+        }
+
+        @Override
+        public Probe getProbe() {
+            return eventHandlerNode.getProbe();
+        }
+
+        @Override
+        public Node getChild() {
+            return child;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            eventHandlerNode.enter(child, vFrame);
+            Object result;
+            try {
+                result = child.execute(vFrame);
+                eventHandlerNode.returnValue(child, vFrame, result);
+            } catch (KillException e) {
+                throw (e);
+            } catch (Exception e) {
+                eventHandlerNode.returnExceptional(child, vFrame, e);
+                throw (e);
+            }
+            return result;
+        }
+    }
+
+    /**
+     * A simple node for our test language to store a value.
+     */
+    static class TestValueNode extends ToolTestLangNode {
+        private final int value;
+
+        public TestValueNode(int value, SourceSection s) {
+            super(s);
+            this.value = value;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return new Integer(this.value);
+        }
+    }
+
+    /**
+     * A node for our test language that adds up two {@link TestValueNode}s.
+     */
+    static class TestAdditionNode extends ToolTestLangNode {
+        @Child private ToolTestLangNode leftChild;
+        @Child private ToolTestLangNode rightChild;
+
+        public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild, SourceSection s) {
+            super(s);
+            this.leftChild = insert(leftChild);
+            this.rightChild = insert(rightChild);
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue());
+        }
+    }
+
+    /**
+     * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST
+     * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
+     * completes an AST. The root nodes serves as our entry point into a program.
+     */
+    static class InstrumentationTestRootNode extends RootNode {
+        @Child private ToolTestLangNode body;
+
+        /**
+         * This constructor emulates the global machinery that applies registered probers to every
+         * newly created AST. Global registry is not used, since that would interfere with other
+         * tests run in the same environment.
+         */
+        public InstrumentationTestRootNode(ToolTestLangNode body) {
+            super(ToolTestLang.class, null, null);
+            this.body = body;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return body.execute(vFrame);
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return true;
+        }
+
+    }
+
+    /**
+     * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST
+     * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
+     * completes an AST. The root nodes serves as our entry point into a program.
+     */
+    static class TestRootNode extends RootNode {
+        @Child private ToolTestLangNode body;
+
+        final Instrumenter instrumenter;
+
+        /**
+         * This constructor emulates the global machinery that applies registered probers to every
+         * newly created AST. Global registry is not used, since that would interfere with other
+         * tests run in the same environment.
+         */
+        public TestRootNode(ToolTestLangNode body, Instrumenter instrumenter) {
+            super(ToolTestLang.class, null, null);
+            this.instrumenter = instrumenter;
+            this.body = body;
+        }
+
+        @Override
+        public Object execute(VirtualFrame vFrame) {
+            return body.execute(vFrame);
+        }
+
+        @Override
+        public boolean isCloningAllowed() {
+            return true;
+        }
+    }
+}
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,21 +24,27 @@
  */
 package com.oracle.truffle.tools.test;
 
-import com.oracle.truffle.api.instrument.InstrumentationTool;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+
+import java.lang.reflect.Field;
+
 import org.junit.Test;
 
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.vm.PolyglotEngine;
+
 /**
  * Test the basic life cycle properties shared by all instances of {@link InstrumentationTool}.
  */
 public class TruffleToolTest {
 
     @Test
-    public void testEmptyLifeCycle() {
+    public void testEmptyLifeCycle() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
         assertFalse(tool.isEnabled());
-        tool.install();
+        instrumenter.install(tool);
         assertTrue(tool.isEnabled());
         tool.reset();
         assertTrue(tool.isEnabled());
@@ -73,45 +79,58 @@
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyInstalled() {
+    public void testAlreadyInstalled() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
-        tool.install();
+        instrumenter.install(tool);
+        instrumenter.install(tool);
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyDisposed1() {
+    public void testAlreadyDisposed1() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        instrumenter.install(tool);
         tool.dispose();
-        tool.install();
+        instrumenter.install(tool);
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyDisposed2() {
+    public void testAlreadyDisposed2() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        instrumenter.install(tool);
         tool.dispose();
         tool.reset();
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyDisposed3() {
+    public void testAlreadyDisposed3() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        instrumenter.install(tool);
         tool.dispose();
         tool.setEnabled(true);
     }
 
     @Test(expected = IllegalStateException.class)
-    public void testAlreadyDisposed4() {
+    public void testAlreadyDisposed4() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
+        final Instrumenter instrumenter = getInstrumenter();
         final DummyTruffleTool tool = new DummyTruffleTool();
-        tool.install();
+        instrumenter.install(tool);
         tool.dispose();
         tool.dispose();
     }
 
-    private static final class DummyTruffleTool extends InstrumentationTool {
+    private static Instrumenter getInstrumenter() throws NoSuchFieldException, IllegalAccessException {
+        final PolyglotEngine vm = PolyglotEngine.buildNew().build();
+        final Field field = PolyglotEngine.class.getDeclaredField("instrumenter");
+        field.setAccessible(true);
+        final Instrumenter instrumenter = (Instrumenter) field.get(vm);
+        return instrumenter;
+    }
+
+    private static final class DummyTruffleTool extends Instrumenter.Tool {
 
         @Override
         protected boolean internalInstall() {
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,9 +24,18 @@
  */
 package com.oracle.truffle.tools;
 
-import com.oracle.truffle.api.instrument.Instrument;
-import com.oracle.truffle.api.instrument.InstrumentationTool;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeSet;
+
+import com.oracle.truffle.api.instrument.Instrumenter;
 import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ProbeInstrument;
 import com.oracle.truffle.api.instrument.ProbeListener;
 import com.oracle.truffle.api.instrument.SimpleInstrumentListener;
 import com.oracle.truffle.api.instrument.StandardSyntaxTag;
@@ -37,33 +46,26 @@
 import com.oracle.truffle.api.source.LineLocation;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.SourceSection;
-import java.io.PrintStream;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.TreeSet;
 
 /**
- * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes that
- * hold a specified {@linkplain SyntaxTag syntax tag}, tabulated by source and line number
- * associated with each node. Syntax tags are presumed to be applied external to the tool. If no tag
- * is specified, {@linkplain StandardSyntaxTag#STATEMENT STATEMENT} is used, corresponding to
- * conventional behavior for code coverage tools.
+ * An {@linkplain Instrumenter.Tool Instrumentation Tool} that counts interpreter
+ * <em>execution calls</em> to AST nodes that hold a specified {@linkplain SyntaxTag syntax tag},
+ * tabulated by source and line number associated with each node. Syntax tags are presumed to be
+ * applied external to the tool. If no tag is specified, {@linkplain StandardSyntaxTag#STATEMENT
+ * STATEMENT} is used, corresponding to conventional behavior for code coverage tools.
  * <p>
  * <b>Tool Life Cycle</b>
  * <p>
- * See {@link InstrumentationTool} for the life cycle common to all such tools.
+ * See {@linkplain Instrumenter.Tool Instrumentation Tool} for the life cycle common to all such
+ * tools.
  * <p>
  * <b>Execution Counts</b>
  * <p>
  * <ul>
  * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented
- * to produce the event {@link SimpleInstrumentListener#enter(Probe)};</li>
- * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which
- * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li>
+ * to produce the event {@link SimpleInstrumentListener#onEnter(Probe)};</li>
+ * <li>Execution calls are tabulated only at nodes where the guest languages supports
+ * {@linkplain Instrumenter#probe(Node) probing}.</li>
  * <li>Execution calls are tabulated only at nodes present in the AST when originally created;
  * dynamically added nodes will not be instrumented.</li>
  * </ul>
@@ -78,16 +80,16 @@
  * any time in a simple textual format, with no other effect on the state of the tool.
  * </p>
  *
- * @see Instrument
+ * @see ProbeInstrument
  * @see SyntaxTag
  */
-public final class CoverageTracker extends InstrumentationTool {
+public final class CoverageTracker extends Instrumenter.Tool {
 
     /** Counting data. */
     private final Map<LineLocation, CoverageRecord> coverageMap = new HashMap<>();
 
     /** Needed for disposal. */
-    private final List<Instrument> instruments = new ArrayList<>();
+    private final List<ProbeInstrument> instruments = new ArrayList<>();
 
     /**
      * Coverage counting is restricted to nodes holding this tag.
@@ -115,7 +117,11 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        final Instrumenter instrumenter = getInstrumenter();
+        for (Probe probe : instrumenter.findProbesTaggedAs(countingTag)) {
+            addCoverageCounter(probe);
+        }
+        instrumenter.addProbeListener(probeListener);
         return true;
     }
 
@@ -126,8 +132,8 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
-        for (Instrument instrument : instruments) {
+        getInstrumenter().removeProbeListener(probeListener);
+        for (ProbeInstrument instrument : instruments) {
             instrument.dispose();
         }
     }
@@ -242,7 +248,7 @@
     private final class CoverageRecord extends DefaultSimpleInstrumentListener {
 
         private final SourceSection srcSection; // The text of the code being counted
-        private Instrument instrument;  // The attached Instrument, in case need to remove.
+        private ProbeInstrument instrument;  // The attached Instrument, in case need to remove.
         private long count = 0;
 
         CoverageRecord(SourceSection srcSection) {
@@ -250,7 +256,7 @@
         }
 
         @Override
-        public void enter(Probe probe) {
+        public void onEnter(Probe probe) {
             if (isEnabled()) {
                 count++;
             }
@@ -273,34 +279,35 @@
         @Override
         public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
             if (countingTag == tag) {
-
-                final SourceSection srcSection = probe.getProbedSourceSection();
-                if (srcSection == null) {
-                    // TODO (mlvdv) report this?
-                    return;
-                }
-                // Get the source line where the
-                final LineLocation lineLocation = srcSection.getLineLocation();
-                CoverageRecord record = coverageMap.get(lineLocation);
-                if (record != null) {
-                    // Another node starts on same line; count only the first (textually)
-                    if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) {
-                        // Existing record, corresponds to code earlier on line
-                        return;
-                    } else {
-                        // Existing record, corresponds to code at a later position; replace it
-                        record.instrument.dispose();
-                    }
-                }
-
-                final CoverageRecord coverage = new CoverageRecord(srcSection);
-                final Instrument instrument = Instrument.create(coverage, CoverageTracker.class.getSimpleName());
-                coverage.instrument = instrument;
-                instruments.add(instrument);
-                probe.attach(instrument);
-                coverageMap.put(lineLocation, coverage);
+                addCoverageCounter(probe);
             }
         }
     }
 
+    private void addCoverageCounter(Probe probe) {
+        final SourceSection srcSection = probe.getProbedSourceSection();
+        if (srcSection == null) {
+            // TODO (mlvdv) report this?
+            return;
+        }
+        // Get the source line where the
+        final LineLocation lineLocation = srcSection.getLineLocation();
+        CoverageRecord record = coverageMap.get(lineLocation);
+        if (record != null) {
+            // Another node starts on same line; count only the first (textually)
+            if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) {
+                // Existing record, corresponds to code earlier on line
+                return;
+            } else {
+                // Existing record, corresponds to code at a later position; replace it
+                record.instrument.dispose();
+            }
+        }
+
+        final CoverageRecord coverageRecord = new CoverageRecord(srcSection);
+        final ProbeInstrument instrument = getInstrumenter().attach(probe, coverageRecord, CoverageTracker.class.getSimpleName());
+        coverageRecord.instrument = instrument;
+        instruments.add(instrument);
+        coverageMap.put(lineLocation, coverageRecord);
+    }
 }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,13 +24,6 @@
  */
 package com.oracle.truffle.tools;
 
-import com.oracle.truffle.api.instrument.InstrumentationTool;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeListener;
-import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
-import com.oracle.truffle.api.source.LineLocation;
-import com.oracle.truffle.api.source.Source;
-import com.oracle.truffle.api.source.SourceSection;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -38,11 +31,19 @@
 import java.util.HashMap;
 import java.util.Map;
 
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ProbeListener;
+import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
+import com.oracle.truffle.api.source.LineLocation;
+import com.oracle.truffle.api.source.Source;
+import com.oracle.truffle.api.source.SourceSection;
+
 /**
- * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST,
- * indexed by {@link Source} and line number.
+ * An {@linkplain Instrumenter.Tool Instrumentation Tool} that builds a map of every {@link Probe}
+ * attached to some AST, indexed by {@link Source} and line number.
  */
-public final class LineToProbesMap extends InstrumentationTool {
+public final class LineToProbesMap extends Instrumenter.Tool {
 
     private static final boolean TRACE = false;
     private static final PrintStream OUT = System.out;
@@ -68,7 +69,11 @@
 
     @Override
     protected boolean internalInstall() {
-        Probe.addProbeListener(probeListener);
+        final Instrumenter instrumenter = getInstrumenter();
+        for (Probe probe : instrumenter.findProbesTaggedAs(null)) {
+            addMapEntry(probe);
+        }
+        instrumenter.addProbeListener(probeListener);
         return true;
     }
 
@@ -79,7 +84,7 @@
 
     @Override
     protected void internalDispose() {
-        Probe.removeProbeListener(probeListener);
+        getInstrumenter().removeProbeListener(probeListener);
     }
 
     /**
@@ -115,21 +120,25 @@
 
         @Override
         public void newProbeInserted(Probe probe) {
-            final SourceSection sourceSection = probe.getProbedSourceSection();
-            if (sourceSection != null && sourceSection.getSource() != null) {
-                final LineLocation lineLocation = sourceSection.getLineLocation();
-                if (TRACE) {
-                    trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());
-                }
-                Collection<Probe> probes = lineToProbesMap.get(lineLocation);
-                if (probes == null) {
-                    probes = new ArrayList<>(2);
-                    lineToProbesMap.put(lineLocation, probes);
-                } else {
-                    assert !probes.contains(probe);
-                }
-                probes.add(probe);
+            addMapEntry(probe);
+        }
+    }
+
+    private void addMapEntry(Probe probe) {
+        final SourceSection sourceSection = probe.getProbedSourceSection();
+        if (sourceSection != null && sourceSection.getSource() != null) {
+            final LineLocation lineLocation = sourceSection.getLineLocation();
+            if (TRACE) {
+                trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription());
             }
+            Collection<Probe> probes = lineToProbesMap.get(lineLocation);
+            if (probes == null) {
+                probes = new ArrayList<>(2);
+                lineToProbesMap.put(lineLocation, probes);
+            } else {
+                assert !probes.contains(probe);
+            }
+            probes.add(probe);
         }
     }
 }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java	Tue Sep 29 18:04:11 2015 +0200
+++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java	Wed Sep 30 16:33:56 2015 -0700
@@ -24,22 +24,6 @@
  */
 package com.oracle.truffle.tools;
 
-import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.frame.VirtualFrame;
-import com.oracle.truffle.api.instrument.ASTProber;
-import com.oracle.truffle.api.instrument.Instrument;
-import com.oracle.truffle.api.instrument.InstrumentationTool;
-import com.oracle.truffle.api.instrument.Probe;
-import com.oracle.truffle.api.instrument.ProbeException;
-import com.oracle.truffle.api.instrument.ProbeFailure;
-import com.oracle.truffle.api.instrument.ProbeListener;
-import com.oracle.truffle.api.instrument.StandardInstrumentListener;
-import com.oracle.truffle.api.instrument.SyntaxTag;
-import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
-import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener;
-import com.oracle.truffle.api.nodes.Node;
-import com.oracle.truffle.api.nodes.Node.Child;
-import com.oracle.truffle.api.nodes.NodeVisitor;
 import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -50,23 +34,42 @@
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
 
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.api.instrument.ASTProber;
+import com.oracle.truffle.api.instrument.Instrumenter;
+import com.oracle.truffle.api.instrument.Probe;
+import com.oracle.truffle.api.instrument.ProbeException;
+import com.oracle.truffle.api.instrument.ProbeFailure;
+import com.oracle.truffle.api.instrument.ProbeInstrument;
+import com.oracle.truffle.api.instrument.ProbeListener;
+import com.oracle.truffle.api.instrument.StandardInstrumentListener;
+import com.oracle.truffle.api.instrument.SyntaxTag;
+import com.oracle.truffle.api.instrument.impl.DefaultProbeListener;
+import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener;
+import com.oracle.truffle.api.nodes.Node;
+import com.oracle.truffle.api.nodes.Node.Child;
+import com.oracle.truffle.api.nodes.NodeVisitor;
+import com.oracle.truffle.api.nodes.RootNode;
+
 /**
- * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes,
- * tabulated by the type of called nodes; counting can be enabled <em>all</em> nodes or restricted
- * to nodes with a specified {@linkplain SyntaxTag tag} that is presumed to be applied external to
- * the tool.
+ * An {@linkplain Instrumenter.Tool Instrumentation Tool} that counts interpreter
+ * <em>execution calls</em> to AST nodes, tabulated by the type of called nodes; counting can be
+ * enabled <em>all</em> nodes or restricted to nodes with a specified {@linkplain SyntaxTag tag}
+ * that is presumed to be applied external to the tool.
  * <p>
- * <b>Tool Life Cycle</b>
+ * s <b>Tool Life Cycle</b>
  * <p>
- * See {@link InstrumentationTool} for the life cycle common to all such tools.
+ * See {@linkplain Instrumenter.Tool Instrumentation Tool} for the life cycle common to all such
+ * tools.
  * </p>
  * <b>Execution Counts</b>
  * <p>
  * <ul>
  * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented
- * to produce the event {@link StandardInstrumentListener#enter(Probe, Node, VirtualFrame)};</li>
+ * to produce the event {@link StandardInstrumentListener#onEnter(Probe, Node, VirtualFrame)};</li>
  * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which
- * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li>
+ * {@linkplain Instrumenter#probe(Node) probing} is supported;</li>
  * <li>Execution calls are tabulated only at nodes present in the AST when originally created;
  * dynamically added nodes will not be instrumented.</li>
  * </ul>
@@ -91,11 +94,11 @@
  * any time in a simple textual format, without effect on the state of the tool.
  * </p>
  *
- * @see Instrument
+ * @see ProbeInstrument
  * @see SyntaxTag
  * @see ProbeFailure
  */
-public final class NodeExecCounter extends InstrumentationTool {
+public final class NodeExecCounter extends Instrumenter.Tool {
 
     /**
      * Execution count for AST nodes of a particular type.
@@ -108,17 +111,17 @@
 
     /**
      * Listener for events at instrumented nodes. Counts are maintained in a shared table, so the
-     * listener is stateless and can be shared by every {@link Instrument}.
+     * listener is stateless and can be shared by every {@link ProbeInstrument}.
      */
     private final StandardInstrumentListener instrumentListener = new DefaultStandardInstrumentListener() {
         @Override
-        public void enter(Probe probe, Node node, VirtualFrame vFrame) {
+        public void onEnter(Probe probe, Node node, VirtualFrame vFrame) {
             if (isEnabled()) {
                 final Class<?> nodeClass = node.getClass();
                 /*
                  * Everything up to here is inlined by Truffle compilation. Delegate the next part
                  * to a method behind an inlining boundary.
-                 * 
+                 *
                  * Note that it is not permitted to pass a {@link VirtualFrame} across an inlining
                  * boundary; they are truly virtual in inlined code.
                  */
@@ -149,7 +152,7 @@
     private final List<ProbeFailure> failures = new ArrayList<>();
 
     /** For disposal. */
-    private final List<Instrument> instruments = new ArrayList<>();
+    private final List<ProbeInstrument> instruments = new ArrayList<>();
 
     /**
      * If non-null, counting is restricted to nodes holding this tag.
@@ -170,7 +173,7 @@
      * Create a per node-type execution counting tool for all nodes in subsequently created ASTs.
      */
     public NodeExecCounter() {
-        this.countingTag = null;
+        this(null);
     }
 
     /**
@@ -185,10 +188,10 @@
     protected boolean internalInstall() {
         if (countingTag == null) {
             astProber = new ExecCounterASTProber();
-            Probe.registerASTProber(astProber);
+            getInstrumenter().registerASTProber(astProber);
         } else {
             probeListener = new NodeExecCounterProbeListener();
-            Probe.addProbeListener(probeListener);
+            getInstrumenter().addProbeListener(probeListener);
         }
         return true;
     }
@@ -202,12 +205,12 @@
     @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) {
+        for (ProbeInstrument instrument : instruments) {
             instrument.dispose();
         }
     }
@@ -289,24 +292,25 @@
     /**
      * 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 RootNode startNode) {
 
-        public boolean visit(Node node) {
+            startNode.accept(new NodeVisitor() {
+
+                public boolean visit(Node node) {
+                    try {
 
-            if (node.isInstrumentable()) {
-                try {
-                    final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter");
-                    instruments.add(instrument);
-                    node.probe().attach(instrument);
-                } catch (ProbeException ex) {
-                    failures.add(ex.getFailure());
+                        final Probe probe = instrumenter.probe(node);
+                        final ProbeInstrument instrument = instrumenter.attach(probe, instrumentListener, "NodeExecCounter");
+                        instruments.add(instrument);
+                    } catch (ProbeException ex) {
+                        failures.add(ex.getFailure());
+                    }
+                    return true;
                 }
-            }
-            return true;
-        }
 
-        public void probeAST(Node node) {
-            node.accept(this);
+            });
         }
     }
 
@@ -319,9 +323,8 @@
         @Override
         public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
             if (countingTag == tag) {
-                final Instrument instrument = Instrument.create(instrumentListener, NodeExecCounter.class.getSimpleName());
+                final ProbeInstrument instrument = getInstrumenter().attach(probe, instrumentListener, NodeExecCounter.class.getSimpleName());
                 instruments.add(instrument);
-                probe.attach(instrument);
             }
         }
     }