comparison truffle/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java @ 21951:9c8c0937da41

Moving all sources into truffle subdirectory
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Wed, 17 Jun 2015 10:58:08 +0200
parents graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java@263ab98b3bf0
children dc83cc1f94f2 3aad794eec0e
comparison
equal deleted inserted replaced
21950:2a5011c7e641 21951:9c8c0937da41
1 /*
2 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23 package com.oracle.truffle.api.test.instrument;
24
25 import static org.junit.Assert.*;
26
27 import java.util.*;
28
29 import org.junit.*;
30
31 import com.oracle.truffle.api.*;
32 import com.oracle.truffle.api.frame.*;
33 import com.oracle.truffle.api.instrument.*;
34 import com.oracle.truffle.api.instrument.ProbeFailure.Reason;
35 import com.oracle.truffle.api.instrument.ProbeNode.WrapperNode;
36 import com.oracle.truffle.api.instrument.impl.*;
37 import com.oracle.truffle.api.nodes.*;
38 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestAdditionNode;
39 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageNode;
40 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestLanguageWrapperNode;
41 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestRootNode;
42 import com.oracle.truffle.api.test.instrument.InstrumentationTestNodes.TestValueNode;
43
44 /**
45 * <h3>AST Instrumentation</h3>
46 *
47 * Instrumentation allows the insertion into Truffle ASTs language-specific instances of
48 * {@link WrapperNode} that propagate execution events through a {@link Probe} to any instances of
49 * {@link Instrument} that might be attached to the particular probe by tools.
50 * <ol>
51 * <li>Creates a simple add AST</li>
52 * <li>Verifies its structure</li>
53 * <li>"Probes" the add node by adding a {@link WrapperNode} and associated {@link Probe}</li>
54 * <li>Attaches a simple {@link Instrument} to the node via the Probe's {@link ProbeNode}</li>
55 * <li>Verifies the structure of the probed AST</li>
56 * <li>Verifies the execution of the probed AST</li>
57 * <li>Verifies the results observed by the instrument.</li>
58 * </ol>
59 * To do these tests, several required classes have been implemented in their most basic form, only
60 * implementing the methods necessary for the tests to pass, with stubs elsewhere.
61 */
62 public class InstrumentationTest {
63
64 private static final SyntaxTag ADD_TAG = new SyntaxTag() {
65
66 @Override
67 public String name() {
68 return "Addition";
69 }
70
71 @Override
72 public String getDescription() {
73 return "Test Language Addition Node";
74 }
75 };
76
77 private static final SyntaxTag VALUE_TAG = new SyntaxTag() {
78
79 @Override
80 public String name() {
81 return "Value";
82 }
83
84 @Override
85 public String getDescription() {
86 return "Test Language Value Node";
87 }
88 };
89
90 @Test
91 public void testInstrumentationStructure() {
92 // Create a simple addition AST
93 final TruffleRuntime runtime = Truffle.getRuntime();
94 final TestValueNode leftValueNode = new TestValueNode(6);
95 final TestValueNode rightValueNode = new TestValueNode(7);
96 final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
97
98 try {
99 addNode.probe();
100 } catch (ProbeException e) {
101 assertEquals(e.getFailure().getReason(), Reason.NO_PARENT);
102 }
103 final TestRootNode rootNode = new TestRootNode(addNode);
104
105 // Creating a call target sets the parent pointers in this tree and is necessary prior to
106 // checking any parent/child relationships
107 final CallTarget callTarget1 = runtime.createCallTarget(rootNode);
108
109 // Check the tree structure
110 assertEquals(addNode, leftValueNode.getParent());
111 assertEquals(addNode, rightValueNode.getParent());
112 Iterator<Node> iterator = addNode.getChildren().iterator();
113 assertEquals(leftValueNode, iterator.next());
114 assertEquals(rightValueNode, iterator.next());
115 assertFalse(iterator.hasNext());
116 assertEquals(rootNode, addNode.getParent());
117 iterator = rootNode.getChildren().iterator();
118 assertEquals(addNode, iterator.next());
119 assertFalse(iterator.hasNext());
120
121 // Ensure it executes correctly
122 assertEquals(13, callTarget1.call());
123
124 // Probe the addition node
125 addNode.probe();
126
127 // Check the modified tree structure
128 assertEquals(addNode, leftValueNode.getParent());
129 assertEquals(addNode, rightValueNode.getParent());
130 iterator = addNode.getChildren().iterator();
131 assertEquals(leftValueNode, iterator.next());
132 assertEquals(rightValueNode, iterator.next());
133 assertFalse(iterator.hasNext());
134
135 // Ensure there's a WrapperNode correctly inserted into the AST
136 iterator = rootNode.getChildren().iterator();
137 Node wrapperNode = iterator.next();
138 assertTrue(wrapperNode instanceof TestLanguageWrapperNode);
139 assertFalse(iterator.hasNext());
140 assertEquals(rootNode, wrapperNode.getParent());
141
142 // Check that the WrapperNode has both the probe and the wrapped node as children
143 iterator = wrapperNode.getChildren().iterator();
144 assertEquals(addNode, iterator.next());
145 ProbeNode probeNode = (ProbeNode) iterator.next();
146 assertTrue(probeNode.getProbe() != null);
147 assertFalse(iterator.hasNext());
148
149 // Check that you can't probe the WrapperNodes
150 TestLanguageWrapperNode wrapper = (TestLanguageWrapperNode) wrapperNode;
151 try {
152 wrapper.probe();
153 fail();
154 } catch (ProbeException e) {
155 assertEquals(e.getFailure().getReason(), Reason.WRAPPER_NODE);
156 }
157
158 // Check that the "probed" AST still executes correctly
159 assertEquals(13, callTarget1.call());
160 }
161
162 @Test
163 public void testListeners() {
164
165 // Create a simple addition AST
166 final TruffleRuntime runtime = Truffle.getRuntime();
167 final TestValueNode leftValueNode = new TestValueNode(6);
168 final TestValueNode rightValueNode = new TestValueNode(7);
169 final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
170 final TestRootNode rootNode = new TestRootNode(addNode);
171
172 // Creating a call target sets the parent pointers in this tree and is necessary prior to
173 // checking any parent/child relationships
174 final CallTarget callTarget = runtime.createCallTarget(rootNode);
175 // Probe the addition node
176 final Probe probe = addNode.probe();
177
178 // Check instrumentation with the simplest kind of counters.
179 // They should all be removed when the check is finished.
180 checkCounters(probe, callTarget, rootNode, new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter(), new TestSimpleInstrumentCounter());
181
182 // Now try with the more complex flavor of listener
183 checkCounters(probe, callTarget, rootNode, new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter(), new TestStandardInstrumentCounter());
184 }
185
186 private static void checkCounters(Probe probe, CallTarget callTarget, RootNode rootNode, TestCounter counterA, TestCounter counterB, TestCounter counterC) {
187
188 // Attach a counting instrument to the probe
189 counterA.attach(probe);
190
191 // Attach a second counting instrument to the probe
192 counterB.attach(probe);
193
194 // Run it again and check that the two instruments are working
195 assertEquals(13, callTarget.call());
196 assertEquals(counterA.enterCount(), 1);
197 assertEquals(counterA.leaveCount(), 1);
198 assertEquals(counterB.enterCount(), 1);
199 assertEquals(counterB.leaveCount(), 1);
200
201 // Remove counterA
202 counterA.dispose();
203
204 // Run it again and check that instrument B is still working but not A
205 assertEquals(13, callTarget.call());
206 assertEquals(counterA.enterCount(), 1);
207 assertEquals(counterA.leaveCount(), 1);
208 assertEquals(counterB.enterCount(), 2);
209 assertEquals(counterB.leaveCount(), 2);
210
211 // Simulate a split by cloning the AST
212 final CallTarget callTarget2 = Truffle.getRuntime().createCallTarget((TestRootNode) rootNode.copy());
213 // Run the clone and check that instrument B is still working but not A
214 assertEquals(13, callTarget2.call());
215 assertEquals(counterA.enterCount(), 1);
216 assertEquals(counterA.leaveCount(), 1);
217 assertEquals(counterB.enterCount(), 3);
218 assertEquals(counterB.leaveCount(), 3);
219
220 // Run the original and check that instrument B is still working but not A
221 assertEquals(13, callTarget2.call());
222 assertEquals(counterA.enterCount(), 1);
223 assertEquals(counterA.leaveCount(), 1);
224 assertEquals(counterB.enterCount(), 4);
225 assertEquals(counterB.leaveCount(), 4);
226
227 // Attach a second instrument to the probe
228 counterC.attach(probe);
229
230 // Run the original and check that instruments B,C working but not A
231 assertEquals(13, callTarget.call());
232 assertEquals(counterA.enterCount(), 1);
233 assertEquals(counterA.leaveCount(), 1);
234 assertEquals(counterB.enterCount(), 5);
235 assertEquals(counterB.leaveCount(), 5);
236 assertEquals(counterC.enterCount(), 1);
237 assertEquals(counterC.leaveCount(), 1);
238
239 // Run the clone and check that instruments B,C working but not A
240 assertEquals(13, callTarget2.call());
241 assertEquals(counterA.enterCount(), 1);
242 assertEquals(counterA.leaveCount(), 1);
243 assertEquals(counterB.enterCount(), 6);
244 assertEquals(counterB.leaveCount(), 6);
245 assertEquals(counterC.enterCount(), 2);
246 assertEquals(counterC.leaveCount(), 2);
247
248 // Remove instrumentC
249 counterC.dispose();
250
251 // Run the original and check that instrument B working but not A,C
252 assertEquals(13, callTarget.call());
253 assertEquals(counterA.enterCount(), 1);
254 assertEquals(counterA.leaveCount(), 1);
255 assertEquals(counterB.enterCount(), 7);
256 assertEquals(counterB.leaveCount(), 7);
257 assertEquals(counterC.enterCount(), 2);
258 assertEquals(counterC.leaveCount(), 2);
259
260 // Run the clone and check that instrument B working but not A,C
261 assertEquals(13, callTarget2.call());
262 assertEquals(counterA.enterCount(), 1);
263 assertEquals(counterA.leaveCount(), 1);
264 assertEquals(counterB.enterCount(), 8);
265 assertEquals(counterB.leaveCount(), 8);
266 assertEquals(counterC.enterCount(), 2);
267 assertEquals(counterC.leaveCount(), 2);
268
269 // Remove instrumentB
270 counterB.dispose();
271
272 // Run both the original and clone, check that no instruments working
273 assertEquals(13, callTarget.call());
274 assertEquals(13, callTarget2.call());
275 assertEquals(counterA.enterCount(), 1);
276 assertEquals(counterA.leaveCount(), 1);
277 assertEquals(counterB.enterCount(), 8);
278 assertEquals(counterB.leaveCount(), 8);
279 assertEquals(counterC.enterCount(), 2);
280 assertEquals(counterC.leaveCount(), 2);
281 }
282
283 @Test
284 public void testTagging() {
285 // Applies appropriate tags
286 final TestASTProber astProber = new TestASTProber();
287 Probe.registerASTProber(astProber);
288
289 // Listens for probes and tags being added
290 final TestProbeListener probeListener = new TestProbeListener();
291 Probe.addProbeListener(probeListener);
292
293 // Counts all entries to all instances of addition nodes
294 final TestMultiCounter additionCounter = new TestMultiCounter();
295
296 // Counts all entries to all instances of value nodes
297 final TestMultiCounter valueCounter = new TestMultiCounter();
298
299 // Create a simple addition AST
300 final TruffleRuntime runtime = Truffle.getRuntime();
301 final TestValueNode leftValueNode = new TestValueNode(6);
302 final TestValueNode rightValueNode = new TestValueNode(7);
303 final TestAdditionNode addNode = new TestAdditionNode(leftValueNode, rightValueNode);
304
305 final TestRootNode rootNode = new TestRootNode(addNode);
306
307 final CallTarget callTarget = runtime.createCallTarget(rootNode);
308
309 // Check that the prober added probes to the tree
310 assertEquals(probeListener.probeCount, 3);
311 assertEquals(probeListener.tagCount, 3);
312
313 assertEquals(Probe.findProbesTaggedAs(ADD_TAG).size(), 1);
314 assertEquals(Probe.findProbesTaggedAs(VALUE_TAG).size(), 2);
315
316 // Check that it executes correctly
317 assertEquals(13, callTarget.call());
318
319 // Dynamically attach a counter for all executions of all Addition nodes
320 for (Probe probe : Probe.findProbesTaggedAs(ADD_TAG)) {
321 additionCounter.attachCounter(probe);
322 }
323 // Dynamically attach a counter for all executions of all Value nodes
324 for (Probe probe : Probe.findProbesTaggedAs(VALUE_TAG)) {
325 valueCounter.attachCounter(probe);
326 }
327
328 // Counters initialized at 0
329 assertEquals(additionCounter.count, 0);
330 assertEquals(valueCounter.count, 0);
331
332 // Execute again
333 assertEquals(13, callTarget.call());
334
335 // There are two value nodes in the AST, but only one addition node
336 assertEquals(additionCounter.count, 1);
337 assertEquals(valueCounter.count, 2);
338
339 Probe.unregisterASTProber(astProber);
340 }
341
342 private interface TestCounter {
343
344 int enterCount();
345
346 int leaveCount();
347
348 void attach(Probe probe);
349
350 void dispose();
351 }
352
353 /**
354 * A counter for the number of times execution enters and leaves a probed AST node.
355 */
356 private class TestSimpleInstrumentCounter implements TestCounter {
357
358 public int enterCount = 0;
359 public int leaveCount = 0;
360 public final Instrument instrument;
361
362 public TestSimpleInstrumentCounter() {
363 this.instrument = Instrument.create(new SimpleInstrumentListener() {
364
365 public void enter(Probe probe) {
366 enterCount++;
367 }
368
369 public void returnVoid(Probe probe) {
370 leaveCount++;
371 }
372
373 public void returnValue(Probe probe, Object result) {
374 leaveCount++;
375 }
376
377 public void returnExceptional(Probe probe, Exception exception) {
378 leaveCount++;
379 }
380
381 }, "Instrumentation Test Counter");
382 }
383
384 @Override
385 public int enterCount() {
386 return enterCount;
387 }
388
389 @Override
390 public int leaveCount() {
391 return leaveCount;
392 }
393
394 @Override
395 public void attach(Probe probe) {
396 probe.attach(instrument);
397 }
398
399 @Override
400 public void dispose() {
401 instrument.dispose();
402 }
403 }
404
405 /**
406 * A counter for the number of times execution enters and leaves a probed AST node.
407 */
408 private class TestStandardInstrumentCounter implements TestCounter {
409
410 public int enterCount = 0;
411 public int leaveCount = 0;
412 public final Instrument instrument;
413
414 public TestStandardInstrumentCounter() {
415 this.instrument = Instrument.create(new StandardInstrumentListener() {
416
417 public void enter(Probe probe, Node node, VirtualFrame vFrame) {
418 enterCount++;
419 }
420
421 public void returnVoid(Probe probe, Node node, VirtualFrame vFrame) {
422 leaveCount++;
423 }
424
425 public void returnValue(Probe probe, Node node, VirtualFrame vFrame, Object result) {
426 leaveCount++;
427 }
428
429 public void returnExceptional(Probe probe, Node node, VirtualFrame vFrame, Exception exception) {
430 leaveCount++;
431 }
432
433 }, "Instrumentation Test Counter");
434 }
435
436 @Override
437 public int enterCount() {
438 return enterCount;
439 }
440
441 @Override
442 public int leaveCount() {
443 return leaveCount;
444 }
445
446 @Override
447 public void attach(Probe probe) {
448 probe.attach(instrument);
449 }
450
451 @Override
452 public void dispose() {
453 instrument.dispose();
454 }
455 }
456
457 /**
458 * Tags selected nodes on newly constructed ASTs.
459 */
460 private static final class TestASTProber implements NodeVisitor, ASTProber {
461
462 @Override
463 public boolean visit(Node node) {
464 if (node instanceof TestLanguageNode) {
465
466 final TestLanguageNode testNode = (TestLanguageNode) node;
467
468 if (node instanceof TestValueNode) {
469 testNode.probe().tagAs(VALUE_TAG, null);
470
471 } else if (node instanceof TestAdditionNode) {
472 testNode.probe().tagAs(ADD_TAG, null);
473
474 }
475 }
476 return true;
477 }
478
479 @Override
480 public void probeAST(Node node) {
481 node.accept(this);
482 }
483 }
484
485 /**
486 * Counts the number of "enter" events at probed nodes using the simplest AST listener.
487 */
488 static final class TestSimpleInstrumentListener extends DefaultSimpleInstrumentListener {
489
490 public int counter = 0;
491
492 @Override
493 public void enter(Probe probe) {
494 counter++;
495 }
496 }
497
498 /**
499 * Counts the number of "enter" events at probed nodes using the AST listener.
500 */
501 static final class TestASTInstrumentListener extends DefaultStandardInstrumentListener {
502
503 public int counter = 0;
504
505 @Override
506 public void enter(Probe probe, Node node, VirtualFrame vFrame) {
507 counter++;
508 }
509 }
510
511 /**
512 * A counter that can count executions at multiple nodes; it attaches a separate instrument at
513 * each Probe, but keeps a total count.
514 */
515 private static final class TestMultiCounter {
516
517 public int count = 0;
518
519 public void attachCounter(Probe probe) {
520
521 // Attach a new instrument for every Probe
522 // where we want to count executions.
523 // it will get copied when ASTs cloned, so
524 // keep the count in this outer class.
525 probe.attach(Instrument.create(new DefaultSimpleInstrumentListener() {
526
527 @Override
528 public void enter(Probe p) {
529 count++;
530 }
531 }, "Instrumentation Test MultiCounter"));
532 }
533 }
534
535 private static final class TestProbeListener extends DefaultProbeListener {
536
537 public int probeCount = 0;
538 public int tagCount = 0;
539
540 @Override
541 public void newProbeInserted(Probe probe) {
542 probeCount++;
543 }
544
545 @Override
546 public void probeTaggedAs(Probe probe, SyntaxTag tag, Object tagValue) {
547 tagCount++;
548 }
549 }
550 }