# HG changeset patch # User Michael Van De Vanter # Date 1428967588 25200 # Node ID 518ce9a36939d45be7d4460c2dae2209404818b3 # Parent 0b2e4d40b683069d7f9ec0157b797d47cd5e6075# Parent f0d8a33aebd1e67d464ea8479d4777d73c146648 Merge with f0d8a33aebd1e67d464ea8479d4777d73c146648 diff -r f0d8a33aebd1 -r 518ce9a36939 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 Mon Apr 13 15:55:23 2015 -0700 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/InstrumentationPartialEvaluationTest.java Mon Apr 13 16:26:28 2015 -0700 @@ -205,43 +205,45 @@ } @Test - public void constantValueInertToolNodeInstrumentListener() { + public void constantValueInertSpliceInstrumentListener() { FrameDescriptor fd = new FrameDescriptor(); AbstractTestNode result = new ConstantTestNode(42); RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - // A listener that could insert a "tool node" into the AST, but which never does. - Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + // A listener that could insert a SplicedNode into the AST, but which never does. + Instrument instrument = Instrument.create(new SpliceInstrumentListener() { - public ToolNode getToolNode(Probe p) { + public SplicedNode getSpliceNode(Probe p) { return null; } }, null); probe.attach(instrument); + // It all gets compiled away assertPartialEvalEquals("constant42", root); } @Test - public void constantValueInertToolNode() { + public void constantValueInertSplicedNode() { FrameDescriptor fd = new FrameDescriptor(); AbstractTestNode result = new ConstantTestNode(42); RootTestNode root = new RootTestNode(fd, "constantValue", result); root.adoptChildren(); Probe probe = result.probe(); - // A listener that inserts a "tool node" with empty methods into the AST. - Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { + // A listener that inserts a SplicedNode with empty methods into the AST. + Instrument instrument = Instrument.create(new SpliceInstrumentListener() { - public ToolNode getToolNode(Probe p) { - return new ToolNode() { + public SplicedNode getSpliceNode(Probe p) { + return new SplicedNode() { }; } }, null); probe.attach(instrument); + // It all gets compiled away. assertPartialEvalEquals("constant42", root); } diff -r f0d8a33aebd1 -r 518ce9a36939 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Mon Apr 13 15:55:23 2015 -0700 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Mon Apr 13 16:26:28 2015 -0700 @@ -86,7 +86,6 @@ public Object execute(VirtualFrame vFrame) { probeNode.enter(child, vFrame); Object result; - try { result = child.execute(vFrame); probeNode.returnValue(child, vFrame, result); @@ -96,7 +95,6 @@ probeNode.returnExceptional(child, vFrame, e); throw (e); } - return result; } } @@ -169,4 +167,17 @@ } } + static class TestSplicedCounterNode extends SplicedNode { + + private long count; + + @Override + public void enter(Node node, VirtualFrame vFrame) { + count++; + } + + public long getCount() { + return count; + } + } } diff -r f0d8a33aebd1 -r 518ce9a36939 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/SpliceInstrumentTest.java Mon Apr 13 16:26:28 2015 -0700 @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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.test.instrument; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestSplicedCounterNode; + +/** + * Tests the kind of instrumentation where a client can provide an AST fragment to be + * spliced directly into the AST. + */ +public class SpliceInstrumentTest { + + @Test + public void testSpliceInstrumentListener() { + // Create a simple addition AST + final TruffleRuntime runtime = Truffle.getRuntime(); + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final TestRootNode rootNode = new TestRootNode(addNode); + final CallTarget callTarget1 = runtime.createCallTarget(rootNode); + + // Ensure it executes correctly + assertEquals(13, callTarget1.call()); + + // Probe the addition node + final Probe probe = addNode.probe(); + + assertEquals(13, callTarget1.call()); + + // Attach a null listener; it never actually attaches a node. + final Instrument instrument = Instrument.create(new SpliceInstrumentListener() { + + public SplicedNode getSpliceNode(Probe p) { + return null; + } + + }, null); + probe.attach(instrument); + + assertEquals(13, callTarget1.call()); + + final TestSplicedCounterNode counter = new TestSplicedCounterNode(); + + // Attach a listener that splices an execution counter into the AST. + probe.attach(Instrument.create(new SpliceInstrumentListener() { + + public SplicedNode getSpliceNode(Probe p) { + return counter; + } + + }, null)); + assertEquals(0, counter.getCount()); + + assertEquals(13, callTarget1.call()); + + assertEquals(1, counter.getCount()); + + } + +} diff -r f0d8a33aebd1 -r 518ce9a36939 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/ToolNodeInstrumentationTest.java Mon Apr 13 15:55:23 2015 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * 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.test.instrument; - -import static org.junit.Assert.*; - -import org.junit.*; - -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; - -/** - * Tests instrumentation where a client can attach a node that gets attached into the AST. - */ -public class ToolNodeInstrumentationTest { - - @Test - public void testToolNodeListener() { - // Create a simple addition AST - final TruffleRuntime runtime = Truffle.getRuntime(); - final TestValueNode leftValueNode = new TestValueNode(6); - final TestValueNode rightValueNode = new TestValueNode(7); - final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); - final TestRootNode rootNode = new TestRootNode(addNode); - final CallTarget callTarget1 = runtime.createCallTarget(rootNode); - - // Ensure it executes correctly - assertEquals(13, callTarget1.call()); - - // Probe the addition node - final Probe probe = addNode.probe(); - - assertEquals(13, callTarget1.call()); - - // Attach a listener that never actually attaches a node. - final Instrument instrument = Instrument.create(new ToolNodeInstrumentListener() { - - public ToolNode getToolNode(Probe p) { - return null; - } - - }, null); - probe.attach(instrument); - - assertEquals(13, callTarget1.call()); - - final int[] count = new int[1]; - - // Attach a listener that never actually attaches a node. - probe.attach(Instrument.create(new ToolNodeInstrumentListener() { - - public ToolNode getToolNode(Probe p) { - return new ToolNode() { - - @Override - public void enter(Node node, VirtualFrame vFrame) { - count[0] = count[0] + 1; - } - }; - } - - }, null)); - assertEquals(0, count[0]); - - assertEquals(13, callTarget1.call()); - - assertEquals(1, count[0]); - - } - -} diff -r f0d8a33aebd1 -r 518ce9a36939 graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java --- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java Mon Apr 13 15:55:23 2015 -0700 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java Mon Apr 13 16:26:28 2015 -0700 @@ -54,9 +54,9 @@ // Initially has only the default tag assertEquals(source.getSourceTags().size(), 1); - assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL)); - assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL)); - assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source)); + assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL)); + assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL)); + assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source)); assertFalse(source.isTaggedAs(testTag)); assertEquals(Source.findSourcesTaggedAs(testTag).size(), 0); @@ -67,9 +67,9 @@ // Now there are exactly two tags assertEquals(source.getSourceTags().size(), 2); - assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL)); - assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL)); - assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source)); + assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL)); + assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL)); + assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source)); assertTrue(source.getSourceTags().contains(testTag)); assertTrue(source.isTaggedAs(testTag)); @@ -82,9 +82,9 @@ // Nothing has changed assertEquals(source.getSourceTags().size(), 2); - assertTrue(source.getSourceTags().contains(StandardSourceTag.FROM_LITERAL)); - assertTrue(source.isTaggedAs(StandardSourceTag.FROM_LITERAL)); - assertTrue(Source.findSourcesTaggedAs(StandardSourceTag.FROM_LITERAL).contains(source)); + assertTrue(source.getSourceTags().contains(Source.Tags.FROM_LITERAL)); + assertTrue(source.isTaggedAs(Source.Tags.FROM_LITERAL)); + assertTrue(Source.findSourcesTaggedAs(Source.Tags.FROM_LITERAL).contains(source)); assertTrue(source.getSourceTags().contains(testTag)); assertTrue(source.isTaggedAs(testTag)); @@ -135,7 +135,7 @@ assertEquals(newSource[0], source); assertEquals(newTagEvents[0], 1); assertEquals(taggedSource[0], source); - assertEquals(newTag[0], StandardSourceTag.FROM_LITERAL); + assertEquals(newTag[0], Source.Tags.FROM_LITERAL); // reset newSource[0] = null; diff -r f0d8a33aebd1 -r 518ce9a36939 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 Mon Apr 13 15:55:23 2015 -0700 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Mon Apr 13 16:26:28 2015 -0700 @@ -30,8 +30,9 @@ import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; -// 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. +// TODO (mlvdv) these statics should not be global. Move them to some kind of context. +// TODO (mlvdv) migrate factory into .impl (together with Probe)? break out nested classes? + /** * A binding between: *
    @@ -41,8 +42,8 @@ *
*

* Client-oriented documentation for the use of Instruments is available online at Listening for - * Execution Events + * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" + * >https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events *

* The implementation of Instruments is complicated by the requirement that Truffle be able to clone * ASTs at any time. In particular, any instrumentation-supporting Nodes that have been attached to @@ -62,29 +63,38 @@ *

* * @see Probe @@ -118,15 +128,16 @@ /** * Creates an instrument that, when executed the first time in any particular AST location, - * invites the tool to provide an AST fragment for attachment/adoption into the running AST. + * invites the tool to provide an AST fragment for splicing directly into the running + * AST. * - * @param toolNodeListener a listener for the tool that can request an AST fragment + * @param spliceListener a callback to the client that requests an AST node to be splice. * @param instrumentInfo instrumentInfo optional description of the instrument's role, useful * for debugging. * @return a new instrument, ready for attachment at a probe. */ - public static Instrument create(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) { - return new ToolNodeInstrument(toolNodeListener, instrumentInfo); + public static Instrument create(SpliceInstrumentListener spliceListener, String instrumentInfo) { + return new SpliceInstrument(spliceListener, instrumentInfo); } // TODO (mlvdv) experimental @@ -187,6 +198,11 @@ abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode); /** + * Removes this instrument from an instrument chain. + */ + abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode); + + /** * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}. */ private static final class SimpleInstrument extends Instrument { @@ -266,11 +282,6 @@ } /** - * Removes this instrument from an instrument chain. - */ - abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode); - - /** * An instrument that propagates events to an instance of {@link StandardInstrumentListener}. */ private static final class StandardInstrument extends Instrument { @@ -351,23 +362,24 @@ // TODO (mlvdv) EXPERIMENTAL- UNDER DEVELOPMENT /** - * An instrument that propagates events to an instance of {@link StandardInstrumentListener}. + * An instrument that allows clients to "splice" an AST fragment directly into a Probe's + * instrumentation chain, and thus directly into the executing Truffle AST. */ - private static final class ToolNodeInstrument extends Instrument { + private static final class SpliceInstrument extends Instrument { /** * Tool-supplied listener for AST events. */ - private final ToolNodeInstrumentListener toolNodeListener; + private final SpliceInstrumentListener spliceListener; - private ToolNodeInstrument(ToolNodeInstrumentListener toolNodeListener, String instrumentInfo) { + private SpliceInstrument(SpliceInstrumentListener spliceListener, String instrumentInfo) { super(instrumentInfo); - this.toolNodeListener = toolNodeListener; + this.spliceListener = spliceListener; } @Override AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) { - return new ToolInstrumentNode(nextNode); + return new SpliceInstrumentNode(nextNode); } @Override @@ -379,7 +391,7 @@ return instrumentNode.nextInstrumentNode; } // Match not at the head of the chain; remove it. - found = instrumentNode.removeFromChain(ToolNodeInstrument.this); + found = instrumentNode.removeFromChain(SpliceInstrument.this); } if (!found) { throw new IllegalStateException("Couldn't find instrument node to remove: " + this); @@ -388,25 +400,25 @@ } @NodeInfo(cost = NodeCost.NONE) - private final class ToolInstrumentNode extends AbstractInstrumentNode { + private final class SpliceInstrumentNode extends AbstractInstrumentNode { - @Child ToolNode toolNode; + @Child SplicedNode splicedNode; - private ToolInstrumentNode(AbstractInstrumentNode nextNode) { + private SpliceInstrumentNode(AbstractInstrumentNode nextNode) { super(nextNode); } public void enter(Node node, VirtualFrame vFrame) { - if (toolNode == null) { - final ToolNode newToolNode = ToolNodeInstrument.this.toolNodeListener.getToolNode(ToolNodeInstrument.this.probe); - if (newToolNode != null) { - toolNode = newToolNode; + if (splicedNode == null) { + final SplicedNode newSplicedNode = SpliceInstrument.this.spliceListener.getSpliceNode(SpliceInstrument.this.probe); + if (newSplicedNode != null) { + splicedNode = newSplicedNode; adoptChildren(); - ToolNodeInstrument.this.probe.invalidateProbeUnchanged(); + SpliceInstrument.this.probe.invalidateProbeUnchanged(); } } - if (toolNode != null) { - toolNode.enter(node, vFrame); + if (splicedNode != null) { + splicedNode.enter(node, vFrame); } if (nextInstrumentNode != null) { nextInstrumentNode.enter(node, vFrame); @@ -414,8 +426,8 @@ } public void returnVoid(Node node, VirtualFrame vFrame) { - if (toolNode != null) { - toolNode.returnVoid(node, vFrame); + if (splicedNode != null) { + splicedNode.returnVoid(node, vFrame); } if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); @@ -423,8 +435,8 @@ } public void returnValue(Node node, VirtualFrame vFrame, Object result) { - if (toolNode != null) { - toolNode.returnValue(node, vFrame, result); + if (splicedNode != null) { + splicedNode.returnValue(node, vFrame, result); } if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); @@ -432,8 +444,8 @@ } public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - if (toolNode != null) { - toolNode.returnExceptional(node, vFrame, exception); + if (splicedNode != null) { + splicedNode.returnExceptional(node, vFrame, exception); } if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); @@ -442,7 +454,7 @@ public String instrumentationInfo() { final String info = getInstrumentInfo(); - return info != null ? info : toolNodeListener.getClass().getSimpleName(); + return info != null ? info : spliceListener.getClass().getSimpleName(); } } } diff -r f0d8a33aebd1 -r 518ce9a36939 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Mon Apr 13 15:55:23 2015 -0700 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Mon Apr 13 16:26:28 2015 -0700 @@ -34,38 +34,62 @@ import com.oracle.truffle.api.source.*; import com.oracle.truffle.api.utilities.*; -//TODO (mlvdv) migrate some of this to external documentation. +//TODO (mlvdv) these statics should not be global. Move them to some kind of context. + /** - * A binding between a particular location in the Truffle AST representation of a running - * Guest Language (GL) program (i.e. a {@link Node}) and a dynamically managed collection of - * "attached" {@linkplain Instrument instrumentation} for use by external tools. The instrumentation - * is intended to persist at the location, even if the specific node instance is - * {@linkplain Node#replace(Node) replaced}. + * A binding between: + *
    + *
  1. A program location in an executing Truffle AST (defined by a {@link SourceSection}), and
  2. + *
  3. A dynamically managed collection of "attached" {@linkplain Instrument Instruments} that + * receive event notifications on behalf of external clients.
  4. + *
*

- * The effect of a binding is to intercept {@linkplain TruffleEvents execution events} arriving at - * the node and notify each attached {@link Instrument} before execution is allowed to proceed to - * the child. + * Client-oriented documentation for the use of Probes is available online at https://wiki.openjdk.java.net/display/Graal/Finding+Probes *

- * A Probe is "inserted" into a GL node via a call to {@link Node#probe()}. No more than one Probe - * can be inserted at a node. + *

Implementation notes:

*

- * The "probing" of a Truffle AST must be done after it is complete (i.e. with parent pointers - * correctly assigned), but before any executions. This is done by creating an instance of - * {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}, after which it - * will be automatically applied to newly created ASTs. - *

- * Each Probe may also have assigned to it any number of {@link SyntaxTag}s, for example identifying - * a node as a {@linkplain StandardSyntaxTag#STATEMENT STATEMENT}. Tags can be queried by tools to - * configure behavior relevant to each probed node. - *

- * Instrumentation is implemented by modifying ASTs, both by inserting nodes into each AST at probed - * locations and by attaching additional nodes that implement dynamically attached instruments. - * Attached instrumentation code become, in effect, part of the GL program, and is subject to the - * same levels of optimization as other GL code. This implementation accounts properly for the fact - * that Truffle frequently clones ASTs, along with any attached instrumentation nodes. A - * {@link Probe}, along with attached {@link Instrument}s, represents a logical binding - * with a source code location, producing event notifications that are (mostly) independent of which - * AST clone is executing. + *

* * @see Instrument * @see ASTProber @@ -173,6 +197,7 @@ return taggedProbes; } + // TODO (mlvdv) can this be generalized to permit multiple traps without a performance hit? /** * Sets the current "tag trap"; there can be no more than one set at a time. *