changeset 15779:8c34e2cc4add

Truffle/Instrumentation: significant reorganization of the instrumentation framework's implementation and connection to the runtime ExecutionContext, with some new features, including a Tag-based "trap" mechanisms.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Mon, 19 May 2014 17:14:36 -0700
parents dcaf3993ad17
children bdf260d8e163
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/Instrument.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/SourceCallback.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.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/ProbeManager.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SourceCallback.java
diffstat 9 files changed, 390 insertions(+), 475 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java	Tue May 13 18:31:18 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java	Mon May 19 17:14:36 2014 -0700
@@ -27,31 +27,131 @@
 import java.util.*;
 
 import com.oracle.truffle.api.instrument.*;
+import com.oracle.truffle.api.instrument.impl.*;
 import com.oracle.truffle.api.source.*;
 
 /**
  * Access to information and basic services in the runtime context for a Truffle-implemented guest
  * language.
  * <p>
- * <strong>Disclaimer:</strong> this interface is under development and will change.
+ * <strong>Disclaimer:</strong> this class is under development and will change.
  */
-public interface ExecutionContext {
+public abstract class ExecutionContext {
+
+    private final ProbeManager probeManager = new ProbeManager();
+    private final SourceManager sourceManager = new SourceManager();
+    private final List<SourceListener> sourceListeners = new ArrayList<>();
+    private Visualizer visualizer = new DefaultVisualizer();
+
+    protected ExecutionContext() {
+    }
+
+    public void initialize() {
+        setSourceCallback(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);
+                }
+            }
+        });
+    }
+
+    /**
+     * Gets access to source management services.
+     */
+    public final SourceManager getSourceManager() {
+        return sourceManager;
+    }
+
+    /**
+     * Registers a tool interested in being notified about the loading of {@link Source}s.
+     */
+    public final void addSourceListener(SourceListener listener) {
+        assert listener != null;
+        sourceListeners.add(listener);
+    }
+
+    /**
+     * Registers a tool interested in being notified about the insertion of a newly created
+     * {@link Probe} into a Truffle AST.
+     */
+    public final void addProbeListener(ProbeListener listener) {
+        probeManager.addProbeListener(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.
+     */
+    public final Probe getProbe(SourceSection sourceSection) {
+        return probeManager.getProbe(sourceSection);
+    }
+
+    /**
+     * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty
+     * collection if no probes found.
+     */
+    public final Collection<Probe> findProbesTaggedAs(PhylumTag tag) {
+        return probeManager.findProbesTaggedAs(tag);
+    }
+
+    /**
+     * Returns all existing probes with first character on a specified line; empty collection if no
+     * probes found.
+     */
+    public final Collection<Probe> findProbesByLine(SourceLineLocation lineLocation) {
+        return probeManager.findProbesByLine(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
+     */
+    public final void setPhylumTrap(PhylumTrap trap) throws IllegalStateException {
+        // TODO (mlvdv) consider allowing multiple traps (without inhibiting Truffle inlining)
+        probeManager.setPhylumTrap(trap);
+    }
+
+    /**
+     * Clears a trap that will halt execution; only one trap may be set at a time.
+     *
+     * @throws IllegalStateException if no trap is set.
+     */
+    public final void clearPhylumTrap() {
+        probeManager.clearPhylumTrap();
+    }
+
+    /**
+     * Access to information visualization services for the specific language.
+     */
+    public final Visualizer getVisualizer() {
+        return visualizer;
+    }
+
+    /**
+     * Assign guest language-specific visualization support for tools. This must be assigned outside
+     * the implementation context to avoid build circularities.
+     */
+    public final void setVisualizer(Visualizer visualizer) {
+        this.visualizer = visualizer;
+    }
 
     /**
      * Gets the name of the language, possibly with version number. in short enough form that it
      * might be used for an interactive prompt.
      */
-    String getLanguageShortName();
-
-    /**
-     * Gets access to source management services.
-     */
-    SourceManager getSourceManager();
-
-    /**
-     * Registers a tool interested in being notified of events related to the loading of sources.
-     */
-    void addSourceListener(SourceListener listener);
+    public abstract String getLanguageShortName();
 
     /**
      * Add instrumentation to subsequently constructed Truffle ASTs for the guest language; every
@@ -60,51 +160,19 @@
      * @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.
-     */
-    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);
+    public abstract void addNodeProber(ASTNodeProber nodeProber) throws IllegalStateException, IllegalArgumentException;
 
     /**
-     * Returns all existing probes with first character on a specified line; empty collection if no
-     * probes found.
+     * Assigns a guest language-specific manager for using {@link ASTNodeProber}s added by tools to
+     * instrument ASTs with {@link Probe}s at specified nodes. This must be assigned outside the
+     * implementation context to avoid build circularities. It must also be set before any
+     * instrumentation probe implementations are assigned.
      */
-    Collection<Probe> findProbesByLine(SourceLineLocation lineLocation);
+    public abstract void setASTProber(ASTProber astProber);
 
     /**
-     * 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
+     * Establishes source event reporting
      */
-    void setTrap(PhylumTrap trap) throws IllegalStateException;
+    protected abstract void setSourceCallback(SourceCallback sourceCallback);
 
-    /**
-     * 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();
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/AbstractExecutionContext.java	Tue May 13 18:31:18 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,217 +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.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 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();
-
-    /**
-     * When non-null, "enter" events with matching tags will trigger a callback.
-     */
-    private PhylumTrap trap = null;
-
-    protected AbstractExecutionContext() {
-    }
-
-    public void initialize() {
-        setSourceCallback(sourceCallback);
-    }
-
-    public final SourceManager getSourceManager() {
-        return sourceManager;
-    }
-
-    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;
-    }
-
-    /**
-     * Assign guest language-specific visualization support for tools. This must be assigned outside
-     * the implementation context to avoid build circularities.
-     */
-    public void setVisualizer(Visualizer visualizer) {
-        this.visualizer = visualizer;
-    }
-
-    /**
-     * Assigns a guest language-specific manager for using {@link ASTNodeProber}s added by tools to
-     * instrument ASTs with {@link Probe}s at specified nodes. This must be assigned outside the
-     * implementation context to avoid build circularities. It must also be set before any
-     * instrumentation probe implementations are assigned.
-     */
-    public abstract void setASTProber(ASTProber astProber);
-
-    /**
-     * Establishes source event reporting
-     */
-    protected abstract void setSourceCallback(SourceCallback sourceCallback);
-
-}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue May 13 18:31:18 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Mon May 19 17:14:36 2014 -0700
@@ -31,29 +31,29 @@
 import com.oracle.truffle.api.nodes.*;
 
 /**
- * A receiver of Truffle AST {@link ExecutionEvents}, propagated from a {@link Probe} to which the
- * instrument is attached, for the benefit of associated <em>tools</em>.
+ * A receiver of Truffle AST runtime {@link ExecutionEvents}, propagated from the {@link Probe} to
+ * which the instrument is attached, for the benefit of associated <em>tools</em>.
  * <p>
- * Guidelines for implementing Instruments, with particular attention to avoiding undesired runtime
+ * Guidelines for implementing {@link Instrument}s, with particular attention to minimize runtime
  * performance overhead:
  * <ol>
- * <li>Extend {@link Instrument} and override only the event handling methods for which some action
- * is needed.</li>
+ * <li>Extend this abstract class and override only the {@linkplain ExecutionEvents event handling
+ * methods} for which intervention is needed.</li>
  * <li>Instruments are Truffle {@link Node}s and should be coded as much as possible in the desired
  * <em>Truffle style</em>, documented more thoroughly elsewhere.</li>
  * <li>Maintain as little state as possible.</li>
- * <li>If state is necessary, make it {@code final} if possible.</li>
- * <li>If non-final state is necessary, annotate it as {@link CompilationFinal} and call
- * {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.</li>
+ * <li>If state is necessary, make object fields {@code final} if at all possible.</li>
+ * <li>If non-final object-valued state is necessary, annotate it as {@link CompilationFinal} and
+ * call {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.</li>
  * <li>Never store a {@link Frame} value in a field.</li>
  * <li>Minimize computation in standard execution paths.</li>
- * <li>Callbacks to tools should be made via callbacks provided at construction and stored in
- * {@code final} fields.</li>
- * <li>Tool callback methods should usually be annotated as {@link SlowPath} to prevent them from
- * being inlined into fast execution paths.</li>
- * <li>If computation is needed, and if performance is important, then the computation is best
- * expressed as a guest language AST and evaluated using standard Truffle mechanisms so that
- * standard Truffle optimizations can be applied.</li>
+ * <li>If runtime calls must be made back to a tool, construct the instrument with a callback stored
+ * in a {@code final} field.</li>
+ * <li>Tool methods called by the instrument should be annotated as {@link SlowPath} to prevent them
+ * from being inlined into fast execution paths.</li>
+ * <li>If computation in the execution path is needed, and if performance is important, then the
+ * computation is best expressed as a guest language AST and evaluated using standard Truffle
+ * mechanisms so that standard Truffle optimizations can be applied.</li>
  * </ol>
  * <p>
  * Guidelines for attachment to a {@link Probe}:
@@ -65,6 +65,18 @@
  * every copy, and so the Instrument will receive events corresponding to the intended syntactic
  * unit of code, independent of which AST copy is being executed.</li>
  * </ol>
+ * <p>
+ * Guidelines for handling {@link ExecutionEvents}:
+ * <ol>
+ * <li>Separate event methods are defined for each kind of possible return: object-valued,
+ * primitive-valued, void-valued, and exceptional.</li>
+ * <li>Override "leave*" primitive methods if the language implementation returns primitives and the
+ * instrument should avoid boxing them.</li>
+ * <li>On the other hand, if boxing all primitives for instrumentation is desired, it is only
+ * necessary to override the object-valued return methods, since the default implementation of each
+ * primitive-valued return method is to box the value and forward it to the object-valued return
+ * method.</li>
+ * </ol>
  *
  * <p>
  * <strong>Disclaimer:</strong> experimental; under development.
@@ -72,7 +84,7 @@
  * @see Probe
  * @see ASTNodeProber
  */
-public class Instrument extends InstrumentationNode {
+public abstract class Instrument extends InstrumentationNode {
 
     protected Instrument() {
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Tue May 13 18:31:18 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java	Mon May 19 17:14:36 2014 -0700
@@ -25,8 +25,6 @@
 package com.oracle.truffle.api.instrument;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.frame.*;
-import com.oracle.truffle.api.nodes.*;
 
 /**
  * A collector of {@link ExecutionEvents} at a specific site (node) in a Truffle AST (generated by a
@@ -54,7 +52,7 @@
  * @see Instrument
  * @see Wrapper
  */
-public interface Probe extends PhylumTagged {
+public interface Probe extends ExecutionEvents, PhylumTagged {
 
     /**
      * The source location with which this probe is (presumably uniquely) associated.
@@ -77,66 +75,4 @@
      */
     void removeInstrument(Instrument oldInstrument);
 
-    // TODO (mlvdv) migrate the remaining methods to another interface.
-
-    /**
-     * @see ExecutionEvents#enter(Node, VirtualFrame)
-     */
-    void notifyEnter(Node astNode, VirtualFrame frame);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, boolean)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, boolean result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, byte)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, byte result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, short)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, short result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, int)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, int result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, long)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, long result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, char)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, char result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, float)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, float result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, double)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, double result);
-
-    /**
-     * @see ExecutionEvents#leave(Node, VirtualFrame, Object)
-     */
-    void notifyLeave(Node astNode, VirtualFrame frame, Object result);
-
-    /**
-     * @see ExecutionEvents#leaveExceptional(Node, VirtualFrame, Exception)
-     */
-    void notifyLeaveExceptional(Node astNode, VirtualFrame frame, Exception e);
-
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceCallback.java	Mon May 19 17:14:36 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;
+
+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);
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Tue May 13 18:31:18 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java	Mon May 19 17:14:36 2014 -0700
@@ -77,13 +77,14 @@
         }
         if (node instanceof PhylumTagged) {
             final PhylumTagged taggedNode = (PhylumTagged) node;
+            p.print("[");
             String prefix = "";
             for (PhylumTag tag : taggedNode.getPhylumTags()) {
                 p.print(prefix);
                 prefix = ",";
                 p.print(tag.toString());
             }
-
+            p.print("]");
         }
 
         ArrayList<NodeField> childFields = new ArrayList<>();
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java	Tue May 13 18:31:18 2014 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java	Mon May 19 17:14:36 2014 -0700
@@ -39,7 +39,7 @@
  */
 public abstract class InstrumentationNode extends Node implements ExecutionEvents {
 
-    public interface ProbeCallback {
+    interface ProbeCallback {
         void newTagAdded(ProbeImpl probe, PhylumTag tag);
     }
 
@@ -49,7 +49,7 @@
      *
      * @return a new probe
      */
-    public static ProbeImpl createProbe(SourceSection sourceSection, ProbeCallback probeCallback) {
+    static ProbeImpl createProbe(SourceSection sourceSection, ProbeCallback probeCallback) {
         return new ProbeImpl(sourceSection, probeCallback);
     }
 
@@ -64,7 +64,7 @@
     /**
      * @return the instance of {@link Probe} to which this instrument is attached.
      */
-    public Probe getProbe() {
+    protected Probe getProbe() {
         final InstrumentationNode parent = (InstrumentationNode) getParent();
         return parent == null ? null : parent.getProbe();
     }
@@ -199,7 +199,7 @@
      * May be categorized by one or more {@linkplain PhylumTag tags}, signifying information useful
      * for instrumentation about its AST location(s).
      */
-    public static final class ProbeImpl extends InstrumentationNode implements Probe {
+    static final class ProbeImpl extends InstrumentationNode implements Probe {
 
         private final ProbeCallback probeCallback;
 
@@ -259,7 +259,7 @@
         }
 
         @Override
-        public Probe getProbe() {
+        protected Probe getProbe() {
             return this;
         }
 
@@ -271,14 +271,14 @@
         }
 
         @SlowPath
-        public void setTrap(PhylumTrap trap) {
+        void setTrap(PhylumTrap trap) {
             assert trap == null || isTaggedAs(trap.getTag());
             probeUnchanged.invalidate();
             this.trap = trap;
             probeUnchanged = Truffle.getRuntime().createAssumption();
         }
 
-        public void notifyEnter(Node astNode, VirtualFrame frame) {
+        public void enter(Node astNode, VirtualFrame frame) {
             if (trap != null || next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -292,7 +292,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame) {
+        public void leave(Node astNode, VirtualFrame frame) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -301,16 +301,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, boolean result) {
-            if (next != null) {
-                if (!probeUnchanged.isValid()) {
-                    CompilerDirectives.transferToInterpreter();
-                }
-                next.internalLeave(astNode, frame, result);
-            }
-        }
-
-        public void notifyLeave(Node astNode, VirtualFrame frame, byte result) {
+        public void leave(Node astNode, VirtualFrame frame, boolean result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -319,16 +310,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, short result) {
-            if (next != null) {
-                if (!probeUnchanged.isValid()) {
-                    CompilerDirectives.transferToInterpreter();
-                }
-                next.internalLeave(astNode, frame, result);
-            }
-        }
-
-        public void notifyLeave(Node astNode, VirtualFrame frame, int result) {
+        public void leave(Node astNode, VirtualFrame frame, byte result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -337,7 +319,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, long result) {
+        public void leave(Node astNode, VirtualFrame frame, short result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -346,16 +328,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, char result) {
-            if (next != null) {
-                if (!probeUnchanged.isValid()) {
-                    CompilerDirectives.transferToInterpreter();
-                }
-                next.internalLeave(astNode, frame, result);
-            }
-        }
-
-        public void notifyLeave(Node astNode, VirtualFrame frame, float result) {
+        public void leave(Node astNode, VirtualFrame frame, int result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -364,7 +337,16 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, double result) {
+        public void leave(Node astNode, VirtualFrame frame, long result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, char result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -373,7 +355,7 @@
             }
         }
 
-        public void notifyLeave(Node astNode, VirtualFrame frame, Object result) {
+        public void leave(Node astNode, VirtualFrame frame, float result) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -382,7 +364,25 @@
             }
         }
 
-        public void notifyLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
+        public void leave(Node astNode, VirtualFrame frame, double result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void leave(Node astNode, VirtualFrame frame, Object result) {
+            if (next != null) {
+                if (!probeUnchanged.isValid()) {
+                    CompilerDirectives.transferToInterpreter();
+                }
+                next.internalLeave(astNode, frame, result);
+            }
+        }
+
+        public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
             if (next != null) {
                 if (!probeUnchanged.isValid()) {
                     CompilerDirectives.transferToInterpreter();
@@ -391,50 +391,6 @@
             }
         }
 
-        public void enter(Node astNode, VirtualFrame frame) {
-        }
-
-        public void leave(Node astNode, VirtualFrame frame) {
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, boolean result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, byte result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, short result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, int result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, long result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, char result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, float result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, double result) {
-            leave(astNode, frame, (Object) result);
-        }
-
-        public void leave(Node astNode, VirtualFrame frame, Object result) {
-        }
-
-        public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) {
-        }
-
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/ProbeManager.java	Mon May 19 17:14:36 2014 -0700
@@ -0,0 +1,159 @@
+/*
+ * 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.instrument.impl.InstrumentationNode.ProbeCallback;
+import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeImpl;
+import com.oracle.truffle.api.source.*;
+
+/**
+ * Factory and services for AST {@link Probe}s
+ */
+public final class ProbeManager {
+
+    // 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 List<ProbeListener> probeListeners = new ArrayList<>();
+
+    private final ProbeCallback probeCallback;
+
+    /**
+     * When non-null, "enter" events with matching tags will trigger a callback.
+     */
+    private PhylumTrap phylumTrap = null;
+
+    public ProbeManager() {
+        this.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 (phylumTrap != null && tag == phylumTrap.getTag()) {
+                    probe.setTrap(phylumTrap);
+                }
+            }
+        };
+    }
+
+    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;
+    }
+
+    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);
+    }
+
+    public void setPhylumTrap(PhylumTrap trap) {
+        assert trap != null;
+        if (this.phylumTrap != null) {
+            throw new IllegalStateException("trap already set");
+        }
+        this.phylumTrap = trap;
+
+        PhylumTag tag = trap.getTag();
+        for (ProbeImpl probe : srcToProbe.values()) {
+            if (probe.isTaggedAs(tag)) {
+                probe.setTrap(trap);
+            }
+        }
+    }
+
+    public void clearPhylumTrap() {
+        if (this.phylumTrap == null) {
+            throw new IllegalStateException("no trap set");
+        }
+        for (ProbeImpl probe : srcToProbe.values()) {
+            if (probe.isTaggedAs(phylumTrap.getTag())) {
+                probe.setTrap(null);
+            }
+        }
+        phylumTrap = null;
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SourceCallback.java	Tue May 13 18:31:18 2014 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,37 +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.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);
-}