Mercurial > hg > truffle
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 } |