comparison graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/instrument/InstrumentationTest.java @ 18989:acd822f17ef5

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