comparison graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/InstrumentationTest.java @ 18485:e3c95cbbb50c

Truffle Instrumentation: major API revision, based around the Probe and Instrument classes; add Instrumentable API for language implementors, with most details automated; reimplemented to handle AST splitting automatically; more JUnit tests.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Sun, 23 Nov 2014 16:07:23 -0800
parents 5d16da2ca0c8
children
comparison
equal deleted inserted replaced
18484:e97e1f07a3d6 18485:e3c95cbbb50c
20 * or visit www.oracle.com if you need additional information or have any 20 * or visit www.oracle.com if you need additional information or have any
21 * questions. 21 * questions.
22 */ 22 */
23 package com.oracle.truffle.api.test; 23 package com.oracle.truffle.api.test;
24 24
25 import java.lang.reflect.*;
25 import java.util.*; 26 import java.util.*;
26 27
27 import org.junit.*; 28 import org.junit.*;
28 29
29 import com.oracle.truffle.api.*; 30 import com.oracle.truffle.api.*;
30 import com.oracle.truffle.api.frame.*; 31 import com.oracle.truffle.api.frame.*;
31 import com.oracle.truffle.api.instrument.*; 32 import com.oracle.truffle.api.instrument.*;
33 import com.oracle.truffle.api.instrument.Probe.ProbeListener;
34 import com.oracle.truffle.api.instrument.ProbeNode.Instrumentable;
35 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
36 import com.oracle.truffle.api.instrument.impl.*;
32 import com.oracle.truffle.api.nodes.*; 37 import com.oracle.truffle.api.nodes.*;
33 import com.oracle.truffle.api.source.*; 38 import com.oracle.truffle.api.source.*;
34 39
35 /** 40 /**
36 * <h3>AST Instrumentation</h3> 41 * <h3>AST Instrumentation</h3>
37 * 42 *
38 * Instrumentation allows the insertion into Truffle ASTs language-specific instances of 43 * Instrumentation allows the insertion into Truffle ASTs language-specific instances of
39 * {@link Wrapper} that propagate {@link ExecutionEvents} through a {@link Probe} to any instances 44 * {@link WrapperNode} that propagate execution events through a {@link Probe} to any instances of
40 * of {@link Instrument} that might be attached to the particular probe by tools. 45 * {@link Instrument} that might be attached to the particular probe by tools.
41 * <ol> 46 * <ol>
42 * <li>Creates a simple add AST</li> 47 * <li>Creates a simple add AST</li>
43 * <li>Verifies its structure</li> 48 * <li>Verifies its structure</li>
44 * <li>"Probes" the add node by adding a {@link Wrapper} and associated {@link Probe}</li> 49 * <li>"Probes" the add node by adding a {@link WrapperNode} and associated {@link Probe}</li>
45 * <li>Attaches a simple {@link Instrument} to the node via its {@link Probe}</li> 50 * <li>Attaches a simple {@link Instrument} to the node via the Probe's {@link ProbeNode}</li>
46 * <li>Verifies the structure of the probed AST</li> 51 * <li>Verifies the structure of the probed AST</li>
47 * <li>Verifies the execution of the probed AST</li> 52 * <li>Verifies the execution of the probed AST</li>
48 * <li>Verifies the results observed by the instrument.</li> 53 * <li>Verifies the results observed by the instrument.</li>
49 * </ol> 54 * </ol>
50 * To do these tests, several required classes have been implemented in their most basic form, only 55 * To do these tests, several required classes have been implemented in their most basic form, only
51 * implementing the methods necessary for the tests to pass, with stubs elsewhere. 56 * implementing the methods necessary for the tests to pass, with stubs elsewhere.
52 */ 57 */
53 public class InstrumentationTest { 58 public class InstrumentationTest {
54 59
60 private static final SyntaxTag ADD_TAG = new SyntaxTag() {
61
62 public String name() {
63 return "Addition";
64 }
65
66 public String getDescription() {
67 return "Test Language Addition Node";
68 }
69 };
70
71 private static final SyntaxTag VALUE_TAG = new SyntaxTag() {
72
73 public String name() {
74 return "Value";
75 }
76
77 public String getDescription() {
78 return "Test Language Value Node";
79 }
80 };
81
55 @Test 82 @Test
56 public void test() { 83 public void testBasicInstrumentation() {
57 // Build a tree 84 // Create a simple addition AST
58 TruffleRuntime runtime = Truffle.getRuntime(); 85 final TruffleRuntime runtime = Truffle.getRuntime();
59 TestChildNode leftChild = new TestChildNode(); 86 final TestValueNode leftValueNode = new TestValueNode(6);
60 TestChildNode rightChild = new TestChildNode(); 87 final TestValueNode rightValueNode = new TestValueNode(7);
61 TestSourceSection sourceSection = new TestSourceSection(); 88 final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
62 TestAddNode addNode = new TestAddNode(leftChild, rightChild, sourceSection); 89 final TestRootNode rootNode = new TestRootNode(addNode);
63 TestRootNode rootNode = new TestRootNode(addNode); 90
64 91 // Creating a call target sets the parent pointers in this tree and is necessary prior to
65 // Have to create a call target before checking parent/child relationships 92 // checking any parent/child relationships
66 CallTarget target = runtime.createCallTarget(rootNode); 93 final CallTarget callTarget1 = runtime.createCallTarget(rootNode);
67 94
68 // Check tree structure 95 // Check the tree structure
69 Assert.assertEquals(addNode, leftChild.getParent()); 96 Assert.assertEquals(addNode, leftValueNode.getParent());
70 Assert.assertEquals(addNode, rightChild.getParent()); 97 Assert.assertEquals(addNode, rightValueNode.getParent());
71 Iterator<Node> iterator = addNode.getChildren().iterator(); 98 Iterator<Node> iterator = addNode.getChildren().iterator();
72 Assert.assertEquals(leftChild, iterator.next()); 99 Assert.assertEquals(leftValueNode, iterator.next());
73 Assert.assertEquals(rightChild, iterator.next()); 100 Assert.assertEquals(rightValueNode, iterator.next());
74 Assert.assertFalse(iterator.hasNext()); 101 Assert.assertFalse(iterator.hasNext());
75 Assert.assertEquals(rootNode, addNode.getParent()); 102 Assert.assertEquals(rootNode, addNode.getParent());
76 iterator = rootNode.getChildren().iterator(); 103 iterator = rootNode.getChildren().iterator();
77 Assert.assertEquals(addNode, iterator.next()); 104 Assert.assertEquals(addNode, iterator.next());
78 Assert.assertFalse(iterator.hasNext()); 105 Assert.assertFalse(iterator.hasNext());
79 Object result = target.call(); 106
80 Assert.assertEquals(42, result); 107 // Ensure it executes correctly
81 108 Assert.assertEquals(13, callTarget1.call());
82 // Create another call target, this time with the "probed" add node 109
83 TestExecutionContext context = new TestExecutionContext(); 110 // Probe the addition node
84 TestWrapper wrapper = new TestWrapper(addNode, context); 111 final Probe probe = addNode.probe();
85 rootNode = new TestRootNode(wrapper); 112
86 target = runtime.createCallTarget(rootNode); 113 // Check the modified tree structure
87 114 Assert.assertEquals(addNode, leftValueNode.getParent());
88 // Check the new tree structure 115 Assert.assertEquals(addNode, rightValueNode.getParent());
89 Assert.assertEquals(addNode, leftChild.getParent());
90 Assert.assertEquals(addNode, rightChild.getParent());
91 iterator = addNode.getChildren().iterator(); 116 iterator = addNode.getChildren().iterator();
92 Assert.assertEquals(leftChild, iterator.next()); 117 Assert.assertEquals(leftValueNode, iterator.next());
93 Assert.assertEquals(rightChild, iterator.next()); 118 Assert.assertEquals(rightValueNode, iterator.next());
94 Assert.assertFalse(iterator.hasNext()); 119 Assert.assertFalse(iterator.hasNext());
95 Assert.assertEquals(wrapper, addNode.getParent()); 120
96 iterator = wrapper.getChildren().iterator(); 121 // Ensure there's a WrapperNode correctly inserted into the AST
122 iterator = rootNode.getChildren().iterator();
123 Node wrapperNode = iterator.next();
124 Assert.assertTrue(wrapperNode instanceof TestLanguageWrapperNode);
125 Assert.assertFalse(iterator.hasNext());
126 Assert.assertEquals(rootNode, wrapperNode.getParent());
127
128 // Check that the WrapperNode has both the probe and the wrapped node as children
129 iterator = wrapperNode.getChildren().iterator();
130 Assert.assertEquals(addNode, iterator.next());
131 ProbeNode probeNode = (ProbeNode) iterator.next();
132 Assert.assertTrue(probeNode.getProbe() != null);
133 Assert.assertFalse(iterator.hasNext());
134
135 // Check that you can't probe the WrapperNodes
136 TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode;
137 try {
138 wrapper.probe();
139 Assert.fail();
140 } catch (IllegalStateException e) {
141 }
142
143 // Check that the "probed" AST still executes correctly
144 Assert.assertEquals(13, callTarget1.call());
145
146 // Attach a counting instrument to the probe
147 final TestCounter counterA = new TestCounter();
148 counterA.attach(probe);
149
150 // Attach a second counting instrument to the probe
151 final TestCounter counterB = new TestCounter();
152 counterB.attach(probe);
153
154 // Run it again and check that the two instruments are working
155 Assert.assertEquals(13, callTarget1.call());
156 Assert.assertEquals(counterA.enterCount, 1);
157 Assert.assertEquals(counterA.leaveCount, 1);
158 Assert.assertEquals(counterB.enterCount, 1);
159 Assert.assertEquals(counterB.leaveCount, 1);
160
161 // Remove counterA and check the "instrument chain"
162 counterA.dispose();
163 iterator = probeNode.getChildren().iterator();
164
165 // Run it again and check that instrument B is still working but not A
166 Assert.assertEquals(13, callTarget1.call());
167 Assert.assertEquals(counterA.enterCount, 1);
168 Assert.assertEquals(counterA.leaveCount, 1);
169 Assert.assertEquals(counterB.enterCount, 2);
170 Assert.assertEquals(counterB.leaveCount, 2);
171
172 // Simulate a split by cloning the AST
173 final CallTarget callTarget2 = runtime.createCallTarget((TestRootNode) rootNode.copy());
174 // Run the clone and check that instrument B is still working but not A
175 Assert.assertEquals(13, callTarget2.call());
176 Assert.assertEquals(counterA.enterCount, 1);
177 Assert.assertEquals(counterA.leaveCount, 1);
178 Assert.assertEquals(counterB.enterCount, 3);
179 Assert.assertEquals(counterB.leaveCount, 3);
180
181 // Run the original and check that instrument B is still working but not A
182 Assert.assertEquals(13, callTarget2.call());
183 Assert.assertEquals(counterA.enterCount, 1);
184 Assert.assertEquals(counterA.leaveCount, 1);
185 Assert.assertEquals(counterB.enterCount, 4);
186 Assert.assertEquals(counterB.leaveCount, 4);
187
188 // Attach a second instrument to the probe
189 final TestCounter counterC = new TestCounter();
190 counterC.attach(probe);
191
192 // Run the original and check that instruments B,C working but not A
193 Assert.assertEquals(13, callTarget1.call());
194 Assert.assertEquals(counterA.enterCount, 1);
195 Assert.assertEquals(counterA.leaveCount, 1);
196 Assert.assertEquals(counterB.enterCount, 5);
197 Assert.assertEquals(counterB.leaveCount, 5);
198 Assert.assertEquals(counterC.enterCount, 1);
199 Assert.assertEquals(counterC.leaveCount, 1);
200
201 // Run the clone and check that instruments B,C working but not A
202 Assert.assertEquals(13, callTarget2.call());
203 Assert.assertEquals(counterA.enterCount, 1);
204 Assert.assertEquals(counterA.leaveCount, 1);
205 Assert.assertEquals(counterB.enterCount, 6);
206 Assert.assertEquals(counterB.leaveCount, 6);
207 Assert.assertEquals(counterC.enterCount, 2);
208 Assert.assertEquals(counterC.leaveCount, 2);
209
210 // Remove instrumentC
211 counterC.dispose();
212
213 // Run the original and check that instrument B working but not A,C
214 Assert.assertEquals(13, callTarget1.call());
215 Assert.assertEquals(counterA.enterCount, 1);
216 Assert.assertEquals(counterA.leaveCount, 1);
217 Assert.assertEquals(counterB.enterCount, 7);
218 Assert.assertEquals(counterB.leaveCount, 7);
219 Assert.assertEquals(counterC.enterCount, 2);
220 Assert.assertEquals(counterC.leaveCount, 2);
221
222 // Run the clone and check that instrument B working but not A,C
223 Assert.assertEquals(13, callTarget2.call());
224 Assert.assertEquals(counterA.enterCount, 1);
225 Assert.assertEquals(counterA.leaveCount, 1);
226 Assert.assertEquals(counterB.enterCount, 8);
227 Assert.assertEquals(counterB.leaveCount, 8);
228 Assert.assertEquals(counterC.enterCount, 2);
229 Assert.assertEquals(counterC.leaveCount, 2);
230
231 // Remove instrumentB
232 counterB.dispose();
233
234 // Run both the original and clone, check that no instruments working
235 Assert.assertEquals(13, callTarget1.call());
236 Assert.assertEquals(13, callTarget2.call());
237 Assert.assertEquals(counterA.enterCount, 1);
238 Assert.assertEquals(counterA.leaveCount, 1);
239 Assert.assertEquals(counterB.enterCount, 8);
240 Assert.assertEquals(counterB.leaveCount, 8);
241 Assert.assertEquals(counterC.enterCount, 2);
242 Assert.assertEquals(counterC.leaveCount, 2);
243 }
244
245 @Test
246 public void testTagging() {
247
248 // Applies appropriate tags
249 final TestASTProber astProber = new TestASTProber();
250 Probe.registerASTProber(astProber);
251
252 // Listens for probes and tags being added
253 final TestProbeListener probeListener = new TestProbeListener();
254 Probe.addProbeListener(probeListener);
255
256 // Counts all entries to all instances of addition nodes
257 final TestMultiCounter additionCounter = new TestMultiCounter();
258
259 // Counts all entries to all instances of value nodes
260 final TestMultiCounter valueCounter = new TestMultiCounter();
261
262 // Create a simple addition AST
263 final TruffleRuntime runtime = Truffle.getRuntime();
264 final TestValueNode leftValueNode = new TestValueNode(6);
265 final TestValueNode rightValueNode = new TestValueNode(7);
266 final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
267
268 final TestRootNode rootNode = new TestRootNode(addNode);
269
270 final CallTarget callTarget = runtime.createCallTarget(rootNode);
271
272 // Check that the prober added probes to the tree
273 Assert.assertEquals(probeListener.probeCount, 3);
274 Assert.assertEquals(probeListener.tagCount, 3);
275
276 Assert.assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1);
277 Assert.assertEquals(Probe.findProbesTaggedAs(VALUE_TAG).size(), 2);
278
279 // Check that it executes correctly
280 Assert.assertEquals(13, callTarget.call());
281
282 // Dynamically attach a counter for all executions of all Addition nodes
283 for (Probe probe : Probe.findProbesTaggedAs(ADD_TAG)) {
284 additionCounter.attachCounter(probe);
285 }
286 // Dynamically attach a counter for all executions of all Value nodes
287 for (Probe probe : Probe.findProbesTaggedAs(VALUE_TAG)) {
288 valueCounter.attachCounter(probe);
289 }
290
291 // Counters initialized at 0
292 Assert.assertEquals(additionCounter.count, 0);
293 Assert.assertEquals(valueCounter.count, 0);
294
295 // Execute again
296 Assert.assertEquals(13, callTarget.call());
297
298 // There are two value nodes in the AST, but only one addition node
299 Assert.assertEquals(additionCounter.count, 1);
300 Assert.assertEquals(valueCounter.count, 2);
301
302 Probe.unregisterASTProber(astProber);
303
304 }
305
306 @Test
307 public void testProbeLite() {
308
309 // Use the "lite-probing" option, limited to a single pass of
310 // probing and a single Instrument at each probed node. This
311 // particular test uses a shared event receiver at every
312 // lite-probed node.
313 final TestEventReceiver receiver = new TestEventReceiver();
314
315 TestASTLiteProber astLiteProber = new TestASTLiteProber(receiver);
316 Probe.registerASTProber(astLiteProber);
317
318 // Create a simple addition AST
319 final TruffleRuntime runtime = Truffle.getRuntime();
320 final TestValueNode leftValueNode = new TestValueNode(6);
321 final TestValueNode rightValueNode = new TestValueNode(7);
322 final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
323 final TestRootNode rootNode = new TestRootNode(addNode);
324
325 // Creating a call target sets the parent pointers in this tree and is necessary prior to
326 // checking any parent/child relationships
327 final CallTarget callTarget = runtime.createCallTarget(rootNode);
328
329 // Check that the instrument is working as expected.
330 Assert.assertEquals(0, receiver.counter);
331 callTarget.call();
332 Assert.assertEquals(2, receiver.counter);
333
334 // Check that you can't probe a node that's already received a probeLite() call
335 try {
336 leftValueNode.probe();
337 Assert.fail();
338 } catch (IllegalStateException e) {
339 }
340
341 try {
342 rightValueNode.probe();
343 Assert.fail();
344 } catch (IllegalStateException e) {
345 }
346
347 // Check tree structure
348 Assert.assertTrue(leftValueNode.getParent() instanceof TestLanguageWrapperNode);
349 Assert.assertTrue(rightValueNode.getParent() instanceof TestLanguageWrapperNode);
350 TestLanguageWrapperNode leftWrapper = (TestLanguageWrapperNode) leftValueNode.getParent();
351 TestLanguageWrapperNode rightWrapper = (TestLanguageWrapperNode) rightValueNode.getParent();
352 Assert.assertEquals(addNode, leftWrapper.getParent());
353 Assert.assertEquals(addNode, rightWrapper.getParent());
354 Iterator<Node> iterator = addNode.getChildren().iterator();
355 Assert.assertEquals(leftWrapper, iterator.next());
356 Assert.assertEquals(rightWrapper, iterator.next());
357 Assert.assertFalse(iterator.hasNext());
358 Assert.assertEquals(rootNode, addNode.getParent());
359 iterator = rootNode.getChildren().iterator();
97 Assert.assertEquals(addNode, iterator.next()); 360 Assert.assertEquals(addNode, iterator.next());
98 Assert.assertFalse(iterator.hasNext()); 361 Assert.assertFalse(iterator.hasNext());
99 Assert.assertEquals(rootNode, wrapper.getParent()); 362
100 iterator = rootNode.getChildren().iterator(); 363 // Check that you can't get a probe on the wrappers because they were "lite-probed"
101 Assert.assertEquals(wrapper, iterator.next()); 364 try {
102 Assert.assertFalse(iterator.hasNext()); 365 leftWrapper.getProbe();
103 result = target.call(); 366 Assert.fail();
104 Assert.assertEquals(42, result); 367 } catch (IllegalStateException e) {
105 368 }
106 // Create some instruments 369 try {
107 final TestInstrument instrumentA = new TestInstrument(); 370 rightWrapper.getProbe();
108 final TestInstrument instrumentB = new TestInstrument(); 371 Assert.fail();
109 372 } catch (IllegalStateException e) {
110 wrapper.getProbe().addInstrument(instrumentA); 373 }
111 374
112 result = target.call(); 375 // Check that you can't probe the wrappers
113 Assert.assertEquals(instrumentA.numInstrumentEnter, 1); 376 try {
114 Assert.assertEquals(instrumentA.numInstrumentLeave, 1); 377 leftWrapper.probe();
115 Assert.assertEquals(instrumentB.numInstrumentEnter, 0); 378 Assert.fail();
116 Assert.assertEquals(instrumentB.numInstrumentLeave, 0); 379 } catch (IllegalStateException e) {
117 Assert.assertEquals(42, result); 380 }
118 381 try {
119 wrapper.getProbe().addInstrument(instrumentB); 382 rightWrapper.probe();
120 383 Assert.fail();
121 result = target.call(); 384 } catch (IllegalStateException e) {
122 Assert.assertEquals(instrumentA.numInstrumentEnter, 2); 385 }
123 Assert.assertEquals(instrumentA.numInstrumentLeave, 2); 386 try {
124 Assert.assertEquals(instrumentB.numInstrumentEnter, 1); 387 leftWrapper.probeLite(null);
125 Assert.assertEquals(instrumentB.numInstrumentLeave, 1); 388 Assert.fail();
126 Assert.assertEquals(42, result); 389 } catch (IllegalStateException e) {
127 390 }
128 wrapper.getProbe().removeInstrument(instrumentA); 391 try {
129 392 rightWrapper.probeLite(null);
130 result = target.call(); 393 Assert.fail();
131 Assert.assertEquals(instrumentA.numInstrumentEnter, 2); 394 } catch (IllegalStateException e) {
132 Assert.assertEquals(instrumentA.numInstrumentLeave, 2); 395 }
133 Assert.assertEquals(instrumentB.numInstrumentEnter, 2); 396
134 Assert.assertEquals(instrumentB.numInstrumentLeave, 2); 397 // Use reflection to check that each WrapperNode has a ProbeLiteNode with a
135 Assert.assertEquals(42, result); 398 // SimpleEventReceiver
136 399 try {
137 } 400 Field probeNodeField = leftWrapper.getClass().getDeclaredField("probeNode");
138 401
139 private class TestRootNode extends RootNode { 402 // cheat: probeNode is private, so we change it's accessibility at runtime
140 @Child private RootNode child; 403 probeNodeField.setAccessible(true);
141 404 ProbeNode probeNode = (ProbeNode) probeNodeField.get(leftWrapper);
142 public TestRootNode(RootNode child) { 405
143 super(null); 406 // hack: Since ProbeLiteNode is not visible, we do a string compare here
407 Assert.assertTrue(probeNode.getClass().toString().endsWith("ProbeLiteNode"));
408
409 // Now we do the same to check the type of the eventReceiver in ProbeLiteNode
410 Field eventReceiverField = probeNode.getClass().getDeclaredField("eventReceiver");
411 eventReceiverField.setAccessible(true);
412 TruffleEventReceiver eventReceiver = (TruffleEventReceiver) eventReceiverField.get(probeNode);
413 Assert.assertTrue(eventReceiver instanceof SimpleEventReceiver);
414
415 // Reset accessibility
416 probeNodeField.setAccessible(false);
417 eventReceiverField.setAccessible(false);
418
419 } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
420 Assert.fail();
421 }
422
423 Probe.unregisterASTProber(astLiteProber);
424
425 }
426
427 /**
428 * A guest language usually has a single language-specific subclass of {@link Node} from which
429 * all other nodes in the guest language subclass. By making this node {@link Instrumentable},
430 * we allow all nodes of the guest language to have {@link Probe}s attach to them.
431 *
432 */
433 private abstract class TestLanguageNode extends Node implements Instrumentable {
434 public abstract Object execute(VirtualFrame frame);
435
436 public Probe probe() {
437 Node parent = getParent();
438
439 if (parent == null) {
440 throw new IllegalStateException("Cannot call probe() on a node without a parent.");
441 }
442
443 if (parent instanceof TestLanguageWrapperNode) {
444 return ((TestLanguageWrapperNode) parent).getProbe();
445 }
446
447 // Create a new wrapper/probe with this node as its child.
448 final TestLanguageWrapperNode wrapper = new TestLanguageWrapperNode(this);
449
450 // Connect it to a Probe
451 final Probe probe = ProbeNode.insertProbe(wrapper);
452
453 // Replace this node in the AST with the wrapper
454 this.replace(wrapper);
455
456 return probe;
457 }
458
459 public void probeLite(TruffleEventReceiver eventReceiver) {
460 Node parent = getParent();
461
462 if (parent == null) {
463 throw new IllegalStateException("Cannot call probeLite() on a node without a parent");
464 }
465
466 if (parent instanceof TestLanguageWrapperNode) {
467 throw new IllegalStateException("Cannot call probeLite() on a node that already has a wrapper.");
468 }
469
470 final TestLanguageWrapperNode wrapper = new TestLanguageWrapperNode(this);
471 ProbeNode.insertProbeLite(wrapper, eventReceiver);
472
473 this.replace(wrapper);
474 }
475 }
476
477 /**
478 * The wrapper node class is usually language-specific and inherits from the language-specific
479 * subclass of {@link Node}, in this case, {@link TestLanguageNode}.
480 */
481 @NodeInfo(cost = NodeCost.NONE)
482 private class TestLanguageWrapperNode extends TestLanguageNode implements WrapperNode {
483 @Child private TestLanguageNode child;
484 @Child private ProbeNode probeNode;
485
486 public TestLanguageWrapperNode(TestLanguageNode child) {
487 assert !(child instanceof TestLanguageWrapperNode);
144 this.child = child; 488 this.child = child;
145 } 489 }
146 490
147 @Override 491 public String instrumentationInfo() {
148 public Object execute(VirtualFrame frame) { 492 return "Wrapper node for testing";
149 return child.execute(frame); 493 }
150 } 494
151 } 495 public void insertProbe(ProbeNode newProbeNode) {
152 496 this.probeNode = newProbeNode;
153 private class TestAddNode extends RootNode { 497 }
154 498
155 @Child private TestChildNode left; 499 public Probe getProbe() {
156 @Child private TestChildNode right; 500 try {
157 501 return probeNode.getProbe();
158 public TestAddNode(TestChildNode left, TestChildNode right, TestSourceSection sourceSection) { 502 } catch (IllegalStateException e) {
159 super(sourceSection); 503 throw new IllegalStateException("Cannot call getProbe() on a wrapper that has no probe");
160 this.left = left; 504 }
161 this.right = right; 505 }
162 } 506
163 507 @Override
164 @Override
165 public Object execute(VirtualFrame frame) {
166 return left.execute() + right.execute();
167 }
168 }
169
170 private class TestChildNode extends Node {
171
172 public TestChildNode() {
173 super(null);
174 }
175
176 public int execute() {
177 return 21;
178 }
179 }
180
181 /**
182 * The wrapper node class is usually language-specific and inherits from the language-specific
183 * subclass of {@link Node}, not {@RootNode}.
184 */
185 private class TestWrapper extends RootNode implements Wrapper {
186 @Child private RootNode child;
187 private Probe probe;
188
189 public TestWrapper(RootNode child, ExecutionContext context) {
190 this.child = insert(child);
191 this.probe = context.createProbe(child.getSourceSection());
192 }
193
194 public Node getChild() { 508 public Node getChild() {
195 return child; 509 return child;
196 } 510 }
197 511
198 public Probe getProbe() {
199 return probe;
200 }
201
202 @Override 512 @Override
203 public Object execute(VirtualFrame frame) { 513 public Object execute(VirtualFrame frame) {
204 probe.enter(child, frame); 514 probeNode.enter(child, frame);
205 Object result; 515 Object result;
206 516
207 try { 517 try {
208 result = child.execute(frame); 518 result = child.execute(frame);
209 probe.leave(child, frame, result); 519 probeNode.returnValue(child, frame, result);
520 } catch (KillException e) {
521 throw (e);
210 } catch (Exception e) { 522 } catch (Exception e) {
211 probe.leaveExceptional(child, frame, e); 523 probeNode.returnExceptional(child, frame, e);
212 throw (e); 524 throw (e);
213 } 525 }
526
214 return result; 527 return result;
215 } 528 }
216 } 529
217 530 @Override
218 /** 531 public Probe probe() {
219 * An "empty" description of the source code that might correspond to a particular AST node. The 532 throw new IllegalStateException("Cannot call probe() on a wrapper.");
220 * instrumentation framework tracks probes that have been inserted by their source location, 533 }
221 * using this as a key. 534
222 */ 535 @Override
223 private class TestSourceSection implements SourceSection { 536 public void probeLite(TruffleEventReceiver eventReceiver) {
224 537 throw new IllegalStateException("Cannot call probeLite() on a wrapper.");
225 public Source getSource() { 538 }
226 return null; 539 }
227 } 540
228 541 /**
229 public int getStartLine() { 542 * A simple node for our test language to store a value.
230 return 0; 543 */
231 } 544 private class TestValueNode extends TestLanguageNode {
232 545 private final int value;
233 public LineLocation getLineLocation() { 546
234 return null; 547 public TestValueNode(int value) {
235 } 548 this.value = value;
236 549 }
237 public int getStartColumn() { 550
238 return 0; 551 @Override
239 } 552 public Object execute(VirtualFrame frame) {
240 553 return new Integer(this.value);
241 public int getCharIndex() { 554 }
242 return 0; 555 }
243 } 556
244 557 /**
245 public int getCharLength() { 558 * A node for our test language that adds up two {@link TestValueNode}s.
246 return 0; 559 */
247 } 560 private class TestAdditionNode extends TestLanguageNode {
248 561 @Child private TestLanguageNode leftChild;
249 public int getCharEndIndex() { 562 @Child private TestLanguageNode rightChild;
250 return 0; 563
251 } 564 public TestAdditionNode(TestValueNode leftChild, TestValueNode rightChild) {
252 565 this.leftChild = insert(leftChild);
253 public String getIdentifier() { 566 this.rightChild = insert(rightChild);
254 return null; 567 }
255 } 568
256 569 @Override
257 public String getCode() { 570 public Object execute(VirtualFrame frame) {
258 return null; 571 return new Integer(((Integer) leftChild.execute(frame)).intValue() + ((Integer) rightChild.execute(frame)).intValue());
259 } 572 }
260 573 }
261 public String getShortDescription() { 574
262 return null; 575 /**
263 } 576 * Truffle requires that all guest languages to have a {@link RootNode} which sits atop any AST
264 577 * of the guest language. This is necessary since creating a {@link CallTarget} is how Truffle
265 } 578 * completes an AST. The root nodes serves as our entry point into a program.
266 579 */
267 private class TestExecutionContext extends ExecutionContext { 580 private class TestRootNode extends RootNode {
268 581 @Child private TestLanguageNode body;
269 @Override 582
270 public String getLanguageShortName() { 583 /**
271 return "test"; 584 * This constructor emulates the global machinery that applies registered probers to every
272 } 585 * newly created AST. Global registry is not used, since that would interfere with other
273 586 * tests run in the same environment.
274 @Override 587 */
275 protected void setSourceCallback(SourceCallback sourceCallback) { 588 public TestRootNode(TestLanguageNode body) {
276 } 589 super(null);
277 590 this.body = body;
278 } 591 }
279 592
280 private class TestInstrument extends Instrument { 593 @Override
281 594 public Object execute(VirtualFrame frame) {
282 public int numInstrumentEnter = 0; 595 return body.execute(frame);
283 public int numInstrumentLeave = 0; 596 }
284 597
285 @Override 598 @Override
286 public void enter(Node astNode, VirtualFrame frame) { 599 public boolean isCloningAllowed() {
287 numInstrumentEnter++; 600 return true;
288 } 601 }
289 602
290 @Override 603 @Override
291 public void leave(Node astNode, VirtualFrame frame, Object result) { 604 public void applyInstrumentation() {
292 numInstrumentLeave++; 605 Probe.applyASTProbers(body);
293 } 606 }
607 }
608
609 /**
610 * A counter for the number of times execution enters and leaves a probed AST node.
611 */
612 private class TestCounter {
613
614 public int enterCount = 0;
615 public int leaveCount = 0;
616 public final Instrument instrument;
617
618 public TestCounter() {
619 instrument = Instrument.create(new SimpleEventReceiver() {
620
621 @Override
622 public void enter(Node node, VirtualFrame frame) {
623 enterCount++;
624 }
625
626 @Override
627 public void returnAny(Node node, VirtualFrame frame) {
628 leaveCount++;
629 }
630 }, "Instrumentation Test Counter");
631 }
632
633 public void attach(Probe probe) {
634 probe.attach(instrument);
635 }
636
637 public void dispose() {
638 instrument.dispose();
639 }
640
641 }
642
643 /**
644 * Tags selected nodes on newly constructed ASTs.
645 */
646 private static final class TestASTProber implements NodeVisitor, ASTProber {
647
648 public boolean visit(Node node) {
649 if (node instanceof TestLanguageNode) {
650
651 final TestLanguageNode testNode = (TestLanguageNode) node;
652
653 if (node instanceof TestValueNode) {
654 testNode.probe().tagAs(VALUE_TAG, null);
655
656 } else if (node instanceof TestAdditionNode) {
657 testNode.probe().tagAs(ADD_TAG, null);
658
659 }
660 }
661 return true;
662 }
663
664 public void probeAST(Node node) {
665 node.accept(this);
666 }
667 }
668
669 /**
670 * "lite-probes" every value node with a shared event receiver.
671 */
672 private static final class TestASTLiteProber implements NodeVisitor, ASTProber {
673 private final TruffleEventReceiver eventReceiver;
674
675 public TestASTLiteProber(SimpleEventReceiver simpleEventReceiver) {
676 this.eventReceiver = simpleEventReceiver;
677 }
678
679 public boolean visit(Node node) {
680 if (node instanceof TestValueNode) {
681 final TestLanguageNode testNode = (TestValueNode) node;
682 testNode.probeLite(eventReceiver);
683 }
684 return true;
685 }
686
687 public void probeAST(Node node) {
688 node.accept(this);
689 }
690 }
691
692 /**
693 * Counts the number of "enter" events at probed nodes.
694 *
695 */
696 static final class TestEventReceiver extends SimpleEventReceiver {
697
698 public int counter = 0;
699
700 @Override
701 public void enter(Node node, VirtualFrame frame) {
702 counter++;
703 }
704
705 }
706
707 /**
708 * A counter that can count executions at multiple nodes; it attaches a separate instrument at
709 * each Probe, but keeps a total count.
710 */
711 private static final class TestMultiCounter {
712
713 public int count = 0;
714
715 public void attachCounter(Probe probe) {
716
717 // Attach a new instrument for every Probe
718 // where we want to count executions.
719 // it will get copied when ASTs cloned, so
720 // keep the count in this outer class.
721 probe.attach(Instrument.create(new SimpleEventReceiver() {
722
723 @Override
724 public void enter(Node node, VirtualFrame frame) {
725 count++;
726 }
727 }, "Instrumentation Test MultiCounter"));
728 }
729 }
730
731 private static final class TestProbeListener implements ProbeListener {
732
733 public int probeCount = 0;
734 public int tagCount = 0;
735
736 public void newProbeInserted(Probe probe) {
737 probeCount++;
738 }
739
740 public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
741 tagCount++;
742 }
743
744 public void startASTProbing(Source source) {
745 }
746
747 public void endASTProbing(Source source) {
748 }
749
294 } 750 }
295 751
296 } 752 }