Mercurial > hg > graal-compiler
comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java @ 18485:e3c95cbbb50c
Truffle Instrumentation: major API revision, based around the Probe and Instrument classes; add Instrumentable API for language implementors, with most details automated; reimplemented to handle AST splitting automatically; more JUnit tests.
author | Michael Van De Vanter <michael.van.de.vanter@oracle.com> |
---|---|
date | Sun, 23 Nov 2014 16:07:23 -0800 |
parents | 85cec9cab17b |
children | 867058575979 |
comparison
equal
deleted
inserted
replaced
18484:e97e1f07a3d6 | 18485:e3c95cbbb50c |
---|---|
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.CompilerDirectives.CompilationFinal; | |
28 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; | 27 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; |
28 import com.oracle.truffle.api.*; | |
29 import com.oracle.truffle.api.frame.*; | 29 import com.oracle.truffle.api.frame.*; |
30 import com.oracle.truffle.api.instrument.impl.*; | 30 import com.oracle.truffle.api.instrument.impl.*; |
31 import com.oracle.truffle.api.nodes.*; | 31 import com.oracle.truffle.api.nodes.*; |
32 | 32 |
33 // TODO (mlvdv) migrate some of this to external documentation. | |
33 /** | 34 /** |
34 * A receiver of Truffle AST runtime {@link ExecutionEvents}, propagated from the {@link Probe} to | 35 * A dynamically added/removed binding between a {@link Probe}, which provides notification of |
35 * which the instrument is attached, for the benefit of associated <em>tools</em>. | 36 * {@linkplain TruffleEventReceiver execution events} taking place at a {@link Node} in a Guest |
36 * <p> | 37 * Language (GL) Truffle AST, and a {@linkplain TruffleEventReceiver receiver}, which consumes |
37 * Guidelines for implementing {@link Instrument}s, with particular attention to minimize runtime | 38 * notifications on behalf of an external tool. |
38 * performance overhead: | 39 * <p> |
40 * <h4>Summary: How to "instrument" an AST location:</h4> | |
39 * <ol> | 41 * <ol> |
40 * <li>Extend this abstract class and override only the {@linkplain ExecutionEvents event handling | 42 * <li>Create an implementation of {@link TruffleEventReceiver} that responds to events on behalf of |
41 * methods} for which intervention is needed.</li> | 43 * a tool.</li> |
42 * <li>Instruments are Truffle {@link Node}s and should be coded as much as possible in the desired | 44 * <li>Create an Instrument via factory method {@link Instrument#create(TruffleEventReceiver)}.</li> |
43 * <em>Truffle style</em>, documented more thoroughly elsewhere.</li> | 45 * <li>"Attach" the Instrument to a Probe via {@link Probe#attach(Instrument)}, at which point event |
44 * <li>Maintain as little state as possible.</li> | 46 * notifications begin to arrive at the receiver.</li> |
45 * <li>If state is necessary, make object fields {@code final} if at all possible.</li> | 47 * <li>When no longer needed, "detach" the Instrument via {@link Instrument#dispose()}, at which |
46 * <li>If non-final object-valued state is necessary, annotate it as {@link CompilationFinal} and | 48 * point event notifications to the receiver cease, and the Instrument becomes unusable.</li> |
47 * call {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.</li> | |
48 * <li>Never store a {@link Frame} value in a field.</li> | |
49 * <li>Minimize computation in standard execution paths.</li> | |
50 * <li>If runtime calls must be made back to a tool, construct the instrument with a callback stored | |
51 * in a {@code final} field.</li> | |
52 * <li>Tool methods called by the instrument should be annotated as {@link TruffleBoundary} to | |
53 * prevent them from being inlined into fast execution paths.</li> | |
54 * <li>If computation in the execution path is needed, and if performance is important, then the | |
55 * computation is best expressed as a guest language AST and evaluated using standard Truffle | |
56 * mechanisms so that standard Truffle optimizations can be applied.</li> | |
57 * </ol> | 49 * </ol> |
58 * <p> | 50 * <p> |
59 * Guidelines for attachment to a {@link Probe}: | 51 * <h4>Options for creating receivers:</h4> |
52 * <p> | |
60 * <ol> | 53 * <ol> |
61 * <li>An Instrument instance must only attached to a single {@link Probe}, each of which is | 54 * <li>Implement the interface {@link TruffleEventReceiver}. The event handling methods account for |
62 * associated uniquely with a specific syntactic unit of a guest language program, and thus | 55 * both the entry into an AST node (about to call) and several possible kinds of exit from an AST |
63 * (initially) to a specific {@linkplain Node Truffle AST node}.</li> | 56 * node (just returned).</li> |
64 * <li>When the AST containing such a node is copied at runtime, the {@link Probe} will be shared by | 57 * <li>Extend {@link DefaultEventReceiver}, which provides a no-op implementation of every |
65 * every copy, and so the Instrument will receive events corresponding to the intended syntactic | 58 * {@link TruffleEventReceiver} method; override the methods of interest.</li> |
66 * unit of code, independent of which AST copy is being executed.</li> | 59 * <li>Extend {@link SimpleEventReceiver}, where return values are ignored so only two methods (for |
60 * "enter" and "return") will notify all events.</li> | |
67 * </ol> | 61 * </ol> |
68 * <p> | 62 * <p> |
69 * Guidelines for handling {@link ExecutionEvents}: | 63 * <h4>General guidelines for receiver implementation:</h4> |
70 * <ol> | 64 * <p> |
71 * <li>Separate event methods are defined for each kind of possible return: object-valued, | 65 * When an Instrument is attached to a Probe, the receiver effectively becomes part of the executing |
72 * primitive-valued, void-valued, and exceptional.</li> | 66 * GL program; performance can be affected by the receiver's implementation. |
73 * <li>Override "leave*" primitive methods if the language implementation returns primitives and the | 67 * <ul> |
74 * instrument should avoid boxing them.</li> | 68 * <li>Do not store {@link Frame} or {@link Node} references in fields.</li> |
75 * <li>On the other hand, if boxing all primitives for instrumentation is desired, it is only | 69 * <li>Prefer {@code final} fields and (where performance is important) short methods.</li> |
76 * necessary to override the object-valued return methods, since the default implementation of each | 70 * <li>If needed, pass along the {@link VirtualFrame} reference from an event notification as far as |
77 * primitive-valued return method is to box the value and forward it to the object-valued return | 71 * possible through code that is expected to be inlined, since this incurs no runtime overhead. When |
78 * method.</li> | 72 * access to frame data is needed, substitute a more expensive {@linkplain Frame#materialize() |
79 * </ol> | 73 * materialized} representation of the frame.</li> |
80 * | 74 * <li>If a receiver calls back to its tool during event handling, and if performance is an issue, |
75 * then this should be through a final "callback" field in the instrument, and the called methods | |
76 * should be minimal.</li> | |
77 * <li>On the other hand, implementations should prevent Truffle from inlining beyond a reasonable | |
78 * point with the method annotation {@link TruffleBoundary}.</li> | |
79 * <li>The implicit "outer" pointer in a non-static inner class is a useful way to implement | |
80 * callbacks to owner tools.</li> | |
81 * <li>Primitive-valued return events are boxed for notification, but Truffle will eliminate the | |
82 * boxing if they are cast back to their primitive values quickly (in particular before crossing any | |
83 * {@link TruffleBoundary} annotations). | |
84 * </ul> | |
85 * <p> | |
86 * <h4>Allowing for AST cloning:</h4> | |
87 * <p> | |
88 * Truffle routinely <em>clones</em> ASTs, which has consequences for receiver implementation. | |
89 * <ul> | |
90 * <li>Even though a {@link Probe} is uniquely associated with a particular location in the | |
91 * executing Guest Language program, execution events at that location will in general be | |
92 * implemented by different {@link Node} instances, i.e. <em>clones</em> of the originally probed | |
93 * node.</li> | |
94 * <li>Because of <em>cloning</em> the {@link Node} supplied with notifications to a particular | |
95 * receiver will vary, but because they all represent the same GL program location the events should | |
96 * be treated as equivalent for most purposes.</li> | |
97 * </ul> | |
98 * <p> | |
99 * <h4>Access to execution state:</h4> | |
100 * <p> | |
101 * <ul> | |
102 * <li>Event notification arguments provide primary access to the GL program's execution states: | |
103 * <ul> | |
104 * <li>{@link Node}: the concrete node (in one of the AST's clones) from which the event originated. | |
105 * </li> | |
106 * <li>{@link VirtualFrame}: the current execution frame. | |
107 * </ul> | |
108 * <li>Some global information is available, for example the execution | |
109 * {@linkplain TruffleRuntime#iterateFrames(FrameInstanceVisitor) stack}.</li> | |
110 * <li>Additional information needed by a receiver could be stored when created, preferably | |
111 * {@code final} of course. For example, a reference to the {@link Probe} to which the receiver's | |
112 * Instrument has been attached would give access to its corresponding | |
113 * {@linkplain Probe#getProbedSourceSection() source location} or to the collection of | |
114 * {@linkplain SyntaxTag tags} currently applied to the Probe.</li> | |
115 * </ul> | |
116 * <p> | |
117 * <h4>Activating and deactivating Instruments:</h4> | |
118 * <p> | |
119 * Instruments are <em>single-use</em>: | |
120 * <ul> | |
121 * <li>An instrument becomes active only when <em>attached</em> to a Probe via | |
122 * {@link Probe#attach(Instrument)}, and it may only be attached to a single Probe. It is a runtime | |
123 * error to attempt attaching a previously attached instrument.</li> | |
124 * <li>Attaching an instrument modifies every existing clone of the AST to which it is being | |
125 * attached, which can trigger deoptimization.</li> | |
126 * <li>The method {@link Instrument#dispose()} makes an instrument inactive by removing it from the | |
127 * Probe to which it was attached and rendering it permanently inert.</li> | |
128 * <li>Disposal removes the implementation of an instrument from all ASTs to which it was attached, | |
129 * which can trigger deoptimization.</li> | |
130 * </ul> | |
131 * <p> | |
132 * <h4>Sharing receivers:</h4> | |
133 * <p> | |
134 * Although an Instrument may only be attached to a single Probe, a receiver can be shared among | |
135 * multiple Instruments. This can be useful for observing events that might happen at different | |
136 * locations in a single AST, for example all assignments to a particular variable. In this case a | |
137 * new Instrument would be created and attached at each assignment node, but all the Instruments | |
138 * would be created with the same receiver. | |
81 * <p> | 139 * <p> |
82 * <strong>Disclaimer:</strong> experimental; under development. | 140 * <strong>Disclaimer:</strong> experimental; under development. |
83 * | 141 * |
84 * @see Probe | 142 * @see Probe |
85 * @see ASTNodeProber | 143 * @see TruffleEventReceiver |
86 */ | 144 */ |
87 public abstract class Instrument extends InstrumentationNode { | 145 public final class Instrument { |
88 | 146 |
89 protected Instrument() { | 147 /** |
90 } | 148 * Creates an instrument that will route execution events to a receiver. |
91 | 149 * |
92 public void enter(Node astNode, VirtualFrame frame) { | 150 * @param receiver a receiver for event generated by the instrument |
93 } | 151 * @param instrumentInfo optional description of the instrument's role |
94 | 152 * @return a new instrument, ready for attachment at a probe |
95 public void leave(Node astNode, VirtualFrame frame) { | 153 */ |
96 } | 154 public static Instrument create(TruffleEventReceiver receiver, String instrumentInfo) { |
97 | 155 return new Instrument(receiver, instrumentInfo); |
98 public void leave(Node astNode, VirtualFrame frame, boolean result) { | 156 } |
99 leave(astNode, frame, (Object) result); | 157 |
100 } | 158 /** |
101 | 159 * Creates an instrument that will route execution events to a receiver. |
102 public void leave(Node astNode, VirtualFrame frame, byte result) { | 160 */ |
103 leave(astNode, frame, (Object) result); | 161 public static Instrument create(TruffleEventReceiver receiver) { |
104 } | 162 return new Instrument(receiver, null); |
105 | 163 } |
106 public void leave(Node astNode, VirtualFrame frame, short result) { | 164 |
107 leave(astNode, frame, (Object) result); | 165 /** |
108 } | 166 * Tool-supplied receiver of events. |
109 | 167 */ |
110 public void leave(Node astNode, VirtualFrame frame, int result) { | 168 private final TruffleEventReceiver toolEventreceiver; |
111 leave(astNode, frame, (Object) result); | 169 |
112 } | 170 /** |
113 | 171 * Optional documentation, mainly for debugging. |
114 public void leave(Node astNode, VirtualFrame frame, long result) { | 172 */ |
115 leave(astNode, frame, (Object) result); | 173 private final String instrumentInfo; |
116 } | 174 |
117 | 175 /** |
118 public void leave(Node astNode, VirtualFrame frame, char result) { | 176 * Has this instrument been disposed? stays true once set. |
119 leave(astNode, frame, (Object) result); | 177 */ |
120 } | 178 private boolean isDisposed = false; |
121 | 179 |
122 public void leave(Node astNode, VirtualFrame frame, float result) { | 180 private Probe probe = null; |
123 leave(astNode, frame, (Object) result); | 181 |
124 } | 182 private Instrument(TruffleEventReceiver receiver, String instrumentInfo) { |
125 | 183 this.toolEventreceiver = receiver; |
126 public void leave(Node astNode, VirtualFrame frame, double result) { | 184 this.instrumentInfo = instrumentInfo; |
127 leave(astNode, frame, (Object) result); | 185 } |
128 } | 186 |
129 | 187 /** |
130 public void leave(Node astNode, VirtualFrame frame, Object result) { | 188 * Removes this instrument (and any clones) from the probe to which it attached and renders the |
131 } | 189 * instrument inert. |
132 | 190 * |
133 public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { | 191 * @throws IllegalStateException if this instrument has already been disposed |
192 */ | |
193 public void dispose() throws IllegalStateException { | |
194 if (isDisposed) { | |
195 throw new IllegalStateException("Attempt to dispose an already disposed Instrumennt"); | |
196 } | |
197 if (probe != null) { | |
198 // It's attached | |
199 probe.disposeInstrument(this); | |
200 } | |
201 this.isDisposed = true; | |
202 } | |
203 | |
204 Probe getProbe() { | |
205 return probe; | |
206 } | |
207 | |
208 void setAttachedTo(Probe probe) { | |
209 this.probe = probe; | |
210 } | |
211 | |
212 /** | |
213 * Has this instrument been disposed and rendered unusable? | |
214 */ | |
215 boolean isDisposed() { | |
216 return isDisposed; | |
217 } | |
218 | |
219 InstrumentNode addToChain(InstrumentNode nextNode) { | |
220 return new InstrumentNode(nextNode); | |
221 } | |
222 | |
223 /** | |
224 * Removes this instrument from an instrument chain. | |
225 */ | |
226 InstrumentNode removeFromChain(InstrumentNode instrumentNode) { | |
227 boolean found = false; | |
228 if (instrumentNode != null) { | |
229 if (instrumentNode.getInstrument() == this) { | |
230 // Found the match at the head of the chain | |
231 return instrumentNode.nextInstrument; | |
232 } | |
233 // Match not at the head of the chain; remove it. | |
234 found = instrumentNode.removeFromChain(Instrument.this); | |
235 } | |
236 if (!found) { | |
237 throw new IllegalStateException("Couldn't find instrument node to remove: " + this); | |
238 } | |
239 return instrumentNode; | |
240 } | |
241 | |
242 @NodeInfo(cost = NodeCost.NONE) | |
243 final class InstrumentNode extends Node implements TruffleEventReceiver, InstrumentationNode { | |
244 | |
245 @Child private InstrumentNode nextInstrument; | |
246 | |
247 private InstrumentNode(InstrumentNode nextNode) { | |
248 this.nextInstrument = nextNode; | |
249 } | |
250 | |
251 /** | |
252 * Gets the instrument that created this node. | |
253 */ | |
254 private Instrument getInstrument() { | |
255 return Instrument.this; | |
256 } | |
257 | |
258 /** | |
259 * Removes the node from this chain that was added by a particular instrument, assuming that | |
260 * the head of the chain is not the one to be replaced. This is awkward, but is required | |
261 * because {@link Node#replace(Node)} won't take a {@code null} argument. This doesn't work | |
262 * for the tail of the list, which would be replacing itself with null. So the replacement | |
263 * must be directed the parent of the node being removed. | |
264 */ | |
265 private boolean removeFromChain(Instrument instrument) { | |
266 assert getInstrument() != instrument; | |
267 if (nextInstrument == null) { | |
268 return false; | |
269 } | |
270 if (nextInstrument.getInstrument() == instrument) { | |
271 // Next is the one to remove | |
272 if (nextInstrument.nextInstrument == null) { | |
273 // Next is at the tail; just forget | |
274 nextInstrument = null; | |
275 } else { | |
276 // Replace next with its successor | |
277 nextInstrument.replace(nextInstrument.nextInstrument); | |
278 } | |
279 return true; | |
280 } | |
281 return nextInstrument.removeFromChain(instrument); | |
282 } | |
283 | |
284 public void enter(Node node, VirtualFrame frame) { | |
285 Instrument.this.toolEventreceiver.enter(node, frame); | |
286 if (nextInstrument != null) { | |
287 nextInstrument.enter(node, frame); | |
288 } | |
289 } | |
290 | |
291 public void returnVoid(Node node, VirtualFrame frame) { | |
292 Instrument.this.toolEventreceiver.returnVoid(node, frame); | |
293 if (nextInstrument != null) { | |
294 nextInstrument.returnVoid(node, frame); | |
295 } | |
296 } | |
297 | |
298 public void returnValue(Node node, VirtualFrame frame, Object result) { | |
299 Instrument.this.toolEventreceiver.returnValue(node, frame, result); | |
300 if (nextInstrument != null) { | |
301 nextInstrument.returnValue(node, frame, result); | |
302 } | |
303 } | |
304 | |
305 public void returnExceptional(Node node, VirtualFrame frame, Exception exception) { | |
306 Instrument.this.toolEventreceiver.returnExceptional(node, frame, exception); | |
307 if (nextInstrument != null) { | |
308 nextInstrument.returnExceptional(node, frame, exception); | |
309 } | |
310 } | |
311 | |
312 public String instrumentationInfo() { | |
313 if (Instrument.this.instrumentInfo != null) { | |
314 return Instrument.this.instrumentInfo; | |
315 } | |
316 return toolEventreceiver.getClass().getSimpleName(); | |
317 } | |
134 } | 318 } |
135 | 319 |
136 } | 320 } |