changeset 19645:10a1fc5e3209

Truffle/Instrumentation: new, experimental kind of Instrument
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Sat, 28 Feb 2015 15:52:13 -0800
parents 3ed1c541f420
children c5f6a7397eb1 aed19d655de9 787b5aa53401
files graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java
diffstat 2 files changed, 261 insertions(+), 85 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java	Sat Feb 28 12:50:25 2015 -0800
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java	Sat Feb 28 15:52:13 2015 -0800
@@ -141,7 +141,7 @@
         final int[] count = {1};
         count[0] = 0;
         // Register a "prober" that will get applied when CallTarget gets created.
-        Probe.registerASTProber(new ASTProber() {
+        final ASTProber prober = new ASTProber() {
 
             @Override
             public void probeAST(Node node) {
@@ -157,31 +157,92 @@
 
                 });
             }
-        });
-        final RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(root);
+        };
+        Probe.registerASTProber(prober);
+        try {
+            final RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(root);
+
+            // The CallTarget has one Probe, attached to the ConstantTestNode, ready to run
+            Assert.assertEquals(42, callTarget.call()); // Correct result
+            Assert.assertEquals(0, count[0]);           // Didn't count anything
+
+            // Add a counting instrument; this changes the "Probe state" and should cause a deopt
+            final Instrument countingInstrument = Instrument.create(new DefaultEventListener() {
+
+                @Override
+                public void enter(Node node, VirtualFrame frame) {
+                    count[0] = count[0] + 1;
+                }
+            });
+            probe[0].attach(countingInstrument);
+
+            Assert.assertEquals(42, callTarget.call()); // Correct result
+            Assert.assertEquals(1, count[0]);           // Counted the first call
 
-        // The CallTarget has one Probe, attached to the ConstantTestNode, ready to run
-        Assert.assertEquals(42, callTarget.call()); // Correct result
-        Assert.assertEquals(0, count[0]);           // Didn't count anything
+            // Remove the counting instrument; this changes the "Probe state" and should cause a
+            // deopt
+            countingInstrument.dispose();
+
+            Assert.assertEquals(42, callTarget.call()); // Correct result
+            Assert.assertEquals(1, count[0]);           // Didn't count this time
+        } finally {
+            Probe.unregisterASTProber(prober);
+        }
+
+    }
 
-        // Add a counting instrument; this changes the "Probe state" and should cause a deopt
-        final Instrument countingInstrument = Instrument.create(new DefaultEventListener() {
+    /**
+     * Experimental feature; not yet validated.
+     */
+    @Test
+    public void specialOptInstrument() {
+        final FrameDescriptor fd = new FrameDescriptor();
+        final AbstractTestNode result = new ConstantTestNode(42);
+        final RootTestNode root = new RootTestNode(fd, "constantValue", result);
+        final Probe[] probe = new Probe[1];
+        final int[] count = {1};
+        count[0] = 0;
+        // Register a "prober" that will get applied when CallTarget gets created.
+        final ASTProber prober = new ASTProber() {
 
             @Override
-            public void enter(Node node, VirtualFrame frame) {
-                count[0] = count[0] + 1;
+            public void probeAST(Node node) {
+                node.accept(new NodeVisitor() {
+
+                    @Override
+                    public boolean visit(Node visitedNode) {
+                        if (visitedNode instanceof ConstantTestNode) {
+                            probe[0] = visitedNode.probe();
+                        }
+                        return true;
+                    }
+                });
             }
-        });
-        probe[0].attach(countingInstrument);
+        };
+        Probe.registerASTProber(prober);
+        try {
+            final RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(root);
 
-        Assert.assertEquals(42, callTarget.call()); // Correct result
-        Assert.assertEquals(1, count[0]);           // Counted the first call
+            // The CallTarget has one Probe, attached to the ConstantTestNode, ready to run
+            Assert.assertEquals(42, callTarget.call()); // Correct result
+
+            final boolean[] isCurrentlyCompiled = {false};
+            final Instrument optInstrument = Instrument.create(new Instrument.TruffleOptListener() {
 
-        // Remove the counting instrument; this changes the "Probe state" and should cause a deopt
-        countingInstrument.dispose();
+                public void notifyIsCompiled(boolean isCompiled) {
+                    isCurrentlyCompiled[0] = isCompiled;
+                }
+            });
+            probe[0].attach(optInstrument);
 
-        Assert.assertEquals(42, callTarget.call()); // Correct result
-        Assert.assertEquals(1, count[0]);           // Didn't count this time
+            Assert.assertEquals(42, callTarget.call()); // Correct result
+            Assert.assertFalse(isCurrentlyCompiled[0]);
+
+            // TODO (mlvdv) compile, call again, and assert that isCurrentlyCompiled == true
+
+        } finally {
+            Probe.unregisterASTProber(prober);
+        }
 
     }
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Sat Feb 28 12:50:25 2015 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Sat Feb 28 15:52:13 2015 -0800
@@ -24,13 +24,15 @@
  */
 package com.oracle.truffle.api.instrument;
 
+import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.frame.*;
 import com.oracle.truffle.api.instrument.impl.*;
 import com.oracle.truffle.api.nodes.*;
 
 // TODO (mlvdv) migrate some of this to external documentation.
+// TODO (mlvdv) move all this to a factory implemented in .impl (together with Probe),
+// then break out some of the nested classes into package privates.
 /**
  * A dynamically added/removed binding between a {@link Probe}, which provides notification of
  * {@linkplain TruffleEventListener execution events} taking place at a {@link Node} in a Guest
@@ -44,8 +46,8 @@
  * <li>Create an Instrument via factory method {@link Instrument#create(TruffleEventListener)}.</li>
  * <li>"Attach" the Instrument to a Probe via {@link Probe#attach(Instrument)}, at which point event
  * notifications begin to arrive at the listener.</li>
- * <li>When no longer needed, "detach" the Instrument via {@link Instrument#dispose()}, at which
- * point event notifications to the listener cease, and the Instrument becomes unusable.</li>
+ * <li>When no longer needed, "detach" the Instrument via {@link TruffleEventInstrument#dispose()},
+ * at which point event notifications to the listener cease, and the Instrument becomes unusable.</li>
  * </ol>
  * <p>
  * <h4>Options for creating listeners:</h4>
@@ -123,8 +125,8 @@
  * error to attempt attaching a previously attached instrument.</li>
  * <li>Attaching an instrument modifies every existing clone of the AST to which it is being
  * attached, which can trigger deoptimization.</li>
- * <li>The method {@link Instrument#dispose()} makes an instrument inactive by removing it from the
- * Probe to which it was attached and rendering it permanently inert.</li>
+ * <li>The method {@link TruffleEventInstrument#dispose()} makes an instrument inactive by removing
+ * it from the Probe to which it was attached and rendering it permanently inert.</li>
  * <li>Disposal removes the implementation of an instrument from all ASTs to which it was attached,
  * which can trigger deoptimization.</li>
  * </ul>
@@ -142,7 +144,7 @@
  * @see Probe
  * @see TruffleEventListener
  */
-public final class Instrument {
+public abstract class Instrument {
 
     /**
      * Creates an instrument that will route execution events to a listener.
@@ -152,25 +154,23 @@
      * @return a new instrument, ready for attachment at a probe
      */
     public static Instrument create(TruffleEventListener listener, String instrumentInfo) {
-        return new Instrument(listener, instrumentInfo);
+        return new TruffleEventInstrument(listener, instrumentInfo);
     }
 
     /**
      * Creates an instrument that will route execution events to a listener.
      */
     public static Instrument create(TruffleEventListener listener) {
-        return new Instrument(listener, null);
+        return new TruffleEventInstrument(listener, null);
     }
 
+    // TODO (mlvdv) experimental
     /**
-     * Tool-supplied listener for events.
+     * For implementation testing.
      */
-    private final TruffleEventListener toolEventListener;
-
-    /**
-     * Optional documentation, mainly for debugging.
-     */
-    private final String instrumentInfo;
+    public static Instrument create(TruffleOptListener listener) {
+        return new TruffleOptInstrument(listener, null);
+    }
 
     /**
      * Has this instrument been disposed? stays true once set.
@@ -179,8 +179,12 @@
 
     private Probe probe = null;
 
-    private Instrument(TruffleEventListener listener, String instrumentInfo) {
-        this.toolEventListener = listener;
+    /**
+     * Optional documentation, mainly for debugging.
+     */
+    private final String instrumentInfo;
+
+    private Instrument(String instrumentInfo) {
         this.instrumentInfo = instrumentInfo;
     }
 
@@ -216,35 +220,176 @@
         return isDisposed;
     }
 
-    InstrumentNode addToChain(InstrumentNode nextNode) {
-        return new InstrumentNode(nextNode);
-    }
+    abstract InstrumentNode addToChain(InstrumentNode nextNode);
 
     /**
      * Removes this instrument from an instrument chain.
      */
-    InstrumentNode removeFromChain(InstrumentNode instrumentNode) {
-        boolean found = false;
-        if (instrumentNode != null) {
-            if (instrumentNode.getInstrument() == this) {
-                // Found the match at the head of the chain
-                return instrumentNode.nextInstrument;
+    abstract InstrumentNode removeFromChain(InstrumentNode instrumentNode);
+
+    private static final class TruffleEventInstrument extends Instrument {
+
+        /**
+         * Tool-supplied listener for events.
+         */
+        private final TruffleEventListener toolEventListener;
+
+        private TruffleEventInstrument(TruffleEventListener listener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.toolEventListener = listener;
+        }
+
+        @Override
+        InstrumentNode addToChain(InstrumentNode nextNode) {
+            return new TruffleEventInstrumentNode(nextNode);
+        }
+
+        @Override
+        InstrumentNode removeFromChain(InstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrument;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(TruffleEventInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
+        }
+
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class TruffleEventInstrumentNode extends InstrumentNode {
+
+            private TruffleEventInstrumentNode(InstrumentNode nextNode) {
+                super(nextNode);
+            }
+
+            public void enter(Node node, VirtualFrame vFrame) {
+                TruffleEventInstrument.this.toolEventListener.enter(node, vFrame);
+                if (nextInstrument != null) {
+                    nextInstrument.enter(node, vFrame);
+                }
+            }
+
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                TruffleEventInstrument.this.toolEventListener.returnVoid(node, vFrame);
+                if (nextInstrument != null) {
+                    nextInstrument.returnVoid(node, vFrame);
+                }
+            }
+
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                TruffleEventInstrument.this.toolEventListener.returnValue(node, vFrame, result);
+                if (nextInstrument != null) {
+                    nextInstrument.returnValue(node, vFrame, result);
+                }
+            }
+
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                TruffleEventInstrument.this.toolEventListener.returnExceptional(node, vFrame, exception);
+                if (nextInstrument != null) {
+                    nextInstrument.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : toolEventListener.getClass().getSimpleName();
             }
-            // Match not at the head of the chain; remove it.
-            found = instrumentNode.removeFromChain(Instrument.this);
+        }
+
+    }
+
+    public interface TruffleOptListener {
+        void notifyIsCompiled(boolean isCompiled);
+    }
+
+    private static final class TruffleOptInstrument extends Instrument {
+
+        private final TruffleOptListener toolOptListener;
+
+        private TruffleOptInstrument(TruffleOptListener listener, String instrumentInfo) {
+            super(instrumentInfo);
+            this.toolOptListener = listener;
+        }
+
+        @Override
+        InstrumentNode addToChain(InstrumentNode nextNode) {
+            return new TruffleOptInstrumentNode(nextNode);
+        }
+
+        @Override
+        InstrumentNode removeFromChain(InstrumentNode instrumentNode) {
+            boolean found = false;
+            if (instrumentNode != null) {
+                if (instrumentNode.getInstrument() == this) {
+                    // Found the match at the head of the chain
+                    return instrumentNode.nextInstrument;
+                }
+                // Match not at the head of the chain; remove it.
+                found = instrumentNode.removeFromChain(TruffleOptInstrument.this);
+            }
+            if (!found) {
+                throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+            }
+            return instrumentNode;
         }
-        if (!found) {
-            throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
+
+        @NodeInfo(cost = NodeCost.NONE)
+        private final class TruffleOptInstrumentNode extends InstrumentNode {
+
+            private boolean isCompiled;
+
+            private TruffleOptInstrumentNode(InstrumentNode nextNode) {
+                super(nextNode);
+                this.isCompiled = CompilerDirectives.inCompiledCode();
+            }
+
+            public void enter(Node node, VirtualFrame vFrame) {
+                if (this.isCompiled != CompilerDirectives.inCompiledCode()) {
+                    this.isCompiled = CompilerDirectives.inCompiledCode();
+                    TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled);
+                }
+                if (nextInstrument != null) {
+                    nextInstrument.enter(node, vFrame);
+                }
+            }
+
+            public void returnVoid(Node node, VirtualFrame vFrame) {
+                if (nextInstrument != null) {
+                    nextInstrument.returnVoid(node, vFrame);
+                }
+            }
+
+            public void returnValue(Node node, VirtualFrame vFrame, Object result) {
+                if (nextInstrument != null) {
+                    nextInstrument.returnValue(node, vFrame, result);
+                }
+            }
+
+            public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
+                if (nextInstrument != null) {
+                    nextInstrument.returnExceptional(node, vFrame, exception);
+                }
+            }
+
+            public String instrumentationInfo() {
+                final String info = getInstrumentInfo();
+                return info != null ? info : toolOptListener.getClass().getSimpleName();
+            }
         }
-        return instrumentNode;
+
     }
 
     @NodeInfo(cost = NodeCost.NONE)
-    final class InstrumentNode extends Node implements TruffleEventListener, InstrumentationNode {
+    abstract class InstrumentNode extends Node implements TruffleEventListener, InstrumentationNode {
+        @Child protected InstrumentNode nextInstrument;
 
-        @Child private InstrumentNode nextInstrument;
-
-        private InstrumentNode(InstrumentNode nextNode) {
+        protected InstrumentNode(InstrumentNode nextNode) {
             this.nextInstrument = nextNode;
         }
 
@@ -286,40 +431,10 @@
             return nextInstrument.removeFromChain(instrument);
         }
 
-        public void enter(Node node, VirtualFrame vFrame) {
-            Instrument.this.toolEventListener.enter(node, vFrame);
-            if (nextInstrument != null) {
-                nextInstrument.enter(node, vFrame);
-            }
-        }
-
-        public void returnVoid(Node node, VirtualFrame vFrame) {
-            Instrument.this.toolEventListener.returnVoid(node, vFrame);
-            if (nextInstrument != null) {
-                nextInstrument.returnVoid(node, vFrame);
-            }
+        protected String getInstrumentInfo() {
+            return Instrument.this.instrumentInfo;
         }
 
-        public void returnValue(Node node, VirtualFrame vFrame, Object result) {
-            Instrument.this.toolEventListener.returnValue(node, vFrame, result);
-            if (nextInstrument != null) {
-                nextInstrument.returnValue(node, vFrame, result);
-            }
-        }
-
-        public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
-            Instrument.this.toolEventListener.returnExceptional(node, vFrame, exception);
-            if (nextInstrument != null) {
-                nextInstrument.returnExceptional(node, vFrame, exception);
-            }
-        }
-
-        public String instrumentationInfo() {
-            if (Instrument.this.instrumentInfo != null) {
-                return Instrument.this.instrumentInfo;
-            }
-            return toolEventListener.getClass().getSimpleName();
-        }
     }
 
 }