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 }