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