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 are traversal. 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 Optional<Class<?>> findCaller() { 799 walk(); 800 return caller != null ? Optional.of(caller) : Optional.empty(); 801 } 802 803 @Override 804 protected Integer consumeFrames() { 805 checkState(OPEN); 806 int n = 0; 807 // skip the API calling this getCallerClass method 808 while (n < 2 && (caller = nextFrame()) != null) { 809 if (isMethodHandleFrame(caller)) continue; 810 if (isDebug) { 811 System.err.println("caller: " + n + " " + caller.getName()); 812 } 813 n++; 814 } 815 return n; 816 } 817 818 @Override 819 protected void initFrameBuffer() { 820 this.frameBuffer = new FrameBuffer(getNextBatchSize()); 821 } 822 823 @Override 824 protected int batchSize(int lastBatchFrameCount) { 825 return MIN_BATCH_SIZE; 826 } 827 828 @Override 829 protected int getNextBatchSize() { 830 return MIN_BATCH_SIZE; 831 } 832 } 833 834 /* 835 * StackTrace caches all frames in the buffer. StackTraceElements are 836 * created lazily when Throwable::getStackTrace is called. 837 */ 838 static class StackTrace extends AbstractStackWalker<Integer> { 839 static { 840 stackWalkImplClasses.add(StackTrace.class); 841 } 842 843 class GrowableBuffer extends FrameBuffer { 844 GrowableBuffer(int initialBatchSize) { 845 super(initialBatchSize); 846 847 this.stackFrames = new StackFrame[initialBatchSize]; 848 for (int i = START_POS; i < initialBatchSize; i++) { 849 stackFrames[i] = new StackFrameInfo(walker); 850 } 851 } 852 853 /* 854 * Returns the next index to fill 855 */ 856 @Override 857 int startIndex() { 858 return origin; 859 } 860 861 /** 862 * Initialize the buffers for VM to fill in the stack frame information. 863 * The next batch will start at the given startIndex to 864 * the length of the buffer. 865 */ 866 @Override 867 void resize(int startIndex, int elements) { 868 // Expand the frame buffer. 869 // Do not call super.resize that will reuse the filled elements 870 // in this frame buffer 871 int size = startIndex+elements; 872 if (classes.length < size) { 873 // resize the frame buffer 874 classes = Arrays.copyOf(classes, size); 875 stackFrames = Arrays.copyOf(stackFrames, size); 876 } 877 for (int i = startIndex; i < size; i++) { 878 stackFrames[i] = new StackFrameInfo(walker); 879 } 880 currentBatchSize = size; 881 } 882 883 StackTraceElement get(int index) { 884 return new StackTraceElement(classes[index].getName(), "unknown", null, -1); 885 } 886 887 /** 888 * Returns an array of StackTraceElement for all stack frames cached in 889 * this StackTrace object. 890 * <p> 891 * This method is intended for Throwable::getOurStackTrace use only. 892 */ 893 StackTraceElement[] toStackTraceElements() { 894 int startIndex = START_POS; 895 for (int i = startIndex; i < classes.length; i++) { 896 if (classes[i] != null && filterStackWalkImpl(classes[i])) { 897 startIndex++; 898 } else { 899 break; 900 } 901 } 902 903 // VM fills in the method name, filename, line number info 904 StackFrameInfo.fillInStackFrames(0, stackFrames, startIndex, startIndex + depth); 905 906 StackTraceElement[] stes = new StackTraceElement[depth]; 907 for (int i = startIndex, j = 0; i < classes.length && j < depth; i++, j++) { 908 if (isDebug) { 909 System.err.println("StackFrame: " + i + " " + stackFrames[i]); 910 } 911 stes[j] = stackFrames[i].toStackTraceElement(); 912 } 913 return stes; 914 } 915 } 916 917 private static final int MAX_STACK_FRAMES = 1024; 918 private static final StackWalker STACKTRACE_WALKER = 919 StackWalker.newInstanceNoCheck(EnumSet.of(Option.SHOW_REFLECT_FRAMES)); 920 921 private StackTraceElement[] stes; 922 static StackTrace dump() { 923 return new StackTrace(); 924 } 925 926 static StackTrace dump(Throwable ex) { 927 return new StackTrace(ex); 928 } 929 930 private StackTrace() { 931 this(STACKTRACE_WALKER, DEFAULT_MODE); 932 } 933 934 /* 935 * Throwable::fillInStackTrace and <init> of Throwable and subclasses 936 * are filtered in the VM. 937 */ 938 private StackTrace(Throwable ex) { 939 this(STACKTRACE_WALKER, FILTER_FILL_IN_STACKTRACE); // skip Throwable::init frames 940 if (isDebug) { 941 System.err.println("dump stack for " + ex.getClass().getName()); 942 } 943 } 944 945 StackTrace(StackWalker walker, int mode) { 946 super(walker, mode, MAX_STACK_FRAMES); 947 948 // snapshot the stack trace 949 walk(); 950 } 951 952 @Override 953 protected Integer consumeFrames() { 954 // traverse all frames and perform the action on the stack frames, if specified 955 int n = 0; 956 while (n < maxDepth && nextFrame() != null) { 957 n++; 958 } 959 return n; 960 } 961 962 @Override 963 protected void initFrameBuffer() { 964 this.frameBuffer = new GrowableBuffer(getNextBatchSize()); 965 } 966 967 // TODO: implement better heuristic 968 @Override 969 protected int batchSize(int lastBatchFrameCount) { 970 // chunk size of VM backtrace is 32 971 return lastBatchFrameCount == 0 ? 32 : 32; 972 } 973 974 /** 975 * Returns an array of StackTraceElement for all stack frames cached in 976 * this StackTrace object. 977 * <p> 978 * This method is intended for Throwable::getOurStackTrace use only. 979 */ 980 synchronized StackTraceElement[] getStackTraceElements() { 981 if (stes == null) { 982 stes = ((GrowableBuffer) frameBuffer).toStackTraceElements(); 983 // release the frameBuffer memory 984 frameBuffer = null; 985 } 986 return stes; 987 } 988 989 /* 990 * Prints stack trace to the given PrintStream. 991 * 992 * Further implementation could skip creating StackTraceElement objects 993 * print directly to the PrintStream. 994 */ 995 void printStackTrace(PrintStream s) { 996 StackTraceElement[] stes = getStackTraceElements(); 997 synchronized (s) { 998 s.println("Stack trace"); 999 for (StackTraceElement traceElement : stes) 1000 s.println("\tat " + traceElement); 1001 } 1002 } 1003 } 1004 1005 static class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> { 1006 static { 1007 stackWalkImplClasses.add(LiveStackInfoTraverser.class); 1008 } 1009 // VM will fill in all method info and live stack info directly in StackFrameInfo 1010 class Buffer extends FrameBuffer { 1011 Buffer(int initialBatchSize) { 1012 super(initialBatchSize); 1013 this.stackFrames = new StackFrame[initialBatchSize]; 1014 for (int i = START_POS; i < initialBatchSize; i++) { 1015 stackFrames[i] = new LiveStackFrameInfo(walker); 1016 } 1017 } 1018 1019 @Override 1020 void resize(int startIndex, int elements) { 1021 super.resize(startIndex, elements); 1022 int size = startIndex + elements; 1023 1024 if (stackFrames.length < size) { 1025 this.stackFrames = new StackFrame[size]; 1026 } 1027 1028 for (int i = startIndex(); i < size; i++) { 1029 stackFrames[i] = new LiveStackFrameInfo(walker); 1030 } 1031 } 1032 1033 @Override 1034 StackFrame nextStackFrame() { 1035 if (isEmpty()) { 1036 throw new NoSuchElementException("origin=" + origin + " fence=" + fence); 1037 } 1038 1039 StackFrame frame = stackFrames[origin]; 1040 origin++; 1041 return frame; 1042 } 1043 } 1044 1045 LiveStackInfoTraverser(StackWalker walker, 1046 Function<? super Stream<StackFrame>, ? extends T> function) { 1047 super(walker, function, DEFAULT_MODE); 1048 } 1049 1050 @Override 1051 protected void initFrameBuffer() { 1052 this.frameBuffer = new Buffer(getNextBatchSize()); 1053 } 1054 } 1055 1056 // avoid loading other subclasses as they may not be used 1057 private static Set<Class<?>> init() { 1058 Set<Class<?>> classes = new HashSet<>(); 1059 classes.add(StackWalker.class); 1060 classes.add(StackStreamFactory.class); 1061 classes.add(AbstractStackWalker.class); 1062 return classes; 1063 } 1064 1065 private static boolean filterStackWalkImpl(Class<?> c) { 1066 return stackWalkImplClasses.contains(c) || 1067 c.getName().startsWith("java.util.stream."); 1068 } 1069 1070 // MethodHandle frames are not hidden and CallerClassFinder has 1071 // to filter them out 1072 private static boolean isMethodHandleFrame(Class<?> c) { 1073 return c.getName().startsWith("java.lang.invoke."); 1074 } 1075 1076 private static boolean isReflectionFrame(Class<?> c) { 1077 if (c.getName().startsWith("sun.reflect") && 1078 !sun.reflect.MethodAccessor.class.isAssignableFrom(c)) { 1079 throw new InternalError("Not sun.reflect.MethodAccessor: " + c.toString()); 1080 } 1081 // ## should filter all @Hidden frames? 1082 return c == Method.class || 1083 sun.reflect.MethodAccessor.class.isAssignableFrom(c) || 1084 c.getName().startsWith("java.lang.invoke.LambdaForm"); 1085 } 1086 1087 private static boolean getProperty(String key, boolean value) { 1088 String s = AccessController.doPrivileged(new PrivilegedAction<>() { 1089 @Override 1090 public String run() { 1091 return System.getProperty(key); 1092 } 1093 }); 1094 if (s != null) { 1095 return Boolean.valueOf(s); 1096 } 1097 return value; 1098 } 1099 }