comparison truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.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/src/com/oracle/truffle/api/instrument/Instrument.java@36285949c1d5
children ff6f34159b8a
comparison
equal deleted inserted replaced
21950:2a5011c7e641 21951:9c8c0937da41
1 /*
2 * Copyright (c) 2013, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25 package com.oracle.truffle.api.instrument;
26
27 import com.oracle.truffle.api.*;
28 import com.oracle.truffle.api.frame.*;
29 import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
30 import com.oracle.truffle.api.nodes.*;
31 import com.oracle.truffle.api.source.*;
32
33 // TODO (mlvdv) these statics should not be global. Move them to some kind of context.
34 // TODO (mlvdv) migrate factory (together with Probe)? break out nested classes?
35
36 /**
37 * A <em>binding</em> between:
38 * <ol>
39 * <li>A {@link Probe}: a source of <em>execution events</em> taking place at a program location in
40 * an executing Truffle AST, and</li>
41 * <li>A <em>listener</em>: a consumer of execution events on behalf of an external client.
42 * </ol>
43 * <p>
44 * Client-oriented documentation for the use of Instruments is available online at <a
45 * HREF="https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events"
46 * >https://wiki.openjdk.java.net/display/Graal/Listening+for+Execution+Events</a>
47 * <p>
48 * The implementation of Instruments is complicated by the requirement that Truffle be able to clone
49 * ASTs at any time. In particular, any instrumentation-supporting Nodes that have been attached to
50 * an AST must be cloned along with the AST: AST clones are not permitted to share Nodes.
51 * <p>
52 * AST cloning is intended to be as <em>transparent</em> as possible to clients. This is encouraged
53 * by providing the {@link SimpleInstrumentListener} for clients that need know nothing more than
54 * the properties associated with a Probe: it's {@link SourceSection} and any associated instances
55 * of {@link SyntaxTag}.
56 * <p>
57 * AST cloning is <em>not transparent</em> to clients that use the
58 * {@link StandardInstrumentListener}, since those event methods identify the concrete Node instance
59 * (and thus the AST instance) where the event takes place.
60 * <p>
61 * <h4>Implementation Notes: the Life Cycle of an {@link Instrument} at a {@link Probe}</h4>
62 * <p>
63 * <ul>
64 * <li>A new Instrument is created in permanent association with a client-provided
65 * <em>listener.</em></li>
66 *
67 * <li>Multiple Instruments may share a single listener.</li>
68 *
69 * <li>An Instrument does nothing until it is {@linkplain Probe#attach(Instrument) attached} to a
70 * Probe, at which time the Instrument begins routing execution events from the Probe's AST location
71 * to the Instrument's listener.</li>
72 *
73 * <li>Neither Instruments nor Probes are {@link Node}s.</li>
74 *
75 * <li>A Probe has a single source-based location in an AST, but manages a separate
76 * <em>instrumentation chain</em> of Nodes at the equivalent location in each clone of the AST.</li>
77 * <li>When a probed AST is cloned, the instrumentation chain associated with each Probe is cloned
78 * along with the rest of the AST.</li>
79 *
80 * <li>When a new Instrument (for example an instance of {@link SimpleInstrument} is attached to a
81 * Probe, the Instrument inserts a new instance of its private Node type,
82 * {@link SimpleInstrument.SimpleInstrumentNode}, into <em>each of the instrument chains</em>
83 * managed by the Probe, i.e. one node instance per existing clone of the AST.</li>
84 *
85 * <li>If an Instrument is attached to a Probe in an AST that subsequently gets cloned, then the
86 * Instrument's private Node type will be cloned along with the rest of the the AST.</li>
87 * <li>Each Instrument's private Node type is a dynamic inner class whose only state is in the
88 * shared (outer) Instrument instance; that state includes a reference to the Instrument's listener.
89 * </li>
90 *
91 * <li>When an Instrument that has been attached to a Probe is {@linkplain #dispose() disposed}, the
92 * Instrument searches every instrument chain associated with the Probe and removes the instance of
93 * its private Node type.</li>
94 *
95 * <li>Attaching and disposing an Instrument at a Probe <em>deoptimizes</em> any compilations of the
96 * AST.</li>
97 *
98 * </ul>
99 *
100 * @see Probe
101 * @see TruffleEvents
102 */
103 public abstract class Instrument {
104
105 /**
106 * Creates a <em>Simple Instrument</em>: this Instrument routes execution events to a
107 * client-provided listener.
108 *
109 * @param listener a listener for execution events
110 * @param instrumentInfo optional description of the instrument's role, intended for debugging.
111 * @return a new instrument, ready for attachment at a probe
112 */
113 public static Instrument create(SimpleInstrumentListener listener, String instrumentInfo) {
114 return new SimpleInstrument(listener, instrumentInfo);
115 }
116
117 /**
118 * Creates a <em>Standard Instrument</em>: this Instrument routes execution events, together
119 * with access to Truffle execution state, to a client-provided listener.
120 *
121 * @param standardListener a listener for execution events and execution state
122 * @param instrumentInfo optional description of the instrument's role, intended for debugging.
123 * @return a new instrument, ready for attachment at a probe
124 */
125 public static Instrument create(StandardInstrumentListener standardListener, String instrumentInfo) {
126 return new StandardInstrument(standardListener, instrumentInfo);
127 }
128
129 /**
130 * Creates an <em>Advanced Instrument</em>: this Instrument executes efficiently, subject to
131 * full Truffle optimization, a client-provided AST fragment every time the Probed node is
132 * entered.
133 * <p>
134 * Any {@link RuntimeException} thrown by execution of the fragment is caught by the framework
135 * and reported to the listener; there is no other notification.
136 *
137 * @param resultListener optional client callback for results/failure notification
138 * @param rootFactory provider of AST fragments on behalf of the client
139 * @param requiredResultType optional requirement, any non-assignable result is reported to the
140 * the listener, if any, as a failure
141 * @param instrumentInfo optional description of the instrument's role, intended for debugging.
142 * @return a new instrument, ready for attachment at a probe
143 */
144 public static Instrument create(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
145 return new AdvancedInstrument(resultListener, rootFactory, requiredResultType, instrumentInfo);
146 }
147
148 // TODO (mlvdv) experimental
149 /**
150 * For implementation testing.
151 */
152 public static Instrument create(TruffleOptListener listener) {
153 return new TruffleOptInstrument(listener, null);
154 }
155
156 /**
157 * Has this instrument been disposed? stays true once set.
158 */
159 private boolean isDisposed = false;
160
161 protected Probe probe = null;
162
163 /**
164 * Optional documentation, mainly for debugging.
165 */
166 private final String instrumentInfo;
167
168 private Instrument(String instrumentInfo) {
169 this.instrumentInfo = instrumentInfo;
170 }
171
172 /**
173 * Gets the {@link Probe} to which this Instrument is currently attached: {@code null} if not
174 * yet attached to a Probe or if this Instrument has been {@linkplain #dispose() disposed}.
175 */
176 public Probe getProbe() {
177 return probe;
178 }
179
180 /**
181 * Removes this Instrument from the Probe to which it attached and renders this Instrument
182 * inert.
183 *
184 * @throws IllegalStateException if this instrument has already been disposed
185 */
186 public void dispose() throws IllegalStateException {
187 if (isDisposed) {
188 throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt");
189 }
190 if (probe != null) {
191 // It's attached
192 probe.disposeInstrument(this);
193 probe = null;
194 }
195 this.isDisposed = true;
196 }
197
198 /**
199 * For internal implementation only.
200 */
201 void setAttachedTo(Probe probe) {
202 this.probe = probe;
203 }
204
205 /**
206 * Has this instrument been disposed and rendered unusable?
207 */
208 boolean isDisposed() {
209 return isDisposed;
210 }
211
212 abstract AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode);
213
214 /**
215 * Removes this instrument from an instrument chain.
216 */
217 abstract AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode);
218
219 /**
220 * An instrument that propagates events to an instance of {@link SimpleInstrumentListener}.
221 */
222 private static final class SimpleInstrument extends Instrument {
223
224 /**
225 * Tool-supplied listener for events.
226 */
227 private final SimpleInstrumentListener simpleListener;
228
229 private SimpleInstrument(SimpleInstrumentListener simpleListener, String instrumentInfo) {
230 super(instrumentInfo);
231 this.simpleListener = simpleListener;
232 }
233
234 @Override
235 AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
236 return new SimpleInstrumentNode(nextNode);
237 }
238
239 @Override
240 AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
241 boolean found = false;
242 if (instrumentNode != null) {
243 if (instrumentNode.getInstrument() == this) {
244 // Found the match at the head of the chain
245 return instrumentNode.nextInstrumentNode;
246 }
247 // Match not at the head of the chain; remove it.
248 found = instrumentNode.removeFromChain(SimpleInstrument.this);
249 }
250 if (!found) {
251 throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
252 }
253 return instrumentNode;
254 }
255
256 /**
257 * Node that implements a {@link SimpleInstrument} in a particular AST.
258 */
259 @NodeInfo(cost = NodeCost.NONE)
260 private final class SimpleInstrumentNode extends AbstractInstrumentNode {
261
262 private SimpleInstrumentNode(AbstractInstrumentNode nextNode) {
263 super(nextNode);
264 }
265
266 public void enter(Node node, VirtualFrame vFrame) {
267 SimpleInstrument.this.simpleListener.enter(SimpleInstrument.this.probe);
268 if (nextInstrumentNode != null) {
269 nextInstrumentNode.enter(node, vFrame);
270 }
271 }
272
273 public void returnVoid(Node node, VirtualFrame vFrame) {
274 SimpleInstrument.this.simpleListener.returnVoid(SimpleInstrument.this.probe);
275 if (nextInstrumentNode != null) {
276 nextInstrumentNode.returnVoid(node, vFrame);
277 }
278 }
279
280 public void returnValue(Node node, VirtualFrame vFrame, Object result) {
281 SimpleInstrument.this.simpleListener.returnValue(SimpleInstrument.this.probe, result);
282 if (nextInstrumentNode != null) {
283 nextInstrumentNode.returnValue(node, vFrame, result);
284 }
285 }
286
287 public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
288 SimpleInstrument.this.simpleListener.returnExceptional(SimpleInstrument.this.probe, exception);
289 if (nextInstrumentNode != null) {
290 nextInstrumentNode.returnExceptional(node, vFrame, exception);
291 }
292 }
293
294 public String instrumentationInfo() {
295 final String info = getInstrumentInfo();
296 return info != null ? info : simpleListener.getClass().getSimpleName();
297 }
298 }
299 }
300
301 /**
302 * An instrument that propagates events to an instance of {@link StandardInstrumentListener}.
303 */
304 private static final class StandardInstrument extends Instrument {
305
306 /**
307 * Tool-supplied listener for AST events.
308 */
309 private final StandardInstrumentListener standardListener;
310
311 private StandardInstrument(StandardInstrumentListener standardListener, String instrumentInfo) {
312 super(instrumentInfo);
313 this.standardListener = standardListener;
314 }
315
316 @Override
317 AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
318 return new StandardInstrumentNode(nextNode);
319 }
320
321 @Override
322 AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
323 boolean found = false;
324 if (instrumentNode != null) {
325 if (instrumentNode.getInstrument() == this) {
326 // Found the match at the head of the chain
327 return instrumentNode.nextInstrumentNode;
328 }
329 // Match not at the head of the chain; remove it.
330 found = instrumentNode.removeFromChain(StandardInstrument.this);
331 }
332 if (!found) {
333 throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
334 }
335 return instrumentNode;
336 }
337
338 /**
339 * Node that implements a {@link StandardInstrument} in a particular AST.
340 */
341 @NodeInfo(cost = NodeCost.NONE)
342 private final class StandardInstrumentNode extends AbstractInstrumentNode {
343
344 private StandardInstrumentNode(AbstractInstrumentNode nextNode) {
345 super(nextNode);
346 }
347
348 public void enter(Node node, VirtualFrame vFrame) {
349 standardListener.enter(StandardInstrument.this.probe, node, vFrame);
350 if (nextInstrumentNode != null) {
351 nextInstrumentNode.enter(node, vFrame);
352 }
353 }
354
355 public void returnVoid(Node node, VirtualFrame vFrame) {
356 standardListener.returnVoid(StandardInstrument.this.probe, node, vFrame);
357 if (nextInstrumentNode != null) {
358 nextInstrumentNode.returnVoid(node, vFrame);
359 }
360 }
361
362 public void returnValue(Node node, VirtualFrame vFrame, Object result) {
363 standardListener.returnValue(StandardInstrument.this.probe, node, vFrame, result);
364 if (nextInstrumentNode != null) {
365 nextInstrumentNode.returnValue(node, vFrame, result);
366 }
367 }
368
369 public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
370 standardListener.returnExceptional(StandardInstrument.this.probe, node, vFrame, exception);
371 if (nextInstrumentNode != null) {
372 nextInstrumentNode.returnExceptional(node, vFrame, exception);
373 }
374 }
375
376 public String instrumentationInfo() {
377 final String info = getInstrumentInfo();
378 return info != null ? info : standardListener.getClass().getSimpleName();
379 }
380 }
381 }
382
383 /**
384 * An instrument that allows clients to provide an AST fragment to be executed directly from
385 * within a Probe's <em>instrumentation chain</em>, and thus directly in the executing Truffle
386 * AST with potential for full optimization.
387 */
388 private static final class AdvancedInstrument extends Instrument {
389
390 private final AdvancedInstrumentResultListener resultListener;
391 private final AdvancedInstrumentRootFactory rootFactory;
392 private final Class<?> requiredResultType;
393
394 private AdvancedInstrument(AdvancedInstrumentResultListener resultListener, AdvancedInstrumentRootFactory rootFactory, Class<?> requiredResultType, String instrumentInfo) {
395 super(instrumentInfo);
396 this.resultListener = resultListener;
397 this.rootFactory = rootFactory;
398 this.requiredResultType = requiredResultType;
399 }
400
401 @Override
402 AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
403 return new AdvancedInstrumentNode(nextNode);
404 }
405
406 @Override
407 AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
408 boolean found = false;
409 if (instrumentNode != null) {
410 if (instrumentNode.getInstrument() == this) {
411 // Found the match at the head of the chain
412 return instrumentNode.nextInstrumentNode;
413 }
414 // Match not at the head of the chain; remove it.
415 found = instrumentNode.removeFromChain(AdvancedInstrument.this);
416 }
417 if (!found) {
418 throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
419 }
420 return instrumentNode;
421 }
422
423 /**
424 * Node that implements a {@link AdvancedInstrument} in a particular AST.
425 */
426 @NodeInfo(cost = NodeCost.NONE)
427 private final class AdvancedInstrumentNode extends AbstractInstrumentNode {
428
429 @Child private AdvancedInstrumentRoot instrumentRoot;
430
431 private AdvancedInstrumentNode(AbstractInstrumentNode nextNode) {
432 super(nextNode);
433 }
434
435 public void enter(Node node, VirtualFrame vFrame) {
436 if (instrumentRoot == null) {
437 try {
438 final AdvancedInstrumentRoot newInstrumentRoot = AdvancedInstrument.this.rootFactory.createInstrumentRoot(AdvancedInstrument.this.probe, node);
439 if (newInstrumentRoot != null) {
440 instrumentRoot = newInstrumentRoot;
441 adoptChildren();
442 AdvancedInstrument.this.probe.invalidateProbeUnchanged();
443 }
444 } catch (RuntimeException ex) {
445 if (resultListener != null) {
446 resultListener.notifyFailure(node, vFrame, ex);
447 }
448 }
449 }
450 if (instrumentRoot != null) {
451 try {
452 final Object result = instrumentRoot.executeRoot(node, vFrame);
453 if (resultListener != null) {
454 checkResultType(result);
455 resultListener.notifyResult(node, vFrame, result);
456 }
457 } catch (RuntimeException ex) {
458 if (resultListener != null) {
459 resultListener.notifyFailure(node, vFrame, ex);
460 }
461 }
462 }
463 if (nextInstrumentNode != null) {
464 nextInstrumentNode.enter(node, vFrame);
465 }
466 }
467
468 private void checkResultType(Object result) {
469 if (requiredResultType == null) {
470 return;
471 }
472 if (result == null) {
473 throw new RuntimeException("Instrument result null: " + requiredResultType.getSimpleName() + " is required");
474 }
475 if (!(requiredResultType.isAssignableFrom(result.getClass()))) {
476 throw new RuntimeException("Instrument result " + result.toString() + " not assignable to " + requiredResultType.getSimpleName());
477 }
478 }
479
480 public void returnVoid(Node node, VirtualFrame vFrame) {
481 if (nextInstrumentNode != null) {
482 nextInstrumentNode.returnVoid(node, vFrame);
483 }
484 }
485
486 public void returnValue(Node node, VirtualFrame vFrame, Object result) {
487 if (nextInstrumentNode != null) {
488 nextInstrumentNode.returnValue(node, vFrame, result);
489 }
490 }
491
492 public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
493 if (nextInstrumentNode != null) {
494 nextInstrumentNode.returnExceptional(node, vFrame, exception);
495 }
496 }
497
498 public String instrumentationInfo() {
499 final String info = getInstrumentInfo();
500 return info != null ? info : rootFactory.getClass().getSimpleName();
501 }
502 }
503 }
504
505 public interface TruffleOptListener {
506 void notifyIsCompiled(boolean isCompiled);
507 }
508
509 private static final class TruffleOptInstrument extends Instrument {
510
511 private final TruffleOptListener toolOptListener;
512
513 private TruffleOptInstrument(TruffleOptListener listener, String instrumentInfo) {
514 super(instrumentInfo);
515 this.toolOptListener = listener;
516 }
517
518 @Override
519 AbstractInstrumentNode addToChain(AbstractInstrumentNode nextNode) {
520 return new TruffleOptInstrumentNode(nextNode);
521 }
522
523 @Override
524 AbstractInstrumentNode removeFromChain(AbstractInstrumentNode instrumentNode) {
525 boolean found = false;
526 if (instrumentNode != null) {
527 if (instrumentNode.getInstrument() == this) {
528 // Found the match at the head of the chain
529 return instrumentNode.nextInstrumentNode;
530 }
531 // Match not at the head of the chain; remove it.
532 found = instrumentNode.removeFromChain(TruffleOptInstrument.this);
533 }
534 if (!found) {
535 throw new IllegalStateException("Couldn't find instrument node to remove: " + this);
536 }
537 return instrumentNode;
538 }
539
540 @NodeInfo(cost = NodeCost.NONE)
541 private final class TruffleOptInstrumentNode extends AbstractInstrumentNode {
542
543 private boolean isCompiled;
544
545 private TruffleOptInstrumentNode(AbstractInstrumentNode nextNode) {
546 super(nextNode);
547 this.isCompiled = CompilerDirectives.inCompiledCode();
548 }
549
550 public void enter(Node node, VirtualFrame vFrame) {
551 if (this.isCompiled != CompilerDirectives.inCompiledCode()) {
552 this.isCompiled = CompilerDirectives.inCompiledCode();
553 TruffleOptInstrument.this.toolOptListener.notifyIsCompiled(this.isCompiled);
554 }
555 if (nextInstrumentNode != null) {
556 nextInstrumentNode.enter(node, vFrame);
557 }
558 }
559
560 public void returnVoid(Node node, VirtualFrame vFrame) {
561 if (nextInstrumentNode != null) {
562 nextInstrumentNode.returnVoid(node, vFrame);
563 }
564 }
565
566 public void returnValue(Node node, VirtualFrame vFrame, Object result) {
567 if (nextInstrumentNode != null) {
568 nextInstrumentNode.returnValue(node, vFrame, result);
569 }
570 }
571
572 public void returnExceptional(Node node, VirtualFrame vFrame, Exception exception) {
573 if (nextInstrumentNode != null) {
574 nextInstrumentNode.returnExceptional(node, vFrame, exception);
575 }
576 }
577
578 public String instrumentationInfo() {
579 final String info = getInstrumentInfo();
580 return info != null ? info : toolOptListener.getClass().getSimpleName();
581 }
582 }
583 }
584
585 @NodeInfo(cost = NodeCost.NONE)
586 abstract class AbstractInstrumentNode extends Node implements TruffleEvents, InstrumentationNode {
587
588 @Child protected AbstractInstrumentNode nextInstrumentNode;
589
590 protected AbstractInstrumentNode(AbstractInstrumentNode nextNode) {
591 this.nextInstrumentNode = nextNode;
592 }
593
594 @Override
595 public boolean isInstrumentable() {
596 return false;
597 }
598
599 /**
600 * Gets the instrument that created this node.
601 */
602 private Instrument getInstrument() {
603 return Instrument.this;
604 }
605
606 /**
607 * Removes the node from this chain that was added by a particular instrument, assuming that
608 * the head of the chain is not the one to be replaced. This is awkward, but is required
609 * because {@link Node#replace(Node)} won't take a {@code null} argument. This doesn't work
610 * for the tail of the list, which would be replacing itself with null. So the replacement
611 * must be directed the parent of the node being removed.
612 */
613 private boolean removeFromChain(Instrument instrument) {
614 assert getInstrument() != instrument;
615 if (nextInstrumentNode == null) {
616 return false;
617 }
618 if (nextInstrumentNode.getInstrument() == instrument) {
619 // Next is the one to remove
620 if (nextInstrumentNode.nextInstrumentNode == null) {
621 // Next is at the tail; just forget
622 nextInstrumentNode = null;
623 } else {
624 // Replace next with its successor
625 nextInstrumentNode.replace(nextInstrumentNode.nextInstrumentNode);
626 }
627 return true;
628 }
629 return nextInstrumentNode.removeFromChain(instrument);
630 }
631
632 protected String getInstrumentInfo() {
633 return Instrument.this.instrumentInfo;
634 }
635 }
636 }