Mercurial > hg > truffle
diff graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java @ 19818:907128d02b31
Truffle/Instrumentation: For clients of Instrumentation, replace the TruffleEventListener interface with two: InstrumentListener, and ASTInstrumentListener. The former is simple, completely Truffle-safe (can't affect Truffle execution), and designed for simple tools. The latter is similar to the previous interface.
author | Michael Van De Vanter <michael.van.de.vanter@oracle.com> |
---|---|
date | Thu, 12 Mar 2015 18:03:05 -0700 |
parents | b5467bb34b24 |
children | 1d6a7ea5de59 |
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Thu Mar 12 15:02:01 2015 -0700 +++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java Thu Mar 12 18:03:05 2015 -0700 @@ -83,7 +83,7 @@ }; @Test - public void testBasicInstrumentation() { + public void testInstrumentationStructure() { // Create a simple addition AST final TruffleRuntime runtime = Truffle.getRuntime(); final TestValueNode leftValueNode = new TestValueNode(6); @@ -117,7 +117,7 @@ assertEquals(13, callTarget1.call()); // Probe the addition node - final Probe probe = addNode.probe(); + addNode.probe(); // Check the modified tree structure assertEquals(addNode, leftValueNode.getParent()); @@ -153,103 +153,129 @@ // Check that the "probed" AST still executes correctly assertEquals(13, callTarget1.call()); + } + + @Test + public void testListeners() { + + // 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); + + // 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(); + + // Check instrumentation with the simplest kind of counters. + // They should all be removed when the check is finished. + checkCounters(probe, callTarget, rootNode, new TestInstrumentCounter(), new TestInstrumentCounter(), new TestInstrumentCounter()); + + // Now try with the more complex flavor of listener + checkCounters(probe, callTarget, rootNode, new TestASTInstrumentCounter(), new TestASTInstrumentCounter(), new TestASTInstrumentCounter()); + + } + + private static void checkCounters(Probe probe, CallTarget callTarget, RootNode rootNode, TestCounter counterA, TestCounter counterB, TestCounter counterC) { + // Attach a counting instrument to the probe - final TestCounter counterA = new TestCounter(); counterA.attach(probe); // Attach a second counting instrument to the probe - final TestCounter counterB = new TestCounter(); counterB.attach(probe); // Run it again and check that the two instruments are working - assertEquals(13, callTarget1.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 1); - assertEquals(counterB.leaveCount, 1); + assertEquals(13, callTarget.call()); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 1); + assertEquals(counterB.leaveCount(), 1); - // Remove counterA and check the "instrument chain" + // Remove counterA counterA.dispose(); - iterator = probeNode.getChildren().iterator(); // Run it again and check that instrument B is still working but not A - assertEquals(13, callTarget1.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 2); - assertEquals(counterB.leaveCount, 2); + assertEquals(13, callTarget.call()); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 2); + assertEquals(counterB.leaveCount(), 2); // Simulate a split by cloning the AST - final CallTarget callTarget2 = runtime.createCallTarget((TestRootNode) rootNode.copy()); + final CallTarget callTarget2 = Truffle.getRuntime().createCallTarget((TestRootNode) rootNode.copy()); // Run the clone and check that instrument B is still working but not A assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 3); - assertEquals(counterB.leaveCount, 3); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 3); + assertEquals(counterB.leaveCount(), 3); // Run the original and check that instrument B is still working but not A assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 4); - assertEquals(counterB.leaveCount, 4); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 4); + assertEquals(counterB.leaveCount(), 4); // Attach a second instrument to the probe - final TestCounter counterC = new TestCounter(); counterC.attach(probe); // Run the original and check that instruments B,C working but not A - assertEquals(13, callTarget1.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 5); - assertEquals(counterB.leaveCount, 5); - assertEquals(counterC.enterCount, 1); - assertEquals(counterC.leaveCount, 1); + assertEquals(13, callTarget.call()); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 5); + assertEquals(counterB.leaveCount(), 5); + assertEquals(counterC.enterCount(), 1); + assertEquals(counterC.leaveCount(), 1); // Run the clone and check that instruments B,C working but not A assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 6); - assertEquals(counterB.leaveCount, 6); - assertEquals(counterC.enterCount, 2); - assertEquals(counterC.leaveCount, 2); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 6); + assertEquals(counterB.leaveCount(), 6); + assertEquals(counterC.enterCount(), 2); + assertEquals(counterC.leaveCount(), 2); // Remove instrumentC counterC.dispose(); // Run the original and check that instrument B working but not A,C - assertEquals(13, callTarget1.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 7); - assertEquals(counterB.leaveCount, 7); - assertEquals(counterC.enterCount, 2); - assertEquals(counterC.leaveCount, 2); + assertEquals(13, callTarget.call()); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 7); + assertEquals(counterB.leaveCount(), 7); + assertEquals(counterC.enterCount(), 2); + assertEquals(counterC.leaveCount(), 2); // Run the clone and check that instrument B working but not A,C assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 8); - assertEquals(counterB.leaveCount, 8); - assertEquals(counterC.enterCount, 2); - assertEquals(counterC.leaveCount, 2); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 8); + assertEquals(counterB.leaveCount(), 8); + assertEquals(counterC.enterCount(), 2); + assertEquals(counterC.leaveCount(), 2); // Remove instrumentB counterB.dispose(); // Run both the original and clone, check that no instruments working - assertEquals(13, callTarget1.call()); + assertEquals(13, callTarget.call()); assertEquals(13, callTarget2.call()); - assertEquals(counterA.enterCount, 1); - assertEquals(counterA.leaveCount, 1); - assertEquals(counterB.enterCount, 8); - assertEquals(counterB.leaveCount, 8); - assertEquals(counterC.enterCount, 2); - assertEquals(counterC.leaveCount, 2); + assertEquals(counterA.enterCount(), 1); + assertEquals(counterA.leaveCount(), 1); + assertEquals(counterB.enterCount(), 8); + assertEquals(counterB.leaveCount(), 8); + assertEquals(counterC.enterCount(), 2); + assertEquals(counterC.leaveCount(), 2); + } @Test @@ -313,131 +339,6 @@ } - @Test - public void testProbeLite() { - - // Use the "lite-probing" option, limited to a single pass of - // probing and a single Instrument at each probed node. This - // particular test uses a shared event listener at every - // lite-probed node. - final TestEventListener listener = new TestEventListener(); - - TestASTLiteProber astLiteProber = new TestASTLiteProber(listener); - Probe.registerASTProber(astLiteProber); - - // 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); - - // 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); - - // Check that the instrument is working as expected. - assertEquals(0, listener.counter); - callTarget.call(); - assertEquals(2, listener.counter); - - // Check that you can't probe a node that's already received a probeLite() call - try { - leftValueNode.probe(); - fail(); - } catch (IllegalStateException e) { - } - - try { - rightValueNode.probe(); - fail(); - } catch (IllegalStateException e) { - } - - // Check tree structure - assertTrue(leftValueNode.getParent() instanceof TestLanguageWrapperNode); - assertTrue(rightValueNode.getParent() instanceof TestLanguageWrapperNode); - TestLanguageWrapperNode leftWrapper = (TestLanguageWrapperNode) leftValueNode.getParent(); - TestLanguageWrapperNode rightWrapper = (TestLanguageWrapperNode) rightValueNode.getParent(); - assertEquals(addNode, leftWrapper.getParent()); - assertEquals(addNode, rightWrapper.getParent()); - Iterator<Node> iterator = addNode.getChildren().iterator(); - assertEquals(leftWrapper, iterator.next()); - assertEquals(rightWrapper, iterator.next()); - assertFalse(iterator.hasNext()); - assertEquals(rootNode, addNode.getParent()); - iterator = rootNode.getChildren().iterator(); - assertEquals(addNode, iterator.next()); - assertFalse(iterator.hasNext()); - - // Check that you can't get a probe on the wrappers because they were "lite-probed" - try { - leftWrapper.getProbe(); - fail(); - } catch (IllegalStateException e) { - } - try { - rightWrapper.getProbe(); - fail(); - } catch (IllegalStateException e) { - } - - // Check that you can't probe the wrappers - try { - leftWrapper.probe(); - fail(); - } catch (ProbeException e) { - assertEquals(e.getFailure().getReason(), ProbeFailure.Reason.WRAPPER_NODE); - } - try { - rightWrapper.probe(); - fail(); - } catch (ProbeException e) { - assertEquals(e.getFailure().getReason(), ProbeFailure.Reason.WRAPPER_NODE); - } - try { - leftWrapper.probeLite(null); - fail(); - } catch (ProbeException e) { - assertEquals(e.getFailure().getReason(), ProbeFailure.Reason.WRAPPER_NODE); - } - try { - rightWrapper.probeLite(null); - fail(); - } catch (ProbeException e) { - assertEquals(e.getFailure().getReason(), ProbeFailure.Reason.WRAPPER_NODE); - } - - // Use reflection to check that each WrapperNode has a ProbeLiteNode with a - // SimpleEventListener - try { - java.lang.reflect.Field probeNodeField = leftWrapper.getClass().getDeclaredField("probeNode"); - - // cheat: probeNode is private, so we change it's accessibility at runtime - probeNodeField.setAccessible(true); - ProbeNode probeNode = (ProbeNode) probeNodeField.get(leftWrapper); - - // hack: Since ProbeLiteNode is not visible, we do a string compare here - assertTrue(probeNode.getClass().toString().endsWith("ProbeLiteNode")); - - // Now we do the same to check the type of the eventListener in ProbeLiteNode - java.lang.reflect.Field eventListenerField = probeNode.getClass().getDeclaredField("eventListener"); - eventListenerField.setAccessible(true); - TruffleEventListener eventListener = (TruffleEventListener) eventListenerField.get(probeNode); - assertTrue(eventListener instanceof SimpleEventListener); - - // Reset accessibility - probeNodeField.setAccessible(false); - eventListenerField.setAccessible(false); - - } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { - fail(); - } - - Probe.unregisterASTProber(astLiteProber); - - } - private abstract class TestLanguageNode extends Node { public abstract Object execute(VirtualFrame vFrame); @@ -578,28 +479,51 @@ } } + private interface TestCounter { + + int enterCount(); + + int leaveCount(); + + void attach(Probe probe); + + void dispose(); + + } + /** - * A counter for the number of times execution enters and leaves a probed AST node. + * A counter for the number of times execution enters and leaves a probed AST node, using the + * simplest kind of listener. */ - private class TestCounter { + private class TestInstrumentCounter implements TestCounter { public int enterCount = 0; public int leaveCount = 0; public final Instrument instrument; - public TestCounter() { - instrument = Instrument.create(new SimpleEventListener() { + public TestInstrumentCounter() { + this.instrument = Instrument.create(new SimpleInstrumentListener() { @Override - public void enter(Node node, VirtualFrame vFrame) { + public void enter(Probe probe) { enterCount++; } @Override - public void returnAny(Node node, VirtualFrame vFrame) { + public void returnAny(Probe probe) { leaveCount++; } + }, "Instrumentation Test Counter"); + + } + + public int enterCount() { + return enterCount; + } + + public int leaveCount() { + return leaveCount; } public void attach(Probe probe) { @@ -609,7 +533,50 @@ public void dispose() { instrument.dispose(); } + } + /** + * A counter for the number of times execution enters and leaves a probed AST node, using the + * simplest kind of listener. + */ + private class TestASTInstrumentCounter implements TestCounter { + + public int enterCount = 0; + public int leaveCount = 0; + public final Instrument instrument; + + public TestASTInstrumentCounter() { + this.instrument = Instrument.create(new SimpleASTInstrumentListener() { + + @Override + public void enter(Probe probe, Node node, VirtualFrame vFrame) { + enterCount++; + } + + @Override + public void returnAny(Probe probe, Node node, VirtualFrame vFrame) { + leaveCount++; + } + + }, "Instrumentation Test Counter"); + + } + + public int enterCount() { + return enterCount; + } + + public int leaveCount() { + return leaveCount; + } + + public void attach(Probe probe) { + probe.attach(instrument); + } + + public void dispose() { + instrument.dispose(); + } } /** @@ -641,38 +608,28 @@ } /** - * "lite-probes" every value node with a shared event listener. + * Counts the number of "enter" events at probed nodes using the simplest AST listener. */ - private static final class TestASTLiteProber implements NodeVisitor, ASTProber { - private final TruffleEventListener eventListener; - - public TestASTLiteProber(SimpleEventListener simpleEventListener) { - this.eventListener = simpleEventListener; - } - - public boolean visit(Node node) { - if (node instanceof TestValueNode) { - final TestLanguageNode testNode = (TestValueNode) node; - testNode.probeLite(eventListener); - } - return true; - } - - public void probeAST(Node node) { - node.accept(this); - } - } - - /** - * Counts the number of "enter" events at probed nodes. - * - */ - static final class TestEventListener extends SimpleEventListener { + static final class TestInstrumentListener extends DefaultInstrumentListener { public int counter = 0; @Override - public void enter(Node node, VirtualFrame frame) { + public void enter(Probe probe) { + counter++; + } + + } + + /** + * Counts the number of "enter" events at probed nodes using the AST listener. + */ + static final class TestASTInstrumentListener extends DefaultASTInstrumentListener { + + public int counter = 0; + + @Override + public void enter(Probe probe, Node node, VirtualFrame vFrame) { counter++; } @@ -692,10 +649,10 @@ // where we want to count executions. // it will get copied when ASTs cloned, so // keep the count in this outer class. - probe.attach(Instrument.create(new SimpleEventListener() { + probe.attach(Instrument.create(new DefaultInstrumentListener() { @Override - public void enter(Node node, VirtualFrame vFrame) { + public void enter(Probe p) { count++; } }, "Instrumentation Test MultiCounter"));