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