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 }