Mercurial > hg > graal-compiler
comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/ProbeNode.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 | |
children | 867058575979 |
comparison
equal
deleted
inserted
replaced
18484:e97e1f07a3d6 | 18485:e3c95cbbb50c |
---|---|
1 /* | |
2 * Copyright (c) 2013, 2014, 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 com.oracle.truffle.api.*; | |
28 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; | |
29 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; | |
30 import com.oracle.truffle.api.frame.*; | |
31 import com.oracle.truffle.api.instrument.Instrument.InstrumentNode; | |
32 import com.oracle.truffle.api.nodes.*; | |
33 import com.oracle.truffle.api.source.*; | |
34 | |
35 /** | |
36 * Implementation interfaces and classes for attaching {@link Probe}s to {@link WrapperNode}s. | |
37 */ | |
38 public abstract class ProbeNode extends Node implements TruffleEventReceiver, InstrumentationNode { | |
39 | |
40 /** | |
41 * Any Truffle node implementing this interface can be "instrumented" by installing a | |
42 * {@link Probe} that intercepts execution events at the node and routes them to any | |
43 * {@link Instrument}s that have been attached to the {@link Probe}. Only one {@link Probe} may | |
44 * be installed at each node; subsequent calls return the one already installed. | |
45 * | |
46 * @see Instrument | |
47 */ | |
48 public interface Instrumentable { | |
49 | |
50 /** | |
51 * Enables "instrumentation" of a Guest Language Truffle node, where the node is presumed to | |
52 * be part of a well-formed Truffle AST that is not being executed. The AST may be modified | |
53 * as a side effect. | |
54 * <p> | |
55 * This interface is not intended to be visible as part of the API for tools | |
56 * (instrumentation clients). | |
57 * | |
58 * @return a (possibly newly created) {@link Probe} associated with this node. | |
59 */ | |
60 Probe probe(); | |
61 | |
62 /** | |
63 * Enables a one-time, unchangeable "instrumentation" of a Guest Language Truffle node, | |
64 * where the node is presumed to be part of a well-formed Truffle AST that is not being | |
65 * executed. The AST may be modified as a side-effect. Unlike {@link #probe()}, once | |
66 * {@link #probeLite(TruffleEventReceiver)} is called at a node, no additional probing can | |
67 * be added and no additional instrumentation can be attached. | |
68 * <p> | |
69 * This interface is not intended to be visible as part of the API for tools | |
70 * (instrumentation clients). | |
71 * | |
72 * @param eventReceiver The {@link TruffleEventReceiver} for the single "instrument" being | |
73 * attached to this node. | |
74 */ | |
75 void probeLite(TruffleEventReceiver eventReceiver); | |
76 } | |
77 | |
78 /** | |
79 * A node that can be inserted into a Truffle AST, and which enables <em>instrumentation</em> at | |
80 * a particular Guest Language (GL) node. | |
81 * <p> | |
82 * The implementation must be GL-specific. A wrapper <em>decorates</em> a GL AST node (the | |
83 * wrapper's <em>child</em>) by acting as a transparent <em>proxy</em> with respect to the GL's | |
84 * execution semantics. | |
85 * <p> | |
86 * Instrumentation at the wrapped node is implemented by an instance of {@link ProbeNode} | |
87 * attached as a second child of the {@link WrapperNode}. | |
88 * <p> | |
89 * A wrapper is obliged to notify its attached {@link ProbeNode} when execution events occur at | |
90 * the wrapped AST node during program execution. | |
91 * <p> | |
92 * When a GL AST is cloned, the {@link WrapperNode}, its {@link ProbeNode} and any | |
93 * {@linkplain Instrument instrumentation} are also cloned; they are in effect part of the GL | |
94 * AST. An instance of {@link Probe} represents abstractly the instrumentation at a particular | |
95 * location in a GL AST; it tracks all the copies of the Wrapper and attached instrumentation, | |
96 * and acts as a single point of access for tools. | |
97 * <p> | |
98 * This interface is not intended to be visible as part of the API for tools (instrumentation | |
99 * clients). | |
100 * <p> | |
101 * Implementation guidelines: | |
102 * <ol> | |
103 * <li>Each GL implementation should include a WrapperNode implementation; usually only one is | |
104 * needed.</li> | |
105 * <li>The wrapper type should descend from the <em>GL-specific node class</em>.</li> | |
106 * <li>Must have a field: {@code @Child private <GL>Node child;}</li> | |
107 * <li>Must have a field: {@code @Child private ProbeNode probeNode;}</li> | |
108 * <li>The wrapper must act as a <em>proxy</em> for its child, which means implementing every | |
109 * possible <em>execute-</em> method that gets called on guest language AST node types by their | |
110 * parents, and passing along each call to its child.</li> | |
111 * <li>Method {@code Probe getProbe()} should be implemented as {@code probeNode.getProbe();} | |
112 * <li>Method {@code insertProbe(ProbeNode)} should be implemented as | |
113 * {@code this.probeNode=insert(newProbeNode);}</li> | |
114 * <li>Most importantly, Wrappers must be implemented so that Truffle optimization will reduce | |
115 * their runtime overhead to zero when there are no attached {@link Instrument}s.</li> | |
116 * </ol> | |
117 * <p> | |
118 * <strong>Disclaimer:</strong> experimental interface under development. | |
119 * | |
120 * @see Instrument | |
121 */ | |
122 public interface WrapperNode extends InstrumentationNode { | |
123 | |
124 /** | |
125 * Gets the node being "wrapped", i.e. the AST node for which | |
126 * {@linkplain TruffleEventReceiver execution events} will be reported through the | |
127 * Instrumentation Framework. | |
128 */ | |
129 Node getChild(); | |
130 | |
131 /** | |
132 * Gets the {@link Probe} responsible for installing this wrapper; none if the wrapper | |
133 * installed via {@linkplain Instrumentable#probeLite(TruffleEventReceiver) "lite-Probing"}. | |
134 */ | |
135 Probe getProbe(); | |
136 | |
137 /** | |
138 * Implementation support for completing a newly created wrapper node. | |
139 */ | |
140 void insertProbe(ProbeNode probeNode); | |
141 | |
142 } | |
143 | |
144 /** | |
145 * Create a new {@link Probe} associated with, and attached to, a Guest Language specific | |
146 * instance of {@link WrapperNode}. | |
147 */ | |
148 public static Probe insertProbe(WrapperNode wrapper) { | |
149 final SourceSection sourceSection = wrapper.getChild().getSourceSection(); | |
150 final ProbeFullNode probeFullNode = new ProbeFullNode(); // private constructor | |
151 final Probe probe = new Probe(probeFullNode, sourceSection); // package private access | |
152 probeFullNode.setProbe(probe); | |
153 wrapper.insertProbe(probeFullNode); | |
154 return probe; | |
155 } | |
156 | |
157 /** | |
158 * Creates a new {@link ProbeLiteNode} associated with, and attached to, a Guest Language | |
159 * specific instance of {@link WrapperNode}. | |
160 */ | |
161 public static void insertProbeLite(WrapperNode wrapper, TruffleEventReceiver eventReceiver) { | |
162 final ProbeLiteNode probeLiteNode = new ProbeLiteNode(eventReceiver); | |
163 wrapper.insertProbe(probeLiteNode); | |
164 } | |
165 | |
166 /** | |
167 * @return the {@link Probe} permanently associated with this {@link ProbeNode}. | |
168 * | |
169 * @throws IllegalStateException if this location was "lite-Probed" | |
170 */ | |
171 public abstract Probe getProbe() throws IllegalStateException; | |
172 | |
173 /** | |
174 * Adds an {@link InstrumentNode} to this chain. | |
175 * | |
176 * @throws IllegalStateException if at a "lite-Probed" location. | |
177 */ | |
178 abstract void addInstrument(Instrument instrument); | |
179 | |
180 /** | |
181 * Removes an instrument from this chain of instruments. | |
182 * | |
183 * @throws IllegalStateException if at a "lite-Probed" location. | |
184 * @throws RuntimeException if no matching instrument is found, | |
185 */ | |
186 abstract void removeInstrument(Instrument instrument); | |
187 | |
188 /** | |
189 * Implementation class & interfaces for enabling the attachment of {@linkplain Probe Probes} to | |
190 * Truffle ASTs. | |
191 * <p> | |
192 * Head of a chain of nodes acting on behalf of {@linkplain Instrument instruments}, attached to | |
193 * a Guest Language (GL) AST as a child of a GL-specific {@link WrapperNode} node. | |
194 * <p> | |
195 * When Truffle clones an AST, the chain, including all attached {@linkplain Instrument | |
196 * instruments} will be cloned along with the {@link WrapperNode} to which it is attached. An | |
197 * instance of {@link Probe} represents abstractly the instrumentation at a particular location | |
198 * in a GL AST, tracks the clones of the chain, and keeps the instrumentation attached to the | |
199 * clones consistent. | |
200 */ | |
201 @NodeInfo(cost = NodeCost.NONE) | |
202 private static final class ProbeFullNode extends ProbeNode { | |
203 | |
204 /** | |
205 * First {@link InstrumentNode} node in chain; {@code null} of no instruments in chain. | |
206 */ | |
207 @Child protected InstrumentNode firstInstrument; | |
208 | |
209 // Never changed once set. | |
210 @CompilationFinal private Probe probe = null; | |
211 | |
212 /** | |
213 * An assumption that the state of the {@link Probe} with which this chain is associated has | |
214 * not changed since the last time checking such an assumption failed and a reference to a | |
215 * new assumption (associated with a new state of the {@link Probe} was retrieved. | |
216 */ | |
217 private Assumption probeUnchangedAssumption; | |
218 | |
219 private ProbeFullNode() { | |
220 this.firstInstrument = null; | |
221 } | |
222 | |
223 @Override | |
224 public Probe getProbe() throws IllegalStateException { | |
225 return probe; | |
226 } | |
227 | |
228 @Override | |
229 public Node copy() { | |
230 Node node = super.copy(); | |
231 probe.registerProbeNodeClone((ProbeNode) node); | |
232 return node; | |
233 } | |
234 | |
235 private void setProbe(Probe probe) { | |
236 this.probe = probe; | |
237 this.probeUnchangedAssumption = probe.getUnchangedAssumption(); | |
238 } | |
239 | |
240 private void checkProbeUnchangedAssumption() { | |
241 try { | |
242 probeUnchangedAssumption.check(); | |
243 } catch (InvalidAssumptionException ex) { | |
244 // Failure creates an implicit deoptimization | |
245 // Get the assumption associated with the new probe state | |
246 this.probeUnchangedAssumption = probe.getUnchangedAssumption(); | |
247 } | |
248 } | |
249 | |
250 @Override | |
251 @TruffleBoundary | |
252 void addInstrument(Instrument instrument) { | |
253 assert instrument.getProbe() == probe; | |
254 // The existing chain of nodes may be empty | |
255 // Attach the modified chain. | |
256 firstInstrument = insert(instrument.addToChain(firstInstrument)); | |
257 } | |
258 | |
259 @Override | |
260 @TruffleBoundary | |
261 void removeInstrument(Instrument instrument) { | |
262 assert instrument.getProbe() == probe; | |
263 final InstrumentNode modifiedChain = instrument.removeFromChain(firstInstrument); | |
264 if (modifiedChain == null) { | |
265 firstInstrument = null; | |
266 } else { | |
267 firstInstrument = insert(modifiedChain); | |
268 } | |
269 } | |
270 | |
271 public void enter(Node node, VirtualFrame frame) { | |
272 final SyntaxTagTrap trap = probe.getTrap(); | |
273 if (trap != null) { | |
274 checkProbeUnchangedAssumption(); | |
275 trap.tagTrappedAt(((WrapperNode) this.getParent()).getChild(), frame.materialize()); | |
276 } | |
277 if (firstInstrument != null) { | |
278 checkProbeUnchangedAssumption(); | |
279 firstInstrument.enter(node, frame); | |
280 } | |
281 } | |
282 | |
283 public void returnVoid(Node node, VirtualFrame frame) { | |
284 if (firstInstrument != null) { | |
285 checkProbeUnchangedAssumption(); | |
286 firstInstrument.returnVoid(node, frame); | |
287 } | |
288 } | |
289 | |
290 public void returnValue(Node node, VirtualFrame frame, Object result) { | |
291 if (firstInstrument != null) { | |
292 checkProbeUnchangedAssumption(); | |
293 firstInstrument.returnValue(node, frame, result); | |
294 } | |
295 } | |
296 | |
297 public void returnExceptional(Node node, VirtualFrame frame, Exception exception) { | |
298 if (firstInstrument != null) { | |
299 checkProbeUnchangedAssumption(); | |
300 firstInstrument.returnExceptional(node, frame, exception); | |
301 } | |
302 } | |
303 | |
304 public String instrumentationInfo() { | |
305 return "Standard probe"; | |
306 } | |
307 | |
308 } | |
309 | |
310 /** | |
311 * Implementation of a probe that only ever has a single "instrument" associated with it. No | |
312 * {@link Instrument} is ever created; instead this method simply delegates the various enter | |
313 * and return events to a {@link TruffleEventReceiver} passed in during construction. | |
314 */ | |
315 @NodeInfo(cost = NodeCost.NONE) | |
316 private static final class ProbeLiteNode extends ProbeNode { | |
317 | |
318 private final TruffleEventReceiver eventReceiver; | |
319 | |
320 private ProbeLiteNode(TruffleEventReceiver eventReceiver) { | |
321 this.eventReceiver = eventReceiver; | |
322 } | |
323 | |
324 @Override | |
325 public Probe getProbe() throws IllegalStateException { | |
326 throw new IllegalStateException("\"lite-Probed\" nodes have no explicit Probe"); | |
327 } | |
328 | |
329 @Override | |
330 @TruffleBoundary | |
331 void addInstrument(Instrument instrument) { | |
332 throw new IllegalStateException("Instruments may not be added at a \"lite-probed\" location"); | |
333 } | |
334 | |
335 @Override | |
336 @TruffleBoundary | |
337 void removeInstrument(Instrument instrument) { | |
338 throw new IllegalStateException("Instruments may not be removed at a \"lite-probed\" location"); | |
339 } | |
340 | |
341 public void enter(Node node, VirtualFrame frame) { | |
342 eventReceiver.enter(node, frame); | |
343 } | |
344 | |
345 public void returnVoid(Node node, VirtualFrame frame) { | |
346 eventReceiver.returnVoid(node, frame); | |
347 } | |
348 | |
349 public void returnValue(Node node, VirtualFrame frame, Object result) { | |
350 eventReceiver.returnValue(node, frame, result); | |
351 } | |
352 | |
353 public void returnExceptional(Node node, VirtualFrame frame, Exception exception) { | |
354 eventReceiver.returnExceptional(node, frame, exception); | |
355 } | |
356 | |
357 public String instrumentationInfo() { | |
358 return "\"Lite\" probe"; | |
359 } | |
360 | |
361 } | |
362 } |