All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.oracle.truffle.api.instrumentation.ProbeNode Maven / Gradle / Ivy

Go to download

Truffle is a multi-language framework for executing dynamic languages that achieves high performance when combined with Graal.

There is a newer version: 1.0.0-rc7
Show newest version
/*
 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.truffle.api.instrumentation;

import java.io.PrintStream;

import com.oracle.truffle.api.Assumption;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.instrumentation.InstrumentableFactory.WrapperNode;
import com.oracle.truffle.api.instrumentation.InstrumentationHandler.InstrumentClientInstrumenter;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.source.SourceSection;

/**
 * 

* Represents an event sink for instrumentation events that is embedded in the AST using wrappers if * needed. Instances of this class are provided by * {@link InstrumentableFactory#createWrapper(Node, ProbeNode)} to notify the instrumentation API * about execution events. *

* * The recommended use of this node for implementing {@link WrapperNode wrapper nodes} looks as * follows: * *
 * @Override
 * public Object execute(VirtualFrame frame) {
 *     try {
 *         probeNode.onEnter(frame);
 *         Object returnValue = delegate.execute(frame);
 *         probeNode.onReturnValue(frame, returnValue);
 *         return returnValue;
 *     } catch (Throwable t) {
 *         probeNode.onReturnExceptional(frame, t);
 *         throw t;
 *     }
 * }
 * 
* * @since 0.12 */ public final class ProbeNode extends Node { private final InstrumentationHandler handler; private final EventContext context; @Child private ProbeNode.EventChainNode chain; /* * We cache to ensure that the instrumented tags and source sections are always compilation * final for listeners and factories. */ @CompilationFinal private Assumption version; /** Instantiated by the instrumentation framework. */ ProbeNode(InstrumentationHandler handler, SourceSection sourceSection) { this.handler = handler; this.context = new EventContext(this, sourceSection); } /** * Should get invoked before the node is invoked. * * @param frame the current frame of the execution. * @since 0.12 */ public void onEnter(VirtualFrame frame) { if (lazyUpdate(frame)) { chain.onEnter(context, frame); } } /** * Should get invoked after the node is invoked successfully. * * @param result the result value of the operation * @param frame the current frame of the execution. * @since 0.12 */ public void onReturnValue(VirtualFrame frame, Object result) { if (lazyUpdate(frame)) { chain.onReturnValue(context, frame, result); } } /** * Should get invoked if the node did not complete successfully. * * @param exception the exception that occurred during the execution * @param frame the current frame of the execution. * @since 0.12 */ public void onReturnExceptional(VirtualFrame frame, Throwable exception) { if (exception instanceof ThreadDeath) { throw (ThreadDeath) exception; } if (lazyUpdate(frame)) { chain.onReturnExceptional(context, frame, exception); } } EventContext getContext() { return context; } WrapperNode findWrapper() throws AssertionError { Node parent = getParent(); if (!(parent instanceof WrapperNode)) { if (parent == null) { throw new AssertionError("Probe node disconnected from AST."); } else { throw new AssertionError("ProbeNodes must have a parent Node that implements NodeWrapper."); } } return (WrapperNode) parent; } void invalidate() { if (version != null) { version.invalidate(); } else { assert chain == null; } } private boolean lazyUpdate(VirtualFrame frame) { if (version == null || !version.isValid()) { CompilerDirectives.transferToInterpreterAndInvalidate(); // Ok to pass in the virtual frame as its instances are always materialized return lazyUpdatedImpl(frame); } return true; } private boolean lazyUpdatedImpl(VirtualFrame frame) { Node nextChain = handler.installBindings(ProbeNode.this); if (nextChain == null) { // chain is null -> remove wrapper; // Note: never set child nodes to null, can cause races InstrumentationHandler.removeWrapper(ProbeNode.this); return false; } EventChainNode oldChain = this.chain; if (oldChain != null) { oldChain.onDispose(context, frame); } this.chain = (EventChainNode) insert(nextChain); this.version = Truffle.getRuntime().createAssumption("Instruments unchanged"); return true; } ProbeNode.EventChainNode createEventChainCallback(EventBinding binding) { ProbeNode.EventChainNode next; Object element = binding.getElement(); if (element instanceof ExecutionEventListener) { next = new EventFilterChainNode(binding, (ExecutionEventListener) element); } else { assert element instanceof ExecutionEventNodeFactory; ExecutionEventNode eventNode = createEventNode(binding, element); if (eventNode == null) { // error occurred creating the event node return null; } next = new EventProviderChainNode(binding, eventNode); } return next; } private ExecutionEventNode createEventNode(EventBinding binding, Object element) { ExecutionEventNode eventNode; try { eventNode = ((ExecutionEventNodeFactory) element).create(context); if (eventNode.getParent() != null) { throw new IllegalStateException(String.format("Returned EventNode %s was already adopted by another AST.", eventNode)); } } catch (Throwable t) { if (binding.isLanguageBinding()) { /* Language bindings can just throw exceptions directly into the AST. */ throw t; } else { /* * Client Instruments are not allowed to disrupt program execution by throwing * exceptions into the AST. */ exceptionEventForClientInstrument(binding, "ProbeNodeFactory.create", t); return null; } } return eventNode; } /** * Handles exceptions from non-language instrumentation code that must not be allowed to alter * guest language execution semantics. Normal response is to log and continue. */ static void exceptionEventForClientInstrument(EventBinding b, String eventName, Throwable t) { assert !b.isLanguageBinding(); if (t instanceof ThreadDeath) { // Terminates guest language execution immediately throw (ThreadDeath) t; } // Exception is a failure in (non-language) instrumentation code; log and continue InstrumentClientInstrumenter instrumenter = (InstrumentClientInstrumenter) b.getInstrumenter(); Class instrumentClass = instrumenter.getInstrumentClass(); String message = String.format("Event %s failed for instrument class %s and listener/factory %s.", // eventName, instrumentClass.getName(), b.getElement()); Exception exception = new Exception(message, t); PrintStream stream = new PrintStream(instrumenter.getEnv().err()); exception.printStackTrace(stream); } /** @since 0.12 */ @Override public NodeCost getCost() { return NodeCost.NONE; } abstract static class EventChainNode extends Node { @Child private ProbeNode.EventChainNode next; private final EventBinding binding; @CompilationFinal private boolean seenException; EventChainNode(EventBinding binding) { this.binding = binding; } final void setNext(ProbeNode.EventChainNode next) { this.next = insert(next); } EventBinding getBinding() { return binding; } ProbeNode.EventChainNode getNext() { return next; } @Override public final NodeCost getCost() { return NodeCost.NONE; } final void onDispose(EventContext context, VirtualFrame frame) { try { innerOnDispose(context, frame); } catch (Throwable t) { if (!seenException) { CompilerDirectives.transferToInterpreterAndInvalidate(); seenException = true; } if (binding.isLanguageBinding()) { throw t; } else { CompilerDirectives.transferToInterpreter(); exceptionEventForClientInstrument(binding, "onEnter", t); } } if (next != null) { next.onDispose(context, frame); } } protected abstract void innerOnDispose(EventContext context, VirtualFrame frame); final void onEnter(EventContext context, VirtualFrame frame) { try { innerOnEnter(context, frame); } catch (Throwable t) { if (!seenException) { CompilerDirectives.transferToInterpreterAndInvalidate(); seenException = true; } if (binding.isLanguageBinding()) { throw t; } else { CompilerDirectives.transferToInterpreter(); exceptionEventForClientInstrument(binding, "onEnter", t); } } if (next != null) { next.onEnter(context, frame); } } protected abstract void innerOnEnter(EventContext context, VirtualFrame frame); final void onReturnValue(EventContext context, VirtualFrame frame, Object result) { try { innerOnReturnValue(context, frame, result); } catch (Throwable t) { if (!seenException) { CompilerDirectives.transferToInterpreterAndInvalidate(); seenException = true; } if (binding.isLanguageBinding()) { throw t; } else { CompilerDirectives.transferToInterpreter(); exceptionEventForClientInstrument(binding, "onReturnValue", t); } } if (next != null) { next.onReturnValue(context, frame, result); } } protected abstract void innerOnReturnValue(EventContext context, VirtualFrame frame, Object result); final void onReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) { try { innerOnReturnExceptional(context, frame, exception); } catch (Throwable t) { if (!seenException) { CompilerDirectives.transferToInterpreterAndInvalidate(); seenException = true; } if (binding.isLanguageBinding()) { exception.addSuppressed(t); } else { CompilerDirectives.transferToInterpreter(); exceptionEventForClientInstrument(binding, "onReturnExceptional", t); } } if (next != null) { next.onReturnExceptional(context, frame, exception); } } protected abstract void innerOnReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception); } private static class EventFilterChainNode extends ProbeNode.EventChainNode { private final ExecutionEventListener listener; EventFilterChainNode(EventBinding binding, ExecutionEventListener listener) { super(binding); this.listener = listener; } @Override protected void innerOnEnter(EventContext context, VirtualFrame frame) { listener.onEnter(context, frame); } @Override protected void innerOnReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) { listener.onReturnExceptional(context, frame, exception); } @Override protected void innerOnReturnValue(EventContext context, VirtualFrame frame, Object result) { listener.onReturnValue(context, frame, result); } @Override protected void innerOnDispose(EventContext context, VirtualFrame frame) { } } private static class EventProviderChainNode extends ProbeNode.EventChainNode { @Child private ExecutionEventNode eventNode; EventProviderChainNode(EventBinding binding, ExecutionEventNode eventNode) { super(binding); this.eventNode = eventNode; } @Override protected void innerOnEnter(EventContext context, VirtualFrame frame) { eventNode.onEnter(frame); } @Override protected void innerOnReturnExceptional(EventContext context, VirtualFrame frame, Throwable exception) { eventNode.onReturnExceptional(frame, exception); } @Override protected void innerOnReturnValue(EventContext context, VirtualFrame frame, Object result) { eventNode.onReturnValue(frame, result); } @Override protected void innerOnDispose(EventContext context, VirtualFrame frame) { eventNode.onDispose(frame); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy