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