changeset 15605:bb9473723904

Truffle/Instrumentation: - Merge instrumentation support into the general execution context; remove separate Instrumentation interface and implementation - Generalize the ?tagging? mechanism for extensibility: the enum PhylumTag is now an interface, and the standard tags moved to the new enum StandardTag - A new ?trap? mechanism interrupts program execution at any probed node holding a specified PhylumTag; this replaces some other special-purpose code. - Refine several interface by factoring out callback methods and simplifying collaboration among key implementation classes.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Mon, 12 May 2014 20:17:25 -0700
parents 8f09b84f325f
children 357e7202de5b
files graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/AbstractExecutionContext.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentEventListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumentation.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationFactory.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/PhylumTag.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/PhylumTagged.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/PhylumTrap.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardTag.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Wrapper.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationImpl.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/NullInstrumentEventListener.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SourceCallback.java
diffstat 19 files changed, 565 insertions(+), 535 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java	Mon May 12 20:17:25 2014 -0700
@@ -24,6 +24,8 @@
  */
 package com.oracle.truffle.api;
 
+import java.util.*;
+
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.source.*;
 
@@ -47,20 +49,62 @@
     SourceManager getSourceManager();
 
     /**
-     * Gets access to AST instrumentation services.
+     * Registers a tool interested in being notified of events related to the loading of sources.
+     */
+    void addSourceListener(SourceListener listener);
+
+    /**
+     * Add instrumentation to subsequently constructed Truffle ASTs for the guest language; every
+     * one added will have the opportunity to add instrumentation.
+     *
+     * @throws IllegalStateException if AST instrumentation not enabled
+     * @throws IllegalArgumentException if prober not usable for the guest language implementation.
+     */
+    void addNodeProber(ASTNodeProber nodeProber) throws IllegalStateException, IllegalArgumentException;
+
+    /**
+     * Registers a tool interested in being notified about the insertion of a newly created
+     * {@link Probe} into a Truffle AST.
+     */
+    void addProbeListener(ProbeListener listener);
+
+    /**
+     * Return the (possibly newly created) {@link Probe} uniquely associated with a particular
+     * source code location. A newly created probe carries no tags.
+     *
+     * @return a probe uniquely associated with an extent of guest language source code.
      */
-    Instrumentation getInstrumentation();
+    Probe getProbe(SourceSection sourceSection);
+
+    /**
+     * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty
+     * collection if no probes found.
+     */
+    Collection<Probe> findProbesTaggedAs(PhylumTag tag);
+
+    /**
+     * Returns all existing probes with first character on a specified line; empty collection if no
+     * probes found.
+     */
+    Collection<Probe> findProbesByLine(SourceLineLocation lineLocation);
+
+    /**
+     * Sets a trap that will make a callback at any AST location where a existing probe holds a
+     * specified tag; only one trap may be set at a time.
+     *
+     * @throws IllegalStateException if a trap is already set
+     */
+    void setTrap(PhylumTrap trap) throws IllegalStateException;
+
+    /**
+     * Clears a trap that will halt execution; only one trap may be set at a time.
+     *
+     * @throws IllegalStateException if no trap is set.
+     */
+    void clearTrap() throws IllegalStateException;
 
     /**
      * Access to information visualization services for the specific language.
      */
     Visualizer getVisualizer();
-
-    /**
-     * Add instrumentation to subsequently constructed Truffle ASTs for the guest language; every
-     * one added will have the opportunity to add instrumentation.
-     *
-     * @throws IllegalArgumentException if prober not usable for the guest language.
-     */
-    void addNodeProber(ASTNodeProber nodeProber);
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/AbstractExecutionContext.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/AbstractExecutionContext.java	Mon May 12 20:17:25 2014 -0700
@@ -24,41 +24,175 @@
  */
 package com.oracle.truffle.api.impl;
 
+import java.util.*;
+
 import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.instrument.impl.*;
+import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeCallback;
+import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeImpl;
 import com.oracle.truffle.api.source.*;
 
 public abstract class AbstractExecutionContext implements ExecutionContext {
 
+    // TODO (mlvdv) use weak references.
+    /**
+     * Map: SourceSection ==> probe associated with that source section in an AST.
+     */
+    private final Map<SourceSection, ProbeImpl> srcToProbe = new HashMap<>();
+
+    // TODO (mlvdv) use weak references.
+    /**
+     * Map: Source line ==> probes associated with source sections starting on the line.
+     */
+    private final Map<SourceLineLocation, Collection<Probe>> lineToProbes = new HashMap<>();
+
     private final SourceManager sourceManager = new SourceManager();
-    private final Instrumentation instrumentation;
+    private final List<SourceListener> sourceListeners = new ArrayList<>();
+    private final List<ProbeListener> probeListeners = new ArrayList<>();
+
+    private final SourceCallback sourceCallback = new SourceCallback() {
+
+        public void startLoading(Source source) {
+            for (SourceListener listener : sourceListeners) {
+                listener.loadStarting(source);
+            }
+        }
+
+        public void endLoading(Source source) {
+            for (SourceListener listener : sourceListeners) {
+                listener.loadEnding(source);
+            }
+        }
+    };
+
+    private final ProbeCallback probeCallback = new ProbeCallback() {
+        /**
+         * Receives (from the {@link Probe} implementation) and distributes notification that a
+         * {@link Probe} has acquired a new {@linkplain PhylumTag tag}.
+         */
+        public void newTagAdded(ProbeImpl probe, PhylumTag tag) {
+            for (ProbeListener listener : probeListeners) {
+                listener.probeTaggedAs(probe, tag);
+            }
+            if (trap != null && tag == trap.getTag()) {
+                probe.setTrap(trap);
+            }
+        }
+    };
+
     private Visualizer visualizer = new DefaultVisualizer();
-    protected ASTProber astProber = null;
+
+    /**
+     * When non-null, "enter" events with matching tags will trigger a callback.
+     */
+    private PhylumTrap trap = null;
 
     protected AbstractExecutionContext() {
-        this.instrumentation = InstrumentationFactory.create(this);
+    }
+
+    public void initialize() {
+        setSourceCallback(sourceCallback);
     }
 
     public final SourceManager getSourceManager() {
         return sourceManager;
     }
 
-    public final Instrumentation getInstrumentation() {
-        return instrumentation;
+    public void addSourceListener(SourceListener listener) {
+        assert listener != null;
+        sourceListeners.add(listener);
+    }
+
+    public void addProbeListener(ProbeListener listener) {
+        assert listener != null;
+        probeListeners.add(listener);
+    }
+
+    public Probe getProbe(SourceSection sourceSection) {
+        assert sourceSection != null;
+
+        ProbeImpl probe = srcToProbe.get(sourceSection);
+
+        if (probe != null) {
+            return probe;
+        }
+        probe = InstrumentationNode.createProbe(sourceSection, probeCallback);
+
+        // Register new probe by unique SourceSection
+        srcToProbe.put(sourceSection, probe);
+
+        // Register new probe by source line, there may be more than one
+        // Create line location for map key
+        final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine());
+
+        Collection<Probe> probes = lineToProbes.get(lineLocation);
+        if (probes == null) {
+            probes = new ArrayList<>(2);
+            lineToProbes.put(lineLocation, probes);
+        }
+        probes.add(probe);
+
+        for (ProbeListener listener : probeListeners) {
+            listener.newProbeInserted(sourceSection, probe);
+        }
+
+        return probe;
+    }
+
+    /**
+     * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty
+     * collection if no probes found.
+     */
+    public Collection<Probe> findProbesTaggedAs(PhylumTag tag) {
+        final List<Probe> probes = new ArrayList<>();
+        for (Probe probe : srcToProbe.values()) {
+            if (tag == null || probe.isTaggedAs(tag)) {
+                probes.add(probe);
+            }
+        }
+        return probes;
+    }
+
+    public Collection<Probe> findProbesByLine(SourceLineLocation lineLocation) {
+        final Collection<Probe> probes = lineToProbes.get(lineLocation);
+        if (probes == null) {
+            return Collections.emptyList();
+        }
+        return new ArrayList<>(probes);
+    }
+
+    // TODO (mlvdv) consider allowing multiple traps (without inhibiting Truffle inlining)
+    public void setTrap(PhylumTrap trap) {
+        assert trap != null;
+        if (this.trap != null) {
+            throw new IllegalStateException("trap already set");
+        }
+        this.trap = trap;
+
+        for (ProbeImpl probe : srcToProbe.values()) {
+            if (probe.isTaggedAs(trap.getTag())) {
+                probe.setTrap(trap);
+            }
+        }
+    }
+
+    public void clearTrap() {
+        if (this.trap == null) {
+            throw new IllegalStateException("no trap set");
+        }
+        for (ProbeImpl probe : srcToProbe.values()) {
+            if (probe.isTaggedAs(trap.getTag())) {
+                probe.setTrap(null);
+            }
+        }
+        trap = null;
     }
 
     public Visualizer getVisualizer() {
         return visualizer;
     }
 
-    public void addNodeProber(ASTNodeProber nodeProber) {
-        if (astProber == null) {
-            throw new IllegalStateException("No ASTProber installed in context");
-        }
-        astProber.addNodeProber(nodeProber);
-    }
-
     /**
      * Assign guest language-specific visualization support for tools. This must be assigned outside
      * the implementation context to avoid build circularities.
@@ -73,19 +207,11 @@
      * implementation context to avoid build circularities. It must also be set before any
      * instrumentation probe implementations are assigned.
      */
-    public void setASTProber(ASTProber astProber) {
-        this.astProber = astProber;
-    }
+    public abstract void setASTProber(ASTProber astProber);
 
     /**
-     * Gets a guest language-specific {@link ASTNodeProber} that will apply all that have been
-     * added; {@code null} if no instrumentation in AST.
+     * Establishes source event reporting
      */
-    public ASTNodeProber getCombinedNodeProber() {
-        return astProber == null ? null : astProber.getCombinedNodeProber();
-    }
+    protected abstract void setSourceCallback(SourceCallback sourceCallback);
 
-    public abstract void setInstrumentEventListener(InstrumentEventListener listener);
-
-    public abstract InstrumentEventListener getInstrumentEventListener();
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java	Mon May 12 20:17:25 2014 -0700
@@ -24,10 +24,24 @@
  */
 package com.oracle.truffle.api.instrument;
 
+import com.oracle.truffle.api.nodes.*;
+
 /**
  * Implementation of a policy for <em>instrumenting</em> Truffle ASTs with {@link Probe}s at
  * particular nodes by inserting node {@link Wrapper}s.
  * <p>
+ * Multiple "node probers" can be added, typically by different tools; the "combined prober" will
+ * apply all of them.
+ * <p>
+ * The current implementation is provisional and does not completely encapsulate everything that
+ * needs to be implemented for a particular use-case or set of use-cases. In particular, the AST
+ * building code for each language implementation must have hand-coded applications of node probing
+ * methods at the desired locations. For the duration of this approach, this must be done for any
+ * node that any client tool wishes to probe.
+ * <p>
+ * A better approach will be to implement such policies as a Truffle {@link NodeVisitor}, but that
+ * is not possible at this time.
+ * <p>
  * <strong>Disclaimer:</strong> experimental interface under development.
  */
 public interface ASTProber {
@@ -35,10 +49,18 @@
     // TODO (mlvdv) This is a provisional interface, more of a marker really
     // TODO (mlvdv) AST probing should eventually be done with visitors.
 
-    void addNodeProber(ASTNodeProber nodeProber);
+    /**
+     * Adds a specification for adding probes at particular kinds of nodes.
+     *
+     * @param nodeProber
+     * @throws IllegalArgumentException if the prober is not applicable to the guest language
+     *             implementation.
+     */
+    void addNodeProber(ASTNodeProber nodeProber) throws IllegalArgumentException;
 
     /**
-     * Gets a prober that applies all added {@link ASTNodeProber}s.
+     * Gets a (possibly guest language-specific) {@link ASTNodeProber} that will apply all that have
+     * been added; {@code null} if no instrumentation in AST.
      */
     ASTNodeProber getCombinedNodeProber();
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Mon May 12 20:17:25 2014 -0700
@@ -69,9 +69,7 @@
  * <p>
  * <strong>Disclaimer:</strong> experimental; under development.
  *
- * @see Instrumentation
  * @see Probe
- * @see Instrument
  * @see ASTNodeProber
  */
 public class Instrument extends InstrumentationNode {
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentEventListener.java	Fri May 02 16:12:07 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, 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.*;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
-
-/**
- * A client of the instrumentation framework that requests event notifications from the language
- * engine.
- */
-public interface InstrumentEventListener {
-
-    /**
-     * The guest language runtime is starting to load a source. Care should be taken to ensure that
-     * under any circumstance there is always a following call to {@link #loadEnding(Source)} with
-     * the same argument.
-     */
-    void loadStarting(Source source);
-
-    /**
-     * The guest language runtime has finished loading a source. Care should be taken to ensure that
-     * under any circumstance there is always a prior call to {@link #loadStarting(Source)} with the
-     * same argument.
-     */
-    void loadEnding(Source source);
-
-    /**
-     * A guest language call is about to be executed.
-     */
-    void callEntering(Node astNode, String name);
-
-    /**
-     * A guest language call has just completed.
-     */
-    void callReturned(Node astNode, String name);
-
-    /**
-     * An opportunity for instrumentation to interact with Truffle AST execution halted at some
-     * node.
-     */
-    void haltedAt(Node astNode, MaterializedFrame frame);
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumentation.java	Fri May 02 16:12:07 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
-/*
- * Copyright (c) 2014, 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.util.*;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.source.*;
-
-public interface Instrumentation {
-
-    /**
-     * Adds a new specification for how to instrument ASTs.
-     */
-    void addNodeProber(ASTNodeProber nodeProber);
-
-    /**
-     * Registers a tool interested in being notified about the insertion of a newly created
-     * {@link Probe} into a Truffle AST.
-     */
-    void addProbeListener(ProbeListener listener);
-
-    /**
-     * Return the (possibly newly created) {@link Probe} uniquely associated with a particular
-     * source code location. A newly created probe carries no tags.
-     *
-     * @param eventListener an optional listener for certain instrumentation-related events
-     * @return a probe uniquely associated with an extent of guest language source code.
-     */
-    Probe getProbe(SourceSection sourceSection, InstrumentEventListener eventListener);
-
-    /**
-     * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty
-     * collection if no probes found.
-     */
-    Collection<Probe> findProbesTaggedAs(PhylumTag tag);
-
-    /**
-     * Returns all existing probes with first character on a specified line; empty collection if no
-     * probes found.
-     */
-    Collection<Probe> findProbesByLine(SourceLineLocation lineLocation);
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationFactory.java	Fri May 02 16:12:07 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2014, 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.*;
-import com.oracle.truffle.api.instrument.impl.*;
-
-public class InstrumentationFactory {
-
-    public static Instrumentation create(ExecutionContext context) {
-        return new InstrumentationImpl(context);
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/PhylumTag.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/PhylumTag.java	Mon May 12 20:17:25 2014 -0700
@@ -25,46 +25,32 @@
 package com.oracle.truffle.api.instrument;
 
 /**
- * Program element "tags" that define user-visible behavior for debugging and other simple tools.
- * These categories (<em>phyla</em>) should correspond to program structures that are meaningful to
- * a guest language programmer.
+ * Program element "tags", presumed to be singletons (best implemented as enums) that define
+ * user-visible behavior for debugging and other simple tools. These categories (<em>phyla</em>)
+ * should correspond to program structures that are meaningful to a guest language programmer.
  * <p>
  * An untagged Truffle node should be understood as an artifact of the guest language implementation
  * and should not be visible to the user of a guest language programming tool. Nodes may also have
  * more than one tag, for example a variable assignment that is also a statement. Finally, the
  * assignment of tags to nodes could depending on the use-case of whatever tool is using them.
  * <p>
- * This is a somewhat language-agnostic set of phyla, suitable for conventional imperative
- * languages, and is being developed incrementally.
- * <p>
- * The need for alternative sets of tags is likely to arise, perhaps for other families of languages
- * (for example for mostly expression-oriented languages) or even for specific languages.
- * <p>
- * These are listed alphabetically so that listing from some collection classes will come out in
- * that order.
- * <p>
  * <strong>Disclaimer:</strong> experimental interface under development.
+ *
+ * @see Probe
+ * @see Wrapper
+ * @see StandardTag
  */
-public enum PhylumTag {
+public interface PhylumTag {
 
     /**
-     * Marker for a variable assignment.
+     * Human-friendly name of guest language program elements belonging to the category, e.g.
+     * "statement".
      */
-    ASSIGNMENT,
-
-    /**
-     * Marker for a call site.
-     */
-    CALL,
+    String name();
 
     /**
-     * Marker for a location where a guest language exception is about to be thrown.
+     * Criteria and example uses for the tag.
      */
-    THROW,
-
-    /**
-     * Marker for a location where ordinary "stepping" should halt.
-     */
-    STATEMENT;
+    String getDescription();
 
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/PhylumTagged.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/PhylumTagged.java	Mon May 12 20:17:25 2014 -0700
@@ -24,13 +24,14 @@
  */
 package com.oracle.truffle.api.instrument;
 
-import java.util.*;
-
 /**
- * Information about a guest language program element that can be marked as belonging to 0 or more
- * {@linkplain PhylumTag tags}.
+ * Information about a guest language program element in a Truffle that can be marked as belonging
+ * to 0 or more {@linkplain PhylumTag tags}.
  * <p>
  * <strong>Disclaimer:</strong> experimental interface under development.
+ *
+ * @see Probe
+ * @see Wrapper
  */
 public interface PhylumTagged {
 
@@ -42,6 +43,6 @@
     /**
      * In which categories has this node been tagged (<em>empty set</em> if none).
      */
-    Set<PhylumTag> getPhylumTags();
+    Iterable<PhylumTag> getPhylumTags();
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/PhylumTrap.java	Mon May 12 20:17:25 2014 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, 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.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * A trap that can be set to interrupt execution at probed nodes carrying a specific tag.
+ */
+public abstract class PhylumTrap {
+
+    private final PhylumTag tag;
+
+    protected PhylumTrap(PhylumTag tag) {
+        this.tag = tag;
+    }
+
+    public final PhylumTag getTag() {
+        return tag;
+    }
+
+    /**
+     * Callback that will be received whenever execution enters a node with the specified tag.
+     */
+    public abstract void phylumTrappedAt(Node node, MaterializedFrame frame);
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Mon May 12 20:17:25 2014 -0700
@@ -48,7 +48,11 @@
  * removed, but some instruments may change their internal state in such a way that the assumption
  * should also be invalidated.
  * <p>
- * <strong>Disclaimer:</strong> experimental interface under development.
+ * <strong>Disclaimer:</strong> experimental interface under development. In particular, the
+ * <em>notify</em> methods must be migrated to another interface.
+ *
+ * @see Instrument
+ * @see Wrapper
  */
 public interface Probe extends PhylumTagged {
 
@@ -73,17 +77,7 @@
      */
     void removeInstrument(Instrument oldInstrument);
 
-    /**
-     * Change <em>stepping mode</em>, which is used in association with nodes tagged as
-     * {@linkplain PhylumTag#STATEMENT statements}.
-     */
-    void setStepping(boolean stepping);
-
-    /**
-     * Value of <em>stepping mode</em>, which is used in association with nodes tagged as
-     * {@linkplain PhylumTag#STATEMENT statements}.
-     */
-    boolean isStepping();
+    // TODO (mlvdv) migrate the remaining methods to another interface.
 
     /**
      * @see ExecutionEvents#enter(Node, VirtualFrame)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceListener.java	Mon May 12 20:17:25 2014 -0700
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, 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.*;
+
+/**
+ * A client of the instrumentation framework that requests event notifications from the language
+ * engine when sources are loaded.
+ * <p>
+ * <strong>Disclaimer:</strong> experimental interface under development.
+ */
+public interface SourceListener {
+
+    /**
+     * The guest language runtime is starting to load a source. Care should be taken to ensure that
+     * under any circumstance there is always a following call to {@link #loadEnding(Source)} with
+     * the same argument.
+     */
+    void loadStarting(Source source);
+
+    /**
+     * The guest language runtime has finished loading a source. Care should be taken to ensure that
+     * under any circumstance there is always a prior call to {@link #loadStarting(Source)} with the
+     * same argument.
+     */
+    void loadEnding(Source source);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardTag.java	Mon May 12 20:17:25 2014 -0700
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2014, 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 somewhat language-agnostic set of phylum categories, suitable for conventional imperative
+ * languages, and is being developed incrementally.
+ * <p>
+ * The need for alternative sets of tags is likely to arise, perhaps for other families of languages
+ * (for example for mostly expression-oriented languages) or even for specific languages.
+ * <p>
+ * <strong>Disclaimer:</strong> experimental interface under development.
+ *
+ * @see Probe
+ * @see Wrapper
+ */
+public enum StandardTag implements PhylumTag {
+
+    /**
+     * Marker for a variable assignment.
+     */
+    ASSIGNMENT("assignment", "a variable assignment"),
+
+    /**
+     * Marker for a call site.
+     */
+    CALL("call", "a method/procedure call site"),
+
+    /**
+     * Marker for a location where a guest language exception is about to be thrown.
+     */
+    THROW("throw", "creator of an exception"),
+
+    /**
+     * Marker for a location where ordinary "stepping" should halt.
+     */
+    STATEMENT("statement", "basic unit of the language, suitable for \"stepping\" in a debugger");
+
+    private final String name;
+    private final String description;
+
+    private StandardTag(String name, String description) {
+        this.name = name;
+        this.description = description;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java	Mon May 12 20:17:25 2014 -0700
@@ -29,13 +29,11 @@
 import com.oracle.truffle.api.nodes.*;
 
 /**
- * Visualization services for the benefit of {@link 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 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.
- *
- * @See Instrumentation
  */
 public interface Visualizer {
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Wrapper.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Wrapper.java	Mon May 12 20:17:25 2014 -0700
@@ -33,14 +33,34 @@
  * A wrapper <em>decorates</em> an AST node (its <em>child</em>) by acting as a transparent
  * <em>proxy</em> for the child with respect to Truffle execution semantics.
  * <p>
- * A wrapper is also expected to notify its associated {@link Probe} when certain
- * {@link ExecutionEvents} occur at the wrapper during program execution.
+ * A wrapper is also expected to notify its associated {@link Probe} when {@link ExecutionEvents}
+ * occur at the wrapper during program execution.
  * <p>
  * The wrapper's {@link Probe} is shared by every copy of the wrapper made when the AST is copied.
  * <p>
- * Wrappers methods must be amenable to Truffle/Graal inlining.
+ * Wrapper implementation guidelines:
+ * <ol>
+ * <li>Every guest language implementation should include a Wrapper implementation; usually only one
+ * is needed.</li>
+ * <li>The Wrapper type should descend from the <em>language-specific node class</em> of the guest
+ * language.</li>
+ * <li>The Wrapper must have a single {@code @Child private <guestLanguage>Node child} field.</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>The Wrapper must have a single {@code private final Probe probe} to which an optional probe
+ * can be attached during node construction.</li>
+ * <li>Wrapper methods must also notify its attached {@link Probe}, if any, in terms of standard
+ * {@link ExecutionEvents}.</li>
+ * <li>Most importantly, Wrappers must be implemented so that Truffle optimization will reduce their
+ * runtime overhead to zero when there is no probe attached or when a probe has no attached
+ * {@link Instrument}s.</li>
+ * </ol>
  * <p>
  * <strong>Disclaimer:</strong> experimental interface under development.
+ *
+ * @see Probe
+ * @see ExecutionEvents
  */
 public interface Wrapper extends PhylumTagged {
 
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationImpl.java	Fri May 02 16:12:07 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,141 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, 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.impl;
-
-import java.util.*;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.source.*;
-
-public final class InstrumentationImpl implements Instrumentation {
-
-    private final ExecutionContext context;
-
-    // TODO (mlvdv) maps should really use weak references.
-
-    /**
-     * Map: SourceSection ==> probe associated with that source section in an AST.
-     */
-    private final Map<SourceSection, Probe> srcToProbe = new HashMap<>();
-
-    /**
-     * Map: Source line ==> probes associated with source sections starting on the line.
-     */
-    private final Map<SourceLineLocation, Collection<Probe>> lineToProbes = new HashMap<>();
-
-    private final List<ProbeListener> probeListeners = new ArrayList<>();
-
-    public InstrumentationImpl(ExecutionContext context) {
-        this.context = context;
-    }
-
-    public void addNodeProber(ASTNodeProber nodeProber) {
-        context.addNodeProber(nodeProber);
-    }
-
-    public void addProbeListener(ProbeListener listener) {
-        assert listener != null;
-        probeListeners.add(listener);
-    }
-
-    /**
-     * Return the (possibly newly created) {@link Probe} uniquely associated with a particular
-     * source code location. A newly created probe carries no tags.
-     *
-     * @param eventListener an optional listener for certain instrumentation-related events
-     * @return a probe uniquely associated with an extent of guest language source code.
-     */
-    public Probe getProbe(SourceSection sourceSection, InstrumentEventListener eventListener) {
-        assert sourceSection != null;
-
-        Probe probe = srcToProbe.get(sourceSection);
-
-        if (probe != null) {
-            return probe;
-        }
-        probe = InstrumentationNode.createProbe(this, sourceSection, eventListener);
-
-        // Register new probe by unique SourceSection
-        srcToProbe.put(sourceSection, probe);
-
-        // Register new probe by source line, there may be more than one
-        // Create line location for map key
-        final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine());
-
-        Collection<Probe> probes = lineToProbes.get(lineLocation);
-        if (probes == null) {
-            probes = new ArrayList<>(2);
-            lineToProbes.put(lineLocation, probes);
-        }
-        probes.add(probe);
-
-        for (ProbeListener listener : probeListeners) {
-            listener.newProbeInserted(sourceSection, probe);
-        }
-
-        return probe;
-    }
-
-    /**
-     * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty
-     * collection if no probes found.
-     */
-    public Collection<Probe> findProbesTaggedAs(PhylumTag tag) {
-        if (tag == null) {
-            return new ArrayList<>(srcToProbe.values());
-        }
-        final List<Probe> probes = new ArrayList<>();
-        for (Probe probe : srcToProbe.values()) {
-            if (probe.isTaggedAs(tag)) {
-                probes.add(probe);
-            }
-        }
-        return probes;
-    }
-
-    /**
-     * Returns all existing probes with first character on a specified line; empty collection if no
-     * probes found.
-     */
-    public Collection<Probe> findProbesByLine(SourceLineLocation lineLocation) {
-        final Collection<Probe> probes = lineToProbes.get(lineLocation);
-        if (probes == null) {
-            return Collections.emptyList();
-        }
-        return new ArrayList<>(probes);
-    }
-
-    /**
-     * Receives (from the {@link Probe} implementation) and distributes notification that a
-     * {@link Probe} has acquired a new {@linkplain PhylumTag tag}.
-     */
-    void newTagAdded(Probe probe, PhylumTag tag) {
-        for (ProbeListener listener : probeListeners) {
-            listener.probeTaggedAs(probe, tag);
-        }
-    }
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java	Fri May 02 16:12:07 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java	Mon May 12 20:17:25 2014 -0700
@@ -27,6 +27,7 @@
 import java.util.*;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.SlowPath;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.*;
 import com.oracle.truffle.api.nodes.*;
@@ -38,17 +39,18 @@
  */
 public abstract class InstrumentationNode extends Node implements ExecutionEvents {
 
-    // TODO (mlvdv) This is a pretty awkward design; it is a priority to revise it.
+    public interface ProbeCallback {
+        void newTagAdded(ProbeImpl probe, PhylumTag tag);
+    }
 
     /**
      * Creates a new {@link Probe}, presumed to be unique to a particular {@linkplain SourceSection}
      * extent of guest language source code.
      *
-     * @param eventListener an optional listener for certain instrumentation-related events.
      * @return a new probe
      */
-    static Probe createProbe(InstrumentationImpl instrumentation, SourceSection sourceSection, InstrumentEventListener eventListener) {
-        return new ProbeImpl(instrumentation, sourceSection, eventListener);
+    public static ProbeImpl createProbe(SourceSection sourceSection, ProbeCallback probeCallback) {
+        return new ProbeImpl(sourceSection, probeCallback);
     }
 
     /**
@@ -103,84 +105,84 @@
         probe.notifyProbeChanged(instrument);
     }
 
-    private void internalEnter(Node astNode, VirtualFrame frame) {
+    protected void internalEnter(Node astNode, VirtualFrame frame) {
         enter(astNode, frame);
         if (next != null) {
             next.internalEnter(astNode, frame);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame) {
+    protected void internalLeave(Node astNode, VirtualFrame frame) {
         leave(astNode, frame);
         if (next != null) {
             next.internalLeave(astNode, frame);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, boolean result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, boolean result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, byte result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, byte result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, short result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, short result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, int result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, int result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, long result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, long result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, char result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, char result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, float result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, float result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, double result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, double result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeave(Node astNode, VirtualFrame frame, Object result) {
+    protected void internalLeave(Node astNode, VirtualFrame frame, Object result) {
         leave(astNode, frame, result);
         if (next != null) {
             next.internalLeave(astNode, frame, result);
         }
     }
 
-    private void internalLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
+    protected void internalLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
         leaveExceptional(astNode, frame, null);
         if (next != null) {
             next.internalLeaveExceptional(astNode, frame, e);
@@ -197,100 +199,92 @@
      * May be categorized by one or more {@linkplain PhylumTag tags}, signifying information useful
      * for instrumentation about its AST location(s).
      */
-    private static final class ProbeImpl extends InstrumentationNode implements Probe {
-
-        final InstrumentationImpl instrumentation;
+    public static final class ProbeImpl extends InstrumentationNode implements Probe {
 
-        final InstrumentEventListener eventListener;
-
-        @CompilerDirectives.CompilationFinal private Assumption probeUnchanged;
+        private final ProbeCallback probeCallback;
 
         /**
-         * When in stepping mode, ordinary line breakpoints are ignored, but every entry at a line
-         * will cause a halt.
-         */
-        @CompilerDirectives.CompilationFinal private boolean stepping;
-
-        /**
-         * Source information about the AST node to which this instrumentation is attached.
+         * Source information about the AST node (and its clones) to which this probe is attached.
          */
         private final SourceSection probedSourceSection;
 
-        private final Set<PhylumTag> tags = EnumSet.noneOf(PhylumTag.class);
+        // TODO (mlvdv) assumption model broken
+        @CompilerDirectives.CompilationFinal private Assumption probeUnchanged;
+
+        @CompilerDirectives.CompilationFinal private PhylumTrap trap = null;
 
-        private ProbeImpl(InstrumentationImpl instrumentation, SourceSection sourceSection, InstrumentEventListener eventListener) {
-            this.instrumentation = instrumentation;
+        private final ArrayList<PhylumTag> tags = new ArrayList<>();
+
+        private ProbeImpl(SourceSection sourceSection, ProbeCallback probeCallback) {
+            this.probeCallback = probeCallback;
             this.probedSourceSection = sourceSection;
-            this.eventListener = eventListener == null ? NullInstrumentEventListener.INSTANCE : eventListener;
             this.probeUnchanged = Truffle.getRuntime().createAssumption();
             this.next = null;
         }
 
+        public SourceSection getSourceLocation() {
+            return probedSourceSection;
+        }
+
+        @SlowPath
+        public void tagAs(PhylumTag tag) {
+            assert tag != null;
+            if (!tags.contains(tag)) {
+                tags.add(tag);
+                probeCallback.newTagAdded(this, tag);
+            }
+        }
+
+        public boolean isTaggedAs(PhylumTag tag) {
+            assert tag != null;
+            return tags.contains(tag);
+        }
+
+        public Iterable<PhylumTag> getPhylumTags() {
+            return tags;
+        }
+
+        @SlowPath
+        public void addInstrument(Instrument instrument) {
+            probeUnchanged.invalidate();
+            super.internalAddInstrument(instrument);
+            probeUnchanged = Truffle.getRuntime().createAssumption();
+        }
+
+        @SlowPath
+        public void removeInstrument(Instrument instrument) {
+            probeUnchanged.invalidate();
+            super.internalRemoveInstrument(instrument);
+            probeUnchanged = Truffle.getRuntime().createAssumption();
+        }
+
         @Override
         public Probe getProbe() {
             return this;
         }
 
         @Override
+        @SlowPath
         protected void notifyProbeChanged(Instrument instrument) {
             probeUnchanged.invalidate();
             probeUnchanged = Truffle.getRuntime().createAssumption();
         }
 
-        public SourceSection getSourceLocation() {
-            return probedSourceSection;
-        }
-
-        public void tagAs(PhylumTag tag) {
-            assert tag != null;
-            if (!tags.contains(tag)) {
-                tags.add(tag);
-                instrumentation.newTagAdded(this, tag);
-            }
-        }
-
-        public boolean isTaggedAs(PhylumTag tag) {
-            assert tag != null;
-            return tags.contains(tag);
-        }
-
-        public Set<PhylumTag> getPhylumTags() {
-            return tags;
-        }
-
-        public void setStepping(boolean stepping) {
-            if (this.stepping != stepping) {
-                this.stepping = stepping;
-                probeUnchanged.invalidate();
-                probeUnchanged = Truffle.getRuntime().createAssumption();
-            }
-        }
-
-        public boolean isStepping() {
-            return stepping;
-        }
-
-        @CompilerDirectives.SlowPath
-        public void addInstrument(Instrument instrument) {
+        @SlowPath
+        public void setTrap(PhylumTrap trap) {
+            assert trap == null || isTaggedAs(trap.getTag());
             probeUnchanged.invalidate();
-            super.internalAddInstrument(instrument);
-            probeUnchanged = Truffle.getRuntime().createAssumption();
-        }
-
-        @CompilerDirectives.SlowPath
-        public void removeInstrument(Instrument instrument) {
-            probeUnchanged.invalidate();
-            super.internalRemoveInstrument(instrument);
+            this.trap = trap;
             probeUnchanged = Truffle.getRuntime().createAssumption();
         }
 
         public void notifyEnter(Node astNode, VirtualFrame frame) {
-            if (stepping || next != null) {
+            if (trap != null || next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
                 }
-                if (stepping) {
-                    eventListener.haltedAt(astNode, frame.materialize());
+                if (trap != null) {
+                    trap.phylumTrappedAt(astNode, frame.materialize());
                 }
                 if (next != null) {
                     next.internalEnter(astNode, frame);
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/NullInstrumentEventListener.java	Fri May 02 16:12:07 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,57 +0,0 @@
-/*
- * Copyright (c) 2013, 2014, 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.impl;
-
-import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.instrument.*;
-import com.oracle.truffle.api.nodes.*;
-
-/**
- * Minimal, mostly no-op implementation of instrumentation services.
- */
-public final class NullInstrumentEventListener implements InstrumentEventListener {
-
-    public static final InstrumentEventListener INSTANCE = new NullInstrumentEventListener();
-
-    private NullInstrumentEventListener() {
-    }
-
-    public void callEntering(Node astNode, String name) {
-    }
-
-    public void callReturned(Node astNode, String name) {
-    }
-
-    public void haltedAt(Node astNode, MaterializedFrame frame) {
-    }
-
-    public void loadStarting(Source source) {
-    }
-
-    public void loadEnding(Source source) {
-    }
-
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SourceCallback.java	Mon May 12 20:17:25 2014 -0700
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2014, 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.impl;
+
+import com.oracle.truffle.api.*;
+
+/**
+ * Instrumentation callback for guest language source-related events.
+ */
+public interface SourceCallback {
+
+    public void startLoading(Source source);
+
+    public void endLoading(Source source);
+}