# HG changeset patch # User Michael Van De Vanter # Date 1425167533 28800 # Node ID 10a1fc5e32090a6a793b64149d6f2fdf6c8c0c28 # Parent 3ed1c541f420e52131d4378f4f11af6d30236e1c Truffle/Instrumentation: new, experimental kind of Instrument diff -r 3ed1c541f420 -r 10a1fc5e3209 graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java --- 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); + } } } diff -r 3ed1c541f420 -r 10a1fc5e3209 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- 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 @@ *
  • Create an Instrument via factory method {@link Instrument#create(TruffleEventListener)}.
  • *
  • "Attach" the Instrument to a Probe via {@link Probe#attach(Instrument)}, at which point event * notifications begin to arrive at the listener.
  • - *
  • 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.
  • + *
  • 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.
  • * *

    *

    Options for creating listeners:

    @@ -123,8 +125,8 @@ * error to attempt attaching a previously attached instrument. *
  • Attaching an instrument modifies every existing clone of the AST to which it is being * attached, which can trigger deoptimization.
  • - *
  • 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.
  • + *
  • 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.
  • *
  • Disposal removes the implementation of an instrument from all ASTs to which it was attached, * which can trigger deoptimization.
  • * @@ -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(); - } } }