comparison truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.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/Probe.java@fd8a92655fbd
children 5bc7f7b867ab
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 java.io.*;
28 import java.lang.ref.*;
29 import java.util.*;
30
31 import com.oracle.truffle.api.*;
32 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
33 import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents;
34 import com.oracle.truffle.api.nodes.*;
35 import com.oracle.truffle.api.source.*;
36 import com.oracle.truffle.api.utilities.*;
37
38 //TODO (mlvdv) these statics should not be global. Move them to some kind of context.
39
40 /**
41 * A <em>binding</em> between:
42 * <ol>
43 * <li>A program location in an executing Truffle AST (corresponding to a {@link SourceSection}),
44 * and</li>
45 * <li>A dynamically managed collection of "attached" {@linkplain Instrument Instruments} that
46 * receive event notifications on behalf of external clients.</li>
47 * </ol>
48 * <p>
49 * Client-oriented documentation for the use of Probes is available online at <a
50 * HREF="https://wiki.openjdk.java.net/display/Graal/Finding+Probes"
51 * >https://wiki.openjdk.java.net/display/Graal/Finding+Probes</a>
52 * <p>
53 * <h4>Implementation notes:</h4>
54 * <p>
55 * <ul>
56 * <li>A Probe must be permanently associated with a <em>program location</em>, defined by a
57 * particular {@link SourceSection}, even though:
58 * <ul>
59 * <li>that location is represented in an AST as a {@link Node}, which might be replaced through
60 * optimizations such as specialization, and</li>
61 * <li>Truffle may <em>clone</em> the AST so that the location is actually represented by multiple
62 * Nodes in multiple ASTs.</li>
63 * </ul>
64 * </li>
65 *
66 * <li>The effect of the binding is to intercept {@linkplain TruffleEvents execution events}
67 * arriving at the "probed" AST Node and notify each attached {@link Instrument} before execution is
68 * allowed to proceed to the child and again after execution completes.</li>
69 *
70 * <li>The method {@link Node#probe()} creates a Probe on an AST Node; redundant calls return the
71 * same Probe.</li>
72 *
73 * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers
74 * correctly assigned), but before any cloning or executions. This is done by creating an instance
75 * of {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}. Once
76 * registered, it will be applied automatically to every newly created AST.</li>
77 *
78 * <li>The "probing" of an AST Node is implemented by insertion of a {@link ProbeNode.WrapperNode}
79 * into the AST (as new parent of the Node being probed), together with an associated
80 * {@link ProbeNode} that routes execution events at the probed Node to all the
81 * {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li>
82 *
83 * <li>When Truffle clones an AST, any attached WrapperNodes and ProbeNodes are cloned as well,
84 * together with their attached instrument chains. Each Probe instance intercepts cloning events and
85 * keeps track of all AST copies.</li>
86 *
87 * <li>All attached {@link InstrumentationNode}s effectively become part of the running program:
88 * <ul>
89 * <li>Good News: instrumentation code implicitly benefits from every kind of Truffle optimization.</li>
90 * <li>Bad News: instrumentation code must be implemented carefully to avoid interfering with any
91 * Truffle optimizations.</li>
92 * </ul>
93 * </li>
94 *
95 * </ul>
96 *
97 * @see Instrument
98 * @see ASTProber
99 * @see ProbeListener
100 * @see SyntaxTag
101 */
102 public final class Probe {
103
104 private static final boolean TRACE = false;
105 private static final String TRACE_PREFIX = "PROBE: ";
106 private static final PrintStream OUT = System.out;
107
108 private static void trace(String format, Object... args) {
109 if (TRACE) {
110 OUT.println(TRACE_PREFIX + String.format(format, args));
111 }
112 }
113
114 private static final List<ASTProber> astProbers = new ArrayList<>();
115
116 private static final List<ProbeListener> probeListeners = new ArrayList<>();
117
118 /**
119 * All Probes that have been created.
120 */
121 private static final List<WeakReference<Probe>> probes = new ArrayList<>();
122
123 /**
124 * A global trap that triggers notification just before executing any Node that is Probed with a
125 * matching tag.
126 */
127 @CompilationFinal private static SyntaxTagTrap beforeTagTrap = null;
128
129 /**
130 * A global trap that triggers notification just after executing any Node that is Probed with a
131 * matching tag.
132 */
133 @CompilationFinal private static SyntaxTagTrap afterTagTrap = null;
134
135 private static final class FindSourceVisitor implements NodeVisitor {
136
137 Source source = null;
138
139 public boolean visit(Node node) {
140 final SourceSection sourceSection = node.getSourceSection();
141 if (sourceSection != null) {
142 source = sourceSection.getSource();
143 return false;
144 }
145 return true;
146 }
147 }
148
149 /**
150 * Walks an AST, looking for the first node with an assigned {@link SourceSection} and returning
151 * the {@link Source}.
152 */
153 private static Source findSource(Node node) {
154 final FindSourceVisitor visitor = new FindSourceVisitor();
155 node.accept(visitor);
156 return visitor.source;
157 }
158
159 /**
160 * Enables instrumentation at selected nodes in all subsequently constructed ASTs.
161 */
162 public static void registerASTProber(ASTProber prober) {
163 astProbers.add(prober);
164 }
165
166 public static void unregisterASTProber(ASTProber prober) {
167 astProbers.remove(prober);
168 }
169
170 /**
171 * Enables instrumentation in a newly created AST by applying all registered instances of
172 * {@link ASTProber}.
173 */
174 public static void applyASTProbers(Node node) {
175
176 String name = "<?>";
177 final Source source = findSource(node);
178 if (source != null) {
179 name = source.getShortName();
180 } else {
181 final SourceSection sourceSection = node.getEncapsulatingSourceSection();
182 if (sourceSection != null) {
183 name = sourceSection.getShortDescription();
184 }
185 }
186 trace("START %s", name);
187 for (ProbeListener listener : probeListeners) {
188 listener.startASTProbing(source);
189 }
190 for (ASTProber prober : astProbers) {
191 prober.probeAST(node);
192 }
193 for (ProbeListener listener : probeListeners) {
194 listener.endASTProbing(source);
195 }
196 trace("FINISHED %s", name);
197 }
198
199 /**
200 * Adds a {@link ProbeListener} to receive events.
201 */
202 public static void addProbeListener(ProbeListener listener) {
203 assert listener != null;
204 probeListeners.add(listener);
205 }
206
207 /**
208 * Removes a {@link ProbeListener}. Ignored if listener not found.
209 */
210 public static void removeProbeListener(ProbeListener listener) {
211 probeListeners.remove(listener);
212 }
213
214 /**
215 * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of
216 * probes if the specified tag is {@code null}.
217 *
218 * @return A collection of probes containing the given tag.
219 */
220 public static Collection<Probe> findProbesTaggedAs(SyntaxTag tag) {
221 final List<Probe> taggedProbes = new ArrayList<>();
222 for (WeakReference<Probe> ref : probes) {
223 Probe probe = ref.get();
224 if (probe != null) {
225 if (tag == null || probe.isTaggedAs(tag)) {
226 taggedProbes.add(ref.get());
227 }
228 }
229 }
230 return taggedProbes;
231 }
232
233 // TODO (mlvdv) generalize to permit multiple "before traps" without a performance hit?
234 /**
235 * Sets the current "<em>before</em> tag trap"; there can be no more than one in effect.
236 * <ul>
237 * <li>The before-trap triggers a callback just <strong><em>before</em></strong> execution
238 * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created)
239 * with the specified {@link SyntaxTag}.</li>
240 * <li>Setting the before-trap to {@code null} clears an existing before-trap.</li>
241 * <li>Setting a non{@code -null} before-trap when one is already set clears the previously set
242 * before-trap.</li>
243 * </ul>
244 *
245 * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set.
246 */
247 public static void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) {
248 beforeTagTrap = newBeforeTagTrap;
249 for (WeakReference<Probe> ref : probes) {
250 final Probe probe = ref.get();
251 if (probe != null) {
252 probe.notifyTrapsChanged();
253 }
254 }
255 }
256
257 // TODO (mlvdv) generalize to permit multiple "after traps" without a performance hit?
258 /**
259 * Sets the current "<em>after</em> tag trap"; there can be no more than one in effect.
260 * <ul>
261 * <li>The after-trap triggers a callback just <strong><em>after</em></strong> execution leaves
262 * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with
263 * the specified {@link SyntaxTag}.</li>
264 * <li>Setting the after-trap to {@code null} clears an existing after-trap.</li>
265 * <li>Setting a non{@code -null} after-trap when one is already set clears the previously set
266 * after-trap.</li>
267 * </ul>
268 *
269 * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set.
270 */
271 public static void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) {
272 afterTagTrap = newAfterTagTrap;
273 for (WeakReference<Probe> ref : probes) {
274 final Probe probe = ref.get();
275 if (probe != null) {
276 probe.notifyTrapsChanged();
277 }
278 }
279 }
280
281 private final SourceSection sourceSection;
282 private final ArrayList<SyntaxTag> tags = new ArrayList<>();
283 private final List<WeakReference<ProbeNode>> probeNodeClones = new ArrayList<>();
284
285 /*
286 * Invalidated whenever something changes in the Probe and its Instrument chain, so need deopt
287 */
288 private final CyclicAssumption probeStateUnchangedCyclic = new CyclicAssumption("Probe state unchanged");
289
290 /*
291 * The assumption that nothing had changed in this probe, the last time anybody checked (when
292 * there may have been a deopt). Every time a check fails, gets replaced by a new unchanged
293 * assumption.
294 */
295 @CompilationFinal private Assumption probeStateUnchangedAssumption = probeStateUnchangedCyclic.getAssumption();
296
297 // Must invalidate whenever changed
298 @CompilationFinal private boolean isBeforeTrapActive = false;
299
300 // Must invalidate whenever changed
301 @CompilationFinal private boolean isAfterTrapActive = false;
302
303 /**
304 * Intended for use only by {@link ProbeNode}.
305 */
306 Probe(ProbeNode probeNode, SourceSection sourceSection) {
307 this.sourceSection = sourceSection;
308 probes.add(new WeakReference<>(this));
309 registerProbeNodeClone(probeNode);
310 if (TRACE) {
311 final String location = this.sourceSection == null ? "<unknown>" : sourceSection.getShortDescription();
312 trace("ADDED %s %s %s", "Probe@", location, getTagsDescription());
313 }
314 for (ProbeListener listener : probeListeners) {
315 listener.newProbeInserted(this);
316 }
317 }
318
319 /**
320 * Is this node tagged as belonging to a particular human-sensible category of language
321 * constructs?
322 */
323 public boolean isTaggedAs(SyntaxTag tag) {
324 assert tag != null;
325 return tags.contains(tag);
326 }
327
328 /**
329 * In which user-sensible categories has this node been tagged (<em>empty set</em> if none).
330 */
331 public Collection<SyntaxTag> getSyntaxTags() {
332 return Collections.unmodifiableCollection(tags);
333 }
334
335 /**
336 * Adds a {@linkplain SyntaxTag tag} to the set of tags associated with this {@link Probe};
337 * {@code no-op} if already in the set.
338 */
339 public void tagAs(SyntaxTag tag, Object tagValue) {
340 assert tag != null;
341 if (!tags.contains(tag)) {
342 tags.add(tag);
343 for (ProbeListener listener : probeListeners) {
344 listener.probeTaggedAs(this, tag, tagValue);
345 }
346
347 // Update the status of this Probe with respect to global tag traps
348 boolean tagTrapsChanged = false;
349 if (beforeTagTrap != null && tag == beforeTagTrap.getTag()) {
350 this.isBeforeTrapActive = true;
351 tagTrapsChanged = true;
352 }
353 if (afterTagTrap != null && tag == afterTagTrap.getTag()) {
354 this.isAfterTrapActive = true;
355 tagTrapsChanged = true;
356 }
357 if (tagTrapsChanged) {
358 invalidateProbeUnchanged();
359 }
360 if (TRACE) {
361 trace("TAGGED as %s: %s", tag, getShortDescription());
362 }
363 }
364 }
365
366 /**
367 * Adds instrumentation at this Probe.
368 *
369 * @param instrument an instrument not yet attached to a probe
370 * @throws IllegalStateException if the instrument has ever been attached before
371 */
372 public void attach(Instrument instrument) throws IllegalStateException {
373 if (instrument.isDisposed()) {
374 throw new IllegalStateException("Attempt to attach disposed instrument");
375 }
376 if (instrument.getProbe() != null) {
377 throw new IllegalStateException("Attampt to attach an already attached instrument");
378 }
379 instrument.setAttachedTo(this);
380 for (WeakReference<ProbeNode> ref : probeNodeClones) {
381 final ProbeNode probeNode = ref.get();
382 if (probeNode != null) {
383 probeNode.addInstrument(instrument);
384 }
385 }
386 invalidateProbeUnchanged();
387 }
388
389 /**
390 * Gets the {@link SourceSection} associated with the Guest Language AST node being
391 * instrumented, possibly {@code null}.
392 */
393 public SourceSection getProbedSourceSection() {
394 return sourceSection;
395 }
396
397 public String getShortDescription() {
398 final String location = sourceSection == null ? "<unknown>" : sourceSection.getShortDescription();
399 return "Probe@" + location + getTagsDescription();
400 }
401
402 /**
403 * Internal method for removing and rendering inert a specific instrument previously attached at
404 * this Probe.
405 *
406 * @param instrument an instrument already attached
407 * @throws IllegalStateException if instrument not attached at this Probe
408 * @see Instrument#dispose()
409 */
410 void disposeInstrument(Instrument instrument) throws IllegalStateException {
411 for (WeakReference<ProbeNode> ref : probeNodeClones) {
412 final ProbeNode probeNode = ref.get();
413 if (probeNode != null) {
414 probeNode.removeInstrument(instrument);
415 }
416 }
417 invalidateProbeUnchanged();
418 }
419
420 /**
421 * Receives notification that a new clone of the instrument chain associated with this
422 * {@link Probe} has been created as a side-effect of AST cloning.
423 */
424 void registerProbeNodeClone(ProbeNode probeNode) {
425 probeNodeClones.add(new WeakReference<>(probeNode));
426 }
427
428 /**
429 * Gets the currently active <strong><em>before</em></strong> {@linkplain SyntaxTagTrap Tag
430 * Trap} at this Probe. Non{@code -null} if the global
431 * {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Before Tag Trap} is set and if this Probe
432 * holds the {@link SyntaxTag} specified in the trap.
433 */
434 SyntaxTagTrap getBeforeTrap() {
435 checkProbeUnchanged();
436 return isBeforeTrapActive ? beforeTagTrap : null;
437 }
438
439 /**
440 * Gets the currently active <strong><em>after</em></strong> {@linkplain SyntaxTagTrap Tag Trap}
441 * at this Probe. Non{@code -null} if the global
442 * {@linkplain Probe#setAfterTagTrap(SyntaxTagTrap) After Tag Trap} is set and if this Probe
443 * holds the {@link SyntaxTag} specified in the trap.
444 */
445 SyntaxTagTrap getAfterTrap() {
446 checkProbeUnchanged();
447 return isAfterTrapActive ? afterTagTrap : null;
448 }
449
450 /**
451 * To be called wherever in the Probe/Instrument chain there are dependencies on the probe
452 * state's @CompilatonFinal fields.
453 */
454 void checkProbeUnchanged() {
455 try {
456 probeStateUnchangedAssumption.check();
457 } catch (InvalidAssumptionException ex) {
458 // Failure creates an implicit deoptimization
459 // Get the assumption associated with the new probe state
460 this.probeStateUnchangedAssumption = probeStateUnchangedCyclic.getAssumption();
461 }
462 }
463
464 void invalidateProbeUnchanged() {
465 probeStateUnchangedCyclic.invalidate();
466 }
467
468 private void notifyTrapsChanged() {
469 this.isBeforeTrapActive = beforeTagTrap != null && this.isTaggedAs(beforeTagTrap.getTag());
470 this.isAfterTrapActive = afterTagTrap != null && this.isTaggedAs(afterTagTrap.getTag());
471 invalidateProbeUnchanged();
472 }
473
474 private String getTagsDescription() {
475 final StringBuilder sb = new StringBuilder();
476 sb.append("[");
477 String prefix = "";
478 for (SyntaxTag tag : tags) {
479 sb.append(prefix);
480 prefix = ",";
481 sb.append(tag.toString());
482 }
483 sb.append("]");
484 return sb.toString();
485 }
486 }