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

com.oracle.truffle.api.debug.DebugStackFrame 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: 24.1.1
Show newest version
/*
 * Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * The Universal Permissive License (UPL), Version 1.0
 *
 * Subject to the condition set forth below, permission is hereby granted to any
 * person obtaining a copy of this software, associated documentation and/or
 * data (collectively the "Software"), free of charge and under any and all
 * copyright rights in the Software, and any and all patent rights owned or
 * freely licensable by each licensor hereunder covering either (i) the
 * unmodified Software as contributed to or provided by such licensor, or (ii)
 * the Larger Works (as defined below), to deal in both
 *
 * (a) the Software, and
 *
 * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
 * one is included with the Software each a "Larger Work" to which the Software
 * is contributed by such licensors),
 *
 * without restriction, including without limitation the rights to copy, create
 * derivative works of, display, perform, and distribute the Software and make,
 * use, sell, offer for sale, import, export, have made, and have sold the
 * Software and the Larger Work(s), and to sublicense the foregoing rights on
 * either these or other terms.
 *
 * This license is subject to the following condition:
 *
 * The above copyright notice and either this complete permission notice or at a
 * minimum a reference to the UPL must be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.oracle.truffle.api.debug;

import java.util.Objects;

import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.debug.DebugValue.HeapValue;
import com.oracle.truffle.api.frame.Frame;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
import com.oracle.truffle.api.instrumentation.InstrumentableNode;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.NodeLibrary;
import com.oracle.truffle.api.nodes.LanguageInfo;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;

/**
 * Represents a frame in the guest language stack. A guest language stack frame consists of a
 * {@link #getName() name}, the current {@link #getSourceSection() source location} and
 * {@link #getScope() scopes} containing local variables and arguments. Furthermore it allows to
 * {@link #eval(String) evaluate} guest language expressions in the lexical context of a particular
 * frame.
 * 

* Debug stack frames are only valid as long as {@link SuspendedEvent suspended events} are valid. * Suspended events are valid as long while the originating {@link SuspendedCallback} is still * executing. All methods of the frame throw {@link IllegalStateException} if they become invalid. *

* Depending on the method, clients may access the stack frame only on the execution thread where * the suspended event of the stack frame was created and the notification was received. For some * methods, access from other threads may throw {@link IllegalStateException}. Please see the * javadoc of the individual method for details. * * @see SuspendedEvent#getStackFrames() * @see SuspendedEvent#getTopStackFrame() * @since 0.17 */ public final class DebugStackFrame { final SuspendedEvent event; private final FrameInstance currentFrame; private final StackTraceElement hostTraceElement; private final int depth; // The frame depth on guest stack. 0 is the top frame private final String name; private final DebugException nameEx; DebugStackFrame(SuspendedEvent session, FrameInstance instance, int depth) { this.event = session; this.currentFrame = instance; this.hostTraceElement = null; this.depth = depth; String frameName = null; DebugException frameNameEx = null; try { frameName = initName(); } catch (DebugException ex) { frameNameEx = ex; } this.name = frameName; this.nameEx = frameNameEx; } DebugStackFrame(SuspendedEvent session, StackTraceElement hostElement, int depth) { this.event = session; this.currentFrame = null; this.hostTraceElement = hostElement; this.depth = depth; this.name = hostElement.getClassName() + '.' + hostElement.getMethodName(); this.nameEx = null; } // Initialize the stack frame name while we're on the execution thread private String initName() throws DebugException { verifyValidState(false); Node node; if (currentFrame == null) { node = getContext().getInstrumentedNode(); } else { node = currentFrame.getCallNode(); node = InstrumentableNode.findInstrumentableParent(node); } try { if (node != null) { Frame frame = findTruffleFrame(FrameAccess.READ_ONLY); NodeLibrary nodeLibrary = NodeLibrary.getUncached(); if (nodeLibrary.hasRootInstance(node, frame)) { Object instance = nodeLibrary.getRootInstance(node, frame); InteropLibrary interop = InteropLibrary.getUncached(); if (interop.hasExecutableName(instance)) { return interop.asString(interop.getExecutableName(instance)); } } } RootNode root = findCurrentRoot(); if (root == null) { return null; } return root.getName(); } catch (ThreadDeath td) { throw td; } catch (Throwable ex) { RootNode root = findCurrentRoot(); LanguageInfo languageInfo = root != null ? root.getLanguageInfo() : null; throw DebugException.create(event.getSession(), ex, languageInfo); } } /** * Returns whether this stack frame is a language implementation artifact that should be hidden * during normal guest language debugging, for example in stack traces. *

* Language implementations sometimes create method calls internally that do not correspond to * anything explicitly written by a programmer, for example when the body of a looping construct * is implemented as callable block. Language implementors mark these methods as * internal. *

*

* Clients of the debugging API should assume that displaying internal frames is * unlikely to help programmers debug guest language programs and might possibly create * confusion. However, clients may choose to display all frames, for example in a special mode * to support development of programming language implementations. *

*

* The decision to mark a method as internal is language-specific, reflects judgments * about tool usability, and is subject to change. *

* This method is thread-safe. * * @since 0.17 */ public boolean isInternal() { verifyValidState(true); if (isHost()) { return false; } RootNode root = findCurrentRoot(); if (root == null) { return true; } return root.isInternal(); } /** * Returns true if this frame is a host frame. Host frames provide * {@link #getHostTraceElement() stack trace element}, have no {@link #getScope() scope}, no * {@link #getSourceSection() source section} and can not {@link #eval(String) evaluate} code. *

* Host frames are provided only when {@link DebuggerSession#setShowHostStackFrames(boolean) * host info} is set to true. * * @since 20.3 * @see DebuggerSession#setShowHostStackFrames(boolean) */ public boolean isHost() { return hostTraceElement != null; } /** * Provides a host frame. Returns the host stack trace element if and only if this is * {@link #isHost() host} frame. * * @return the host stack trace element, or null when not a host frame. * @since 20.3 * @see #isHost() */ public StackTraceElement getHostTraceElement() { return hostTraceElement; } /** * A description of the AST (expected to be a method or procedure name in most languages) that * identifies the AST for the benefit of guest language programmers using tools; it might * appear, for example in the context of a stack dump or trace and is not expected to be called * often. If the language does not provide such a description then null is * returned. * *

* This method is thread-safe. * * @since 0.17 */ public String getName() throws DebugException { verifyValidState(true); if (nameEx != null) { throw nameEx; } return name; } /** * Returns the source section of the location where the debugging session was suspended. The * source section is null if the source location is not available. * *

* This method is thread-safe. * * @since 0.17 */ public SourceSection getSourceSection() { verifyValidState(true); if (isHost()) { return null; } if (currentFrame == null) { SuspendedContext context = getContext(); return event.getSession().resolveSection(context.getInstrumentedSourceSection()); } else { Node callNode = currentFrame.getCallNode(); if (callNode != null) { return event.getSession().resolveSection(callNode); } return null; } } /** * Returns public information about the language of this frame. * * @return the language info, or null when no language is associated with this * frame. * @since 19.0 */ public LanguageInfo getLanguage() { verifyValidState(true); if (isHost()) { return null; } RootNode root = findCurrentRoot(); if (root == null) { return null; } return root.getLanguageInfo(); } /** * Get the current inner-most scope. The scope remain valid as long as the current stack frame * remains valid. *

* Use {@link DebuggerSession#getTopScope(java.lang.String)} to get scopes with global validity. *

* This method is not thread-safe and will throw an {@link IllegalStateException} if called on * another thread than it was created with. * * @return the scope, or null when no language is associated with this frame * location, or when no local scope exists. * @throws DebugException when guest language code throws an exception * @since 0.26 */ public DebugScope getScope() throws DebugException { verifyValidState(false); if (isHost()) { return null; } SuspendedContext context = getContext(); RootNode root = findCurrentRoot(); if (root == null) { return null; } Node node; if (currentFrame == null) { node = context.getInstrumentedNode(); } else { node = currentFrame.getCallNode(); if (node == null) { return null; } node = InstrumentableNode.findInstrumentableParent(node); } DebuggerSession session = event.getSession(); Frame frame = findTruffleFrame(FrameAccess.READ_WRITE); try { if (!NodeLibrary.getUncached().hasScope(node, frame)) { return null; } Object scope = NodeLibrary.getUncached().getScope(node, frame, isEnter()); return new DebugScope(scope, session, event, node, frame, root); } catch (ThreadDeath td) { throw td; } catch (Throwable ex) { throw DebugException.create(session, ex, root.getLanguageInfo()); } } private boolean isEnter() { return depth == 0 && SuspendAnchor.BEFORE.equals(event.getSuspendAnchor()); } /** * Returns the current node for this stack frame, or null if the requesting * language class does not match the root node guest language. * * This method is permitted only if the guest language class is available. This is the case if * you want to utilize the Debugger API directly from within a guest language, or if you are an * instrument bound/dependent on a specific language. * * @param languageClass the Truffle language class for a given guest language * @return the node associated with the frame * * @since 20.1 */ public Node getRawNode(Class> languageClass) { Objects.requireNonNull(languageClass); RootNode rootNode = findCurrentRoot(); if (rootNode == null) { return null; } // check if language class of the root node corresponds to the input language TruffleLanguage language = Debugger.ACCESSOR.nodeSupport().getLanguage(rootNode); return language != null && language.getClass() == languageClass ? getCurrentNode() : null; } /** * Returns the underlying frame for this debug stack frame or null if the * requesting language class does not match the root node guest language. * * This method is permitted only if the guest language class is available. This is the case if * you want to utilize the Debugger API directly from within a guest language, or if you are an * instrument bound/dependent on a specific language. * * @param languageClass the Truffle language class for a given guest language * @param access the frame access mode * @return the frame * * @since 20.1 */ public Frame getRawFrame(Class> languageClass, FrameAccess access) { Objects.requireNonNull(languageClass); RootNode rootNode = findCurrentRoot(); if (rootNode == null) { return null; } // check if language class of the root node corresponds to the input language TruffleLanguage language = Debugger.ACCESSOR.nodeSupport().getLanguage(rootNode); return language != null && language.getClass() == languageClass ? findTruffleFrame(access) : null; } DebugValue wrapHeapValue(Object result) { assert !isHost() : "Can not wrap values in host frames."; LanguageInfo language; RootNode root = findCurrentRoot(); if (root != null) { language = root.getLanguageInfo(); } else { language = null; } return new HeapValue(event.getSession(), language, null, result); } /** * Evaluates the given code in the state of the current execution and in the same guest language * as the current language is defined in. Returns a heap value that remains valid even if this * stack frame becomes invalid. * *

* This method is not thread-safe and will throw an {@link IllegalStateException} if called on * another thread than it was created with. * * @param code the code to evaluate * @return the return value of the expression * @throws DebugException when guest language code throws an exception * @throws IllegalStateException if called on another thread than this frame was created with, * or if {@link #getLanguage() language} of this frame is not * {@link LanguageInfo#isInteractive() interactive}. * @since 0.17 */ public DebugValue eval(String code) throws DebugException { verifyValidState(false); if (isHost()) { throw new IllegalStateException("Can not evaluate code in host frames."); } Object result = DebuggerSession.evalInContext(event, code, currentFrame); return wrapHeapValue(result); } /** * @since 19.0 */ @Override public boolean equals(Object obj) { if (obj instanceof DebugStackFrame) { DebugStackFrame other = (DebugStackFrame) obj; return event == other.event && hostTraceElement == other.hostTraceElement && (currentFrame == other.currentFrame || currentFrame != null && other.currentFrame != null && currentFrame.getFrame(FrameAccess.READ_ONLY) == other.currentFrame.getFrame(FrameAccess.READ_ONLY)); } return false; } /** * @since 19.0 */ @Override public int hashCode() { return Objects.hash(event, currentFrame); } Frame findTruffleFrame(FrameAccess access) { assert !isHost() : "No Truffle frame in host stack frame"; if (currentFrame == null) { // The top frame has already been materialized // so we can safely return that frame return event.getMaterializedFrame(); } else { return currentFrame.getFrame(access); } } int getDepth() { return depth; } private SuspendedContext getContext() { SuspendedContext context = event.getContext(); if (context == null) { // there is a race condition here if the event // got disposed between the parent verifyValidState and getContext. // if the context is null we assume the event got disposed so we re-check // the disposed flag. return null should therefore not be reachable. verifyValidState(true); assert false : "should not be reachable"; } return context; } RootNode findCurrentRoot() { if (isHost()) { return null; } SuspendedContext context = getContext(); if (currentFrame == null) { return context.getInstrumentedNode().getRootNode(); } else { return ((RootCallTarget) currentFrame.getCallTarget()).getRootNode(); } } RootCallTarget getCallTarget() { if (isHost()) { return null; } SuspendedContext context = getContext(); if (currentFrame == null) { return context.getInstrumentedNode().getRootNode().getCallTarget(); } else { return (RootCallTarget) currentFrame.getCallTarget(); } } Node getCurrentNode() { if (isHost()) { return null; } if (currentFrame == null) { return getContext().getInstrumentedNode(); } else { Node callNode = currentFrame.getCallNode(); if (callNode != null) { return callNode; } CallTarget target = currentFrame.getCallTarget(); if (target instanceof RootCallTarget) { return ((RootCallTarget) target).getRootNode(); } return null; } } void verifyValidState(boolean allowDifferentThread) { event.verifyValidState(allowDifferentThread); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy