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.Arrays; 37 import java.util.EnumSet; 38 import java.util.HashSet; 39 import java.util.NoSuchElementException; 40 import java.util.Objects; 41 import java.util.Optional; 42 import java.util.Set; 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 // Stack walk implementation classes to be excluded during stack walking 69 // lazily add subclasses when they are loaded. 70 private final static Set<Class<?>> stackWalkImplClasses = init(); 71 72 private static final int SMALL_BATCH = 8; 73 private static final int BATCH_SIZE = 32; 74 private static final int LARGE_BATCH_SIZE = 256; 75 private static final int MIN_BATCH_SIZE = SMALL_BATCH; 76 77 // These flags must match the values maintained in the VM 78 private static final int DEFAULT_MODE = 0x0; 79 private static final int FILL_CLASS_REFS_ONLY = 0x2; 80 private static final int FILTER_FILL_IN_STACKTRACE = 0x10; 81 private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM 82 private static final int FILL_LOCALS_OPERANDS = 0x100; 83 84 /* 85 * For Throwable to use StackWalker, set useNewThrowable to true. 86 * Performance work and extensive testing is needed to replace the 87 * VM built-in backtrace filled in Throwable with the StackWalker. 88 */ 89 final static boolean useNewThrowable = getProperty("stackwalk.newThrowable", false); 90 final static boolean isDebug = getProperty("stackwalk.debug", false); 91 92 static <T> StackFrameTraverser<T> 93 makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function) 94 { 95 if (walker.hasLocalsOperandsOption()) 96 return new LiveStackInfoTraverser<T>(walker, function); 97 else 98 return new StackFrameTraverser<T>(walker, function); 99 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 // 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 this(walker, mode, Integer.MAX_VALUE); 150 } 151 protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) { 152 this.thread = Thread.currentThread(); 153 this.mode = toStackWalkMode(walker, mode); 154 this.walker = walker; 155 this.maxDepth = maxDepth; 156 this.depth = 0; 157 } 158 159 private int toStackWalkMode(StackWalker walker, int mode) { 160 int newMode = mode; 161 if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES) && 162 !fillCallerClassOnly(newMode) /* don't show hidden frames for getCallerClass */) 163 newMode |= SHOW_HIDDEN_FRAMES; 164 if (walker.hasLocalsOperandsOption()) 165 newMode |= FILL_LOCALS_OPERANDS; 166 return newMode; 167 } 168 169 private boolean fillCallerClassOnly(int mode) { 170 return (mode|FILL_CLASS_REFS_ONLY) != FILL_CLASS_REFS_ONLY; 171 } 172 /** 173 * A callback method to consume the stack frames. This method is invoked 174 * once stack walking begins (i.e. it is only invoked when walkFrames is called). 175 * 176 * Each specialized AbstractStackWalker subclass implements the consumeFrames method 177 * to control the following: 178 * 1. fetch the subsequent batches of stack frames 179 * 2. reuse or expand the allocated buffers 180 * 3. create specialized StackFrame objects 181 * 182 * @return the number of consumed frames 183 */ 184 protected abstract T consumeFrames(); 185 186 /** 187 * Initialize FrameBuffer. Subclass should implement this method to 188 * create its custom frame buffers. 189 */ 190 protected abstract void initFrameBuffer(); 191 192 /** 193 * Returns the suggested next batch size. 194 * 195 * Subclass should override this method to change the batch size 196 * or invoke the function passed to the StackWalker::walk method 197 * 198 * @param lastBatchFrameCount number of frames in the last batch; or zero 199 * @return suggested batch size 200 */ 201 protected abstract int batchSize(int lastBatchFrameCount); 202 203 /* 204 * Returns the next batch size, always >= minimum batch size (32) 205 * 206 * Subclass may override this method if the minimum batch size is different. 207 */ 208 protected int getNextBatchSize() { 209 int lastBatchSize = depth == 0 ? 0 : frameBuffer.curBatchFrameCount(); 210 int nextBatchSize = batchSize(lastBatchSize); 211 if (isDebug) { 212 System.err.println("last batch size = " + lastBatchSize + 213 " next batch size = " + nextBatchSize); 214 } 215 return nextBatchSize >= MIN_BATCH_SIZE ? nextBatchSize : MIN_BATCH_SIZE; 216 } 217 218 /* 219 * Checks if this stream is in the given state. Otherwise, throws IllegalStateException. 220 * 221 * VM also validates this stream if it's anchored for stack walking 222 * when stack frames are fetched for each batch. 223 */ 224 final void checkState(WalkerState state) { 225 if (thread != Thread.currentThread()) { 226 throw new IllegalStateException("Invalid thread walking this stack stream: " + 227 Thread.currentThread().getName() + " " + thread.getName()); 228 } 229 switch (state) { 230 case NEW: 231 if (this.anchor != 0) { 232 throw new IllegalStateException("This stack stream is being reused."); 233 } 234 break; 235 case OPEN: 236 if (this.anchor <= 0) { 237 throw new IllegalStateException("This stack stream is not valid for walking"); 238 } 239 break; 240 case CLOSED: 241 if (this.anchor != -1L) { 242 throw new IllegalStateException("This stack stream is not closed."); 243 } 244 } 245 } 246 247 /* 248 * Close this stream. This stream becomes invalid to walk. 249 */ 250 private void close() { 251 this.anchor = -1L; 252 } 253 254 /* 255 * Walks stack frames until {@link #consumeFrames} is done consuming 256 * the frames it is interested in. 257 */ 258 final T walk() { 259 checkState(NEW); 260 try { 261 // VM will need to stablize the stack before walking. It will invoke 262 // the AbstractStackWalker::doStackWalk method once it fetches the first batch. 263 // the callback will be invoked within the scope of the callStackWalk frame. 264 return beginStackWalk(); 265 } finally { 266 close(); // done traversal; close the stream 267 } 268 } 269 270 private boolean skipReflectionFrames() { 271 return !walker.hasOption(Option.SHOW_REFLECT_FRAMES) && 272 !walker.hasOption(Option.SHOW_HIDDEN_FRAMES); 273 } 274 275 /* 276 * Returns {@code Class} object at the current frame; 277 * or {@code null} if no more frame. If advanceToNextBatch is true, 278 * it will only fetch the next batch. 279 */ 280 final Class<?> peekFrame() { 281 while (frameBuffer.isActive() && depth < maxDepth) { 282 if (frameBuffer.isEmpty()) { 283 // fetch another batch of stack frames 284 getNextBatch(); 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() != 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 if (initialBatchSize < MIN_BATCH_SIZE) { 479 throw new IllegalArgumentException(initialBatchSize + " < minimum batch size: " + MIN_BATCH_SIZE); 480 } 481 this.origin = START_POS; 482 this.fence = 0; 483 this.currentBatchSize = initialBatchSize; 484 this.classes = new Class<?>[currentBatchSize]; 485 } 486 487 int curBatchFrameCount() { 488 return currentBatchSize-START_POS; 489 } 490 491 /* 492 * Tests if this frame buffer is empty. All frames are fetched. 493 */ 494 final boolean isEmpty() { 495 return origin >= fence || (origin == START_POS && fence == 0); 496 } 497 498 /* 499 * Freezes this frame buffer. The stack stream source is done fetching. 500 */ 501 final void freeze() { 502 origin = 0; 503 fence = 0; 504 } 505 506 /* 507 * Tests if this frame buffer is active. It is inactive when 508 * it is done for traversal. All stack frames have been traversed. 509 */ 510 final boolean isActive() { 511 return origin > 0 && (fence == 0 || origin < fence || fence == currentBatchSize); 512 } 513 514 /** 515 * Gets the class at the current frame and move to the next frame. 516 */ 517 final Class<?> next() { 518 if (isEmpty()) { 519 throw new NoSuchElementException("origin=" + origin + " fence=" + fence); 520 } 521 Class<?> c = classes[origin++]; 522 if (isDebug) { 523 int index = origin-1; 524 System.out.format(" next frame at %d: %s (origin %d fence %d)%n", index, 525 Objects.toString(c), index, fence); 526 } 527 return c; 528 } 529 530 /** 531 * Gets the class at the current frame. 532 */ 533 final Class<?> get() { 534 if (isEmpty()) { 535 throw new NoSuchElementException("origin=" + origin + " fence=" + fence); 536 } 537 return classes[origin]; 538 } 539 540 /* 541 * Returns the index of the current frame. 542 */ 543 final int getIndex() { 544 return origin; 545 } 546 547 /* 548 * Set the start and end index of a new batch of stack frames that have 549 * been filled in this frame buffer. 550 */ 551 final void setBatch(int startIndex, int endIndex) { 552 if (startIndex <= 0 || endIndex <= 0) 553 throw new IllegalArgumentException("startIndex=" + startIndex + " endIndex=" + endIndex); 554 555 this.origin = startIndex; 556 this.fence = endIndex; 557 if (depth == 0 && fence > 0) { 558 // filter the frames due to the stack stream implementation 559 for (int i = START_POS; i < fence; i++) { 560 Class<?> c = classes[i]; 561 if (isDebug) System.err.format(" frame %d: %s%n", i, c); 562 if (filterStackWalkImpl(c)) { 563 origin++; 564 } else { 565 break; 566 } 567 } 568 } 569 } 570 571 /* 572 * Checks if the origin is the expected start index. 573 */ 574 final void check(int skipFrames) { 575 int index = skipFrames + START_POS; 576 if (origin != index) { 577 // stack walk must continue with the previous frame depth 578 throw new IllegalStateException("origin " + origin + " != " + index); 579 } 580 } 581 582 // ------ subclass may override the following methods ------- 583 /** 584 * Resizes the buffers for VM to fill in the next batch of stack frames. 585 * The next batch will start at the given startIndex with the maximum number 586 * of elements. 587 * 588 * <p> Subclass may override this method to manage the allocated buffers. 589 * 590 * @param startIndex the start index for the first frame of the next batch to fill in. 591 * @param elements the number of elements for the next batch to fill in. 592 * 593 */ 594 void resize(int startIndex, int elements) { 595 if (!isActive()) 596 throw new IllegalStateException("inactive frame buffer can't be resized"); 597 598 int size = startIndex+elements; 599 if (classes.length < size) { 600 // copy the elements in classes array to the newly allocated one. 601 // classes[0] is a Thread object 602 Class<?>[] prev = classes; 603 classes = new Class<?>[size]; 604 System.arraycopy(prev, 0, classes, 0, START_POS); 605 } 606 currentBatchSize = size; 607 } 608 609 /* 610 * Returns the start index for this frame buffer is refilled. 611 * 612 * This implementation reuses the allocated buffer for the next batch 613 * of stack frames. For subclass to retain the fetched stack frames, 614 * it should override this method to return the index at which the frame 615 * should be filled in for the next batch. 616 */ 617 int startIndex() { 618 return START_POS; 619 } 620 621 /** 622 * Returns next StackFrame object in the current batch of stack frames 623 */ 624 StackFrame nextStackFrame() { 625 throw new InternalError("should not reach here"); 626 } 627 } 628 } 629 630 /* 631 * This StackFrameTraverser supports {@link Stream} traversal. 632 * 633 * This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance. 634 */ 635 static class StackFrameTraverser<T> extends AbstractStackWalker<T> 636 implements Spliterator<StackFrame> 637 { 638 static { 639 stackWalkImplClasses.add(StackFrameTraverser.class); 640 } 641 private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE; 642 class Buffer extends FrameBuffer { 643 Buffer(int initialBatchSize) { 644 super(initialBatchSize); 645 646 this.stackFrames = new StackFrame[initialBatchSize]; 647 for (int i = START_POS; i < initialBatchSize; i++) { 648 stackFrames[i] = new StackFrameInfo(walker); 649 } 650 } 651 652 @Override 653 void resize(int startIndex, int elements) { 654 super.resize(startIndex, elements); 655 656 int size = startIndex+elements; 657 if (stackFrames.length < size) { 658 stackFrames = new StackFrame[size]; 659 } 660 for (int i = startIndex(); i < size; i++) { 661 stackFrames[i] = new StackFrameInfo(walker); 662 } 663 } 664 665 @Override 666 StackFrame nextStackFrame() { 667 if (isEmpty()) { 668 throw new NoSuchElementException("origin=" + origin + " fence=" + fence); 669 } 670 671 StackFrame frame = stackFrames[origin]; 672 origin++; 673 return frame; 674 } 675 } 676 677 final Function<? super Stream<StackFrame>, ? extends T> function; // callback 678 679 StackFrameTraverser(StackWalker walker, 680 Function<? super Stream<StackFrame>, ? extends T> function) { 681 this(walker, function, DEFAULT_MODE); 682 } 683 StackFrameTraverser(StackWalker walker, 684 Function<? super Stream<StackFrame>, ? extends T> function, 685 int mode) { 686 super(walker, mode); 687 this.function = function; 688 } 689 690 /** 691 * Returns next StackFrame object in the current batch of stack frames; 692 * or null if no more stack frame. 693 */ 694 StackFrame nextStackFrame() { 695 if (!hasNext()) { 696 return null; 697 } 698 699 StackFrame frame = frameBuffer.nextStackFrame(); 700 depth++; 701 return frame; 702 } 703 704 @Override 705 protected T consumeFrames() { 706 checkState(OPEN); 707 Stream<StackFrame> stream = StreamSupport.stream(this, false); 708 if (function != null) { 709 return function.apply(stream); 710 } else 711 throw new UnsupportedOperationException(); 712 } 713 714 @Override 715 protected void initFrameBuffer() { 716 this.frameBuffer = new Buffer(getNextBatchSize()); 717 } 718 719 @Override 720 protected int batchSize(int lastBatchFrameCount) { 721 if (lastBatchFrameCount == 0) { 722 // First batch, use estimateDepth if not exceed the large batch size 723 // and not too small 724 int initialBatchSize = Math.max(walker.estimateDepth(), SMALL_BATCH); 725 return Math.min(initialBatchSize, LARGE_BATCH_SIZE); 726 } else { 727 if (lastBatchFrameCount > BATCH_SIZE) { 728 return lastBatchFrameCount; 729 } else { 730 return Math.min(lastBatchFrameCount*2, BATCH_SIZE); 731 } 732 } 733 } 734 735 // ------- Implementation of Spliterator 736 737 @Override 738 public Spliterator<StackFrame> trySplit() { 739 return null; // ordered stream and do not allow to split 740 } 741 742 @Override 743 public long estimateSize() { 744 return maxDepth; 745 } 746 747 @Override 748 public int characteristics() { 749 return CHARACTERISTICS; 750 } 751 752 @Override 753 public void forEachRemaining(Consumer<? super StackFrame> action) { 754 checkState(OPEN); 755 for (int n = 0; n < maxDepth; n++) { 756 StackFrame frame = nextStackFrame(); 757 if (frame == null) break; 758 759 action.accept(frame); 760 } 761 } 762 763 @Override 764 public boolean tryAdvance(Consumer<? super StackFrame> action) { 765 checkState(OPEN); 766 767 int index = frameBuffer.getIndex(); 768 if (hasNext()) { 769 StackFrame frame = nextStackFrame(); 770 action.accept(frame); 771 if (isDebug) { 772 System.err.println("tryAdvance: " + index + " " + frame); 773 } 774 return true; 775 } 776 if (isDebug) { 777 System.err.println("tryAdvance: " + index + " NO element"); 778 } 779 return false; 780 } 781 } 782 783 /* 784 * CallerClassFinder is specialized to return Class<?> for each stack frame. 785 * StackFrame is not requested. 786 */ 787 static class CallerClassFinder extends AbstractStackWalker<Integer> { 788 static { 789 stackWalkImplClasses.add(CallerClassFinder.class); 790 } 791 792 private Class<?> caller; 793 794 CallerClassFinder(StackWalker walker) { 795 super(walker, FILL_CLASS_REFS_ONLY); 796 } 797 798 Class<?> findCaller() { 799 walk(); 800 return caller; 801 } 802 803 @Override 804 protected Integer consumeFrames() { 805 checkState(OPEN); 806 int n = 0; 807 Class<?>[] frames = new Class<?>[2]; 808 // skip the API calling this getCallerClass method 809 // 0: StackWalker::getCallerClass 810 // 1: caller-sensitive method 811 // 2: caller class, Thread::run, or no such frame 812 while (n < 2 && (caller = nextFrame()) != null) { 813 if (isMethodHandleFrame(caller)) continue; 814 815 frames[n++] = caller; 816 } 817 818 // methods in Thread class doesn't call getCallerClass 819 // it's safe to simply check Thread.class 820 if (frames[1] == null || frames[1] == Thread.class) { 821 // return the entry point of the thread 822 caller = frames[0]; 823 } 824 return n; 825 } 826 827 @Override 828 protected void initFrameBuffer() { 829 this.frameBuffer = new FrameBuffer(getNextBatchSize()); 830 } 831 832 @Override 833 protected int batchSize(int lastBatchFrameCount) { 834 return MIN_BATCH_SIZE; 835 } 836 837 @Override 838 protected int getNextBatchSize() { 839 return MIN_BATCH_SIZE; 840 } 841 } 842 843 /* 844 * StackTrace caches all frames in the buffer. StackTraceElements are 845 * created lazily when Throwable::getStackTrace is called. 846 */ 847 static class StackTrace extends AbstractStackWalker<Integer> { 848 static { 849 stackWalkImplClasses.add(StackTrace.class); 850 } 851 852 class GrowableBuffer extends FrameBuffer { 853 GrowableBuffer(int initialBatchSize) { 854 super(initialBatchSize); 855 856 this.stackFrames = new StackFrame[initialBatchSize]; 857 for (int i = START_POS; i < initialBatchSize; i++) { 858 stackFrames[i] = new StackFrameInfo(walker); 859 } 860 } 861 862 /* 863 * Returns the next index to fill 864 */ 865 @Override 866 int startIndex() { 867 return origin; 868 } 869 870 /** 871 * Initialize the buffers for VM to fill in the stack frame information. 872 * The next batch will start at the given startIndex to 873 * the length of the buffer. 874 */ 875 @Override 876 void resize(int startIndex, int elements) { 877 // Expand the frame buffer. 878 // Do not call super.resize that will reuse the filled elements 879 // in this frame buffer 880 int size = startIndex+elements; 881 if (classes.length < size) { 882 // resize the frame buffer 883 classes = Arrays.copyOf(classes, size); 884 stackFrames = Arrays.copyOf(stackFrames, size); 885 } 886 for (int i = startIndex; i < size; i++) { 887 stackFrames[i] = new StackFrameInfo(walker); 888 } 889 currentBatchSize = size; 890 } 891 892 StackTraceElement get(int index) { 893 return new StackTraceElement(classes[index].getName(), "unknown", null, -1); 894 } 895 896 /** 897 * Returns an array of StackTraceElement for all stack frames cached in 898 * this StackTrace object. 899 * <p> 900 * This method is intended for Throwable::getOurStackTrace use only. 901 */ 902 StackTraceElement[] toStackTraceElements() { 903 int startIndex = START_POS; 904 for (int i = startIndex; i < classes.length; i++) { 905 if (classes[i] != null && filterStackWalkImpl(classes[i])) { 906 startIndex++; 907 } else { 908 break; 909 } 910 } 911 912 // VM fills in the method name, filename, line number info 913 StackFrameInfo.fillInStackFrames(0, stackFrames, startIndex, startIndex + depth); 914 915 StackTraceElement[] stes = new StackTraceElement[depth]; 916 for (int i = startIndex, j = 0; i < classes.length && j < depth; i++, j++) { 917 if (isDebug) { 918 System.err.println("StackFrame: " + i + " " + stackFrames[i]); 919 } 920 stes[j] = stackFrames[i].toStackTraceElement(); 921 } 922 return stes; 923 } 924 } 925 926 private static final int MAX_STACK_FRAMES = 1024; 927 private static final StackWalker STACKTRACE_WALKER = 928 StackWalker.newInstanceNoCheck(EnumSet.of(Option.SHOW_REFLECT_FRAMES)); 929 930 private StackTraceElement[] stes; 931 static StackTrace dump() { 932 return new StackTrace(); 933 } 934 935 static StackTrace dump(Throwable ex) { 936 return new StackTrace(ex); 937 } 938 939 private StackTrace() { 940 this(STACKTRACE_WALKER, DEFAULT_MODE); 941 } 942 943 /* 944 * Throwable::fillInStackTrace and <init> of Throwable and subclasses 945 * are filtered in the VM. 946 */ 947 private StackTrace(Throwable ex) { 948 this(STACKTRACE_WALKER, FILTER_FILL_IN_STACKTRACE); // skip Throwable::init frames 949 if (isDebug) { 950 System.err.println("dump stack for " + ex.getClass().getName()); 951 } 952 } 953 954 StackTrace(StackWalker walker, int mode) { 955 super(walker, mode, MAX_STACK_FRAMES); 956 957 // snapshot the stack trace 958 walk(); 959 } 960 961 @Override 962 protected Integer consumeFrames() { 963 // traverse all frames and perform the action on the stack frames, if specified 964 int n = 0; 965 while (n < maxDepth && nextFrame() != null) { 966 n++; 967 } 968 return n; 969 } 970 971 @Override 972 protected void initFrameBuffer() { 973 this.frameBuffer = new GrowableBuffer(getNextBatchSize()); 974 } 975 976 // TODO: implement better heuristic 977 @Override 978 protected int batchSize(int lastBatchFrameCount) { 979 // chunk size of VM backtrace is 32 980 return lastBatchFrameCount == 0 ? 32 : 32; 981 } 982 983 /** 984 * Returns an array of StackTraceElement for all stack frames cached in 985 * this StackTrace object. 986 * <p> 987 * This method is intended for Throwable::getOurStackTrace use only. 988 */ 989 synchronized StackTraceElement[] getStackTraceElements() { 990 if (stes == null) { 991 stes = ((GrowableBuffer) frameBuffer).toStackTraceElements(); 992 // release the frameBuffer memory 993 frameBuffer = null; 994 } 995 return stes; 996 } 997 998 /* 999 * Prints stack trace to the given PrintStream. 1000 * 1001 * Further implementation could skip creating StackTraceElement objects 1002 * print directly to the PrintStream. 1003 */ 1004 void printStackTrace(PrintStream s) { 1005 StackTraceElement[] stes = getStackTraceElements(); 1006 synchronized (s) { 1007 s.println("Stack trace"); 1008 for (StackTraceElement traceElement : stes) 1009 s.println("\tat " + traceElement); 1010 } 1011 } 1012 } 1013 1014 static class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> { 1015 static { 1016 stackWalkImplClasses.add(LiveStackInfoTraverser.class); 1017 } 1018 // VM will fill in all method info and live stack info directly in StackFrameInfo 1019 class Buffer extends FrameBuffer { 1020 Buffer(int initialBatchSize) { 1021 super(initialBatchSize); 1022 this.stackFrames = new StackFrame[initialBatchSize]; 1023 for (int i = START_POS; i < initialBatchSize; i++) { 1024 stackFrames[i] = new LiveStackFrameInfo(walker); 1025 } 1026 } 1027 1028 @Override 1029 void resize(int startIndex, int elements) { 1030 super.resize(startIndex, elements); 1031 int size = startIndex + elements; 1032 1033 if (stackFrames.length < size) { 1034 this.stackFrames = new StackFrame[size]; 1035 } 1036 1037 for (int i = startIndex(); i < size; i++) { 1038 stackFrames[i] = new LiveStackFrameInfo(walker); 1039 } 1040 } 1041 1042 @Override 1043 StackFrame nextStackFrame() { 1044 if (isEmpty()) { 1045 throw new NoSuchElementException("origin=" + origin + " fence=" + fence); 1046 } 1047 1048 StackFrame frame = stackFrames[origin]; 1049 origin++; 1050 return frame; 1051 } 1052 } 1053 1054 LiveStackInfoTraverser(StackWalker walker, 1055 Function<? super Stream<StackFrame>, ? extends T> function) { 1056 super(walker, function, DEFAULT_MODE); 1057 } 1058 1059 @Override 1060 protected void initFrameBuffer() { 1061 this.frameBuffer = new Buffer(getNextBatchSize()); 1062 } 1063 } 1064 1065 // avoid loading other subclasses as they may not be used 1066 private static Set<Class<?>> init() { 1067 Set<Class<?>> classes = new HashSet<>(); 1068 classes.add(StackWalker.class); 1069 classes.add(StackStreamFactory.class); 1070 classes.add(AbstractStackWalker.class); 1071 return classes; 1072 } 1073 1074 private static boolean filterStackWalkImpl(Class<?> c) { 1075 return stackWalkImplClasses.contains(c) || 1076 c.getName().startsWith("java.util.stream."); 1077 } 1078 1079 // MethodHandle frames are not hidden and CallerClassFinder has 1080 // to filter them out 1081 private static boolean isMethodHandleFrame(Class<?> c) { 1082 return c.getName().startsWith("java.lang.invoke."); 1083 } 1084 1085 private static boolean isReflectionFrame(Class<?> c) { 1086 if (c.getName().startsWith("sun.reflect") && 1087 !sun.reflect.MethodAccessor.class.isAssignableFrom(c)) { 1088 throw new InternalError("Not sun.reflect.MethodAccessor: " + c.toString()); 1089 } 1090 // ## should filter all @Hidden frames? 1091 return c == Method.class || 1092 sun.reflect.MethodAccessor.class.isAssignableFrom(c) || 1093 c.getName().startsWith("java.lang.invoke.LambdaForm"); 1094 } 1095 1096 private static boolean getProperty(String key, boolean value) { 1097 String s = AccessController.doPrivileged(new PrivilegedAction<>() { 1098 @Override 1099 public String run() { 1100 return System.getProperty(key); 1101 } 1102 }); 1103 if (s != null) { 1104 return Boolean.valueOf(s); 1105 } 1106 return value; 1107 } 1108 }