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 }