Path: blob/master/jcl/src/java.base/share/classes/java/lang/StackWalker.java
12513 views
/*[INCLUDE-IF JAVA_SPEC_VERSION > 8]*/1/*******************************************************************************2* Copyright (c) 2016, 2022 IBM Corp. and others3*4* This program and the accompanying materials are made available under5* the terms of the Eclipse Public License 2.0 which accompanies this6* distribution and is available at https://www.eclipse.org/legal/epl-2.0/7* or the Apache License, Version 2.0 which accompanies this distribution and8* is available at https://www.apache.org/licenses/LICENSE-2.0.9*10* This Source Code may also be made available under the following11* Secondary Licenses when the conditions for such availability set12* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU13* General Public License, version 2 with the GNU Classpath14* Exception [1] and GNU General Public License, version 2 with the15* OpenJDK Assembly Exception [2].16*17* [1] https://www.gnu.org/software/classpath/license.html18* [2] http://openjdk.java.net/legal/assembly-exception.html19*20* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception21*******************************************************************************/22package java.lang;2324import java.lang.StackWalker.StackFrameImpl;25/*[IF JAVA_SPEC_VERSION >= 10]*/26import java.lang.invoke.MethodType;27/*[ENDIF] JAVA_SPEC_VERSION >= 10 */28import java.lang.module.ModuleDescriptor;29import java.lang.module.ModuleDescriptor.Version;30import java.security.Permission;31import java.util.Collections;32import java.util.HashSet;33import java.util.List;34import java.util.Objects;35import java.util.Optional;36import java.util.Set;37import java.util.function.Consumer;38import java.util.function.Function;39import java.util.stream.Collectors;40import java.util.stream.Stream;4142/**43* This provides a facility for iterating over the call stack of the current44* thread. A StackWalker object may be used multiple times by different threads,45* but it will represent the state of the current thread at the time the stack46* is walked. A StackWalker may be provided with one or more Option settings to47* include information and stack frames such as reflection methods, hidden48* frames, and Class objects.49*/50public final class StackWalker {5152private static final int DEFAULT_BUFFER_SIZE = 1;53private final static int J9_RETAIN_CLASS_REFERENCE = 1;54private final static int J9_SHOW_REFLECT_FRAMES = 2;55private final static int J9_SHOW_HIDDEN_FRAMES = 4;5657final Set<Option> walkerOptions;5859private final int bufferSize;60private int flags;6162private StackWalker(Set<Option> options, int estimatedDepth) {63super();64/* Caller is responsible for copying the client set (if any) */65walkerOptions = options;66bufferSize = estimatedDepth;67if (estimatedDepth <= 0) {68/*[MSG "K0641", "estimatedDepth must be greater than 0"]*/69throw new IllegalArgumentException(com.ibm.oti.util.Msg.getString("K0641")); //$NON-NLS-1$70}71if (options.contains(Option.RETAIN_CLASS_REFERENCE)) {72@SuppressWarnings("removal")73SecurityManager securityMgr = System.getSecurityManager();74if (null != securityMgr) {75securityMgr.checkPermission(PermissionSingleton.perm);76}77}78flags = 0;79if (walkerOptions.contains(Option.RETAIN_CLASS_REFERENCE)) {80flags |= J9_RETAIN_CLASS_REFERENCE;81}82if (walkerOptions.contains(Option.SHOW_REFLECT_FRAMES)) {83flags |= J9_SHOW_REFLECT_FRAMES;84}85if (walkerOptions.contains(Option.SHOW_HIDDEN_FRAMES)) {86flags |= J9_SHOW_HIDDEN_FRAMES;87}88}8990/**91* Factory method to create a StackWalker instance with no options set.92*93* @return StackWalker StackWalker object94*/95public static StackWalker getInstance() {96return getInstance(Collections.emptySet(), DEFAULT_BUFFER_SIZE);97}9899/**100* Factory method to create a StackWalker with one option. This is provided101* for the case where only a single option is required.102*103* @param option104* select the type of information to include105* @return StackWalker instance configured with the value of option106* @throws SecurityException107* if option is RETAIN_CLASS_REFERENCE and the security manager108* check fails.109*/110public static StackWalker getInstance(Option option) {111Objects.requireNonNull(option);112return getInstance(Collections.singleton(option), DEFAULT_BUFFER_SIZE);113}114115/**116* Factory method to create a StackWalker with any number of options.117*118* @param options119* select the types of information to include.120* @return StackWalker instance configured with the given options121* @throws SecurityException122* if RETAIN_CLASS_REFERENCE is requested and not permitted by123* the security manager124*/125public static StackWalker getInstance(Set<Option> options) {126return getInstance(new HashSet<>(options), DEFAULT_BUFFER_SIZE);127}128129/**130* Factory method to create a StackWalker.131*132* @param options133* select the types of information to include.134* @param estimatedDepth135* Hint for the size of buffer to use. Must be 1 or greater.136* @return StackWalker instance with the given options specifying the stack137* frame information it can access.138* @throws SecurityException139* if RETAIN_CLASS_REFERENCE is requested and not permitted by140* the security manager141*/142public static StackWalker getInstance(Set<Option> options, int estimatedDepth) {143return new StackWalker(new HashSet<>(options), estimatedDepth);144}145146/**147* @param action148* {@link Consumer} object Iterate over the stack from top to149* bottom and apply the {@link Consumer} to each150* {@link StackFrame}151*/152public void forEach(Consumer<? super StackFrame> action) {153walkWrapperImpl(flags, "forEach", s -> { //$NON-NLS-1$154s.forEach(action);155return null;156});157}158159/**160* Get the caller of the caller of this function, eliding any reflection or161* hidden frames.162*163* @return Class object for the method calling the current method.164* @throws UnsupportedOperationException165* if the StackWalker was not created with166* {@link Option#RETAIN_CLASS_REFERENCE}167* @throws IllegalStateException168* if the caller is at the bottom of the stack.169*/170public Class<?> getCallerClass() {171if (!walkerOptions.contains(Option.RETAIN_CLASS_REFERENCE)) {172/*[MSG "K0639", "Stack walker not configured with RETAIN_CLASS_REFERENCE"]*/173throw new UnsupportedOperationException(com.ibm.oti.util.Msg.getString("K0639")); //$NON-NLS-1$174}175/*176* Get the top two stack frames: the client calling getCallerClass and177* the client's caller. Ignore reflection and special frames.178*/179List<StackFrame> result = StackWalker.walkWrapperImpl(J9_RETAIN_CLASS_REFERENCE, "getCallerClass", //$NON-NLS-1$180s -> s.limit(2).collect(Collectors.toList()));181if (result.size() < 2) {182/*[MSG "K0640", "getCallerClass() called from method with no caller"]*/183throw new IllegalCallerException(com.ibm.oti.util.Msg.getString("K0640")); //$NON-NLS-1$184}185if (((StackFrameImpl)result.get(0)).callerSensitive) {186/*[MSG "K0644", "Caller-sensitive method called StackWalker.getCallerClass()"]*/187throw new UnsupportedOperationException(com.ibm.oti.util.Msg.getString("K0644")); //$NON-NLS-1$188}189StackFrame clientsCaller = result.get(1);190191return clientsCaller.getDeclaringClass();192}193194private native static <T> T walkWrapperImpl(int flags, String walkerMethod,195Function<? super Stream<StackFrame>, ? extends T> function);196197/**198* Traverse the calling thread's stack at the time this method is called and199* apply {@code function} to each stack frame.200*201* @param <T> the type of the return value from applying function to the stream202* @param function operation to apply to the stream203* @param walkState Pointer to a J9StackWalkState struct204* @return the value returned by {@code function}.205*/206private static <T> T walkImpl(Function<? super Stream<StackFrame>, ? extends T> function, long walkState) {207T result;208try (Stream<StackFrame> frameStream = Stream.iterate(getImpl(walkState), x -> (null != x), x -> getImpl(walkState))) {209result = function.apply(frameStream);210}211return result;212}213214private static native StackFrameImpl getImpl(long walkState);215216/**217* Traverse the calling thread's stack at the time this method is called and218* apply {@code function} to each stack frame.219*220* @param <T> the type of the return value from applying function to the stream221* @param function operation to apply to the stream222* @return the value returned by {@code function}.223*/224public <T> T walk(Function<? super Stream<StackFrame>, ? extends T> function) {225return walkWrapperImpl(flags, "walk", function); //$NON-NLS-1$226}227228/**229* Selects what type of stack and method information is provided by the230* StackWalker231*232*/233public static enum Option {234/**235* Allow clients to obtain a method's Class object.236*/237RETAIN_CLASS_REFERENCE,238/**239* Include stack frames for reflection methods.240*/241SHOW_REFLECT_FRAMES,242/**243* Include stack frames for reflection methods, as well as JVM special244* stack frames, such as frames for anonymous classes.245*/246SHOW_HIDDEN_FRAMES;247}248249/**250* Contains information about the StackWalker's current stack frame.251*/252public static interface StackFrame {253254/**255* @return the offset of the current bytecode in the method represented256* by this frame.257*/258int getByteCodeIndex();259260/**261* @return the binary name of the declaring class of this frame's262* method.263*/264String getClassName();265266/**267* @return the Class object of the declaring class of this frame's268* method.269* @throws UnsupportedOperationException270* if the StackWalker was not created with271* Option.RETAIN_CLASS_REFERENCE272*/273Class<?> getDeclaringClass();274275/**276* @return File name of the class containing the current method. May be277* null.278*/279String getFileName();280281/**282* @return Location of the current point of execution in the source283* file, or a negative number if this information is unavailable284* or the method is native.285*/286int getLineNumber();287288/**289* @return the name of this StackFrame's method290*/291String getMethodName();292293/**294* @return true if the method represented by this StackFrame is a native295* method296*/297boolean isNativeMethod();298299/**300* Converts this StackFrame into a StackTraceElement.301*302* @return StackTraceElement303*/304StackTraceElement toStackTraceElement();305306/*[IF JAVA_SPEC_VERSION >= 10]*/307/**308* @throws UnsupportedOperationException if this method is not overridden309* @return MethodType containing the parameter and return types for the associated method.310* @since 10311*/312default MethodType getMethodType() {313throw new UnsupportedOperationException();314}315316/**317* @throws UnsupportedOperationException if this method is not overridden or the StackWalker318* instance is not configured with RETAIN_CLASS_REFERENCE.319* @return method descriptor string representing the type of this frame's method.320* @since 10321*/322default String getDescriptor() {323throw new UnsupportedOperationException();324}325326/*[ENDIF] JAVA_SPEC_VERSION >= 10 */327}328329final static class StackFrameImpl implements StackFrame {330331private Class<?> declaringClass;332private String fileName;333private int bytecodeIndex;334private String classLoaderName;335private String className;336private int lineNumber;337private Module frameModule;338private String methodName;339private String methodSignature;340boolean callerSensitive;341342@Override343public int getByteCodeIndex() {344return bytecodeIndex;345}346347@Override348public String getClassName() {349return className;350}351352@Override353public Class<?> getDeclaringClass() {354if (null == declaringClass) {355/*[MSG "K0639","Stack walker not configured with RETAIN_CLASS_REFERENCE"]*/356throw new UnsupportedOperationException(com.ibm.oti.util.Msg.getString("K0639")); //$NON-NLS-1$357} else {358return declaringClass;359}360}361362@Override363public String getFileName() {364return fileName;365}366367@Override368public int getLineNumber() {369return lineNumber;370}371372@Override373public String getMethodName() {374return methodName;375}376377@Override378public boolean isNativeMethod() {379return -2 == lineNumber;380}381382@Override383public StackTraceElement toStackTraceElement() {384String moduleName = null;385String moduleVersion = null;386if (null != frameModule && frameModule.isNamed()) {387ModuleDescriptor desc = frameModule.getDescriptor();388moduleName = desc.name();389Optional<Version> versionInfo = desc.version();390if (versionInfo.isPresent()) {391moduleVersion = versionInfo.get().toString();392}393}394395StackTraceElement element = new StackTraceElement(classLoaderName, moduleName, moduleVersion, className, methodName, fileName,396lineNumber);397398/**399* Disable including classloader name and module version in stack trace output400* until StackWalker StackTraceElement include info flags can be set properly.401*402* See: https://github.com/eclipse-openj9/openj9/issues/11774403*/404element.disableIncludeInfoFlags();405406return element;407}408409@Override410public String toString() {411StackTraceElement stackTraceElement = toStackTraceElement();412return stackTraceElement.toString();413}414415/*[IF JAVA_SPEC_VERSION >= 10]*/416/**417* Creates a MethodType object for the method associated with this frame.418* @throws UnsupportedOperationException if the StackWalker object is not configured with RETAIN_CLASS_REFERENCE419* @return MethodType object420* @since 10421*/422@Override423public MethodType getMethodType() {424if (null == declaringClass) {425/*[MSG "K0639","Stack walker not configured with RETAIN_CLASS_REFERENCE"]*/426throw new UnsupportedOperationException(com.ibm.oti.util.Msg.getString("K0639")); //$NON-NLS-1$427}428return MethodType.fromMethodDescriptorString(methodSignature, declaringClass.internalGetClassLoader());429}430431/**432* Creates a string containing the signature of the method associated with this frame.433* @return String signature434* @since 10435*/436@Override437public java.lang.String getDescriptor() {438return methodSignature;439}440/*[ENDIF] JAVA_SPEC_VERSION >= 10 */441442}443444static class PermissionSingleton {445static final Permission perm =446new RuntimePermission("getStackWalkerWithClassReference"); //$NON-NLS-1$447}448}449450451