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