changeset 22256:75e5db92973a

TruffleInstrumentation: fold the "TagTrap" mechanism into the general framework of Instruments, Listeners, and management via Instrumenter.attach() methods.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Sun, 27 Sep 2015 21:34:39 -0700
parents e7643754d982
children 3168715cb34d
files truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeInstrument.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardAfterInstrumentListener.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardBeforeInstrumentListener.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/TagInstrument.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java
diffstat 12 files changed, 554 insertions(+), 245 deletions(-) [+]
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Fri Sep 25 14:58:35 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java	Sun Sep 27 21:34:39 2015 -0700
@@ -34,6 +34,7 @@
 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;
@@ -44,9 +45,11 @@
 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;
@@ -406,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) {
@@ -415,26 +420,40 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            instrumenter.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", "");
                 }
-            });
-            instrumenter.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) {
@@ -443,13 +462,13 @@
                     }
                     strategyTrace("RESUME AFTER", "");
                 }
-            });
+            }, "Debugger StepInto");
         }
 
         @Override
         protected void unsetStrategy() {
-            instrumenter.setBeforeTagTrap(null);
-            instrumenter.setAfterTagTrap(null);
+            beforeTagInstrument.dispose();
+            afterTagInstrument.dispose();
         }
     }
 
@@ -470,27 +489,41 @@
      */
     private final class StepOut extends StepStrategy {
 
+        private TagInstrument afterTagInstrument;
+
         @Override
         protected void setStrategy(final int stackDepth) {
-            instrumenter.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() {
-            instrumenter.setAfterTagTrap(null);
+            afterTagInstrument.dispose();
         }
     }
 
@@ -508,6 +541,8 @@
      * </ul>
      */
     private final class StepOver extends StepStrategy {
+        private TagInstrument beforeTagInstrument;
+        private TagInstrument afterTagInstrument;
         private int unfinishedStepCount;
 
         StepOver(int stepCount) {
@@ -516,20 +551,21 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            instrumenter.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
@@ -539,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());
+                }
 
-            instrumenter.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);
@@ -557,13 +605,13 @@
                         strategyTrace("RESUME AFTER", "");
                     }
                 }
-            });
+            }, "Debugger StepOver");
         }
 
         @Override
         protected void unsetStrategy() {
-            instrumenter.setBeforeTagTrap(null);
-            instrumenter.setAfterTagTrap(null);
+            beforeTagInstrument.dispose();
+            afterTagInstrument.dispose();
         }
     }
 
@@ -581,6 +629,7 @@
      * </ul>
      */
     private final class StepOverNested extends StepStrategy {
+        private TagInstrument beforeTagInstrument;
         private int unfinishedStepCount;
         private final int startStackDepth;
 
@@ -591,28 +640,28 @@
 
         @Override
         protected void setStrategy(final int stackDepth) {
-            instrumenter.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() {
-            instrumenter.setBeforeTagTrap(null);
+            beforeTagInstrument.dispose();
         }
     }
 
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Fri Sep 25 14:58:35 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java	Sun Sep 27 21:34:39 2015 -0700
@@ -46,11 +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.ProbeInstrument;
 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;
@@ -63,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 Instrumenter#setBeforeTagTrap(SyntaxTagTrap) Tag Trap}, which is built directly
- * into the Instrumentation Framework, does the same thing more efficiently, but there may only be
- * one Tag Trap active at a time. Any number of tag breakpoints may coexist with the Tag Trap, but
- * it would be confusing to have a Tag Breakpoint set for the same Tag as the current Tag Trap.
+ * 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 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 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>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Sun Sep 27 21:34:39 2015 -0700
@@ -0,0 +1,57 @@
+/*
+ * 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;
+
+/**
+ * A <em>binding</em> between:
+ * <ol>
+ * <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>
+ *
+ * @See Instrumenter
+ */
+abstract class Instrument {
+
+    protected Instrument() {
+    }
+
+    /**
+     * Removes this from its source of execution events and renders itself Instrument inert.
+     *
+     * @throws IllegalStateException if this has already been disposed
+     */
+    public abstract void dispose() throws IllegalStateException;
+
+    /**
+     * Has this been detached from its source of execution events.
+     */
+    public abstract boolean isDisposed();
+
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java	Fri Sep 25 14:58:35 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java	Sun Sep 27 21:34:39 2015 -0700
@@ -37,6 +37,8 @@
 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;
@@ -80,7 +82,8 @@
      * <ul>
      * <li>A newly created tool is inert until {@linkplain Instrumenter#install(Tool) installed}.</li>
      * <li>An installed tool becomes <em>enabled</em> and immediately begins installing
-     * {@linkplain ProbeInstrument instrumentation} on ASTs and collecting execution data from them.</li>
+     * {@linkplain ProbeInstrument instrumentation} on ASTs and collecting execution data from them.
+     * </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>
@@ -231,24 +234,24 @@
     private final List<WeakReference<Probe>> probes = new ArrayList<>();
 
     /**
-     * A global trap that triggers notification just before executing any Node that is Probed with a
-     * matching tag.
+     * A global instrument that triggers notification just before executing any Node that is Probed
+     * with a matching tag.
      */
-    @CompilationFinal private SyntaxTagTrap beforeTagTrap = null;
+    @CompilationFinal private BeforeTagInstrument beforeTagInstrument = null;
 
     /**
-     * A global trap that triggers notification just after executing any Node that is Probed with a
-     * matching tag.
+     * A global instrument that triggers notification just after executing any Node that is Probed
+     * with a matching tag.
      */
-    @CompilationFinal private SyntaxTagTrap afterTagTrap = null;
+    @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.
+     * 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>
@@ -357,54 +360,6 @@
         return taggedProbes;
     }
 
-    // TODO (mlvdv) generalize to permit multiple "before traps" without a performance hit?
-    /**
-     * Sets the current "<em>before</em> tag trap"; there can be no more than one in effect.
-     * <ul>
-     * <li>The before-trap triggers a callback just <strong><em>before</em></strong> execution
-     * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created)
-     * with the specified {@link SyntaxTag}.</li>
-     * <li>Setting the before-trap to {@code null} clears an existing before-trap.</li>
-     * <li>Setting a non{@code -null} before-trap when one is already set clears the previously set
-     * before-trap.</li>
-     * </ul>
-     *
-     * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set.
-     */
-    public void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) {
-        beforeTagTrap = newBeforeTagTrap;
-        for (WeakReference<Probe> ref : probes) {
-            final Probe probe = ref.get();
-            if (probe != null) {
-                probe.notifyTrapsChanged();
-            }
-        }
-    }
-
-    // TODO (mlvdv) generalize to permit multiple "after traps" without a performance hit?
-    /**
-     * Sets the current "<em>after</em> tag trap"; there can be no more than one in effect.
-     * <ul>
-     * <li>The after-trap triggers a callback just <strong><em>after</em></strong> execution leaves
-     * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with
-     * the specified {@link SyntaxTag}.</li>
-     * <li>Setting the after-trap to {@code null} clears an existing after-trap.</li>
-     * <li>Setting a non{@code -null} after-trap when one is already set clears the previously set
-     * after-trap.</li>
-     * </ul>
-     *
-     * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set.
-     */
-    public void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) {
-        afterTagTrap = newAfterTagTrap;
-        for (WeakReference<Probe> ref : probes) {
-            final Probe probe = ref.get();
-            if (probe != null) {
-                probe.notifyTrapsChanged();
-            }
-        }
-    }
-
     /**
      * Enables instrumentation at selected nodes in all subsequently constructed ASTs. Ignored if
      * the argument is already registered, runtime error if argument is {@code null}.
@@ -486,6 +441,66 @@
         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(tag, listener, instrumentInfo);
+        for (WeakReference<Probe> ref : probes) {
+            final Probe probe = ref.get();
+            if (probe != null) {
+                probe.notifyTagInstrumentsChanged();
+            }
+        }
+        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(tag, listener, instrumentInfo);
+        for (WeakReference<Probe> ref : probes) {
+            final Probe probe = ref.get();
+            if (probe != null) {
+                probe.notifyTagInstrumentsChanged();
+            }
+        }
+        return afterTagInstrument;
+    }
+
     /**
      * Connects the tool to some part of the Truffle runtime, and enable data collection to start.
      *
@@ -514,12 +529,12 @@
         }
     }
 
-    SyntaxTagTrap getBeforeTagTrap() {
-        return beforeTagTrap;
+    BeforeTagInstrument getBeforeTagInstrument() {
+        return beforeTagInstrument;
     }
 
-    SyntaxTagTrap getAfterTagTrap() {
-        return afterTagTrap;
+    AfterTagInstrument getAfterTagInstrument() {
+        return afterTagInstrument;
     }
 
     // TODO (mlvdv) build this in as a VM event?
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Fri Sep 25 14:58:35 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Sun Sep 27 21:34:39 2015 -0700
@@ -34,6 +34,8 @@
 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;
@@ -44,12 +46,13 @@
  * <ol>
  * <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 ProbeInstrument Instruments}
- * that receive event notifications from the Probe's AST location on behalf of external clients.</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 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.
+ * <strong>Note</strong>:The relationship for {@link ProlbeInstrument} 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.
@@ -94,10 +97,10 @@
     @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;
 
     /**
      * Constructor for use only by {@link ProbeNode}.
@@ -130,7 +133,8 @@
      * <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>
+     * 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
@@ -163,19 +167,19 @@
             tags.add(tag);
             instrumenter.tagAdded(this, tag, tagValue);
 
-            // Update the status of this Probe with respect to global tag traps
-            boolean tagTrapsChanged = false;
-            final SyntaxTagTrap beforeTagTrap = instrumenter.getBeforeTagTrap();
-            if (beforeTagTrap != null && tag == beforeTagTrap.getTag()) {
-                this.isBeforeTrapActive = true;
-                tagTrapsChanged = true;
+            // 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;
             }
-            final SyntaxTagTrap afterTagTrap = instrumenter.getAfterTagTrap();
-            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) {
@@ -264,25 +268,23 @@
     }
 
     /**
-     * Gets the currently active <strong><em>before</em></strong> {@linkplain SyntaxTagTrap Tag
-     * Trap} at this Probe. Non{@code -null} if the global
-     * {@linkplain Instrumenter#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 ? instrumenter.getBeforeTagTrap() : 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 Instrumenter#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 ? instrumenter.getAfterTagTrap() : null;
+        return isAfterTagInstrumentActive ? instrumenter.getAfterTagInstrument() : null;
     }
 
     Class<? extends TruffleLanguage> getLanguage() {
@@ -307,11 +309,11 @@
         probeStateUnchangedCyclic.invalidate();
     }
 
-    void notifyTrapsChanged() {
-        final SyntaxTagTrap beforeTagTrap = instrumenter.getBeforeTagTrap();
-        this.isBeforeTrapActive = beforeTagTrap != null && this.isTaggedAs(beforeTagTrap.getTag());
-        final SyntaxTagTrap afterTagTrap = instrumenter.getAfterTagTrap();
-        this.isAfterTrapActive = afterTagTrap != null && this.isTaggedAs(afterTagTrap.getTag());
+    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();
     }
 
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeInstrument.java	Fri Sep 25 14:58:35 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeInstrument.java	Sun Sep 27 21:34:39 2015 -0700
@@ -45,9 +45,14 @@
  * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a>
  *
  * @see Probe
- * @see EventHandlerNode
+ * @see Instrumenter
  */
-public abstract class ProbeInstrument {
+public abstract class ProbeInstrument extends Instrument {
+
+    /**
+     * Optional documentation, mainly for debugging.
+     */
+    private final String instrumentInfo;
 
     /**
      * Has this instrument been disposed? stays true once set.
@@ -57,11 +62,6 @@
     protected Probe probe = null;
 
     /**
-     * Optional documentation, mainly for debugging.
-     */
-    private final String instrumentInfo;
-
-    /**
      * <h4>Implementation Notes</h4>
      * <p>
      * The implementation of Instruments is complicated by the requirement that Truffle be able to
@@ -86,9 +86,9 @@
      *
      * <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>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>
      *
@@ -121,19 +121,12 @@
     }
 
     /**
-     * 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
      */
+    @Override
     public void dispose() throws IllegalStateException {
         if (isDisposed) {
             throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt");
@@ -146,6 +139,19 @@
         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.
      */
@@ -153,13 +159,6 @@
         this.probe = probe;
     }
 
-    /**
-     * Has this instrument been disposed and rendered unusable?
-     */
-    boolean isDisposed() {
-        return isDisposed;
-    }
-
     abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode);
 
     /**
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Fri Sep 25 14:58:35 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java	Sun Sep 27 21:34:39 2015 -0700
@@ -28,6 +28,8 @@
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.frame.VirtualFrame;
 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;
@@ -36,14 +38,14 @@
  * Implementation class & interface for enabling the attachment of {@linkplain Probe Probes} to
  * Truffle ASTs.
  * <p>
- * 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.
+ * 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 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
+ * 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)
@@ -74,9 +76,9 @@
     @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);
@@ -89,9 +91,9 @@
         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);
         }
     }
 
@@ -101,9 +103,9 @@
         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);
         }
     }
 
@@ -113,9 +115,9 @@
         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);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardAfterInstrumentListener.java	Sun Sep 27 21:34:39 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	Sun Sep 27 21:34:39 2015 -0700
@@ -0,0 +1,57 @@
+/*
+ * 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/SyntaxTagTrap.java	Fri Sep 25 14:58:35 2015 -0700
+++ /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	Sun Sep 27 21:34:39 2015 -0700
@@ -0,0 +1,106 @@
+/*
+ * 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;
+
+    /**
+     * Has this instrument been disposed? stays true once set.
+     */
+    private boolean isDisposed = false;
+
+    private SyntaxTag tag = null;
+
+    protected TagInstrument(SyntaxTag tag, String instrumentInfo) {
+        this.tag = tag;
+        this.instrumentInfo = instrumentInfo;
+    }
+
+    @Override
+    public void dispose() throws IllegalStateException {
+        if (isDisposed) {
+            throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt");
+        }
+
+        // TODO (mlvdv)
+        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(SyntaxTag tag, StandardBeforeInstrumentListener listener, String instrumentInfo) {
+            super(tag, instrumentInfo);
+            this.listener = listener;
+        }
+
+        StandardBeforeInstrumentListener getListener() {
+            return listener;
+        }
+    }
+
+    static final class AfterTagInstrument extends TagInstrument {
+
+        private final StandardAfterInstrumentListener listener;
+
+        AfterTagInstrument(SyntaxTag tag, StandardAfterInstrumentListener listener, String instrumentInfo) {
+            super(tag, instrumentInfo);
+            this.listener = listener;
+        }
+
+        StandardAfterInstrumentListener getListener() {
+            return listener;
+        }
+    }
+}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java	Fri Sep 25 14:58:35 2015 -0700
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/package-info.java	Sun Sep 27 21:34:39 2015 -0700
@@ -145,25 +145,25 @@
  * location, where it will be executed subject to full Truffle optimization.</li>
  * </ul></li>
  *
- * <li><strong>Wide-area Instrumentation: traps</strong>
+ * <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.SyntaxTagTrap tag trap} to every
+ * {@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.SyntaxTagTrap tag trap} may optionally be set
- * to be notified <em>before</em> every <em>AST execution event</em> where the specified
+ * {@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.SyntaxTagTrap tag trap} may optionally be set
- * to be notified <em>after</em> every <em>AST execution event</em> where the specified
+ * {@linkplain com.oracle.truffle.api.instrument.StandardBeforeInstrumentListener StandardBeforeInstrumentListener}
+ * 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.SyntaxTagTrap tag trap} mechanism is independent
+ * {@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 Probe}.  It is especially valuable for
+ * {@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>