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.reflect.Method;
  34 import java.security.AccessController;
  35 import java.security.PrivilegedAction;
  36 import java.util.ArrayList;
  37 import java.util.Arrays;
  38 import java.util.EnumSet;
  39 import java.util.List;
  40 import java.util.NoSuchElementException;
  41 import java.util.Objects;
  42 import java.util.Optional;
  43 import java.util.Spliterator;
  44 import java.util.function.Consumer;
  45 import java.util.function.Function;
  46 import java.util.stream.Stream;
  47 import java.util.stream.StreamSupport;
  48 
  49 import static java.lang.StackStreamFactory.WalkerState.*;
  50 
  51 /**
  52  * StackStreamFactory class provides static factory methods
  53  * to get different kinds of stack walker/traverser.
  54  *
  55  * AbstractStackWalker provides the basic stack walking support
  56  * fetching stack frames from VM in batches.
  57  *
  58  * AbstractStackWalker subclass is specialized for a specific kind of stack traversal
  59  * to avoid overhead of Stream/Lambda
  60  * 1. Support traversing Stream<StackFrame>
  61  * 2. StackWalker::getCallerClass
  62  * 3. Throwable::init and Throwable::getStackTrace
  63  * 4. AccessControlContext getting ProtectionDomain
  64  */
  65 final class StackStreamFactory {
  66     private StackStreamFactory() {}
  67 
  68     private static final int SMALL_BATCH       = 8;
  69     private static final int BATCH_SIZE        = 32;
  70     private static final int LARGE_BATCH_SIZE  = 256;
  71     private static final int MIN_BATCH_SIZE    = SMALL_BATCH;
  72 
  73     // These flags must match the values maintained in the VM
  74     private static final int DEFAULT_MODE              = 0x0;
  75     private static final int FILL_CLASS_REFS_ONLY      = 0x2;
  76     private static final int FILTER_FILL_IN_STACKTRACE = 0x10;
  77     private static final int SHOW_HIDDEN_FRAMES        = 0x20;  // LambdaForms are hidden by the VM
  78     private static final int FILL_LOCALS_OPERANDS      = 0x100;
  79 
  80     /*
  81      * For Throwable to use StackWalker, set useNewThrowable to true.
  82      * Performance work and extensive testing is needed to replace the
  83      * VM built-in backtrace filled in Throwable with the StackWalker.
  84      */
  85     final static boolean useNewThrowable = getProperty("stackwalk.newThrowable", false);
  86     final static boolean isDebug = getProperty("stackwalk.debug", false);
  87 
  88     static <T> StackFrameTraverser<T>
  89         makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function)
  90     {
  91         if (walker.hasLocalsOperandsOption())
  92             return new LiveStackInfoTraverser<T>(walker, function);
  93         else
  94             return new StackFrameTraverser<T>(walker, function);
  95 
  96     }
  97 
  98     /**
  99      * Gets a stack stream to find caller class.
 100      */
 101     static CallerClassFinder makeCallerFinder(StackWalker walker) {
 102         return new CallerClassFinder(walker);
 103     }
 104 
 105 
 106     private static final StackWalker STACKTRACE_WALKER =
 107         StackWalker.newInstanceNoCheck(EnumSet.of(Option.SHOW_REFLECT_FRAMES));
 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 new StackTrace(STACKTRACE_WALKER, ex);
 121     }
 122 
 123     /*
 124      * This creates StackTrace for Thread::dumpThread to use.
 125      */
 126     static StackTrace makeStackTrace() {
 127         return new StackTrace(STACKTRACE_WALKER);
 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         // VM writes to these arrays to fill the stack frame information
 138         // for each batch
 139         protected final StackWalker walker;
 140         protected final Thread thread;
 141         protected final int maxDepth;
 142         protected final long mode;
 143         protected int depth;          // traversed stack depth
 144         protected FrameBuffer frameBuffer;
 145         protected long anchor;
 146 
 147         // buffers to fill in stack frame information
 148         protected AbstractStackWalker(StackWalker walker, int mode) {
 149             // TODO: use MAX_VALUE when StackWalker(maxDepth, ..) is gone
 150             // this(walker, mode, Integer.MAX_VALUE);
 151             this(walker, mode, walker.maxDepth());
 152         }
 153         protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) {
 154             this.thread = Thread.currentThread();
 155             this.mode = toStackWalkMode(walker, mode);
 156             this.walker = walker;
 157             this.maxDepth = maxDepth;
 158             this.depth = 0;
 159         }
 160 
 161         private int toStackWalkMode(StackWalker walker, int mode) {
 162             int newMode = mode;
 163             if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES))
 164                 newMode |= SHOW_HIDDEN_FRAMES;
 165             if (walker.hasLocalsOperandsOption())
 166                 newMode |= FILL_LOCALS_OPERANDS;
 167             return newMode;
 168         }
 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          * or invoke the function passed to the StackWalker::walk method
 195          *
 196          * @param lastBatchFrameCount number of frames in the last batch; or zero
 197          * @return suggested batch size
 198          */
 199         protected abstract int batchSize(int lastBatchFrameCount);
 200 
 201         /*
 202          * Returns the next batch size, always >= minimum batch size (32)
 203          *
 204          * Subclass may override this method if the minimum batch size is different.
 205          */
 206         protected int getNextBatchSize() {
 207             int lastBatchSize = depth == 0 ? 0 : frameBuffer.curBatchFrameCount();
 208             int nextBatchSize = batchSize(lastBatchSize);
 209             if (isDebug) {
 210                 System.err.println("last batch size = " + lastBatchSize +
 211                                    " next batch size = " + nextBatchSize);
 212             }
 213             return nextBatchSize >= MIN_BATCH_SIZE ? nextBatchSize : MIN_BATCH_SIZE;
 214         }
 215 
 216         /*
 217          * Checks if this stream is in the given state. Otherwise, throws IllegalStateException.
 218          *
 219          * VM also validates this stream if it's anchored for stack walking
 220          * when stack frames are fetched for each batch.
 221          */
 222         final void checkState(WalkerState state) {
 223             if (thread != Thread.currentThread()) {
 224                 throw new IllegalStateException("Invalid thread walking this stack stream: " +
 225                         Thread.currentThread().getName() + " " + thread.getName());
 226             }
 227             switch (state) {
 228                 case NEW:
 229                     if (this.anchor != 0) {
 230                         throw new IllegalStateException("This stack stream is being reused.");
 231                     }
 232                     break;
 233                 case OPEN:
 234                     if (this.anchor <= 0) {
 235                         throw new IllegalStateException("This stack stream is not valid for walking");
 236                     }
 237                     break;
 238                 case CLOSED:
 239                     if (this.anchor != -1L) {
 240                         throw new IllegalStateException("This stack stream is not closed.");
 241                     }
 242             }
 243         }
 244 
 245         /*
 246          * Close this stream.  This stream becomes invalid to walk.
 247          */
 248         private void close() {
 249             this.anchor = -1L;
 250         }
 251 
 252         /*
 253          * Walks stack frames until {@link #consumeFrames} is done consuming
 254          * the frames it is interested in.
 255          */
 256         final T walk() {
 257             checkState(NEW);
 258             try {
 259                 // VM will need to stablize the stack before walking.  It will invoke
 260                 // the AbstractStackWalker::doStackWalk method once it fetches the first batch.
 261                 // the callback will be invoked within the scope of the callStackWalk frame.
 262                 return beginStackWalk();
 263             } finally {
 264                 close();  // done traversal; close the stream
 265             }
 266         }
 267 
 268         private boolean skipReflectionFrames() {
 269             return !walker.hasOption(Option.SHOW_REFLECT_FRAMES) &&
 270                        !walker.hasOption(Option.SHOW_HIDDEN_FRAMES);
 271         }
 272 
 273         /*
 274          * Returns {@code Class} object at the current frame;
 275          * or {@code null} if no more frame. If advanceToNextBatch is true,
 276          * it will only fetch the next batch.
 277          */
 278         final Class<?> peekFrame(boolean advanceToNextBatch) {
 279             while (frameBuffer.isActive() && depth < maxDepth) {
 280                 if (frameBuffer.isEmpty()) {
 281                     if (advanceToNextBatch)
 282                         getNextBatch();  // fetch another batch of stack frames
 283                     else
 284                         return null;     // stop fetching
 285                 } else {
 286                     Class<?> c = frameBuffer.get();
 287                     if (skipReflectionFrames() && isReflectionFrame(c)) {
 288                         if (isDebug)
 289                             System.err.println("  skip: frame " + frameBuffer.getIndex() + " " + c);
 290 
 291                         frameBuffer.next();
 292                         depth++;
 293                         continue;
 294                     } else {
 295                         return c;
 296                     }
 297                 }
 298             }
 299             return null;
 300         }
 301 
 302         /*
 303          * This method is only invoked by VM.
 304          *
 305          * It will invoke the consumeFrames method to start the stack walking
 306          * with the first batch of stack frames.  Each specialized AbstractStackWalker
 307          * subclass implements the consumeFrames method to control the following:
 308          * 1. fetch the subsequent batches of stack frames
 309          * 2. reuse or expand the allocated buffers
 310          * 3. create specialized StackFrame objects
 311          */
 312         private Object doStackWalk(long anchor, int skipFrames, int batchSize,
 313                                                 int bufStartIndex, int bufEndIndex) {
 314             checkState(NEW);
 315 
 316             frameBuffer.check(skipFrames);
 317 
 318             if (isDebug) {
 319                 System.err.format("doStackWalk: skip %d start %d end %d%n",
 320                         skipFrames, bufStartIndex, bufEndIndex);
 321             }
 322 
 323             this.anchor = anchor;  // set anchor for this bulk stack frame traversal
 324             frameBuffer.setBatch(bufStartIndex, bufEndIndex);
 325 
 326             // traverse all frames and perform the action on the stack frames, if specified
 327             return consumeFrames();
 328         }
 329 
 330         /*
 331          * Get next batch of stack frames.
 332          */
 333         private int getNextBatch() {
 334             int nextBatchSize = Math.min(maxDepth - depth, getNextBatchSize());
 335             if (!frameBuffer.isActive() || nextBatchSize <= 0) {
 336                 if (isDebug) {
 337                     System.out.format("  more stack walk done%n");
 338                 }
 339                 frameBuffer.freeze();   // stack walk done
 340                 return 0;
 341             }
 342 
 343             return fetchStackFrames(nextBatchSize);
 344         }
 345 
 346         /*
 347          * This method traverses the next stack frame and returns the Class
 348          * invoking that stack frame.
 349          *
 350          * This method can only be called during the walk method.  This is intended
 351          * to be used to walk the stack frames in one single invocation and
 352          * this stack stream will be invalidated once walk is done.
 353          *
 354          * @see #tryNextFrame
 355          */
 356         final Class<?> nextFrame() {
 357             if (!hasNext()) {
 358                 return null;
 359             }
 360 
 361             Class<?> c = frameBuffer.next();
 362             depth++;
 363             return c;
 364         }
 365 
 366         /*
 367          * Returns true if there is next frame to be traversed.
 368          * This skips hidden frames unless this StackWalker has
 369          * {@link Option#SHOW_REFLECT_FRAMES}
 370          */
 371         final boolean hasNext() {
 372             return peekFrame(true) != null;
 373         }
 374 
 375         /**
 376          * Begin stack walking - pass the allocated arrays to the VM to fill in
 377          * stack frame information.
 378          *
 379          * VM first anchors the frame of the current thread.  A traversable stream
 380          * on this thread's stack will be opened.  The VM will fetch the first batch
 381          * of stack frames and call AbstractStackWalker::doStackWalk to invoke the
 382          * stack walking function on each stack frame.
 383          *
 384          * If all fetched stack frames are traversed, AbstractStackWalker::fetchStackFrames will
 385          * fetch the next batch of stack frames to continue.
 386          */
 387         private T beginStackWalk() {
 388             // initialize buffers for VM to fill the stack frame info
 389             initFrameBuffer();
 390 
 391             return callStackWalk(mode, 0,
 392                                  frameBuffer.curBatchFrameCount(),
 393                                  frameBuffer.startIndex(),
 394                                  frameBuffer.classes,
 395                                  frameBuffer.stackFrames);
 396         }
 397 
 398         /*
 399          * Fetches stack frames.
 400          *
 401          * @params batchSize number of elements of the frame  buffers for this batch
 402          * @returns number of frames fetched in this batch
 403          */
 404         private int fetchStackFrames(int batchSize) {
 405             int startIndex = frameBuffer.startIndex();
 406             frameBuffer.resize(startIndex, batchSize);
 407 
 408             int endIndex = fetchStackFrames(mode, anchor, batchSize,
 409                                             startIndex,
 410                                             frameBuffer.classes,
 411                                             frameBuffer.stackFrames);
 412             if (isDebug) {
 413                 System.out.format("  more stack walk requesting %d got %d to %d frames%n",
 414                                   batchSize, frameBuffer.startIndex(), endIndex);
 415             }
 416             int numFrames = endIndex - startIndex;
 417             if (numFrames == 0) {
 418                 frameBuffer.freeze(); // done stack walking
 419             } else {
 420                 frameBuffer.setBatch(startIndex, endIndex);
 421             }
 422             return numFrames;
 423         }
 424 
 425         /**
 426          * Begins stack walking.  This method anchors this frame and invokes
 427          * AbstractStackWalker::doStackWalk after fetching the firt batch of stack frames.
 428          *
 429          * @param mode        mode of stack walking
 430          * @param skipframes  number of frames to be skipped before filling the frame buffer.
 431          * @param batchSize   the batch size, max. number of elements to be filled in the frame buffers.
 432          * @param startIndex  start index of the frame buffers to be filled.
 433          * @param classes     Classes buffer of the stack frames
 434          * @param frames      StackFrame buffer, or null
 435          * @return            Result of AbstractStackWalker::doStackWalk
 436          */
 437         private native T callStackWalk(long mode, int skipframes,
 438                                        int batchSize, int startIndex,
 439                                        Class<?>[] classes,
 440                                        StackFrame[] frames);
 441 
 442         /**
 443          * Fetch the next batch of stack frames.
 444          *
 445          * @param mode        mode of stack walking
 446          * @param anchor
 447          * @param batchSize   the batch size, max. number of elements to be filled in the frame buffers.
 448          * @param startIndex  start index of the frame buffers to be filled.
 449          * @param classes     Classes buffer of the stack frames
 450          * @param frames      StackFrame buffer, or null
 451          *
 452          * @return the end index to the frame buffers
 453          */
 454         private native int fetchStackFrames(long mode, long anchor,
 455                                             int batchSize, int startIndex,
 456                                             Class<?>[] classes,
 457                                             StackFrame[] frames);
 458 
 459 
 460         /*
 461          * Frame buffer
 462          *
 463          * Each specialized AbstractStackWalker subclass may subclass the FrameBuffer.
 464          */
 465         class FrameBuffer {
 466             static final int START_POS = 2;     // 0th and 1st elements are reserved
 467 
 468             // buffers for VM to fill stack frame info
 469             int currentBatchSize;    // current batch size
 470             Class<?>[] classes;      // caller class for fast path
 471 
 472             StackFrame[] stackFrames;
 473 
 474             int origin;         // index to the current traversed stack frame
 475             int fence;          // index to the last frame in the current batch
 476 
 477             FrameBuffer(int initialBatchSize) {
 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 are traversal.
 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                 if (origin != skipFrames + START_POS) {
 573                     // stack walk must continue with the previous frame depth
 574                     throw new IllegalStateException("origin " + origin + " != " + skipFrames);
 575                 }
 576             }
 577 
 578             // ------ subclass may override the following methods -------
 579             /**
 580              * Resizes the buffers for VM to fill in the next batch of stack frames.
 581              * The next batch will start at the given startIndex with the maximum number
 582              * of elements.
 583              *
 584              * <p> Subclass may override this method to manage the allocated buffers.
 585              *
 586              * @param startIndex the start index for the first frame of the next batch to fill in.
 587              * @param elements the number of elements for the next batch to fill in.
 588              *
 589              */
 590             void resize(int startIndex, int elements) {
 591                 if (!isActive())
 592                     throw new IllegalStateException("inactive frame buffer can't be resized");
 593 
 594                 int size = startIndex+elements;
 595                 if (classes.length < size) {
 596                     // copy the elements in classes array to the newly allocated one.
 597                     // classes[0] is a Thread object
 598                     Class<?>[] prev = classes;
 599                     classes = new Class<?>[size];
 600                     System.arraycopy(prev, 0, classes, 0, START_POS);
 601                 }
 602                 currentBatchSize = size;
 603             }
 604 
 605             /*
 606              * Returns the start index for this frame buffer is refilled.
 607              *
 608              * This implementation reuses the allocated buffer for the next batch
 609              * of stack frames.  For subclass to retain the fetched stack frames,
 610              * it should override this method to return the index at which the frame
 611              * should be filled in for the next batch.
 612              */
 613             int startIndex() {
 614                 return START_POS;
 615             }
 616 
 617             /**
 618              * Returns next StackFrame object in the current batch of stack frames
 619              */
 620             StackFrame nextStackFrame() {
 621                 throw new InternalError("should not reach here");
 622             }
 623         }
 624     }
 625 
 626     /*
 627      * This StackFrameTraverser supports {@link Stream} traversal.
 628      *
 629      * This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance.
 630      */
 631     static class StackFrameTraverser<T> extends AbstractStackWalker<T>
 632             implements Spliterator<StackFrame>
 633     {
 634         private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE;
 635 
 636         class Buffer extends FrameBuffer {
 637             Buffer(int initialBatchSize) {
 638                 super(initialBatchSize);
 639 
 640                 this.stackFrames = new StackFrame[initialBatchSize];
 641                 for (int i = START_POS; i < initialBatchSize; i++) {
 642                     stackFrames[i] = new StackFrameInfo(walker);
 643                 }
 644             }
 645 
 646             @Override
 647             void resize(int startIndex, int elements) {
 648                 super.resize(startIndex, elements);
 649 
 650                 int size = startIndex+elements;
 651                 if (stackFrames.length < size) {
 652                     stackFrames = new StackFrame[size];
 653                 }
 654                 for (int i = startIndex(); i < size; i++) {
 655                     stackFrames[i] = new StackFrameInfo(walker);
 656                 }
 657             }
 658 
 659             @Override
 660             StackFrame nextStackFrame() {
 661                 if (isEmpty()) {
 662                     throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
 663                 }
 664 
 665                 StackFrame frame = stackFrames[origin];
 666                 origin++;
 667                 return frame;
 668             }
 669         }
 670 
 671         final Function<? super Stream<StackFrame>, ? extends T> function;  // callback
 672 
 673         StackFrameTraverser(StackWalker walker,
 674                             Function<? super Stream<StackFrame>, ? extends T> function) {
 675             this(walker, function, DEFAULT_MODE);
 676         }
 677         StackFrameTraverser(StackWalker walker,
 678                             Function<? super Stream<StackFrame>, ? extends T> function,
 679                             int mode) {
 680             super(walker, mode);
 681             this.function = function;
 682         }
 683 
 684         /**
 685          * Returns next StackFrame object in the current batch of stack frames;
 686          * or null if no more stack frame.
 687          */
 688         StackFrame nextStackFrame() {
 689             if (!hasNext()) {
 690                 return null;
 691             }
 692 
 693             StackFrame frame = frameBuffer.nextStackFrame();
 694             depth++;
 695             return frame;
 696         }
 697 
 698         @Override
 699         protected T consumeFrames() {
 700             checkState(OPEN);
 701             Stream<StackFrame> stream = StreamSupport.stream(this, false);
 702             if (function != null) {
 703                 return function.apply(stream);
 704             } else
 705                 throw new UnsupportedOperationException();
 706         }
 707 
 708         @Override
 709         protected void initFrameBuffer() {
 710             this.frameBuffer = new Buffer(getNextBatchSize());
 711         }
 712 
 713         @Override
 714         protected int batchSize(int lastBatchFrameCount) {
 715             if (lastBatchFrameCount == 0) {
 716                 // First batch, use estimateDepth if not exceed the large batch size
 717                 // and not too small
 718                 int initialBatchSize = Math.max(walker.estimateDepth(), SMALL_BATCH);
 719                 return Math.min(initialBatchSize, LARGE_BATCH_SIZE);
 720             } else {
 721                 if (lastBatchFrameCount > BATCH_SIZE) {
 722                     return lastBatchFrameCount;
 723                 } else {
 724                     return Math.min(lastBatchFrameCount*2, BATCH_SIZE);
 725                 }
 726             }
 727         }
 728 
 729         // ------- Implementation of Spliterator
 730 
 731         @Override
 732         public Spliterator<StackFrame> trySplit() {
 733             return null;   // ordered stream and do not allow to split
 734         }
 735 
 736         @Override
 737         public long estimateSize() {
 738             return maxDepth;
 739         }
 740 
 741         @Override
 742         public int characteristics() {
 743             return CHARACTERISTICS;
 744         }
 745 
 746         @Override
 747         public void forEachRemaining(Consumer<? super StackFrame> action) {
 748             checkState(OPEN);
 749             for (int n = 0; n < maxDepth; n++) {
 750                 StackFrame frame = nextStackFrame();
 751                 if (frame == null) break;
 752 
 753                 action.accept(frame);
 754             }
 755         }
 756 
 757         @Override
 758         public boolean tryAdvance(Consumer<? super StackFrame> action) {
 759             checkState(OPEN);
 760 
 761             int index = frameBuffer.getIndex();
 762             if (hasNext()) {
 763                 StackFrame frame = nextStackFrame();
 764                 action.accept(frame);
 765                 if (isDebug) {
 766                     System.err.println("tryAdvance: " + index + " " + frame);
 767                 }
 768                 return true;
 769             }
 770             if (isDebug) {
 771                 System.err.println("tryAdvance: " + index + " NO element");
 772             }
 773             return false;
 774         }
 775     }
 776 
 777     /*
 778      * CallerClassFinder is specialized to return Class<?> for each stack frame.
 779      * StackFrame is not requested.
 780      */
 781     static class CallerClassFinder extends AbstractStackWalker<Integer> {
 782         private Class<?> caller;
 783 
 784         CallerClassFinder(StackWalker walker) {
 785             super(walker, FILL_CLASS_REFS_ONLY);
 786         }
 787 
 788         Optional<Class<?>> findCaller() {
 789             walk();
 790             return caller != null ? Optional.of(caller) : Optional.empty();
 791         }
 792 
 793         @Override
 794         protected Integer consumeFrames() {
 795             checkState(OPEN);
 796             int n = 0;
 797             // skip the API calling this getCallerClass method
 798             while (n < 2 && (caller = nextFrame()) != null) {
 799                 if (isDebug) {
 800                     System.err.println("caller: " + n + " " + caller.getName());
 801                 }
 802                 n++;
 803             }
 804             return n;
 805         }
 806 
 807         @Override
 808         protected void initFrameBuffer() {
 809             this.frameBuffer = new FrameBuffer(getNextBatchSize());
 810         }
 811 
 812         @Override
 813         protected int batchSize(int lastBatchFrameCount) {
 814             return MIN_BATCH_SIZE;
 815         }
 816 
 817         @Override
 818         protected int getNextBatchSize() {
 819             return MIN_BATCH_SIZE;
 820         }
 821     }
 822 
 823     /*
 824      * StackTrace caches all frames in the buffer.  StackTraceElements are
 825      * created lazily when Throwable::getStackTrace is called.
 826      */
 827     static class StackTrace extends AbstractStackWalker<Integer> {
 828         class GrowableBuffer extends FrameBuffer {
 829             GrowableBuffer(int initialBatchSize) {
 830                 super(initialBatchSize);
 831 
 832                 this.stackFrames = new StackFrame[initialBatchSize];
 833                 for (int i = START_POS; i < initialBatchSize; i++) {
 834                     stackFrames[i] = new StackFrameInfo(walker);
 835                 }
 836             }
 837 
 838             /*
 839              * Returns the next index to fill
 840              */
 841             @Override
 842             int startIndex() {
 843                 return origin;
 844             }
 845 
 846             /**
 847              * Initialize the buffers for VM to fill in the stack frame information.
 848              * The next batch will start at the given startIndex to
 849              * the length of the buffer.
 850              */
 851             @Override
 852             void resize(int startIndex, int elements) {
 853                 // Expand the frame buffer.
 854                 // Do not call super.resize that will reuse the filled elements
 855                 // in this frame buffer
 856                 int size = startIndex+elements;
 857                 if (classes.length < size) {
 858                     // resize the frame buffer
 859                     classes = Arrays.copyOf(classes, size);
 860                     stackFrames = Arrays.copyOf(stackFrames, size);
 861                 }
 862                 for (int i = startIndex; i < size; i++) {
 863                     stackFrames[i] = new StackFrameInfo(walker);
 864                 }
 865                 currentBatchSize = size;
 866             }
 867 
 868             StackTraceElement get(int index) {
 869                 return new StackTraceElement(classes[index].getName(), "unknown", null, -1);
 870             }
 871 
 872             /**
 873              * Returns an array of StackTraceElement for all stack frames cached in
 874              * this StackTrace object.
 875              * <p>
 876              * This method is intended for Throwable::getOurStackTrace use only.
 877              */
 878             StackTraceElement[] toStackTraceElements() {
 879                 int startIndex = START_POS;
 880                 for (int i = startIndex; i < classes.length; i++) {
 881                     if (classes[i] != null && filterStackWalkImpl(classes[i])) {
 882                         startIndex++;
 883                     } else {
 884                         break;
 885                     }
 886                 }
 887 
 888                 // VM fills in the method name, filename, line number info
 889                 StackFrameInfo.fillInStackFrames(0, stackFrames, startIndex, startIndex + depth);
 890 
 891                 StackTraceElement[] stes = new StackTraceElement[depth];
 892                 for (int i = startIndex, j = 0; i < classes.length && j < depth; i++, j++) {
 893                     if (isDebug) {
 894                         System.err.println("StackFrame: " + i + " " + stackFrames[i]);
 895                     }
 896                     stes[j] = stackFrames[i].toStackTraceElement();
 897                 }
 898                 return stes;
 899             }
 900         }
 901 
 902         private static final int MAX_STACK_FRAMES = 1024;
 903         private StackTraceElement[] stes;
 904 
 905         StackTrace(StackWalker walker) {
 906             this(walker, DEFAULT_MODE);
 907         }
 908 
 909         /*
 910          * Throwable::fillInStackTrace and <init> of Throwable and subclasses
 911          * are filtered in the VM.
 912          */
 913         StackTrace(StackWalker walker, Throwable ex) {
 914             this(walker, FILTER_FILL_IN_STACKTRACE);  // skip Throwable::init frames
 915             if (isDebug) {
 916                 System.err.println("dump stack for " + ex.getClass().getName());
 917             }
 918         }
 919 
 920         StackTrace(StackWalker walker, int mode) {
 921             super(walker, mode, MAX_STACK_FRAMES);
 922 
 923             // snapshot the stack trace
 924             walk();
 925         }
 926 
 927         @Override
 928         protected Integer consumeFrames() {
 929             // traverse all frames and perform the action on the stack frames, if specified
 930             int n = 0;
 931             while (n < maxDepth && nextFrame() != null) {
 932                 n++;
 933             }
 934             return n;
 935         }
 936 
 937         @Override
 938         protected void initFrameBuffer() {
 939             this.frameBuffer = new GrowableBuffer(getNextBatchSize());
 940         }
 941 
 942         // TODO: implement better heuristic
 943         @Override
 944         protected int batchSize(int lastBatchFrameCount) {
 945             // chunk size of VM backtrace is 32
 946             return lastBatchFrameCount == 0 ? 32 : 32;
 947         }
 948 
 949         /**
 950          * Returns an array of StackTraceElement for all stack frames cached in
 951          * this StackTrace object.
 952          * <p>
 953          * This method is intended for Throwable::getOurStackTrace use only.
 954          */
 955         synchronized StackTraceElement[] getStackTraceElements() {
 956             if (stes == null) {
 957                 stes = ((GrowableBuffer) frameBuffer).toStackTraceElements();
 958                 // release the frameBuffer memory
 959                 frameBuffer = null;
 960             }
 961             return stes;
 962         }
 963 
 964         /*
 965          * Prints stack trace to the given PrintStream.
 966          *
 967          * Further implementation could skip creating StackTraceElement objects
 968          * print directly to the PrintStream.
 969          */
 970         void printStackTrace(PrintStream s) {
 971             StackTraceElement[] stes = getStackTraceElements();
 972             synchronized (s) {
 973                 s.println("Stack trace");
 974                 for (StackTraceElement traceElement : stes)
 975                     s.println("\tat " + traceElement);
 976             }
 977         }
 978     }
 979 
 980     static class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> {
 981         // VM will fill in all method info and live stack info directly in StackFrameInfo
 982         class Buffer extends FrameBuffer {
 983             Buffer(int initialBatchSize) {
 984                 super(initialBatchSize);
 985                 this.stackFrames = new StackFrame[initialBatchSize];
 986                 for (int i = START_POS; i < initialBatchSize; i++) {
 987                     stackFrames[i] = new LiveStackFrameInfo(walker);
 988                 }
 989             }
 990 
 991             @Override
 992             void resize(int startIndex, int elements) {
 993                 super.resize(startIndex, elements);
 994                 int size = startIndex + elements;
 995 
 996                 if (stackFrames.length < size) {
 997                     this.stackFrames = new StackFrame[size];
 998                 }
 999 
1000                 for (int i = startIndex(); i < size; i++) {
1001                     stackFrames[i] = new LiveStackFrameInfo(walker);
1002                 }
1003             }
1004 
1005             @Override
1006             StackFrame nextStackFrame() {
1007                 if (isEmpty()) {
1008                     throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
1009                 }
1010 
1011                 StackFrame frame = stackFrames[origin];
1012                 origin++;
1013                 return frame;
1014             }
1015         }
1016 
1017         LiveStackInfoTraverser(StackWalker walker,
1018                                Function<? super Stream<StackFrame>, ? extends T> function) {
1019             super(walker, function, DEFAULT_MODE);
1020         }
1021 
1022         @Override
1023         protected void initFrameBuffer() {
1024             this.frameBuffer = new Buffer(getNextBatchSize());
1025         }
1026     }
1027 
1028     // Stack walk implementation classes to be excluded during stack walking
1029     private final static List<Class<?>> stackWalkImplClasses = implClasses();
1030 
1031     private static List<Class<?>> implClasses() {
1032         Class<?>[] innerClasses = StackStreamFactory.class.getDeclaredClasses();
1033         List<Class<?>> retVal = new ArrayList<>(innerClasses.length+2);
1034         retVal.add(StackStreamFactory.class);
1035         retVal.add(StackWalker.class);
1036         for (Class<?> clazz : innerClasses) {
1037             retVal.add(clazz);
1038         }
1039         return retVal;
1040     }
1041 
1042      private static boolean filterStackWalkImpl(Class<?> clazz) {
1043          return stackWalkImplClasses.contains(clazz) ||
1044                  clazz.getName().startsWith("java.util.stream.");
1045      }
1046 
1047     private static boolean isReflectionFrame(Class<?> c) {
1048         if (c.getName().startsWith("sun.reflect") &&
1049                 !sun.reflect.MethodAccessor.class.isAssignableFrom(c)) {
1050             throw new InternalError("Not sun.reflect.MethodAccessor: " + c.toString());
1051         }
1052         // ## should filter all @Hidden frames?
1053         return c == Method.class ||
1054                 sun.reflect.MethodAccessor.class.isAssignableFrom(c) ||
1055                 c.getName().startsWith("java.lang.invoke.LambdaForm");
1056     }
1057 
1058     private static boolean getProperty(String key, boolean value) {
1059         String s = AccessController.doPrivileged(new PrivilegedAction<>() {
1060             @Override
1061             public String run() {
1062                 return System.getProperty(key);
1063             }
1064         });
1065         if (s != null) {
1066             return Boolean.valueOf(s);
1067         }
1068         return value;
1069     }
1070 }