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 jdk.internal.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 (anchor != 0) {
 229                         throw new IllegalStateException("This stack stream is being reused.");
 230                     }
 231                     break;
 232                 case OPEN:
 233                     if (anchor == 0 || anchor == -1L) {
 234                         throw new IllegalStateException("This stack stream is not valid for walking.");
 235                     }
 236                     break;
 237                 case CLOSED:
 238                     if (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 }