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:
parent
a45dc82702
commit
eb2c6c52bb
@ -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;
|
||||
|
226
jdk/src/java.base/share/classes/java/lang/LiveStackFrame.java
Normal file
226
jdk/src/java.base/share/classes/java/lang/LiveStackFrame.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
136
jdk/src/java.base/share/classes/java/lang/StackFrameInfo.java
Normal file
136
jdk/src/java.base/share/classes/java/lang/StackFrameInfo.java
Normal 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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
1106
jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java
Normal file
1106
jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java
Normal file
File diff suppressed because it is too large
Load Diff
563
jdk/src/java.base/share/classes/java/lang/StackWalker.java
Normal file
563
jdk/src/java.base/share/classes/java/lang/StackWalker.java
Normal 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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
59
jdk/src/java.base/share/native/libjava/StackFrameInfo.c
Normal file
59
jdk/src/java.base/share/native/libjava/StackFrameInfo.c
Normal 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);
|
||||
}
|
78
jdk/src/java.base/share/native/libjava/StackStreamFactory.c
Normal file
78
jdk/src/java.base/share/native/libjava/StackStreamFactory.c
Normal 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);
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
227
jdk/test/java/lang/StackWalker/AcrossThreads.java
Normal file
227
jdk/test/java/lang/StackWalker/AcrossThreads.java
Normal 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);
|
||||
}
|
||||
}
|
139
jdk/test/java/lang/StackWalker/Basic.java
Normal file
139
jdk/test/java/lang/StackWalker/Basic.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
137
jdk/test/java/lang/StackWalker/CallerFromMain.java
Normal file
137
jdk/test/java/lang/StackWalker/CallerFromMain.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
234
jdk/test/java/lang/StackWalker/DumpStackTest.java
Normal file
234
jdk/test/java/lang/StackWalker/DumpStackTest.java
Normal 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 + "]");
|
||||
}
|
||||
}
|
||||
}
|
158
jdk/test/java/lang/StackWalker/EmbeddedStackWalkTest.java
Normal file
158
jdk/test/java/lang/StackWalker/EmbeddedStackWalkTest.java
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
253
jdk/test/java/lang/StackWalker/GetCallerClassTest.java
Normal file
253
jdk/test/java/lang/StackWalker/GetCallerClassTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
108
jdk/test/java/lang/StackWalker/HiddenFrames.java
Normal file
108
jdk/test/java/lang/StackWalker/HiddenFrames.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
118
jdk/test/java/lang/StackWalker/LocalsAndOperands.java
Normal file
118
jdk/test/java/lang/StackWalker/LocalsAndOperands.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
362
jdk/test/java/lang/StackWalker/MultiThreadStackWalk.java
Normal file
362
jdk/test/java/lang/StackWalker/MultiThreadStackWalk.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
82
jdk/test/java/lang/StackWalker/SanityTest.java
Normal file
82
jdk/test/java/lang/StackWalker/SanityTest.java
Normal 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) {}
|
||||
}
|
||||
}
|
48
jdk/test/java/lang/StackWalker/SecurityExceptions.java
Normal file
48
jdk/test/java/lang/StackWalker/SecurityExceptions.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
137
jdk/test/java/lang/StackWalker/StackRecorderUtil.java
Normal file
137
jdk/test/java/lang/StackWalker/StackRecorderUtil.java
Normal 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 + ")");
|
||||
}
|
||||
}
|
||||
}
|
74
jdk/test/java/lang/StackWalker/StackStreamState.java
Normal file
74
jdk/test/java/lang/StackWalker/StackStreamState.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
290
jdk/test/java/lang/StackWalker/StackStreamTest.java
Normal file
290
jdk/test/java/lang/StackWalker/StackStreamTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
355
jdk/test/java/lang/StackWalker/StackWalkTest.java
Normal file
355
jdk/test/java/lang/StackWalker/StackWalkTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
284
jdk/test/java/lang/StackWalker/VerifyStackTrace.java
Normal file
284
jdk/test/java/lang/StackWalker/VerifyStackTrace.java
Normal 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
89
jdk/test/java/lang/StackWalker/WalkFunction.java
Normal file
89
jdk/test/java/lang/StackWalker/WalkFunction.java
Normal 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;
|
||||
}
|
||||
}
|
5
jdk/test/java/lang/StackWalker/noperms.policy
Normal file
5
jdk/test/java/lang/StackWalker/noperms.policy
Normal file
@ -0,0 +1,5 @@
|
||||
/*
|
||||
* grant nothing
|
||||
*/
|
||||
grant {};
|
||||
|
4
jdk/test/java/lang/StackWalker/stackwalk.policy
Normal file
4
jdk/test/java/lang/StackWalker/stackwalk.policy
Normal file
@ -0,0 +1,4 @@
|
||||
grant {
|
||||
permission java.lang.StackFramePermission "retainClassReference";
|
||||
};
|
||||
|
5
jdk/test/java/lang/StackWalker/stackwalktest.policy
Normal file
5
jdk/test/java/lang/StackWalker/stackwalktest.policy
Normal file
@ -0,0 +1,5 @@
|
||||
grant {
|
||||
permission java.lang.StackFramePermission "retainClassReference";
|
||||
permission java.util.PropertyPermission "seed", "read";
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user