8143911: Reintegrate JEP 259: Stack-Walking API

Co-authored-by: Brent Christian <brent.christian@oracle.com>
Co-authored-by: Daniel Fuchs <daniel.fuchs@oracle.com>
Co-authored-by: Hamlin Li <huaming.li@oracle.com>
Reviewed-by: coleenp, dfuchs, bchristi, psandoz, sspitsyn
This commit is contained in:
Mandy Chung 2015-11-24 15:05:58 -08:00
parent a45dc82702
commit eb2c6c52bb
39 changed files with 5807 additions and 97 deletions

View File

@ -138,9 +138,14 @@ SUNWprivate_1.1 {
Java_java_lang_Double_longBitsToDouble;
Java_java_lang_Double_doubleToRawLongBits;
Java_java_lang_reflect_Proxy_defineClass0;
Java_java_lang_Shutdown_runAllFinalizers;
Java_java_lang_Float_intBitsToFloat;
Java_java_lang_Float_floatToRawIntBits;
Java_java_lang_StackFrameInfo_fillInStackFrames;
Java_java_lang_StackFrameInfo_setMethodInfo;
Java_java_lang_StackStreamFactory_checkStackWalkModes;
Java_java_lang_StackStreamFactory_00024AbstractStackWalker_callStackWalk;
Java_java_lang_StackStreamFactory_00024AbstractStackWalker_fetchStackFrames;
Java_java_lang_Shutdown_runAllFinalizers;
Java_java_lang_StrictMath_IEEEremainder;
Java_java_lang_StrictMath_acos;
Java_java_lang_StrictMath_asin;

View File

@ -0,0 +1,226 @@
/*
* Copyright (c) 2015, 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 java.lang;
import java.lang.StackWalker.StackFrame;
import java.util.EnumSet;
import java.util.Set;
import static java.lang.StackWalker.ExtendedOption.LOCALS_AND_OPERANDS;
/**
* <em>UNSUPPORTED</em> This interface is intended to be package-private
* or move to an internal package.<p>
*
* {@code LiveStackFrame} represents a frame storing data and partial results.
* Each frame has its own array of local variables (JVMS section 2.6.1),
* its own operand stack (JVMS section 2.6.2) for a method invocation.
*
* @jvms 2.6 Frames
*/
/* package-private */
interface LiveStackFrame extends StackFrame {
/**
* Return the monitors held by this stack frame. This method returns
* an empty array if no monitor is held by this stack frame.
*
* @return the monitors held by this stack frames
*/
public Object[] getMonitors();
/**
* Gets the local variable array of this stack frame.
*
* <p>A single local variable can hold a value of type boolean, byte, char,
* short, int, float, reference or returnAddress. A pair of local variables
* can hold a value of type long or double. In other words,
* a value of type long or type double occupies two consecutive local
* variables. For a value of primitive type, the element in the
* local variable array is an {@link PrimitiveValue} object;
* otherwise, the element is an {@code Object}.
*
* @return the local variable array of this stack frame.
*/
public Object[] getLocals();
/**
* Gets the operand stack of this stack frame.
*
* <p>
* The 0-th element of the returned array represents the top of the operand stack.
* This method returns an empty array if the operand stack is empty.
*
* <p>Each entry on the operand stack can hold a value of any Java Virtual
* Machine Type.
* For a value of primitive type, the element in the returned array is
* an {@link PrimitiveValue} object; otherwise, the element is the {@code Object}
* on the operand stack.
*
* @return the operand stack of this stack frame.
*/
public Object[] getStack();
/**
* <em>UNSUPPORTED</em> This interface is intended to be package-private
* or move to an internal package.<p>
*
* Represents a local variable or an entry on the operand whose value is
* of primitive type.
*/
public abstract class PrimitiveValue {
/**
* Returns the base type of this primitive value, one of
* {@code B, D, C, F, I, J, S, Z}.
*
* @return Name of a base type
* @jvms table 4.3-A
*/
abstract char type();
/**
* Returns the boolean value if this primitive value is of type boolean.
* @return the boolean value if this primitive value is of type boolean.
*
* @throws UnsupportedOperationException if this primitive value is not
* of type boolean.
*/
public boolean booleanValue() {
throw new UnsupportedOperationException("this primitive of type " + type());
}
/**
* Returns the int value if this primitive value is of type int.
* @return the int value if this primitive value is of type int.
*
* @throws UnsupportedOperationException if this primitive value is not
* of type int.
*/
public int intValue() {
throw new UnsupportedOperationException("this primitive of type " + type());
}
/**
* Returns the long value if this primitive value is of type long.
* @return the long value if this primitive value is of type long.
*
* @throws UnsupportedOperationException if this primitive value is not
* of type long.
*/
public long longValue() {
throw new UnsupportedOperationException("this primitive of type " + type());
}
/**
* Returns the char value if this primitive value is of type char.
* @return the char value if this primitive value is of type char.
*
* @throws UnsupportedOperationException if this primitive value is not
* of type char.
*/
public char charValue() {
throw new UnsupportedOperationException("this primitive of type " + type());
}
/**
* Returns the byte value if this primitive value is of type byte.
* @return the byte value if this primitive value is of type byte.
*
* @throws UnsupportedOperationException if this primitive value is not
* of type byte.
*/
public byte byteValue() {
throw new UnsupportedOperationException("this primitive of type " + type());
}
/**
* Returns the short value if this primitive value is of type short.
* @return the short value if this primitive value is of type short.
*
* @throws UnsupportedOperationException if this primitive value is not
* of type short.
*/
public short shortValue() {
throw new UnsupportedOperationException("this primitive of type " + type());
}
/**
* Returns the float value if this primitive value is of type float.
* @return the float value if this primitive value is of type float.
*
* @throws UnsupportedOperationException if this primitive value is not
* of type float.
*/
public float floatValue() {
throw new UnsupportedOperationException("this primitive of type " + type());
}
/**
* Returns the double value if this primitive value is of type double.
* @return the double value if this primitive value is of type double.
*
* @throws UnsupportedOperationException if this primitive value is not
* of type double.
*/
public double doubleValue() {
throw new UnsupportedOperationException("this primitive of type " + type());
}
}
/**
* Gets {@code StackWalker} that can get locals and operands.
*
* @throws SecurityException if the security manager is present and
* denies access to {@code RuntimePermission("liveStackFrames")}
*/
public static StackWalker getStackWalker() {
return getStackWalker(EnumSet.noneOf(StackWalker.Option.class));
}
/**
* Gets a {@code StackWalker} instance with the given options specifying
* the stack frame information it can access, and which will traverse at most
* the given {@code maxDepth} number of stack frames. If no option is
* specified, this {@code StackWalker} obtains the method name and
* the class name with all
* {@linkplain StackWalker.Option#SHOW_HIDDEN_FRAMES hidden frames} skipped.
* The returned {@code StackWalker} can get locals and operands.
*
* @param options stack walk {@link StackWalker.Option options}
*
* @throws SecurityException if the security manager is present and
* it denies access to {@code RuntimePermission("liveStackFrames")}; or
* or if the given {@code options} contains
* {@link StackWalker.Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE}
* and it denies access to {@code StackFramePermission("retainClassReference")}.
*/
public static StackWalker getStackWalker(Set<StackWalker.Option> options) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("liveStackFrames"));
}
return StackWalker.newInstance(options, LOCALS_AND_OPERANDS);
}
}

View File

@ -0,0 +1,271 @@
/*
* Copyright (c) 2015, 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 java.lang;
import java.lang.StackWalker.Option;
import java.util.EnumSet;
import java.util.Set;
import static java.lang.StackWalker.ExtendedOption.*;
final class LiveStackFrameInfo extends StackFrameInfo implements LiveStackFrame {
private static Object[] EMPTY_ARRAY = new Object[0];
LiveStackFrameInfo(StackWalker walker) {
super(walker);
}
// These fields are initialized by the VM if ExtendedOption.LOCALS_AND_OPERANDS is set
private Object[] monitors = EMPTY_ARRAY;
private Object[] locals = EMPTY_ARRAY;
private Object[] operands = EMPTY_ARRAY;
@Override
public Object[] getMonitors() {
return monitors;
}
@Override
public Object[] getLocals() {
return locals;
}
@Override
public Object[] getStack() {
return operands;
}
/*
* Convert primitive value to {@code Primitive} object to represent
* a local variable or an element on the operand stack of primitive type.
*/
static PrimitiveValue asPrimitive(boolean value) {
return new BooleanPrimitive(value);
}
static PrimitiveValue asPrimitive(int value) {
return new IntPrimitive(value);
}
static PrimitiveValue asPrimitive(short value) {
return new ShortPrimitive(value);
}
static PrimitiveValue asPrimitive(char value) {
return new CharPrimitive(value);
}
static PrimitiveValue asPrimitive(byte value) {
return new BytePrimitive(value);
}
static PrimitiveValue asPrimitive(long value) {
return new LongPrimitive(value);
}
static PrimitiveValue asPrimitive(float value) {
return new FloatPrimitive(value);
}
static PrimitiveValue asPrimitive(double value) {
return new DoublePrimitive(value);
}
private static class IntPrimitive extends PrimitiveValue {
final int value;
IntPrimitive(int value) {
this.value = value;
}
@Override
public char type() {
return 'I';
}
@Override
public int intValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
private static class ShortPrimitive extends PrimitiveValue {
final short value;
ShortPrimitive(short value) {
this.value = value;
}
@Override
public char type() {
return 'S';
}
@Override
public short shortValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
private static class BooleanPrimitive extends PrimitiveValue {
final boolean value;
BooleanPrimitive(boolean value) {
this.value = value;
}
@Override
public char type() {
return 'Z';
}
@Override
public boolean booleanValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
private static class CharPrimitive extends PrimitiveValue {
final char value;
CharPrimitive(char value) {
this.value = value;
}
@Override
public char type() {
return 'C';
}
@Override
public char charValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
private static class BytePrimitive extends PrimitiveValue {
final byte value;
BytePrimitive(byte value) {
this.value = value;
}
@Override
public char type() {
return 'B';
}
@Override
public byte byteValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
private static class LongPrimitive extends PrimitiveValue {
final long value;
LongPrimitive(long value) {
this.value = value;
}
@Override
public char type() {
return 'J';
}
@Override
public long longValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
private static class FloatPrimitive extends PrimitiveValue {
final float value;
FloatPrimitive(float value) {
this.value = value;
}
@Override
public char type() {
return 'F';
}
@Override
public float floatValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
private static class DoublePrimitive extends PrimitiveValue {
final double value;
DoublePrimitive(double value) {
this.value = value;
}
@Override
public char type() {
return 'D';
}
@Override
public double doubleValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright (c) 2015, 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 java.lang;
import jdk.internal.misc.JavaLangInvokeAccess;
import jdk.internal.misc.SharedSecrets;
import static java.lang.StackWalker.Option.*;
import java.lang.StackWalker.StackFrame;
import java.util.Optional;
import java.util.OptionalInt;
class StackFrameInfo implements StackFrame {
private final static JavaLangInvokeAccess jlInvokeAccess =
SharedSecrets.getJavaLangInvokeAccess();
// -XX:+MemberNameInStackFrame will initialize MemberName and all other fields;
// otherwise, VM will set the hidden fields (injected by the VM).
// -XX:+MemberNameInStackFrame is temporary to enable performance measurement
//
// Footprint improvement: MemberName::clazz and MemberName::name
// can replace StackFrameInfo::declaringClass and StackFrameInfo::methodName
// Currently VM sets StackFrameInfo::methodName instead of expanding MemberName::name
final StackWalker walker;
final Class<?> declaringClass;
final Object memberName;
final int bci;
// methodName, fileName, and lineNumber will be lazily set by the VM
// when first requested.
private String methodName;
private String fileName = null; // default for unavailable filename
private int lineNumber = -1; // default for unavailable lineNumber
/*
* Create StackFrameInfo for StackFrameTraverser and LiveStackFrameTraverser
* to use
*/
StackFrameInfo(StackWalker walker) {
this.walker = walker;
this.declaringClass = null;
this.bci = -1;
this.memberName = jlInvokeAccess.newMemberName();
}
@Override
public String getClassName() {
return declaringClass.getName();
}
@Override
public Class<?> getDeclaringClass() {
walker.ensureAccessEnabled(RETAIN_CLASS_REFERENCE);
return declaringClass;
}
// Call the VM to set methodName, lineNumber, and fileName
private synchronized void ensureMethodInfoInitialized() {
if (methodName == null) {
setMethodInfo();
}
}
@Override
public String getMethodName() {
ensureMethodInfoInitialized();
return methodName;
}
@Override
public Optional<String> getFileName() {
ensureMethodInfoInitialized();
return fileName != null ? Optional.of(fileName) : Optional.empty();
}
@Override
public OptionalInt getLineNumber() {
ensureMethodInfoInitialized();
return lineNumber > 0 ? OptionalInt.of(lineNumber) : OptionalInt.empty();
}
@Override
public boolean isNativeMethod() {
ensureMethodInfoInitialized();
return lineNumber == -2;
}
@Override
public String toString() {
ensureMethodInfoInitialized();
// similar format as StackTraceElement::toString
if (isNativeMethod()) {
return getClassName() + "." + getMethodName() + "(Native Method)";
} else {
// avoid allocating Optional objects
return getClassName() + "." + getMethodName() +
"(" + (fileName != null ? fileName : "Unknown Source") +
(lineNumber > 0 ? ":" + lineNumber : " bci:" + bci) + ")";
}
}
/**
* Lazily initialize method name, file name, line number
*/
private native void setMethodInfo();
/**
* Fill in source file name and line number of the given StackFrame array.
*/
static native void fillInStackFrames(int startIndex,
Object[] stackframes,
int fromIndex, int toIndex);
}

View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 2015, 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 java.lang;
/**
* Permission to access {@link StackWalker.StackFrame}.
*
* @see java.lang.StackWalker.Option#RETAIN_CLASS_REFERENCE
* @see StackWalker.StackFrame#getDeclaringClass()
*/
public class StackFramePermission extends java.security.BasicPermission {
private static final long serialVersionUID = 2841894854386706014L;
/**
* Creates a new {@code StackFramePermission} object.
*
* @param name Permission name. Must be "retainClassReference".
*
* @throws IllegalArgumentException if {@code name} is invalid.
* @throws NullPointerException if {@code name} is {@code null}.
*/
public StackFramePermission(String name) {
super(name);
if (!name.equals("retainClassReference")) {
throw new IllegalArgumentException("name: " + name);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,563 @@
/*
* Copyright (c) 2015, 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 java.lang;
import sun.reflect.CallerSensitive;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* A stack walker.
*
* <p> The {@link StackWalker#walk walk} method opens a sequential stream
* of {@link StackFrame StackFrame}s for the current thread and then applies
* the given function to walk the {@code StackFrame} stream.
* The stream reports stack frame elements in order, from the top most frame
* that represents the execution point at which the stack was generated to
* the bottom most frame.
* The {@code StackFrame} stream is closed when the {@code walk} method returns.
* If an attempt is made to reuse the closed stream,
* {@code IllegalStateException} will be thrown.
*
* <p> The {@linkplain Option <em>stack walking options</em>} of a
* {@code StackWalker} determines the information of
* {@link StackFrame StackFrame} objects to be returned.
* By default, stack frames of the reflection API and implementation
* classes are {@linkplain Option#SHOW_HIDDEN_FRAMES hidden}
* and {@code StackFrame}s have the class name and method name
* available but not the {@link StackFrame#getDeclaringClass() Class reference}.
*
* <p> {@code StackWalker} is thread-safe. Multiple threads can share
* a single {@code StackWalker} object to traverse its own stack.
* A permission check is performed when a {@code StackWalker} is created,
* according to the options it requests.
* No further permission check is done at stack walking time.
*
* @apiNote
* Examples
*
* <p>1. To find the first caller filtering a known list of implementation class:
* <pre>{@code
* StackWalker walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
* Optional<Class<?>> callerClass = walker.walk(s ->
* s.map(StackFrame::getDeclaringClass)
* .filter(interestingClasses::contains)
* .findFirst());
* }</pre>
*
* <p>2. To snapshot the top 10 stack frames of the current thread,
* <pre>{@code
* List<StackFrame> stack = StackWalker.getInstance().walk(s ->
* s.limit(10).collect(Collectors.toList()));
* }</pre>
*
* Unless otherwise noted, passing a {@code null} argument to a
* constructor or method in this {@code StackWalker} class
* will cause a {@link NullPointerException NullPointerException}
* to be thrown.
*
* @since 1.9
*/
public final class StackWalker {
/**
* A {@code StackFrame} object represents a method invocation returned by
* {@link StackWalker}.
*
* <p> The {@link #getDeclaringClass()} method may be unsupported as determined
* by the {@linkplain Option stack walking options} of a {@linkplain
* StackWalker stack walker}.
*
* @since 1.9
* @jvms 2.6
*/
public static interface StackFrame {
/**
* Gets the <a href="ClassLoader.html#name">binary name</a>
* of the declaring class of the method represented by this stack frame.
*
* @return the binary name of the declaring class of the method
* represented by this stack frame
*
* @jls 13.1 The Form of a Binary
*/
public String getClassName();
/**
* Gets the name of the method represented by this stack frame.
* @return the name of the method represented by this stack frame
*/
public String getMethodName();
/**
* Gets the declaring {@code Class} for the method represented by
* this stack frame.
*
* @return the declaring {@code Class} of the method represented by
* this stack frame
*
* @throws UnsupportedOperationException if this {@code StackWalker}
* is not configured with {@link Option#RETAIN_CLASS_REFERENCE
* Option.RETAIN_CLASS_REFERENCE}.
*/
public Class<?> getDeclaringClass();
/**
* Returns the name of the source file containing the execution point
* represented by this stack frame. Generally, this corresponds
* to the {@code SourceFile} attribute of the relevant {@code class}
* file as defined by <cite>The Java Virtual Machine Specification</cite>.
* In some systems, the name may refer to some source code unit
* other than a file, such as an entry in a source repository.
*
* @return the name of the file containing the execution point
* represented by this stack frame, or empty {@code Optional}
* is unavailable.
*
* @jvms 4.7.10 The {@code SourceFile} Attribute
*/
public Optional<String> getFileName();
/**
* Returns the line number of the source line containing the execution
* point represented by this stack frame. Generally, this is
* derived from the {@code LineNumberTable} attribute of the relevant
* {@code class} file as defined by <cite>The Java Virtual Machine
* Specification</cite>.
*
* @return the line number of the source line containing the execution
* point represented by this stack frame, or empty
* {@code Optional} if this information is unavailable.
*
* @jvms 4.7.12 The {@code LineNumberTable} Attribute
*/
public OptionalInt getLineNumber();
/**
* Returns {@code true} if the method containing the execution point
* represented by this stack frame is a native method.
*
* @return {@code true} if the method containing the execution point
* represented by this stack frame is a native method.
*/
public boolean isNativeMethod();
/**
* Gets a {@code StackTraceElement} for this stack frame.
*
* @return {@code StackTraceElement} for this stack frame.
*
* */
public default StackTraceElement toStackTraceElement() {
int lineNumber = isNativeMethod() ? -2
: getLineNumber().orElse(-1);
return new StackTraceElement(getClassName(), getMethodName(),
getFileName().orElse(null),
lineNumber);
}
}
/**
* Stack walker option to configure the {@linkplain StackFrame stack frame}
* information obtained by a {@code StackWalker}.
*
* @since 1.9
*/
public enum Option {
/**
* Retains {@code Class} object in {@code StackFrame}s
* walked by this {@code StackWalker}.
*
* <p> A {@code StackWalker} configured with this option will support
* {@link StackWalker#getCallerClass()} and
* {@link StackFrame#getDeclaringClass() StackFrame.getDeclaringClass()}.
*/
RETAIN_CLASS_REFERENCE,
/**
* Shows all reflection frames.
*
* <p>By default, reflection frames are hidden. This includes the
* {@link java.lang.reflect.Method#invoke} method
* and the reflection implementation classes. A {@code StackWalker} with
* this {@code SHOW_REFLECT_FRAMES} option will show all reflection frames.
* The {@link #SHOW_HIDDEN_FRAMES} option can also be used to show all
* reflection frames and it will also show other hidden frames that
* are implementation-specific.
*/
SHOW_REFLECT_FRAMES,
/**
* Shows all hidden frames.
*
* <p>A Java Virtual Machine implementation may hide implementation
* specific frames in addition to {@linkplain #SHOW_REFLECT_FRAMES
* reflection frames}. A {@code StackWalker} with this {@code SHOW_HIDDEN_FRAMES}
* option will show all hidden frames (including reflection frames).
*/
SHOW_HIDDEN_FRAMES;
}
enum ExtendedOption {
/**
* Obtain monitors, locals and operands.
*/
LOCALS_AND_OPERANDS
};
static final EnumSet<Option> DEFAULT_EMPTY_OPTION = EnumSet.noneOf(Option.class);
private final static StackWalker DEFAULT_WALKER =
new StackWalker(DEFAULT_EMPTY_OPTION);
private final Set<Option> options;
private final ExtendedOption extendedOption;
private final int estimateDepth;
/**
* Returns a {@code StackWalker} instance.
*
* <p> This {@code StackWalker} is configured to skip all
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
*
* @return a {@code StackWalker} configured to skip all
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and
* no {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
*
*/
public static StackWalker getInstance() {
// no permission check needed
return DEFAULT_WALKER;
}
/**
* Returns a {@code StackWalker} instance with the given option specifying
* the stack frame information it can access.
*
* <p>
* If a security manager is present and the given {@code option} is
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
* it calls its {@link SecurityManager#checkPermission checkPermission}
* method for {@code StackFramePermission("retainClassReference")}.
*
* @param option {@link Option stack walking option}
*
* @return a {@code StackWalker} configured with the given option
*
* @throws SecurityException if a security manager exists and its
* {@code checkPermission} method denies access.
*/
public static StackWalker getInstance(Option option) {
return getInstance(EnumSet.of(Objects.requireNonNull(option)));
}
/**
* Returns a {@code StackWalker} instance with the given {@code options} specifying
* the stack frame information it can access. If the given {@code options}
* is empty, this {@code StackWalker} is configured to skip all
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
*
* <p>
* If a security manager is present and the given {@code options} contains
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
* it calls its {@link SecurityManager#checkPermission checkPermission}
* method for {@code StackFramePermission("retainClassReference")}.
*
* @param options {@link Option stack walking option}
*
* @return a {@code StackWalker} configured with the given options
*
* @throws SecurityException if a security manager exists and its
* {@code checkPermission} method denies access.
*/
public static StackWalker getInstance(Set<Option> options) {
if (options.isEmpty()) {
return DEFAULT_WALKER;
}
checkPermission(options);
return new StackWalker(toEnumSet(options));
}
/**
* Returns a {@code StackWalker} instance with the given {@ocde options} specifying
* the stack frame information it can access. If the given {@ocde options}
* is empty, this {@code StackWalker} is configured to skip all
* {@linkplain Option#SHOW_HIDDEN_FRAMES hidden frames} and no
* {@linkplain Option#RETAIN_CLASS_REFERENCE class reference} is retained.
*
* <p>
* If a security manager is present and the given {@code options} contains
* {@link Option#RETAIN_CLASS_REFERENCE Option.RETAIN_CLASS_REFERENCE},
* it calls its {@link SecurityManager#checkPermission checkPermission}
* method for {@code StackFramePermission("retainClassReference")}.
*
* <p>
* The {@code estimateDepth} specifies the estimate number of stack frames
* this {@code StackWalker} will traverse that the {@code StackWalker} could
* use as a hint for the buffer size.
*
* @param options {@link Option stack walking options}
* @param estimateDepth Estimate number of stack frames to be traversed.
*
* @return a {@code StackWalker} configured with the given options
*
* @throws IllegalArgumentException if {@code estimateDepth <= 0}
* @throws SecurityException if a security manager exists and its
* {@code checkPermission} method denies access.
*/
public static StackWalker getInstance(Set<Option> options, int estimateDepth) {
if (estimateDepth <= 0) {
throw new IllegalArgumentException("estimateDepth must be > 0");
}
checkPermission(options);
return new StackWalker(toEnumSet(options), estimateDepth);
}
// ----- private constructors ------
private StackWalker(EnumSet<Option> options) {
this(options, 0, null);
}
private StackWalker(EnumSet<Option> options, int estimateDepth) {
this(options, estimateDepth, null);
}
private StackWalker(EnumSet<Option> options, int estimateDepth, ExtendedOption extendedOption) {
this.options = options;
this.estimateDepth = estimateDepth;
this.extendedOption = extendedOption;
}
private static void checkPermission(Set<Option> options) {
Objects.requireNonNull(options);
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
if (options.contains(Option.RETAIN_CLASS_REFERENCE)) {
sm.checkPermission(new StackFramePermission("retainClassReference"));
}
}
}
/*
* Returns a defensive copy
*/
private static EnumSet<Option> toEnumSet(Set<Option> options) {
Objects.requireNonNull(options);
if (options.isEmpty()) {
return DEFAULT_EMPTY_OPTION;
} else {
return EnumSet.copyOf(options);
}
}
/**
* Applies the given function to the stream of {@code StackFrame}s
* for the current thread, traversing from the top frame of the stack,
* which is the method calling this {@code walk} method.
*
* <p>The {@code StackFrame} stream will be closed when
* this method returns. When a closed {@code Stream<StackFrame>} object
* is reused, {@code IllegalStateException} will be thrown.
*
* @apiNote
* For example, to find the first 10 calling frames, first skipping those frames
* whose declaring class is in package {@code com.foo}:
* <blockquote>
* <pre>{@code
* List<StackFrame> frames = StackWalker.getInstance().walk(s ->
* s.dropWhile(f -> f.getClassName().startsWith("com.foo."))
* .limit(10)
* .collect(Collectors.toList()));
* }</pre></blockquote>
*
* <p>This method takes a {@code Function} accepting a {@code Stream<StackFrame>},
* rather than returning a {@code Stream<StackFrame>} and allowing the
* caller to directly manipulate the stream. The Java virtual machine is
* free to reorganize a thread's control stack, for example, via
* deoptimization. By taking a {@code Function} parameter, this method
* allows access to stack frames through a stable view of a thread's control
* stack.
*
* <p>Parallel execution is effectively disabled and stream pipeline
* execution will only occur on the current thread.
*
* @implNote The implementation stabilizes the stack by anchoring a frame
* specific to the stack walking and ensures that the stack walking is
* performed above the anchored frame. When the stream object is closed or
* being reused, {@code IllegalStateException} will be thrown.
*
* @param function a function that takes a stream of
* {@linkplain StackFrame stack frames} and returns a result.
* @param <T> The type of the result of applying the function to the
* stream of {@linkplain StackFrame stack frame}.
*
* @return the result of applying the function to the stream of
* {@linkplain StackFrame stack frame}.
*/
@CallerSensitive
public <T> T walk(Function<? super Stream<StackFrame>, ? extends T> function) {
// Returning a Stream<StackFrame> would be unsafe, as the stream could
// be used to access the stack frames in an uncontrolled manner. For
// example, a caller might pass a Spliterator of stack frames after one
// or more frames had been traversed. There is no robust way to detect
// whether the execution point when
// Spliterator.tryAdvance(java.util.function.Consumer<? super T>) is
// invoked is the exact same execution point where the stack frame
// traversal is expected to resume.
Objects.requireNonNull(function);
return StackStreamFactory.makeStackTraverser(this, function)
.walk();
}
/**
* Performs the given action on each element of {@code StackFrame} stream
* of the current thread, traversing from the top frame of the stack,
* which is the method calling this {@code forEach} method.
*
* <p> This method is equivalent to calling
* <blockquote>
* {@code walk(s -> { s.forEach(action); return null; });}
* </blockquote>
*
* @param action an action to be performed on each {@code StackFrame}
* of the stack of the current thread
*/
@CallerSensitive
public void forEach(Consumer<? super StackFrame> action) {
Objects.requireNonNull(action);
StackStreamFactory.makeStackTraverser(this, s -> {
s.forEach(action);
return null;
}).walk();
}
/**
* Gets the {@code Class} object of the caller invoking the method
* that calls this {@code getCallerClass} method.
*
* <p> Reflection frames, {@link java.lang.invoke.MethodHandle} and
* hidden frames are filtered regardless of the
* {@link Option#SHOW_REFLECT_FRAMES SHOW_REFLECT_FRAMES}
* and {@link Option#SHOW_HIDDEN_FRAMES SHOW_HIDDEN_FRAMES} options
* this {@code StackWalker} has been configured.
*
* <p> This method throws {@code UnsupportedOperationException}
* if this {@code StackWalker} is not configured with
* {@link Option#RETAIN_CLASS_REFERENCE RETAIN_CLASS_REFERENCE} option,
* This method should be called when a caller frame is present. If
* it is called from the last frame on the stack;
* {@code IllegalStateException} will be thrown.
*
* @apiNote
* For example, {@code Util::getResourceBundle} loads a resource bundle
* on behalf of the caller. It calls this {@code getCallerClass} method
* to find the method calling {@code Util::getResourceBundle} and use the caller's
* class loader to load the resource bundle. The caller class in this example
* is the {@code MyTool} class.
*
* <pre>{@code
* class Util {
* private final StackWalker walker = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE);
* public ResourceBundle getResourceBundle(String bundleName) {
* Class<?> caller = walker.getCallerClass();
* return ResourceBundle.getBundle(bundleName, Locale.getDefault(), caller.getClassLoader());
* }
* }
*
* class MyTool {
* private final Util util = new Util();
* private void init() {
* ResourceBundle rb = util.getResourceBundle("mybundle");
* }
* }
* }</pre>
*
* An equivalent way to find the caller class using the
* {@link StackWalker#walk walk} method is as follows
* (filtering the reflection frames, {@code MethodHandle} and hidden frames
* not shown below):
* <pre>{@code
* Optional<Class<?>> caller = walker.walk(s ->
* s.map(StackFrame::getDeclaringClass)
* .skip(2)
* .findFirst());
* }</pre>
*
* When the {@code getCallerClass} method is called from a method that
* is the last frame on the stack,
* for example, {@code static public void main} method launched by the
* {@code java} launcher or a method invoked from a JNI attached thread.
* {@code IllegalStateException} is thrown.
*
* @return {@code Class} object of the caller's caller invoking this method.
*
* @throws UnsupportedOperationException if this {@code StackWalker}
* is not configured with {@link Option#RETAIN_CLASS_REFERENCE
* Option.RETAIN_CLASS_REFERENCE}.
* @throws IllegalStateException if there is no caller frame, i.e.
* when this {@code getCallerClass} method is called from a method
* which is the last frame on the stack.
*/
@CallerSensitive
public Class<?> getCallerClass() {
if (!options.contains(Option.RETAIN_CLASS_REFERENCE)) {
throw new UnsupportedOperationException("This stack walker " +
"does not have RETAIN_CLASS_REFERENCE access");
}
return StackStreamFactory.makeCallerFinder(this).findCaller();
}
// ---- package access ----
static StackWalker newInstanceNoCheck(EnumSet<Option> options) {
return new StackWalker(options, 0, null);
}
static StackWalker newInstance(Set<Option> options, ExtendedOption extendedOption) {
checkPermission(options);
return new StackWalker(toEnumSet(options), 0, extendedOption);
}
int estimateDepth() {
return estimateDepth;
}
boolean hasOption(Option option) {
return options.contains(option);
}
boolean hasLocalsOperandsOption() {
return extendedOption == ExtendedOption.LOCALS_AND_OPERANDS;
}
void ensureAccessEnabled(Option access) {
if (!hasOption(access)) {
throw new UnsupportedOperationException("No access to " + access +
": " + options.toString());
}
}
}

View File

@ -1896,12 +1896,6 @@ public final class System {
public void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook) {
Shutdown.add(slot, registerShutdownInProgress, hook);
}
public int getStackTraceDepth(Throwable t) {
return t.getStackTraceDepth();
}
public StackTraceElement getStackTraceElement(Throwable t, int i) {
return t.getStackTraceElement(i);
}
public String newStringUnsafe(char[] chars) {
return new String(chars, true);
}

View File

@ -1329,11 +1329,9 @@ class Thread implements Runnable {
/**
* Prints a stack trace of the current thread to the standard error stream.
* This method is used only for debugging.
*
* @see Throwable#printStackTrace()
*/
public static void dumpStack() {
new Exception("Stack trace").printStackTrace();
StackStreamFactory.makeStackTrace().printStackTrace(System.err);
}
/**
@ -1556,7 +1554,7 @@ class Thread implements Runnable {
return stackTrace;
} else {
// Don't need JVM help for current thread
return (new Exception()).getStackTrace();
return StackStreamFactory.makeStackTrace().getStackTraceElements();
}
}

View File

@ -24,6 +24,8 @@
*/
package java.lang;
import sun.misc.VM;
import java.io.*;
import java.util.*;
@ -778,7 +780,11 @@ public class Throwable implements Serializable {
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
if (backtrace == null && StackStreamFactory.useStackTrace(this)) {
backtrace = StackStreamFactory.makeStackTrace(this);
} else {
fillInStackTrace(0);
}
stackTrace = UNASSIGNED_STACK;
}
return this;
@ -819,10 +825,14 @@ public class Throwable implements Serializable {
// backtrace if this is the first call to this method
if (stackTrace == UNASSIGNED_STACK ||
(stackTrace == null && backtrace != null) /* Out of protocol state */) {
int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth];
for (int i=0; i < depth; i++)
stackTrace[i] = getStackTraceElement(i);
if (backtrace instanceof StackStreamFactory.StackTrace) {
stackTrace = ((StackStreamFactory.StackTrace)backtrace).getStackTraceElements();
} else {
int depth = getStackTraceDepth();
stackTrace = new StackTraceElement[depth];
for (int i = 0; i < depth; i++)
stackTrace[i] = getStackTraceElement(i);
}
} else if (stackTrace == null) {
return UNASSIGNED_STACK;
}

View File

@ -1077,4 +1077,13 @@ import java.util.Objects;
// System.out.println("Hello world! My methods are:");
// System.out.println(Factory.INSTANCE.getMethods(MemberName.class, true, null));
// }
static {
// Allow privileged classes outside of java.lang
jdk.internal.misc.SharedSecrets.setJavaLangInvokeAccess(new jdk.internal.misc.JavaLangInvokeAccess() {
public Object newMemberName() {
return new MemberName();
}
});
}
}

View File

@ -31,13 +31,12 @@ import java.io.StringWriter;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.time.ZonedDateTime;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.Function;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.function.Predicate;
import java.util.function.Supplier;
import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;
import sun.util.logging.PlatformLogger;
import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration;
@ -169,45 +168,58 @@ public class SimpleConsoleLogger extends LoggerConfiguration
// Returns the caller's class and method's name; best effort
// if cannot infer, return the logger's name.
private String getCallerInfo() {
String sourceClassName = null;
String sourceMethodName = null;
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
Throwable throwable = new Throwable();
int depth = access.getStackTraceDepth(throwable);
String logClassName = "sun.util.logging.PlatformLogger";
String simpleLoggerClassName = "jdk.internal.logger.SimpleConsoleLogger";
boolean lookingForLogger = true;
for (int ix = 0; ix < depth; ix++) {
// Calling getStackTraceElement directly prevents the VM
// from paying the cost of building the entire stack frame.
final StackTraceElement frame =
access.getStackTraceElement(throwable, ix);
final String cname = frame.getClassName();
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
if (cname.equals(logClassName) || cname.equals(simpleLoggerClassName)) {
lookingForLogger = false;
}
} else {
if (skipLoggingFrame(cname)) continue;
if (!cname.equals(logClassName) && !cname.equals(simpleLoggerClassName)) {
// We've found the relevant frame.
sourceClassName = cname;
sourceMethodName = frame.getMethodName();
break;
}
}
}
if (sourceClassName != null) {
return sourceClassName + " " + sourceMethodName;
Optional<StackWalker.StackFrame> frame = new CallerFinder().get();
if (frame.isPresent()) {
return frame.get().getClassName() + " " + frame.get().getMethodName();
} else {
return name;
}
}
/*
* CallerFinder is a stateful predicate.
*/
static final class CallerFinder implements Predicate<StackWalker.StackFrame> {
static final StackWalker WALKER = StackWalker.getInstance();
/**
* Returns StackFrame of the caller's frame.
* @return StackFrame of the caller's frame.
*/
Optional<StackWalker.StackFrame> get() {
return WALKER.walk((s) -> s.filter(this).findFirst());
}
private boolean lookingForLogger = true;
/**
* Returns true if we have found the caller's frame, false if the frame
* must be skipped.
*
* @param t The frame info.
* @return true if we have found the caller's frame, false if the frame
* must be skipped.
*/
@Override
public boolean test(StackWalker.StackFrame t) {
final String cname = t.getClassName();
// We should skip all frames until we have found the logger,
// because these frames could be frames introduced by e.g. custom
// sub classes of Handler.
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
lookingForLogger = !isLoggerImplFrame(cname);
return false;
}
// We've found the relevant frame.
return !skipLoggingFrame(cname) && !isLoggerImplFrame(cname);
}
private boolean isLoggerImplFrame(String cname) {
return (cname.equals("sun.util.logging.PlatformLogger") ||
cname.equals("jdk.internal.logger.SimpleConsoleLogger"));
}
}
private String getCallerInfo(String sourceClassName, String sourceMethodName) {
if (sourceClassName == null) return name;
if (sourceMethodName == null) return sourceClassName;

View File

@ -102,16 +102,6 @@ public interface JavaLangAccess {
*/
void registerShutdownHook(int slot, boolean registerShutdownInProgress, Runnable hook);
/**
* Returns the number of stack frames represented by the given throwable.
*/
int getStackTraceDepth(Throwable t);
/**
* Returns the ith StackTraceElement for the given throwable.
*/
StackTraceElement getStackTraceElement(Throwable t, int i);
/**
* Returns a new string backed by the provided character array. The
* character array is not copied and must never be modified after the

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2015, 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 jdk.internal.misc;
public interface JavaLangInvokeAccess {
/**
* Create a new MemberName instance
*/
Object newMemberName();
}

View File

@ -45,6 +45,7 @@ public class SharedSecrets {
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static JavaUtilJarAccess javaUtilJarAccess;
private static JavaLangAccess javaLangAccess;
private static JavaLangInvokeAccess javaLangInvokeAccess;
private static JavaLangRefAccess javaLangRefAccess;
private static JavaIOAccess javaIOAccess;
private static JavaNetAccess javaNetAccess;
@ -80,6 +81,20 @@ public class SharedSecrets {
return javaLangAccess;
}
public static void setJavaLangInvokeAccess(JavaLangInvokeAccess jlia) {
javaLangInvokeAccess = jlia;
}
public static JavaLangInvokeAccess getJavaLangInvokeAccess() {
if (javaLangInvokeAccess == null) {
try {
Class<?> c = Class.forName("java.lang.invoke.MemberName");
unsafe.ensureClassInitialized(c);
} catch (ClassNotFoundException e) {};
}
return javaLangInvokeAccess;
}
public static void setJavaLangRefAccess(JavaLangRefAccess jlra) {
javaLangRefAccess = jlra;
}

View File

@ -177,6 +177,37 @@ JVM_GetStackTraceDepth(JNIEnv *env, jobject throwable);
JNIEXPORT jobject JNICALL
JVM_GetStackTraceElement(JNIEnv *env, jobject throwable, jint index);
/*
* java.lang.StackWalker
*/
enum {
JVM_STACKWALK_FILL_CLASS_REFS_ONLY = 0x2,
JVM_STACKWALK_FILTER_FILL_IN_STACK_TRACE = 0x10,
JVM_STACKWALK_SHOW_HIDDEN_FRAMES = 0x20,
JVM_STACKWALK_FILL_LIVE_STACK_FRAMES = 0x100
};
JNIEXPORT jobject JNICALL
JVM_CallStackWalk(JNIEnv *env, jobject stackStream, jlong mode,
jint skip_frames, jint frame_count, jint start_index,
jobjectArray classes,
jobjectArray frames);
JNIEXPORT jint JNICALL
JVM_MoreStackWalk(JNIEnv *env, jobject stackStream, jlong mode, jlong anchor,
jint frame_count, jint start_index,
jobjectArray classes,
jobjectArray frames);
JNIEXPORT void JNICALL
JVM_FillStackFrames(JNIEnv* env, jclass cls,
jint start_index,
jobjectArray stackFrames,
jint from_index, jint toIndex);
JNIEXPORT void JNICALL
JVM_SetMethodInfo(JNIEnv* env, jobject frame);
/*
* java.lang.Thread
*/

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2015, 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.
*/
/*
* Implementation of class StackFrameInfo
*/
#include <stdio.h>
#include <signal.h>
#include "jni.h"
#include "jvm.h"
#include "java_lang_StackFrameInfo.h"
/*
* Class: java_lang_StackFrameInfo
* Method: fillInStackFrames
* Signature: (I[Ljava/lang/Object;[Ljava/lang/Object;II)V
*/
JNIEXPORT void JNICALL Java_java_lang_StackFrameInfo_fillInStackFrames
(JNIEnv *env, jclass dummy, jint startIndex,
jobjectArray stackFrames, jint fromIndex, jint toIndex) {
JVM_FillStackFrames(env, dummy, startIndex,
stackFrames, fromIndex, toIndex);
}
/*
* Class: java_lang_StackFrameInfo
* Method: setMethodInfo
* Signature: (Ljava/lang/Class;)V
*/
JNIEXPORT void JNICALL Java_java_lang_StackFrameInfo_setMethodInfo
(JNIEnv *env, jobject stackframeinfo) {
JVM_SetMethodInfo(env, stackframeinfo);
}

View File

@ -0,0 +1,78 @@
/*
* Copyright (c) 2015, 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.
*/
/*
* Implementation of class StackStreamfactory and AbstractStackWalker
*/
#include <stdio.h>
#include <signal.h>
#include "jni.h"
#include "jvm.h"
#include "java_lang_StackStreamFactory.h"
#include "java_lang_StackStreamFactory_AbstractStackWalker.h"
/*
* Class: java_lang_StackStreamFactory
* Method: checkStackWalkModes
* Signature: ()
*/
JNIEXPORT jboolean JNICALL Java_java_lang_StackStreamFactory_checkStackWalkModes
(JNIEnv *env, jclass dummy)
{
return JVM_STACKWALK_FILL_CLASS_REFS_ONLY == java_lang_StackStreamFactory_FILL_CLASS_REFS_ONLY &&
JVM_STACKWALK_FILTER_FILL_IN_STACK_TRACE == java_lang_StackStreamFactory_FILTER_FILL_IN_STACKTRACE &&
JVM_STACKWALK_SHOW_HIDDEN_FRAMES == java_lang_StackStreamFactory_SHOW_HIDDEN_FRAMES &&
JVM_STACKWALK_FILL_LIVE_STACK_FRAMES == java_lang_StackStreamFactory_FILL_LIVE_STACK_FRAMES;
}
/*
* Class: java_lang_StackStreamFactory_AbstractStackWalker
* Method: callStackWalk
* Signature: (JIII[Ljava/lang/Class;[Ljava/lang/StackWalker/StackFrame;)Ljava/lang/Object;
*/
JNIEXPORT jobject JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_callStackWalk
(JNIEnv *env, jobject stackstream, jlong mode, jint skipFrames, jint batchSize, jint startIndex,
jobjectArray classes, jobjectArray frames)
{
return JVM_CallStackWalk(env, stackstream, mode, skipFrames, batchSize,
startIndex, classes, frames);
}
/*
* Class: java_lang_StackStreamFactory_AbstractStackWalker
* Method: fetchStackFrames
* Signature: (JJII[Ljava/lang/Class;[Ljava/lang/StackWalker/StackFrame;)I
*/
JNIEXPORT jint JNICALL Java_java_lang_StackStreamFactory_00024AbstractStackWalker_fetchStackFrames
(JNIEnv *env, jobject stackstream, jlong mode, jlong anchor,
jint batchSize, jint startIndex,
jobjectArray classes, jobjectArray frames)
{
return JVM_MoreStackWalk(env, stackstream, mode, anchor, batchSize,
startIndex, classes, frames);
}

View File

@ -30,9 +30,8 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.io.*;
import java.time.Clock;
import java.util.function.Predicate;
import jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;
import static jdk.internal.logger.SimpleConsoleLogger.skipLoggingFrame;
/**
@ -661,42 +660,58 @@ public class LogRecord implements java.io.Serializable {
//
private void inferCaller() {
needToInferCaller = false;
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
Throwable throwable = new Throwable();
int depth = access.getStackTraceDepth(throwable);
// Skip all frames until we have found the first logger frame.
Optional<StackWalker.StackFrame> frame = new CallerFinder().get();
frame.ifPresent(f -> {
setSourceClassName(f.getClassName());
setSourceMethodName(f.getMethodName());
});
boolean lookingForLogger = true;
for (int ix = 0; ix < depth; ix++) {
// Calling getStackTraceElement directly prevents the VM
// from paying the cost of building the entire stack frame.
StackTraceElement frame =
access.getStackTraceElement(throwable, ix);
String cname = frame.getClassName();
boolean isLoggerImpl = isLoggerImplFrame(cname);
if (lookingForLogger) {
// Skip all frames until we have found the first logger frame.
if (isLoggerImpl) {
lookingForLogger = false;
}
} else {
if (!isLoggerImpl) {
// skip logging/logger infrastructure and reflection calls
if (!skipLoggingFrame(cname)) {
// We've found the relevant frame.
setSourceClassName(cname);
setSourceMethodName(frame.getMethodName());
return;
}
}
}
}
// We haven't found a suitable frame, so just punt. This is
// OK as we are only committed to making a "best effort" here.
}
private boolean isLoggerImplFrame(String cname) {
// the log record could be created for a platform logger
return (cname.equals("java.util.logging.Logger") ||
cname.startsWith("sun.util.logging.PlatformLogger"));
/*
* CallerFinder is a stateful predicate.
*/
static final class CallerFinder implements Predicate<StackWalker.StackFrame> {
static final StackWalker WALKER = StackWalker.getInstance();
/**
* Returns StackFrame of the caller's frame.
* @return StackFrame of the caller's frame.
*/
Optional<StackWalker.StackFrame> get() {
return WALKER.walk((s) -> s.filter(this).findFirst());
}
private boolean lookingForLogger = true;
/**
* Returns true if we have found the caller's frame, false if the frame
* must be skipped.
*
* @param t The frame info.
* @return true if we have found the caller's frame, false if the frame
* must be skipped.
*/
@Override
public boolean test(StackWalker.StackFrame t) {
final String cname = t.getClassName();
// We should skip all frames until we have found the logger,
// because these frames could be frames introduced by e.g. custom
// sub classes of Handler.
if (lookingForLogger) {
// the log record could be created for a platform logger
lookingForLogger = !isLoggerImplFrame(cname);
return false;
}
// skip logging/logger infrastructure and reflection calls
return !skipLoggingFrame(cname);
}
private boolean isLoggerImplFrame(String cname) {
return (cname.equals("java.util.logging.Logger") ||
cname.startsWith("sun.util.logging.PlatformLogger"));
}
}
}

View File

@ -0,0 +1,227 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8143214
* @summary Verify that StackWalker works well when one instance of StackWalker
* is used by several threads sequentially or concurrently.
* @run testng AcrossThreads
*/
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.StackWalker.StackFrame;
import static java.lang.StackWalker.Option.*;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class AcrossThreads {
static final StackWalker WALKERS[] = new StackWalker[] {
StackWalker.getInstance(RETAIN_CLASS_REFERENCE),
StackWalker.getInstance(EnumSet.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE)),
StackWalker.getInstance(EnumSet.of(SHOW_HIDDEN_FRAMES, RETAIN_CLASS_REFERENCE))
};
@DataProvider
public StackWalker[][] walkerProvider() {
return new StackWalker[][] {
new StackWalker[] { WALKERS[0] },
new StackWalker[] { WALKERS[1] },
new StackWalker[] { WALKERS[2] }
};
}
@Test(dataProvider = "walkerProvider")
public void test(StackWalker walker) {
Thread t1 = new T1(walker);
// call methods of one instance of StackWalker sequentially in T1, T2, T3.
t1.start();
try {
t1.join();
} catch (InterruptedException e) { }
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 100; i++) {
threads.add(new T1(walker));
threads.add(new T2(walker));
threads.add(new T3(walker));
}
// call methods of one instance of StackWalker concurrently in several threads.
threads.parallelStream().forEach(t -> {
t.setDaemon(true);
t.start();
});
threads.parallelStream().forEach(t -> {
try {
t.join();
} catch (InterruptedException e) { }
});
}
interface Consumer {
final int LOOPS = 5;
public void consume();
default public void assertWalker(StackWalker walker, int n) {
if (--n == 0) {
Map<String, Integer> methods = new HashMap<String, Integer>();
walker.forEach(f -> {
Integer i = methods.putIfAbsent(f.getMethodName(), 1);
if (i != null) {
methods.put(f.getMethodName(), i + 1);
}
});
// verify that walker.forEach(...) reaches the specified methods.
assertTrue(methods.get("consume") == 1);
assertTrue(methods.get("run") == 1);
assertTrue(methods.get("assertWalker") == LOOPS);
// verify that walker.walk(...) reaches the specified methods.
assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
.filter(mn -> mn.equals("consume"))
.count()) == 1);
assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
.filter(mn -> mn.equals("run"))
.count()) == 1);
assertTrue(walker.walk(s -> s.map(StackFrame::getMethodName)
.filter(mn -> mn.equals("assertWalker"))
.count()) == LOOPS);
} else {
assertWalker(walker, n);
}
}
}
class T1 extends Thread implements Consumer {
final StackWalker walker;
public T1(StackWalker walker) {
this.walker = walker;
}
public void run() {
consume();
Thread t2 = new T2(walker);
t2.start();
try {
t2.join();
} catch (InterruptedException e) { }
consume();
}
public void consume() {
assertWalker(walker, LOOPS);
// verify walker.walk() reaches T1 class through methods run() and consume().
assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
.count()) == 2);
assertCallerClass(walker);
assertEquals(T1.class, walker.getCallerClass());
}
}
class T2 extends Thread implements Consumer {
final StackWalker walker;
public T2(StackWalker walker) {
this.walker = walker;
}
public void run() {
consume();
Thread t3 = new T3(walker);
t3.start();
try {
t3.join();
} catch (InterruptedException e) { }
consume();
}
public void consume() {
assertWalker(walker, LOOPS);
// verify walker.walk() reaches T2 class through methods run() and consume().
assertTrue(walker.walk(s -> s.filter(f -> T2.class == f.getDeclaringClass())
.count()) == 2);
// verify T1 is not reached, even if call is invoked
// from test()->T1.start()->T1.run()->T2
assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
.count()) == 0);
assertCallerClass(walker);
assertEquals(T2.class, walker.getCallerClass());
}
}
class T3 extends Thread implements Consumer {
final StackWalker walker;
public T3(StackWalker walker) {
this.walker = walker;
}
public void run() {
consume();
}
public void consume() {
assertWalker(walker, LOOPS);
// verify walker.walk() reaches T1 class through methods run() and consume().
assertTrue(walker.walk(s -> s.filter(f -> T3.class == f.getDeclaringClass())
.count()) == 2);
// verify T1, T2 is not reached, even if call is invoked
// from test() -> T1.start() -> T1.run() -> T2.start() -> T2.run() -> T3
assertTrue(walker.walk(s -> s.filter(f -> T2.class == f.getDeclaringClass())
.count()) == 0);
assertTrue(walker.walk(s -> s.filter(f -> T1.class == f.getDeclaringClass())
.count()) == 0);
assertCallerClass(walker);
assertEquals(T3.class, walker.getCallerClass());
}
}
static void assertCallerClass(StackWalker walker) {
// verify walker.getCallerClass() get the expected class.
call(walker);
}
static void call(StackWalker walker) {
Class<?> c = walker.getCallerClass();
assertEquals(c, AcrossThreads.class);
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8140450
* @summary Basic test for the StackWalker::walk method
* @run testng Basic
*/
import java.lang.StackWalker.StackFrame;
import java.util.List;
import java.util.stream.Collectors;
import static java.lang.StackWalker.Option.*;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class Basic {
private static boolean verbose = false;
@DataProvider(name = "stackDepths")
public static Object[][] stackDepths() {
return new Object[][] {
{ new int[] { 12 }, new int[] { 4, 8, 12} },
{ new int[] { 18 }, new int[] { 8, 16, 20} },
{ new int[] { 32 }, new int[] { 16, 32, 64} },
};
}
/**
* For a stack of a given depth, it creates a StackWalker with an estimate.
* Test walking different number of frames
*/
@Test(dataProvider = "stackDepths")
public static void test(int[] depth, int[] estimates) {
Basic test = new Basic(depth[0]);
for (int estimate : estimates) {
test.walk(estimate);
}
}
private final int depth;
Basic(int depth) {
this.depth = depth;
}
/*
* Setup a stack builder with the expected stack depth
* Walk the stack and count the frames.
*/
void walk(int estimate) {
int limit = Math.min(depth, 16);
List<StackFrame> frames = new StackBuilder(depth, limit).build();
System.out.format("depth=%d estimate=%d expected=%d walked=%d%n",
depth, estimate, limit, frames.size());
assertEquals(limit, frames.size());
}
class StackBuilder {
private final int stackDepth;
private final int limit;
private int depth = 0;
private List<StackFrame> result;
StackBuilder(int stackDepth, int limit) {
this.stackDepth = stackDepth; // build method;
this.limit = limit;
}
List<StackFrame> build() {
trace("build");
m1();
return result;
}
void m1() {
trace("m1");
m2();
}
void m2() {
trace("m2");
m3();
}
void m3() {
trace("m3");
m4();
}
void m4() {
trace("m4");
int remaining = stackDepth-depth-1;
if (remaining >= 4) {
m1();
} else {
filler(remaining);
}
}
void filler(int i) {
trace("filler");
if (i == 0)
walk();
else
filler(--i);
}
void walk() {
StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
result = walker.walk(s -> s.limit(limit).collect(Collectors.toList()));
}
void trace(String methodname) {
++depth;
if (verbose)
System.out.format("%2d: %s%n", depth, methodname);
}
}
static void assertEquals(int x, int y) {
if (x != y) {
throw new RuntimeException(x + " != " + y);
}
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8140450
* @library /lib/testlibrary
* @build jdk.testlibrary.*
* @summary Test if the getCallerClass method returns empty optional
* @run main CallerFromMain exec
*/
import jdk.testlibrary.ProcessTools;
import jdk.testlibrary.OutputAnalyzer;
public class CallerFromMain {
private static final StackWalker sw = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
public static void main(String[] args) throws Exception {
if (args.length > 0) {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, "CallerFromMain");
OutputAnalyzer output = ProcessTools.executeProcess(pb);
System.out.println(output.getOutput());
output.shouldHaveExitValue(0);
return;
}
// StackWalker::getCallerClass
// CallerFromMain::main
// no caller
try {
Class<?> c = sw.getCallerClass();
throw new RuntimeException("UOE not thrown. Caller: " + c);
} catch (IllegalStateException e) {}
// StackWalker::getCallerClass
// Runnable::run
// Thread::run
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
Class<?> c = sw.getCallerClass();
System.out.println("Call from Thread.run: " + c);
assertThreadClassAsCaller(c);
}
});
t1.setDaemon(true);
t1.start();
// StackWalker::getCallerClass
// CallerFromMain::doit
// Thread::run
Thread t2 = new Thread(CallerFromMain::doit);
t2.setDaemon(true);
t2.start();
// StackWalker::getCallerClass
// MyRunnable::run
// Thread::run
Thread t3 = new Thread(new MyRunnable());
t3.setDaemon(true);
t3.start();
// StackWalker::getCallerClass
// Runnable::run
// MyThread::run
Thread t4 = new MyThread(new Runnable() {
@Override
public void run() {
Class<?> c = sw.getCallerClass();
System.out.println("Call from MyThread.run: " + c);
if (c != MyThread.class) {
throw new RuntimeException("Expected MyThread.class but got " + c);
}
}
});
t4.setDaemon(true);
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
}
static class MyThread extends Thread {
final Runnable runnable;
MyThread(Runnable runnable) {
super("MyThread");
this.runnable = runnable;
}
public void run() {
runnable.run();
}
}
static class MyRunnable implements Runnable {
@Override
public void run() {
Class<?> c = sw.getCallerClass();
System.out.println("Call from Thread::run: " + c);
assertThreadClassAsCaller(c);
}
}
static void doit() {
Class<?> c = sw.getCallerClass();
System.out.println("Call from CallerFromMain.doit: " + c);
assertThreadClassAsCaller(c);
}
static void assertThreadClassAsCaller(Class<?> caller) {
if (caller != Thread.class) {
throw new RuntimeException("Expected Thread.class but got " + caller);
}
}
}

View File

@ -0,0 +1,234 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8143214
* @summary Verify outputs of Thread.dumpStack() and Throwable.printStackTrace().
* This test should also been run against jdk9 successfully except of
* VM option MemberNameInStackFrame.
* @run main/othervm DumpStackTest
* @run main/othervm -Dstackwalk.newThrowable=false DumpStackTest
* @run main/othervm -Dstackwalk.newThrowable=true -XX:-MemberNameInStackFrame DumpStackTest
* @run main/othervm -Dstackwalk.newThrowable=true -XX:+MemberNameInStackFrame DumpStackTest
*/
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.Consumer;
public class DumpStackTest {
public static void main(String args[]) {
test();
testThread();
testLambda();
testMethodInvoke();
testMethodHandle();
}
static class CallFrame {
final String classname;
final String methodname;
CallFrame(Class<?> c, String methodname) {
this(c.getName(), methodname);
}
CallFrame(String classname, String methodname) {
this.classname = classname;
this.methodname = methodname;
}
String getClassName() {
return classname;
}
String getMethodName() {
return methodname;
}
String getFileName() {
int i = classname.lastIndexOf('.');
int j = classname.lastIndexOf('$');
String name = classname.substring(i+1, j >= 0 ? j : classname.length());
return name + ".java";
}
@Override
public String toString() {
return classname + "." + methodname + "(" + getFileName() + ")";
}
}
static void test() {
CallFrame[] callStack = new CallFrame[] {
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(DumpStackTest.class, "test"),
new CallFrame(DumpStackTest.class, "main"),
// if invoked from jtreg
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"), // non-public class
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(Thread.class, "run"),
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
getStackTrace(callStack);
}
static void getStackTrace(CallFrame[] callStack) {
// this method is the top of the stack
callStack[0] = new CallFrame(DumpStackTest.class, "getStackTrace");
try {
throw new RuntimeException();
} catch(RuntimeException ex) {
assertStackTrace(ex.getStackTrace(), callStack);
}
}
static void testThread() {
Thread t1 = new Thread() {
public void run() {
c();
}
void c() {
CallFrame[] callStack = new CallFrame[] {
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(this.getClass(), "c"),
new CallFrame(this.getClass(), "run")
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
DumpStackTest.getStackTrace(callStack);
}
};
t1.start();
try {
t1.join();
} catch(InterruptedException e) {}
}
static void testLambda() {
Consumer<Void> c = (x) -> consumeLambda();
c.accept(null);
}
static void consumeLambda() {
CallFrame[] callStack = new CallFrame[]{
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(DumpStackTest.class, "consumeLambda"),
new CallFrame(DumpStackTest.class, "lambda$testLambda$0"),
new CallFrame(DumpStackTest.class, "testLambda"),
new CallFrame(DumpStackTest.class, "main"),
// if invoked from jtreg
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"),
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(Thread.class, "run")
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
DumpStackTest.getStackTrace(callStack);
}
static void testMethodInvoke() {
try {
Method m = DumpStackTest.class.getDeclaredMethod("methodInvoke");
m.invoke(null);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
static void methodInvoke() {
CallFrame[] callStack = new CallFrame[] {
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(DumpStackTest.class, "methodInvoke"),
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"),
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(DumpStackTest.class, "testMethodInvoke"),
new CallFrame(DumpStackTest.class, "main"),
// if invoked from jtreg
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"),
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(Thread.class, "run")
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
DumpStackTest.getStackTrace(callStack);
}
static void testMethodHandle() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
MethodHandle handle = lookup.findStatic(DumpStackTest.class, "methodHandle",
MethodType.methodType(void.class));
handle.invoke();
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
static void methodHandle() {
CallFrame[] callStack = new CallFrame[]{
new CallFrame(Thread.class, "getStackTrace"),
new CallFrame(DumpStackTest.class, "methodHandle"),
new CallFrame(DumpStackTest.class, "testMethodHandle"),
new CallFrame(DumpStackTest.class, "main"),
// if invoked from jtreg
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke0"),
new CallFrame("sun.reflect.NativeMethodAccessorImpl", "invoke"),
new CallFrame("sun.reflect.DelegatingMethodAccessorImpl", "invoke"),
new CallFrame(Method.class, "invoke"),
new CallFrame(Thread.class, "run")
};
assertStackTrace(Thread.currentThread().getStackTrace(), callStack);
DumpStackTest.getStackTrace(callStack);
}
static void assertStackTrace(StackTraceElement[] actual, CallFrame[] expected) {
System.out.println("--- Actual ---");
Arrays.stream(actual).forEach(e -> System.out.println(e));
System.out.println("--- Expected ---");
Arrays.stream(expected).forEach(e -> System.out.println(e));
for (int i = 0, j = 0; i < actual.length; i++) {
// filter test framework classes
if (actual[i].getClassName().startsWith("com.sun.javatest.regtest"))
continue;
assertEquals(actual[i], expected[j++], i);
}
}
static void assertEquals(StackTraceElement actual, CallFrame expected, int idx) {
if (!actual.getClassName().equals(expected.getClassName()) ||
!actual.getFileName().equals(expected.getFileName()) ||
!actual.getMethodName().equals(expected.getMethodName())) {
throw new RuntimeException("StackTraceElements mismatch at index " + idx +
". Expected [" + expected + "], but get [" + actual + "]");
}
}
}

View File

@ -0,0 +1,158 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/**
* @test
* @bug 8143214
* @summary Verify StackWalker works well when embedded in another
* StackWalker's functions.
* @run testng/othervm EmbeddedStackWalkTest
*/
import java.lang.StackWalker.StackFrame;
import static java.lang.StackWalker.Option.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays;
import java.util.EnumSet;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class EmbeddedStackWalkTest {
static final StackWalker WALKERS[] = new StackWalker[] {
StackWalker.getInstance(RETAIN_CLASS_REFERENCE),
StackWalker.getInstance(EnumSet.of(SHOW_REFLECT_FRAMES, RETAIN_CLASS_REFERENCE)),
StackWalker.getInstance(EnumSet.of(SHOW_HIDDEN_FRAMES, RETAIN_CLASS_REFERENCE))
};
static final int BIG_LOOP = 30;
static final int SMALL_LOOP = 5;
@DataProvider
public StackWalker[][] walkerProvider() {
return new StackWalker[][] {
new StackWalker[] { WALKERS[0] },
new StackWalker[] { WALKERS[1] },
new StackWalker[] { WALKERS[2] }
};
}
@Test(dataProvider = "walkerProvider")
public void test(StackWalker walker) {
C1.call(walker, BIG_LOOP);
}
// line numbers are hardcoded for now.
// Should annotate the line numbers and auto-generated these constants
// for test verification instead
static final int BEGIN_LINE = 71; // the begin line number of approximate range.
static final int END_LINE = 136; // the end line number of approximate range.
static class C1 { // here is the begin line number of approximate range, L71.
public static void call(StackWalker walker, int loops) {
if (loops == 0) {
String caller = walker.walk(s ->
s.map(StackFrame::getClassName)
.filter(cn -> !cn.startsWith("sun.reflect.") && !cn.startsWith("java.lang.invoke"))
.skip(2).findFirst()
).get();
assertEquals(caller, C1.class.getName());
walker.forEach(f -> C2.testEmbeddedWalker());
} else {
call(walker, --loops);
}
}
}
static class C2 {
static final StackWalker embeddedWalkers[] = new StackWalker[] {
StackWalker.getInstance(),
StackWalker.getInstance(SHOW_REFLECT_FRAMES),
StackWalker.getInstance(SHOW_HIDDEN_FRAMES)
};
public static void testEmbeddedWalker() {
walk(SMALL_LOOP);
}
static void walk(int loops) {
if (loops == 0) {
Arrays.stream(embeddedWalkers)
.forEach(walker -> run(walker));
} else {
walk(--loops);
}
}
static void run(StackWalker walker) {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = null;
try {
handle = lookup.findStatic(C2.class, "call",
MethodType.methodType(void.class, StackWalker.class));
handle.invoke(walker);
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
static void call(StackWalker walker) {
String caller = walker.walk(s ->
s.map(StackFrame::getClassName)
.filter(cn -> !cn.startsWith("sun.reflect.") && !cn.startsWith("java.lang.invoke"))
.skip(2).findFirst()
).get();
assertEquals(caller, C2.class.getName());
verify(walker, C1.class, "call");
verify(walker, C2.class, "call");
verify(walker, C2.class, "run");
verify(walker, C2.class, "walk");
verify(walker, C2.class, "testEmbeddedWalker");
} // here is the end line number of approximate range, L136.
static void verify(StackWalker walker, Class<?> c, String mn) {
final String fileName = "EmbeddedStackWalkTest.java";
walker.walk(s -> {
s.limit(BIG_LOOP)
.filter(f -> c.getName().equals(f.getClassName()) && mn.equals(f.getMethodName()))
.forEach(f -> {
assertEquals(f.getFileName().get(), fileName);
int line = f.getLineNumber().getAsInt();
assertTrue(line >= BEGIN_LINE && line <= END_LINE);
StackTraceElement st = f.toStackTraceElement();
assertEquals(c.getName(), st.getClassName());
assertEquals(mn, st.getMethodName());
assertEquals(st.getFileName(), fileName);
line = st.getLineNumber();
assertTrue(line >= BEGIN_LINE && line <= END_LINE);
});
return null;
});
}
}
}

View File

@ -0,0 +1,253 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8140450
* @summary Basic test for StackWalker.getCallerClass()
* @run main/othervm -XX:-MemberNameInStackFrame GetCallerClassTest
* @run main/othervm -XX:+MemberNameInStackFrame GetCallerClassTest
* @run main/othervm GetCallerClassTest sm
*/
import static java.lang.StackWalker.Option.*;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.ProtectionDomain;
import java.util.Arrays;
import java.util.List;
public class GetCallerClassTest {
private final StackWalker walker;
private final boolean expectUOE;
public GetCallerClassTest(StackWalker sw, boolean expect) {
this.walker = sw;
this.expectUOE = expect;
}
public static void main(String... args) throws Exception {
if (args.length > 0 && args[0].equals("sm")) {
PermissionCollection perms = new Permissions();
perms.add(new StackFramePermission("retainClassReference"));
Policy.setPolicy(new Policy() {
@Override
public boolean implies(ProtectionDomain domain, Permission p) {
return perms.implies(p);
}
});
System.setSecurityManager(new SecurityManager());
}
new GetCallerClassTest(StackWalker.getInstance(), true).test();
new GetCallerClassTest(StackWalker.getInstance(RETAIN_CLASS_REFERENCE), false).test();
}
public void test() {
new TopLevelCaller().run();
new Nested().createNestedCaller().run();
new InnerClassCaller().run();
new ReflectionTest().run();
List<Thread> threads = Arrays.asList(
new Thread(new TopLevelCaller()),
new Thread(new Nested().createNestedCaller()),
new Thread(new InnerClassCaller()),
new Thread(new ReflectionTest())
);
threads.stream().forEach(Thread::start);
threads.stream().forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
}
public static void staticGetCallerClass(StackWalker stackWalker,
Class<?> expected,
boolean expectUOE) {
try {
Class<?> c = stackWalker.getCallerClass();
assertEquals(c, expected);
if (expectUOE) { // Should have thrown
throw new RuntimeException("Didn't get expected exception");
}
} catch (RuntimeException e) { // also catches UOE
if (expectUOE && causeIsUOE(e)) {
return; /* expected */
}
System.err.println("Unexpected exception:");
throw e;
}
}
public static void reflectiveGetCallerClass(StackWalker stackWalker,
Class<?> expected,
boolean expectUOE) {
try {
Method m = StackWalker.class.getMethod("getCallerClass");
Class<?> c = (Class<?>) m.invoke(stackWalker);
assertEquals(c, expected);
if (expectUOE) { // Should have thrown
throw new RuntimeException("Didn't get expected exception");
}
} catch (Throwable e) {
if (expectUOE && causeIsUOE(e)) {
return; /* expected */
}
System.err.println("Unexpected exception:");
throw new RuntimeException(e);
}
}
public static void methodHandleGetCallerClass(StackWalker stackWalker,
Class<?> expected,
boolean expectUOE) {
MethodHandles.Lookup lookup = MethodHandles.lookup();
try {
MethodHandle mh = lookup.findVirtual(StackWalker.class, "getCallerClass",
MethodType.methodType(Class.class));
Class<?> c = (Class<?>) mh.invokeExact(stackWalker);
assertEquals(c, expected);
if (expectUOE) { // Should have thrown
throw new RuntimeException("Didn't get expected exception");
}
} catch (Throwable e) {
if (expectUOE && causeIsUOE(e)) {
return; /* expected */
}
System.err.println("Unexpected exception:");
throw new RuntimeException(e);
}
}
public static void assertEquals(Class<?> c, Class<?> expected) {
if (expected != c) {
throw new RuntimeException(c + " != " + expected);
}
}
/** Is there an UnsupportedOperationException in there? */
public static boolean causeIsUOE(Throwable t) {
while (t != null) {
if (t instanceof UnsupportedOperationException) {
return true;
}
t = t.getCause();
}
return false;
}
class TopLevelCaller implements Runnable {
public void run() {
GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
}
}
class Nested {
NestedClassCaller createNestedCaller() { return new NestedClassCaller(); }
class NestedClassCaller implements Runnable {
public void run() {
GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
}
}
}
class InnerClassCaller implements Runnable {
public void run() {
new Inner().test();
}
class Inner {
void test() {
GetCallerClassTest.staticGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.reflectiveGetCallerClass(walker, this.getClass(), expectUOE);
GetCallerClassTest.methodHandleGetCallerClass(walker, this.getClass(), expectUOE);
}
}
}
class ReflectionTest implements Runnable {
final MethodType methodType =
MethodType.methodType(void.class, StackWalker.class, Class.class, boolean.class);
public void run() {
callMethodHandle();
callMethodHandleRefl();
callMethodInvoke();
callMethodInvokeRefl();
}
void callMethodHandle() {
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
try {
MethodHandle mh = lookup.findStatic(GetCallerClassTest.class,
"staticGetCallerClass",
methodType);
mh.invokeExact(walker, ReflectionTest.class, expectUOE);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
void callMethodHandleRefl() {
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
try {
MethodHandle mh = lookup.findStatic(GetCallerClassTest.class,
"reflectiveGetCallerClass",
methodType);
mh.invokeExact(walker, ReflectionTest.class, expectUOE);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
void callMethodInvoke() {
try {
Method m = GetCallerClassTest.class.getMethod("staticGetCallerClass",
StackWalker.class, Class.class, boolean.class);
m.invoke(null, walker, ReflectionTest.class, expectUOE);
} catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
throw new RuntimeException(e);
}
}
void callMethodInvokeRefl() {
try {
Method m = GetCallerClassTest.class.getMethod("reflectiveGetCallerClass",
StackWalker.class, Class.class, boolean.class);
m.invoke(null, walker, ReflectionTest.class, expectUOE);
} catch (UnsupportedOperationException e) {
throw e;
} catch (NoSuchMethodException|IllegalAccessException|InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8020968
* @summary Basic test for hidden frames
* @run main HiddenFrames
*/
import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class HiddenFrames {
public static void main(String... args) throws Exception {
new HiddenFrames().test();
new HiddenFrames(Option.SHOW_REFLECT_FRAMES).test();
new HiddenFrames(Option.SHOW_HIDDEN_FRAMES).test();
}
private final Option option;
private final StackWalker walker;
private final List<StackFrame> lambdas = new ArrayList<>();
private final List<StackFrame> reflects = new ArrayList<>();
HiddenFrames() {
this.option = null;
this.walker = StackWalker.getInstance();
}
HiddenFrames(Option option) {
this.option = option;
this.walker = StackWalker.getInstance(option);
}
void test() throws Exception {
walk();
walkFromReflection();
}
void walk() {
Stream.of(0).forEach(i -> walker.walk(s ->
{
s.forEach(this::checkFrame);
return null;
}));
// only check hidden frames but not reflection frames
// walk is not invoked via reflection
if (option == null && !lambdas.isEmpty()) {
throw new RuntimeException("Hidden frames are shown");
}
if (option == Option.SHOW_HIDDEN_FRAMES && lambdas.isEmpty()) {
throw new RuntimeException("No hidden Lambda frame");
}
}
void walkFromReflection() throws Exception {
Method m = HiddenFrames.class.getDeclaredMethod("walk");
m.invoke(this);
if (option == null && !lambdas.isEmpty()) {
throw new RuntimeException("Hidden frames are shown");
}
if (option == Option.SHOW_HIDDEN_FRAMES && lambdas.isEmpty()) {
throw new RuntimeException("No hidden Lambda frame");
}
if (option != null && reflects.isEmpty()) {
throw new RuntimeException("No reflection frame");
}
}
void checkFrame(StackFrame frame) {
String cn = frame.getClassName();
if (cn.startsWith("java.lang.reflect.") || cn.startsWith("sun.reflect.")) {
reflects.add(frame);
}
if (cn.contains("$$Lambda$")) {
lambdas.add(frame);
}
}
}

View File

@ -0,0 +1,118 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8020968
* @summary Sanity test for locals and operands
* @run main LocalsAndOperands
*/
import java.lang.StackWalker.StackFrame;
import java.lang.reflect.*;
import java.util.List;
import java.util.stream.Collectors;
public class LocalsAndOperands {
static Class<?> liveStackFrameClass;
static Class<?> primitiveValueClass;
static StackWalker extendedWalker;
static Method getLocals;
static Method getOperands;
static Method getMonitors;
static Method primitiveType;
public static void main(String... args) throws Exception {
liveStackFrameClass = Class.forName("java.lang.LiveStackFrame");
primitiveValueClass = Class.forName("java.lang.LiveStackFrame$PrimitiveValue");
getLocals = liveStackFrameClass.getDeclaredMethod("getLocals");
getLocals.setAccessible(true);
getOperands = liveStackFrameClass.getDeclaredMethod("getStack");
getOperands.setAccessible(true);
getMonitors = liveStackFrameClass.getDeclaredMethod("getMonitors");
getMonitors.setAccessible(true);
primitiveType = primitiveValueClass.getDeclaredMethod("type");
primitiveType.setAccessible(true);
Method method = liveStackFrameClass.getMethod("getStackWalker");
method.setAccessible(true);
extendedWalker = (StackWalker) method.invoke(null);
new LocalsAndOperands(extendedWalker, true).test();
// no access to local and operands.
new LocalsAndOperands(StackWalker.getInstance(), false).test();
}
private final StackWalker walker;
private final boolean extended;
LocalsAndOperands(StackWalker walker, boolean extended) {
this.walker = walker;
this.extended = extended;
}
synchronized void test() throws Exception {
int x = 10;
char c = 'z';
String hi = "himom";
long l = 1000000L;
double d = 3.1415926;
List<StackWalker.StackFrame> frames = walker.walk(s -> s.collect(Collectors.toList()));
if (extended) {
for (StackWalker.StackFrame f : frames) {
System.out.println("frame: " + f);
Object[] locals = (Object[]) getLocals.invoke(f);
for (int i = 0; i < locals.length; i++) {
System.out.format("local %d: %s type %s%n", i, locals[i], type(locals[i]));
}
Object[] operands = (Object[]) getOperands.invoke(f);
for (int i = 0; i < operands.length; i++) {
System.out.format("operand %d: %s type %s%n", i, operands[i], type(operands[i]));
}
Object[] monitors = (Object[]) getMonitors.invoke(f);
for (int i = 0; i < monitors.length; i++) {
System.out.format("monitor %d: %s%n", i, monitors[i]);
}
}
} else {
for (StackFrame f : frames) {
if (liveStackFrameClass.isInstance(f))
throw new RuntimeException("should not be LiveStackFrame");
}
}
}
String type(Object o) throws Exception {
if (primitiveValueClass.isInstance(o)) {
char c = (char)primitiveType.invoke(o);
return String.valueOf(c);
} else {
return o.getClass().getName();
}
}
}

View File

@ -0,0 +1,362 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.lang.StackWalker.StackFrame;
import static java.lang.StackWalker.Option.*;
/**
* @test
* @bug 8140450
* @summary This test will walk the stack using different methods, called
* from several threads running concurrently.
* Except in the case of MTSTACKSTREAM - which takes a snapshot
* of the stack before walking, all the methods only allow to
* walk the current thread stack.
* @run main/othervm MultiThreadStackWalk
* @author danielfuchs
*/
public class MultiThreadStackWalk {
static Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
"sun.reflect.NativeMethodAccessorImpl",
"sun.reflect.DelegatingMethodAccessorImpl",
"java.lang.reflect.Method",
"com.sun.javatest.regtest.MainWrapper$MainThread",
"java.lang.Thread"
));
static final List<Class<?>> streamPipelines = Arrays.asList(
classForName("java.util.stream.AbstractPipeline"),
classForName("java.util.stream.TerminalOp")
);
static Class<?> classForName(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e){
throw new RuntimeException(e);
}
}
private static boolean isStreamPipeline(Class<?> clazz) {
for (Class<?> c : streamPipelines) {
if (c.isAssignableFrom(clazz)) {
return true;
}
}
return false;
}
/**
* An object that contains variables pertaining to the execution
* of the test within one thread.
* A small amount of those variable are shared with sub threads when
* the stack walk is executed in parallel - that is when spliterators
* obtained from trySplit are handed over to an instance of SplitThread
* in order to parallelize thread walking.
* @see WalkThread#handOff(MultiThreadStackWalk.Env, java.util.Spliterator, boolean, boolean)
* @see Env#split(MultiThreadStackWalk.Env)
*/
public static class Env {
final AtomicLong frameCounter; // private: the counter for the current thread.
final long checkMarkAt; // constant: the point at which we expect to
// find the marker in consume()
final long max; // constant: the maximum number of recursive
// calls to Call.
final AtomicBoolean debug ; // shared: whether debug is active for the
// instance of Test from which this instance
// of Env was spawned
final AtomicLong markerCalled; // shared: whether the marker was reached
final AtomicLong maxReached; // shared: whether max was reached
final Set<String> unexpected; // shared: list of unexpected infrastructure
// classes encountered after max is reached
public Env(long total, long markAt, AtomicBoolean debug) {
this.debug = debug;
frameCounter = new AtomicLong();
maxReached = new AtomicLong();
unexpected = Collections.synchronizedSet(new TreeSet<>());
this.max = total+2;
this.checkMarkAt = total - markAt + 1;
this.markerCalled = new AtomicLong();
}
// Used when delegating part of the stack walking to a sub thread
// see WalkThread.handOff.
private Env(Env orig, long start) {
debug = orig.debug;
frameCounter = new AtomicLong(start);
maxReached = orig.maxReached;
unexpected = orig.unexpected;
max = orig.max;
checkMarkAt = orig.checkMarkAt;
markerCalled = orig.markerCalled;
}
// The stack walk consumer method, where all the checks are
// performed.
public void consume(StackFrame sfi) {
if (frameCounter.get() == 0 && isStreamPipeline(sfi.getDeclaringClass())) {
return;
}
final long count = frameCounter.getAndIncrement();
final StringBuilder builder = new StringBuilder();
builder.append("Declaring class[")
.append(count)
.append("]: ")
.append(sfi.getDeclaringClass());
builder.append('\n');
builder.append("\t")
.append(sfi.getClassName())
.append(".")
.append(sfi.toStackTraceElement().getMethodName())
.append(sfi.toStackTraceElement().isNativeMethod()
? "(native)"
: "(" + sfi.toStackTraceElement().getFileName()
+":"+sfi.toStackTraceElement().getLineNumber()+")");
builder.append('\n');
if (debug.get()) {
System.out.print("[debug] " + builder.toString());
builder.setLength(0);
}
if (count == max) {
maxReached.incrementAndGet();
}
if (count == checkMarkAt) {
if (sfi.getDeclaringClass() != MultiThreadStackWalk.Marker.class) {
throw new RuntimeException("Expected Marker at " + count
+ ", found " + sfi.getDeclaringClass());
}
} else {
if (count <= 0 && sfi.getDeclaringClass() != MultiThreadStackWalk.Call.class) {
throw new RuntimeException("Expected Call at " + count
+ ", found " + sfi.getDeclaringClass());
} else if (count > 0 && count < max && sfi.getDeclaringClass() != MultiThreadStackWalk.Test.class) {
throw new RuntimeException("Expected Test at " + count
+ ", found " + sfi.getDeclaringClass());
} else if (count == max && sfi.getDeclaringClass() != MultiThreadStackWalk.class) {
throw new RuntimeException("Expected MultiThreadStackWalk at "
+ count + ", found " + sfi.getDeclaringClass());
} else if (count == max && !sfi.toStackTraceElement().getMethodName().equals("runTest")) {
throw new RuntimeException("Expected runTest method at "
+ count + ", found " + sfi.toStackTraceElement().getMethodName());
} else if (count == max+1) {
if (sfi.getDeclaringClass() != MultiThreadStackWalk.WalkThread.class) {
throw new RuntimeException("Expected MultiThreadStackWalk at "
+ count + ", found " + sfi.getDeclaringClass());
}
if (count == max && !sfi.toStackTraceElement().getMethodName().equals("run")) {
throw new RuntimeException("Expected main method at "
+ count + ", found " + sfi.toStackTraceElement().getMethodName());
}
} else if (count > max+1) {
// expect JTreg infrastructure...
if (!infrastructureClasses.contains(sfi.getDeclaringClass().getName())) {
System.err.println("**** WARNING: encountered unexpected infrastructure class at "
+ count +": " + sfi.getDeclaringClass().getName());
unexpected.add(sfi.getDeclaringClass().getName());
}
}
}
if (count == 100) {
// Maybe we should had some kind of checking inside that lambda
// too. For the moment we should be satisfied if it doesn't throw
// any exception and doesn't make the outer walk fail...
StackWalker.getInstance(RETAIN_CLASS_REFERENCE).forEach(x -> {
StackTraceElement st = x.toStackTraceElement();
StringBuilder b = new StringBuilder();
b.append("*** inner walk: ")
.append(x.getClassName())
.append(st == null ? "- no stack trace element -" :
("." + st.getMethodName()
+ (st.isNativeMethod() ? "(native)" :
"(" + st.getFileName()
+ ":" + st.getLineNumber() + ")")))
.append('\n');
if (debug.get()) {
System.out.print(b.toString());
b.setLength(0);
}
});
}
}
}
public interface Call {
enum WalkType {
WALKSTACK, // use Thread.walkStack
}
default WalkType getWalkType() { return WalkType.WALKSTACK;}
default void walk(Env env) {
WalkType walktype = getWalkType();
System.out.println("Thread "+ Thread.currentThread().getName()
+" starting walk with " + walktype);
switch(walktype) {
case WALKSTACK:
StackWalker.getInstance(RETAIN_CLASS_REFERENCE)
.forEach(env::consume);
break;
default:
throw new InternalError("Unknown walk type: " + walktype);
}
}
default void call(Env env, Call next, int total, int current, int markAt) {
if (current < total) {
next.call(env, next, total, current+1, markAt);
}
}
}
public static class Marker implements Call {
final WalkType walkType;
Marker(WalkType walkType) {
this.walkType = walkType;
}
@Override
public WalkType getWalkType() {
return walkType;
}
@Override
public void call(Env env, Call next, int total, int current, int markAt) {
env.markerCalled.incrementAndGet();
if (current < total) {
next.call(env, next, total, current+1, markAt);
} else {
next.walk(env);
}
}
}
public static class Test implements Call {
final Marker marker;
final WalkType walkType;
final AtomicBoolean debug;
Test(WalkType walkType) {
this.walkType = walkType;
this.marker = new Marker(walkType);
this.debug = new AtomicBoolean();
}
@Override
public WalkType getWalkType() {
return walkType;
}
@Override
public void call(Env env, Call next, int total, int current, int markAt) {
if (current < total) {
int nexti = current + 1;
Call nextObj = nexti==markAt ? marker : next;
nextObj.call(env, next, total, nexti, markAt);
} else {
walk(env);
}
}
}
public static Env runTest(Test test, int total, int markAt) {
Env env = new Env(total, markAt, test.debug);
test.call(env, test, total, 0, markAt);
return env;
}
public static void checkTest(Env env, Test test) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + ": Marker called: " + env.markerCalled.get());
System.out.println(threadName + ": Max reached: " + env.maxReached.get());
System.out.println(threadName + ": Frames consumed: " + env.frameCounter.get());
if (env.markerCalled.get() == 0) {
throw new RuntimeException(Thread.currentThread().getName() + ": Marker was not called.");
}
if (env.markerCalled.get() > 1) {
throw new RuntimeException(Thread.currentThread().getName()
+ ": Marker was called more than once: " + env.maxReached.get());
}
if (!env.unexpected.isEmpty()) {
System.out.flush();
System.err.println("Encountered some unexpected infrastructure classes below 'main': "
+ env.unexpected);
}
if (env.maxReached.get() == 0) {
throw new RuntimeException(Thread.currentThread().getName()
+ ": max not reached");
}
if (env.maxReached.get() > 1) {
throw new RuntimeException(Thread.currentThread().getName()
+ ": max was reached more than once: " + env.maxReached.get());
}
}
static class WalkThread extends Thread {
final static AtomicLong walkersCount = new AtomicLong();
Throwable failed = null;
final Test test;
public WalkThread(Test test) {
super("WalkThread[" + walkersCount.incrementAndGet() + ", type="
+ test.getWalkType() + "]");
this.test = test;
}
public void run() {
try {
Env env = runTest(test, 2000, 10);
//waitWalkers(env);
checkTest(env, test);
} catch(Throwable t) {
failed = t;
}
}
}
public static void main(String[] args) throws Throwable {
WalkThread[] threads = new WalkThread[Call.WalkType.values().length*3];
Throwable failed = null;
for (int i=0; i<threads.length; i++) {
Test test = new Test(Call.WalkType.values()[i%Call.WalkType.values().length]);
threads[i] = new WalkThread(test);
}
for (int i=0; i<threads.length; i++) {
threads[i].start();
}
for (int i=0; i<threads.length; i++) {
threads[i].join();
if (failed == null) failed = threads[i].failed;
else if (threads[i].failed == null) {
failed.addSuppressed(threads[i].failed);
}
}
if (failed != null) {
throw failed;
}
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/**
* @test
* @bug 8140450
* @summary Sanity test for exception cases
* @run testng SanityTest
*/
import java.util.Collections;
import java.util.Set;
import org.testng.annotations.Test;
public class SanityTest {
@Test
public static void testNPE() {
try {
StackWalker sw = StackWalker.getInstance((Set<StackWalker.Option>) null);
throw new RuntimeException("NPE expected");
} catch (NullPointerException e) {}
try {
StackWalker sw = StackWalker.getInstance((StackWalker.Option) null);
throw new RuntimeException("NPE expected");
} catch (NullPointerException e) {}
}
@Test
public static void testUOE() {
try {
StackWalker.getInstance().getCallerClass();
throw new RuntimeException("UOE expected");
} catch (UnsupportedOperationException expected) {}
}
@Test
public static void testInvalidEstimateDepth() {
try {
StackWalker sw = StackWalker.getInstance(Collections.emptySet(), 0);
throw new RuntimeException("Illegal estimateDepth should throw IAE");
} catch (IllegalArgumentException e) {}
}
@Test
public static void testNullFuncation() {
try {
StackWalker.getInstance().walk(null);
throw new RuntimeException("NPE expected");
} catch (NullPointerException e) {}
}
@Test
public static void testNullConsumer() {
try {
StackWalker.getInstance().forEach(null);
throw new RuntimeException("NPE expected");
} catch (NullPointerException e) {}
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test 8020968
* @summary Test security permission check
* @run main/othervm/java.security.policy=noperms.policy SecurityExceptions true
* @run main/othervm/java.security.policy=stackwalk.policy SecurityExceptions false
*/
public class SecurityExceptions {
public static void main(String[] args) {
boolean expectException = Boolean.parseBoolean(args[0]);
StackWalker sw = StackWalker.getInstance();
try {
sw = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
if (expectException) {
throw new RuntimeException("Expected SecurityException, but none thrown");
}
} catch (SecurityException e) {
if (!expectException) {
System.err.println("Unexpected security exception:");
throw e;
}
}
}
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.util.*;
/**
* Utility class for recording a stack trace for later comparison to
* StackWalker results.
*
* StackTraceElement comparison does not include line number, isNativeMethod
*/
public class StackRecorderUtil implements Iterable<StackRecorderUtil.TestFrame> {
private List<TestFrame> testFrames = new LinkedList();
private boolean compareClasses;
private boolean compareClassNames = true;
private boolean compareMethodNames = true;
private boolean compareSTEs;
public StackRecorderUtil(Set<StackWalker.Option> swOptions) {
compareClasses = swOptions.contains(Option.RETAIN_CLASS_REFERENCE);
compareSTEs = true;
}
/**
* Add a method call to this recorded stack.
*/
public void add(Class declaringClass, String methodName, String fileName) {
testFrames.add(0, new TestFrame(declaringClass, methodName, fileName));
}
public int frameCount() { return testFrames.size(); }
/**
* Compare the given StackFrame returned from the StackWalker to the
* recorded frame at the given index.
*
* Tests for equality, as well as functional correctness with respect to
* the StackWalker's options (e.g. throws or doesn't throw exceptions)
*/
public void compareFrame(int index, StackFrame sf) {
TestFrame tf = testFrames.get(index);
if (compareClasses) {
if (!tf.declaringClass.equals(sf.getDeclaringClass())) {
throw new RuntimeException("Expected class: " +
tf.declaringClass.toString() + ", but got: " +
sf.getDeclaringClass().toString());
}
} else {
boolean caught = false;
try {
sf.getDeclaringClass();
} catch (UnsupportedOperationException e) {
caught = true;
}
if (!caught) {
throw new RuntimeException("StackWalker did not have " +
"RETAIN_CLASS_REFERENCE Option, but did not throw " +
"UnsupportedOperationException");
}
}
if (compareClassNames && !tf.className().equals(sf.getClassName())) {
throw new RuntimeException("Expected class name: " + tf.className() +
", but got: " + sf.getClassName());
}
if (compareMethodNames && !tf.methodName.equals(sf.getMethodName())) {
throw new RuntimeException("Expected method name: " + tf.methodName +
", but got: " + sf.getMethodName());
}
if (compareSTEs) {
StackTraceElement ste = sf.toStackTraceElement();
if (!(ste.getClassName().equals(tf.className()) &&
ste.getMethodName().equals(tf.methodName)) &&
ste.getFileName().equals(tf.fileName)) {
throw new RuntimeException("Expected StackTraceElement info: " +
tf + ", but got: " + ste);
}
if (!Objects.equals(ste.getClassName(), sf.getClassName())
|| !Objects.equals(ste.getMethodName(), sf.getMethodName())
|| !Objects.equals(ste.getFileName(), sf.getFileName().orElse(null))
|| !Objects.equals(ste.getLineNumber(), sf.getLineNumber().orElse(-1))
|| !Objects.equals(ste.isNativeMethod(), sf.isNativeMethod())) {
throw new RuntimeException("StackFrame and StackTraceElement differ: " +
"sf=" + sf + ", ste=" + ste);
}
}
}
public Iterator<TestFrame> iterator() {
return testFrames.iterator();
}
/**
* Class used to record stack frame information.
*/
public static class TestFrame {
public Class declaringClass;
public String methodName;
public String fileName = null;
public TestFrame (Class declaringClass, String methodName, String fileName) {
this.declaringClass = declaringClass;
this.methodName = methodName;
this.fileName = fileName;
}
public String className() {
return declaringClass.getName();
}
public String toString() {
return "TestFrame: " + className() + "." + methodName +
(fileName == null ? "" : "(" + fileName + ")");
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8020968
* @summary Basic test for Stream<StackFrame> state
* @run main StackStreamState
*/
import java.lang.StackWalker.StackFrame;
import java.util.stream.Stream;
public class StackStreamState {
public static void main(String... args) {
StackStreamState test = new StackStreamState();
test.testStatic();
test.testInstance();
test.testLocal();
}
private static Stream<StackFrame> staticStream;
private Stream<StackFrame> instanceStream;
private final StackWalker walker = StackWalker.getInstance();
void testStatic() {
walker.walk(s -> {
staticStream = s;
return null;
});
checkStreamState(staticStream);
}
void testInstance() {
walker.walk(s -> {
instanceStream = s;
return null;
});
checkStreamState(instanceStream);
}
void testLocal() {
Stream<StackFrame> stream = walker.walk(s -> {
return s;
});
checkStreamState(stream);
}
void checkStreamState(Stream<StackFrame> stream) {
try {
stream.count();
throw new RuntimeException("IllegalStateException not thrown");
} catch (IllegalStateException e) {
System.out.println("Got expected IllegalStateException: " + e.getMessage());
e.printStackTrace(System.out);
}
}
}

View File

@ -0,0 +1,290 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
import static java.lang.StackWalker.Option.*;
import java.lang.StackWalker.StackFrame;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* @test
* @bug 8140450
* @summary Stack Stream Test
* @run main/othervm StackStreamTest
*/
public class StackStreamTest {
public static void main(String[] argv) throws Exception {
new StackStreamTest().test();
}
private static Logger logger = Logger.getLogger("stackstream");
public StackStreamTest() {
}
public void test() {
A.a();
}
static class A {
public static void a() {
B.b();
}
}
static class B {
public static void b() {
C.c();
}
}
static class C {
public static void c() {
D.d();
}
}
static class D {
public static void d() {
E.e();
}
}
static class E {
public static void e() {
F.f();
}
}
static class F {
public static void f() {
logger.severe("log message");
G.g();
new K().k();
}
}
private static boolean isTestClass(StackFrame f) {
// Filter jtreg frames from the end of the stack
return f.getClassName().startsWith("StackStreamTest");
}
static class G {
static StackWalker STE_WALKER = StackWalker.getInstance();
static StackWalker DEFAULT_WALKER = StackWalker.getInstance();
private static final List<String> GOLDEN_CLASS_NAMES =
Arrays.asList("StackStreamTest$G",
"StackStreamTest$F",
"StackStreamTest$E",
"StackStreamTest$D",
"StackStreamTest$C",
"StackStreamTest$B",
"StackStreamTest$A",
"StackStreamTest",
"StackStreamTest");
private static final List<String> GOLDEN_METHOD_NAMES =
Arrays.asList("g", "f", "e", "d", "c", "b", "a", "test", "main");
public static void g() {
System.out.println("Thread dump");
Thread.dumpStack();
caller();
firstFrame();
// Check class names
System.out.println("check class names");
List<String> sfs = DEFAULT_WALKER.walk(s -> {
return s.filter(StackStreamTest::isTestClass)
.map(StackFrame::getClassName)
.collect(Collectors.toList());
});
equalsOrThrow("class names", sfs, GOLDEN_CLASS_NAMES);
// Check method names
System.out.println("methodNames()");
sfs = DEFAULT_WALKER.walk(s -> {
return s.filter(StackStreamTest::isTestClass)
.map(StackFrame::getMethodName)
.collect(Collectors.toList());}
);
equalsOrThrow("method names", sfs, GOLDEN_METHOD_NAMES);
Exception exc = new Exception("G.g stack");
exc.printStackTrace();
System.out.println("Stream of StackTraceElement");
StackWalker.getInstance()
.walk(s ->
{
s.map(StackFrame::toStackTraceElement)
.forEach(ste -> System.out.println("STE: " + ste));
return null;
});
// Do we need this?
System.out.println("Collect StackTraceElement");
List<StackTraceElement> stacktrace = STE_WALKER.walk(s ->
{
// Filter out jtreg frames
return s.filter(StackStreamTest::isTestClass)
.collect(Collectors.mapping(StackFrame::toStackTraceElement, Collectors.toList()));
});
int i=0;
for (StackTraceElement s : stacktrace) {
System.out.format(" %d: %s%n", i++, s);
}
// Check STEs for correctness
checkStackTraceElements(GOLDEN_CLASS_NAMES, GOLDEN_METHOD_NAMES, stacktrace);
}
static void checkStackTraceElements(List<String> classNames,
List<String> methodNames,
List<StackTraceElement> stes) {
if (classNames.size() != methodNames.size() ) {
throw new RuntimeException("Test error: classNames and methodNames should be same size");
}
if (classNames.size() != stes.size()) {
dumpSTEInfo(classNames, methodNames, stes);
throw new RuntimeException("wrong number of elements in stes");
}
for (int i = 0; i < classNames.size() ; i++) {
if (!classNames.get(i).equals(stes.get(i).getClassName()) ||
!methodNames.get(i).equals(stes.get(i).getMethodName())) {
dumpSTEInfo(classNames, methodNames, stes);
throw new RuntimeException("class & method names don't match");
}
}
}
static void dumpSTEInfo(List<String> classNames, List<String> methodNames,
List<StackTraceElement> stes) {
System.out.println("Observed class, method names:");
for (StackTraceElement ste : stes) {
System.out.println(" " + ste.getClassName() + ", " + ste.getMethodName());
}
System.out.println("Expected class, method names:");
for (int i = 0; i < classNames.size(); i++) {
System.out.println(" " + classNames.get(i) + ", " + methodNames.get(i));
}
}
static void firstFrame() {
System.out.println("first frame()");
StackWalker sw = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
sw.forEach(e -> {
System.out.println(e.getClassName() + "," + e.getMethodName());
});
System.out.println("\n");
Optional<StackFrame> frame = sw.walk(s ->
{
return s.filter(e -> {
System.err.println(e.getClassName() + " == " +
e.getClassName().equals("StackStreamTest"));
return e.getClassName().equals("StackStreamTest");
}).findFirst();
});
Class<?> c = frame.get().getDeclaringClass();
System.out.println("\nfirst frame: " + c);
if (c != StackStreamTest.class) {
throw new RuntimeException("Unexpected first caller class " + c);
}
}
}
private static <T> void equalsOrThrow(String label, List<T> list, List<T> expected) {
System.out.println("List: " + list);
System.out.println("Expectd: " + list);
if (!list.equals(expected)) {
System.err.println("Observed " + label);
for (T s1 : list) {
System.out.println(" " + s1);
}
System.err.println("Expected " + label);
for (T s2 : expected) {
System.out.println(" " + s2);
}
throw new RuntimeException("Error with " + label);
}
}
static class K {
void k() {
k1();
}
void k1() {
k2();
}
void k2() {
k3();
}
void k3() {
k4();
}
void k4() {
k5();
}
void k5() {
k6();
}
void k6() {
k7();
}
void k7() {
k8();
}
void k8() {
k9();
}
void k9() {
k10();
}
void k10() {
k20();
}
void k20() {
new Caller().test();
}
class Caller {
void test() {
Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
System.out.println("\nTesting K class : " + c);
Thread.dumpStack();
if (c != K.class) {
throw new RuntimeException("Unexpected caller class "+ c);
}
}
}
}
static void caller() {
Class<?> c = StackWalker.getInstance(RETAIN_CLASS_REFERENCE).getCallerClass();
System.out.println("\ncaller class : " + c);
if (c != G.class) {
throw new RuntimeException("Unexpected caller class "+ c);
}
}
}

View File

@ -0,0 +1,355 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
import static java.lang.StackWalker.Option.*;
import java.lang.StackWalker.StackFrame;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import jdk.testlibrary.RandomFactory;
/**
* @test
* @bug 8140450
* @summary Stack Walk Test (use -Dseed=X to set PRNG seed)
* @library /lib/testlibrary
* @build jdk.testlibrary.*
* @compile StackRecorderUtil.java
* @run main/othervm StackWalkTest
* @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest
* @run main/othervm StackWalkTest -random:50
* @run main/othervm/java.security.policy=stackwalktest.policy StackWalkTest -random:50
* @run main/othervm -XX:-MemberNameInStackFrame -Dstackwalk.newThrowable=false StackWalkTest -random:50
* @run main/othervm -XX:-MemberNameInStackFrame -Dstackwalk.newThrowable=true StackWalkTest -random:50
* @run main/othervm -XX:+MemberNameInStackFrame -Dstackwalk.newThrowable=false StackWalkTest -random:50
* @run main/othervm -XX:+MemberNameInStackFrame -Dstackwalk.newThrowable=true StackWalkTest -random:50
* @author danielfuchs, bchristi
* @key randomness
*/
public class StackWalkTest {
private static boolean random = false;
private static boolean verbose = false;
private static int randomRuns = 50;
private static final int MAX_RANDOM_DEPTH = 1000;
static final Set<String> infrastructureClasses = new TreeSet<>(Arrays.asList(
"sun.reflect.NativeMethodAccessorImpl",
"sun.reflect.DelegatingMethodAccessorImpl",
"java.lang.reflect.Method",
"com.sun.javatest.regtest.MainWrapper$MainThread",
"com.sun.javatest.regtest.agent.MainWrapper$MainThread",
"java.lang.Thread"
));
static final List<Class<?>> streamPipelines = Arrays.asList(
classForName("java.util.stream.AbstractPipeline"),
classForName("java.util.stream.TerminalOp")
);
static Class<?> classForName(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e){
throw new RuntimeException(e);
}
}
private static boolean isStreamPipeline(Class<?> clazz) {
for (Class<?> c : streamPipelines) {
if (c.isAssignableFrom(clazz)) {
return true;
}
}
return false;
}
StackRecorderUtil recorder;
int count = 0;
boolean didWalk = false;
final int estDepth;
final Set<StackWalker.Option> swOptions;
public StackWalkTest() {
this(EnumSet.noneOf(StackWalker.Option.class), -1);
}
public StackWalkTest(Set<StackWalker.Option> swOptions) {
this(swOptions, -1);
}
public StackWalkTest(int estimatedDepth) {
this(EnumSet.noneOf(StackWalker.Option.class), -1);
}
public StackWalkTest(Set<StackWalker.Option> swOptions, int estimatedDepth) {
this.swOptions = swOptions;
this.estDepth = estimatedDepth;
}
private StackWalker createStackWalker() {
// test all StackWalker factory methods
if (this.estDepth < 0) {
if (swOptions.isEmpty()) {
return StackWalker.getInstance();
} else {
return StackWalker.getInstance(swOptions);
}
}
return StackWalker.getInstance(swOptions, estDepth);
}
public void consume(StackFrame sf) {
if (count == 0 && swOptions.contains(StackWalker.Option.RETAIN_CLASS_REFERENCE)
&& isStreamPipeline(sf.getDeclaringClass())) {
return;
}
if (verbose) {
System.out.println("\t" + sf.getClassName() + "." + sf.getMethodName());
}
if (count >= recorder.frameCount()) {
// We've gone past main()...
if (infrastructureClasses.contains(sf.getClassName())) {
// safe to ignore
return;
}
}
try {
recorder.compareFrame(count, sf);
} catch (IndexOutOfBoundsException e) {
// Extra non-infra frame in stream
throw new RuntimeException("extra non-infra stack frame at count "
+ count + ": <" + sf + ">", e);
}
count++;
}
public class Call {
public void walk(int total, int markAt) {
recorder.add(Call.class, "walk", "StackWalkTest.java");
long swFrameCount = createStackWalker().walk(s -> s.count());
if (verbose) {
System.out.println("Call.walk() total=" + total + ", markAt=" + markAt);
System.out.println("recorder frames:");
for (StackRecorderUtil.TestFrame f : recorder) {
System.out.println("\t" + f.declaringClass + "." + f.methodName);
}
System.out.println("\nStackWalker recorded " + swFrameCount + " frames");
System.out.flush();
}
long recFrameCount = (long)recorder.frameCount();
if (swFrameCount < recFrameCount) {
throw new RuntimeException("StackWalker recorded fewer frames ("+
swFrameCount + ") than recorded ("+ recorder.frameCount() +
") - " + "estimatedDepth set to " + estDepth);
}
if (verbose) {
System.out.println("StackWalker frames:");
}
createStackWalker().forEach(StackWalkTest.this::consume);
didWalk = true;
}
public void call(int total, int current, int markAt) {
recorder.add(Call.class, "call", "StackWalkTest.java");
if (current < total) {
testCall.call(total, current+1, markAt);
} else {
walk(total, markAt);
}
}
}
public class Marker extends Call {
@Override
public void call(int total, int current, int markAt) {
recorder.add(Marker.class, "call", "StackWalkTest.java");
if (current < total) {
testCall.call(total, current+1, markAt);
} else {
walk(total, markAt);
}
}
}
private Call markerCall = new Marker();
public class Test extends Call {
@Override
public void call(int total, int current, int markAt) {
recorder.add(Test.class, "call", "StackWalkTest.java");
if (current < total) {
int nexti = current + 1;
if (nexti==markAt) {
markerCall.call(total, nexti, markAt);
} else {
testCall.call2(total, nexti, markAt);
}
} else {
walk(total, markAt);
}
}
public void call2(int total, int current, int markAt) {
recorder.add(Test.class, "call2", "StackWalkTest.java");
if (current < total) {
int nexti = current + 1;
if (nexti==markAt) {
markerCall.call(total, nexti, markAt);
} else {
test2Call.call(total, nexti, markAt);
}
} else {
walk(total, markAt);
}
}
}
private Test testCall = new Test();
/** Inherits call() from Call */
public class Test2 extends Call {}
private Test2 test2Call = new Test2();
public void runTest(Class callerClass, String callerMethod, int stackDepth,
int markAt) {
if (didWalk) {
throw new IllegalStateException("StackWalkTest already used");
}
assert markAt <= stackDepth : "markAt(" + markAt + ") > stackDepth("
+ stackDepth + ")";
System.out.print("runTest(" + swOptions
+ "), estimatedDepth=" + estDepth);
recorder = new StackRecorderUtil(swOptions);
recorder.add(callerClass, callerMethod, "StackWalkTest.java");
recorder.add(StackWalkTest.class, "runTest", "StackWalkTest.java");
Test test1 = new Test();
test1.call(stackDepth, 0, markAt);
System.out.println(" finished");
if (!didWalk) {
throw new IllegalStateException("Test wasn't actually performed");
}
}
public static void main(String[] args) {
String rand = "-random";
String randItems = "-random:";
for(String arg : args) {
if (arg.startsWith(rand)) {
random = true;
try {
if(arg.startsWith(randItems)) {
randomRuns = Integer.valueOf(arg.substring(randItems.length()));
}
} catch(NumberFormatException e) {}
} else if("-verbose".equals(arg)) {
verbose = true;
}
}
if (random) {
Random rng = RandomFactory.getRandom();
for (int iters = 0; iters < randomRuns; iters++) {
Set<StackWalker.Option> opts = new HashSet<>();
if (rng.nextBoolean()) {
opts.add(RETAIN_CLASS_REFERENCE);
}
int depth = 1 + rng.nextInt(MAX_RANDOM_DEPTH);
StackWalkTest swt;
if (rng.nextBoolean() && depth > 1) {
// Test that specifying an estimatedDepth doesn't prevent
// full stack traversal
swt = new StackWalkTest(opts, 1+rng.nextInt(depth-1));
} else {
swt = new StackWalkTest(opts);
}
int markAt = rng.nextInt(depth+1);
System.out.print(depth + "@" + markAt + " ");
System.out.flush();
swt.runTest(StackWalkTest.class, "main", depth, markAt);
}
} else {
// Long stack, default maxDepth
StackWalkTest swt;
swt = new StackWalkTest();
swt.runTest(StackWalkTest.class, "main", 2000, 10);
// Long stack, matching maxDepth
swt = new StackWalkTest(2000);
swt.runTest(StackWalkTest.class, "main", 2000, 10);
// Long stack, maximum maxDepth
swt = new StackWalkTest(Integer.MAX_VALUE);
swt.runTest(StackWalkTest.class, "main", 2000, 10);
//
// Single batch
//
swt = new StackWalkTest(); // default maxDepth
swt.runTest(StackWalkTest.class, "main", 6, 3);
swt = new StackWalkTest(4); // maxDepth < stack
swt.runTest(StackWalkTest.class, "main", 6, 3);
swt = new StackWalkTest(2); // maxDepth < marker
swt.runTest(StackWalkTest.class, "main", 6, 4);
//
// 2 batches
//
swt = new StackWalkTest(); // default maxDepth
swt.runTest(StackWalkTest.class, "main", 24, 10);
swt = new StackWalkTest(18); // maxDepth < stack
swt.runTest(StackWalkTest.class, "main", 24, 10);
swt = new StackWalkTest(8); // maxDepth < marker
swt.runTest(StackWalkTest.class, "main", 24, 10);
//
// 3 batch
//
swt = new StackWalkTest(); // default maxDepth
swt.runTest(StackWalkTest.class, "main", 60, 20);
swt = new StackWalkTest(35); // maxDepth < stack
swt.runTest(StackWalkTest.class, "main", 60, 20);
swt = new StackWalkTest(8); // maxDepth < marker
swt.runTest(StackWalkTest.class, "main", 60, 20);
//
// StackWalker.Options
//
swt = new StackWalkTest();
swt.runTest(StackWalkTest.class, "main", 50, 10);
swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE));
swt.runTest(StackWalkTest.class, "main", 80, 40);
swt = new StackWalkTest(EnumSet.of(RETAIN_CLASS_REFERENCE), 50);
swt.runTest(StackWalkTest.class, "main", 2000, 1048);
}
}
}

View File

@ -0,0 +1,284 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.EnumSet;
import java.util.concurrent.atomic.AtomicLong;
import java.lang.StackWalker.StackFrame;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Objects;
import static java.lang.StackWalker.Option.*;
/**
* @test
* @bug 8140450
* @summary Verify stack trace information obtained with respect to StackWalker
* options, when the stack contains lambdas, method handle invoke
* virtual calls, and reflection.
* @run main/othervm -XX:-MemberNameInStackFrame VerifyStackTrace
* @run main/othervm -XX:+MemberNameInStackFrame VerifyStackTrace
* @run main/othervm/java.security.policy=stackwalk.policy VerifyStackTrace
* @author danielfuchs
*/
public class VerifyStackTrace {
static interface TestCase {
StackWalker walker();
String description();
String expected();
}
static final class TestCase1 implements TestCase {
private final StackWalker walker = StackWalker.getInstance(RETAIN_CLASS_REFERENCE);
private final String description = "StackWalker.getInstance(" +
"StackWalker.Option.RETAIN_CLASS_REFERENCE)";
// Note: line numbers and lambda hashes will be erased when
// comparing stack traces. However, the stack may change
// if some methods are being renamed in the code base.
// If the JDKcode base changes and the test fails because of that,
// then after validating that the actual stack trace obtained
// is indeed correct (no frames are skipped that shouldn't)
// then you can cut & paste the <-- actual --> stack printed in the
// test output in here:
private final String expected =
"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:209)\n" +
"2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:145)\n" +
"3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:158)\n" +
"4: VerifyStackTrace.invoke(VerifyStackTrace.java:188)\n" +
"5: VerifyStackTrace$1.run(VerifyStackTrace.java:218)\n" +
"6: java.security.AccessController.doPrivileged(Native Method)\n" +
"7: VerifyStackTrace.test(VerifyStackTrace.java:227)\n" +
"8: VerifyStackTrace.main(VerifyStackTrace.java:182)\n";
@Override public StackWalker walker() { return walker;}
@Override public String description() { return description;}
@Override public String expected() { return expected;}
}
static final class TestCase2 implements TestCase {
private final StackWalker walker = StackWalker.getInstance(
EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_REFLECT_FRAMES));
private final String description = "nStackWalker.getInstance(" +
"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
"StackWalker.Option.SHOW_REFLECT_FRAMES)";
// Note: line numbers and lambda hashes will be erased when
// comparing stack traces. However, the stack may change
// if some methods are being renamed in the code base.
// If the JDK code base changes and the test fails because of that,
// then after validating that the actual stack trace obtained
// is indeed correct (no frames are skipped that shouldn't)
// then you can cut & paste the <-- actual --> stack printed in the
// test output in here (don't forget the final \n):
private final String expected =
"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:211)\n" +
"2: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:147)\n" +
"3: VerifyStackTrace$Handle.run(VerifyStackTrace.java:160)\n" +
"4: VerifyStackTrace.invoke(VerifyStackTrace.java:190)\n" +
"5: sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
"6: sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +
"7: sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +
"8: java.lang.reflect.Method.invoke(Method.java:520)\n" +
"9: VerifyStackTrace$1.run(VerifyStackTrace.java:220)\n" +
"10: java.security.AccessController.doPrivileged(Native Method)\n" +
"11: VerifyStackTrace.test(VerifyStackTrace.java:229)\n" +
"12: VerifyStackTrace.main(VerifyStackTrace.java:185)\n";
@Override public StackWalker walker() { return walker;}
@Override public String description() { return description;}
@Override public String expected() { return expected;}
}
static class TestCase3 implements TestCase {
private final StackWalker walker = StackWalker.getInstance(
EnumSet.of(RETAIN_CLASS_REFERENCE, SHOW_HIDDEN_FRAMES));
private final String description = "StackWalker.getInstance(" +
"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
"StackWalker.Option.SHOW_HIDDEN_FRAMES)";
// Note: line numbers and lambda hashes will be erased when
// comparing stack traces. However, the stack may change
// if some methods are being renamed in the code base.
// If the JDK code base changes and the test fails because of that,
// then after validating that the actual stack trace obtained
// is indeed correct (no frames are skipped that shouldn't)
// then you can cut & paste the <-- actual --> stack printed in the
// test output in here (don't forget the final \n):
private final String expected =
"1: VerifyStackTrace.lambda$test$1(VerifyStackTrace.java:213)\n" +
"2: VerifyStackTrace$$Lambda$1/662441761.run(Unknown Source)\n" +
"3: VerifyStackTrace$Handle.execute(VerifyStackTrace.java:149)\n" +
"4: java.lang.invoke.LambdaForm$DMH/2008017533.invokeVirtual_LL_V(LambdaForm$DMH)\n" +
"5: java.lang.invoke.LambdaForm$MH/1395089624.invoke_MT(LambdaForm$MH)\n" +
"6: VerifyStackTrace$Handle.run(VerifyStackTrace.java:162)\n" +
"7: VerifyStackTrace.invoke(VerifyStackTrace.java:192)\n" +
"8: sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n" +
"9: sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n" +
"10: sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n" +
"11: java.lang.reflect.Method.invoke(Method.java:520)\n" +
"12: VerifyStackTrace$1.run(VerifyStackTrace.java:222)\n" +
"13: java.security.AccessController.doPrivileged(Native Method)\n" +
"14: VerifyStackTrace.test(VerifyStackTrace.java:231)\n" +
"15: VerifyStackTrace.main(VerifyStackTrace.java:188)\n";
@Override public StackWalker walker() { return walker;}
@Override public String description() { return description;}
@Override public String expected() { return expected;}
}
static final class TestCase4 extends TestCase3 {
private final StackWalker walker = StackWalker.getInstance(
EnumSet.allOf(StackWalker.Option.class));
private final String description = "StackWalker.getInstance(" +
"StackWalker.Option.RETAIN_CLASS_REFERENCE, " +
"StackWalker.Option.SHOW_HIDDEN_FRAMES, " +
"StackWalker.Option.SHOW_REFLECT_FRAMES)";
@Override public StackWalker walker() {return walker;}
@Override public String description() {return description;}
}
public static class Handle implements Runnable {
Runnable impl;
public Handle(Runnable run) {
this.impl = run;
}
public void execute(Runnable run) {
run.run();
}
public void run() {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = null;
try {
handle = lookup.findVirtual(Handle.class, "execute",
MethodType.methodType(void.class, Runnable.class));
} catch(NoSuchMethodException | IllegalAccessException x) {
throw new RuntimeException(x);
}
try {
handle.invoke(this, impl);
} catch(Error | RuntimeException x) {
throw x;
} catch(Throwable t) {
throw new RuntimeException(t);
}
}
}
static String prepare(String produced, boolean eraseSensitiveInfo) {
if (eraseSensitiveInfo) {
// Erase sensitive information before comparing:
// comparing line numbers is too fragile, so we just erase them
// out before comparing. We also erase the hash-like names of
// synthetic frames introduced by lambdas & method handles
return produced.replaceAll(":[1-9][0-9]*\\)", ":00)")
.replaceAll("/[0-9]+\\.run", "/xxxxxxxx.run")
.replaceAll("/[0-9]+\\.invoke", "/xxxxxxxx.invoke")
.replaceAll("\\$[0-9]+", "\\$??");
} else {
return produced;
}
}
public static void main(String[] args) {
test(new TestCase1());
test(new TestCase2());
test(new TestCase3());
test(new TestCase4());
}
public static void invoke(Runnable run) {
run.run();
}
static final class Recorder {
boolean found; // stop recording after main
public void recordSTE(long counter, StringBuilder s, StackFrame f) {
if (found) return;
found = VerifyStackTrace.class.equals(f.getDeclaringClass()) &&
"main".equals(f.getMethodName());
String line = String.format("%d: %s", counter, f.toStackTraceElement());
s.append(line).append('\n');
System.out.println(line);
}
}
static void test(TestCase test) {
System.out.println("\nTesting: " + test.description());
final AtomicLong counter = new AtomicLong();
final StringBuilder builder = new StringBuilder();
final Recorder recorder = new Recorder();
final Runnable run = () -> test.walker().forEach(
f -> recorder.recordSTE(counter.incrementAndGet(), builder, f));
final Handle handle = new Handle(run);
// We're not using lambda on purpose here. We want the anonymous
// class on the stack.
PrivilegedAction<Object> pa = new PrivilegedAction<Object>() {
@Override
public Object run() {
try {
return VerifyStackTrace.class
.getMethod("invoke", Runnable.class)
.invoke(null, handle);
} catch (NoSuchMethodException
| IllegalAccessException
| InvocationTargetException ex) {
System.out.flush();
throw new RuntimeException(ex);
}
}
};
AccessController.doPrivileged(pa);
System.out.println("Main found: " + recorder.found);
if (!Objects.equals(prepare(test.expected(), true), prepare(builder.toString(), true))) {
System.out.flush();
try {
// sleep to make it less likely that System.out & System.err will
// interleave.
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
System.err.println("\nUnexpected stack trace: "
+ "\n<!-- expected -->\n"
+ prepare(test.expected(), true)
+ "\n<-- actual -->\n"
+ prepare(builder.toString(), false));
throw new RuntimeException("Unexpected stack trace for: " + test.description());
}
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
/*
* @test
* @bug 8020968
* @summary Sanity test for Function wildcard signature
* @run main WalkFunction
*/
import java.lang.StackWalker.StackFrame;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Stream;
public class WalkFunction {
private static final StackWalker walker = StackWalker.getInstance();
public static void main(String... args) throws Exception {
testFunctions();
testWildcards();
walker.walk(counter());
walker.walk(wildCounter());
}
private static void testFunctions() {
walker.walk(Stream::count);
try {
walker.walk(null);
throw new RuntimeException("NPE expected");
} catch (NullPointerException e) {}
Optional<StackFrame> result = walker.walk(WalkFunction::reduce);
if (!result.get().getClassName().equals(WalkFunction.class.getName())) {
throw new RuntimeException(result.get() + " expected: " + WalkFunction.class.getName());
}
}
static Optional<StackFrame> reduce(Stream<StackFrame> stream) {
return stream.reduce((r,f) -> r.getClassName().compareTo(f.getClassName()) > 0 ? f : r);
}
private static void testWildcards() {
Function<? super Stream<? extends StackFrame>, Void> f1 = WalkFunction::function;
Function<? super Stream<? super StackFrame>, Void> f2 = WalkFunction::function;
Function<? super Stream<StackFrame>, Void> f3 = WalkFunction::function;
Function<Stream<? extends StackFrame>, Void> f4 = WalkFunction::function;
Function<Stream<? super StackFrame>, Void> f5 = WalkFunction::function;
Function<Stream<StackFrame>, Void> f6 = WalkFunction::function;
walker.walk(f1);
walker.walk(f2);
walker.walk(f3);
walker.walk(f4);
walker.walk(f5);
walker.walk(f6);
}
private static Void function(Stream<?> s) {
return null;
}
private static Function<Stream<?>, Long> wildCounter() {
return Stream::count;
}
private static <T> Function<Stream<T>, Long> counter() {
return Stream::count;
}
}

View File

@ -0,0 +1,5 @@
/*
* grant nothing
*/
grant {};

View File

@ -0,0 +1,4 @@
grant {
permission java.lang.StackFramePermission "retainClassReference";
};

View File

@ -0,0 +1,5 @@
grant {
permission java.lang.StackFramePermission "retainClassReference";
permission java.util.PropertyPermission "seed", "read";
};