# HG changeset patch # User Michael Van De Vanter # Date 1442296791 25200 # Node ID 3aad794eec0e7738819a235b27f66999a2c50117 # Parent 1d804d691dc7ee471e9cd70e0997d756049ac784 Truffle/Instrumentation: first large merge of instrumentation code into the TruffleVM framework - introduce the Instrumenter class, held by the TruffleVM to support instrumentation services - reimplement Probe-realated services (formerly statics in Probe.java) to be provided by the Instrumenter- add new Accessors - change the TruffleVM startup sequence - change the APIs of the Debugger and many other classes diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java --- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/TestingLanguage.java Mon Sep 14 22:59:51 2015 -0700 @@ -25,10 +25,18 @@ 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.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; +import java.util.Collections; +import java.util.List; public final class TestingLanguage extends TruffleLanguage { public static final TestingLanguage INSTANCE = new TestingLanguage(); @@ -58,13 +66,41 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { return null; } @Override + protected List getASTProbers() { + return Collections.emptyList(); + } + + @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 diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java --- a/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.dsl.test/src/com/oracle/truffle/api/dsl/test/processor/LanguageRegistrationTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -22,14 +22,22 @@ */ package com.oracle.truffle.api.dsl.test.processor; -import java.io.*; +import java.io.IOException; +import java.util.Collections; +import java.util.List; -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.debug.*; -import com.oracle.truffle.api.dsl.test.*; -import com.oracle.truffle.api.instrument.*; +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.ToolSupportProvider; +import com.oracle.truffle.api.instrument.Visualizer; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.source.Source; public class LanguageRegistrationTest { @@ -76,13 +84,41 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected List getASTProbers() { + return Collections.emptyList(); + } + + @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 +156,41 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected List getASTProbers() { + return Collections.emptyList(); + } + + @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 +224,41 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected List getASTProbers() { + return Collections.emptyList(); + } + + @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 diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/TestingLanguage.java Mon Sep 14 22:59:51 2015 -0700 @@ -25,10 +25,18 @@ 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.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; +import java.util.Collections; +import java.util.List; public final class TestingLanguage extends TruffleLanguage { public static final TestingLanguage INSTANCE = new TestingLanguage(); @@ -57,13 +65,41 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { return null; } @Override + protected List getASTProbers() { + return Collections.emptyList(); + } + + @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 diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/AdvancedInstrumentTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -41,20 +41,22 @@ public class AdvancedInstrumentTest { @Test - public void testAdvancedInstrumentListener() { + public void testAdvancedInstrumentListener() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter(); + // 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 TestRootNode rootNode = new TestRootNode(addNode, instrumenter); final CallTarget callTarget1 = runtime.createCallTarget(rootNode); // Ensure it executes correctly assertEquals(13, callTarget1.call()); // Probe the addition node - final Probe probe = addNode.probe(); + final Probe probe = instrumenter.probe(addNode); assertEquals(13, callTarget1.call()); diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -88,7 +88,9 @@ }; @Test - public void testInstrumentationStructure() { + public void testInstrumentationStructure() throws IllegalAccessException, SecurityException, IllegalArgumentException, NoSuchFieldException { + + final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter(); // Create a simple addition AST final TruffleRuntime runtime = Truffle.getRuntime(); final TestValueNode leftValueNode = new TestValueNode(6); @@ -96,11 +98,11 @@ final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); try { - addNode.probe(); + instrumenter.probe(addNode); } catch (ProbeException e) { assertEquals(e.getFailure().getReason(), Reason.NO_PARENT); } - final TestRootNode rootNode = new TestRootNode(addNode); + final TestRootNode rootNode = new TestRootNode(addNode, instrumenter); // Creating a call target sets the parent pointers in this tree and is necessary prior to // checking any parent/child relationships @@ -122,7 +124,7 @@ assertEquals(13, callTarget1.call()); // Probe the addition node - addNode.probe(); + instrumenter.probe(addNode); // Check the modified tree structure assertEquals(addNode, leftValueNode.getParent()); @@ -149,7 +151,7 @@ // Check that you can't probe the WrapperNodes TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode; try { - wrapper.probe(); + instrumenter.probe(wrapper); fail(); } catch (ProbeException e) { assertEquals(e.getFailure().getReason(), Reason.WRAPPER_NODE); @@ -160,20 +162,22 @@ } @Test - public void testListeners() { + public void testListeners() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + + final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter(); // 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 TestRootNode rootNode = new TestRootNode(addNode, instrumenter); // 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(); + final Probe probe = instrumenter.probe(addNode); // Check instrumentation with the simplest kind of counters. // They should all be removed when the check is finished. @@ -281,14 +285,16 @@ } @Test - public void testTagging() { + public void testTagging() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = InstrumentationTestNodes.createInstrumenter(); + // Applies appropriate tags - final TestASTProber astProber = new TestASTProber(); - Probe.registerASTProber(astProber); + final TestASTProber astProber = new TestASTProber(instrumenter); + instrumenter.registerASTProber(astProber); // Listens for probes and tags being added final TestProbeListener probeListener = new TestProbeListener(); - Probe.addProbeListener(probeListener); + instrumenter.addProbeListener(probeListener); // Counts all entries to all instances of addition nodes final TestMultiCounter additionCounter = new TestMultiCounter(); @@ -302,7 +308,7 @@ final TestValueNode rightValueNode = new TestValueNode(7); final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode); - final TestRootNode rootNode = new TestRootNode(addNode); + final TestRootNode rootNode = new TestRootNode(addNode, instrumenter); final CallTarget callTarget = runtime.createCallTarget(rootNode); @@ -310,18 +316,18 @@ assertEquals(probeListener.probeCount, 3); assertEquals(probeListener.tagCount, 3); - assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1); - assertEquals(Probe.findProbesTaggedAs(VALUE_TAG).size(), 2); + assertEquals(instrumenter.findProbesTaggedAs(ADD_TAG).size(), 1); + assertEquals(instrumenter.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)) { + for (Probe probe : instrumenter.findProbesTaggedAs(ADD_TAG)) { additionCounter.attachCounter(probe); } // Dynamically attach a counter for all executions of all Value nodes - for (Probe probe : Probe.findProbesTaggedAs(VALUE_TAG)) { + for (Probe probe : instrumenter.findProbesTaggedAs(VALUE_TAG)) { valueCounter.attachCounter(probe); } @@ -336,7 +342,7 @@ assertEquals(additionCounter.count, 1); assertEquals(valueCounter.count, 2); - Probe.unregisterASTProber(astProber); + instrumenter.unregisterASTProber(astProber); } private interface TestCounter { @@ -459,6 +465,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) { @@ -466,10 +478,10 @@ final TestLanguageNode testNode = (TestLanguageNode) node; if (node instanceof TestValueNode) { - testNode.probe().tagAs(VALUE_TAG, null); + instrumenter.probe(testNode).tagAs(VALUE_TAG, null); } else if (node instanceof TestAdditionNode) { - testNode.probe().tagAs(ADD_TAG, null); + instrumenter.probe(testNode).tagAs(ADD_TAG, null); } } @@ -477,7 +489,7 @@ } @Override - public void probeAST(Node node) { + public void probeAST(Instrumenter inst, Node node) { node.accept(this); } } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTestNodes.java Mon Sep 14 22:59:51 2015 -0700 @@ -22,18 +22,29 @@ */ package com.oracle.truffle.api.test.instrument; +import java.lang.reflect.*; + import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.*; 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); @@ -142,13 +153,16 @@ 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) { + public TestRootNode(TestLanguageNode body, Instrumenter instrumenter) { super(TestingLanguage.class, null, null); + this.instrumenter = instrumenter; this.body = body; } @@ -164,7 +178,14 @@ @Override public void applyInstrumentation() { - Probe.applyASTProbers(body); + 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"); + } } } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/ImplicitExplicitExportTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -22,19 +22,39 @@ */ package com.oracle.truffle.api.test.vm; -import com.oracle.truffle.api.*; +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.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executors; + +import org.junit.After; +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.*; -import com.oracle.truffle.api.instrument.*; +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.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.*; -import com.oracle.truffle.api.vm.*; -import java.io.*; -import java.util.*; -import java.util.concurrent.Executors; -import org.junit.*; -import static org.junit.Assert.*; +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.TruffleVM; public class ImplicitExplicitExportTest { private static Thread mainThread; @@ -162,13 +182,41 @@ } @Override - protected ToolSupportProvider getToolSupport() { + protected Visualizer getVisualizer() { + return null; + } + + @Override + protected List getASTProbers() { + return Collections.emptyList(); + } + + @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) { diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java --- a/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/vm/InitializationTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -22,25 +22,43 @@ */ package com.oracle.truffle.api.test.vm; -import static org.junit.Assert.*; +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.*; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; -import com.oracle.truffle.api.*; +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; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; +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.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.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; /** * Bug report validating test. @@ -90,13 +108,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 { @@ -118,34 +140,7 @@ @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; - } - - @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); + return new ANodeWrapper(this); } Object constant() { @@ -154,6 +149,36 @@ } + private static class ANodeWrapper extends ANode implements ProbeNode.WrapperNode { + @Child ANode child; + private ProbeNode probeNode; + + ANodeWrapper(ANode node) { + super(1); // dummy + this.child = node; + } + + @Override + 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(); + } + } + private abstract static class AbstractLanguage extends TruffleLanguage { } @@ -161,6 +186,22 @@ public static final class TestLanguage extends AbstractLanguage implements DebugSupportProvider { 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")); @@ -187,23 +228,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(); } @@ -213,6 +256,12 @@ } @Override + protected List getASTProbers() { + return Arrays.asList(prober); + } + + @SuppressWarnings("deprecation") + @Override public void enableASTProbing(ASTProber astProber) { throw new UnsupportedOperationException(); } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java --- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/SymbolInvokerImpl.java Mon Sep 14 22:59:51 2015 -0700 @@ -26,6 +26,7 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; +import com.oracle.truffle.api.impl.Accessor; import com.oracle.truffle.api.interop.*; import com.oracle.truffle.api.nodes.*; @@ -77,6 +78,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 { @@ -120,4 +126,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(); } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java --- a/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api.vm/src/com/oracle/truffle/api/vm/TruffleVM.java Mon Sep 14 22:59:51 2015 -0700 @@ -33,6 +33,7 @@ import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.java.JavaInterop; import com.oracle.truffle.api.source.*; + import java.io.*; import java.net.*; import java.nio.file.*; @@ -85,6 +86,7 @@ private final Writer out; private final EventConsumer[] handlers; private final Map globals; + private final Instrumenter instrumenter; private Debugger debugger; /** @@ -99,6 +101,7 @@ this.handlers = null; this.globals = null; this.executor = null; + this.instrumenter = null; } /** @@ -112,6 +115,7 @@ this.handlers = handlers; this.initThread = Thread.currentThread(); this.globals = new HashMap<>(globals); + this.instrumenter = SPI.createInstrumenter(); this.langs = new HashMap<>(); Enumeration en; try { @@ -445,7 +449,6 @@ try (Closeable d = SPI.executionStart(this, fillIn, s)) { TruffleLanguage langImpl = l.getImpl(); fillLang[0] = langImpl; - TruffleVM.findDebuggerSupport(langImpl); if (debugger == null) { debugger = fillIn[0]; } @@ -607,7 +610,7 @@ * delegates to * {@link JavaInterop#asJavaObject(java.lang.Class, com.oracle.truffle.api.interop.TruffleObject)} * just handles primitive types as well. - * + * * @param 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 @@ -773,6 +776,10 @@ String n = props.getProperty(prefix + "className"); try { impl = LanguageCache.find(n, loader()); + Instrumenter inst = TruffleVM.this.instrumenter; + for (ASTProber prober : SPI.getASTProbers(TruffleVM.this, impl.getClass())) { + inst.registerASTProber(prober); + } env = SPI.attachEnv(TruffleVM.this, impl, out, err, in); } catch (Exception ex) { throw new IllegalStateException("Cannot initialize " + getShortName() + " language with implementation " + n, ex); @@ -798,6 +805,16 @@ // Accessor helper methods // + TruffleLanguage findLanguage(Class languageClazz) { + for (Map.Entry entrySet : langs.entrySet()) { + Language languageDescription = entrySet.getValue(); + if (languageClazz.isInstance(languageDescription.impl)) { + return languageDescription.impl; + } + } + throw new IllegalStateException("Cannot find language " + languageClazz + " among " + langs); + } + TruffleLanguage findLanguage(Probe probe) { Class languageClazz = SPI.findLanguage(probe); for (Map.Entry entrySet : langs.entrySet()) { @@ -819,10 +836,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) { @@ -876,14 +889,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() { + return super.createInstrumenter(); + } + + @Override + protected List getASTProbers(Object vm, Class languageClass) { + return super.getASTProbers(vm, languageClass); + } + + @Override + protected Instrumenter getInstrumenter(Object obj) { + final TruffleVM vm = (TruffleVM) obj; + return vm.instrumenter; } @Override @@ -898,6 +929,12 @@ } @Override + protected TruffleLanguage findLanguageImpl(Object obj, Class 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); diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java Mon Sep 14 22:59:51 2015 -0700 @@ -24,17 +24,32 @@ */ package com.oracle.truffle.api; -import com.oracle.truffle.api.debug.*; -import com.oracle.truffle.api.impl.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.*; -import java.io.*; -import java.lang.annotation.*; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.Collections; +import java.util.List; 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.Instrument; +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; + /** * 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 @@ -173,11 +188,59 @@ */ 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); + + /** + * Gets the current specification for AST instrumentation for the language; empty if + * none. + */ + protected abstract List getASTProbers(); + + /** + * 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 Instrument#create(AdvancedInstrumentResultListener, AdvancedInstrumentRootFactory, Class, String) + * Advanced Instrument}. + * + * @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. @@ -332,6 +395,14 @@ } @Override + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class 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); } @@ -357,10 +428,18 @@ } @Override + protected List getASTProbers(Object vm, Class languageClass) { + TruffleLanguage impl = findLanguageImpl(vm, languageClass); + return impl.getASTProbers(); + } + + @SuppressWarnings("deprecation") + @Override protected ToolSupportProvider getToolSupport(TruffleLanguage l) { return l.getToolSupport(); } + @SuppressWarnings("deprecation") @Override protected DebugSupportProvider getDebugSupport(TruffleLanguage l) { return l.getDebugSupport(); diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/Debugger.java Mon Sep 14 22:59:51 2015 -0700 @@ -45,7 +45,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; @@ -58,6 +58,7 @@ } } + private final Instrumenter instrumenter; private final Object vm; private Source lastSource; @@ -77,6 +78,9 @@ void addWarning(String warning); } + private final BreakpointCallback breakpointCallback; + private final WarningLog warningLog; + /** * Implementation of line-oriented breakpoints. */ @@ -92,9 +96,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 @@ -102,7 +106,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) { @@ -110,7 +114,7 @@ } }; - final WarningLog warningLog = new WarningLog() { + warningLog = new WarningLog() { public void addWarning(String warning) { assert debugContext != null; @@ -122,10 +126,6 @@ this.tagBreaks = new TagBreakpointFactory(this, breakpointCallback, warningLog); } - Object vm() { - return vm; - } - /** * Sets a breakpoint to halt at a source line. * @@ -254,6 +254,7 @@ 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 @@ -269,15 +270,12 @@ */ @SuppressWarnings("rawtypes") AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Probe probe, String expr, AdvancedInstrumentResultListener resultListener) throws IOException { - try { - Class 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 languageClass = ACCESSOR.findLanguage(probe); + return ACCESSOR.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); + } + + Instrumenter getInstrumenter() { + return instrumenter; } /** @@ -401,7 +399,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) { @@ -415,7 +413,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) { @@ -434,8 +432,8 @@ @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - Probe.setAfterTagTrap(null); + instrumenter.setBeforeTagTrap(null); + instrumenter.setAfterTagTrap(null); } } @@ -458,7 +456,7 @@ @Override protected void setStrategy(final int stackDepth) { - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { @TruffleBoundary @Override @@ -476,7 +474,7 @@ @Override protected void unsetStrategy() { - Probe.setAfterTagTrap(null); + instrumenter.setAfterTagTrap(null); } } @@ -502,7 +500,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) { @@ -527,7 +525,7 @@ } }); - Probe.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { + instrumenter.setAfterTagTrap(new SyntaxTagTrap(CALL_TAG) { @TruffleBoundary @Override public void tagTrappedAt(Node node, MaterializedFrame mFrame) { @@ -548,8 +546,8 @@ @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); - Probe.setAfterTagTrap(null); + instrumenter.setBeforeTagTrap(null); + instrumenter.setAfterTagTrap(null); } } @@ -577,7 +575,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) { @@ -598,7 +596,7 @@ @Override protected void unsetStrategy() { - Probe.setBeforeTagTrap(null); + instrumenter.setBeforeTagTrap(null); } } @@ -798,7 +796,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]; } @@ -827,8 +826,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 languageClass, String expr, + AdvancedInstrumentResultListener resultListener) throws IOException { + return super.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); } @Override diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineBreakpointFactory.java Mon Sep 14 22:59:51 2015 -0700 @@ -67,7 +67,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) { @@ -90,6 +90,7 @@ } }; + private final Debugger debugger; private final BreakpointCallback breakpointCallback; private final WarningLog warningLog; @@ -111,17 +112,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) { diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/LineToProbesMap.java Mon Sep 14 22:59:51 2015 -0700 @@ -61,7 +61,7 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + getInstrumenter().addProbeListener(probeListener); return true; } @@ -72,7 +72,7 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); } /** diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/debug/TagBreakpointFactory.java Mon Sep 14 22:59:51 2015 -0700 @@ -47,10 +47,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}. *

- * 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. *

* Notes: *

    @@ -88,6 +88,7 @@ } }; + private final Debugger debugger; private final BreakpointCallback breakpointCallback; private final WarningLog warningLog; @@ -102,14 +103,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) { @@ -180,7 +180,7 @@ tagToBreakpoint.put(tag, breakpoint); - for (Probe probe : Probe.findProbesTaggedAs(tag)) { + for (Probe probe : debugger.getInstrumenter().findProbesTaggedAs(tag)) { breakpoint.attach(probe); } } else { diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java Mon Sep 14 22:59:51 2015 -0700 @@ -24,26 +24,45 @@ */ package com.oracle.truffle.api.impl; -import com.oracle.truffle.api.*; +import java.io.Closeable; +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.Collections; +import java.util.List; + +import com.oracle.truffle.api.Assumption; +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.*; -import com.oracle.truffle.api.instrument.*; +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.nodes.Node; import com.oracle.truffle.api.nodes.RootNode; -import com.oracle.truffle.api.source.*; -import java.io.*; -import java.lang.ref.Reference; -import java.lang.ref.WeakReference; +import com.oracle.truffle.api.source.Source; /** - * 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 TOOL; private static Accessor DEBUG; private static final ThreadLocal CURRENT_VM = new ThreadLocal<>(); @@ -64,11 +83,13 @@ return false; } + @SuppressWarnings("deprecation") @Override protected ToolSupportProvider getToolSupport() { - return null; + throw new UnsupportedOperationException(); } + @SuppressWarnings("deprecation") @Override protected DebugSupportProvider getDebugSupport() { return null; @@ -83,12 +104,39 @@ protected Object createContext(TruffleLanguage.Env env) { return null; } + + @Override + protected List getASTProbers() { + return Collections.emptyList(); + } + + @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); @@ -106,6 +154,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(); @@ -116,6 +169,11 @@ throw new IllegalStateException(); } DEBUG = this; + } else if (this.getClass().getSimpleName().endsWith("Tool")) { + if (TOOL != null) { + throw new IllegalStateException(); + } + TOOL = this; } else { if (SPI != null) { throw new IllegalStateException(); @@ -144,12 +202,23 @@ return API.languageGlobal(env); } - protected ToolSupportProvider getToolSupport(TruffleLanguage l) { - return API.getToolSupport(l); + @Deprecated + protected ToolSupportProvider getToolSupport(@SuppressWarnings("unused") TruffleLanguage l) { + throw new UnsupportedOperationException(); } - protected DebugSupportProvider getDebugSupport(TruffleLanguage l) { - return API.getDebugSupport(l); + @Deprecated + protected DebugSupportProvider getDebugSupport(@SuppressWarnings("unused") TruffleLanguage l) { + throw new UnsupportedOperationException(); + } + + protected List getASTProbers(Object vm, Class languageClass) { + return API.getASTProbers(vm, languageClass); + } + + protected AdvancedInstrumentRootFactory createAdvancedInstrumentRootFactory(Object vm, Class languageClass, String expr, AdvancedInstrumentResultListener resultListener) + throws IOException { + return API.createAdvancedInstrumentRootFactory(vm, languageClass, expr, resultListener); } protected Class findLanguage(RootNode n) { @@ -176,6 +245,36 @@ return SPI.findLanguage(vm, languageClass); } + protected TruffleLanguage findLanguageImpl(Object known, Class 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() { + return INSTRUMENT.createInstrumenter(); + } + private static Reference previousVM = new WeakReference<>(null); private static Assumption oneVM = Truffle.getRuntime().createAssumption(); @@ -207,7 +306,7 @@ return oneVM; } - @SuppressWarnings({"unchecked", "rawtypes"}) + @SuppressWarnings({"unchecked"}) static C findContext(Class type) { Env env = SPI.findLanguage(CURRENT_VM.get(), type); return (C) API.findContext(env); @@ -229,4 +328,8 @@ protected TruffleLanguage findLanguage(Env env) { return API.findLanguage(env); } + + protected void applyInstrumentation(Node node) { + INSTRUMENT.applyInstrumentation(node); + } } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ASTProber.java Mon Sep 14 22:59:51 2015 -0700 @@ -31,7 +31,7 @@ * not yet executed) AST. * * @see Probe - * @see Probe#addProbeListener(ProbeListener) + * @see Instrumenter#addProbeListener(ProbeListener) */ public interface ASTProber { @@ -40,6 +40,6 @@ * {@linkplain Probe Probes} to them. Ignore {@linkplain Node#isInstrumentable() * non-instrumentable} nodes. */ - void probeAST(Node node); + void probeAST(Instrumenter instrumenter, Node node); } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/InstrumentationTool.java Mon Sep 14 22:59:51 2015 -0700 @@ -24,13 +24,15 @@ */ package com.oracle.truffle.api.instrument; +import com.oracle.truffle.api.impl.Accessor; + /** * {@linkplain Instrument Instrumentation}-based tools that gather data during Guest Language * program execution. *

    * Tools share a common life cycle: *

      - *
    • A newly created tool is inert until {@linkplain #install() installed}.
    • + *
    • A newly created tool is inert until {@linkplain #install(Instrumenter) installed}.
    • *
    • An installed tool becomes enabled and immediately begins installing * {@linkplain Instrument instrumentation} on subsequently created ASTs and collecting data from * those instruments
    • @@ -83,6 +85,8 @@ private ToolState toolState = ToolState.UNINSTALLED; + private Instrumenter instrumenter; + protected InstrumentationTool() { } @@ -92,8 +96,11 @@ * * @throws IllegalStateException if the tool has previously been installed. */ - public final void install() { + public final void install(Instrumenter inst) { checkUninstalled(); + if (inst != null) { + this.instrumenter = inst; + } if (internalInstall()) { toolState = ToolState.ENABLED; } @@ -157,6 +164,13 @@ protected abstract void internalDispose(); + protected final Instrumenter getInstrumenter() { + if (instrumenter == null) { + instrumenter = ACCESSOR.getInstrumenter(null); + } + return instrumenter; + } + /** * Ensure that the tool is currently installed. * @@ -182,4 +196,14 @@ } } + static final class AccessorTool extends Accessor { + + @Override + protected Instrumenter getInstrumenter(Object vm) { + return super.getInstrumenter(null); + } + } + + static final AccessorTool ACCESSOR = new AccessorTool(); + } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrumenter.java --- /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 14 22:59:51 2015 -0700 @@ -0,0 +1,347 @@ +/* + * 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.List; + +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.ProbeNode.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; + +/** + * 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 final List astProbers = new ArrayList<>(); + + private final List probeListeners = new ArrayList<>(); + + /** + * All Probes that have been created. + */ + private final List> 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() { + } + + /** + * 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 Node#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 + */ + @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 ? "" : sourceSection.getShortDescription(); + trace("PROBE FOUND %s %s %s", "Probe@", location, probe.getTagsDescription()); + } + return wrapper.getProbe(); + } + + if (!(node.isInstrumentable())) { + throw new ProbeException(ProbeFailure.Reason.NOT_INSTRUMENTABLE, parent, node, null); + } + + // Create a new wrapper/probe with this node as its child. + final WrapperNode wrapper = node.createWrapperNode(); + + 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 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.insertProbe(probeNode); + node.replace(wrapperNode); + if (TRACE) { + final String location = sourceSection == null ? "" : 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 findProbesTaggedAs(SyntaxTag tag) { + final List taggedProbes = new ArrayList<>(); + for (WeakReference 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 "before tag trap"; there can be no more than one in effect. + *
        + *
      • The before-trap triggers a callback just before execution + * reaches any {@link Probe} (either existing or subsequently created) + * with the specified {@link SyntaxTag}.
      • + *
      • Setting the before-trap to {@code null} clears an existing before-trap.
      • + *
      • Setting a non{@code -null} before-trap when one is already set clears the previously set + * before-trap.
      • + *
      + * + * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set. + */ + public void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) { + beforeTagTrap = newBeforeTagTrap; + for (WeakReference 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 "after tag trap"; there can be no more than one in effect. + *
        + *
      • The after-trap triggers a callback just after execution leaves + * any {@link Probe} (either existing or subsequently created) with + * the specified {@link SyntaxTag}.
      • + *
      • Setting the after-trap to {@code null} clears an existing after-trap.
      • + *
      • Setting a non{@code -null} after-trap when one is already set clears the previously set + * after-trap.
      • + *
      + * + * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set. + */ + public void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) { + afterTagTrap = newAfterTagTrap; + for (WeakReference ref : probes) { + final Probe probe = ref.get(); + if (probe != null) { + probe.notifyTrapsChanged(); + } + } + } + + /** + * Enables instrumentation at selected nodes in all subsequently constructed ASTs. + */ + public void registerASTProber(ASTProber prober) { + astProbers.add(prober); + } + + public void unregisterASTProber(ASTProber prober) { + astProbers.remove(prober); + } + + @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; + } + + /** + * 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() { + return new Instrumenter(); + } + + @SuppressWarnings("rawtypes") + @Override + protected Class findLanguage(RootNode n) { + return super.findLanguage(n); + } + + @SuppressWarnings("rawtypes") + @Override + protected Class findLanguage(Probe probe) { + return probe.getLanguage(); + } + + @Override + protected void applyInstrumentation(Node node) { + super.getInstrumenter(null).applyInstrumentation(node); + } + } + + static final AccessorInstrument ACCESSOR = new AccessorInstrument(); + +} diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Mon Sep 14 22:59:51 2015 -0700 @@ -24,19 +24,21 @@ */ package com.oracle.truffle.api.instrument; -import java.io.*; -import java.lang.ref.*; -import java.util.*; +import java.io.PrintStream; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; -import com.oracle.truffle.api.*; +import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.impl.Accessor; +import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; -import com.oracle.truffle.api.nodes.*; -import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.utilities.*; - -//TODO (mlvdv) these statics should not be global. Move them to some kind of context. +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 binding between: @@ -68,13 +70,14 @@ * 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. * - *
    • The method {@link Node#probe()} creates a Probe on an AST Node; redundant calls return the - * same Probe.
    • + *
    • The method {@link Instrumenter#probe(Node)} creates a Probe on an AST Node; redundant calls + * return the same Probe.
    • * *
    • 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.
    • + * 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. * *
    • 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 @@ -114,173 +117,7 @@ } } - private static final List astProbers = new ArrayList<>(); - - private static final List probeListeners = new ArrayList<>(); - - /** - * All Probes that have been created. - */ - private static final List> 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 findProbesTaggedAs(SyntaxTag tag) { - final List taggedProbes = new ArrayList<>(); - for (WeakReference 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 "before tag trap"; there can be no more than one in effect. - *
        - *
      • The before-trap triggers a callback just before execution - * reaches any {@link Probe} (either existing or subsequently created) - * with the specified {@link SyntaxTag}.
      • - *
      • Setting the before-trap to {@code null} clears an existing before-trap.
      • - *
      • Setting a non{@code -null} before-trap when one is already set clears the previously set - * before-trap.
      • - *
      - * - * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set. - */ - public static void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) { - beforeTagTrap = newBeforeTagTrap; - for (WeakReference 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 "after tag trap"; there can be no more than one in effect. - *
        - *
      • The after-trap triggers a callback just after execution leaves - * any {@link Probe} (either existing or subsequently created) with - * the specified {@link SyntaxTag}.
      • - *
      • Setting the after-trap to {@code null} clears an existing after-trap.
      • - *
      • Setting a non{@code -null} after-trap when one is already set clears the previously set - * after-trap.
      • - *
      - * - * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set. - */ - public static void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) { - afterTagTrap = newAfterTagTrap; - for (WeakReference ref : probes) { - final Probe probe = ref.get(); - if (probe != null) { - probe.notifyTrapsChanged(); - } - } - } - + private final Instrumenter instrumenter; private final SourceSection sourceSection; private final ArrayList tags = new ArrayList<>(); private final List> probeNodeClones = new ArrayList<>(); @@ -306,17 +143,10 @@ /** * Intended for use only by {@link ProbeNode}. */ - Probe(Class l, ProbeNode probeNode, SourceSection sourceSection) { + Probe(Instrumenter instrumenter, Class 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 ? "" : sourceSection.getShortDescription(); - trace("ADDED %s %s %s", "Probe@", location, getTagsDescription()); - } - for (ProbeListener listener : probeListeners) { - listener.newProbeInserted(this); - } this.language = l; } @@ -344,16 +174,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; @@ -432,23 +262,27 @@ /** * Gets the currently active before {@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 after {@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 getLanguage() { + return language; } /** @@ -469,13 +303,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 = ""; @@ -487,18 +323,4 @@ sb.append("]"); return sb.toString(); } - - static final class AccessorInstrument extends Accessor { - @Override - protected Class findLanguage(RootNode n) { - return super.findLanguage(n); - } - - @Override - protected Class findLanguage(Probe probe) { - return probe.language; - } - } - - static final AccessorInstrument ACCESSOR = new AccessorInstrument(); } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeException.java Mon Sep 14 22:59:51 2015 -0700 @@ -28,7 +28,8 @@ import com.oracle.truffle.api.nodes.*; /** - * 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. *

      * Language and tool implementations should ensure that clients of tools never see this exception. */ diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeFailure.java Mon Sep 14 22:59:51 2015 -0700 @@ -76,7 +76,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 diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeListener.java Mon Sep 14 22:59:51 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)}. *

      * 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. */ diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.java Mon Sep 14 22:59:51 2015 -0700 @@ -26,12 +26,12 @@ 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.*; +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.*; -import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.nodes.NodeCost; +import com.oracle.truffle.api.nodes.NodeInfo; /** * Implementation class & interface for enabling the attachment of {@linkplain Probe Probes} to @@ -114,20 +114,6 @@ 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 l = Probe.ACCESSOR.findLanguage(wrapper.getChild().getRootNode()); - probeNode.probe = new Probe(l, probeNode, sourceSection); // package private access - wrapper.insertProbe(probeNode); - return probeNode.probe; - } - // Never changed once set. @CompilationFinal Probe probe = null; /** diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Mon Sep 14 22:59:51 2015 -0700 @@ -24,17 +24,28 @@ */ package com.oracle.truffle.api.nodes; -import java.lang.annotation.*; -import java.util.*; -import java.util.concurrent.*; +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.*; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; +import com.oracle.truffle.api.ReplaceObserver; +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.*; +import com.oracle.truffle.api.instrument.Instrument; +import com.oracle.truffle.api.instrument.Probe; import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; -import com.oracle.truffle.api.source.*; -import com.oracle.truffle.api.utilities.*; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.api.utilities.JSONHelper; /** * Abstract base class for all Truffle nodes. @@ -436,57 +447,6 @@ } /** - * 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 @@ -563,6 +523,10 @@ return ""; } + protected void applyInstrumentation(Node node) { + ACCESSOR.applyInstrumentation(node); + } + private static final Object GIL = new Object(); private static final ThreadLocal IN_ATOMIC_BLOCK = new ThreadLocal() { @@ -592,8 +556,13 @@ protected Class 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(); } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/RootNode.java Mon Sep 14 22:59:51 2015 -0700 @@ -180,9 +180,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. *

      * If this is not done, then the AST will not be subject to debugging or any other * instrumentation-supported tooling. @@ -190,7 +191,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() { } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java --- a/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/instrument/SLInstrumentTestRunner.java Mon Sep 14 22:59:51 2015 -0700 @@ -40,27 +40,46 @@ */ package com.oracle.truffle.sl.test.instrument; -import java.io.*; -import java.nio.charset.*; -import java.nio.file.*; -import java.nio.file.attribute.*; -import java.util.*; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.nio.charset.Charset; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; -import org.junit.*; -import org.junit.internal.*; -import org.junit.runner.*; -import org.junit.runner.manipulation.*; -import org.junit.runner.notification.*; -import org.junit.runners.*; -import org.junit.runners.model.*; +import org.junit.Assert; +import org.junit.internal.TextListener; +import org.junit.runner.Description; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.manipulation.Filter; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runner.notification.Failure; +import org.junit.runner.notification.RunNotifier; +import org.junit.runners.ParentRunner; +import org.junit.runners.model.InitializationError; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.instrument.impl.*; +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.StandardSyntaxTag; +import com.oracle.truffle.api.instrument.impl.DefaultSimpleInstrumentListener; import com.oracle.truffle.api.source.Source; -import com.oracle.truffle.api.vm.*; -import com.oracle.truffle.sl.nodes.instrument.*; -import com.oracle.truffle.sl.nodes.local.*; -import com.oracle.truffle.sl.test.*; +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; /** @@ -80,7 +99,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; @@ -101,16 +120,12 @@ private final List 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); } } @@ -222,18 +237,23 @@ ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintWriter printer = new PrintWriter(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().stdIn(new BufferedReader(new StringReader(testCase.testInput))).stdOut(printer).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(printer); probe.attach(Instrument.create(slPrintAssigmentValueListener, "SL print assignment value")); } @@ -250,13 +270,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); diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLLanguage.java Mon Sep 14 22:59:51 2015 -0700 @@ -58,12 +58,14 @@ import com.oracle.truffle.sl.nodes.local.*; import com.oracle.truffle.sl.parser.*; import com.oracle.truffle.sl.runtime.*; + 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.ArrayList; import java.util.Collections; import java.util.List; @@ -142,7 +144,7 @@ */ /* - * + * *

      Tools:
      The use of some of Truffle's support for developer tools (based on the * Truffle Instrumentation Framework) are demonstrated in this file, for example:

      • a * {@linkplain NodeExecCounter counter for node executions}, tabulated by node type; and
      • a @@ -154,10 +156,11 @@ public final class SLLanguage extends TruffleLanguage { private static List> builtins = Collections.emptyList(); private static Visualizer visualizer = new SLDefaultVisualizer(); - private static ASTProber registeredASTProber; // non-null if prober already registered - private DebugSupportProvider debugSupport; + private List astProbers; private SLLanguage() { + this.astProbers = new ArrayList<>(); + astProbers.add(new SLStandardASTProber()); } public static final SLLanguage INSTANCE = new SLLanguage(); @@ -210,6 +213,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"); @@ -432,28 +436,56 @@ } @Override - protected ToolSupportProvider getToolSupport() { - return getDebugSupport(); + protected Visualizer getVisualizer() { + if (visualizer == null) { + visualizer = new SLDefaultVisualizer(); + } + return visualizer; + } + + @Override + protected List getASTProbers() { + return astProbers; + } + + @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(); @@ -485,46 +517,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"); - } - - } - } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Mon Sep 14 22:59:51 2015 -0700 @@ -41,13 +41,14 @@ package com.oracle.truffle.sl.nodes; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.nodes.*; +import com.oracle.truffle.api.frame.FrameDescriptor; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.sl.SLLanguage; -import com.oracle.truffle.sl.builtins.*; -import com.oracle.truffle.sl.nodes.controlflow.*; -import com.oracle.truffle.sl.runtime.*; +import com.oracle.truffle.sl.builtins.SLBuiltinNode; +import com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode; +import com.oracle.truffle.sl.runtime.SLContext; /** * The root of all SL execution trees. It is a Truffle requirement that the tree root extends the @@ -97,7 +98,7 @@ @Override public void applyInstrumentation() { - Probe.applyASTProbers(bodyNode); + super.applyInstrumentation(bodyNode); } @Override diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java --- a/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/instrument/SLStandardASTProber.java Mon Sep 14 22:59:51 2015 -0700 @@ -52,39 +52,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} - *

        - * 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); + }); } } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java --- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/CoverageTrackerTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -36,10 +36,11 @@ public class CoverageTrackerTest { @Test - public void testNoExecution() { + public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); 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); @@ -52,37 +53,40 @@ } @Test - public void testToolCreatedTooLate() { - final RootNode expr13rootNode = createExpr13TestRootNode(); + public void testToolCreatedTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); + final RootNode expr13rootNode = TestNodes.createExpr13TestRootNode(instrumenter); final CoverageTracker tool = new CoverageTracker(); - tool.install(); + tool.install(instrumenter); assertEquals(13, expr13rootNode.execute(null)); assertTrue(tool.getCounts().isEmpty()); tool.dispose(); } @Test - public void testToolInstalledcTooLate() { + public void testToolInstalledcTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); final CoverageTracker tool = new CoverageTracker(); - final RootNode expr13rootNode = createExpr13TestRootNode(); - tool.install(); + final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter); + tool.install(instrumenter); assertEquals(13, expr13rootNode.execute(null)); assertTrue(tool.getCounts().isEmpty()); tool.dispose(); } @Test - public void testCountingCoverage() { + public void testCountingCoverage() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); final CoverageTracker tool = new CoverageTracker(); - tool.install(); - final RootNode expr13rootNode = createExpr13TestRootNode(); + tool.install(instrumenter); + final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter); // Not probed yet. assertEquals(13, expr13rootNode.execute(null)); assertTrue(tool.getCounts().isEmpty()); final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); + final Probe probe = instrumenter.probe(addNode); // Probed but not tagged yet. assertEquals(13, expr13rootNode.execute(null)); diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java --- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/LineToProbesMapTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -37,15 +37,16 @@ public class LineToProbesMapTest { @Test - public void testToolCreatedTooLate() { - final RootNode expr13rootNode = createExpr13TestRootNode(); + public void testToolCreatedTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); + final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter); final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); + final Probe probe = instrumenter.probe(addNode); final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); assertEquals(lineLocation, expr13Line2); final LineToProbesMap tool = new LineToProbesMap(); - tool.install(); + tool.install(instrumenter); assertNull(tool.findFirstProbe(expr13Line1)); assertNull(tool.findFirstProbe(expr13Line2)); @@ -53,16 +54,17 @@ } @Test - public void testToolInstalledTooLate() { + public void testToolInstalledTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); final LineToProbesMap tool = new LineToProbesMap(); - final RootNode expr13rootNode = createExpr13TestRootNode(); + final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter); final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); + final Probe probe = instrumenter.probe(addNode); final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); assertEquals(lineLocation, expr13Line2); - tool.install(); + tool.install(instrumenter); assertNull(tool.findFirstProbe(expr13Line1)); assertNull(tool.findFirstProbe(expr13Line2)); @@ -70,13 +72,14 @@ } @Test - public void testMapping() { + public void testMapping() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); final LineToProbesMap tool = new LineToProbesMap(); - tool.install(); + tool.install(instrumenter); - final RootNode expr13rootNode = createExpr13TestRootNode(); + final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter); final Node addNode = expr13rootNode.getChildren().iterator().next(); - final Probe probe = addNode.probe(); + final Probe probe = instrumenter.probe(addNode); final LineLocation lineLocation = probe.getProbedSourceSection().getLineLocation(); assertEquals(lineLocation, expr13Line2); diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java --- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/NodeExecCounterTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -40,10 +40,11 @@ public class NodeExecCounterTest { @Test - public void testNoExecution() { + public void testNoExecution() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); 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); @@ -56,30 +57,33 @@ } @Test - public void testToolCreatedTooLate() { - final CallTarget expr13callTarget = createExpr13TestCallTarget(); + public void testToolCreatedTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); + final CallTarget expr13callTarget = createExpr13TestCallTarget(instrumenter); final NodeExecCounter tool = new NodeExecCounter(); - tool.install(); + tool.install(instrumenter); assertEquals(13, expr13callTarget.call()); assertEquals(tool.getCounts().length, 0); tool.dispose(); } @Test - public void testToolInstalledcTooLate() { + public void testToolInstalledcTooLate() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); final NodeExecCounter tool = new NodeExecCounter(); - final CallTarget expr13callTarget = createExpr13TestCallTarget(); - tool.install(); + final CallTarget expr13callTarget = createExpr13TestCallTarget(instrumenter); + tool.install(instrumenter); assertEquals(13, expr13callTarget.call()); assertEquals(tool.getCounts().length, 0); tool.dispose(); } @Test - public void testCountingAll() { + public void testCountingAll() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); final NodeExecCounter tool = new NodeExecCounter(); - tool.install(); - final CallTarget expr13callTarget = createExpr13TestCallTarget(); + tool.install(instrumenter); + final CallTarget expr13callTarget = createExpr13TestCallTarget(instrumenter); // execute once assertEquals(13, expr13callTarget.call()); @@ -121,17 +125,18 @@ } @Test - public void testCountingTagged() { + public void testCountingTagged() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); final NodeExecCounter tool = new NodeExecCounter(StandardSyntaxTag.STATEMENT); - tool.install(); - final RootNode expr13rootNode = createExpr13TestRootNode(); + tool.install(instrumenter); + final RootNode expr13rootNode = createExpr13TestRootNode(instrumenter); // 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(); + final Probe probe = instrumenter.probe(addNode); // Probed but not tagged yet. assertEquals(13, expr13rootNode.execute(null)); diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java --- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TestNodes.java Mon Sep 14 22:59:51 2015 -0700 @@ -24,12 +24,15 @@ */ package com.oracle.truffle.tools.test; +import java.lang.reflect.*; + import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.instrument.*; import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.api.source.*; +import com.oracle.truffle.api.vm.TruffleVM; /** * Nodes and an {@linkplain CallTarget executable ASTs} for testing. @@ -46,21 +49,29 @@ /** * An executable addition expression that evaluates to 13. */ - static CallTarget createExpr13TestCallTarget() { - final RootNode rootNode = createExpr13TestRootNode(); + static CallTarget createExpr13TestCallTarget(Instrumenter instrumenter) { + final RootNode rootNode = createExpr13TestRootNode(instrumenter); return Truffle.getRuntime().createCallTarget(rootNode); } /** * Root holding an addition expression that evaluates to 13. */ - static RootNode createExpr13TestRootNode() { + static RootNode createExpr13TestRootNode(Instrumenter instrumenter) { final TestLanguageNode ast = createExpr13AST(); - final TestRootNode rootNode = new TestRootNode(ast); + final TestRootNode rootNode = new TestRootNode(ast, instrumenter); rootNode.adoptChildren(); return rootNode; } + 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; + } + /** * Addition expression that evaluates to 13, with faked source attribution. */ @@ -156,13 +167,16 @@ static class TestRootNode extends RootNode { @Child private TestLanguageNode body; + private 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) { + public TestRootNode(TestLanguageNode body, Instrumenter instrumenter) { super(TruffleLanguage.class, null, null); + this.instrumenter = instrumenter; this.body = body; } @@ -178,7 +192,14 @@ @Override public void applyInstrumentation() { - Probe.applyASTProbers(body); + 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("TestNodes"); + } } } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java --- a/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.tools.test/src/com/oracle/truffle/tools/test/TruffleToolTest.java Mon Sep 14 22:59:51 2015 -0700 @@ -36,10 +36,11 @@ public class TruffleToolTest { @Test - public void testEmptyLifeCycle() { + public void testEmptyLifeCycle() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); assertFalse(tool.isEnabled()); - tool.install(); + tool.install(instrumenter); assertTrue(tool.isEnabled()); tool.reset(); assertTrue(tool.isEnabled()); @@ -74,40 +75,45 @@ } @Test(expected = IllegalStateException.class) - public void testAlreadyInstalled() { + public void testAlreadyInstalled() throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { + final Instrumenter instrumenter = TestNodes.createInstrumenter(); 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 = TestNodes.createInstrumenter(); 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 = TestNodes.createInstrumenter(); 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 = TestNodes.createInstrumenter(); 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 = TestNodes.createInstrumenter(); final DummyTruffleTool tool = new DummyTruffleTool(); - tool.install(); + tool.install(instrumenter); tool.dispose(); tool.dispose(); } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java --- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/CoverageTracker.java Mon Sep 14 22:59:51 2015 -0700 @@ -102,7 +102,7 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + getInstrumenter().addProbeListener(probeListener); return true; } @@ -113,7 +113,7 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); for (Instrument instrument : instruments) { instrument.dispose(); } diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java --- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/LineToProbesMap.java Mon Sep 14 22:59:51 2015 -0700 @@ -61,7 +61,7 @@ @Override protected boolean internalInstall() { - Probe.addProbeListener(probeListener); + getInstrumenter().addProbeListener(probeListener); return true; } @@ -72,7 +72,7 @@ @Override protected void internalDispose() { - Probe.removeProbeListener(probeListener); + getInstrumenter().removeProbeListener(probeListener); } /** diff -r 1d804d691dc7 -r 3aad794eec0e truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java --- a/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java Mon Sep 07 17:54:35 2015 +0200 +++ b/truffle/com.oracle.truffle.tools/src/com/oracle/truffle/tools/NodeExecCounter.java Mon Sep 14 22:59:51 2015 -0700 @@ -155,7 +155,7 @@ * Create a per node-type execution counting tool for all nodes in subsequently created ASTs. */ public NodeExecCounter() { - this.countingTag = null; + this(null); } /** @@ -170,10 +170,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; } @@ -187,10 +187,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(); @@ -274,24 +274,27 @@ /** * 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 (node.isInstrumentable()) { - try { - final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter"); - instruments.add(instrument); - node.probe().attach(instrument); - } catch (ProbeException ex) { - failures.add(ex.getFailure()); + if (node.isInstrumentable()) { + try { + final Instrument instrument = Instrument.create(instrumentListener, "NodeExecCounter"); + instruments.add(instrument); + instrumenter.probe(node).attach(instrument); + } catch (ProbeException ex) { + failures.add(ex.getFailure()); + } + } + return true; } - } - return true; - } - public void probeAST(Node node) { - node.accept(this); + }); } }