Mercurial > hg > truffle
comparison truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java @ 22219:1c0f490984d5
Merge with f47b601edbc626dcfe8b3636933b4834c89f7779
author | Michael Van De Vanter <michael.van.de.vanter@oracle.com> |
---|---|
date | Wed, 16 Sep 2015 15:36:22 -0700 |
parents | dc83cc1f94f2 3aad794eec0e |
children | 776dc0a09749 |
comparison
equal
deleted
inserted
replaced
22160:0599e2df6a9f | 22219:1c0f490984d5 |
---|---|
22 * or visit www.oracle.com if you need additional information or have any | 22 * or visit www.oracle.com if you need additional information or have any |
23 * questions. | 23 * questions. |
24 */ | 24 */ |
25 package com.oracle.truffle.api.instrument; | 25 package com.oracle.truffle.api.instrument; |
26 | 26 |
27 import com.oracle.truffle.api.Assumption; | |
28 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; | |
29 import com.oracle.truffle.api.TruffleLanguage; | |
30 import com.oracle.truffle.api.impl.Accessor; | |
31 import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; | |
32 import com.oracle.truffle.api.nodes.InvalidAssumptionException; | |
33 import com.oracle.truffle.api.nodes.Node; | |
34 import com.oracle.truffle.api.nodes.NodeVisitor; | |
35 import com.oracle.truffle.api.nodes.RootNode; | |
36 import com.oracle.truffle.api.source.Source; | |
37 import com.oracle.truffle.api.source.SourceSection; | |
38 import com.oracle.truffle.api.utilities.CyclicAssumption; | |
39 import java.io.PrintStream; | 27 import java.io.PrintStream; |
40 import java.lang.ref.WeakReference; | 28 import java.lang.ref.WeakReference; |
41 import java.util.ArrayList; | 29 import java.util.ArrayList; |
42 import java.util.Collection; | 30 import java.util.Collection; |
43 import java.util.Collections; | 31 import java.util.Collections; |
44 import java.util.List; | 32 import java.util.List; |
45 | 33 |
46 //TODO (mlvdv) these statics should not be global. Move them to some kind of context. | 34 import com.oracle.truffle.api.Assumption; |
35 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; | |
36 import com.oracle.truffle.api.TruffleLanguage; | |
37 import com.oracle.truffle.api.instrument.InstrumentationNode.TruffleEvents; | |
38 import com.oracle.truffle.api.nodes.InvalidAssumptionException; | |
39 import com.oracle.truffle.api.nodes.Node; | |
40 import com.oracle.truffle.api.source.SourceSection; | |
41 import com.oracle.truffle.api.utilities.CyclicAssumption; | |
47 | 42 |
48 /** | 43 /** |
49 * A <em>binding</em> between: | 44 * A <em>binding</em> between: |
50 * <ol> | 45 * <ol> |
51 * <li>A program location in an executing Truffle AST (corresponding to a {@link SourceSection}), | 46 * <li>A program location in an executing Truffle AST (corresponding to a {@link SourceSection}), |
73 * | 68 * |
74 * <li>The effect of the binding is to intercept {@linkplain TruffleEvents execution events} | 69 * <li>The effect of the binding is to intercept {@linkplain TruffleEvents execution events} |
75 * arriving at the "probed" AST Node and notify each attached {@link Instrument} before execution is | 70 * arriving at the "probed" AST Node and notify each attached {@link Instrument} before execution is |
76 * allowed to proceed to the child and again after execution completes.</li> | 71 * allowed to proceed to the child and again after execution completes.</li> |
77 * | 72 * |
78 * <li>The method {@link Node#probe()} creates a Probe on an AST Node; redundant calls return the | 73 * <li>The method {@link Instrumenter#probe(Node)} creates a Probe on an AST Node; redundant calls |
79 * same Probe.</li> | 74 * return the same Probe.</li> |
80 * | 75 * |
81 * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers | 76 * <li>The "probing" of a Truffle AST must be done after the AST is complete (i.e. parent pointers |
82 * correctly assigned), but before any cloning or executions. This is done by creating an instance | 77 * correctly assigned), but before any cloning or executions. This is done by applying instances of |
83 * of {@link ASTProber} and registering it via {@link #registerASTProber(ASTProber)}. Once | 78 * {@link ASTProber} provided by each language implementation, combined with any instances |
84 * registered, it will be applied automatically to every newly created AST.</li> | 79 * registered by tools via {@link Instrumenter#registerASTProber(ASTProber)}. Once registered, these |
80 * will be applied automatically to every newly created AST.</li> | |
85 * | 81 * |
86 * <li>The "probing" of an AST Node is implemented by insertion of a {@link ProbeNode.WrapperNode} | 82 * <li>The "probing" of an AST Node is implemented by insertion of a {@link ProbeNode.WrapperNode} |
87 * into the AST (as new parent of the Node being probed), together with an associated | 83 * into the AST (as new parent of the Node being probed), together with an associated |
88 * {@link ProbeNode} that routes execution events at the probed Node to all the | 84 * {@link ProbeNode} that routes execution events at the probed Node to all the |
89 * {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li> | 85 * {@linkplain Instrument Instruments} attached to the Probe's <em>instrument chain</em>.</li> |
119 if (TRACE) { | 115 if (TRACE) { |
120 OUT.println(TRACE_PREFIX + String.format(format, args)); | 116 OUT.println(TRACE_PREFIX + String.format(format, args)); |
121 } | 117 } |
122 } | 118 } |
123 | 119 |
124 private static final List<ASTProber> astProbers = new ArrayList<>(); | 120 private final Instrumenter instrumenter; |
125 | |
126 private static final List<ProbeListener> probeListeners = new ArrayList<>(); | |
127 | |
128 /** | |
129 * All Probes that have been created. | |
130 */ | |
131 private static final List<WeakReference<Probe>> probes = new ArrayList<>(); | |
132 | |
133 /** | |
134 * A global trap that triggers notification just before executing any Node that is Probed with a | |
135 * matching tag. | |
136 */ | |
137 @CompilationFinal private static SyntaxTagTrap beforeTagTrap = null; | |
138 | |
139 /** | |
140 * A global trap that triggers notification just after executing any Node that is Probed with a | |
141 * matching tag. | |
142 */ | |
143 @CompilationFinal private static SyntaxTagTrap afterTagTrap = null; | |
144 | |
145 private static final class FindSourceVisitor implements NodeVisitor { | |
146 | |
147 Source source = null; | |
148 | |
149 public boolean visit(Node node) { | |
150 final SourceSection sourceSection = node.getSourceSection(); | |
151 if (sourceSection != null) { | |
152 source = sourceSection.getSource(); | |
153 return false; | |
154 } | |
155 return true; | |
156 } | |
157 } | |
158 | |
159 /** | |
160 * Walks an AST, looking for the first node with an assigned {@link SourceSection} and returning | |
161 * the {@link Source}. | |
162 */ | |
163 private static Source findSource(Node node) { | |
164 final FindSourceVisitor visitor = new FindSourceVisitor(); | |
165 node.accept(visitor); | |
166 return visitor.source; | |
167 } | |
168 | |
169 /** | |
170 * Enables instrumentation at selected nodes in all subsequently constructed ASTs. | |
171 */ | |
172 public static void registerASTProber(ASTProber prober) { | |
173 astProbers.add(prober); | |
174 } | |
175 | |
176 public static void unregisterASTProber(ASTProber prober) { | |
177 astProbers.remove(prober); | |
178 } | |
179 | |
180 /** | |
181 * Enables instrumentation in a newly created AST by applying all registered instances of | |
182 * {@link ASTProber}. | |
183 */ | |
184 public static void applyASTProbers(Node node) { | |
185 | |
186 String name = "<?>"; | |
187 final Source source = findSource(node); | |
188 if (source != null) { | |
189 name = source.getShortName(); | |
190 } else { | |
191 final SourceSection sourceSection = node.getEncapsulatingSourceSection(); | |
192 if (sourceSection != null) { | |
193 name = sourceSection.getShortDescription(); | |
194 } | |
195 } | |
196 trace("START %s", name); | |
197 for (ProbeListener listener : probeListeners) { | |
198 listener.startASTProbing(source); | |
199 } | |
200 for (ASTProber prober : astProbers) { | |
201 prober.probeAST(node); | |
202 } | |
203 for (ProbeListener listener : probeListeners) { | |
204 listener.endASTProbing(source); | |
205 } | |
206 trace("FINISHED %s", name); | |
207 } | |
208 | |
209 /** | |
210 * Adds a {@link ProbeListener} to receive events. | |
211 */ | |
212 public static void addProbeListener(ProbeListener listener) { | |
213 assert listener != null; | |
214 probeListeners.add(listener); | |
215 } | |
216 | |
217 /** | |
218 * Removes a {@link ProbeListener}. Ignored if listener not found. | |
219 */ | |
220 public static void removeProbeListener(ProbeListener listener) { | |
221 probeListeners.remove(listener); | |
222 } | |
223 | |
224 /** | |
225 * Returns all {@link Probe}s holding a particular {@link SyntaxTag}, or the whole collection of | |
226 * probes if the specified tag is {@code null}. | |
227 * | |
228 * @return A collection of probes containing the given tag. | |
229 */ | |
230 public static Collection<Probe> findProbesTaggedAs(SyntaxTag tag) { | |
231 final List<Probe> taggedProbes = new ArrayList<>(); | |
232 for (WeakReference<Probe> ref : probes) { | |
233 Probe probe = ref.get(); | |
234 if (probe != null) { | |
235 if (tag == null || probe.isTaggedAs(tag)) { | |
236 taggedProbes.add(ref.get()); | |
237 } | |
238 } | |
239 } | |
240 return taggedProbes; | |
241 } | |
242 | |
243 // TODO (mlvdv) generalize to permit multiple "before traps" without a performance hit? | |
244 /** | |
245 * Sets the current "<em>before</em> tag trap"; there can be no more than one in effect. | |
246 * <ul> | |
247 * <li>The before-trap triggers a callback just <strong><em>before</em></strong> execution | |
248 * reaches <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) | |
249 * with the specified {@link SyntaxTag}.</li> | |
250 * <li>Setting the before-trap to {@code null} clears an existing before-trap.</li> | |
251 * <li>Setting a non{@code -null} before-trap when one is already set clears the previously set | |
252 * before-trap.</li> | |
253 * </ul> | |
254 * | |
255 * @param newBeforeTagTrap The new "before" {@link SyntaxTagTrap} to set. | |
256 */ | |
257 public static void setBeforeTagTrap(SyntaxTagTrap newBeforeTagTrap) { | |
258 beforeTagTrap = newBeforeTagTrap; | |
259 for (WeakReference<Probe> ref : probes) { | |
260 final Probe probe = ref.get(); | |
261 if (probe != null) { | |
262 probe.notifyTrapsChanged(); | |
263 } | |
264 } | |
265 } | |
266 | |
267 // TODO (mlvdv) generalize to permit multiple "after traps" without a performance hit? | |
268 /** | |
269 * Sets the current "<em>after</em> tag trap"; there can be no more than one in effect. | |
270 * <ul> | |
271 * <li>The after-trap triggers a callback just <strong><em>after</em></strong> execution leaves | |
272 * <strong><em>any</em></strong> {@link Probe} (either existing or subsequently created) with | |
273 * the specified {@link SyntaxTag}.</li> | |
274 * <li>Setting the after-trap to {@code null} clears an existing after-trap.</li> | |
275 * <li>Setting a non{@code -null} after-trap when one is already set clears the previously set | |
276 * after-trap.</li> | |
277 * </ul> | |
278 * | |
279 * @param newAfterTagTrap The new "after" {@link SyntaxTagTrap} to set. | |
280 */ | |
281 public static void setAfterTagTrap(SyntaxTagTrap newAfterTagTrap) { | |
282 afterTagTrap = newAfterTagTrap; | |
283 for (WeakReference<Probe> ref : probes) { | |
284 final Probe probe = ref.get(); | |
285 if (probe != null) { | |
286 probe.notifyTrapsChanged(); | |
287 } | |
288 } | |
289 } | |
290 | |
291 private final SourceSection sourceSection; | 121 private final SourceSection sourceSection; |
292 private final ArrayList<SyntaxTag> tags = new ArrayList<>(); | 122 private final ArrayList<SyntaxTag> tags = new ArrayList<>(); |
293 private final List<WeakReference<ProbeNode>> probeNodeClones = new ArrayList<>(); | 123 private final List<WeakReference<ProbeNode>> probeNodeClones = new ArrayList<>(); |
294 | 124 |
295 /* | 125 /* |
311 @CompilationFinal private boolean isAfterTrapActive = false; | 141 @CompilationFinal private boolean isAfterTrapActive = false; |
312 | 142 |
313 /** | 143 /** |
314 * Intended for use only by {@link ProbeNode}. | 144 * Intended for use only by {@link ProbeNode}. |
315 */ | 145 */ |
316 Probe(Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) { | 146 Probe(Instrumenter instrumenter, Class<? extends TruffleLanguage> l, ProbeNode probeNode, SourceSection sourceSection) { |
147 this.instrumenter = instrumenter; | |
317 this.sourceSection = sourceSection; | 148 this.sourceSection = sourceSection; |
318 probes.add(new WeakReference<>(this)); | |
319 registerProbeNodeClone(probeNode); | 149 registerProbeNodeClone(probeNode); |
320 if (TRACE) { | |
321 final String location = this.sourceSection == null ? "<unknown>" : sourceSection.getShortDescription(); | |
322 trace("ADDED %s %s %s", "Probe@", location, getTagsDescription()); | |
323 } | |
324 for (ProbeListener listener : probeListeners) { | |
325 listener.newProbeInserted(this); | |
326 } | |
327 this.language = l; | 150 this.language = l; |
328 } | 151 } |
329 | 152 |
330 /** | 153 /** |
331 * Is this node tagged as belonging to a particular human-sensible category of language | 154 * Is this node tagged as belonging to a particular human-sensible category of language |
349 */ | 172 */ |
350 public void tagAs(SyntaxTag tag, Object tagValue) { | 173 public void tagAs(SyntaxTag tag, Object tagValue) { |
351 assert tag != null; | 174 assert tag != null; |
352 if (!tags.contains(tag)) { | 175 if (!tags.contains(tag)) { |
353 tags.add(tag); | 176 tags.add(tag); |
354 for (ProbeListener listener : probeListeners) { | 177 instrumenter.tagAdded(this, tag, tagValue); |
355 listener.probeTaggedAs(this, tag, tagValue); | |
356 } | |
357 | 178 |
358 // Update the status of this Probe with respect to global tag traps | 179 // Update the status of this Probe with respect to global tag traps |
359 boolean tagTrapsChanged = false; | 180 boolean tagTrapsChanged = false; |
181 final SyntaxTagTrap beforeTagTrap = instrumenter.getBeforeTagTrap(); | |
360 if (beforeTagTrap != null && tag == beforeTagTrap.getTag()) { | 182 if (beforeTagTrap != null && tag == beforeTagTrap.getTag()) { |
361 this.isBeforeTrapActive = true; | 183 this.isBeforeTrapActive = true; |
362 tagTrapsChanged = true; | 184 tagTrapsChanged = true; |
363 } | 185 } |
186 final SyntaxTagTrap afterTagTrap = instrumenter.getAfterTagTrap(); | |
364 if (afterTagTrap != null && tag == afterTagTrap.getTag()) { | 187 if (afterTagTrap != null && tag == afterTagTrap.getTag()) { |
365 this.isAfterTrapActive = true; | 188 this.isAfterTrapActive = true; |
366 tagTrapsChanged = true; | 189 tagTrapsChanged = true; |
367 } | 190 } |
368 if (tagTrapsChanged) { | 191 if (tagTrapsChanged) { |
437 } | 260 } |
438 | 261 |
439 /** | 262 /** |
440 * Gets the currently active <strong><em>before</em></strong> {@linkplain SyntaxTagTrap Tag | 263 * Gets the currently active <strong><em>before</em></strong> {@linkplain SyntaxTagTrap Tag |
441 * Trap} at this Probe. Non{@code -null} if the global | 264 * Trap} at this Probe. Non{@code -null} if the global |
442 * {@linkplain Probe#setBeforeTagTrap(SyntaxTagTrap) Before Tag Trap} is set and if this Probe | 265 * {@linkplain Instrumenter#setBeforeTagTrap(SyntaxTagTrap) Before Tag Trap} is set and if this |
443 * holds the {@link SyntaxTag} specified in the trap. | 266 * Probe holds the {@link SyntaxTag} specified in the trap. |
444 */ | 267 */ |
445 SyntaxTagTrap getBeforeTrap() { | 268 SyntaxTagTrap getBeforeTrap() { |
446 checkProbeUnchanged(); | 269 checkProbeUnchanged(); |
447 return isBeforeTrapActive ? beforeTagTrap : null; | 270 return isBeforeTrapActive ? instrumenter.getBeforeTagTrap() : null; |
448 } | 271 } |
449 | 272 |
450 /** | 273 /** |
451 * Gets the currently active <strong><em>after</em></strong> {@linkplain SyntaxTagTrap Tag Trap} | 274 * Gets the currently active <strong><em>after</em></strong> {@linkplain SyntaxTagTrap Tag Trap} |
452 * at this Probe. Non{@code -null} if the global | 275 * at this Probe. Non{@code -null} if the global |
453 * {@linkplain Probe#setAfterTagTrap(SyntaxTagTrap) After Tag Trap} is set and if this Probe | 276 * {@linkplain Instrumenter#setAfterTagTrap(SyntaxTagTrap) After Tag Trap} is set and if this |
454 * holds the {@link SyntaxTag} specified in the trap. | 277 * Probe holds the {@link SyntaxTag} specified in the trap. |
455 */ | 278 */ |
456 SyntaxTagTrap getAfterTrap() { | 279 SyntaxTagTrap getAfterTrap() { |
457 checkProbeUnchanged(); | 280 checkProbeUnchanged(); |
458 return isAfterTrapActive ? afterTagTrap : null; | 281 return isAfterTrapActive ? instrumenter.getAfterTagTrap() : null; |
282 } | |
283 | |
284 Class<? extends TruffleLanguage> getLanguage() { | |
285 return language; | |
459 } | 286 } |
460 | 287 |
461 /** | 288 /** |
462 * To be called wherever in the Probe/Instrument chain there are dependencies on the probe | 289 * To be called wherever in the Probe/Instrument chain there are dependencies on the probe |
463 * state's @CompilatonFinal fields. | 290 * state's @CompilatonFinal fields. |
474 | 301 |
475 void invalidateProbeUnchanged() { | 302 void invalidateProbeUnchanged() { |
476 probeStateUnchangedCyclic.invalidate(); | 303 probeStateUnchangedCyclic.invalidate(); |
477 } | 304 } |
478 | 305 |
479 private void notifyTrapsChanged() { | 306 void notifyTrapsChanged() { |
307 final SyntaxTagTrap beforeTagTrap = instrumenter.getBeforeTagTrap(); | |
480 this.isBeforeTrapActive = beforeTagTrap != null && this.isTaggedAs(beforeTagTrap.getTag()); | 308 this.isBeforeTrapActive = beforeTagTrap != null && this.isTaggedAs(beforeTagTrap.getTag()); |
309 final SyntaxTagTrap afterTagTrap = instrumenter.getAfterTagTrap(); | |
481 this.isAfterTrapActive = afterTagTrap != null && this.isTaggedAs(afterTagTrap.getTag()); | 310 this.isAfterTrapActive = afterTagTrap != null && this.isTaggedAs(afterTagTrap.getTag()); |
482 invalidateProbeUnchanged(); | 311 invalidateProbeUnchanged(); |
483 } | 312 } |
484 | 313 |
485 private String getTagsDescription() { | 314 String getTagsDescription() { |
486 final StringBuilder sb = new StringBuilder(); | 315 final StringBuilder sb = new StringBuilder(); |
487 sb.append("["); | 316 sb.append("["); |
488 String prefix = ""; | 317 String prefix = ""; |
489 for (SyntaxTag tag : tags) { | 318 for (SyntaxTag tag : tags) { |
490 sb.append(prefix); | 319 sb.append(prefix); |
492 sb.append(tag.toString()); | 321 sb.append(tag.toString()); |
493 } | 322 } |
494 sb.append("]"); | 323 sb.append("]"); |
495 return sb.toString(); | 324 return sb.toString(); |
496 } | 325 } |
497 | |
498 static final class AccessorInstrument extends Accessor { | |
499 @Override | |
500 protected Class<? extends TruffleLanguage> findLanguage(RootNode n) { | |
501 return super.findLanguage(n); | |
502 } | |
503 | |
504 @Override | |
505 protected Class<? extends TruffleLanguage> findLanguage(Probe probe) { | |
506 return probe.language; | |
507 } | |
508 } | |
509 | |
510 static final AccessorInstrument ACCESSOR = new AccessorInstrument(); | |
511 } | 326 } |