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