/*
* Copyright (c) 2017, 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.tools.profiler;
import java.util.Objects;
import java.util.Set;
import com.oracle.truffle.api.instrumentation.EventContext;
import com.oracle.truffle.api.instrumentation.Instrumenter;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.instrumentation.StandardTags.ExpressionTag;
import com.oracle.truffle.api.instrumentation.StandardTags.RootTag;
import com.oracle.truffle.api.instrumentation.StandardTags.StatementTag;
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.Source;
import com.oracle.truffle.api.source.SourceSection;
An entry in a stack trace, as returned by CPUSampler.takeSample()
. Each entry represents a single element on the stack that is currently being executed. Stack trace entries may represent a root, expression or statement execution. The frame at the top of the stack represents the execution point at which the stack trace was generated. See Also: Since: 19.0
/**
* An entry in a stack trace, as returned by {@link CPUSampler#takeSample()}. Each entry represents
* a single element on the stack that is currently being executed. Stack trace entries may represent
* a root, expression or statement execution. The frame at the top of the stack represents the
* execution point at which the stack trace was generated.
*
* @see CPUSampler#takeSample()
* @since 19.0
*/
public final class StackTraceEntry {
/*
* Unknown is used when it is used as part of a payload.
*/
static final byte STATE_UNKNOWN = 0;
static final byte STATE_INTERPRETED = 1;
static final byte STATE_COMPILED = 2;
static final byte STATE_COMPILATION_ROOT = 3;
private final SourceSection sourceSection;
private final String rootName;
private final Set<Class<?>> tags;
private final Node instrumentedNode;
private final byte state;
private volatile StackTraceElement stackTraceElement;
StackTraceEntry(Instrumenter instrumenter, EventContext context, byte state) {
this.tags = instrumenter.queryTags(context.getInstrumentedNode());
this.sourceSection = context.getInstrumentedSourceSection();
this.instrumentedNode = context.getInstrumentedNode();
this.rootName = extractRootName(instrumentedNode);
this.state = state;
}
StackTraceEntry(StackTraceEntry location, byte state) {
this.sourceSection = location.sourceSection;
this.instrumentedNode = location.instrumentedNode;
this.rootName = location.rootName;
this.tags = location.tags;
this.stackTraceElement = location.stackTraceElement;
this.state = state;
}
StackTraceEntry(Instrumenter instrumenter, Node node, byte state) {
this.tags = instrumenter.queryTags(node);
this.sourceSection = node.getSourceSection();
this.instrumentedNode = node;
this.rootName = extractRootName(instrumentedNode);
this.state = state;
}
Returns true
if this stack entry was executed in compiled mode at the time when
the stack trace was captured, else false
.
Since: 19.0
/**
* Returns <code>true</code> if this stack entry was executed in compiled mode at the time when
* the stack trace was captured, else <code>false</code>.
*
* @since 19.0
*/
public boolean isCompiled() {
return state == STATE_COMPILED || state == STATE_COMPILATION_ROOT;
}
Returns true
if this stack entry was executed in interpreted mode at the time
when the stack trace was captured, else false
.
Since: 19.0
/**
* Returns <code>true</code> if this stack entry was executed in interpreted mode at the time
* when the stack trace was captured, else <code>false</code>.
*
* @since 19.0
*/
public boolean isInterpreted() {
return state == STATE_INTERPRETED;
}
Returns true
if this stack entry was executed in compiled mode and was inlined
in a parent stack entry at the time when the stack trace was captured, else
false
.
Since: 19.0
/**
* Returns <code>true</code> if this stack entry was executed in compiled mode and was inlined
* in a parent stack entry at the time when the stack trace was captured, else
* <code>false</code>.
*
* @since 19.0
*/
public boolean isInlined() {
return state == STATE_COMPILED;
}
Returns the source section of the stack trace entry.
Since: 19.0
/**
* Returns the source section of the stack trace entry.
*
* @since 19.0
*/
public SourceSection getSourceSection() {
return sourceSection;
}
Returns the name of the root node. For elements that don't represent a guest language root
like statements and expressions this returns the name of the enclosing root.
See Also: - getName.getName()
Since: 19.0
/**
* Returns the name of the root node. For elements that don't represent a guest language root
* like statements and expressions this returns the name of the enclosing root.
*
* @see RootNode#getName()
* @since 19.0
*/
public String getRootName() {
return rootName;
}
Returns a set tags a stack location marked with. Common tags are root
, statement
and expression
. Whether statement or expression stack trace entries appear depends on the configured
filter
. Never null
.
See Also: Since: 19.0
/**
* Returns a set tags a stack location marked with. Common tags are {@link RootTag root},
* {@link StatementTag statement} and {@link ExpressionTag expression}. Whether statement or
* expression stack trace entries appear depends on the configured
* {@link CPUSampler#setFilter(com.oracle.truffle.api.instrumentation.SourceSectionFilter)
* filter}. Never <code>null</code>.
*
* @see Instrumenter#queryTags(Node)
* @since 19.0
*/
public Set<Class<?>> getTags() {
return tags;
}
Converts the stack trace entry to a Java stack trace element. No guarantees are provided
about the format of the stack trace element. The format of the stack trace element may change
without notice.
Since: 19.0
/**
* Converts the stack trace entry to a Java stack trace element. No guarantees are provided
* about the format of the stack trace element. The format of the stack trace element may change
* without notice.
*
* @since 19.0
*/
public StackTraceElement toStackTraceElement() {
/*
* This should be in sync with the behavior of PolyglotException.StackTrace#toHost().
*/
StackTraceElement stack = this.stackTraceElement;
if (stack != null) {
return stack;
}
LanguageInfo languageInfo = getInstrumentedNode().getRootNode().getLanguageInfo();
String declaringClass;
if (languageInfo != null) {
declaringClass = languageInfo.getId();
} else {
declaringClass = "";
}
SourceSection sourceLocation = getSourceSection();
String methodName = rootName == null ? "" : rootName;
if (!tags.contains(StandardTags.RootTag.class)) {
// non-root nodes needs to specify where in the method the element is
methodName += "~" + formatIndices(sourceSection, true);
}
// root nodes don't need formatted indices in the file name
String fileName = formatFileName();
int startLine = sourceLocation != null ? sourceLocation.getStartLine() : -1;
return this.stackTraceElement = new StackTraceElement(declaringClass, methodName, fileName, startLine);
}
// custom version of SourceSection#getShortDescription
private String formatFileName() {
if (sourceSection == null) {
return "<Unknown>";
}
Source source = sourceSection.getSource();
if (source == null) {
// TODO the source == null branch can be removed if the deprecated
// SourceSection#createUnavailable has be removed.
return "<Unknown>";
} else if (source.getPath() == null) {
return source.getName();
} else {
return source.getPath();
}
}
private static String formatIndices(SourceSection sourceSection, boolean needsColumnSpecifier) {
StringBuilder b = new StringBuilder();
boolean singleLine = sourceSection.getStartLine() == sourceSection.getEndLine();
if (singleLine) {
b.append(sourceSection.getStartLine());
} else {
b.append(sourceSection.getStartLine()).append("-").append(sourceSection.getEndLine());
}
if (needsColumnSpecifier) {
b.append(":");
if (sourceSection.getCharLength() <= 1) {
b.append(sourceSection.getCharIndex());
} else {
b.append(sourceSection.getCharIndex()).append("-").append(sourceSection.getCharIndex() + sourceSection.getCharLength() - 1);
}
}
return b.toString();
}
private static String extractRootName(Node instrumentedNode) {
RootNode rootNode = instrumentedNode.getRootNode();
if (rootNode != null) {
if (rootNode.getName() == null) {
return rootNode.toString();
} else {
return rootNode.getName();
}
} else {
return "<Unknown>";
}
}
Node getInstrumentedNode() {
return instrumentedNode;
}
{@inheritDoc}
Since: 19.0
/**
* {@inheritDoc}
*
* @since 19.0
*/
@Override
public int hashCode() {
return 31 * (31 + rootName.hashCode()) + sourceSection.hashCode();
}
{@inheritDoc}
Since: 19.0
/**
* {@inheritDoc}
*
* @since 19.0
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StackTraceEntry)) {
return false;
}
StackTraceEntry other = (StackTraceEntry) obj;
return Objects.equals(sourceSection, other.sourceSection) && Objects.equals(rootName, other.rootName);
}
{@inheritDoc}
Since: 19.0
/**
* {@inheritDoc}
*
* @since 19.0
*/
@Override
public String toString() {
String s = "";
switch (state) {
case STATE_UNKNOWN:
s = "";
break;
case STATE_COMPILATION_ROOT:
s = ", Interpreted";
break;
case STATE_COMPILED:
s = ", Compiled";
break;
case STATE_INTERPRETED:
s = ", Interpreted";
break;
}
return "StackLocation [rootName=" + rootName + ", tags=" + tags + ", sourceSection=" + sourceSection + s + "]";
}
}