Mercurial > hg > truffle
changeset 22232:526de3af756d
Merge with 0480c4873a4a8a71250c9570b702cea98ab1c7bc
author | Michael Van De Vanter <michael.van.de.vanter@oracle.com> |
---|---|
date | Mon, 21 Sep 2015 12:15:38 -0700 |
parents | 59e022cee529 (diff) 0480c4873a4a (current diff) |
children | 1f19e3cada3d |
files | truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java |
diffstat | 66 files changed, 3247 insertions(+), 2235 deletions(-) [+] |
line wrap: on
line diff
--- a/mx.truffle/suite.py Mon Sep 21 13:11:41 2015 +0200 +++ b/mx.truffle/suite.py Mon Sep 21 12:15:38 2015 -0700 @@ -193,6 +193,7 @@ ], "checkstyle" : "com.oracle.truffle.api", "javaCompliance" : "1.7", + "annotationProcessors" : ["TRUFFLE_DSL_PROCESSOR"], "workingSets" : "Truffle,Tools", },
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ArrayTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,6 +22,9 @@ */ package com.oracle.truffle.api.dsl.test; +import org.junit.Assert; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.dsl.ImplicitCast; @@ -36,8 +39,6 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.UnexpectedResultException; -import org.junit.Assert; -import org.junit.Test; public class ArrayTest {
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ImplicitCastTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,6 +22,9 @@ */ package com.oracle.truffle.api.dsl.test; +import org.junit.Assert; +import org.junit.Test; + import com.oracle.truffle.api.dsl.ImplicitCast; import com.oracle.truffle.api.dsl.NodeChild; import com.oracle.truffle.api.dsl.NodeChildren; @@ -34,8 +37,6 @@ import com.oracle.truffle.api.dsl.test.TypeSystemTest.TestRootNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; import com.oracle.truffle.api.frame.VirtualFrame; -import org.junit.Assert; -import org.junit.Test; public class ImplicitCastTest {
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/ShortCircuitTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,6 +22,10 @@ */ package com.oracle.truffle.api.dsl.test; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.dsl.ImplicitCast; import com.oracle.truffle.api.dsl.NodeChild; @@ -36,8 +40,6 @@ import com.oracle.truffle.api.dsl.test.ShortCircuitTestFactory.VarArgsNodeFactory; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ArgumentNode; import com.oracle.truffle.api.dsl.test.TypeSystemTest.ValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; public class ShortCircuitTest {
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,13 +22,20 @@ */ package com.oracle.truffle.api.dsl.test; +import java.io.IOException; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; -import java.io.IOException; public final class TestingLanguage extends TruffleLanguage<Object> { public static final TestingLanguage INSTANCE = new TestingLanguage(); @@ -58,13 +65,51 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { return null; } @Override + protected ASTProber getDefaultASTProber() { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override protected DebugSupportProvider getDebugSupport() { - return null; + throw new UnsupportedOperationException(); } @Override
--- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,14 +22,21 @@ */ package com.oracle.truffle.api.dsl.test.processor; +import java.io.IOException; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.debug.DebugSupportProvider; import com.oracle.truffle.api.dsl.test.ExpectError; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; -import java.io.IOException; public class LanguageRegistrationTest { @@ -76,13 +83,51 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected ASTProber getDefaultASTProber() { return null; } @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override protected DebugSupportProvider getDebugSupport() { - return null; + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); } @Override @@ -120,13 +165,51 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected ASTProber getDefaultASTProber() { return null; } @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override protected DebugSupportProvider getDebugSupport() { - return null; + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); } @Override @@ -160,13 +243,51 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected ASTProber getDefaultASTProber() { return null; } @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override protected DebugSupportProvider getDebugSupport() { - return null; + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); } @Override
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/ArgumentsTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,14 +22,15 @@ */ package com.oracle.truffle.api.test; +import org.junit.Assert; +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleRuntime; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; -import org.junit.Assert; -import org.junit.Test; /** * <h3>Passing Arguments</h3>
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,13 +22,20 @@ */ package com.oracle.truffle.api.test; +import java.io.IOException; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; -import java.io.IOException; public final class TestingLanguage extends TruffleLanguage<Object> { public static final TestingLanguage INSTANCE = new TestingLanguage(); @@ -57,13 +64,51 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected ASTProber getDefaultASTProber() { return null; } @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override protected DebugSupportProvider getDebugSupport() { - return null; + throw new UnsupportedOperationException(); } @Override
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,20 +22,26 @@ */ package com.oracle.truffle.api.test.instrument; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleRuntime; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ProbeListener; +import com.oracle.truffle.api.instrument.SyntaxTag; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdvancedInstrumentCounterRoot; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; -import static org.junit.Assert.assertEquals; -import org.junit.Test; +import com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.InstrumentTestTag; +import com.oracle.truffle.api.vm.TruffleVM; /** * Tests the kind of instrumentation where a client can provide an AST fragment to be @@ -44,47 +50,60 @@ public class AdvancedInstrumentTest { @Test - public void testAdvancedInstrumentListener() { - // 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); + public void testAdvancedInstrumentListener() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = Source.fromText("testAdvancedInstrumentListener text", "testAdvancedInstrumentListener").withMimeType("text/x-instTest"); + + final Probe[] addNodeProbe = new Probe[1]; + instrumenter.addProbeListener(new ProbeListener() { + + public void startASTProbing(Source s) { + } + + public void newProbeInserted(Probe p) { + } - // Ensure it executes correctly - assertEquals(13, callTarget1.call()); + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + if (tag == InstrumentTestTag.ADD_TAG) { + assertNull("only one add node", addNodeProbe[0]); + addNodeProbe[0] = probe; + } + } - // Probe the addition node - final Probe probe = addNode.probe(); + public void endASTProbing(Source s) { + } - assertEquals(13, callTarget1.call()); + }); + assertEquals(vm.eval(source).get(), 13); + assertNotNull("Add node should be probed", addNodeProbe[0]); - // Attach a null factory; it never actually attaches a node. - final Instrument instrument = Instrument.create(null, new AdvancedInstrumentRootFactory() { + // Attach a factory that never actually attaches a node. + final AdvancedInstrumentRootFactory rootFactory1 = new AdvancedInstrumentRootFactory() { public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) { return null; } - }, null, "test AdvancedInstrument"); - probe.attach(instrument); + }; + instrumenter.attach(addNodeProbe[0], null, rootFactory1, null, "test AdvancedInstrument"); - assertEquals(13, callTarget1.call()); - - final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot(); + assertEquals(vm.eval(source).get(), 13); // Attach a factory that splices an execution counter into the AST. - probe.attach(Instrument.create(null, new AdvancedInstrumentRootFactory() { + final TestAdvancedInstrumentCounterRoot counter = new TestAdvancedInstrumentCounterRoot(); + final AdvancedInstrumentRootFactory rootFactory2 = new AdvancedInstrumentRootFactory() { public AdvancedInstrumentRoot createInstrumentRoot(Probe p, Node n) { return counter; } - }, null, "test AdvancedInstrument")); - assertEquals(0, counter.getCount()); + }; + instrumenter.attach(addNodeProbe[0], null, rootFactory2, null, "test AdvancedInstrument"); - assertEquals(13, callTarget1.call()); - + assertEquals(0, counter.getCount()); + assertEquals(vm.eval(source).get(), 13); assertEquals(1, counter.getCount()); } }
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,17 +22,22 @@ */ package com.oracle.truffle.api.test.instrument; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleRuntime; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.ASTProber; import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeException; -import com.oracle.truffle.api.instrument.ProbeFailure.Reason; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.ProbeListener; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.instrument.SimpleInstrumentListener; import com.oracle.truffle.api.instrument.StandardInstrumentListener; import com.oracle.truffle.api.instrument.SyntaxTag; @@ -41,18 +46,12 @@ import com.oracle.truffle.api.instrument.impl.DefaultStandardInstrumentListener; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeVisitor; -import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode; -import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode; import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; -import java.util.Iterator; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import org.junit.Test; +import com.oracle.truffle.api.test.instrument.InstrumentationTestingLanguage.InstrumentTestTag; +import com.oracle.truffle.api.vm.TruffleVM; /** * <h3>AST Instrumentation</h3> @@ -60,143 +59,86 @@ * Instrumentation allows the insertion into Truffle ASTs language-specific instances of * {@link WrapperNode} that propagate execution events through a {@link Probe} to any instances of * {@link Instrument} that might be attached to the particular probe by tools. - * <ol> - * <li>Creates a simple add AST</li> - * <li>Verifies its structure</li> - * <li>"Probes" the add node by adding a {@link WrapperNode} and associated {@link Probe}</li> - * <li>Attaches a simple {@link Instrument} to the node via the Probe's {@link ProbeNode}</li> - * <li>Verifies the structure of the probed AST</li> - * <li>Verifies the execution of the probed AST</li> - * <li>Verifies the results observed by the instrument.</li> - * </ol> - * To do these tests, several required classes have been implemented in their most basic form, only - * implementing the methods necessary for the tests to pass, with stubs elsewhere. */ public class InstrumentationTest { - private static final SyntaxTag ADD_TAG = new SyntaxTag() { - - @Override - public String name() { - return "Addition"; - } - - @Override - public String getDescription() { - return "Test Language Addition Node"; - } - }; - - private static final SyntaxTag VALUE_TAG = new SyntaxTag() { - - @Override - public String name() { - return "Value"; - } + @Test + public void testProbing() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = Source.fromText("testProbing text", "testProbing").withMimeType("text/x-instTest"); - @Override - public String getDescription() { - return "Test Language Value Node"; - } - }; + final Probe[] probes = new Probe[3]; + instrumenter.addProbeListener(new ProbeListener() { - @Test - public void testInstrumentationStructure() { - // 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); + public void startASTProbing(Source s) { + } - try { - addNode.probe(); - } catch (ProbeException e) { - assertEquals(e.getFailure().getReason(), Reason.NO_PARENT); - } - final TestRootNode rootNode = new TestRootNode(addNode); - - // Creating a call target sets the parent pointers in this tree and is necessary prior to - // checking any parent/child relationships - final CallTarget callTarget1 = runtime.createCallTarget(rootNode); + public void newProbeInserted(Probe probe) { + } - // Check the tree structure - assertEquals(addNode, leftValueNode.getParent()); - assertEquals(addNode, rightValueNode.getParent()); - Iterator<Node> iterator = addNode.getChildren().iterator(); - assertEquals(leftValueNode, iterator.next()); - assertEquals(rightValueNode, iterator.next()); - assertFalse(iterator.hasNext()); - assertEquals(rootNode, addNode.getParent()); - iterator = rootNode.getChildren().iterator(); - assertEquals(addNode, iterator.next()); - assertFalse(iterator.hasNext()); - - // Ensure it executes correctly - assertEquals(13, callTarget1.call()); - - // Probe the addition node - addNode.probe(); - - // Check the modified tree structure - assertEquals(addNode, leftValueNode.getParent()); - assertEquals(addNode, rightValueNode.getParent()); - iterator = addNode.getChildren().iterator(); - assertEquals(leftValueNode, iterator.next()); - assertEquals(rightValueNode, iterator.next()); - assertFalse(iterator.hasNext()); + public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { + if (tag == InstrumentTestTag.ADD_TAG) { + assertEquals(probes[0], null); + probes[0] = probe; + } else if (tag == InstrumentTestTag.VALUE_TAG) { + if (probes[1] == null) { + probes[1] = probe; + } else if (probes[2] == null) { + probes[2] = probe; + } else { + fail("Should only be three probes"); + } + } + } - // Ensure there's a WrapperNode correctly inserted into the AST - iterator = rootNode.getChildren().iterator(); - Node wrapperNode = iterator.next(); - assertTrue(wrapperNode instanceof TestLanguageWrapperNode); - assertFalse(iterator.hasNext()); - assertEquals(rootNode, wrapperNode.getParent()); + public void endASTProbing(Source s) { + } - // Check that the WrapperNode has both the probe and the wrapped node as children - iterator = wrapperNode.getChildren().iterator(); - assertEquals(addNode, iterator.next()); - ProbeNode probeNode = (ProbeNode) iterator.next(); - assertTrue(probeNode.getProbe() != null); - assertFalse(iterator.hasNext()); + }); + assertEquals(vm.eval(source).get(), 13); + assertNotNull("Add node should be probed", probes[0]); + assertNotNull("Value nodes should be probed", probes[1]); + assertNotNull("Value nodes should be probed", probes[2]); + // Check instrumentation with the simplest kind of counters. + // They should all be removed when the check is finished. + checkCounters(probes[0], vm, source, new TestSimpleInstrumentCounter(instrumenter), new TestSimpleInstrumentCounter(instrumenter), new TestSimpleInstrumentCounter(instrumenter)); - // Check that you can't probe the WrapperNodes - TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode; - try { - wrapper.probe(); - fail(); - } catch (ProbeException e) { - assertEquals(e.getFailure().getReason(), Reason.WRAPPER_NODE); - } + // Now try with the more complex flavor of listener + checkCounters(probes[0], vm, source, new TestStandardInstrumentCounter(instrumenter), new TestStandardInstrumentCounter(instrumenter), new TestStandardInstrumentCounter(instrumenter)); - // Check that the "probed" AST still executes correctly - assertEquals(13, callTarget1.call()); } @Test - public void testListeners() { + public void testTagging() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { - // 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 TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = Source.fromText("testTagging text", "testTagging").withMimeType("text/x-instTest"); + + // Applies appropriate tags + final TestASTProber astProber = new TestASTProber(instrumenter); + instrumenter.registerASTProber(astProber); - // Creating a call target sets the parent pointers in this tree and is necessary prior to - // checking any parent/child relationships - final CallTarget callTarget = runtime.createCallTarget(rootNode); - // Probe the addition node - final Probe probe = addNode.probe(); + // Listens for probes and tags being added + final TestProbeListener probeListener = new TestProbeListener(); + instrumenter.addProbeListener(probeListener); + + assertEquals(13, vm.eval(source).get()); - // Check instrumentation with the simplest kind of counters. - // They should all be removed when the check is finished. - checkCounters(probe, callTarget, rootNode, new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter()); + // Check that the prober added probes to the tree + assertEquals(probeListener.probeCount, 3); + assertEquals(probeListener.tagCount, 3); - // Now try with the more complex flavor of listener - checkCounters(probe, callTarget, rootNode, new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter()); + assertEquals(instrumenter.findProbesTaggedAs(InstrumentTestTag.ADD_TAG).size(), 1); + assertEquals(instrumenter.findProbesTaggedAs(InstrumentTestTag.VALUE_TAG).size(), 2); } - private static void checkCounters(Probe probe, CallTarget callTarget, RootNode rootNode, TestCounter counterA, TestCounter counterB, TestCounter counterC) { + private static void checkCounters(Probe probe, TruffleVM vm, Source source, TestCounter counterA, TestCounter counterB, TestCounter counterC) throws IOException { // Attach a counting instrument to the probe counterA.attach(probe); @@ -205,7 +147,7 @@ counterB.attach(probe); // Run it again and check that the two instruments are working - assertEquals(13, callTarget.call()); + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); assertEquals(counterB.enterCount(), 1); @@ -215,141 +157,47 @@ counterA.dispose(); // Run it again and check that instrument B is still working but not A - assertEquals(13, callTarget.call()); + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); assertEquals(counterB.enterCount(), 2); assertEquals(counterB.leaveCount(), 2); - // Simulate a split by cloning the AST - final CallTarget callTarget2 = Truffle.getRuntime().createCallTarget((TestRootNode) rootNode.copy()); - // Run the clone and check that instrument B is still working but not A - assertEquals(13, callTarget2.call()); + // Attach a second instrument to the probe + counterC.attach(probe); + + // Run the original and check that instruments B,C working but not A + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); assertEquals(counterB.enterCount(), 3); assertEquals(counterB.leaveCount(), 3); - - // Run the original and check that instrument B is still working but not A - assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount(), 1); - assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 4); - assertEquals(counterB.leaveCount(), 4); - - // Attach a second instrument to the probe - counterC.attach(probe); - - // Run the original and check that instruments B,C working but not A - assertEquals(13, callTarget.call()); - assertEquals(counterA.enterCount(), 1); - assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 5); - assertEquals(counterB.leaveCount(), 5); assertEquals(counterC.enterCount(), 1); assertEquals(counterC.leaveCount(), 1); - // Run the clone and check that instruments B,C working but not A - assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount(), 1); - assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 6); - assertEquals(counterB.leaveCount(), 6); - assertEquals(counterC.enterCount(), 2); - assertEquals(counterC.leaveCount(), 2); - // Remove instrumentC counterC.dispose(); // Run the original and check that instrument B working but not A,C - assertEquals(13, callTarget.call()); + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 7); - assertEquals(counterB.leaveCount(), 7); - assertEquals(counterC.enterCount(), 2); - assertEquals(counterC.leaveCount(), 2); - - // Run the clone and check that instrument B working but not A,C - assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount(), 1); - assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 8); - assertEquals(counterB.leaveCount(), 8); - assertEquals(counterC.enterCount(), 2); - assertEquals(counterC.leaveCount(), 2); + assertEquals(counterB.enterCount(), 4); + assertEquals(counterB.leaveCount(), 4); + assertEquals(counterC.enterCount(), 1); + assertEquals(counterC.leaveCount(), 1); // Remove instrumentB counterB.dispose(); - // Run both the original and clone, check that no instruments working - assertEquals(13, callTarget.call()); - assertEquals(13, callTarget2.call()); + // Check that no instruments working + assertEquals(13, vm.eval(source).get()); assertEquals(counterA.enterCount(), 1); assertEquals(counterA.leaveCount(), 1); - assertEquals(counterB.enterCount(), 8); - assertEquals(counterB.leaveCount(), 8); - assertEquals(counterC.enterCount(), 2); - assertEquals(counterC.leaveCount(), 2); - } - - @Test - public void testTagging() { - // Applies appropriate tags - final TestASTProber astProber = new TestASTProber(); - Probe.registerASTProber(astProber); - - // Listens for probes and tags being added - final TestProbeListener probeListener = new TestProbeListener(); - Probe.addProbeListener(probeListener); - - // Counts all entries to all instances of addition nodes - final TestMultiCounter additionCounter = new TestMultiCounter(); - - // Counts all entries to all instances of value nodes - final TestMultiCounter valueCounter = new TestMultiCounter(); - - // 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 callTarget = runtime.createCallTarget(rootNode); - - // Check that the prober added probes to the tree - assertEquals(probeListener.probeCount, 3); - assertEquals(probeListener.tagCount, 3); - - assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1); - assertEquals(Probe.findProbesTaggedAs(VALUE_TAG).size(), 2); - - // Check that it executes correctly - assertEquals(13, callTarget.call()); - - // Dynamically attach a counter for all executions of all Addition nodes - for (Probe probe : Probe.findProbesTaggedAs(ADD_TAG)) { - additionCounter.attachCounter(probe); - } - // Dynamically attach a counter for all executions of all Value nodes - for (Probe probe : Probe.findProbesTaggedAs(VALUE_TAG)) { - valueCounter.attachCounter(probe); - } - - // Counters initialized at 0 - assertEquals(additionCounter.count, 0); - assertEquals(valueCounter.count, 0); - - // Execute again - assertEquals(13, callTarget.call()); - - // There are two value nodes in the AST, but only one addition node - assertEquals(additionCounter.count, 1); - assertEquals(valueCounter.count, 2); - - Probe.unregisterASTProber(astProber); + assertEquals(counterB.enterCount(), 4); + assertEquals(counterB.leaveCount(), 4); + assertEquals(counterC.enterCount(), 1); + assertEquals(counterC.leaveCount(), 1); } private interface TestCounter { @@ -370,28 +218,11 @@ public int enterCount = 0; public int leaveCount = 0; - public final Instrument instrument; - - public TestSimpleInstrumentCounter() { - this.instrument = Instrument.create(new SimpleInstrumentListener() { - - public void enter(Probe probe) { - enterCount++; - } + public Instrumenter instrumenter; + private Instrument instrument; - public void returnVoid(Probe probe) { - leaveCount++; - } - - public void returnValue(Probe probe, Object result) { - leaveCount++; - } - - public void returnExceptional(Probe probe, Exception exception) { - leaveCount++; - } - - }, "Instrumentation Test Counter"); + public TestSimpleInstrumentCounter(Instrumenter instrumenter) { + this.instrumenter = instrumenter; } @Override @@ -406,7 +237,25 @@ @Override public void attach(Probe probe) { - probe.attach(instrument); + instrument = instrumenter.attach(probe, new SimpleInstrumentListener() { + + public void onEnter(Probe p) { + enterCount++; + } + + public void onReturnVoid(Probe p) { + leaveCount++; + } + + public void onReturnValue(Probe p, Object result) { + leaveCount++; + } + + public void onReturnExceptional(Probe p, Exception exception) { + leaveCount++; + } + + }, "Instrumentation Test Counter"); } @Override @@ -422,28 +271,11 @@ public int enterCount = 0; public int leaveCount = 0; - public final Instrument instrument; - - public TestStandardInstrumentCounter() { - this.instrument = Instrument.create(new StandardInstrumentListener() { - - public void enter(Probe probe, Node node, VirtualFrame vFrame) { - enterCount++; - } + public final Instrumenter instrumenter; + public Instrument instrument; - public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) { - leaveCount++; - } - - public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { - leaveCount++; - } - - public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { - leaveCount++; - } - - }, "Instrumentation Test Counter"); + public TestStandardInstrumentCounter(Instrumenter instrumenter) { + this.instrumenter = instrumenter; } @Override @@ -458,7 +290,25 @@ @Override public void attach(Probe probe) { - probe.attach(instrument); + instrument = instrumenter.attach(probe, new StandardInstrumentListener() { + + public void onEnter(Probe p, Node node, VirtualFrame vFrame) { + enterCount++; + } + + public void onReturnVoid(Probe p, Node node, VirtualFrame vFrame) { + leaveCount++; + } + + public void onReturnValue(Probe p, Node node, VirtualFrame vFrame, Object result) { + leaveCount++; + } + + public void onReturnExceptional(Probe p, Node node, VirtualFrame vFrame, Exception exception) { + leaveCount++; + } + + }, "Instrumentation Test Counter"); } @Override @@ -472,6 +322,12 @@ */ private static final class TestASTProber implements NodeVisitor, ASTProber { + private final Instrumenter instrumenter; + + TestASTProber(Instrumenter instrumenter) { + this.instrumenter = instrumenter; + } + @Override public boolean visit(Node node) { if (node instanceof TestLanguageNode) { @@ -479,10 +335,10 @@ final TestLanguageNode testNode = (TestLanguageNode) node; if (node instanceof TestValueNode) { - testNode.probe().tagAs(VALUE_TAG, null); + instrumenter.probe(testNode).tagAs(InstrumentTestTag.VALUE_TAG, null); } else if (node instanceof TestAdditionNode) { - testNode.probe().tagAs(ADD_TAG, null); + instrumenter.probe(testNode).tagAs(InstrumentTestTag.ADD_TAG, null); } } @@ -490,7 +346,7 @@ } @Override - public void probeAST(Node node) { + public void probeAST(Instrumenter inst, Node node) { node.accept(this); } } @@ -503,7 +359,7 @@ public int counter = 0; @Override - public void enter(Probe probe) { + public void onEnter(Probe probe) { counter++; } } @@ -516,35 +372,11 @@ public int counter = 0; @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { counter++; } } - /** - * A counter that can count executions at multiple nodes; it attaches a separate instrument at - * each Probe, but keeps a total count. - */ - private static final class TestMultiCounter { - - public int count = 0; - - public void attachCounter(Probe probe) { - - // Attach a new instrument for every Probe - // where we want to count executions. - // it will get copied when ASTs cloned, so - // keep the count in this outer class. - probe.attach(Instrument.create(new DefaultSimpleInstrumentListener() { - - @Override - public void enter(Probe p) { - count++; - } - }, "Instrumentation Test MultiCounter")); - } - } - private static final class TestProbeListener extends DefaultProbeListener { public int probeCount = 0;
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,42 +22,46 @@ */ package com.oracle.truffle.api.test.instrument; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; +import com.oracle.truffle.api.instrument.EventHandlerNode; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.test.TestingLanguage; +import com.oracle.truffle.api.vm.TruffleVM; /** * Tests instrumentation where a client can attach a node that gets attached into the AST. */ class InstrumentationTestNodes { + static Instrumenter createInstrumenter() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrument = (Instrumenter) field.get(vm); + return instrument; + } + abstract static class TestLanguageNode extends Node { public abstract Object execute(VirtualFrame vFrame); - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new TestLanguageWrapperNode(this); - } } @NodeInfo(cost = NodeCost.NONE) static class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode { @Child private TestLanguageNode child; - @Child private ProbeNode probeNode; + @Child private EventHandlerNode eventHandlerNode; public TestLanguageWrapperNode(TestLanguageNode child) { assert !(child instanceof TestLanguageWrapperNode); @@ -70,18 +74,13 @@ } @Override - public boolean isInstrumentable() { - return false; - } - - @Override - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; } @Override public Probe getProbe() { - return probeNode.getProbe(); + return eventHandlerNode.getProbe(); } @Override @@ -91,15 +90,15 @@ @Override public Object execute(VirtualFrame vFrame) { - probeNode.enter(child, vFrame); + eventHandlerNode.enter(child, vFrame); Object result; try { result = child.execute(vFrame); - probeNode.returnValue(child, vFrame, result); + eventHandlerNode.returnValue(child, vFrame, result); } catch (KillException e) { throw (e); } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); + eventHandlerNode.returnExceptional(child, vFrame, e); throw (e); } return result; @@ -145,7 +144,7 @@ * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle * completes an AST. The root nodes serves as our entry point into a program. */ - static class TestRootNode extends RootNode { + static class InstrumentationTestRootNode extends RootNode { @Child private TestLanguageNode body; /** @@ -153,8 +152,8 @@ * newly created AST. Global registry is not used, since that would interfere with other * tests run in the same environment. */ - public TestRootNode(TestLanguageNode body) { - super(TestingLanguage.class, null, null); + public InstrumentationTestRootNode(TestLanguageNode body) { + super(InstrumentationTestingLanguage.class, null, null); this.body = body; } @@ -170,7 +169,51 @@ @Override public void applyInstrumentation() { - Probe.applyASTProbers(body); + super.applyInstrumentation(body); + } + } + + /** + * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST + * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle + * completes an AST. The root nodes serves as our entry point into a program. + */ + static class TestRootNode extends RootNode { + @Child private TestLanguageNode body; + + final Instrumenter instrumenter; + + /** + * This constructor emulates the global machinery that applies registered probers to every + * newly created AST. Global registry is not used, since that would interfere with other + * tests run in the same environment. + */ + public TestRootNode(TestLanguageNode body, Instrumenter instrumenter) { + super(InstrumentationTestingLanguage.class, null, null); + this.instrumenter = instrumenter; + this.body = body; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return body.execute(vFrame); + } + + @Override + public boolean isCloningAllowed() { + return true; + } + + @Override + public void applyInstrumentation() { + Method method; + try { + method = Instrumenter.class.getDeclaredMethod("applyInstrumentation", Node.class); + method.setAccessible(true); + method.invoke(instrumenter, body); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new RuntimeException("InstrumentationTestNodes"); + } } } @@ -192,4 +235,5 @@ return null; } } + }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestingLanguage.java Mon Sep 21 12:15:38 2015 -0700 @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014, 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 java.io.IOException; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleRuntime; +import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeVisitor; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.InstrumentationTestRootNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode; +import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode; + +@TruffleLanguage.Registration(name = "instrumentationTestLanguage", version = "0", mimeType = "text/x-instTest") +public final class InstrumentationTestingLanguage extends TruffleLanguage<Object> { + + public static final InstrumentationTestingLanguage INSTANCE = new InstrumentationTestingLanguage(); + + static enum InstrumentTestTag implements SyntaxTag { + + ADD_TAG("addition", "test language addition node"), + + VALUE_TAG("value", "test language value node"); + + private final String name; + private final String description; + + private InstrumentTestTag(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + } + + private final ASTProber prober = new TestASTProber(); + + private InstrumentationTestingLanguage() { + } + + @Override + protected CallTarget parse(Source code, Node context, String... argumentNames) throws IOException { + final TestValueNode leftValueNode = new TestValueNode(6); + final TestValueNode rightValueNode = new TestValueNode(7); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); + final InstrumentationTestRootNode rootNode = new InstrumentationTestRootNode(addNode); + final TruffleRuntime runtime = Truffle.getRuntime(); + final CallTarget callTarget = runtime.createCallTarget(rootNode); + return callTarget; + } + + @Override + protected Object findExportedSymbol(Object context, String globalName, boolean onlyExplicit) { + return null; + } + + @Override + protected Object getLanguageGlobal(Object context) { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + + @Override + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected ASTProber getDefaultASTProber() { + return prober; + } + + @Override + protected boolean isInstrumentable(Node node) { + return node instanceof TestAdditionNode || node instanceof TestValueNode; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + if (isInstrumentable(node)) { + return new TestLanguageWrapperNode((TestLanguageNode) node); + } + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override + protected DebugSupportProvider getDebugSupport() { + throw new UnsupportedOperationException(); + } + + @Override + protected Object createContext(Env env) { + return null; + } + + static final class TestASTProber implements ASTProber { + + public void probeAST(final Instrumenter instrumenter, Node startNode) { + startNode.accept(new NodeVisitor() { + + @Override + public boolean visit(Node node) { + if (node instanceof TestLanguageNode) { + + final TestLanguageNode testNode = (TestLanguageNode) node; + + if (node instanceof TestValueNode) { + instrumenter.probe(testNode).tagAs(InstrumentTestTag.VALUE_TAG, null); + + } else if (node instanceof TestAdditionNode) { + instrumenter.probe(testNode).tagAs(InstrumentTestTag.ADD_TAG, null); + + } + } + return true; + } + }); + } + } + +}
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/source/SourceTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -91,7 +91,8 @@ w.write(text); } - Source s1 = Source.fromFileName(file.getPath()); + // JDK8 default fails on OS X: https://bugs.openjdk.java.net/browse/JDK-8129632 + Source s1 = Source.fromFileName(file.getPath()).withMimeType("text/x-java"); assertEquals("Recognized as Java", "text/x-java", s1.getMimeType()); Source s2 = s1.withMimeType("text/x-c"); assertEquals("They have the same content", s1.getCode(), s2.getCode()); @@ -107,7 +108,8 @@ String text = "// Hello"; - Source s1 = Source.fromFileName(text, file.getPath()); + // JDK8 default fails on OS X: https://bugs.openjdk.java.net/browse/JDK-8129632 + Source s1 = Source.fromFileName(text, file.getPath()).withMimeType("text/x-java"); assertEquals("Recognized as Java", "text/x-java", s1.getMimeType()); Source s2 = s1.withMimeType("text/x-c"); assertEquals("They have the same content", s1.getCode(), s2.getCode());
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,16 +22,10 @@ */ package com.oracle.truffle.api.test.vm; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.RootCallTarget; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.debug.DebugSupportProvider; -import com.oracle.truffle.api.instrument.ToolSupportProvider; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.vm.TruffleVM; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.io.Reader; import java.util.Enumeration; @@ -39,13 +33,28 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.Executors; + import org.junit.After; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.TruffleVM; + public class ImplicitExplicitExportTest { private static Thread mainThread; private TruffleVM vm; @@ -172,13 +181,51 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected ASTProber getDefaultASTProber() { return null; } @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override protected DebugSupportProvider getDebugSupport() { - return null; + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); } private Object importExport(Source code) {
--- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -22,12 +22,20 @@ */ package com.oracle.truffle.api.test.vm; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.junit.Test; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.debug.Breakpoint; -import com.oracle.truffle.api.debug.DebugSupportException; import com.oracle.truffle.api.debug.DebugSupportProvider; import com.oracle.truffle.api.debug.Debugger; import com.oracle.truffle.api.debug.ExecutionEvent; @@ -36,23 +44,20 @@ import com.oracle.truffle.api.instrument.ASTProber; import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.EventHandlerNode; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; import com.oracle.truffle.api.instrument.StandardSyntaxTag; import com.oracle.truffle.api.instrument.ToolSupportProvider; import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeVisitor; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.vm.EventConsumer; import com.oracle.truffle.api.vm.TruffleVM; -import java.io.IOException; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import org.junit.Test; /** * Bug report validating test. @@ -75,7 +80,7 @@ } }).build(); - Source source = Source.fromText("any text", "any text").withMimeType("application/x-abstrlang"); + Source source = Source.fromText("accessProbeForAbstractLanguage text", "accessProbeForAbstractLanguage").withMimeType("application/x-abstrlang"); vm.eval(source); @@ -102,13 +107,17 @@ super(AbstractLanguage.class, ss, null); node = new ANode(42); adoptChildren(); - node.probe().tagAs(StandardSyntaxTag.STATEMENT, this); } @Override public Object execute(VirtualFrame frame) { return node.constant(); } + + @Override + public void applyInstrumentation() { + super.applyInstrumentation(node); + } } private static class ANode extends Node { @@ -123,56 +132,64 @@ return getRootNode().getSourceSection(); } - @Override - public boolean isInstrumentable() { - return true; + Object constant() { + return constant; + } + } + + private static class ANodeWrapper extends ANode implements WrapperNode { + @Child ANode child; + @Child private EventHandlerNode eventHandlerNode; + + ANodeWrapper(ANode node) { + super(1); // dummy + this.child = node; } @Override - public ProbeNode.WrapperNode createWrapperNode() { - class WN extends ANode implements ProbeNode.WrapperNode { - private ProbeNode probeNode; - - public WN(int constant) { - super(constant); - } - - @Override - public Node getChild() { - return ANode.this; - } + public Node getChild() { + return child; + } - @Override - public Probe getProbe() { - return probeNode.getProbe(); - } - - @Override - public void insertProbe(ProbeNode pn) { - this.probeNode = pn; - } - - @Override - public String instrumentationInfo() { - throw new UnsupportedOperationException(); - } - } - return new WN(constant); + @Override + public Probe getProbe() { + return eventHandlerNode.getProbe(); } - Object constant() { - return constant; + @Override + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; } + @Override + public String instrumentationInfo() { + throw new UnsupportedOperationException(); + } } private abstract static class AbstractLanguage extends TruffleLanguage<Object> { } @TruffleLanguage.Registration(mimeType = "application/x-abstrlang", name = "AbstrLang", version = "0.1") - public static final class TestLanguage extends AbstractLanguage implements DebugSupportProvider { + public static final class TestLanguage extends AbstractLanguage { public static final TestLanguage INSTANCE = new TestLanguage(); + private final ASTProber prober = new ASTProber() { + + public void probeAST(final Instrumenter instrumenter, Node startNode) { + startNode.accept(new NodeVisitor() { + + public boolean visit(Node node) { + + if (node instanceof ANode) { + instrumenter.probe(node).tagAs(StandardSyntaxTag.STATEMENT, null); + } + return true; + } + }); + } + }; + @Override protected Object createContext(Env env) { assertNull("Not defined symbol", env.importSymbol("unknown")); @@ -199,23 +216,25 @@ throw new UnsupportedOperationException(); } + @SuppressWarnings("deprecation") @Override protected ToolSupportProvider getToolSupport() { throw new UnsupportedOperationException(); } + @SuppressWarnings("deprecation") @Override protected DebugSupportProvider getDebugSupport() { - return this; - } - - @Override - public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException { throw new UnsupportedOperationException(); } @Override - public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException { + public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) { + throw new UnsupportedOperationException(); + } + + @Override + public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) { throw new InstrumentOKException(); } @@ -225,6 +244,22 @@ } @Override + protected boolean isInstrumentable(Node node) { + return node instanceof ANode; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return node instanceof ANode ? new ANodeWrapper((ANode) node) : null; + } + + @Override + protected ASTProber getDefaultASTProber() { + return prober; + } + + @SuppressWarnings("deprecation") + @Override public void enableASTProbing(ASTProber astProber) { throw new UnsupportedOperationException(); }
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java Mon Sep 21 12:15:38 2015 -0700 @@ -28,6 +28,7 @@ import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.impl.Accessor; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.Message; import com.oracle.truffle.api.interop.TruffleObject; @@ -82,6 +83,11 @@ Object tmp = ForeignAccess.execute(foreignAccess, frame, function, args); return convert.convert(frame, tmp); } + + @Override + public void applyInstrumentation() { + SymbolInvokerImpl.ACCESSOR_INTEROP.applyInstrumentation(foreignAccess); + } } private static final class ConvertNode extends Node { @@ -125,4 +131,14 @@ return obj; } } + + static final class AccessorInterop extends Accessor { + + @Override + protected void applyInstrumentation(Node node) { + super.applyInstrumentation(node); + } + } + + static final AccessorInterop ACCESSOR_INTEROP = new AccessorInterop(); }
--- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,20 +24,6 @@ */ package com.oracle.truffle.api.vm; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.TruffleLanguage.Env; -import com.oracle.truffle.api.TruffleLanguage.Registration; -import com.oracle.truffle.api.debug.DebugSupportProvider; -import com.oracle.truffle.api.debug.Debugger; -import com.oracle.truffle.api.debug.ExecutionEvent; -import com.oracle.truffle.api.debug.SuspendedEvent; -import com.oracle.truffle.api.impl.Accessor; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ToolSupportProvider; -import com.oracle.truffle.api.interop.TruffleObject; -import com.oracle.truffle.api.interop.java.JavaInterop; -import com.oracle.truffle.api.source.Source; import java.io.Closeable; import java.io.File; import java.io.IOException; @@ -65,6 +51,23 @@ import java.util.logging.Level; import java.util.logging.Logger; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.TruffleLanguage.Registration; +import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.debug.ExecutionEvent; +import com.oracle.truffle.api.debug.SuspendedEvent; +import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.java.JavaInterop; +import com.oracle.truffle.api.source.Source; + /** * <em>Virtual machine</em> for Truffle based languages. Term virtual machine is a bit overloaded, * so don't think of <em>Java virtual machine</em> here - while we are running and using @@ -109,6 +112,7 @@ private final OutputStream out; private final EventConsumer<?>[] handlers; private final Map<String, Object> globals; + private final Instrumenter instrumenter; private Debugger debugger; /** @@ -123,6 +127,7 @@ this.handlers = null; this.globals = null; this.executor = null; + this.instrumenter = null; } /** @@ -136,6 +141,7 @@ this.handlers = handlers; this.initThread = Thread.currentThread(); this.globals = new HashMap<>(globals); + this.instrumenter = SPI.createInstrumenter(this); Map<String, Language> map = new HashMap<>(); for (Map.Entry<String, LanguageCache> en : LanguageCache.languages().entrySet()) { map.put(en.getKey(), new Language(en.getValue())); @@ -463,7 +469,6 @@ try (Closeable d = SPI.executionStart(this, fillIn, s)) { TruffleLanguage<?> langImpl = l.getImpl(true); fillLang[0] = langImpl; - TruffleVM.findDebuggerSupport(langImpl); if (debugger == null) { debugger = fillIn[0]; } @@ -625,7 +630,7 @@ * delegates to * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject)} * just handles primitive types as well. - * + * * @param <T> the type of the view one wants to obtain * @param representation the class of the view interface (it has to be an interface) * @return instance of the view wrapping the object of this symbol @@ -796,7 +801,14 @@ TruffleLanguage<?> getImpl(boolean create) { getEnv(create); - return info.getImpl(false); + TruffleLanguage<?> impl = info.getImpl(false); + if (impl != null) { + ASTProber prober = SPI.getDefaultASTProber(impl); + if (prober != null) { + instrumenter.registerASTProber(prober); + } + } + return impl; } TruffleLanguage.Env getEnv(boolean create) { @@ -816,8 +828,7 @@ // Accessor helper methods // - TruffleLanguage<?> findLanguage(Probe probe) { - Class<? extends TruffleLanguage> languageClazz = SPI.findLanguage(probe); + TruffleLanguage<?> findLanguage(Class<? extends TruffleLanguage> languageClazz) { for (Map.Entry<String, Language> entrySet : langs.entrySet()) { Language languageDescription = entrySet.getValue(); final TruffleLanguage<?> impl = languageDescription.getImpl(false); @@ -828,6 +839,10 @@ throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); } + TruffleLanguage<?> findLanguage(Probe probe) { + return findLanguage(SPI.findLanguage(probe)); + } + Env findEnv(Class<? extends TruffleLanguage> languageClazz) { for (Map.Entry<String, Language> entrySet : langs.entrySet()) { Language languageDescription = entrySet.getValue(); @@ -839,10 +854,6 @@ throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); } - static DebugSupportProvider findDebuggerSupport(TruffleLanguage<?> l) { - return SPI.getDebugSupport(l); - } - private static class SPIAccessor extends Accessor { @Override public Object importSymbol(Object vmObj, TruffleLanguage<?> ownLang, String globalName) { @@ -898,14 +909,32 @@ return super.languageGlobal(env); } + @SuppressWarnings("deprecation") @Override public ToolSupportProvider getToolSupport(TruffleLanguage<?> l) { - return super.getToolSupport(l); + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override + public DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { + throw new UnsupportedOperationException(); } @Override - public DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { - return super.getDebugSupport(l); + protected Instrumenter createInstrumenter(Object vm) { + return super.createInstrumenter(vm); + } + + @Override + protected ASTProber getDefaultASTProber(TruffleLanguage impl) { + return super.getDefaultASTProber(impl); + } + + @Override + protected Instrumenter getInstrumenter(Object obj) { + final TruffleVM vm = (TruffleVM) obj; + return vm.instrumenter; } @Override @@ -920,6 +949,12 @@ } @Override + protected TruffleLanguage findLanguageImpl(Object obj, Class<? extends TruffleLanguage> languageClazz) { + final TruffleVM vm = (TruffleVM) obj; + return vm.findLanguage(languageClazz); + } + + @Override protected Closeable executionStart(Object obj, Debugger[] fillIn, Source s) { TruffleVM vm = (TruffleVM) obj; return super.executionStart(vm, fillIn, s);
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,12 +24,6 @@ */ package com.oracle.truffle.api; -import com.oracle.truffle.api.debug.DebugSupportProvider; -import com.oracle.truffle.api.impl.Accessor; -import com.oracle.truffle.api.impl.FindContextNode; -import com.oracle.truffle.api.instrument.ToolSupportProvider; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.Source; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -45,6 +39,21 @@ import java.util.Map; import java.util.WeakHashMap; +import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.impl.FindContextNode; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.Source; + /** * An entry point for everyone who wants to implement a Truffle based language. By providing an * implementation of this type and registering it using {@link Registration} annotation, your @@ -183,11 +192,81 @@ */ protected abstract boolean isObjectOfLanguage(Object object); + @Deprecated protected abstract ToolSupportProvider getToolSupport(); + @Deprecated protected abstract DebugSupportProvider getDebugSupport(); /** + * Gets visualization services for language-specific information. + */ + protected abstract Visualizer getVisualizer(); + + /** + * Enables AST probing on all subsequently created ASTs (sources parsed). + * + * @param astProber optional AST prober to enable; the default for the language used if + * {@code null} + */ + @Deprecated + protected abstract void enableASTProbing(ASTProber astProber); + + /** + * Returns {@code true} for a node can be "instrumented" by + * {@linkplain Instrumenter#probe(Node) probing}. + * <p> + * <b>Note:</b> instrumentation requires a appropriate {@link WrapperNode} + * + * @see Node#createWrapperNode() + */ + protected abstract boolean isInstrumentable(Node node); + + /** + * For nodes in this language that are {@linkplain #isInstrumentable() instrumentable}, this + * method returns an {@linkplain Node AST node} that: + * <ol> + * <li>implements {@link WrapperNode};</li> + * <li>has the node argument as it's child; and</li> + * <li>whose type is safe for replacement of the node in the parent.</li> + * </ol> + * + * @return an appropriately typed {@link WrapperNode} + */ + protected abstract WrapperNode createWrapperNode(Node node); + + /** + * Gets the current specification for AST instrumentation for the language. + */ + protected abstract ASTProber getDefaultASTProber(); + + /** + * Runs source code in a halted execution context, or at top level. + * + * @param source the code to run + * @param node node where execution halted, {@code null} if no execution context + * @param mFrame frame where execution halted, {@code null} if no execution context + * @return result of running the code in the context, or at top level if no execution context. + * @throws IOException if the evaluation cannot be performed + */ + protected abstract Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException; + + /** + * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot} + * that, when executed, computes the result of a textual expression in the language; used to + * create an + * {@linkplain Instrumenter#attach(com.oracle.truffle.api.instrument.Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String)} + * . + * + * @param expr a guest language expression + * @param resultListener optional listener for the result of each evaluation. + * @return a new factory + * @throws IOException if the factory cannot be created, for example if the expression is badly + * formed. + */ + protected abstract AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException; + + /** * Allows a language implementor to create a node that can effectively lookup up the context * associated with current execution. The context is created by * {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)} method. @@ -357,6 +436,14 @@ } @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr, + AdvancedInstrumentResultListener resultListener) throws IOException { + + final TruffleLanguage language = findLanguageImpl(vm, languageClass); + return language.createAdvancedInstrumentRootFactory(expr, resultListener); + } + + @Override protected Object findExportedSymbol(TruffleLanguage.Env env, String globalName, boolean onlyExplicit) { return env.langCtx.findExportedSymbol(globalName, onlyExplicit); } @@ -382,10 +469,27 @@ } @Override + protected boolean isInstrumentable(Node node, TruffleLanguage language) { + return language.isInstrumentable(node); + } + + @Override + protected WrapperNode createWrapperNode(Node node, TruffleLanguage language) { + return language.createWrapperNode(node); + } + + @Override + protected ASTProber getDefaultASTProber(TruffleLanguage language) { + return language.getDefaultASTProber(); + } + + @SuppressWarnings("deprecation") + @Override protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) { return l.getToolSupport(); } + @SuppressWarnings("deprecation") @Override protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { return l.getDebugSupport();
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/DebugSupportProvider.java Mon Sep 21 12:15:38 2015 -0700 @@ -28,7 +28,7 @@ import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.ToolSupportProvider; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.Source; @@ -52,7 +52,7 @@ * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot} * that, when executed, computes the result of a textual expression in the language; used to * create an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(com.oracle.truffle.api.instrument.Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. * * @param expr a guest language expression
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,6 +24,13 @@ */ package com.oracle.truffle.api.debug; +import java.io.Closeable; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; @@ -34,7 +41,7 @@ import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.AdvancedInstrumentRoot; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; -import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.StandardSyntaxTag; @@ -43,12 +50,6 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.source.LineLocation; import com.oracle.truffle.api.source.Source; -import java.io.Closeable; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; /** * Represents debugging related state of a {@link com.oracle.truffle.api.vm.TruffleVM}. Instance of @@ -60,7 +61,7 @@ public final class Debugger { private static final boolean TRACE = false; - private static final String TRACE_PREFIX = "DEBUG ENGINE: "; + private static final String TRACE_PREFIX = "Debugger: "; private static final PrintStream OUT = System.out; @@ -73,6 +74,7 @@ } } + private final Instrumenter instrumenter; private final Object vm; private Source lastSource; @@ -92,6 +94,9 @@ void addWarning(String warning); } + private final BreakpointCallback breakpointCallback; + private final WarningLog warningLog; + /** * Implementation of line-oriented breakpoints. */ @@ -107,9 +112,9 @@ */ private DebugExecutionContext debugContext; - Debugger(Object vm) { + Debugger(Object vm, Instrumenter instrumenter) { this.vm = vm; - + this.instrumenter = instrumenter; Source.setFileCaching(true); // Initialize execution context stack @@ -117,7 +122,7 @@ prepareContinue(); debugContext.contextTrace("START EXEC DEFAULT"); - final BreakpointCallback breakpointCallback = new BreakpointCallback() { + breakpointCallback = new BreakpointCallback() { @TruffleBoundary public void haltedAt(Node astNode, MaterializedFrame mFrame, String haltReason) { @@ -125,7 +130,7 @@ } }; - final WarningLog warningLog = new WarningLog() { + warningLog = new WarningLog() { public void addWarning(String warning) { assert debugContext != null; @@ -137,10 +142,6 @@ this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog); } - Object vm() { - return vm; - } - /** * Sets a breakpoint to halt at a source line. * @@ -269,11 +270,12 @@ debugContext.setStrategy(new StepOver(stepCount)); } + // TODO (mlvdv) used by the breakpoint factories; to be deprecated/replaced. /** * Creates a language-specific factory to produce instances of {@link AdvancedInstrumentRoot} * that, when executed, computes the result of a textual expression in the language; used to * create an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. * * @param expr a guest language expression @@ -284,15 +286,12 @@ */ @SuppressWarnings("rawtypes") AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Probe probe, String expr, AdvancedInstrumentResultListener resultListener) throws IOException { - try { - Class<? extends TruffleLanguage> langugageClass = ACCESSOR.findLanguage(probe); - TruffleLanguage.Env env = ACCESSOR.findLanguage(vm, langugageClass); - TruffleLanguage<?> l = ACCESSOR.findLanguage(env); - DebugSupportProvider dsp = ACCESSOR.getDebugSupport(l); - return dsp.createAdvancedInstrumentRootFactory(expr, resultListener); - } catch (DebugSupportException ex) { - throw new IOException(ex); - } + Class<? extends TruffleLanguage> languageClass = ACCESSOR.findLanguage(probe); + return ACCESSOR.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); + } + + Instrumenter getInstrumenter() { + return instrumenter; } /** @@ -416,7 +415,7 @@ @Override protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + instrumenter.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { @TruffleBoundary @Override public void tagTrappedAt(Node node, MaterializedFrame mFrame) { @@ -430,7 +429,7 @@ strategyTrace("RESUME BEFORE", ""); } }); - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { @TruffleBoundary @Override public void tagTrappedAt(Node node, MaterializedFrame mFrame) { @@ -449,8 +448,8 @@ @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - Probe.setAfterTagTrap(null); + instrumenter.setBeforeTagTrap(null); + instrumenter.setAfterTagTrap(null); } } @@ -473,7 +472,7 @@ @Override protected void setStrategy(final int stackDepth) { - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { @TruffleBoundary @Override @@ -491,7 +490,7 @@ @Override protected void unsetStrategy() { - Probe.setAfterTagTrap(null); + instrumenter.setAfterTagTrap(null); } } @@ -517,7 +516,7 @@ @Override protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + instrumenter.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { @TruffleBoundary @Override public void tagTrappedAt(Node node, MaterializedFrame mFrame) { @@ -542,7 +541,7 @@ } }); - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { @TruffleBoundary @Override public void tagTrappedAt(Node node, MaterializedFrame mFrame) { @@ -563,8 +562,8 @@ @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - Probe.setAfterTagTrap(null); + instrumenter.setBeforeTagTrap(null); + instrumenter.setAfterTagTrap(null); } } @@ -592,7 +591,7 @@ @Override protected void setStrategy(final int stackDepth) { - Probe.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { + instrumenter.setBeforeTagTrap(new SyntaxTagTrap(STEPPING_TAG) { @TruffleBoundary @Override public void tagTrappedAt(Node node, MaterializedFrame mFrame) { @@ -613,7 +612,7 @@ @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); + instrumenter.setBeforeTagTrap(null); } } @@ -813,7 +812,8 @@ protected Closeable executionStart(Object vm, Debugger[] fillIn, Source s) { final Debugger d; if (fillIn[0] == null) { - d = fillIn[0] = new Debugger(vm); + final Instrumenter instrumenter = ACCESSOR.getInstrumenter(vm); + d = fillIn[0] = new Debugger(vm, instrumenter); } else { d = fillIn[0]; } @@ -842,8 +842,14 @@ } @Override - protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { - return super.getDebugSupport(l); + protected Instrumenter getInstrumenter(Object vm) { + return super.getInstrumenter(vm); + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr, + AdvancedInstrumentResultListener resultListener) throws IOException { + return super.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); } @Override
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,20 +24,32 @@ */ package com.oracle.truffle.api.debug; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.Truffle; import static com.oracle.truffle.api.debug.Breakpoint.State.DISABLED; import static com.oracle.truffle.api.debug.Breakpoint.State.DISABLED_UNRESOLVED; import static com.oracle.truffle.api.debug.Breakpoint.State.DISPOSED; import static com.oracle.truffle.api.debug.Breakpoint.State.ENABLED; import static com.oracle.truffle.api.debug.Breakpoint.State.ENABLED_UNRESOLVED; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; +import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.debug.Debugger.BreakpointCallback; import com.oracle.truffle.api.debug.Debugger.WarningLog; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.StandardSyntaxTag; import com.oracle.truffle.api.instrument.SyntaxTag; @@ -48,15 +60,6 @@ import com.oracle.truffle.api.source.LineLocation; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.utilities.CyclicAssumption; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; //TODO (mlvdv) some common functionality could be factored out of this and TagBreakpointSupport @@ -83,7 +86,7 @@ private static final boolean TRACE = false; private static final PrintStream OUT = System.out; - private static final String BREAKPOINT_NAME = "LINE BREAKPOINT"; + private static final String BREAKPOINT_NAME = "Line Breakpoints"; @TruffleBoundary private static void trace(String format, Object... args) { @@ -106,6 +109,7 @@ } }; + private final Debugger debugger; private final BreakpointCallback breakpointCallback; private final WarningLog warningLog; @@ -127,17 +131,17 @@ */ @CompilationFinal private boolean breakpointsActive = true; private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active"); - private final Debugger debugger; LineBreakpointFactory(Debugger debugger, BreakpointCallback breakpointCallback, final WarningLog warningLog) { this.debugger = debugger; this.breakpointCallback = breakpointCallback; this.warningLog = warningLog; + final Instrumenter instrumenter = debugger.getInstrumenter(); lineToProbesMap = new LineToProbesMap(); - lineToProbesMap.install(); + lineToProbesMap.install(instrumenter); - Probe.addProbeListener(new DefaultProbeListener() { + instrumenter.addProbeListener(new DefaultProbeListener() { @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { @@ -378,12 +382,12 @@ throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME); } Instrument newInstrument = null; + final Instrumenter instrumenter = debugger.getInstrumenter(); if (conditionExpr == null) { - newInstrument = Instrument.create(new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME); + newInstrument = instrumenter.attach(newProbe, new UnconditionalLineBreakInstrumentListener(), BREAKPOINT_NAME); } else { - newInstrument = Instrument.create(this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); + newInstrument = instrumenter.attach(newProbe, this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); } - newProbe.attach(newInstrument); instruments.add(newInstrument); changeState(isEnabled ? ENABLED : DISABLED); } @@ -442,7 +446,7 @@ } } - public void notifyResult(Node node, VirtualFrame vFrame, Object result) { + public void onExecution(Node node, VirtualFrame vFrame, Object result) { final boolean condition = (Boolean) result; if (TRACE) { trace("breakpoint condition = %b %s", condition, getShortDescription()); @@ -452,7 +456,7 @@ } } - public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { + public void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { addExceptionWarning(ex); if (TRACE) { trace("breakpoint failure = %s %s", ex, getShortDescription()); @@ -479,7 +483,7 @@ private final class UnconditionalLineBreakInstrumentListener extends DefaultStandardInstrumentListener { @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { LineBreakpointImpl.this.nodeEnter(node, vFrame); } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,13 +24,6 @@ */ package com.oracle.truffle.api.debug; -import com.oracle.truffle.api.instrument.InstrumentationTool; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeListener; -import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; -import com.oracle.truffle.api.source.LineLocation; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; @@ -38,11 +31,19 @@ import java.util.HashMap; import java.util.Map; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ProbeListener; +import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; +import com.oracle.truffle.api.source.LineLocation; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; + /** * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST, * indexed by {@link Source} and line number. */ -final class LineToProbesMap extends InstrumentationTool { +final class LineToProbesMap extends Instrumenter.Tool { private static final boolean TRACE = false; private static final PrintStream OUT = System.out; @@ -68,7 +69,11 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + final Instrumenter instrumenter = getInstrumenter(); + for (Probe probe : instrumenter.findProbesTaggedAs(null)) { + addMapEntry(probe); + } + instrumenter.addProbeListener(probeListener); return true; } @@ -79,7 +84,7 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); } /** @@ -115,21 +120,25 @@ @Override public void newProbeInserted(Probe probe) { - final SourceSection sourceSection = probe.getProbedSourceSection(); - if (sourceSection != null && sourceSection.getSource() != null) { - final LineLocation lineLocation = sourceSection.getLineLocation(); - if (TRACE) { - trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription()); - } - Collection<Probe> probes = lineToProbesMap.get(lineLocation); - if (probes == null) { - probes = new ArrayList<>(2); - lineToProbesMap.put(lineLocation, probes); - } else { - assert !probes.contains(probe); - } - probes.add(probe); + addMapEntry(probe); + } + } + + private void addMapEntry(Probe probe) { + final SourceSection sourceSection = probe.getProbedSourceSection(); + if (sourceSection != null && sourceSection.getSource() != null) { + final LineLocation lineLocation = sourceSection.getLineLocation(); + if (TRACE) { + trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription()); } + Collection<Probe> probes = lineToProbesMap.get(lineLocation); + if (probes == null) { + probes = new ArrayList<>(2); + lineToProbesMap.put(lineLocation, probes); + } else { + assert !probes.contains(probe); + } + probes.add(probe); } } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Mon Sep 21 12:15:38 2015 -0700 @@ -47,6 +47,7 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.SyntaxTag; import com.oracle.truffle.api.instrument.SyntaxTagTrap; @@ -62,10 +63,10 @@ * Support class for creating and managing "Tag Breakpoints". A Tag Breakpoint halts execution just * before reaching any node whose Probe carries a specified {@linkplain SyntaxTag Tag}. * <p> - * The {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Tag Trap}, which is built directly into the - * Instrumentation Framework, does the same thing more efficiently, but there may only be one Tag - * Trap active at a time. Any number of tag breakpoints may coexist with the Tag Trap, but it would - * be confusing to have a Tag Breakpoint set for the same Tag as the current Tag Trap. + * The {@linkplain Instrumenter#setBeforeTagTrap(SyntaxTagTrap) Tag Trap}, which is built directly + * into the Instrumentation Framework, does the same thing more efficiently, but there may only be + * one Tag Trap active at a time. Any number of tag breakpoints may coexist with the Tag Trap, but + * it would be confusing to have a Tag Breakpoint set for the same Tag as the current Tag Trap. * <p> * Notes: * <ol> @@ -103,6 +104,7 @@ } }; + private final Debugger debugger; private final BreakpointCallback breakpointCallback; private final WarningLog warningLog; @@ -117,14 +119,13 @@ */ @CompilationFinal private boolean breakpointsActive = true; private final CyclicAssumption breakpointsActiveUnchanged = new CyclicAssumption(BREAKPOINT_NAME + " globally active"); - private final Debugger debugger; TagBreakpointFactory(Debugger debugger, BreakpointCallback breakpointCallback, final WarningLog warningLog) { this.debugger = debugger; this.breakpointCallback = breakpointCallback; this.warningLog = warningLog; - Probe.addProbeListener(new DefaultProbeListener() { + debugger.getInstrumenter().addProbeListener(new DefaultProbeListener() { @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { @@ -195,7 +196,7 @@ tagToBreakpoint.put(tag, breakpoint); - for (Probe probe : Probe.findProbesTaggedAs(tag)) { + for (Probe probe : debugger.getInstrumenter().findProbesTaggedAs(tag)) { breakpoint.attach(probe); } } else { @@ -341,12 +342,12 @@ throw new IllegalStateException("Attempt to attach a disposed " + BREAKPOINT_NAME); } Instrument newInstrument = null; + final Instrumenter instrumenter = debugger.getInstrumenter(); if (conditionExpr == null) { - newInstrument = Instrument.create(new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME); + newInstrument = instrumenter.attach(newProbe, new UnconditionalTagBreakInstrumentListener(), BREAKPOINT_NAME); } else { - newInstrument = Instrument.create(this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); + instrumenter.attach(newProbe, this, debugger.createAdvancedInstrumentRootFactory(newProbe, conditionExpr, this), Boolean.class, BREAKPOINT_NAME); } - newProbe.attach(newInstrument); instruments.add(newInstrument); changeState(isEnabled ? ENABLED : DISABLED); } @@ -406,7 +407,7 @@ } - public void notifyResult(Node node, VirtualFrame vFrame, Object result) { + public void onExecution(Node node, VirtualFrame vFrame, Object result) { final boolean condition = (Boolean) result; if (TRACE) { trace("breakpoint condition = %b %s", condition, getShortDescription()); @@ -416,7 +417,7 @@ } } - public void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { + public void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex) { addExceptionWarning(ex); if (TRACE) { trace("breakpoint failure = %s %s", ex, getShortDescription()); @@ -443,7 +444,7 @@ private final class UnconditionalTagBreakInstrumentListener extends DefaultStandardInstrumentListener { @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { TagBreakpointImpl.this.nodeEnter(node, vFrame); } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,6 +24,13 @@ */ package com.oracle.truffle.api.impl; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; + import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; @@ -31,26 +38,28 @@ import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.debug.DebugSupportProvider; import com.oracle.truffle.api.debug.Debugger; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.Source; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; /** - * Communication between TruffleVM and TruffleLanguage API/SPI. + * Communication between TruffleVM, TruffleLanguage API/SPI, and other services. */ @SuppressWarnings("rawtypes") public abstract class Accessor { private static Accessor API; private static Accessor SPI; private static Accessor NODES; + private static Accessor INTEROP; private static Accessor INSTRUMENT; private static Accessor DEBUG; private static final ThreadLocal<Object> CURRENT_VM = new ThreadLocal<>(); @@ -72,11 +81,13 @@ return false; } + @SuppressWarnings("deprecation") @Override protected ToolSupportProvider getToolSupport() { - return null; + throw new UnsupportedOperationException(); } + @SuppressWarnings("deprecation") @Override protected DebugSupportProvider getDebugSupport() { return null; @@ -91,12 +102,49 @@ protected Object createContext(TruffleLanguage.Env env) { return null; } + + @Override + protected boolean isInstrumentable(Node node) { + return false; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + return null; + } + + @Override + protected ASTProber getDefaultASTProber() { + return null; + } + + @Override + protected Visualizer getVisualizer() { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } }; lng.hashCode(); new Node(null) { }.getRootNode(); try { + Class.forName(Instrumenter.class.getName(), true, Instrumenter.class.getClassLoader()); Class.forName(Debugger.class.getName(), true, Debugger.class.getClassLoader()); } catch (ClassNotFoundException ex) { throw new IllegalStateException(ex); @@ -114,6 +162,11 @@ throw new IllegalStateException(); } NODES = this; + } else if (this.getClass().getSimpleName().endsWith("Interop")) { + if (INTEROP != null) { + throw new IllegalStateException(); + } + INTEROP = this; } else if (this.getClass().getSimpleName().endsWith("Instrument")) { if (INSTRUMENT != null) { throw new IllegalStateException(); @@ -152,12 +205,45 @@ return API.languageGlobal(env); } - protected ToolSupportProvider getToolSupport(TruffleLanguage<?> l) { - return API.getToolSupport(l); + @Deprecated + protected ToolSupportProvider getToolSupport(@SuppressWarnings("unused") TruffleLanguage<?> l) { + throw new UnsupportedOperationException(); + } + + @Deprecated + protected DebugSupportProvider getDebugSupport(@SuppressWarnings("unused") TruffleLanguage<?> l) { + throw new UnsupportedOperationException(); + } + + protected boolean isInstrumentable(Object vm, Node node) { + final RootNode rootNode = node.getRootNode(); + Class<? extends TruffleLanguage> languageClazz = findLanguage(rootNode); + TruffleLanguage language = findLanguageImpl(vm, languageClazz); + return isInstrumentable(node, language); } - protected DebugSupportProvider getDebugSupport(TruffleLanguage<?> l) { - return API.getDebugSupport(l); + protected boolean isInstrumentable(Node node, TruffleLanguage language) { + return API.isInstrumentable(node, language); + } + + protected WrapperNode createWrapperNode(Object vm, Node node) { + final RootNode rootNode = node.getRootNode(); + Class<? extends TruffleLanguage> languageClazz = findLanguage(rootNode); + TruffleLanguage language = findLanguageImpl(vm, languageClazz); + return createWrapperNode(node, language); + } + + protected WrapperNode createWrapperNode(Node node, TruffleLanguage language) { + return API.createWrapperNode(node, language); + } + + protected ASTProber getDefaultASTProber(TruffleLanguage language) { + return API.getDefaultASTProber(language); + } + + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class<? extends TruffleLanguage> languageClass, String expr, AdvancedInstrumentResultListener resultListener) + throws IOException { + return API.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); } protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { @@ -184,6 +270,36 @@ return SPI.findLanguage(vm, languageClass); } + protected TruffleLanguage findLanguageImpl(Object known, Class<? extends TruffleLanguage> languageClass) { + Object vm; + if (known == null) { + vm = CURRENT_VM.get(); + if (vm == null) { + throw new IllegalStateException(); + } + } else { + vm = known; + } + return SPI.findLanguageImpl(vm, languageClass); + } + + protected Instrumenter getInstrumenter(Object known) { + Object vm; + if (known == null) { + vm = CURRENT_VM.get(); + if (vm == null) { + throw new IllegalStateException(); + } + } else { + vm = known; + } + return SPI.getInstrumenter(vm); + } + + protected Instrumenter createInstrumenter(Object vm) { + return INSTRUMENT.createInstrumenter(vm); + } + private static Reference<Object> previousVM = new WeakReference<>(null); private static Assumption oneVM = Truffle.getRuntime().createAssumption(); @@ -215,7 +331,7 @@ return oneVM; } - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings({"unchecked"}) static <C> C findContext(Class<? extends TruffleLanguage> type) { Env env = SPI.findLanguage(CURRENT_VM.get(), type); return (C) API.findContext(env); @@ -237,4 +353,8 @@ protected TruffleLanguage<?> findLanguage(Env env) { return API.findLanguage(env); } + + protected void applyInstrumentation(Node node) { + INSTRUMENT.applyInstrumentation(node); + } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Mon Sep 21 12:15:38 2015 -0700 @@ -31,15 +31,15 @@ * not yet executed) AST. * * @see Probe - * @see Probe#addProbeListener(ProbeListener) + * @see Instrumenter#addProbeListener(ProbeListener) */ public interface ASTProber { /** * Walk the AST starting at a node and enable instrumentation at selected nodes by attaching - * {@linkplain Probe Probes} to them. Ignore {@linkplain Node#isInstrumentable() + * {@linkplain Probe Probes} to them. Ignore {@linkplain Instrumenter#isInstrumentable(Node) * non-instrumentable} nodes. */ - void probeAST(Node node); + void probeAST(Instrumenter instrumenter, Node node); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentResultListener.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentResultListener.java Mon Sep 21 12:15:38 2015 -0700 @@ -31,8 +31,11 @@ /** * Listener for receiving the result a client-provided {@linkplain AdvancedInstrumentRoot AST * fragment}, when executed by a - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. + * <p> + * Notification is fully synchronous, so overrides have performance implications. Non-trivial + * methods should be coded with Truffle guidelines and cautions in mind. * * @see Instrument * @see AdvancedInstrumentRoot @@ -43,7 +46,7 @@ /** * Notifies listener that a client-provided {@linkplain AdvancedInstrumentRoot AST fragment} has * been executed by an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument} with the specified result, possibly {@code null}. * <p> * <strong>Note: </strong> Truffle will attempt to optimize implementations through partial @@ -54,7 +57,7 @@ * @param vFrame execution frame at the guest-language AST node * @param result the result of this AST fragment's execution */ - void notifyResult(Node node, VirtualFrame vFrame, Object result); + void onExecution(Node node, VirtualFrame vFrame, Object result); /** * Notifies listener that execution of client-provided {@linkplain AdvancedInstrumentRoot AST @@ -69,6 +72,6 @@ * @param vFrame execution frame at the guest-language AST node * @param ex the exception */ - void notifyFailure(Node node, VirtualFrame vFrame, RuntimeException ex); + void onFailure(Node node, VirtualFrame vFrame, RuntimeException ex); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRoot.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRoot.java Mon Sep 21 12:15:38 2015 -0700 @@ -30,7 +30,7 @@ /** * Root of a client-provided AST fragment that can be executed efficiently, subject to full Truffle * optimization, by an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. * * @see Instrument
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRootFactory.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/AdvancedInstrumentRootFactory.java Mon Sep 21 12:15:38 2015 -0700 @@ -29,7 +29,7 @@ /** * Creator of {@linkplain AdvancedInstrumentRoot AST fragments} suitable for efficient execution, * subject to full Truffle optimization, by an - * {@linkplain Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * {@linkplain Instrumenter#attach(Probe, AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) * Advanced Instrument}. * * @see Instrument
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/EventHandlerNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument; + +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; + +/** + * An Instrumentation-managed {@link Node} that synchronously propagates notification of AST + * Execution Events through the Instrumentation Framework. + */ +public abstract class EventHandlerNode extends Node implements InstrumentationNode { + + protected EventHandlerNode() { + } + + /** + * An AST node's execute method is about to be called. + */ + public abstract void enter(Node node, VirtualFrame vFrame); + + /** + * An AST Node's {@code void}-valued execute method has just returned. + */ + public abstract void returnVoid(Node node, VirtualFrame vFrame); + + /** + * An AST Node's execute method has just returned a value (boxed if primitive). + */ + public abstract void returnValue(Node node, VirtualFrame vFrame, Object result); + + /** + * An AST Node's execute method has just thrown an exception. + */ + public abstract void returnExceptional(Node node, VirtualFrame vFrame, Exception exception); + + /** + * Gets the {@link Probe} that manages this chain of event handling. + */ + public abstract Probe getProbe(); + +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Mon Sep 21 12:15:38 2015 -0700 @@ -27,15 +27,11 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; -// TODO (mlvdv) these statics should not be global. Move them to some kind of context. -// TODO (mlvdv) migrate factory (together with Probe)? break out nested classes? - /** * A <em>binding</em> between: * <ol> @@ -47,117 +43,13 @@ * Client-oriented documentation for the use of Instruments is available online at <a * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events" >https:// * wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a> - * <p> - * 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 - * an AST must be cloned along with the AST: AST clones are not permitted to share Nodes. - * <p> - * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is encouraged - * by providing the {@link SimpleInstrumentListener} for clients that need know nothing more than - * the properties associated with a Probe: it's {@link SourceSection} and any associated instances - * of {@link SyntaxTag}. - * <p> - * AST cloning is <em>not transparent</em> to clients that use the - * {@link StandardInstrumentListener}, since those event methods identify the concrete Node instance - * (and thus the AST instance) where the event takes place. - * <p> - * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4> - * <p> - * <ul> - * <li>A new Instrument is created in permanent association with a client-provided - * <em>listener.</em></li> - * - * <li>Multiple Instruments may share a single listener.</li> - * - * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to a - * Probe, at which time the Instrument begins routing execution events from the Probe's AST location - * to the Instrument's listener.</li> - * - * <li>Neither Instruments nor Probes are {@link Node}s.</li> - * - * <li>A Probe has a single source-based location in an AST, but manages a separate - * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li> - * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is cloned - * along with the rest of the AST.</li> - * - * <li>When a new Instrument (for example an instance created by - * {@link Instrument#create(com.oracle.truffle.api.instrument.SimpleInstrumentListener, java.lang.String)} - * is attached to a Probe, the Instrument inserts a new instance of its private Node type into - * <em>each of the instrument chains</em> managed by the Probe, i.e. one node instance per existing - * clone of the AST.</li> - * - * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the - * Instrument's private Node type will be cloned along with the rest of the the AST.</li> - * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the - * shared (outer) Instrument instance; that state includes a reference to the Instrument's listener. - * </li> - * - * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, the - * Instrument searches every instrument chain associated with the Probe and removes the instance of - * its private Node type.</li> - * - * <li>Attaching and disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of the - * AST.</li> - * - * </ul> * * @see Probe - * @see TruffleEvents + * @see EventHandlerNode */ public abstract class Instrument { /** - * Creates a <em>Simple Instrument</em>: this Instrument routes execution events to a - * client-provided listener. - * - * @param listener a listener for execution events - * @param instrumentInfo optional description of the instrument's role, intended for debugging. - * @return a new instrument, ready for attachment at a probe - */ - public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) { - return new SimpleInstrument(listener, instrumentInfo); - } - - /** - * Creates a <em>Standard Instrument</em>: this Instrument routes execution events, together - * with access to Truffle execution state, to a client-provided listener. - * - * @param standardListener a listener for execution events and execution state - * @param instrumentInfo optional description of the instrument's role, intended for debugging. - * @return a new instrument, ready for attachment at a probe - */ - public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) { - return new StandardInstrument(standardListener, instrumentInfo); - } - - /** - * Creates an <em>Advanced Instrument</em>: this Instrument executes efficiently, subject to - * full Truffle optimization, a client-provided AST fragment every time the Probed node is - * entered. - * <p> - * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework - * and reported to the listener; there is no other notification. - * - * @param resultListener optional client callback for results/failure notification - * @param rootFactory provider of AST fragments on behalf of the client - * @param requiredResultType optional requirement, any non-assignable result is reported to the - * the listener, if any, as a failure - * @param instrumentInfo optional description of the instrument's role, intended for debugging. - * @return a new instrument, ready for attachment at a probe - */ - public static Instrument create(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) { - return new AdvancedInstrument(resultListener, rootFactory, requiredResultType, instrumentInfo); - } - - // TODO (mlvdv) experimental - /** - * For implementation testing. - */ - public static Instrument create(TruffleOptListener listener) { - return new TruffleOptInstrument(listener, null); - } - - /** * Has this instrument been disposed? stays true once set. */ private boolean isDisposed = false; @@ -169,6 +61,61 @@ */ private final String instrumentInfo; + /** + * <h4>Implementation Notes</h4> + * <p> + * 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 an AST must be cloned along with the AST: AST clones are not permitted to share + * Nodes. + * <p> + * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is + * encouraged by providing the {@link SimpleInstrumentListener} for clients that need know + * nothing more than the properties associated with a Probe: its {@link SourceSection} and any + * associated instances of {@link SyntaxTag}. + * <p> + * AST cloning is <em>not transparent</em> to clients that use the + * {@link StandardInstrumentListener}, since those event methods identify the concrete Node + * instance (and thus the AST instance) where the event takes place. + * <p> + * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4> + * <p> + * <ul> + * <li>A new Instrument is created in permanent association with a client-provided + * <em>listener.</em></li> + * + * <li>Multiple Instruments may share a single listener.</li> + * + * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to + * a Probe, at which time the Instrument begins routing execution events from the Probe's AST + * location to the Instrument's listener.</li> + * + * <li>Neither Instruments nor Probes are {@link Node}s.</li> + * + * <li>A Probe has a single source-based location in an AST, but manages a separate + * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li> + * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is + * cloned along with the rest of the AST.</li> + * + * <li>When a new Instrument is attached to a Probe, the Instrument inserts a new instance of + * its private Node type into <em>each of the instrument chains</em> managed by the Probe, i.e. + * one node instance per existing clone of the AST.</li> + * + * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the + * Instrument's private Node type will be cloned along with the rest of the the AST.</li> + * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the + * shared (outer) Instrument instance; that state includes a reference to the Instrument's + * listener.</li> + * + * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, + * the Instrument searches every instrument chain associated with the Probe and removes the + * instance of its private Node type.</li> + * + * <li>Attaching or disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of + * the AST.</li> + * + * </ul> + */ private Instrument(String instrumentInfo) { this.instrumentInfo = instrumentInfo; } @@ -223,14 +170,14 @@ /** * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}. */ - private static final class SimpleInstrument extends Instrument { + static final class SimpleInstrument extends Instrument { /** * Tool-supplied listener for events. */ private final SimpleInstrumentListener simpleListener; - private SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) { + SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) { super(instrumentInfo); this.simpleListener = simpleListener; } @@ -267,34 +214,39 @@ super(nextNode); } + @Override public void enter(Node node, VirtualFrame vFrame) { - SimpleInstrument.this.simpleListener.enter(SimpleInstrument.this.probe); + SimpleInstrument.this.simpleListener.onEnter(SimpleInstrument.this.probe); if (nextInstrumentNode != null) { nextInstrumentNode.enter(node, vFrame); } } + @Override public void returnVoid(Node node, VirtualFrame vFrame) { - SimpleInstrument.this.simpleListener.returnVoid(SimpleInstrument.this.probe); + SimpleInstrument.this.simpleListener.onReturnVoid(SimpleInstrument.this.probe); if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); } } + @Override public void returnValue(Node node, VirtualFrame vFrame, Object result) { - SimpleInstrument.this.simpleListener.returnValue(SimpleInstrument.this.probe, result); + SimpleInstrument.this.simpleListener.onReturnValue(SimpleInstrument.this.probe, result); if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); } } + @Override public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - SimpleInstrument.this.simpleListener.returnExceptional(SimpleInstrument.this.probe, exception); + SimpleInstrument.this.simpleListener.onReturnExceptional(SimpleInstrument.this.probe, exception); if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); } } + @Override public String instrumentationInfo() { final String info = getInstrumentInfo(); return info != null ? info : simpleListener.getClass().getSimpleName(); @@ -305,14 +257,14 @@ /** * An instrument that propagates events to an instance of {@link StandardInstrumentListener}. */ - private static final class StandardInstrument extends Instrument { + static final class StandardInstrument extends Instrument { /** * Tool-supplied listener for AST events. */ private final StandardInstrumentListener standardListener; - private StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) { + StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) { super(instrumentInfo); this.standardListener = standardListener; } @@ -349,34 +301,39 @@ super(nextNode); } + @Override public void enter(Node node, VirtualFrame vFrame) { - standardListener.enter(StandardInstrument.this.probe, node, vFrame); + standardListener.onEnter(StandardInstrument.this.probe, node, vFrame); if (nextInstrumentNode != null) { nextInstrumentNode.enter(node, vFrame); } } + @Override public void returnVoid(Node node, VirtualFrame vFrame) { - standardListener.returnVoid(StandardInstrument.this.probe, node, vFrame); + standardListener.onReturnVoid(StandardInstrument.this.probe, node, vFrame); if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); } } + @Override public void returnValue(Node node, VirtualFrame vFrame, Object result) { - standardListener.returnValue(StandardInstrument.this.probe, node, vFrame, result); + standardListener.onReturnValue(StandardInstrument.this.probe, node, vFrame, result); if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); } } + @Override public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { - standardListener.returnExceptional(StandardInstrument.this.probe, node, vFrame, exception); + standardListener.onReturnExceptional(StandardInstrument.this.probe, node, vFrame, exception); if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); } } + @Override public String instrumentationInfo() { final String info = getInstrumentInfo(); return info != null ? info : standardListener.getClass().getSimpleName(); @@ -389,13 +346,13 @@ * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle * AST with potential for full optimization. */ - private static final class AdvancedInstrument extends Instrument { + static final class AdvancedInstrument extends Instrument { private final AdvancedInstrumentResultListener resultListener; private final AdvancedInstrumentRootFactory rootFactory; private final Class<?> requiredResultType; - private AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) { + AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) { super(instrumentInfo); this.resultListener = resultListener; this.rootFactory = rootFactory; @@ -436,6 +393,7 @@ super(nextNode); } + @Override public void enter(Node node, VirtualFrame vFrame) { if (instrumentRoot == null) { try { @@ -447,7 +405,7 @@ } } catch (RuntimeException ex) { if (resultListener != null) { - resultListener.notifyFailure(node, vFrame, ex); + resultListener.onFailure(node, vFrame, ex); } } } @@ -456,11 +414,11 @@ final Object result = instrumentRoot.executeRoot(node, vFrame); if (resultListener != null) { checkResultType(result); - resultListener.notifyResult(node, vFrame, result); + resultListener.onExecution(node, vFrame, result); } } catch (RuntimeException ex) { if (resultListener != null) { - resultListener.notifyFailure(node, vFrame, ex); + resultListener.onFailure(node, vFrame, ex); } } } @@ -491,24 +449,28 @@ return new RuntimeException("Instrument result " + result.toString() + " not assignable to " + requiredResultType.getSimpleName()); } + @Override public void returnVoid(Node node, VirtualFrame vFrame) { if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); } } + @Override public void returnValue(Node node, VirtualFrame vFrame, Object result) { if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); } } + @Override public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); } } + @Override public String instrumentationInfo() { final String info = getInstrumentInfo(); return info != null ? info : rootFactory.getClass().getSimpleName(); @@ -516,10 +478,12 @@ } } + // TODO (mlvdv) experimental public interface TruffleOptListener { void notifyIsCompiled(boolean isCompiled); } + @SuppressWarnings("unused") private static final class TruffleOptInstrument extends Instrument { private final TruffleOptListener toolOptListener; @@ -561,6 +525,7 @@ this.isCompiled = CompilerDirectives.inCompiledCode(); } + @Override public void enter(Node node, VirtualFrame vFrame) { if (this.isCompiled != CompilerDirectives.inCompiledCode()) { this.isCompiled = CompilerDirectives.inCompiledCode(); @@ -571,24 +536,28 @@ } } + @Override public void returnVoid(Node node, VirtualFrame vFrame) { if (nextInstrumentNode != null) { nextInstrumentNode.returnVoid(node, vFrame); } } + @Override public void returnValue(Node node, VirtualFrame vFrame, Object result) { if (nextInstrumentNode != null) { nextInstrumentNode.returnValue(node, vFrame, result); } } + @Override public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { if (nextInstrumentNode != null) { nextInstrumentNode.returnExceptional(node, vFrame, exception); } } + @Override public String instrumentationInfo() { final String info = getInstrumentInfo(); return info != null ? info : toolOptListener.getClass().getSimpleName(); @@ -597,7 +566,7 @@ } @NodeInfo(cost = NodeCost.NONE) - abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode { + abstract class AbstractInstrumentNode extends EventHandlerNode { @Child protected AbstractInstrumentNode nextInstrumentNode; @@ -606,8 +575,8 @@ } @Override - public boolean isInstrumentable() { - return false; + public Probe getProbe() { + return probe; } /**
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationException.java Mon Sep 21 13:11:41 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +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. 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; - -public class InstrumentationException extends Exception { - - public InstrumentationException(RuntimeException ex) { - super(ex); - } - - private static final long serialVersionUID = 447857066220935502L; - -}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,7 +24,6 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; /** @@ -39,30 +38,4 @@ */ String instrumentationInfo(); - /** - * Events that propagate through the {@linkplain InstrumentationNode implementation nodes} of - * the Instrumentation Framework, not visible in this form to Instrumentation clients. - */ - interface TruffleEvents { - - /** - * An AST node's execute method is about to be called. - */ - void enter(Node node, VirtualFrame vFrame); - - /** - * An AST Node's {@code void}-valued execute method has just returned. - */ - void returnVoid(Node node, VirtualFrame vFrame); - - /** - * An AST Node's execute method has just returned a value (boxed if primitive). - */ - void returnValue(Node node, VirtualFrame vFrame, Object result); - - /** - * An AST Node's execute method has just thrown an exception. - */ - void returnExceptional(Node node, VirtualFrame vFrame, Exception exception); - } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java Mon Sep 21 13:11:41 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,185 +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. 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; - -/** - * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language - * program execution. - * <p> - * Tools share a common <em>life cycle</em>: - * <ul> - * <li>A newly created tool is inert until {@linkplain #install() installed}.</li> - * <li>An installed tool becomes <em>enabled</em> and immediately begins installing - * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from - * those instruments</li> - * <li>A tool may only be installed once.</li> - * <li>It should be possible to install multiple instances of a tool, possibly (but not necessarily) - * configured differently with respect to what data is being collected.</li> - * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) enabled and disabled} - * arbitrarily.</li> - * <li>A disabled tool: - * <ul> - * <li>Collects no data;</li> - * <li>Retains existing AST instrumentation;</li> - * <li>Continues to instrument newly created ASTs; and</li> - * <li>Retains previously collected data.</li> - * </ul> - * </li> - * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool - * installed but with all previously collected data removed.</li> - * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not - * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data persists.</li> - * </ul> - * <p> - * Tool-specific methods that access data collected by the tool should: - * <ul> - * <li>Return modification-safe representations of the data; and</li> - * <li>Not change the state of the data.</li> - * </ul> - * <b>Note:</b><br> - * Tool installation is currently <em>global</em> to the Truffle Execution environment. When - * language-agnostic management of individual execution environments is added to the platform, - * installation will be (optionally) specific to a single execution environment. - */ -public abstract class InstrumentationTool { - // TODO (mlvdv) still thinking about the most appropriate name for this class of tools - - private enum ToolState { - - /** Not yet installed, inert. */ - UNINSTALLED, - - /** Installed, collecting data. */ - ENABLED, - - /** Installed, not collecting data. */ - DISABLED, - - /** Was installed, but now removed, inactive, and no longer usable. */ - DISPOSED; - } - - private ToolState toolState = ToolState.UNINSTALLED; - - protected InstrumentationTool() { - } - - /** - * Connect the tool to some part of the Truffle runtime, and enable data collection to start. - * Instrumentation will only be added to subsequently created ASTs. - * - * @throws IllegalStateException if the tool has previously been installed. - */ - public final void install() { - checkUninstalled(); - if (internalInstall()) { - toolState = ToolState.ENABLED; - } - } - - /** - * @return whether the tool is currently collecting data. - */ - public final boolean isEnabled() { - return toolState == ToolState.ENABLED; - } - - /** - * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not - * collecting data, but keeping data already collected). - * - * @throws IllegalStateException if not yet installed or disposed. - */ - public final void setEnabled(boolean isEnabled) { - checkInstalled(); - internalSetEnabled(isEnabled); - toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED; - } - - /** - * Clears any data already collected, but otherwise does not change the state of the tool. - * - * @throws IllegalStateException if not yet installed or disposed. - */ - public final void reset() { - checkInstalled(); - internalReset(); - } - - /** - * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data already - * collected. - * - * @throws IllegalStateException if not yet installed or disposed. - */ - public final void dispose() { - checkInstalled(); - internalDispose(); - toolState = ToolState.DISPOSED; - } - - /** - * @return whether the installation succeeded. - */ - protected abstract boolean internalInstall(); - - /** - * No subclass action required. - * - * @param isEnabled - */ - protected void internalSetEnabled(boolean isEnabled) { - } - - protected abstract void internalReset(); - - protected abstract void internalDispose(); - - /** - * Ensure that the tool is currently installed. - * - * @throws IllegalStateException - */ - private void checkInstalled() throws IllegalStateException { - if (toolState == ToolState.UNINSTALLED) { - throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed"); - } - if (toolState == ToolState.DISPOSED) { - throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed"); - } - } - - /** - * Ensure that the tool has not yet been installed. - * - * @throws IllegalStateException - */ - private void checkUninstalled() { - if (toolState != ToolState.UNINSTALLED) { - throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed"); - } - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java Mon Sep 21 12:15:38 2015 -0700 @@ -0,0 +1,638 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.truffle.api.instrument; + +import java.io.PrintStream; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeVisitor; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; + +/** + * Access to instrumentation services in an instance of {@link TruffleVM}. + */ +public final class Instrumenter { + + private static final boolean TRACE = false; + private static final String TRACE_PREFIX = "Instrumenter: "; + private static final PrintStream OUT = System.out; + + private static void trace(String format, Object... args) { + if (TRACE) { + OUT.println(TRACE_PREFIX + String.format(format, args)); + } + } + + /** + * Walks an AST, looking for the first node with an assigned {@link SourceSection} and returning + * the {@link Source}. + */ + private static Source findSource(Node node) { + final FindSourceVisitor visitor = new FindSourceVisitor(); + node.accept(visitor); + return visitor.source; + } + + private enum ToolState { + + /** Not yet installed, inert. */ + UNINSTALLED, + + /** Installed, collecting data. */ + ENABLED, + + /** Installed, not collecting data. */ + DISABLED, + + /** Was installed, but now removed, inactive, and no longer usable. */ + DISPOSED; + } + + /** + * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language + * program execution. + * <p> + * Tools share a common <em>life cycle</em>: + * <ul> + * <li>A newly created tool is inert until {@linkplain #install(Instrumenter) installed}.</li> + * <li>An installed tool becomes <em>enabled</em> and immediately begins installing + * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from + * those instruments</li> + * <li>A tool may only be installed once.</li> + * <li>It should be possible to install multiple instances of a tool, possibly (but not + * necessarily) configured differently with respect to what data is being collected.</li> + * <li>Once installed, a tool can be {@linkplain #setEnabled(boolean) enabled and disabled} + * arbitrarily.</li> + * <li>A disabled tool: + * <ul> + * <li>Collects no data;</li> + * <li>Retains existing AST instrumentation;</li> + * <li>Continues to instrument newly created ASTs; and</li> + * <li>Retains previously collected data.</li> + * </ul> + * </li> + * <li>An installed tool may be {@linkplain #reset() reset} at any time, which leaves the tool + * installed but with all previously collected data removed.</li> + * <li>A {@linkplain #dispose() disposed} tool removes all instrumentation (but not + * {@linkplain Probe probes}) and becomes permanently disabled; previously collected data + * persists.</li> + * </ul> + * <p> + * Tool-specific methods that access data collected by the tool should: + * <ul> + * <li>Return modification-safe representations of the data; and</li> + * <li>Not change the state of the data.</li> + * </ul> + * <b>Note:</b><br> + * Tool installation is currently <em>global</em> to the Truffle Execution environment. When + * language-agnostic management of individual execution environments is added to the platform, + * installation will be (optionally) specific to a single execution environment. + */ + public abstract static class Tool { + // TODO (mlvdv) still thinking about the most appropriate name for this class of tools + + private ToolState toolState = ToolState.UNINSTALLED; + + private Instrumenter instrumenter; + + protected Tool() { + } + + /** + * Connect the tool to some part of the Truffle runtime, and enable data collection to + * start. Instrumentation will only be added to subsequently created ASTs. + * + * @throws IllegalStateException if the tool has previously been installed. + */ + public final void install(Instrumenter inst) { + checkUninstalled(); + this.instrumenter = inst; + + if (internalInstall()) { + toolState = ToolState.ENABLED; + } + instrumenter.tools.add(this); + } + + /** + * @return whether the tool is currently collecting data. + */ + public final boolean isEnabled() { + return toolState == ToolState.ENABLED; + } + + /** + * Switches tool state between <em>enabled</em> (collecting data) and <em>disabled</em> (not + * collecting data, but keeping data already collected). + * + * @throws IllegalStateException if not yet installed or disposed. + */ + public final void setEnabled(boolean isEnabled) { + checkInstalled(); + internalSetEnabled(isEnabled); + toolState = isEnabled ? ToolState.ENABLED : ToolState.DISABLED; + } + + /** + * Clears any data already collected, but otherwise does not change the state of the tool. + * + * @throws IllegalStateException if not yet installed or disposed. + */ + public final void reset() { + checkInstalled(); + internalReset(); + } + + /** + * Makes the tool permanently <em>disabled</em>, removes instrumentation, but keeps data + * already collected. + * + * @throws IllegalStateException if not yet installed or disposed. + */ + public final void dispose() { + checkInstalled(); + internalDispose(); + toolState = ToolState.DISPOSED; + instrumenter.tools.remove(this); + } + + /** + * @return whether the installation succeeded. + */ + protected abstract boolean internalInstall(); + + /** + * No subclass action required. + * + * @param isEnabled + */ + protected void internalSetEnabled(boolean isEnabled) { + } + + protected abstract void internalReset(); + + protected abstract void internalDispose(); + + protected final Instrumenter getInstrumenter() { + return instrumenter; + } + + /** + * Ensure that the tool is currently installed. + * + * @throws IllegalStateException + */ + private void checkInstalled() throws IllegalStateException { + if (toolState == ToolState.UNINSTALLED) { + throw new IllegalStateException("Tool " + getClass().getSimpleName() + " not yet installed"); + } + if (toolState == ToolState.DISPOSED) { + throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has been disposed"); + } + } + + /** + * Ensure that the tool has not yet been installed. + * + * @throws IllegalStateException + */ + private void checkUninstalled() { + if (toolState != ToolState.UNINSTALLED) { + throw new IllegalStateException("Tool " + getClass().getSimpleName() + " has already been installed"); + } + } + } + + private final Object vm; + + /** Tools that have been created, but not yet disposed. */ + private final Set<Tool> tools = Collections.synchronizedSet(new LinkedHashSet<Tool>()); + + private final Set<ASTProber> astProbers = Collections.synchronizedSet(new LinkedHashSet<ASTProber>()); + + private final List<ProbeListener> probeListeners = new ArrayList<>(); + + /** + * All Probes that have been created. + */ + private final List<WeakReference<Probe>> probes = new ArrayList<>(); + + /** + * A global trap that triggers notification just before executing any Node that is Probed with a + * matching tag. + */ + @CompilationFinal private SyntaxTagTrap beforeTagTrap = null; + + /** + * A global trap that triggers notification just after executing any Node that is Probed with a + * matching tag. + */ + @CompilationFinal private SyntaxTagTrap afterTagTrap = null; + + private static final class FindSourceVisitor implements NodeVisitor { + + Source source = null; + + public boolean visit(Node node) { + final SourceSection sourceSection = node.getSourceSection(); + if (sourceSection != null) { + source = sourceSection.getSource(); + return false; + } + return true; + } + } + + Instrumenter(Object vm) { + this.vm = vm; + } + + /** + * Returns {@code true} if the AST node can be "instrumented" by {@linkplain #probe(Node) + * Probing}. + * <p> + * <b>Note:</b> instrumentation requires a appropriate {@linkplain #createWrapperNode(Node) + * WrapperNode}. + */ + public boolean isInstrumentable(Node node) { + return ACCESSOR.isInstrumentable(vm, node); + } + + /** + * For AST nodes that are {@linkplain #isInstrumentable() instrumentable}, returns a + * <em>wrapper node</em> that: + * <ol> + * <li>implements {@link WrapperNode}</li> + * <li>has the node as its single child, and</li> + * <li>whose type is safe for replacement of the node in the parent.</li> + * </ol> + * + * @return an appropriately typed {@link WrapperNode} + */ + public WrapperNode createWrapperNode(Node node) { + return ACCESSOR.createWrapperNode(vm, node); + } + + /** + * Prepares an AST node for {@linkplain Instrument instrumentation}, where the node is presumed + * to be part of a well-formed Truffle AST that has not yet been executed. + * <p> + * <em>Probing</em> a node is idempotent: + * <ul> + * <li>If the node has not been Probed, modifies the AST by first inserting a + * {@linkplain #createWrapperNode(Node) wrapper node} between the node and its parent and then + * returning the newly created Probe associated with the wrapper.</li> + * <li>If the node has been Probed, returns the Probe associated with its existing wrapper.</li> + * <li>No more than one {@link Probe} may be associated with a node, so a wrapper may not wrap + * another wrapper.</li> + * </ul> + * It is a runtime error to attempt Probing an AST node with no parent. + * + * @return a (possibly newly created) {@link Probe} associated with this node. + * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged + */ + @SuppressWarnings("rawtypes") + public Probe probe(Node node) { + + final Node parent = node.getParent(); + + if (node instanceof WrapperNode) { + throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, node, null); + } + + if (parent == null) { + throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, node, null); + } + + if (parent instanceof WrapperNode) { + final WrapperNode wrapper = (WrapperNode) parent; + if (TRACE) { + final Probe probe = wrapper.getProbe(); + final SourceSection sourceSection = wrapper.getChild().getSourceSection(); + final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription(); + trace("PROBE FOUND %s %s %s", "Probe@", location, probe.getTagsDescription()); + } + return wrapper.getProbe(); + } + + if (!isInstrumentable(node)) { + throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, node, null); + } + + // Create a new wrapper/probe with this node as its child. + final WrapperNode wrapper = createWrapperNode(node); + + if (wrapper == null || !(wrapper instanceof Node)) { + throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, node, wrapper); + } + + final Node wrapperNode = (Node) wrapper; + + if (!node.isSafelyReplaceableBy(wrapperNode)) { + throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, node, wrapper); + } + + final SourceSection sourceSection = wrapper.getChild().getSourceSection(); + final ProbeNode probeNode = new ProbeNode(); + Class<? extends TruffleLanguage> l = ACCESSOR.findLanguage(wrapper.getChild().getRootNode()); + final Probe probe = new Probe(this, l, probeNode, sourceSection); + probes.add(new WeakReference<>(probe)); + probeNode.probe = probe; // package private access + wrapper.insertEventHandlerNode(probeNode); + node.replace(wrapperNode); + if (TRACE) { + final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription(); + trace("PROBED %s %s %s", "Probe@", location, probe.getTagsDescription()); + } + for (ProbeListener listener : probeListeners) { + listener.newProbeInserted(probe); + } + return probe; + } + + /** + * Adds a {@link ProbeListener} to receive events. + */ + public void addProbeListener(ProbeListener listener) { + assert listener != null; + probeListeners.add(listener); + } + + /** + * Removes a {@link ProbeListener}. Ignored if listener not found. + */ + public void removeProbeListener(ProbeListener listener) { + probeListeners.remove(listener); + } + + /** + * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of + * probes if the specified tag is {@code null}. + * + * @return A collection of probes containing the given tag. + */ + public Collection<Probe> findProbesTaggedAs(SyntaxTag tag) { + final List<Probe> taggedProbes = new ArrayList<>(); + for (WeakReference<Probe> ref : probes) { + Probe probe = ref.get(); + if (probe != null) { + if (tag == null || probe.isTaggedAs(tag)) { + taggedProbes.add(ref.get()); + } + } + } + return taggedProbes; + } + + // TODO (mlvdv) generalize to permit multiple "before traps" without a performance hit? + /** + * Sets the current "<em>before</em> tag trap"; there can be no more than one in effect. + * <ul> + * <li>The before-trap triggers a callback just <strong><em>before</em></strong> execution + * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) + * with the specified {@link SyntaxTag}.</li> + * <li>Setting the before-trap to {@code null} clears an existing before-trap.</li> + * <li>Setting a non{@code -null} before-trap when one is already set clears the previously set + * before-trap.</li> + * </ul> + * + * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set. + */ + public void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) { + beforeTagTrap = newBeforeTagTrap; + for (WeakReference<Probe> ref : probes) { + final Probe probe = ref.get(); + if (probe != null) { + probe.notifyTrapsChanged(); + } + } + } + + // TODO (mlvdv) generalize to permit multiple "after traps" without a performance hit? + /** + * Sets the current "<em>after</em> tag trap"; there can be no more than one in effect. + * <ul> + * <li>The after-trap triggers a callback just <strong><em>after</em></strong> execution leaves + * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with + * the specified {@link SyntaxTag}.</li> + * <li>Setting the after-trap to {@code null} clears an existing after-trap.</li> + * <li>Setting a non{@code -null} after-trap when one is already set clears the previously set + * after-trap.</li> + * </ul> + * + * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set. + */ + public void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) { + afterTagTrap = newAfterTagTrap; + for (WeakReference<Probe> ref : probes) { + final Probe probe = ref.get(); + if (probe != null) { + probe.notifyTrapsChanged(); + } + } + } + + /** + * Enables instrumentation at selected nodes in all subsequently constructed ASTs. Ignored if + * the argument is already registered, runtime error if argument is {@code null}. + */ + public void registerASTProber(ASTProber prober) { + if (prober == null) { + throw new IllegalArgumentException("Register non-null ASTProbers"); + } + astProbers.add(prober); + } + + public void unregisterASTProber(ASTProber prober) { + astProbers.remove(prober); + } + + /** + * <em>Attaches</em> a {@link SimpleInstrumentListener listener} to a {@link Probe}, creating a + * <em>binding</em> called an {@link Instrument}. Until the Instrument is + * {@linkplain Instrument#dispose() disposed}, it routes synchronous notification of + * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to + * the listener. + * + * @param probe source of execution events + * @param listener receiver of execution events + * @param instrumentInfo optional documentation about the Instrument + * @return a handle for access to the binding + */ + @SuppressWarnings("static-method") + public Instrument attach(Probe probe, SimpleInstrumentListener listener, String instrumentInfo) { + final Instrument instrument = new Instrument.SimpleInstrument(listener, instrumentInfo); + probe.attach(instrument); + return instrument; + } + + /** + * <em>Attaches</em> a {@link StandardInstrumentListener listener} to a {@link Probe}, creating + * a <em>binding</em> called an {@link Instrument}. Until the Instrument is + * {@linkplain Instrument#dispose() disposed}, it routes synchronous notification of + * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to + * the listener. + * + * @param probe source of execution events + * @param listener receiver of execution events + * @param instrumentInfo optional documentation about the Instrument + * @return a handle for access to the binding + */ + @SuppressWarnings("static-method") + public Instrument attach(Probe probe, StandardInstrumentListener listener, String instrumentInfo) { + final Instrument instrument = new Instrument.StandardInstrument(listener, instrumentInfo); + probe.attach(instrument); + return instrument; + } + + /** + * <em>Attaches</em> a {@link AdvancedInstrumentResultListener listener} to a {@link Probe}, + * creating a <em>binding</em> called an {@link Instrument}. Until the Instrument is + * {@linkplain Instrument#dispose() disposed}, it routes synchronous notification of + * {@linkplain EventHandlerNode execution events} taking place at the Probe's AST location to + * the listener. + * <p> + * This Instrument executes efficiently, subject to full Truffle optimization, a client-provided + * AST fragment every time the Probed node is entered. + * <p> + * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework + * and reported to the listener; there is no other notification. + * + * @param probe probe source of execution events + * @param listener optional client callback for results/failure notification + * @param rootFactory provider of AST fragments on behalf of the client + * @param requiredResultType optional requirement, any non-assignable result is reported to the + * the listener, if any, as a failure + * @param instrumentInfo instrumentInfo optional documentation about the Instrument + * @return a handle for access to the binding + */ + @SuppressWarnings("static-method") + public Instrument attach(Probe probe, AdvancedInstrumentResultListener listener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) { + final Instrument instrument = new Instrument.AdvancedInstrument(listener, rootFactory, requiredResultType, instrumentInfo); + probe.attach(instrument); + return instrument; + } + + @SuppressWarnings("unused") + void executionStarted(Source s) { + } + + void executionEnded() { + } + + void tagAdded(Probe probe, SyntaxTag tag, Object tagValue) { + for (ProbeListener listener : probeListeners) { + listener.probeTaggedAs(probe, tag, tagValue); + } + } + + SyntaxTagTrap getBeforeTagTrap() { + return beforeTagTrap; + } + + SyntaxTagTrap getAfterTagTrap() { + return afterTagTrap; + } + + // TODO (mlvdv) build this in as a VM event? + /** + * Enables instrumentation in a newly created AST by applying all registered instances of + * {@link ASTProber}. + */ + private void applyInstrumentation(Node node) { + + String name = "<?>"; + final Source source = findSource(node); + if (source != null) { + name = source.getShortName(); + } else { + final SourceSection sourceSection = node.getEncapsulatingSourceSection(); + if (sourceSection != null) { + name = sourceSection.getShortDescription(); + } + } + trace("START %s", name); + for (ProbeListener listener : probeListeners) { + listener.startASTProbing(source); + } + for (ASTProber prober : astProbers) { + prober.probeAST(this, node); // TODO (mlvdv) + } + for (ProbeListener listener : probeListeners) { + listener.endASTProbing(source); + } + trace("FINISHED %s", name); + } + + static final class AccessorInstrument extends Accessor { + + @Override + protected Instrumenter createInstrumenter(Object vm) { + return new Instrumenter(vm); + } + + @Override + protected boolean isInstrumentable(Object vm, Node node) { + return super.isInstrumentable(vm, node); + } + + @Override + public WrapperNode createWrapperNode(Object vm, Node node) { + return super.createWrapperNode(vm, node); + } + + @SuppressWarnings("rawtypes") + @Override + protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { + return super.findLanguage(n); + } + + @SuppressWarnings("rawtypes") + @Override + protected Class<? extends TruffleLanguage> findLanguage(Probe probe) { + return probe.getLanguage(); + } + + @Override + protected void applyInstrumentation(Node node) { + super.getInstrumenter(null).applyInstrumentation(node); + } + } + + static final AccessorInstrument ACCESSOR = new AccessorInstrument(); + +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,18 +24,6 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.Assumption; -import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.impl.Accessor; -import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; -import com.oracle.truffle.api.nodes.InvalidAssumptionException; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.NodeVisitor; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.api.utilities.CyclicAssumption; import java.io.PrintStream; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -43,65 +31,32 @@ import java.util.Collections; import java.util.List; -//TODO (mlvdv) these statics should not be global. Move them to some kind of context. +import com.oracle.truffle.api.Assumption; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.nodes.InvalidAssumptionException; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.utilities.CyclicAssumption; /** * A <em>binding</em> between: * <ol> - * <li>A program location in an executing Truffle AST (corresponding to a {@link SourceSection}), - * and</li> - * <li>A dynamically managed collection of "attached" {@linkplain Instrument Instruments} that - * receive event notifications on behalf of external clients.</li> + * <li>A <em>guest language program location</em> in an executing Truffle AST (corresponding to a + * {@link SourceSection}), and</li> + * <li>A dynamically managed collection of <em>attached</em> {@linkplain Instrument Instruments} + * that receive event notifications from the Probe's AST location on behalf of external clients.</li> * </ol> + * <strong>Note</strong>:The relationship must be with an AST <em>location</em>, not a specific + * {@link Node}, because ASTs are routinely <em>cloned</em> at runtime. An AST <em>location</em> is + * best represented as the {@link SourceSection} from which the original AST Node was created. * <p> * Client-oriented documentation for the use of Probes is available online at <a * HREF="https://wiki.openjdk.java.net/display/Graal/Finding+Probes" >https://wiki.openjdk.java. * net/display/Graal/Finding+Probes</a> * <p> - * <h4>Implementation notes:</h4> - * <p> - * <ul> - * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a - * particular {@link SourceSection}, even though: - * <ul> - * <li>that location is represented in an AST as a {@link Node}, which might be replaced through - * optimizations such as specialization, and</li> - * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by multiple - * Nodes in multiple ASTs.</li> - * </ul> - * </li> * - * <li>The effect of the binding is to intercept {@linkplain TruffleEvents execution events} - * arriving at the "probed" AST Node and notify each attached {@link Instrument} before execution is - * allowed to proceed to the child and again after execution completes.</li> - * - * <li>The method {@link Node#probe()} creates a Probe on an AST Node; redundant calls return the - * same Probe.</li> - * - * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers - * correctly assigned), but before any cloning or executions. This is done by creating an instance - * of {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}. Once - * registered, it will be applied automatically to every newly created AST.</li> - * - * <li>The "probing" of an AST Node is implemented by insertion of a {@link ProbeNode.WrapperNode} - * into the AST (as new parent of the Node being probed), together with an associated - * {@link ProbeNode} that routes execution events at the probed Node to all the - * {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li> - * - * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well, - * together with their attached instrument chains. Each Probe instance intercepts cloning events and - * keeps track of all AST copies.</li> - * - * <li>All attached {@link InstrumentationNode}s effectively become part of the running program: - * <ul> - * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle optimization.</li> - * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with any - * Truffle optimizations.</li> - * </ul> - * </li> - * - * </ul> - * + * @see Instrumenter * @see Instrument * @see ASTProber * @see ProbeListener @@ -121,173 +76,7 @@ } } - private static final List<ASTProber> astProbers = new ArrayList<>(); - - private static final List<ProbeListener> probeListeners = new ArrayList<>(); - - /** - * All Probes that have been created. - */ - private static final List<WeakReference<Probe>> probes = new ArrayList<>(); - - /** - * A global trap that triggers notification just before executing any Node that is Probed with a - * matching tag. - */ - @CompilationFinal private static SyntaxTagTrap beforeTagTrap = null; - - /** - * A global trap that triggers notification just after executing any Node that is Probed with a - * matching tag. - */ - @CompilationFinal private static SyntaxTagTrap afterTagTrap = null; - - private static final class FindSourceVisitor implements NodeVisitor { - - Source source = null; - - public boolean visit(Node node) { - final SourceSection sourceSection = node.getSourceSection(); - if (sourceSection != null) { - source = sourceSection.getSource(); - return false; - } - return true; - } - } - - /** - * Walks an AST, looking for the first node with an assigned {@link SourceSection} and returning - * the {@link Source}. - */ - private static Source findSource(Node node) { - final FindSourceVisitor visitor = new FindSourceVisitor(); - node.accept(visitor); - return visitor.source; - } - - /** - * Enables instrumentation at selected nodes in all subsequently constructed ASTs. - */ - public static void registerASTProber(ASTProber prober) { - astProbers.add(prober); - } - - public static void unregisterASTProber(ASTProber prober) { - astProbers.remove(prober); - } - - /** - * Enables instrumentation in a newly created AST by applying all registered instances of - * {@link ASTProber}. - */ - public static void applyASTProbers(Node node) { - - String name = "<?>"; - final Source source = findSource(node); - if (source != null) { - name = source.getShortName(); - } else { - final SourceSection sourceSection = node.getEncapsulatingSourceSection(); - if (sourceSection != null) { - name = sourceSection.getShortDescription(); - } - } - trace("START %s", name); - for (ProbeListener listener : probeListeners) { - listener.startASTProbing(source); - } - for (ASTProber prober : astProbers) { - prober.probeAST(node); - } - for (ProbeListener listener : probeListeners) { - listener.endASTProbing(source); - } - trace("FINISHED %s", name); - } - - /** - * Adds a {@link ProbeListener} to receive events. - */ - public static void addProbeListener(ProbeListener listener) { - assert listener != null; - probeListeners.add(listener); - } - - /** - * Removes a {@link ProbeListener}. Ignored if listener not found. - */ - public static void removeProbeListener(ProbeListener listener) { - probeListeners.remove(listener); - } - - /** - * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of - * probes if the specified tag is {@code null}. - * - * @return A collection of probes containing the given tag. - */ - public static Collection<Probe> findProbesTaggedAs(SyntaxTag tag) { - final List<Probe> taggedProbes = new ArrayList<>(); - for (WeakReference<Probe> ref : probes) { - Probe probe = ref.get(); - if (probe != null) { - if (tag == null || probe.isTaggedAs(tag)) { - taggedProbes.add(ref.get()); - } - } - } - return taggedProbes; - } - - // TODO (mlvdv) generalize to permit multiple "before traps" without a performance hit? - /** - * Sets the current "<em>before</em> tag trap"; there can be no more than one in effect. - * <ul> - * <li>The before-trap triggers a callback just <strong><em>before</em></strong> execution - * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) - * with the specified {@link SyntaxTag}.</li> - * <li>Setting the before-trap to {@code null} clears an existing before-trap.</li> - * <li>Setting a non{@code -null} before-trap when one is already set clears the previously set - * before-trap.</li> - * </ul> - * - * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set. - */ - public static void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) { - beforeTagTrap = newBeforeTagTrap; - for (WeakReference<Probe> ref : probes) { - final Probe probe = ref.get(); - if (probe != null) { - probe.notifyTrapsChanged(); - } - } - } - - // TODO (mlvdv) generalize to permit multiple "after traps" without a performance hit? - /** - * Sets the current "<em>after</em> tag trap"; there can be no more than one in effect. - * <ul> - * <li>The after-trap triggers a callback just <strong><em>after</em></strong> execution leaves - * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with - * the specified {@link SyntaxTag}.</li> - * <li>Setting the after-trap to {@code null} clears an existing after-trap.</li> - * <li>Setting a non{@code -null} after-trap when one is already set clears the previously set - * after-trap.</li> - * </ul> - * - * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set. - */ - public static void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) { - afterTagTrap = newAfterTagTrap; - for (WeakReference<Probe> ref : probes) { - final Probe probe = ref.get(); - if (probe != null) { - probe.notifyTrapsChanged(); - } - } - } - + private final Instrumenter instrumenter; private final SourceSection sourceSection; private final ArrayList<SyntaxTag> tags = new ArrayList<>(); private final List<WeakReference<ProbeNode>> probeNodeClones = new ArrayList<>(); @@ -311,39 +100,60 @@ @CompilationFinal private boolean isAfterTrapActive = false; /** - * Intended for use only by {@link ProbeNode}. + * Constructor for use only by {@link ProbeNode}. + * <p> + * <h4>Probe Implementation notes:</h4> + * <p> + * <ul> + * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a + * particular {@link SourceSection}, even though: + * <ul> + * <li>that location is represented in an AST as a {@link Node}, which might be replaced through + * optimizations such as specialization, and</li> + * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by + * multiple Nodes in multiple ASTs.</li> + * </ul> + * </li> + * <li>The effect of the binding is to intercept {@linkplain EventHandlerNode execution events} + * arriving at the "probed" AST Node and notify each attached {@link Instrument} before + * execution is allowed to proceed to the child and again after execution completes.</li> + * + * <li>The method {@link Instrumenter#probe(Node)} creates a Probe on an AST Node; redundant + * calls return the same Probe.</li> + * + * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent + * pointers correctly assigned), but before any cloning or executions. This is done by applying + * instances of {@link ASTProber} provided by each language implementation, combined with any + * instances registered by tools via {@link Instrumenter#registerASTProber(ASTProber)}. Once + * registered, these will be applied automatically to every newly created AST.</li> + * + * <li>The "probing" of an AST Node is implemented by insertion of a + * {@link ProbeNode.WrapperNode} into the AST (as new parent of the Node being probed), together + * with an associated {@link ProbeNode} that routes execution events at the probed Node to all + * the {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li> + * + * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well, + * together with their attached instrument chains. Each Probe instance intercepts cloning events + * and keeps track of all AST copies.</li> + * + * <li>All attached {@link InstrumentationNode}s effectively become part of the running program: + * <ul> + * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle + * optimization.</li> + * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with + * any Truffle optimizations.</li> + * </ul> + * </li> + * </ul> */ - Probe(Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) { + Probe(Instrumenter instrumenter, Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) { + this.instrumenter = instrumenter; this.sourceSection = sourceSection; - probes.add(new WeakReference<>(this)); registerProbeNodeClone(probeNode); - if (TRACE) { - final String location = this.sourceSection == null ? "<unknown>" : sourceSection.getShortDescription(); - trace("ADDED %s %s %s", "Probe@", location, getTagsDescription()); - } - for (ProbeListener listener : probeListeners) { - listener.newProbeInserted(this); - } this.language = l; } /** - * Is this node tagged as belonging to a particular human-sensible category of language - * constructs? - */ - public boolean isTaggedAs(SyntaxTag tag) { - assert tag != null; - return tags.contains(tag); - } - - /** - * In which user-sensible categories has this node been tagged (<em>empty set</em> if none). - */ - public Collection<SyntaxTag> getSyntaxTags() { - return Collections.unmodifiableCollection(tags); - } - - /** * Adds a {@linkplain SyntaxTag tag} to the set of tags associated with this {@link Probe}; * {@code no-op} if already in the set. */ @@ -351,16 +161,16 @@ assert tag != null; if (!tags.contains(tag)) { tags.add(tag); - for (ProbeListener listener : probeListeners) { - listener.probeTaggedAs(this, tag, tagValue); - } + instrumenter.tagAdded(this, tag, tagValue); // Update the status of this Probe with respect to global tag traps boolean tagTrapsChanged = false; + final SyntaxTagTrap beforeTagTrap = instrumenter.getBeforeTagTrap(); if (beforeTagTrap != null && tag == beforeTagTrap.getTag()) { this.isBeforeTrapActive = true; tagTrapsChanged = true; } + final SyntaxTagTrap afterTagTrap = instrumenter.getAfterTagTrap(); if (afterTagTrap != null && tag == afterTagTrap.getTag()) { this.isAfterTrapActive = true; tagTrapsChanged = true; @@ -375,12 +185,29 @@ } /** + * Is the <em>Probed node</em> tagged as belonging to a particular human-sensible category of + * language constructs? + */ + public boolean isTaggedAs(SyntaxTag tag) { + assert tag != null; + return tags.contains(tag); + } + + /** + * In which user-sensible categories has the <em>Probed node</em> been tagged ( + * <em>empty set</em> if none). + */ + public Collection<SyntaxTag> getSyntaxTags() { + return Collections.unmodifiableCollection(tags); + } + + /** * Adds instrumentation at this Probe. * * @param instrument an instrument not yet attached to a probe * @throws IllegalStateException if the instrument has ever been attached before */ - public void attach(Instrument instrument) throws IllegalStateException { + void attach(Instrument instrument) throws IllegalStateException { if (instrument.isDisposed()) { throw new IllegalStateException("Attempt to attach disposed instrument"); } @@ -398,8 +225,8 @@ } /** - * Gets the {@link SourceSection} associated with the Guest Language AST node being - * instrumented, possibly {@code null}. + * Gets the {@link SourceSection} associated with the <en>Probed AST node</em>, possibly + * {@code null}. */ public SourceSection getProbedSourceSection() { return sourceSection; @@ -439,23 +266,27 @@ /** * Gets the currently active <strong><em>before</em></strong> {@linkplain SyntaxTagTrap Tag * Trap} at this Probe. Non{@code -null} if the global - * {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Before Tag Trap} is set and if this Probe - * holds the {@link SyntaxTag} specified in the trap. + * {@linkplain Instrumenter#setBeforeTagTrap(SyntaxTagTrap) Before Tag Trap} is set and if this + * Probe holds the {@link SyntaxTag} specified in the trap. */ SyntaxTagTrap getBeforeTrap() { checkProbeUnchanged(); - return isBeforeTrapActive ? beforeTagTrap : null; + return isBeforeTrapActive ? instrumenter.getBeforeTagTrap() : null; } /** * Gets the currently active <strong><em>after</em></strong> {@linkplain SyntaxTagTrap Tag Trap} * at this Probe. Non{@code -null} if the global - * {@linkplain Probe#setAfterTagTrap(SyntaxTagTrap) After Tag Trap} is set and if this Probe - * holds the {@link SyntaxTag} specified in the trap. + * {@linkplain Instrumenter#setAfterTagTrap(SyntaxTagTrap) After Tag Trap} is set and if this + * Probe holds the {@link SyntaxTag} specified in the trap. */ SyntaxTagTrap getAfterTrap() { checkProbeUnchanged(); - return isAfterTrapActive ? afterTagTrap : null; + return isAfterTrapActive ? instrumenter.getAfterTagTrap() : null; + } + + Class<? extends TruffleLanguage> getLanguage() { + return language; } /** @@ -476,13 +307,15 @@ probeStateUnchangedCyclic.invalidate(); } - private void notifyTrapsChanged() { + void notifyTrapsChanged() { + final SyntaxTagTrap beforeTagTrap = instrumenter.getBeforeTagTrap(); this.isBeforeTrapActive = beforeTagTrap != null && this.isTaggedAs(beforeTagTrap.getTag()); + final SyntaxTagTrap afterTagTrap = instrumenter.getAfterTagTrap(); this.isAfterTrapActive = afterTagTrap != null && this.isTaggedAs(afterTagTrap.getTag()); invalidateProbeUnchanged(); } - private String getTagsDescription() { + String getTagsDescription() { final StringBuilder sb = new StringBuilder(); sb.append("["); String prefix = ""; @@ -494,18 +327,4 @@ sb.append("]"); return sb.toString(); } - - static final class AccessorInstrument extends Accessor { - @Override - protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { - return super.findLanguage(n); - } - - @Override - protected Class<? extends TruffleLanguage> findLanguage(Probe probe) { - return probe.language; - } - } - - static final AccessorInstrument ACCESSOR = new AccessorInstrument(); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java Mon Sep 21 12:15:38 2015 -0700 @@ -28,7 +28,8 @@ import com.oracle.truffle.api.nodes.Node; /** - * An exception thrown when {@link Node#probe()} fails because of an implementation failure. + * An exception thrown when {@link Instrumenter#probe(Node)} fails because of an implementation + * failure. * <p> * Language and tool implementations should ensure that clients of tools never see this exception. */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,7 +24,6 @@ */ package com.oracle.truffle.api.instrument; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeFieldAccessor; import com.oracle.truffle.api.nodes.NodeUtil; @@ -78,7 +77,7 @@ private final Object wrapper; /** - * Description of an internal failure of {@link Node#probe()}. + * Description of an internal failure of {@link Instrumenter#probe(Node)}. * * @param reason what caused the failure * @param parent the parent, if known, of the child being probed
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java Mon Sep 21 12:15:38 2015 -0700 @@ -41,10 +41,11 @@ void startASTProbing(Source source); /** - * Notifies that a {@link Probe} has been newly attached to an AST via {@link Node#probe()}. + * Notifies that a {@link Probe} has been newly attached to an AST via + * {@link Instrumenter#probe(Node)}. * <p> * There can be no more than one {@link Probe} at a node; this notification will only be - * delivered the first time {@linkplain Node#probe() probe()} is called at a particular AST + * delivered the first time {@linkplain Instrumenter#probe(Node)} is called at a particular AST * node. There will also be no notification when the AST to which the Probe is attached is * cloned. */
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -26,14 +26,11 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.Instrument.AbstractInstrumentNode; -import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; -import com.oracle.truffle.api.source.SourceSection; /** * Implementation class & interface for enabling the attachment of {@linkplain Probe Probes} to @@ -50,85 +47,7 @@ * clones consistent. */ @NodeInfo(cost = NodeCost.NONE) -public final class ProbeNode extends Node implements TruffleEvents, InstrumentationNode { - - /** - * A node that can be inserted into a Truffle AST, and which enables {@linkplain Instrument - * instrumentation} at a particular Guest Language (GL) node. Implementations must extend - * {@link Node} and should override {@link Node#isInstrumentable()} to return {@code false}. - * <p> - * The implementation must be GL-specific. A wrapper <em>decorates</em> a GL AST node (the - * wrapper's <em>child</em>) by acting as a transparent <em>proxy</em> with respect to the GL's - * execution semantics. - * <p> - * Instrumentation at the wrapped node is implemented by an instance of {@link ProbeNode} - * attached as a second child of the {@link WrapperNode}. - * <p> - * A wrapper is obliged to notify its attached {@link ProbeNode} when execution events occur at - * the wrapped AST node during program execution. - * <p> - * When a GL AST is cloned, the {@link WrapperNode}, its {@link ProbeNode} and any - * {@linkplain Instrument instrumentation} are also cloned; they are in effect part of the GL - * AST. An instance of {@link Probe} represents abstractly the instrumentation at a particular - * location in a GL AST; it tracks all the copies of the Wrapper and attached instrumentation, - * and acts as a single point of access for tools. - * <p> - * This interface is not intended to be visible as part of the API for tools (instrumentation - * clients). - * <p> - * Implementation guidelines: - * <ol> - * <li>Each GL implementation should include a WrapperNode implementation; usually only one is - * needed.</li> - * <li>The wrapper type should descend from the <em>GL-specific node class</em>.</li> - * <li>Must have a field: {@code @Child private <GL>Node child;}</li> - * <li>Must have a field: {@code @Child private ProbeNode probeNode;}</li> - * <li>The wrapper must act as a <em>proxy</em> for its child, which means implementing every - * possible <em>execute-</em> method that gets called on guest language AST node types by their - * parents, and passing along each call to its child.</li> - * <li>Method {@code Probe getProbe()} should be implemented as {@code probeNode.getProbe();} - * <li>Method {@code insertProbe(ProbeNode)} should be implemented as - * {@code this.probeNode=insert(newProbeNode);}</li> - * <li>Most importantly, Wrappers must be implemented so that Truffle optimization will reduce - * their runtime overhead to zero when there are no attached {@link Instrument}s.</li> - * </ol> - * <p> - * - * @see Instrument - */ - public interface WrapperNode extends InstrumentationNode { - - /** - * Gets the node being "wrapped", i.e. the AST node for which - * {@linkplain InstrumentationNode.TruffleEvents execution events} will be reported through - * the Instrumentation Framework. - */ - Node getChild(); - - /** - * Gets the {@link Probe} responsible for installing this wrapper. - */ - Probe getProbe(); - - /** - * Implementation support for completing a newly created wrapper node. - */ - void insertProbe(ProbeNode probeNode); - } - - /** - * Create a new {@link Probe} associated with, and attached to, a Guest Language specific - * instance of {@link WrapperNode}. - */ - @SuppressWarnings("rawtypes") - public static Probe insertProbe(WrapperNode wrapper) { - final SourceSection sourceSection = wrapper.getChild().getSourceSection(); - final ProbeNode probeNode = new ProbeNode(); // private constructor - Class<? extends TruffleLanguage> l = Probe.ACCESSOR.findLanguage(wrapper.getChild().getRootNode()); - probeNode.probe = new Probe(l, probeNode, sourceSection); // package private access - wrapper.insertProbe(probeNode); - return probeNode.probe; - } +final class ProbeNode extends EventHandlerNode { // Never changed once set. @CompilationFinal Probe probe = null; @@ -138,11 +57,6 @@ @Child protected AbstractInstrumentNode firstInstrumentNode; @Override - public boolean isInstrumentable() { - return false; - } - - @Override public Node copy() { Node node = super.copy(); probe.registerProbeNodeClone((ProbeNode) node); @@ -152,10 +66,12 @@ /** * @return the {@link Probe} permanently associated with this {@link ProbeNode}. */ + @Override public Probe getProbe() { return probe; } + @Override public void enter(Node node, VirtualFrame vFrame) { this.probe.checkProbeUnchanged(); final SyntaxTagTrap beforeTagTrap = probe.getBeforeTrap(); @@ -167,6 +83,7 @@ } } + @Override public void returnVoid(Node node, VirtualFrame vFrame) { this.probe.checkProbeUnchanged(); if (firstInstrumentNode != null) { @@ -178,6 +95,7 @@ } } + @Override public void returnValue(Node node, VirtualFrame vFrame, Object result) { this.probe.checkProbeUnchanged(); if (firstInstrumentNode != null) { @@ -189,6 +107,7 @@ } } + @Override public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) { this.probe.checkProbeUnchanged(); if (firstInstrumentNode != null) { @@ -200,6 +119,7 @@ } } + @Override public String instrumentationInfo() { return "Standard probe"; }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SimpleInstrumentListener.java Mon Sep 21 12:15:38 2015 -0700 @@ -27,7 +27,7 @@ import com.oracle.truffle.api.source.SourceSection; /** - * A receiver of Truffle execution events that can act on behalf of an external client. + * A receiver of Truffle AST execution events that can act on behalf of an external client. * <p> * The {@link Probe} instance provides access to the {@link SourceSection} associated with the * event, as well as any {@link SyntaxTag}s that have been applied at that program's location. @@ -39,6 +39,9 @@ * Clients are free, of course, to record additional information in the listener implementation that * carries additional information about the context and reason for the particular {@link Instrument} * that is to be created from the listener. + * <p> + * Notification is fully synchronous, so overrides have performance implications. Non-trivial + * methods should be coded with Truffle guidelines and cautions in mind. */ public interface SimpleInstrumentListener { @@ -47,7 +50,7 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void enter(Probe probe); + void onEnter(Probe probe); /** * Receive notification that a program location's {@code void}-valued execution has just @@ -55,7 +58,7 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnVoid(Probe probe); + void onReturnVoid(Probe probe); /** * Receive notification that a program location's execution has just completed and returned a @@ -63,12 +66,12 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnValue(Probe probe, Object result); + void onReturnValue(Probe probe, Object result); /** * Receive notification that a program location's execution has just thrown an exception. * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnExceptional(Probe probe, Exception exception); + void onReturnExceptional(Probe probe, Exception exception); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/StandardInstrumentListener.java Mon Sep 21 12:15:38 2015 -0700 @@ -29,10 +29,10 @@ import com.oracle.truffle.api.source.SourceSection; /** - * A receiver of Truffle execution events that can act on behalf of an external client. + * A receiver of Truffle AST execution events that can act on behalf of an external client. * <p> * The {@link Probe} argument provides access to the {@link SourceSection} associated with the - * event, as well as any {@link SyntaxTag}s that have been applied at that AST node. + * event, as well as any {@link SyntaxTag}s that have been applied at that program's location. * <p> * This listener is designed for clients that also require access to the AST execution state at the * time of the event. Clients that do not require access to the AST execution state should use the @@ -41,6 +41,9 @@ * Clients are free, of course, to record additional information in the listener implementation that * carries additional information about the context and reason for the particular {@link Instrument} * that is to be created from the listener. + * <p> + * Notification is fully synchronous, so overrides have performance implications. Non-trivial + * methods should be coded with Truffle guidelines and cautions in mind. */ public interface StandardInstrumentListener { @@ -49,14 +52,14 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void enter(Probe probe, Node node, VirtualFrame vFrame); + void onEnter(Probe probe, Node node, VirtualFrame vFrame); /** * Receive notification that an AST Node's {@code void}-valued execute method has just returned. * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnVoid(Probe probe, Node node, VirtualFrame vFrame); + void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame); /** * Receive notification that an AST Node's execute method has just returned a value (boxed if @@ -64,12 +67,12 @@ * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result); + void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result); /** * Receive notification that an AST Node's execute method has just thrown an exception. * <p> * <strong>Synchronous</strong>: Truffle execution waits until the call returns. */ - void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception); + void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/WrapperNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2013, 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. 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.TruffleLanguage; +import com.oracle.truffle.api.nodes.Node; + +/** + * A {@link Node} instance that must be inserted into a Truffle AST in order to enable + * {@linkplain Instrument instrumentation} at a particular Guest Language (GL) node. Implementations + * must ensure that {@link TruffleLanguage#isInstrumentable(Node)} returns {@code false}. + * <p> + * The implementation must be GL-specific. A wrapper <em>decorates</em> a GL AST node (the wrapper's + * <em>child</em>) by acting as a transparent <em>proxy</em> with respect to the GL's execution + * semantics. + * <p> + * Instrumentation at the wrapped node is implemented by an instance of {@link EventHandlerNode} + * attached as a second child of the {@link WrapperNode}. + * <p> + * A wrapper is obliged to notify its attached {@link EventHandlerNode} when execution events occur + * at the wrapped AST node during program execution. + * <p> + * When a GL AST is cloned, the {@link WrapperNode}, its {@link EventHandlerNode} and any + * {@linkplain Instrument instrumentation} are also cloned; they are in effect part of the GL AST. + * An instance of {@link Probe} represents abstractly the instrumentation at a particular location + * in a GL AST; it tracks all the copies of the Wrapper and attached instrumentation, and acts as a + * single point of access for tools. + * <p> + * Implementation guidelines: + * <ol> + * <li>Each GL implementation must implement a WrapperNode implementation for each AST context in + * which Instrumentation is to be supported.</li> + * <li>The wrapper type should descend from the <em>GL-specific node class</em>.</li> + * <li>Must have a field: {@code @Child private <GL>Node child;}</li> + * <li>Must have a field: {@code @Child private EventHandlerNode eventHandlerNode;}</li> + * <li>The wrapper must act as a <em>proxy</em> for its child, which means implementing every + * possible <em>execute-</em> method that gets called on guest language AST node types by their + * parents, and passing along each call to its child.</li> + * <li>Method {@code Probe getProbe()} should be implemented as {@code eventHandlerNode.getProbe();} + * <li>Method {@code insertProbe(EventHandlerNode)} should be implemented as + * {@code this.eventHandlerNode=insert(eventHandlerNode);}</li> + * <li>Most importantly, Wrappers must be implemented so that Truffle optimization will reduce their + * runtime overhead to zero when there are no attached {@link Instrument}s.</li> + * </ol> + * <p> + * + * @see Instrument + */ +public interface WrapperNode extends InstrumentationNode { + + /** + * Gets the node being "wrapped", i.e. the AST node for which + * {@linkplain InstrumentationNode.EventHandlerNode execution events} will be reported through + * the Instrumentation Framework. + */ + Node getChild(); + + /** + * Gets the {@link Probe} responsible for installing this wrapper. + */ + Probe getProbe(); + + /** + * Implementation support for completing a newly created wrapper node. + */ + void insertEventHandlerNode(EventHandlerNode eventHandlerNode); +}
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java Mon Sep 21 12:15:38 2015 -0700 @@ -26,7 +26,7 @@ import com.oracle.truffle.api.instrument.ASTPrinter; import com.oracle.truffle.api.instrument.InstrumentationNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeClass; import com.oracle.truffle.api.nodes.NodeFieldAccessor;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultSimpleInstrumentListener.java Mon Sep 21 12:15:38 2015 -0700 @@ -32,15 +32,15 @@ */ public class DefaultSimpleInstrumentListener implements SimpleInstrumentListener { - public void enter(Probe probe) { + public void onEnter(Probe probe) { } - public void returnVoid(Probe probe) { + public void onReturnVoid(Probe probe) { } - public void returnValue(Probe probe, Object result) { + public void onReturnValue(Probe probe, Object result) { } - public void returnExceptional(Probe probe, Exception exception) { + public void onReturnExceptional(Probe probe, Exception exception) { } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultStandardInstrumentListener.java Mon Sep 21 12:15:38 2015 -0700 @@ -35,16 +35,16 @@ */ public class DefaultStandardInstrumentListener implements StandardInstrumentListener { - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { } - public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) { + public void onReturnVoid(Probe probe, Node node, VirtualFrame vFrame) { } - public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { + public void onReturnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) { } - public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { + public void onReturnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) { } }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,6 +24,15 @@ */ package com.oracle.truffle.api.nodes; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.Callable; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -32,22 +41,9 @@ import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.impl.Accessor; -import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeException; -import com.oracle.truffle.api.instrument.ProbeFailure; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.utilities.JSONHelper; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.Callable; /** * Abstract base class for all Truffle nodes. @@ -420,86 +416,22 @@ } /** - * Any node for which this is {@code true} can be "instrumented" by installing a {@link Probe} - * that intercepts execution events at the node and routes them to any {@link Instrument}s that - * have been attached to the {@link Probe}. Only one {@link Probe} may be installed at each - * node; subsequent calls return the one already installed. - * <p> - * <b>Note:</b> instrumentation requires a appropriate {@link WrapperNode}, which must be - * provided by {@link #createWrapperNode()}. - * - * @see Instrument + * @see TruffleLanguage */ + @Deprecated public boolean isInstrumentable() { return false; } /** - * For any node that {@link #isInstrumentable()}, this method must return a {@link Node} that: - * <ol> - * <li>implements {@link WrapperNode}</li> - * <li>has {@code this} as it's child, and</li> - * <li>whose type is safe for replacement of {@code this} in the parent.</li> - * </ol> - * - * @return an appropriately typed {@link WrapperNode} if {@link #isInstrumentable()}. + * @see TruffleLanguage */ + @Deprecated public WrapperNode createWrapperNode() { return null; } /** - * Enables {@linkplain Instrument instrumentation} of a node, where the node is presumed to be - * part of a well-formed Truffle AST that is not being executed. If this node has not already - * been probed, modifies the AST by inserting a {@linkplain WrapperNode wrapper node} between - * the node and its parent; the wrapper node must be provided by implementations of - * {@link #createWrapperNode()}. No more than one {@link Probe} may be associated with a node, - * so a {@linkplain WrapperNode wrapper} may not wrap another {@linkplain WrapperNode wrapper}. - * - * @return a (possibly newly created) {@link Probe} associated with this node. - * @throws ProbeException (unchecked) when a probe cannot be created, leaving the AST unchanged - */ - public final Probe probe() { - - if (this instanceof WrapperNode) { - throw new ProbeException(ProbeFailure.Reason.WRAPPER_NODE, null, this, null); - } - - if (parent == null) { - throw new ProbeException(ProbeFailure.Reason.NO_PARENT, null, this, null); - } - - if (parent instanceof WrapperNode) { - return ((WrapperNode) parent).getProbe(); - } - - if (!isInstrumentable()) { - throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, this, null); - } - - // Create a new wrapper/probe with this node as its child. - final WrapperNode wrapper = createWrapperNode(); - - if (wrapper == null || !(wrapper instanceof Node)) { - throw new ProbeException(ProbeFailure.Reason.NO_WRAPPER, parent, this, wrapper); - } - - final Node wrapperNode = (Node) wrapper; - - if (!this.isSafelyReplaceableBy(wrapperNode)) { - throw new ProbeException(ProbeFailure.Reason.WRAPPER_TYPE, parent, this, wrapper); - } - - // Connect it to a Probe - final Probe probe = ProbeNode.insertProbe(wrapper); - - // Replace this node in the AST with the wrapper - this.replace(wrapperNode); - - return probe; - } - - /** * Converts this node to a textual representation useful for debugging. */ @Override @@ -576,6 +508,10 @@ return ""; } + protected void applyInstrumentation(Node node) { + ACCESSOR.applyInstrumentation(node); + } + private static final Object GIL = new Object(); private static final ThreadLocal<Integer> IN_ATOMIC_BLOCK = new ThreadLocal<Integer>() { @@ -605,8 +541,13 @@ protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { return n.language; } + + @Override + protected void applyInstrumentation(Node node) { + super.applyInstrumentation(node); + } } // registers into Accessor.NODES - @SuppressWarnings("unused") private static final AccessorNodes ACCESSOR = new AccessorNodes(); + private static final AccessorNodes ACCESSOR = new AccessorNodes(); }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Mon Sep 21 12:15:38 2015 -0700 @@ -27,7 +27,7 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.instrument.StandardSyntaxTag; import com.oracle.truffle.api.instrument.SyntaxTag; import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind;
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -36,7 +36,6 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.impl.DefaultCompilerOptions; import com.oracle.truffle.api.instrument.ASTProber; -import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.source.SourceSection; /** @@ -162,7 +161,7 @@ * stack) without prior knowledge of the language it has come from. * * Used for instance to determine the language of a <code>RootNode<code>: - * + * * <pre> * <code> * rootNode.getExecutionContext().getLanguageShortName(); @@ -188,9 +187,10 @@ } /** - * Apply all registered instances of {@link ASTProber} to the AST, if any, held by this root - * node. This can only be done once the AST is complete, notably once all parent pointers are - * correctly assigned. But it also must be done before any AST cloning or execution. + * Apply to the AST all instances of {@link ASTProber} specified for the language, if any, held + * by this root node. This can only be done once the AST is complete, notably once all parent + * pointers are correctly assigned. But it also must be done before any AST cloning or + * execution. * <p> * If this is not done, then the AST will not be subject to debugging or any other * instrumentation-supported tooling. @@ -198,7 +198,7 @@ * Implementations should ensure that instrumentation is never applied more than once to an AST, * as this is not guaranteed to be error-free. * - * @see Probe#registerASTProber(com.oracle.truffle.api.instrument.ASTProber) + * @see TruffleLanguage */ public void applyInstrumentation() { }
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,8 +24,6 @@ */ package com.oracle.truffle.api.source; -import com.oracle.truffle.api.CompilerAsserts; -import com.oracle.truffle.api.TruffleLanguage.Registration; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; @@ -54,6 +52,9 @@ import java.util.logging.Level; import java.util.logging.Logger; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.TruffleLanguage.Registration; + /** * Representation of a guest language source code unit and its contents. Sources originate in * several ways: @@ -425,7 +426,7 @@ /** * The URL if the source is retrieved via URL. - * + * * @return URL or <code>null</code> */ public abstract URL getURL(); @@ -909,6 +910,8 @@ return "text/x-c"; } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) { return "application/x-r"; + } else if (file.getName().endsWith(".js") || file.getName().endsWith(".JS")) { + return "application/javascript"; } else { try { return Files.probeContentType(file.toPath()); @@ -997,6 +1000,8 @@ return "text/x-c"; } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) { return "application/x-r"; + } else if (file.getName().endsWith(".js") || file.getName().endsWith(".JS")) { + return "application/javascript"; } else { try { return Files.probeContentType(file.toPath());
--- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Mon Sep 21 12:15:38 2015 -0700 @@ -40,22 +40,12 @@ */ package com.oracle.truffle.sl.test.instrument; -import com.oracle.truffle.api.instrument.ASTProber; -import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.StandardSyntaxTag; -import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.vm.TruffleVM; -import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber; -import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode; -import com.oracle.truffle.sl.test.SLTestRunner; -import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintStream; +import java.lang.reflect.Field; import java.nio.charset.Charset; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; @@ -65,6 +55,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; + import org.junit.Assert; import org.junit.internal.TextListener; import org.junit.runner.Description; @@ -77,6 +68,18 @@ import org.junit.runners.ParentRunner; import org.junit.runners.model.InitializationError; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.StandardSyntaxTag; +import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.TruffleVM; +import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber; +import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode; +import com.oracle.truffle.sl.test.SLTestRunner; +import com.oracle.truffle.sl.test.instrument.SLInstrumentTestRunner.InstrumentTestCase; + /** * This class builds and executes the tests for instrumenting SL. Although much of this class is * written with future automation in mind, at the moment the tests that are created are hard-coded @@ -94,7 +97,7 @@ private static final String LF = System.getProperty("line.separator"); - static class InstrumentTestCase { + static final class InstrumentTestCase { protected final Description name; protected final Path path; protected final String baseName; @@ -115,16 +118,12 @@ private final List<InstrumentTestCase> testCases; - public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError { + public SLInstrumentTestRunner(Class<?> testClass) throws InitializationError, SecurityException, IllegalArgumentException { super(testClass); - final SLStandardASTProber prober = new SLStandardASTProber(); - Probe.registerASTProber(prober); try { testCases = createTests(testClass); } catch (IOException e) { throw new InitializationError(e); - } finally { - Probe.unregisterASTProber(prober); } } @@ -236,20 +235,25 @@ ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(out); final ASTProber prober = new SLStandardASTProber(); - Probe.registerASTProber(prober); + try { // We use the name of the file to determine what visitor to attach to it. if (testCase.baseName.endsWith(ASSIGNMENT_VALUE_SUFFIX)) { // Set up the execution context for Simple and register our two listeners TruffleVM vm = TruffleVM.newVM().setIn(new ByteArrayInputStream(testCase.testInput.getBytes("UTF-8"))).setOut(out).build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + instrumenter.registerASTProber(prober); + final String src = readAllLines(testCase.path); vm.eval(Source.fromText(src, testCase.path.toString()).withMimeType("application/x-sl")); // Attach an instrument to every probe tagged as an assignment - for (Probe probe : Probe.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) { + for (Probe probe : instrumenter.findProbesTaggedAs(StandardSyntaxTag.ASSIGNMENT)) { SLPrintAssigmentValueListener slPrintAssigmentValueListener = new SLPrintAssigmentValueListener(ps); - probe.attach(Instrument.create(slPrintAssigmentValueListener, "SL print assignment value")); + instrumenter.attach(probe, slPrintAssigmentValueListener, "SL print assignment value"); } TruffleVM.Symbol main = vm.findGlobalSymbol("main"); @@ -263,13 +267,12 @@ } catch (Throwable ex) { notifier.fireTestFailure(new Failure(testCase.name, ex)); } finally { - Probe.unregisterASTProber(prober); notifier.fireTestFinished(testCase.name); } } - public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException { + public static void runInMain(Class<?> testClass, String[] args) throws InitializationError, NoTestsRemainException, SecurityException, IllegalArgumentException { JUnitCore core = new JUnitCore(); core.addListener(new TextListener(System.out)); SLInstrumentTestRunner suite = new SLInstrumentTestRunner(testClass); @@ -314,7 +317,7 @@ } @Override - public void returnValue(Probe probe, Object result) { + public void onReturnValue(Probe probe, Object result) { output.println(result); } }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Mon Sep 21 12:15:38 2015 -0700 @@ -40,11 +40,19 @@ */ package com.oracle.truffle.sl; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.math.BigInteger; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.debug.DebugSupportException; import com.oracle.truffle.api.debug.DebugSupportProvider; import com.oracle.truffle.api.dsl.NodeFactory; import com.oracle.truffle.api.dsl.UnsupportedSpecializationException; @@ -52,7 +60,7 @@ import com.oracle.truffle.api.instrument.ASTProber; import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; -import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.instrument.ToolSupportProvider; import com.oracle.truffle.api.instrument.Visualizer; import com.oracle.truffle.api.nodes.GraphPrintVisitor; @@ -68,7 +76,9 @@ import com.oracle.truffle.sl.builtins.SLNanoTimeBuiltin; import com.oracle.truffle.sl.builtins.SLPrintlnBuiltin; import com.oracle.truffle.sl.builtins.SLReadlnBuiltin; +import com.oracle.truffle.sl.nodes.SLExpressionNode; import com.oracle.truffle.sl.nodes.SLRootNode; +import com.oracle.truffle.sl.nodes.SLStatementNode; import com.oracle.truffle.sl.nodes.SLTypes; import com.oracle.truffle.sl.nodes.call.SLDispatchNode; import com.oracle.truffle.sl.nodes.call.SLInvokeNode; @@ -92,7 +102,9 @@ import com.oracle.truffle.sl.nodes.expression.SLStringLiteralNode; import com.oracle.truffle.sl.nodes.expression.SLSubNode; import com.oracle.truffle.sl.nodes.instrument.SLDefaultVisualizer; +import com.oracle.truffle.sl.nodes.instrument.SLExpressionWrapperNode; import com.oracle.truffle.sl.nodes.instrument.SLStandardASTProber; +import com.oracle.truffle.sl.nodes.instrument.SLStatementWrapperNode; import com.oracle.truffle.sl.nodes.local.SLReadLocalVariableNode; import com.oracle.truffle.sl.nodes.local.SLWriteLocalVariableNode; import com.oracle.truffle.sl.parser.Parser; @@ -102,14 +114,6 @@ import com.oracle.truffle.sl.runtime.SLFunction; import com.oracle.truffle.sl.runtime.SLFunctionRegistry; import com.oracle.truffle.sl.runtime.SLNull; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.math.BigInteger; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; /** * SL is a simple language to demonstrate and showcase features of Truffle. The implementation is as @@ -198,8 +202,7 @@ public final class SLLanguage extends TruffleLanguage<SLContext> { private static List<NodeFactory<? extends SLBuiltinNode>> builtins = Collections.emptyList(); private static Visualizer visualizer = new SLDefaultVisualizer(); - private static ASTProber registeredASTProber; // non-null if prober already registered - private DebugSupportProvider debugSupport; + private ASTProber astProber = new SLStandardASTProber(); private SLLanguage() { } @@ -256,6 +259,7 @@ /** * Temporary method during API evolution, supports debugger integration. */ + @Deprecated public static void run(Source source) throws IOException { TruffleVM vm = TruffleVM.newVM().build(); assert vm.getLanguages().containsKey("application/x-sl"); @@ -478,28 +482,72 @@ } @Override - protected ToolSupportProvider getToolSupport() { - return getDebugSupport(); + protected Visualizer getVisualizer() { + if (visualizer == null) { + visualizer = new SLDefaultVisualizer(); + } + return visualizer; + } + + @Override + protected boolean isInstrumentable(Node node) { + return node instanceof SLStatementNode; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + if (node instanceof SLExpressionNode) { + return new SLExpressionWrapperNode((SLExpressionNode) node); + } + if (node instanceof SLStatementNode) { + return new SLStatementWrapperNode((SLStatementNode) node); + } + return null; } @Override + protected ASTProber getDefaultASTProber() { + return astProber; + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber prober) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + throw new IllegalStateException("evalInContext not supported in this language"); + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + throw new IllegalStateException("createAdvancedInstrumentRootFactory not supported in this language"); + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + return null; + } + + @SuppressWarnings("deprecation") + @Override protected DebugSupportProvider getDebugSupport() { - if (debugSupport == null) { - debugSupport = new SLDebugProvider(); - } - return debugSupport; + return null; + } + + public Node createFindContextNode0() { + return createFindContextNode(); + } + + public SLContext findContext0(Node contextNode) { + return findContext(contextNode); } // TODO (mlvdv) remove the static hack when we no longer have the static demo variables private static void setupToolDemos() { - // if (statementCounts || coverage) { - // if (registeredASTProber == null) { - // final ASTProber newProber = new SLStandardASTProber(); - // // This should be registered on the TruffleVM - // Probe.registerASTProber(newProber); - // registeredASTProber = newProber; - // } - // } // if (nodeExecCounts) { // nodeExecCounter = new NodeExecCounter(); // nodeExecCounter.install(); @@ -531,46 +579,4 @@ // } } - public Node createFindContextNode0() { - return createFindContextNode(); - } - - public SLContext findContext0(Node contextNode) { - return findContext(contextNode); - } - - private final class SLDebugProvider implements DebugSupportProvider { - - public SLDebugProvider() { - if (registeredASTProber == null) { - registeredASTProber = new SLStandardASTProber(); - // This should be registered on the TruffleVM - Probe.registerASTProber(registeredASTProber); - } - } - - public Visualizer getVisualizer() { - if (visualizer == null) { - visualizer = new SLDefaultVisualizer(); - } - return visualizer; - } - - public void enableASTProbing(ASTProber prober) { - if (prober != null) { - // This should be registered on the TruffleVM - Probe.registerASTProber(prober); - } - } - - public Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws DebugSupportException { - throw new DebugSupportException("evalInContext not supported in this language"); - } - - public AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws DebugSupportException { - throw new DebugSupportException("createAdvancedInstrumentRootFactory not supported in this language"); - } - - } - }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -42,11 +42,9 @@ import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.UnexpectedResultException; import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.sl.nodes.instrument.SLExpressionWrapperNode; import com.oracle.truffle.sl.runtime.SLFunction; /** @@ -94,15 +92,4 @@ public boolean executeBoolean(VirtualFrame frame) throws UnexpectedResultException { return SLTypesGen.expectBoolean(executeGeneric(frame)); } - - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new SLExpressionWrapperNode(this); - } - }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -43,7 +43,6 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.sl.SLLanguage; @@ -100,7 +99,7 @@ @Override public void applyInstrumentation() { - Probe.applyASTProbers(bodyNode); + super.applyInstrumentation(bodyNode); } @Override
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -40,13 +40,12 @@ */ package com.oracle.truffle.sl.nodes; +import java.io.File; + import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.sl.nodes.instrument.SLStatementWrapperNode; -import java.io.File; /** * The base class of all Truffle nodes for SL. All nodes (even expressions) can be used as @@ -101,15 +100,4 @@ return String.format("%s:%d%s", sourceName, startLine, estimated ? "~" : ""); } } - - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new SLStatementWrapperNode(this); - } - }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLExpressionWrapperNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -41,11 +41,11 @@ package com.oracle.truffle.sl.nodes.instrument; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrument.EventHandlerNode; import com.oracle.truffle.api.instrument.Instrument; import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; @@ -63,7 +63,7 @@ @NodeInfo(cost = NodeCost.NONE) public final class SLExpressionWrapperNode extends SLExpressionNode implements WrapperNode { @Child private SLExpressionNode child; - @Child private ProbeNode probeNode; + @Child private EventHandlerNode eventHandlerNode; /** * Constructor. @@ -81,21 +81,16 @@ } @Override - public boolean isInstrumentable() { - return false; - } - - @Override public SLExpressionNode getNonWrapperNode() { return child; } - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; } public Probe getProbe() { - return probeNode.getProbe(); + return eventHandlerNode.getProbe(); } public Node getChild() { @@ -105,16 +100,16 @@ @Override public Object executeGeneric(VirtualFrame vFrame) { - probeNode.enter(child, vFrame); + eventHandlerNode.enter(child, vFrame); Object result; try { result = child.executeGeneric(vFrame); - probeNode.returnValue(child, vFrame, result); + eventHandlerNode.returnValue(child, vFrame, result); } catch (KillException e) { throw (e); } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); + eventHandlerNode.returnExceptional(child, vFrame, e); throw (e); } return result; @@ -132,14 +127,14 @@ @Override public SLFunction executeFunction(VirtualFrame vFrame) throws UnexpectedResultException { - probeNode.enter(child, vFrame); + eventHandlerNode.enter(child, vFrame); SLFunction result; try { result = child.executeFunction(vFrame); - probeNode.returnValue(child, vFrame, result); + eventHandlerNode.returnValue(child, vFrame, result); } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); + eventHandlerNode.returnExceptional(child, vFrame, e); throw (e); } return result;
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java Mon Sep 21 12:15:38 2015 -0700 @@ -40,12 +40,14 @@ */ package com.oracle.truffle.sl.nodes.instrument; -import com.oracle.truffle.api.instrument.ASTProber; -import com.oracle.truffle.api.instrument.InstrumentationNode; -import com.oracle.truffle.api.instrument.Probe; import static com.oracle.truffle.api.instrument.StandardSyntaxTag.ASSIGNMENT; import static com.oracle.truffle.api.instrument.StandardSyntaxTag.START_LOOP; import static com.oracle.truffle.api.instrument.StandardSyntaxTag.STATEMENT; + +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.InstrumentationNode; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeVisitor; import com.oracle.truffle.sl.nodes.SLExpressionNode; @@ -57,39 +59,39 @@ * A visitor which traverses a completely parsed Simple AST (presumed not yet executed) and enables * instrumentation at a few standard kinds of nodes. */ -public class SLStandardASTProber implements NodeVisitor, ASTProber { +public class SLStandardASTProber implements ASTProber { + + public void probeAST(final Instrumenter instrumenter, Node startNode) { + startNode.accept(new NodeVisitor() { - /** - * {@inheritDoc} - * <p> - * Instruments and tags all relevant {@link SLStatementNode}s and {@link SLExpressionNode}s. - * Currently, only SLStatementNodes that are not SLExpressionNodes are tagged as statements. - */ - public boolean visit(Node node) { + /** + * Instruments and tags all relevant {@link SLStatementNode}s and + * {@link SLExpressionNode}s. Currently, only SLStatementNodes that are not + * SLExpressionNodes are tagged as statements. + */ + public boolean visit(Node node) { - if (!(node instanceof InstrumentationNode) && node instanceof SLStatementNode && node.getParent() != null && node.getSourceSection() != null) { - // All SL nodes are instrumentable, but treat expressions specially + if (!(node instanceof InstrumentationNode) && node instanceof SLStatementNode && node.getParent() != null && node.getSourceSection() != null) { + // All SL nodes are instrumentable, but treat expressions specially - if (node instanceof SLExpressionNode) { - SLExpressionNode expressionNode = (SLExpressionNode) node; - Probe probe = expressionNode.probe(); - if (node instanceof SLWriteLocalVariableNode) { - probe.tagAs(STATEMENT, null); - probe.tagAs(ASSIGNMENT, null); + if (node instanceof SLExpressionNode) { + SLExpressionNode expressionNode = (SLExpressionNode) node; + final Probe probe = instrumenter.probe(expressionNode); + if (node instanceof SLWriteLocalVariableNode) { + probe.tagAs(STATEMENT, null); + probe.tagAs(ASSIGNMENT, null); + } + } else { + SLStatementNode statementNode = (SLStatementNode) node; + final Probe probe = instrumenter.probe(statementNode); + probe.tagAs(STATEMENT, null); + if (node instanceof SLWhileNode) { + probe.tagAs(START_LOOP, null); + } + } } - } else { - SLStatementNode statementNode = (SLStatementNode) node; - Probe probe = statementNode.probe(); - probe.tagAs(STATEMENT, null); - if (node instanceof SLWhileNode) { - probe.tagAs(START_LOOP, null); - } + return true; } - } - return true; - } - - public void probeAST(Node node) { - node.accept(this); + }); } }
--- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStatementWrapperNode.java Mon Sep 21 12:15:38 2015 -0700 @@ -41,11 +41,11 @@ package com.oracle.truffle.sl.nodes.instrument; import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrument.EventHandlerNode; import com.oracle.truffle.api.instrument.Instrument; import com.oracle.truffle.api.instrument.KillException; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; +import com.oracle.truffle.api.instrument.WrapperNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.nodes.NodeInfo; @@ -61,7 +61,7 @@ public final class SLStatementWrapperNode extends SLStatementNode implements WrapperNode { @Child private SLStatementNode child; - @Child private ProbeNode probeNode; + @Child private EventHandlerNode eventHandlerNode; public SLStatementWrapperNode(SLStatementNode child) { super(child.getSourceSection()); @@ -74,21 +74,17 @@ } @Override - public boolean isInstrumentable() { - return false; - } - - @Override public SLStatementNode getNonWrapperNode() { return child; } - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; + @Override + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; } public Probe getProbe() { - return probeNode.getProbe(); + return eventHandlerNode.getProbe(); } @Override @@ -98,15 +94,15 @@ @Override public void executeVoid(VirtualFrame vFrame) { - probeNode.enter(child, vFrame); + eventHandlerNode.enter(child, vFrame); try { child.executeVoid(vFrame); - probeNode.returnVoid(child, vFrame); + eventHandlerNode.returnVoid(child, vFrame); } catch (KillException e) { throw (e); } catch (Exception e) { - probeNode.returnExceptional(child, vFrame, e); + eventHandlerNode.returnExceptional(child, vFrame, e); throw (e); } }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,25 +24,33 @@ */ package com.oracle.truffle.tools.test; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.StandardSyntaxTag; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Map; + +import org.junit.Test; + +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.TruffleVM; import com.oracle.truffle.tools.CoverageTracker; -import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import org.junit.Test; +import com.oracle.truffle.tools.test.ToolTestUtil.ToolTestTag; public class CoverageTrackerTest { @Test - public void testNoExecution() { + public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); final CoverageTracker tool = new CoverageTracker(); assertEquals(tool.getCounts().entrySet().size(), 0); - tool.install(); + tool.install(instrumenter); assertEquals(tool.getCounts().entrySet().size(), 0); tool.setEnabled(false); assertEquals(tool.getCounts().entrySet().size(), 0); @@ -54,66 +62,55 @@ assertEquals(tool.getCounts().entrySet().size(), 0); } - @Test - public void testToolCreatedTooLate() { - final RootNode expr13rootNode = createExpr13TestRootNode(); - final CoverageTracker tool = new CoverageTracker(); - tool.install(); - assertEquals(13, expr13rootNode.execute(null)); - assertTrue(tool.getCounts().isEmpty()); - tool.dispose(); - } - - @Test - public void testToolInstalledcTooLate() { - final CoverageTracker tool = new CoverageTracker(); - final RootNode expr13rootNode = createExpr13TestRootNode(); - tool.install(); - assertEquals(13, expr13rootNode.execute(null)); - assertTrue(tool.getCounts().isEmpty()); - tool.dispose(); + void checkCounts(Source source, CoverageTracker coverage, Long[] expectedCounts) { + final Map<Source, Long[]> countMap = coverage.getCounts(); + assertEquals(countMap.size(), 1); + final Long[] resultCounts = countMap.get(source); + assertTrue(Arrays.equals(resultCounts, expectedCounts)); } @Test - public void testCountingCoverage() { - final CoverageTracker tool = new CoverageTracker(); - tool.install(); - final RootNode expr13rootNode = createExpr13TestRootNode(); + public void testCountingCoverage() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = ToolTestUtil.createTestSource("testCountingCoverage"); - // Not probed yet. - assertEquals(13, expr13rootNode.execute(null)); - assertTrue(tool.getCounts().isEmpty()); + final CoverageTracker valueCoverage = new CoverageTracker(ToolTestTag.VALUE_TAG); + final CoverageTracker addCoverage = new CoverageTracker(ToolTestTag.ADD_TAG); - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); + valueCoverage.install(instrumenter); + assertTrue(valueCoverage.getCounts().isEmpty()); + + assertEquals(vm.eval(source).get(), 13); - // Probed but not tagged yet. - assertEquals(13, expr13rootNode.execute(null)); - assertTrue(tool.getCounts().isEmpty()); + checkCounts(source, valueCoverage, new Long[]{Long.valueOf(1), null, Long.valueOf(1), null}); - probe.tagAs(StandardSyntaxTag.STATEMENT, "fake statement for testing"); + addCoverage.install(instrumenter); - // Counting now; execute once - assertEquals(13, expr13rootNode.execute(null)); + assertEquals(vm.eval(source).get(), 13); - final Long[] longs1 = tool.getCounts().get(addNode.getSourceSection().getSource()); - assertNotNull(longs1); - assertEquals(longs1.length, 2); - assertNull(longs1[0]); // Line 1 is empty (text lines are 1-based) - assertEquals(1L, longs1[1].longValue()); // Expression is on line 2 + checkCounts(source, valueCoverage, new Long[]{Long.valueOf(2), null, Long.valueOf(2), null}); + checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(1), null, null}); + + valueCoverage.setEnabled(false); + assertEquals(vm.eval(source).get(), 13); - // Execute 99 more times - for (int i = 0; i < 99; i++) { - assertEquals(13, expr13rootNode.execute(null)); - } + checkCounts(source, valueCoverage, new Long[]{Long.valueOf(2), null, Long.valueOf(2), null}); + checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(2), null, null}); + + valueCoverage.setEnabled(true); + assertEquals(vm.eval(source).get(), 13); - final Long[] longs100 = tool.getCounts().get(addNode.getSourceSection().getSource()); - assertNotNull(longs100); - assertEquals(longs100.length, 2); - assertNull(longs100[0]); - assertEquals(100L, longs100[1].longValue()); + checkCounts(source, valueCoverage, new Long[]{Long.valueOf(3), null, Long.valueOf(3), null}); + checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(3), null, null}); + + valueCoverage.dispose(); + assertEquals(vm.eval(source).get(), 13); - tool.dispose(); + checkCounts(source, addCoverage, new Long[]{null, Long.valueOf(4), null, null}); + + addCoverage.dispose(); } - }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,67 +24,97 @@ */ package com.oracle.truffle.tools.test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.LineLocation; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.TruffleVM; import com.oracle.truffle.tools.LineToProbesMap; -import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode; -import static com.oracle.truffle.tools.test.TestNodes.expr13Line1; -import static com.oracle.truffle.tools.test.TestNodes.expr13Line2; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import org.junit.Test; +import com.oracle.truffle.tools.test.ToolTestUtil.ToolTestTag; public class LineToProbesMapTest { @Test - public void testToolCreatedTooLate() { - final RootNode expr13rootNode = createExpr13TestRootNode(); - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); - final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); - assertEquals(lineLocation, expr13Line2); - - final LineToProbesMap tool = new LineToProbesMap(); - tool.install(); - - assertNull(tool.findFirstProbe(expr13Line1)); - assertNull(tool.findFirstProbe(expr13Line2)); - tool.dispose(); + public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = ToolTestUtil.createTestSource("testNoExecution"); + final LineToProbesMap probesMap = new LineToProbesMap(); + probesMap.install(instrumenter); + assertNull(probesMap.findFirstProbe(source.createLineLocation(1))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(2))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(3))); + probesMap.dispose(); } @Test - public void testToolInstalledTooLate() { - final LineToProbesMap tool = new LineToProbesMap(); + public void testMapping1() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = ToolTestUtil.createTestSource("testMapping1"); + final LineToProbesMap probesMap = new LineToProbesMap(); + + assertNull(probesMap.findFirstProbe(source.createLineLocation(1))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(2))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(3))); - final RootNode expr13rootNode = createExpr13TestRootNode(); - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); - final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); - assertEquals(lineLocation, expr13Line2); + // Map installed before AST gets created + probesMap.install(instrumenter); + assertEquals(vm.eval(source).get(), 13); - tool.install(); + final Probe probe1 = probesMap.findFirstProbe(source.createLineLocation(1)); + assertNotNull(probe1); + assertTrue(probe1.isTaggedAs(ToolTestTag.VALUE_TAG)); + final Probe probe2 = probesMap.findFirstProbe(source.createLineLocation(2)); + assertNotNull(probe2); + assertTrue(probe2.isTaggedAs(ToolTestTag.ADD_TAG)); + final Probe probe3 = probesMap.findFirstProbe(source.createLineLocation(3)); + assertNotNull(probe3); + assertTrue(probe3.isTaggedAs(ToolTestTag.VALUE_TAG)); - assertNull(tool.findFirstProbe(expr13Line1)); - assertNull(tool.findFirstProbe(expr13Line2)); - tool.dispose(); + probesMap.dispose(); } @Test - public void testMapping() { - final LineToProbesMap tool = new LineToProbesMap(); - tool.install(); + public void testMapping2() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = ToolTestUtil.createTestSource("testMapping2"); + final LineToProbesMap probesMap = new LineToProbesMap(); + + assertNull(probesMap.findFirstProbe(source.createLineLocation(1))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(2))); + assertNull(probesMap.findFirstProbe(source.createLineLocation(3))); - final RootNode expr13rootNode = createExpr13TestRootNode(); - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); - final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); - assertEquals(lineLocation, expr13Line2); + // Map installed after AST gets created + assertEquals(vm.eval(source).get(), 13); + probesMap.install(instrumenter); - assertNull(tool.findFirstProbe(expr13Line1)); - assertEquals(tool.findFirstProbe(expr13Line2), probe); - tool.dispose(); + final Probe probe1 = probesMap.findFirstProbe(source.createLineLocation(1)); + assertNotNull(probe1); + assertTrue(probe1.isTaggedAs(ToolTestTag.VALUE_TAG)); + final Probe probe2 = probesMap.findFirstProbe(source.createLineLocation(2)); + assertNotNull(probe2); + assertTrue(probe2.isTaggedAs(ToolTestTag.ADD_TAG)); + final Probe probe3 = probesMap.findFirstProbe(source.createLineLocation(3)); + assertNotNull(probe3); + assertTrue(probe3.isTaggedAs(ToolTestTag.VALUE_TAG)); + + probesMap.dispose(); } - }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,29 +24,31 @@ */ package com.oracle.truffle.tools.test; -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.StandardSyntaxTag; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.RootNode; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.lang.reflect.Field; + +import org.junit.Test; + +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.TruffleVM; import com.oracle.truffle.tools.NodeExecCounter; import com.oracle.truffle.tools.NodeExecCounter.NodeExecutionCount; -import com.oracle.truffle.tools.test.TestNodes.TestAddNode; -import com.oracle.truffle.tools.test.TestNodes.TestValueNode; -import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestCallTarget; -import static com.oracle.truffle.tools.test.TestNodes.createExpr13TestRootNode; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import org.junit.Test; public class NodeExecCounterTest { @Test - public void testNoExecution() { + public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); final NodeExecCounter tool = new NodeExecCounter(); assertEquals(tool.getCounts().length, 0); - tool.install(); + tool.install(instrumenter); assertEquals(tool.getCounts().length, 0); tool.setEnabled(false); assertEquals(tool.getCounts().length, 0); @@ -58,113 +60,45 @@ assertEquals(tool.getCounts().length, 0); } - @Test - public void testToolCreatedTooLate() { - final CallTarget expr13callTarget = createExpr13TestCallTarget(); - final NodeExecCounter tool = new NodeExecCounter(); - tool.install(); - assertEquals(13, expr13callTarget.call()); - assertEquals(tool.getCounts().length, 0); - tool.dispose(); - } - - @Test - public void testToolInstalledcTooLate() { - final NodeExecCounter tool = new NodeExecCounter(); - final CallTarget expr13callTarget = createExpr13TestCallTarget(); - tool.install(); - assertEquals(13, expr13callTarget.call()); - assertEquals(tool.getCounts().length, 0); - tool.dispose(); + void checkCounts(NodeExecCounter execTool, int addCount, int valueCount) { + NodeExecutionCount[] counts = execTool.getCounts(); + assertEquals(counts.length, 2); + for (NodeExecutionCount counter : counts) { + if (counter.nodeClass() == ToolTestUtil.TestAdditionNode.class) { + assertEquals(counter.executionCount(), addCount); + } else if (counter.nodeClass() == ToolTestUtil.TestValueNode.class) { + assertEquals(counter.executionCount(), valueCount); + } else { + fail("correct classes counted"); + } + } } @Test - public void testCountingAll() { - final NodeExecCounter tool = new NodeExecCounter(); - tool.install(); - final CallTarget expr13callTarget = createExpr13TestCallTarget(); - - // execute once - assertEquals(13, expr13callTarget.call()); - final NodeExecutionCount[] count1 = tool.getCounts(); - assertNotNull(count1); - assertEquals(count1.length, 2); - for (NodeExecutionCount count : count1) { - final Class<?> class1 = count.nodeClass(); - final long executionCount = count.executionCount(); - if (class1 == TestAddNode.class) { - assertEquals(executionCount, 1); - } else if (class1 == TestValueNode.class) { - assertEquals(executionCount, 2); - } else { - fail(); - } - } + public void testCounting() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, IOException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + final Source source = ToolTestUtil.createTestSource("testCounting"); + final NodeExecCounter execTool = new NodeExecCounter(); + execTool.install(instrumenter); - // Execute 99 more times - for (int i = 0; i < 99; i++) { - assertEquals(13, expr13callTarget.call()); - } - final NodeExecutionCount[] counts100 = tool.getCounts(); - assertNotNull(counts100); - assertEquals(counts100.length, 2); - for (NodeExecutionCount count : counts100) { - final Class<?> class1 = count.nodeClass(); - final long executionCount = count.executionCount(); - if (class1 == TestAddNode.class) { - assertEquals(executionCount, 100); - } else if (class1 == TestValueNode.class) { - assertEquals(executionCount, 200); - } else { - fail(); - } - } + assertEquals(execTool.getCounts().length, 0); - tool.dispose(); - } + assertEquals(vm.eval(source).get(), 13); - @Test - public void testCountingTagged() { - final NodeExecCounter tool = new NodeExecCounter(StandardSyntaxTag.STATEMENT); - tool.install(); - final RootNode expr13rootNode = createExpr13TestRootNode(); - - // Not probed yet. - assertEquals(13, expr13rootNode.execute(null)); - assertEquals(tool.getCounts().length, 0); - - final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); - - // Probed but not tagged yet. - assertEquals(13, expr13rootNode.execute(null)); - assertEquals(tool.getCounts().length, 0); - - probe.tagAs(StandardSyntaxTag.STATEMENT, "fake statement for testing"); + checkCounts(execTool, 1, 2); - // Counting now; execute once - assertEquals(13, expr13rootNode.execute(null)); - final NodeExecutionCount[] counts1 = tool.getCounts(); - assertNotNull(counts1); - assertEquals(counts1.length, 1); - final NodeExecutionCount count1 = counts1[0]; - assertNotNull(count1); - assertEquals(count1.nodeClass(), addNode.getClass()); - assertEquals(count1.executionCount(), 1); + for (int i = 0; i < 99; i++) { + assertEquals(vm.eval(source).get(), 13); + } + checkCounts(execTool, 100, 200); - // Execute 99 more times - for (int i = 0; i < 99; i++) { - assertEquals(13, expr13rootNode.execute(null)); - } + execTool.setEnabled(false); + assertEquals(vm.eval(source).get(), 13); + checkCounts(execTool, 100, 200); - final NodeExecutionCount[] counts100 = tool.getCounts(); - assertNotNull(counts100); - assertEquals(counts100.length, 1); - final NodeExecutionCount count100 = counts100[0]; - assertNotNull(count100); - assertEquals(count100.nodeClass(), addNode.getClass()); - assertEquals(count100.executionCount(), 100); - - tool.dispose(); + execTool.dispose(); } }
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java Mon Sep 21 13:11:41 2015 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2014, 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. 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.tools.test; - -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.Truffle; -import com.oracle.truffle.api.TruffleLanguage; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.instrument.KillException; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeNode; -import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.nodes.NodeCost; -import com.oracle.truffle.api.nodes.NodeInfo; -import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.LineLocation; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; - -/** - * Nodes and an {@linkplain CallTarget executable ASTs} for testing. - */ -class TestNodes { - - /** - * A fake source used for testing: empty line 1, expression on line 2. - */ - public static final Source expr13Source = Source.fromText("\n6+7\n", "Test Source: expression on line 2 that evaluates to 13"); - public static final LineLocation expr13Line1 = expr13Source.createLineLocation(1); - public static final LineLocation expr13Line2 = expr13Source.createLineLocation(2); - - /** - * An executable addition expression that evaluates to 13. - */ - static CallTarget createExpr13TestCallTarget() { - final RootNode rootNode = createExpr13TestRootNode(); - return Truffle.getRuntime().createCallTarget(rootNode); - } - - /** - * Root holding an addition expression that evaluates to 13. - */ - static RootNode createExpr13TestRootNode() { - final TestLanguageNode ast = createExpr13AST(); - final TestRootNode rootNode = new TestRootNode(ast); - rootNode.adoptChildren(); - return rootNode; - } - - /** - * Addition expression that evaluates to 13, with faked source attribution. - */ - static TestLanguageNode createExpr13AST() { - final SourceSection leftSourceSection = expr13Source.createSection("left", 1, 1); - final TestValueNode leftValueNode = new TestValueNode(6, leftSourceSection); - final SourceSection rightSourceSection = expr13Source.createSection("right", 3, 1); - final TestValueNode rightValueNode = new TestValueNode(7, rightSourceSection); - final SourceSection exprSourceSection = expr13Source.createSection("expr", 1, 3); - return new TestAddNode(leftValueNode, rightValueNode, exprSourceSection); - } - - abstract static class TestLanguageNode extends Node { - public abstract Object execute(VirtualFrame frame); - - public TestLanguageNode() { - } - - public TestLanguageNode(SourceSection srcSection) { - super(srcSection); - } - - @Override - public boolean isInstrumentable() { - return true; - } - - @Override - public WrapperNode createWrapperNode() { - return new TestWrapperNode(this); - } - } - - @NodeInfo(cost = NodeCost.NONE) - static class TestWrapperNode extends TestLanguageNode implements WrapperNode { - @Child private TestLanguageNode child; - @Child private ProbeNode probeNode; - - public TestWrapperNode(TestLanguageNode child) { - assert !(child instanceof TestWrapperNode); - this.child = child; - } - - @Override - public String instrumentationInfo() { - return "Wrapper node for testing"; - } - - @Override - public boolean isInstrumentable() { - return false; - } - - @Override - public void insertProbe(ProbeNode newProbeNode) { - this.probeNode = newProbeNode; - } - - @Override - public Probe getProbe() { - return probeNode.getProbe(); - } - - @Override - public Node getChild() { - return child; - } - - @Override - public Object execute(VirtualFrame frame) { - probeNode.enter(child, frame); - Object result; - - try { - result = child.execute(frame); - probeNode.returnValue(child, frame, result); - } catch (KillException e) { - throw (e); - } catch (Exception e) { - probeNode.returnExceptional(child, frame, e); - throw (e); - } - - return result; - } - } - - /** - * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST - * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle - * completes an AST. The root nodes serves as our entry point into a program. - */ - static class TestRootNode extends RootNode { - @Child private TestLanguageNode body; - - /** - * This constructor emulates the global machinery that applies registered probers to every - * newly created AST. Global registry is not used, since that would interfere with other - * tests run in the same environment. - */ - public TestRootNode(TestLanguageNode body) { - super(TruffleLanguage.class, null, null); - this.body = body; - } - - @Override - public Object execute(VirtualFrame frame) { - return body.execute(frame); - } - - @Override - public boolean isCloningAllowed() { - return true; - } - - @Override - public void applyInstrumentation() { - Probe.applyASTProbers(body); - } - } - - static class TestValueNode extends TestLanguageNode { - private final int value; - - public TestValueNode(int value) { - this.value = value; - } - - public TestValueNode(int value, SourceSection srcSection) { - super(srcSection); - this.value = value; - } - - @Override - public Object execute(VirtualFrame frame) { - return new Integer(this.value); - } - } - - static class TestAddNode extends TestLanguageNode { - @Child private TestLanguageNode leftChild; - @Child private TestLanguageNode rightChild; - - public TestAddNode(TestValueNode leftChild, TestValueNode rightChild, SourceSection sourceSection) { - super(sourceSection); - this.leftChild = insert(leftChild); - this.rightChild = insert(rightChild); - } - - @Override - public Object execute(VirtualFrame frame) { - return new Integer(((Integer) leftChild.execute(frame)).intValue() + ((Integer) rightChild.execute(frame)).intValue()); - } - } - -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/ToolTestUtil.java Mon Sep 21 12:15:38 2015 -0700 @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2015, 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. 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.tools.test; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.TruffleRuntime; +import com.oracle.truffle.api.debug.DebugSupportProvider; +import com.oracle.truffle.api.frame.MaterializedFrame; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.instrument.ASTProber; +import com.oracle.truffle.api.instrument.AdvancedInstrumentResultListener; +import com.oracle.truffle.api.instrument.AdvancedInstrumentRootFactory; +import com.oracle.truffle.api.instrument.EventHandlerNode; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.KillException; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.SyntaxTag; +import com.oracle.truffle.api.instrument.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; +import com.oracle.truffle.api.instrument.WrapperNode; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeCost; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.nodes.NodeVisitor; +import com.oracle.truffle.api.nodes.RootNode; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; + +public class ToolTestUtil { + + static final String MIME_TYPE = "text/x-toolTest"; + + static enum ToolTestTag implements SyntaxTag { + + ADD_TAG("addition", "test language addition node"), + + VALUE_TAG("value", "test language value node"); + + private final String name; + private final String description; + + private ToolTestTag(String name, String description) { + this.name = name; + this.description = description; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + } + + static Source createTestSource(String description) { + return Source.fromText("6\n+\n7\n" + description, description).withMimeType(MIME_TYPE); + } + + @TruffleLanguage.Registration(name = "toolTestLanguage", version = "0", mimeType = MIME_TYPE) + public static final class ToolTestLang extends TruffleLanguage<Object> { + + public static final ToolTestLang INSTANCE = new ToolTestLang(); + + private final ASTProber prober = new TestASTProber(); + + private ToolTestLang() { + } + + @Override + protected CallTarget parse(Source source, Node context, String... argumentNames) throws IOException { + final TestValueNode leftValueNode = new TestValueNode(6, source.createSection("6", 0, 1)); + final TestValueNode rightValueNode = new TestValueNode(7, source.createSection("7", 4, 1)); + final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode, source.createSection("+", 2, 1)); + final InstrumentationTestRootNode rootNode = new InstrumentationTestRootNode(addNode); + final TruffleRuntime runtime = Truffle.getRuntime(); + final CallTarget callTarget = runtime.createCallTarget(rootNode); + return callTarget; + } + + @Override + protected Object findExportedSymbol(Object context, String globalName, boolean onlyExplicit) { + return null; + } + + @Override + protected Object getLanguageGlobal(Object context) { + return null; + } + + @Override + protected boolean isObjectOfLanguage(Object object) { + return false; + } + + @Override + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected ASTProber getDefaultASTProber() { + return prober; + } + + @Override + protected boolean isInstrumentable(Node node) { + return node instanceof TestAdditionNode || node instanceof TestValueNode; + } + + @Override + protected WrapperNode createWrapperNode(Node node) { + if (isInstrumentable(node)) { + return new ToolTestWrapperNode((ToolTestLangNode) node); + } + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected void enableASTProbing(ASTProber astProber) { + throw new UnsupportedOperationException(); + } + + @Override + protected Object evalInContext(Source source, Node node, MaterializedFrame mFrame) throws IOException { + return null; + } + + @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(String expr, AdvancedInstrumentResultListener resultListener) throws IOException { + return null; + } + + @SuppressWarnings("deprecation") + @Override + protected ToolSupportProvider getToolSupport() { + throw new UnsupportedOperationException(); + } + + @SuppressWarnings("deprecation") + @Override + protected DebugSupportProvider getDebugSupport() { + throw new UnsupportedOperationException(); + } + + @Override + protected Object createContext(Env env) { + return null; + } + + } + + static final class TestASTProber implements ASTProber { + + public void probeAST(final Instrumenter instrumenter, Node startNode) { + startNode.accept(new NodeVisitor() { + + @Override + public boolean visit(Node node) { + if (node instanceof ToolTestLangNode) { + + final ToolTestLangNode testNode = (ToolTestLangNode) node; + + if (node instanceof TestValueNode) { + instrumenter.probe(testNode).tagAs(ToolTestTag.VALUE_TAG, null); + + } else if (node instanceof TestAdditionNode) { + instrumenter.probe(testNode).tagAs(ToolTestTag.ADD_TAG, null); + + } + } + return true; + } + }); + } + } + + abstract static class ToolTestLangNode extends Node { + public abstract Object execute(VirtualFrame vFrame); + + protected ToolTestLangNode(SourceSection ss) { + super(ss); + } + } + + @NodeInfo(cost = NodeCost.NONE) + static class ToolTestWrapperNode extends ToolTestLangNode implements WrapperNode { + @Child private ToolTestLangNode child; + @Child private EventHandlerNode eventHandlerNode; + + public ToolTestWrapperNode(ToolTestLangNode child) { + super(null); + assert !(child instanceof ToolTestWrapperNode); + this.child = child; + } + + @Override + public String instrumentationInfo() { + return "Wrapper node for testing"; + } + + public void insertEventHandlerNode(EventHandlerNode eventHandler) { + this.eventHandlerNode = eventHandler; + } + + @Override + public Probe getProbe() { + return eventHandlerNode.getProbe(); + } + + @Override + public Node getChild() { + return child; + } + + @Override + public Object execute(VirtualFrame vFrame) { + eventHandlerNode.enter(child, vFrame); + Object result; + try { + result = child.execute(vFrame); + eventHandlerNode.returnValue(child, vFrame, result); + } catch (KillException e) { + throw (e); + } catch (Exception e) { + eventHandlerNode.returnExceptional(child, vFrame, e); + throw (e); + } + return result; + } + } + + /** + * A simple node for our test language to store a value. + */ + static class TestValueNode extends ToolTestLangNode { + private final int value; + + public TestValueNode(int value, SourceSection s) { + super(s); + this.value = value; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return new Integer(this.value); + } + } + + /** + * A node for our test language that adds up two {@link TestValueNode}s. + */ + static class TestAdditionNode extends ToolTestLangNode { + @Child private ToolTestLangNode leftChild; + @Child private ToolTestLangNode rightChild; + + public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild, SourceSection s) { + super(s); + this.leftChild = insert(leftChild); + this.rightChild = insert(rightChild); + } + + @Override + public Object execute(VirtualFrame vFrame) { + return new Integer(((Integer) leftChild.execute(vFrame)).intValue() + ((Integer) rightChild.execute(vFrame)).intValue()); + } + } + + /** + * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST + * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle + * completes an AST. The root nodes serves as our entry point into a program. + */ + static class InstrumentationTestRootNode extends RootNode { + @Child private ToolTestLangNode body; + + /** + * This constructor emulates the global machinery that applies registered probers to every + * newly created AST. Global registry is not used, since that would interfere with other + * tests run in the same environment. + */ + public InstrumentationTestRootNode(ToolTestLangNode body) { + super(ToolTestLang.class, null, null); + this.body = body; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return body.execute(vFrame); + } + + @Override + public boolean isCloningAllowed() { + return true; + } + + @Override + public void applyInstrumentation() { + super.applyInstrumentation(body); + } + } + + /** + * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST + * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle + * completes an AST. The root nodes serves as our entry point into a program. + */ + static class TestRootNode extends RootNode { + @Child private ToolTestLangNode body; + + final Instrumenter instrumenter; + + /** + * This constructor emulates the global machinery that applies registered probers to every + * newly created AST. Global registry is not used, since that would interfere with other + * tests run in the same environment. + */ + public TestRootNode(ToolTestLangNode body, Instrumenter instrumenter) { + super(ToolTestLang.class, null, null); + this.instrumenter = instrumenter; + this.body = body; + } + + @Override + public Object execute(VirtualFrame vFrame) { + return body.execute(vFrame); + } + + @Override + public boolean isCloningAllowed() { + return true; + } + + @Override + public void applyInstrumentation() { + Method method; + try { + method = Instrumenter.class.getDeclaredMethod("applyInstrumentation", Node.class); + method.setAccessible(true); + method.invoke(instrumenter, body); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new RuntimeException("InstrumentationTestNodes"); + } + } + } + +}
--- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,21 +24,27 @@ */ package com.oracle.truffle.tools.test; -import com.oracle.truffle.api.instrument.InstrumentationTool; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Field; + import org.junit.Test; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.vm.TruffleVM; + /** * Test the basic life cycle properties shared by all instances of {@link InstrumentationTool}. */ public class TruffleToolTest { @Test - public void testEmptyLifeCycle() { + public void testEmptyLifeCycle() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); assertFalse(tool.isEnabled()); - tool.install(); + tool.install(instrumenter); assertTrue(tool.isEnabled()); tool.reset(); assertTrue(tool.isEnabled()); @@ -73,45 +79,58 @@ } @Test(expected = IllegalStateException.class) - public void testAlreadyInstalled() { + public void testAlreadyInstalled() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); - tool.install(); + tool.install(instrumenter); + tool.install(instrumenter); } @Test(expected = IllegalStateException.class) - public void testAlreadyDisposed1() { + public void testAlreadyDisposed1() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + tool.install(instrumenter); tool.dispose(); - tool.install(); + tool.install(instrumenter); } @Test(expected = IllegalStateException.class) - public void testAlreadyDisposed2() { + public void testAlreadyDisposed2() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + tool.install(instrumenter); tool.dispose(); tool.reset(); } @Test(expected = IllegalStateException.class) - public void testAlreadyDisposed3() { + public void testAlreadyDisposed3() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + tool.install(instrumenter); tool.dispose(); tool.setEnabled(true); } @Test(expected = IllegalStateException.class) - public void testAlreadyDisposed4() { + public void testAlreadyDisposed4() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = getInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + tool.install(instrumenter); tool.dispose(); tool.dispose(); } - private static final class DummyTruffleTool extends InstrumentationTool { + private static Instrumenter getInstrumenter() throws NoSuchFieldException, IllegalAccessException { + final TruffleVM vm = TruffleVM.newVM().build(); + final Field field = TruffleVM.class.getDeclaredField("instrumenter"); + field.setAccessible(true); + final Instrumenter instrumenter = (Instrumenter) field.get(vm); + return instrumenter; + } + + private static final class DummyTruffleTool extends Instrumenter.Tool { @Override protected boolean internalInstall() {
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,8 +24,17 @@ */ package com.oracle.truffle.tools; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeSet; + import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.InstrumentationTool; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.ProbeListener; import com.oracle.truffle.api.instrument.SimpleInstrumentListener; @@ -37,14 +46,6 @@ import com.oracle.truffle.api.source.LineLocation; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.TreeSet; /** * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes that @@ -61,9 +62,9 @@ * <p> * <ul> * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented - * to produce the event {@link SimpleInstrumentListener#enter(Probe)};</li> + * to produce the event {@link SimpleInstrumentListener#onEnter(Probe)};</li> * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which - * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li> + * {@link Instrumenter#isInstrumentable(Node)}{@code == true};</li> * <li>Execution calls are tabulated only at nodes present in the AST when originally created; * dynamically added nodes will not be instrumented.</li> * </ul> @@ -81,7 +82,7 @@ * @see Instrument * @see SyntaxTag */ -public final class CoverageTracker extends InstrumentationTool { +public final class CoverageTracker extends Instrumenter.Tool { /** Counting data. */ private final Map<LineLocation, CoverageRecord> coverageMap = new HashMap<>(); @@ -115,7 +116,11 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + final Instrumenter instrumenter = getInstrumenter(); + for (Probe probe : instrumenter.findProbesTaggedAs(countingTag)) { + addCoverageCounter(probe); + } + instrumenter.addProbeListener(probeListener); return true; } @@ -126,7 +131,7 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); for (Instrument instrument : instruments) { instrument.dispose(); } @@ -250,7 +255,7 @@ } @Override - public void enter(Probe probe) { + public void onEnter(Probe probe) { if (isEnabled()) { count++; } @@ -273,34 +278,35 @@ @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { if (countingTag == tag) { - - final SourceSection srcSection = probe.getProbedSourceSection(); - if (srcSection == null) { - // TODO (mlvdv) report this? - return; - } - // Get the source line where the - final LineLocation lineLocation = srcSection.getLineLocation(); - CoverageRecord record = coverageMap.get(lineLocation); - if (record != null) { - // Another node starts on same line; count only the first (textually) - if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) { - // Existing record, corresponds to code earlier on line - return; - } else { - // Existing record, corresponds to code at a later position; replace it - record.instrument.dispose(); - } - } - - final CoverageRecord coverage = new CoverageRecord(srcSection); - final Instrument instrument = Instrument.create(coverage, CoverageTracker.class.getSimpleName()); - coverage.instrument = instrument; - instruments.add(instrument); - probe.attach(instrument); - coverageMap.put(lineLocation, coverage); + addCoverageCounter(probe); } } } + private void addCoverageCounter(Probe probe) { + final SourceSection srcSection = probe.getProbedSourceSection(); + if (srcSection == null) { + // TODO (mlvdv) report this? + return; + } + // Get the source line where the + final LineLocation lineLocation = srcSection.getLineLocation(); + CoverageRecord record = coverageMap.get(lineLocation); + if (record != null) { + // Another node starts on same line; count only the first (textually) + if (srcSection.getCharIndex() > record.srcSection.getCharIndex()) { + // Existing record, corresponds to code earlier on line + return; + } else { + // Existing record, corresponds to code at a later position; replace it + record.instrument.dispose(); + } + } + + final CoverageRecord coverageRecord = new CoverageRecord(srcSection); + final Instrument instrument = getInstrumenter().attach(probe, coverageRecord, CoverageTracker.class.getSimpleName()); + coverageRecord.instrument = instrument; + instruments.add(instrument); + coverageMap.put(lineLocation, coverageRecord); + } }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,13 +24,6 @@ */ package com.oracle.truffle.tools; -import com.oracle.truffle.api.instrument.InstrumentationTool; -import com.oracle.truffle.api.instrument.Probe; -import com.oracle.truffle.api.instrument.ProbeListener; -import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; -import com.oracle.truffle.api.source.LineLocation; -import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.source.SourceSection; import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; @@ -38,11 +31,19 @@ import java.util.HashMap; import java.util.Map; +import com.oracle.truffle.api.instrument.Instrumenter; +import com.oracle.truffle.api.instrument.Probe; +import com.oracle.truffle.api.instrument.ProbeListener; +import com.oracle.truffle.api.instrument.impl.DefaultProbeListener; +import com.oracle.truffle.api.source.LineLocation; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.source.SourceSection; + /** * An {@link InstrumentationTool} that builds a map of every {@link Probe} attached to some AST, * indexed by {@link Source} and line number. */ -public final class LineToProbesMap extends InstrumentationTool { +public final class LineToProbesMap extends Instrumenter.Tool { private static final boolean TRACE = false; private static final PrintStream OUT = System.out; @@ -68,7 +69,11 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + final Instrumenter instrumenter = getInstrumenter(); + for (Probe probe : instrumenter.findProbesTaggedAs(null)) { + addMapEntry(probe); + } + instrumenter.addProbeListener(probeListener); return true; } @@ -79,7 +84,7 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); } /** @@ -115,21 +120,25 @@ @Override public void newProbeInserted(Probe probe) { - final SourceSection sourceSection = probe.getProbedSourceSection(); - if (sourceSection != null && sourceSection.getSource() != null) { - final LineLocation lineLocation = sourceSection.getLineLocation(); - if (TRACE) { - trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription()); - } - Collection<Probe> probes = lineToProbesMap.get(lineLocation); - if (probes == null) { - probes = new ArrayList<>(2); - lineToProbesMap.put(lineLocation, probes); - } else { - assert !probes.contains(probe); - } - probes.add(probe); + addMapEntry(probe); + } + } + + private void addMapEntry(Probe probe) { + final SourceSection sourceSection = probe.getProbedSourceSection(); + if (sourceSection != null && sourceSection.getSource() != null) { + final LineLocation lineLocation = sourceSection.getLineLocation(); + if (TRACE) { + trace("ADD " + lineLocation.getShortDescription() + " ==> " + probe.getShortDescription()); } + Collection<Probe> probes = lineToProbesMap.get(lineLocation); + if (probes == null) { + probes = new ArrayList<>(2); + lineToProbesMap.put(lineLocation, probes); + } else { + assert !probes.contains(probe); + } + probes.add(probe); } } }
--- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java Mon Sep 21 13:11:41 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java Mon Sep 21 12:15:38 2015 -0700 @@ -24,11 +24,21 @@ */ package com.oracle.truffle.tools; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrument.ASTProber; import com.oracle.truffle.api.instrument.Instrument; -import com.oracle.truffle.api.instrument.InstrumentationTool; +import com.oracle.truffle.api.instrument.Instrumenter; import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.ProbeException; import com.oracle.truffle.api.instrument.ProbeFailure; @@ -40,15 +50,6 @@ import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.Node.Child; import com.oracle.truffle.api.nodes.NodeVisitor; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; /** * An {@link InstrumentationTool} that counts interpreter <em>execution calls</em> to AST nodes, @@ -64,9 +65,9 @@ * <p> * <ul> * <li>"Execution call" on a node is is defined as invocation of a node method that is instrumented - * to produce the event {@link StandardInstrumentListener#enter(Probe, Node, VirtualFrame)};</li> + * to produce the event {@link StandardInstrumentListener#onEnter(Probe, Node, VirtualFrame)};</li> * <li>Execution calls are tabulated only at <em>instrumented</em> nodes, i.e. those for which - * {@linkplain Node#isInstrumentable() isInstrumentable() == true};</li> + * {@linkplain Instrumenter#isInstrumentable(Node) isInstrumentable() == true};</li> * <li>Execution calls are tabulated only at nodes present in the AST when originally created; * dynamically added nodes will not be instrumented.</li> * </ul> @@ -95,7 +96,7 @@ * @see SyntaxTag * @see ProbeFailure */ -public final class NodeExecCounter extends InstrumentationTool { +public final class NodeExecCounter extends Instrumenter.Tool { /** * Execution count for AST nodes of a particular type. @@ -112,13 +113,13 @@ */ private final StandardInstrumentListener instrumentListener = new DefaultStandardInstrumentListener() { @Override - public void enter(Probe probe, Node node, VirtualFrame vFrame) { + public void onEnter(Probe probe, Node node, VirtualFrame vFrame) { if (isEnabled()) { final Class<?> nodeClass = node.getClass(); /* * Everything up to here is inlined by Truffle compilation. Delegate the next part * to a method behind an inlining boundary. - * + * * Note that it is not permitted to pass a {@link VirtualFrame} across an inlining * boundary; they are truly virtual in inlined code. */ @@ -170,7 +171,7 @@ * Create a per node-type execution counting tool for all nodes in subsequently created ASTs. */ public NodeExecCounter() { - this.countingTag = null; + this(null); } /** @@ -185,10 +186,10 @@ protected boolean internalInstall() { if (countingTag == null) { astProber = new ExecCounterASTProber(); - Probe.registerASTProber(astProber); + getInstrumenter().registerASTProber(astProber); } else { probeListener = new NodeExecCounterProbeListener(); - Probe.addProbeListener(probeListener); + getInstrumenter().addProbeListener(probeListener); } return true; } @@ -202,10 +203,10 @@ @Override protected void internalDispose() { if (astProber != null) { - Probe.unregisterASTProber(astProber); + getInstrumenter().unregisterASTProber(astProber); } if (probeListener != null) { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); } for (Instrument instrument : instruments) { instrument.dispose(); @@ -289,24 +290,28 @@ /** * A prober that attempts to probe and instrument every node. */ - private class ExecCounterASTProber implements ASTProber, NodeVisitor { + private class ExecCounterASTProber implements ASTProber { + + public void probeAST(final Instrumenter instrumenter, final Node startNode) { - public boolean visit(Node node) { + startNode.accept(new NodeVisitor() { + + public boolean visit(Node node) { + + if (instrumenter.isInstrumentable(node)) { + try { - if (node.isInstrumentable()) { - try { - final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter"); - instruments.add(instrument); - node.probe().attach(instrument); - } catch (ProbeException ex) { - failures.add(ex.getFailure()); + final Probe probe = instrumenter.probe(node); + final Instrument instrument = instrumenter.attach(probe, instrumentListener, "NodeExecCounter"); + instruments.add(instrument); + } catch (ProbeException ex) { + failures.add(ex.getFailure()); + } + } + return true; } - } - return true; - } - public void probeAST(Node node) { - node.accept(this); + }); } } @@ -319,9 +324,8 @@ @Override public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) { if (countingTag == tag) { - final Instrument instrument = Instrument.create(instrumentListener, NodeExecCounter.class.getSimpleName()); + final Instrument instrument = getInstrumenter().attach(probe, instrumentListener, NodeExecCounter.class.getSimpleName()); instruments.add(instrument); - probe.attach(instrument); } } }