jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java
changeset 34362 3396ae214e7d
child 34373 a551d53250dd
equal deleted inserted replaced
33875:c1c71107d45f 34362:3396ae214e7d
       
     1 /*
       
     2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package java.lang;
       
    26 
       
    27 import sun.misc.VM;
       
    28 
       
    29 import java.io.PrintStream;
       
    30 import java.lang.StackWalker.Option;
       
    31 import java.lang.StackWalker.StackFrame;
       
    32 
       
    33 import java.lang.annotation.Native;
       
    34 import java.lang.reflect.Method;
       
    35 import java.security.AccessController;
       
    36 import java.security.PrivilegedAction;
       
    37 import java.util.Arrays;
       
    38 import java.util.EnumSet;
       
    39 import java.util.HashSet;
       
    40 import java.util.NoSuchElementException;
       
    41 import java.util.Objects;
       
    42 import java.util.Optional;
       
    43 import java.util.Set;
       
    44 import java.util.Spliterator;
       
    45 import java.util.function.Consumer;
       
    46 import java.util.function.Function;
       
    47 import java.util.stream.Stream;
       
    48 import java.util.stream.StreamSupport;
       
    49 
       
    50 import static java.lang.StackStreamFactory.WalkerState.*;
       
    51 
       
    52 /**
       
    53  * StackStreamFactory class provides static factory methods
       
    54  * to get different kinds of stack walker/traverser.
       
    55  *
       
    56  * AbstractStackWalker provides the basic stack walking support
       
    57  * fetching stack frames from VM in batches.
       
    58  *
       
    59  * AbstractStackWalker subclass is specialized for a specific kind of stack traversal
       
    60  * to avoid overhead of Stream/Lambda
       
    61  * 1. Support traversing Stream<StackFrame>
       
    62  * 2. StackWalker::getCallerClass
       
    63  * 3. Throwable::init and Throwable::getStackTrace
       
    64  * 4. AccessControlContext getting ProtectionDomain
       
    65  */
       
    66 final class StackStreamFactory {
       
    67     private StackStreamFactory() {}
       
    68 
       
    69     // Stack walk implementation classes to be excluded during stack walking
       
    70     // lazily add subclasses when they are loaded.
       
    71     private final static Set<Class<?>> stackWalkImplClasses = init();
       
    72 
       
    73     private static final int SMALL_BATCH       = 8;
       
    74     private static final int BATCH_SIZE        = 32;
       
    75     private static final int LARGE_BATCH_SIZE  = 256;
       
    76     private static final int MIN_BATCH_SIZE    = SMALL_BATCH;
       
    77 
       
    78     // These flags must match the values maintained in the VM
       
    79     @Native private static final int DEFAULT_MODE              = 0x0;
       
    80     @Native private static final int FILL_CLASS_REFS_ONLY      = 0x2;
       
    81     @Native private static final int FILTER_FILL_IN_STACKTRACE = 0x10;
       
    82     @Native private static final int SHOW_HIDDEN_FRAMES        = 0x20;  // LambdaForms are hidden by the VM
       
    83     @Native private static final int FILL_LIVE_STACK_FRAMES    = 0x100;
       
    84 
       
    85     /*
       
    86      * For Throwable to use StackWalker, set useNewThrowable to true.
       
    87      * Performance work and extensive testing is needed to replace the
       
    88      * VM built-in backtrace filled in Throwable with the StackWalker.
       
    89      */
       
    90     final static boolean useNewThrowable = getProperty("stackwalk.newThrowable", false);
       
    91     final static boolean isDebug = getProperty("stackwalk.debug", false);
       
    92 
       
    93     static <T> StackFrameTraverser<T>
       
    94         makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function)
       
    95     {
       
    96         if (walker.hasLocalsOperandsOption())
       
    97             return new LiveStackInfoTraverser<T>(walker, function);
       
    98         else
       
    99             return new StackFrameTraverser<T>(walker, function);
       
   100     }
       
   101 
       
   102     /**
       
   103      * Gets a stack stream to find caller class.
       
   104      */
       
   105     static CallerClassFinder makeCallerFinder(StackWalker walker) {
       
   106         return new CallerClassFinder(walker);
       
   107     }
       
   108 
       
   109     static boolean useStackTrace(Throwable t) {
       
   110         if (t instanceof VirtualMachineError)
       
   111             return false;
       
   112 
       
   113         return VM.isBooted() && StackStreamFactory.useNewThrowable;
       
   114     }
       
   115 
       
   116     /*
       
   117      * This should only be used by Throwable::<init>.
       
   118      */
       
   119     static StackTrace makeStackTrace(Throwable ex) {
       
   120         return StackTrace.dump(ex);
       
   121     }
       
   122 
       
   123     /*
       
   124      * This creates StackTrace for Thread::dumpThread to use.
       
   125      */
       
   126     static StackTrace makeStackTrace() {
       
   127         return StackTrace.dump();
       
   128     }
       
   129 
       
   130     enum WalkerState {
       
   131         NEW,     // the stream is new and stack walking has not started
       
   132         OPEN,    // the stream is open when it is being traversed.
       
   133         CLOSED;  // the stream is closed when the stack walking is done
       
   134     }
       
   135 
       
   136     static abstract class AbstractStackWalker<T> {
       
   137         protected final StackWalker walker;
       
   138         protected final Thread thread;
       
   139         protected final int maxDepth;
       
   140         protected final long mode;
       
   141         protected int depth;                 // traversed stack depth
       
   142         protected FrameBuffer frameBuffer;   // buffer for VM to fill in
       
   143         protected long anchor;
       
   144 
       
   145         // buffers to fill in stack frame information
       
   146         protected AbstractStackWalker(StackWalker walker, int mode) {
       
   147             this(walker, mode, Integer.MAX_VALUE);
       
   148         }
       
   149         protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) {
       
   150             this.thread = Thread.currentThread();
       
   151             this.mode = toStackWalkMode(walker, mode);
       
   152             this.walker = walker;
       
   153             this.maxDepth = maxDepth;
       
   154             this.depth = 0;
       
   155         }
       
   156 
       
   157         private int toStackWalkMode(StackWalker walker, int mode) {
       
   158             int newMode = mode;
       
   159             if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES) &&
       
   160                     !fillCallerClassOnly(newMode) /* don't show hidden frames for getCallerClass */)
       
   161                 newMode |= SHOW_HIDDEN_FRAMES;
       
   162             if (walker.hasLocalsOperandsOption())
       
   163                 newMode |= FILL_LIVE_STACK_FRAMES;
       
   164             return newMode;
       
   165         }
       
   166 
       
   167         private boolean fillCallerClassOnly(int mode) {
       
   168             return (mode|FILL_CLASS_REFS_ONLY) != FILL_CLASS_REFS_ONLY;
       
   169         }
       
   170         /**
       
   171          * A callback method to consume the stack frames.  This method is invoked
       
   172          * once stack walking begins (i.e. it is only invoked when walkFrames is called).
       
   173          *
       
   174          * Each specialized AbstractStackWalker subclass implements the consumeFrames method
       
   175          * to control the following:
       
   176          * 1. fetch the subsequent batches of stack frames
       
   177          * 2. reuse or expand the allocated buffers
       
   178          * 3. create specialized StackFrame objects
       
   179          *
       
   180          * @return the number of consumed frames
       
   181          */
       
   182          protected abstract T consumeFrames();
       
   183 
       
   184         /**
       
   185          * Initialize FrameBuffer.  Subclass should implement this method to
       
   186          * create its custom frame buffers.
       
   187          */
       
   188          protected abstract void initFrameBuffer();
       
   189 
       
   190         /**
       
   191          * Returns the suggested next batch size.
       
   192          *
       
   193          * Subclass should override this method to change the batch size
       
   194          *
       
   195          * @param lastBatchFrameCount number of frames in the last batch; or zero
       
   196          * @return suggested batch size
       
   197          */
       
   198         protected abstract int batchSize(int lastBatchFrameCount);
       
   199 
       
   200         /*
       
   201          * Returns the next batch size, always >= minimum batch size (32)
       
   202          *
       
   203          * Subclass may override this method if the minimum batch size is different.
       
   204          */
       
   205         protected int getNextBatchSize() {
       
   206             int lastBatchSize = depth == 0 ? 0 : frameBuffer.curBatchFrameCount();
       
   207             int nextBatchSize = batchSize(lastBatchSize);
       
   208             if (isDebug) {
       
   209                 System.err.println("last batch size = " + lastBatchSize +
       
   210                                    " next batch size = " + nextBatchSize);
       
   211             }
       
   212             return nextBatchSize >= MIN_BATCH_SIZE ? nextBatchSize : MIN_BATCH_SIZE;
       
   213         }
       
   214 
       
   215         /*
       
   216          * Checks if this stream is in the given state. Otherwise, throws IllegalStateException.
       
   217          *
       
   218          * VM also validates this stream if it's anchored for stack walking
       
   219          * when stack frames are fetched for each batch.
       
   220          */
       
   221         final void checkState(WalkerState state) {
       
   222             if (thread != Thread.currentThread()) {
       
   223                 throw new IllegalStateException("Invalid thread walking this stack stream: " +
       
   224                         Thread.currentThread().getName() + " " + thread.getName());
       
   225             }
       
   226             switch (state) {
       
   227                 case NEW:
       
   228                     if (this.anchor != 0) {
       
   229                         throw new IllegalStateException("This stack stream is being reused.");
       
   230                     }
       
   231                     break;
       
   232                 case OPEN:
       
   233                     if (this.anchor <= 0) {
       
   234                         throw new IllegalStateException("This stack stream is not valid for walking");
       
   235                     }
       
   236                     break;
       
   237                 case CLOSED:
       
   238                     if (this.anchor != -1L) {
       
   239                         throw new IllegalStateException("This stack stream is not closed.");
       
   240                     }
       
   241             }
       
   242         }
       
   243 
       
   244         /*
       
   245          * Close this stream.  This stream becomes invalid to walk.
       
   246          */
       
   247         private void close() {
       
   248             this.anchor = -1L;
       
   249         }
       
   250 
       
   251         /*
       
   252          * Walks stack frames until {@link #consumeFrames} is done consuming
       
   253          * the frames it is interested in.
       
   254          */
       
   255         final T walk() {
       
   256             checkState(NEW);
       
   257             try {
       
   258                 // VM will need to stablize the stack before walking.  It will invoke
       
   259                 // the AbstractStackWalker::doStackWalk method once it fetches the first batch.
       
   260                 // the callback will be invoked within the scope of the callStackWalk frame.
       
   261                 return beginStackWalk();
       
   262             } finally {
       
   263                 close();  // done traversal; close the stream
       
   264             }
       
   265         }
       
   266 
       
   267         private boolean skipReflectionFrames() {
       
   268             return !walker.hasOption(Option.SHOW_REFLECT_FRAMES) &&
       
   269                        !walker.hasOption(Option.SHOW_HIDDEN_FRAMES);
       
   270         }
       
   271 
       
   272         /*
       
   273          * Returns {@code Class} object at the current frame;
       
   274          * or {@code null} if no more frame. If advanceToNextBatch is true,
       
   275          * it will only fetch the next batch.
       
   276          */
       
   277         final Class<?> peekFrame() {
       
   278             while (frameBuffer.isActive() && depth < maxDepth) {
       
   279                 if (frameBuffer.isEmpty()) {
       
   280                     // fetch another batch of stack frames
       
   281                     getNextBatch();
       
   282                 } else {
       
   283                     Class<?> c = frameBuffer.get();
       
   284                     if (skipReflectionFrames() && isReflectionFrame(c)) {
       
   285                         if (isDebug)
       
   286                             System.err.println("  skip: frame " + frameBuffer.getIndex() + " " + c);
       
   287 
       
   288                         frameBuffer.next();
       
   289                         depth++;
       
   290                         continue;
       
   291                     } else {
       
   292                         return c;
       
   293                     }
       
   294                 }
       
   295             }
       
   296             return null;
       
   297         }
       
   298 
       
   299         /*
       
   300          * This method is only invoked by VM.
       
   301          *
       
   302          * It will invoke the consumeFrames method to start the stack walking
       
   303          * with the first batch of stack frames.  Each specialized AbstractStackWalker
       
   304          * subclass implements the consumeFrames method to control the following:
       
   305          * 1. fetch the subsequent batches of stack frames
       
   306          * 2. reuse or expand the allocated buffers
       
   307          * 3. create specialized StackFrame objects
       
   308          */
       
   309         private Object doStackWalk(long anchor, int skipFrames, int batchSize,
       
   310                                                 int bufStartIndex, int bufEndIndex) {
       
   311             checkState(NEW);
       
   312 
       
   313             frameBuffer.check(skipFrames);
       
   314 
       
   315             if (isDebug) {
       
   316                 System.err.format("doStackWalk: skip %d start %d end %d%n",
       
   317                         skipFrames, bufStartIndex, bufEndIndex);
       
   318             }
       
   319 
       
   320             this.anchor = anchor;  // set anchor for this bulk stack frame traversal
       
   321             frameBuffer.setBatch(bufStartIndex, bufEndIndex);
       
   322 
       
   323             // traverse all frames and perform the action on the stack frames, if specified
       
   324             return consumeFrames();
       
   325         }
       
   326 
       
   327         /*
       
   328          * Get next batch of stack frames.
       
   329          */
       
   330         private int getNextBatch() {
       
   331             int nextBatchSize = Math.min(maxDepth - depth, getNextBatchSize());
       
   332             if (!frameBuffer.isActive() || nextBatchSize <= 0) {
       
   333                 if (isDebug) {
       
   334                     System.out.format("  more stack walk done%n");
       
   335                 }
       
   336                 frameBuffer.freeze();   // stack walk done
       
   337                 return 0;
       
   338             }
       
   339 
       
   340             return fetchStackFrames(nextBatchSize);
       
   341         }
       
   342 
       
   343         /*
       
   344          * This method traverses the next stack frame and returns the Class
       
   345          * invoking that stack frame.
       
   346          *
       
   347          * This method can only be called during the walk method.  This is intended
       
   348          * to be used to walk the stack frames in one single invocation and
       
   349          * this stack stream will be invalidated once walk is done.
       
   350          *
       
   351          * @see #tryNextFrame
       
   352          */
       
   353         final Class<?> nextFrame() {
       
   354             if (!hasNext()) {
       
   355                 return null;
       
   356             }
       
   357 
       
   358             Class<?> c = frameBuffer.next();
       
   359             depth++;
       
   360             return c;
       
   361         }
       
   362 
       
   363         /*
       
   364          * Returns true if there is next frame to be traversed.
       
   365          * This skips hidden frames unless this StackWalker has
       
   366          * {@link Option#SHOW_REFLECT_FRAMES}
       
   367          */
       
   368         final boolean hasNext() {
       
   369             return peekFrame() != null;
       
   370         }
       
   371 
       
   372         /**
       
   373          * Begin stack walking - pass the allocated arrays to the VM to fill in
       
   374          * stack frame information.
       
   375          *
       
   376          * VM first anchors the frame of the current thread.  A traversable stream
       
   377          * on this thread's stack will be opened.  The VM will fetch the first batch
       
   378          * of stack frames and call AbstractStackWalker::doStackWalk to invoke the
       
   379          * stack walking function on each stack frame.
       
   380          *
       
   381          * If all fetched stack frames are traversed, AbstractStackWalker::fetchStackFrames will
       
   382          * fetch the next batch of stack frames to continue.
       
   383          */
       
   384         private T beginStackWalk() {
       
   385             // initialize buffers for VM to fill the stack frame info
       
   386             initFrameBuffer();
       
   387 
       
   388             return callStackWalk(mode, 0,
       
   389                                  frameBuffer.curBatchFrameCount(),
       
   390                                  frameBuffer.startIndex(),
       
   391                                  frameBuffer.classes,
       
   392                                  frameBuffer.stackFrames);
       
   393         }
       
   394 
       
   395         /*
       
   396          * Fetches stack frames.
       
   397          *
       
   398          * @params batchSize number of elements of the frame  buffers for this batch
       
   399          * @returns number of frames fetched in this batch
       
   400          */
       
   401         private int fetchStackFrames(int batchSize) {
       
   402             int startIndex = frameBuffer.startIndex();
       
   403             frameBuffer.resize(startIndex, batchSize);
       
   404 
       
   405             int endIndex = fetchStackFrames(mode, anchor, batchSize,
       
   406                                             startIndex,
       
   407                                             frameBuffer.classes,
       
   408                                             frameBuffer.stackFrames);
       
   409             if (isDebug) {
       
   410                 System.out.format("  more stack walk requesting %d got %d to %d frames%n",
       
   411                                   batchSize, frameBuffer.startIndex(), endIndex);
       
   412             }
       
   413             int numFrames = endIndex - startIndex;
       
   414             if (numFrames == 0) {
       
   415                 frameBuffer.freeze(); // done stack walking
       
   416             } else {
       
   417                 frameBuffer.setBatch(startIndex, endIndex);
       
   418             }
       
   419             return numFrames;
       
   420         }
       
   421 
       
   422         /**
       
   423          * Begins stack walking.  This method anchors this frame and invokes
       
   424          * AbstractStackWalker::doStackWalk after fetching the firt batch of stack frames.
       
   425          *
       
   426          * @param mode        mode of stack walking
       
   427          * @param skipframes  number of frames to be skipped before filling the frame buffer.
       
   428          * @param batchSize   the batch size, max. number of elements to be filled in the frame buffers.
       
   429          * @param startIndex  start index of the frame buffers to be filled.
       
   430          * @param classes     Classes buffer of the stack frames
       
   431          * @param frames      StackFrame buffer, or null
       
   432          * @return            Result of AbstractStackWalker::doStackWalk
       
   433          */
       
   434         private native T callStackWalk(long mode, int skipframes,
       
   435                                        int batchSize, int startIndex,
       
   436                                        Class<?>[] classes,
       
   437                                        StackFrame[] frames);
       
   438 
       
   439         /**
       
   440          * Fetch the next batch of stack frames.
       
   441          *
       
   442          * @param mode        mode of stack walking
       
   443          * @param anchor
       
   444          * @param batchSize   the batch size, max. number of elements to be filled in the frame buffers.
       
   445          * @param startIndex  start index of the frame buffers to be filled.
       
   446          * @param classes     Classes buffer of the stack frames
       
   447          * @param frames      StackFrame buffer, or null
       
   448          *
       
   449          * @return the end index to the frame buffers
       
   450          */
       
   451         private native int fetchStackFrames(long mode, long anchor,
       
   452                                             int batchSize, int startIndex,
       
   453                                             Class<?>[] classes,
       
   454                                             StackFrame[] frames);
       
   455 
       
   456 
       
   457         /*
       
   458          * Frame buffer
       
   459          *
       
   460          * Each specialized AbstractStackWalker subclass may subclass the FrameBuffer.
       
   461          */
       
   462         class FrameBuffer {
       
   463             static final int START_POS = 2;     // 0th and 1st elements are reserved
       
   464 
       
   465             // buffers for VM to fill stack frame info
       
   466             int currentBatchSize;    // current batch size
       
   467             Class<?>[] classes;      // caller class for fast path
       
   468 
       
   469             StackFrame[] stackFrames;
       
   470 
       
   471             int origin;         // index to the current traversed stack frame
       
   472             int fence;          // index to the last frame in the current batch
       
   473 
       
   474             FrameBuffer(int initialBatchSize) {
       
   475                 if (initialBatchSize < MIN_BATCH_SIZE) {
       
   476                     throw new IllegalArgumentException(initialBatchSize + " < minimum batch size: " + MIN_BATCH_SIZE);
       
   477                 }
       
   478                 this.origin = START_POS;
       
   479                 this.fence = 0;
       
   480                 this.currentBatchSize = initialBatchSize;
       
   481                 this.classes = new Class<?>[currentBatchSize];
       
   482             }
       
   483 
       
   484             int curBatchFrameCount() {
       
   485                 return currentBatchSize-START_POS;
       
   486             }
       
   487 
       
   488             /*
       
   489              * Tests if this frame buffer is empty.  All frames are fetched.
       
   490              */
       
   491             final boolean isEmpty() {
       
   492                 return origin >= fence || (origin == START_POS && fence == 0);
       
   493             }
       
   494 
       
   495             /*
       
   496              * Freezes this frame buffer.  The stack stream source is done fetching.
       
   497              */
       
   498             final void freeze() {
       
   499                 origin = 0;
       
   500                 fence = 0;
       
   501             }
       
   502 
       
   503             /*
       
   504              * Tests if this frame buffer is active.  It is inactive when
       
   505              * it is done for traversal.  All stack frames have been traversed.
       
   506              */
       
   507             final boolean isActive() {
       
   508                 return origin > 0 && (fence == 0 || origin < fence || fence == currentBatchSize);
       
   509             }
       
   510 
       
   511             /**
       
   512              * Gets the class at the current frame and move to the next frame.
       
   513              */
       
   514             final Class<?> next() {
       
   515                 if (isEmpty()) {
       
   516                     throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
       
   517                 }
       
   518                 Class<?> c = classes[origin++];
       
   519                 if (isDebug) {
       
   520                     int index = origin-1;
       
   521                     System.out.format("  next frame at %d: %s (origin %d fence %d)%n", index,
       
   522                                       Objects.toString(c), index, fence);
       
   523                 }
       
   524                 return c;
       
   525             }
       
   526 
       
   527             /**
       
   528              * Gets the class at the current frame.
       
   529              */
       
   530             final Class<?> get() {
       
   531                 if (isEmpty()) {
       
   532                     throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
       
   533                 }
       
   534                 return classes[origin];
       
   535             }
       
   536 
       
   537             /*
       
   538              * Returns the index of the current frame.
       
   539              */
       
   540             final int getIndex() {
       
   541                 return origin;
       
   542             }
       
   543 
       
   544             /*
       
   545              * Set the start and end index of a new batch of stack frames that have
       
   546              * been filled in this frame buffer.
       
   547              */
       
   548             final void setBatch(int startIndex, int endIndex) {
       
   549                 if (startIndex <= 0 || endIndex <= 0)
       
   550                     throw new IllegalArgumentException("startIndex=" + startIndex + " endIndex=" + endIndex);
       
   551 
       
   552                 this.origin = startIndex;
       
   553                 this.fence = endIndex;
       
   554                 if (depth == 0 && fence > 0) {
       
   555                     // filter the frames due to the stack stream implementation
       
   556                     for (int i = START_POS; i < fence; i++) {
       
   557                         Class<?> c = classes[i];
       
   558                         if (isDebug) System.err.format("  frame %d: %s%n", i, c);
       
   559                         if (filterStackWalkImpl(c)) {
       
   560                             origin++;
       
   561                         } else {
       
   562                             break;
       
   563                         }
       
   564                     }
       
   565                 }
       
   566             }
       
   567 
       
   568             /*
       
   569              * Checks if the origin is the expected start index.
       
   570              */
       
   571             final void check(int skipFrames) {
       
   572                 int index = skipFrames + START_POS;
       
   573                 if (origin != index) {
       
   574                     // stack walk must continue with the previous frame depth
       
   575                     throw new IllegalStateException("origin " + origin + " != " + index);
       
   576                 }
       
   577             }
       
   578 
       
   579             // ------ subclass may override the following methods -------
       
   580             /**
       
   581              * Resizes the buffers for VM to fill in the next batch of stack frames.
       
   582              * The next batch will start at the given startIndex with the maximum number
       
   583              * of elements.
       
   584              *
       
   585              * <p> Subclass may override this method to manage the allocated buffers.
       
   586              *
       
   587              * @param startIndex the start index for the first frame of the next batch to fill in.
       
   588              * @param elements the number of elements for the next batch to fill in.
       
   589              *
       
   590              */
       
   591             void resize(int startIndex, int elements) {
       
   592                 if (!isActive())
       
   593                     throw new IllegalStateException("inactive frame buffer can't be resized");
       
   594 
       
   595                 int size = startIndex+elements;
       
   596                 if (classes.length < size) {
       
   597                     // copy the elements in classes array to the newly allocated one.
       
   598                     // classes[0] is a Thread object
       
   599                     Class<?>[] prev = classes;
       
   600                     classes = new Class<?>[size];
       
   601                     System.arraycopy(prev, 0, classes, 0, START_POS);
       
   602                 }
       
   603                 currentBatchSize = size;
       
   604             }
       
   605 
       
   606             /*
       
   607              * Returns the start index for this frame buffer is refilled.
       
   608              *
       
   609              * This implementation reuses the allocated buffer for the next batch
       
   610              * of stack frames.  For subclass to retain the fetched stack frames,
       
   611              * it should override this method to return the index at which the frame
       
   612              * should be filled in for the next batch.
       
   613              */
       
   614             int startIndex() {
       
   615                 return START_POS;
       
   616             }
       
   617 
       
   618             /**
       
   619              * Returns next StackFrame object in the current batch of stack frames
       
   620              */
       
   621             StackFrame nextStackFrame() {
       
   622                 throw new InternalError("should not reach here");
       
   623             }
       
   624         }
       
   625     }
       
   626 
       
   627     /*
       
   628      * This StackFrameTraverser supports {@link Stream} traversal.
       
   629      *
       
   630      * This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance.
       
   631      */
       
   632     static class StackFrameTraverser<T> extends AbstractStackWalker<T>
       
   633             implements Spliterator<StackFrame>
       
   634     {
       
   635         static {
       
   636             stackWalkImplClasses.add(StackFrameTraverser.class);
       
   637         }
       
   638         private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE;
       
   639         class Buffer extends FrameBuffer {
       
   640             Buffer(int initialBatchSize) {
       
   641                 super(initialBatchSize);
       
   642 
       
   643                 this.stackFrames = new StackFrame[initialBatchSize];
       
   644                 for (int i = START_POS; i < initialBatchSize; i++) {
       
   645                     stackFrames[i] = new StackFrameInfo(walker);
       
   646                 }
       
   647             }
       
   648 
       
   649             @Override
       
   650             void resize(int startIndex, int elements) {
       
   651                 super.resize(startIndex, elements);
       
   652 
       
   653                 int size = startIndex+elements;
       
   654                 if (stackFrames.length < size) {
       
   655                     stackFrames = new StackFrame[size];
       
   656                 }
       
   657                 for (int i = startIndex(); i < size; i++) {
       
   658                     stackFrames[i] = new StackFrameInfo(walker);
       
   659                 }
       
   660             }
       
   661 
       
   662             @Override
       
   663             StackFrame nextStackFrame() {
       
   664                 if (isEmpty()) {
       
   665                     throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
       
   666                 }
       
   667 
       
   668                 StackFrame frame = stackFrames[origin];
       
   669                 origin++;
       
   670                 return frame;
       
   671             }
       
   672         }
       
   673 
       
   674         final Function<? super Stream<StackFrame>, ? extends T> function;  // callback
       
   675 
       
   676         StackFrameTraverser(StackWalker walker,
       
   677                             Function<? super Stream<StackFrame>, ? extends T> function) {
       
   678             this(walker, function, DEFAULT_MODE);
       
   679         }
       
   680         StackFrameTraverser(StackWalker walker,
       
   681                             Function<? super Stream<StackFrame>, ? extends T> function,
       
   682                             int mode) {
       
   683             super(walker, mode);
       
   684             this.function = function;
       
   685         }
       
   686 
       
   687         /**
       
   688          * Returns next StackFrame object in the current batch of stack frames;
       
   689          * or null if no more stack frame.
       
   690          */
       
   691         StackFrame nextStackFrame() {
       
   692             if (!hasNext()) {
       
   693                 return null;
       
   694             }
       
   695 
       
   696             StackFrame frame = frameBuffer.nextStackFrame();
       
   697             depth++;
       
   698             return frame;
       
   699         }
       
   700 
       
   701         @Override
       
   702         protected T consumeFrames() {
       
   703             checkState(OPEN);
       
   704             Stream<StackFrame> stream = StreamSupport.stream(this, false);
       
   705             if (function != null) {
       
   706                 return function.apply(stream);
       
   707             } else
       
   708                 throw new UnsupportedOperationException();
       
   709         }
       
   710 
       
   711         @Override
       
   712         protected void initFrameBuffer() {
       
   713             this.frameBuffer = new Buffer(getNextBatchSize());
       
   714         }
       
   715 
       
   716         @Override
       
   717         protected int batchSize(int lastBatchFrameCount) {
       
   718             if (lastBatchFrameCount == 0) {
       
   719                 // First batch, use estimateDepth if not exceed the large batch size
       
   720                 // and not too small
       
   721                 int initialBatchSize = Math.max(walker.estimateDepth(), SMALL_BATCH);
       
   722                 return Math.min(initialBatchSize, LARGE_BATCH_SIZE);
       
   723             } else {
       
   724                 if (lastBatchFrameCount > BATCH_SIZE) {
       
   725                     return lastBatchFrameCount;
       
   726                 } else {
       
   727                     return Math.min(lastBatchFrameCount*2, BATCH_SIZE);
       
   728                 }
       
   729             }
       
   730         }
       
   731 
       
   732         // ------- Implementation of Spliterator
       
   733 
       
   734         @Override
       
   735         public Spliterator<StackFrame> trySplit() {
       
   736             return null;   // ordered stream and do not allow to split
       
   737         }
       
   738 
       
   739         @Override
       
   740         public long estimateSize() {
       
   741             return maxDepth;
       
   742         }
       
   743 
       
   744         @Override
       
   745         public int characteristics() {
       
   746             return CHARACTERISTICS;
       
   747         }
       
   748 
       
   749         @Override
       
   750         public void forEachRemaining(Consumer<? super StackFrame> action) {
       
   751             checkState(OPEN);
       
   752             for (int n = 0; n < maxDepth; n++) {
       
   753                 StackFrame frame = nextStackFrame();
       
   754                 if (frame == null) break;
       
   755 
       
   756                 action.accept(frame);
       
   757             }
       
   758         }
       
   759 
       
   760         @Override
       
   761         public boolean tryAdvance(Consumer<? super StackFrame> action) {
       
   762             checkState(OPEN);
       
   763 
       
   764             int index = frameBuffer.getIndex();
       
   765             if (hasNext()) {
       
   766                 StackFrame frame = nextStackFrame();
       
   767                 action.accept(frame);
       
   768                 if (isDebug) {
       
   769                     System.err.println("tryAdvance: " + index + " " + frame);
       
   770                 }
       
   771                 return true;
       
   772             }
       
   773             if (isDebug) {
       
   774                 System.err.println("tryAdvance: " + index + " NO element");
       
   775             }
       
   776             return false;
       
   777         }
       
   778     }
       
   779 
       
   780     /*
       
   781      * CallerClassFinder is specialized to return Class<?> for each stack frame.
       
   782      * StackFrame is not requested.
       
   783      */
       
   784     static class CallerClassFinder extends AbstractStackWalker<Integer> {
       
   785         static {
       
   786             stackWalkImplClasses.add(CallerClassFinder.class);
       
   787         }
       
   788 
       
   789         private Class<?> caller;
       
   790 
       
   791         CallerClassFinder(StackWalker walker) {
       
   792             super(walker, FILL_CLASS_REFS_ONLY);
       
   793         }
       
   794 
       
   795         Class<?> findCaller() {
       
   796             walk();
       
   797             return caller;
       
   798         }
       
   799 
       
   800         @Override
       
   801         protected Integer consumeFrames() {
       
   802             checkState(OPEN);
       
   803             int n = 0;
       
   804             Class<?>[] frames = new Class<?>[2];
       
   805             // skip the API calling this getCallerClass method
       
   806             // 0: StackWalker::getCallerClass
       
   807             // 1: caller-sensitive method
       
   808             // 2: caller class
       
   809             while (n < 2 && (caller = nextFrame()) != null) {
       
   810                 if (isMethodHandleFrame(caller)) continue;
       
   811                 frames[n++] = caller;
       
   812             }
       
   813 
       
   814             if (frames[1] == null)
       
   815                 throw new IllegalStateException("no caller frame");
       
   816             return n;
       
   817         }
       
   818 
       
   819         @Override
       
   820         protected void initFrameBuffer() {
       
   821             this.frameBuffer = new FrameBuffer(getNextBatchSize());
       
   822         }
       
   823 
       
   824         @Override
       
   825         protected int batchSize(int lastBatchFrameCount) {
       
   826             return MIN_BATCH_SIZE;
       
   827         }
       
   828 
       
   829         @Override
       
   830         protected int getNextBatchSize() {
       
   831             return MIN_BATCH_SIZE;
       
   832         }
       
   833     }
       
   834 
       
   835     /*
       
   836      * StackTrace caches all frames in the buffer.  StackTraceElements are
       
   837      * created lazily when Throwable::getStackTrace is called.
       
   838      */
       
   839     static class StackTrace extends AbstractStackWalker<Integer> {
       
   840         static {
       
   841             stackWalkImplClasses.add(StackTrace.class);
       
   842         }
       
   843 
       
   844         class GrowableBuffer extends FrameBuffer {
       
   845             GrowableBuffer(int initialBatchSize) {
       
   846                 super(initialBatchSize);
       
   847 
       
   848                 this.stackFrames = new StackFrame[initialBatchSize];
       
   849                 for (int i = START_POS; i < initialBatchSize; i++) {
       
   850                     stackFrames[i] = new StackFrameInfo(walker);
       
   851                 }
       
   852             }
       
   853 
       
   854             /*
       
   855              * Returns the next index to fill
       
   856              */
       
   857             @Override
       
   858             int startIndex() {
       
   859                 return origin;
       
   860             }
       
   861 
       
   862             /**
       
   863              * Initialize the buffers for VM to fill in the stack frame information.
       
   864              * The next batch will start at the given startIndex to
       
   865              * the length of the buffer.
       
   866              */
       
   867             @Override
       
   868             void resize(int startIndex, int elements) {
       
   869                 // Expand the frame buffer.
       
   870                 // Do not call super.resize that will reuse the filled elements
       
   871                 // in this frame buffer
       
   872                 int size = startIndex+elements;
       
   873                 if (classes.length < size) {
       
   874                     // resize the frame buffer
       
   875                     classes = Arrays.copyOf(classes, size);
       
   876                     stackFrames = Arrays.copyOf(stackFrames, size);
       
   877                 }
       
   878                 for (int i = startIndex; i < size; i++) {
       
   879                     stackFrames[i] = new StackFrameInfo(walker);
       
   880                 }
       
   881                 currentBatchSize = size;
       
   882             }
       
   883 
       
   884             StackTraceElement get(int index) {
       
   885                 return new StackTraceElement(classes[index].getName(), "unknown", null, -1);
       
   886             }
       
   887 
       
   888             /**
       
   889              * Returns an array of StackTraceElement for all stack frames cached in
       
   890              * this StackTrace object.
       
   891              * <p>
       
   892              * This method is intended for Throwable::getOurStackTrace use only.
       
   893              */
       
   894             StackTraceElement[] toStackTraceElements() {
       
   895                 int startIndex = START_POS;
       
   896                 for (int i = startIndex; i < classes.length; i++) {
       
   897                     if (classes[i] != null && filterStackWalkImpl(classes[i])) {
       
   898                         startIndex++;
       
   899                     } else {
       
   900                         break;
       
   901                     }
       
   902                 }
       
   903 
       
   904                 // VM fills in the method name, filename, line number info
       
   905                 StackFrameInfo.fillInStackFrames(0, stackFrames, startIndex, startIndex + depth);
       
   906 
       
   907                 StackTraceElement[] stes = new StackTraceElement[depth];
       
   908                 for (int i = startIndex, j = 0; i < classes.length && j < depth; i++, j++) {
       
   909                     if (isDebug) {
       
   910                         System.err.println("StackFrame: " + i + " " + stackFrames[i]);
       
   911                     }
       
   912                     stes[j] = stackFrames[i].toStackTraceElement();
       
   913                 }
       
   914                 return stes;
       
   915             }
       
   916         }
       
   917 
       
   918         private static final int MAX_STACK_FRAMES = 1024;
       
   919         private static final StackWalker STACKTRACE_WALKER =
       
   920             StackWalker.newInstanceNoCheck(EnumSet.of(Option.SHOW_REFLECT_FRAMES));
       
   921 
       
   922         private StackTraceElement[] stes;
       
   923         static StackTrace dump() {
       
   924             return new StackTrace();
       
   925         }
       
   926 
       
   927         static StackTrace dump(Throwable ex) {
       
   928             return new StackTrace(ex);
       
   929         }
       
   930 
       
   931         private StackTrace() {
       
   932             this(STACKTRACE_WALKER, DEFAULT_MODE);
       
   933         }
       
   934 
       
   935         /*
       
   936          * Throwable::fillInStackTrace and <init> of Throwable and subclasses
       
   937          * are filtered in the VM.
       
   938          */
       
   939         private StackTrace(Throwable ex) {
       
   940             this(STACKTRACE_WALKER, FILTER_FILL_IN_STACKTRACE);  // skip Throwable::init frames
       
   941             if (isDebug) {
       
   942                 System.err.println("dump stack for " + ex.getClass().getName());
       
   943             }
       
   944         }
       
   945 
       
   946         StackTrace(StackWalker walker, int mode) {
       
   947             super(walker, mode, MAX_STACK_FRAMES);
       
   948 
       
   949             // snapshot the stack trace
       
   950             walk();
       
   951         }
       
   952 
       
   953         @Override
       
   954         protected Integer consumeFrames() {
       
   955             // traverse all frames and perform the action on the stack frames, if specified
       
   956             int n = 0;
       
   957             while (n < maxDepth && nextFrame() != null) {
       
   958                 n++;
       
   959             }
       
   960             return n;
       
   961         }
       
   962 
       
   963         @Override
       
   964         protected void initFrameBuffer() {
       
   965             this.frameBuffer = new GrowableBuffer(getNextBatchSize());
       
   966         }
       
   967 
       
   968         // TODO: implement better heuristic
       
   969         @Override
       
   970         protected int batchSize(int lastBatchFrameCount) {
       
   971             // chunk size of VM backtrace is 32
       
   972             return lastBatchFrameCount == 0 ? 32 : 32;
       
   973         }
       
   974 
       
   975         /**
       
   976          * Returns an array of StackTraceElement for all stack frames cached in
       
   977          * this StackTrace object.
       
   978          * <p>
       
   979          * This method is intended for Throwable::getOurStackTrace use only.
       
   980          */
       
   981         synchronized StackTraceElement[] getStackTraceElements() {
       
   982             if (stes == null) {
       
   983                 stes = ((GrowableBuffer) frameBuffer).toStackTraceElements();
       
   984                 // release the frameBuffer memory
       
   985                 frameBuffer = null;
       
   986             }
       
   987             return stes;
       
   988         }
       
   989 
       
   990         /*
       
   991          * Prints stack trace to the given PrintStream.
       
   992          *
       
   993          * Further implementation could skip creating StackTraceElement objects
       
   994          * print directly to the PrintStream.
       
   995          */
       
   996         void printStackTrace(PrintStream s) {
       
   997             StackTraceElement[] stes = getStackTraceElements();
       
   998             synchronized (s) {
       
   999                 s.println("Stack trace");
       
  1000                 for (StackTraceElement traceElement : stes)
       
  1001                     s.println("\tat " + traceElement);
       
  1002             }
       
  1003         }
       
  1004     }
       
  1005 
       
  1006     static class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> {
       
  1007         static {
       
  1008             stackWalkImplClasses.add(LiveStackInfoTraverser.class);
       
  1009         }
       
  1010         // VM will fill in all method info and live stack info directly in StackFrameInfo
       
  1011         class Buffer extends FrameBuffer {
       
  1012             Buffer(int initialBatchSize) {
       
  1013                 super(initialBatchSize);
       
  1014                 this.stackFrames = new StackFrame[initialBatchSize];
       
  1015                 for (int i = START_POS; i < initialBatchSize; i++) {
       
  1016                     stackFrames[i] = new LiveStackFrameInfo(walker);
       
  1017                 }
       
  1018             }
       
  1019 
       
  1020             @Override
       
  1021             void resize(int startIndex, int elements) {
       
  1022                 super.resize(startIndex, elements);
       
  1023                 int size = startIndex + elements;
       
  1024 
       
  1025                 if (stackFrames.length < size) {
       
  1026                     this.stackFrames = new StackFrame[size];
       
  1027                 }
       
  1028 
       
  1029                 for (int i = startIndex(); i < size; i++) {
       
  1030                     stackFrames[i] = new LiveStackFrameInfo(walker);
       
  1031                 }
       
  1032             }
       
  1033 
       
  1034             @Override
       
  1035             StackFrame nextStackFrame() {
       
  1036                 if (isEmpty()) {
       
  1037                     throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
       
  1038                 }
       
  1039 
       
  1040                 StackFrame frame = stackFrames[origin];
       
  1041                 origin++;
       
  1042                 return frame;
       
  1043             }
       
  1044         }
       
  1045 
       
  1046         LiveStackInfoTraverser(StackWalker walker,
       
  1047                                Function<? super Stream<StackFrame>, ? extends T> function) {
       
  1048             super(walker, function, DEFAULT_MODE);
       
  1049         }
       
  1050 
       
  1051         @Override
       
  1052         protected void initFrameBuffer() {
       
  1053             this.frameBuffer = new Buffer(getNextBatchSize());
       
  1054         }
       
  1055     }
       
  1056 
       
  1057     private static native boolean checkStackWalkModes();
       
  1058 
       
  1059     // avoid loading other subclasses as they may not be used
       
  1060     private static Set<Class<?>> init() {
       
  1061         if (!checkStackWalkModes()) {
       
  1062             throw new InternalError("StackWalker mode values do not match with JVM");
       
  1063         }
       
  1064 
       
  1065         Set<Class<?>> classes = new HashSet<>();
       
  1066         classes.add(StackWalker.class);
       
  1067         classes.add(StackStreamFactory.class);
       
  1068         classes.add(AbstractStackWalker.class);
       
  1069         return classes;
       
  1070     }
       
  1071 
       
  1072     private static boolean filterStackWalkImpl(Class<?> c) {
       
  1073         return stackWalkImplClasses.contains(c) ||
       
  1074                 c.getName().startsWith("java.util.stream.");
       
  1075     }
       
  1076 
       
  1077     // MethodHandle frames are not hidden and CallerClassFinder has
       
  1078     // to filter them out
       
  1079     private static boolean isMethodHandleFrame(Class<?> c) {
       
  1080         return c.getName().startsWith("java.lang.invoke.");
       
  1081     }
       
  1082 
       
  1083     private static boolean isReflectionFrame(Class<?> c) {
       
  1084         if (c.getName().startsWith("sun.reflect") &&
       
  1085                 !sun.reflect.MethodAccessor.class.isAssignableFrom(c)) {
       
  1086             throw new InternalError("Not sun.reflect.MethodAccessor: " + c.toString());
       
  1087         }
       
  1088         // ## should filter all @Hidden frames?
       
  1089         return c == Method.class ||
       
  1090                 sun.reflect.MethodAccessor.class.isAssignableFrom(c) ||
       
  1091                 c.getName().startsWith("java.lang.invoke.LambdaForm");
       
  1092     }
       
  1093 
       
  1094     private static boolean getProperty(String key, boolean value) {
       
  1095         String s = AccessController.doPrivileged(new PrivilegedAction<>() {
       
  1096             @Override
       
  1097             public String run() {
       
  1098                 return System.getProperty(key);
       
  1099             }
       
  1100         });
       
  1101         if (s != null) {
       
  1102             return Boolean.valueOf(s);
       
  1103         }
       
  1104         return value;
       
  1105     }
       
  1106 }