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 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 = 82 "true".equals(GetPropertyAction.privilegedGetProperty("stackwalk.debug")); 83 84 static <T> StackFrameTraverser<T> 85 makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function) 86 { 87 if (walker.hasLocalsOperandsOption()) 88 return new LiveStackInfoTraverser<>(walker, function); 89 else 90 return new StackFrameTraverser<>(walker, function); 91 } 92 93 /** 94 * Gets a stack stream to find caller class. 95 */ 96 static CallerClassFinder makeCallerFinder(StackWalker walker) { 97 return new CallerClassFinder(walker); 98 } 99 100 enum WalkerState { 101 NEW, // the stream is new and stack walking has not started 102 OPEN, // the stream is open when it is being traversed. 103 CLOSED; // the stream is closed when the stack walking is done 104 } 105 106 /** 107 * Subclass of AbstractStackWalker implements a specific stack walking logic. 108 * It needs to set up the frame buffer and stack walking mode. 109 * 110 * It initiates the VM stack walking via the callStackWalk method that serves 111 * as the anchored frame and VM will call up to AbstractStackWalker::doStackWalk. 112 * 113 * @param <R> the type of the result returned from stack walking 114 * @param <T> the type of the data gathered for each frame. 115 * For example, StackFrameInfo for StackWalker::walk or 116 * Class<?> for StackWalker::getCallerClass 117 */ 118 static abstract class AbstractStackWalker<R, T> { 119 protected final StackWalker walker; 120 protected final Thread thread; 121 protected final int maxDepth; 122 protected final long mode; 123 protected int depth; // traversed stack depth 124 protected FrameBuffer<? extends T> frameBuffer; 125 protected long anchor; 126 127 // buffers to fill in stack frame information 128 protected AbstractStackWalker(StackWalker walker, int mode) { 129 this(walker, mode, Integer.MAX_VALUE); 130 } 131 protected AbstractStackWalker(StackWalker walker, int mode, int maxDepth) { 132 this.thread = Thread.currentThread(); 133 this.mode = toStackWalkMode(walker, mode); 134 this.walker = walker; 135 this.maxDepth = maxDepth; 136 this.depth = 0; 137 } 138 139 private int toStackWalkMode(StackWalker walker, int mode) { 140 int newMode = mode; 141 if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES) && 142 (mode & FILL_CLASS_REFS_ONLY) != FILL_CLASS_REFS_ONLY) 143 newMode |= SHOW_HIDDEN_FRAMES; 144 if (walker.hasLocalsOperandsOption()) 145 newMode |= FILL_LIVE_STACK_FRAMES; 146 return newMode; 147 } 148 149 /** 150 * A callback method to consume the stack frames. This method is invoked 151 * once stack walking begins (i.e. it is only invoked when walkFrames is called). 152 * 153 * Each specialized AbstractStackWalker subclass implements the consumeFrames method 154 * to control the following: 155 * 1. fetch the subsequent batches of stack frames 156 * 2. reuse or expand the allocated buffers 157 * 3. create specialized StackFrame objects 158 * 159 * @return the number of consumed frames 160 */ 161 protected abstract R consumeFrames(); 162 163 /** 164 * Initialize FrameBuffer. Subclass should implement this method to 165 * create its custom frame buffers. 166 */ 167 protected abstract void initFrameBuffer(); 168 169 /** 170 * Returns the suggested next batch size. 171 * 172 * Subclass should override this method to change the batch size 173 * 174 * @param lastBatchFrameCount number of frames in the last batch; or zero 175 * @return suggested batch size 176 */ 177 protected abstract int batchSize(int lastBatchFrameCount); 178 179 /* 180 * Returns the next batch size, always >= minimum batch size (32) 181 * 182 * Subclass may override this method if the minimum batch size is different. 183 */ 184 protected int getNextBatchSize() { 185 int lastBatchSize = depth == 0 ? 0 : frameBuffer.curBatchFrameCount(); 186 int nextBatchSize = batchSize(lastBatchSize); 187 if (isDebug) { 188 System.err.println("last batch size = " + lastBatchSize + 189 " next batch size = " + nextBatchSize); 190 } 191 return nextBatchSize >= MIN_BATCH_SIZE ? nextBatchSize : MIN_BATCH_SIZE; 192 } 193 194 /* 195 * Checks if this stream is in the given state. Otherwise, throws IllegalStateException. 196 * 197 * VM also validates this stream if it's anchored for stack walking 198 * when stack frames are fetched for each batch. 199 */ 200 final void checkState(WalkerState state) { 201 if (thread != Thread.currentThread()) { 202 throw new IllegalStateException("Invalid thread walking this stack stream: " + 203 Thread.currentThread().getName() + " " + thread.getName()); 204 } 205 switch (state) { 206 case NEW: 207 if (anchor != 0) { 208 throw new IllegalStateException("This stack stream is being reused."); 209 } 210 break; 211 case OPEN: 212 if (anchor == 0 || anchor == -1L) { 213 throw new IllegalStateException("This stack stream is not valid for walking."); 214 } 215 break; 216 case CLOSED: 217 if (anchor != -1L) { 218 throw new IllegalStateException("This stack stream is not closed."); 219 } 220 } 221 } 222 223 /* 224 * Close this stream. This stream becomes invalid to walk. 225 */ 226 private void close() { 227 this.anchor = -1L; 228 } 229 230 /* 231 * Walks stack frames until {@link #consumeFrames} is done consuming 232 * the frames it is interested in. 233 */ 234 final R walk() { 235 checkState(NEW); 236 try { 237 // VM will need to stablize the stack before walking. It will invoke 238 // the AbstractStackWalker::doStackWalk method once it fetches the first batch. 239 // the callback will be invoked within the scope of the callStackWalk frame. 240 return beginStackWalk(); 241 } finally { 242 close(); // done traversal; close the stream 243 } 244 } 245 246 private boolean skipReflectionFrames() { 247 return !walker.hasOption(Option.SHOW_REFLECT_FRAMES) && 248 !walker.hasOption(Option.SHOW_HIDDEN_FRAMES); 249 } 250 251 /* 252 * Returns {@code Class} object at the current frame; 253 * or {@code null} if no more frame. If advanceToNextBatch is true, 254 * it will only fetch the next batch. 255 */ 256 final Class<?> peekFrame() { 257 while (frameBuffer.isActive() && depth < maxDepth) { 258 if (frameBuffer.isEmpty()) { 259 // fetch another batch of stack frames 260 getNextBatch(); 261 } else { 262 Class<?> c = frameBuffer.get(); 263 if (skipReflectionFrames() && isReflectionFrame(c)) { 264 if (isDebug) 265 System.err.println(" skip: frame " + frameBuffer.getIndex() + " " + c); 266 267 frameBuffer.next(); 268 depth++; 269 continue; 270 } else { 271 return c; 272 } 273 } 274 } 275 return null; 276 } 277 278 /* 279 * This method is only invoked by VM. 280 * 281 * It will invoke the consumeFrames method to start the stack walking 282 * with the first batch of stack frames. Each specialized AbstractStackWalker 283 * subclass implements the consumeFrames method to control the following: 284 * 1. fetch the subsequent batches of stack frames 285 * 2. reuse or expand the allocated buffers 286 * 3. create specialized StackFrame objects 287 */ 288 private Object doStackWalk(long anchor, int skipFrames, int batchSize, 289 int bufStartIndex, int bufEndIndex) { 290 checkState(NEW); 291 292 frameBuffer.check(skipFrames); 293 294 if (isDebug) { 295 System.err.format("doStackWalk: skip %d start %d end %d%n", 296 skipFrames, bufStartIndex, bufEndIndex); 297 } 298 299 this.anchor = anchor; // set anchor for this bulk stack frame traversal 300 frameBuffer.setBatch(depth, bufStartIndex, bufEndIndex); 301 302 // traverse all frames and perform the action on the stack frames, if specified 303 return consumeFrames(); 304 } 305 306 /* 307 * Get next batch of stack frames. 308 */ 309 private int getNextBatch() { 310 int nextBatchSize = Math.min(maxDepth - depth, getNextBatchSize()); 311 if (!frameBuffer.isActive() || nextBatchSize <= 0) { 312 if (isDebug) { 313 System.out.format(" more stack walk done%n"); 314 } 315 frameBuffer.freeze(); // stack walk done 316 return 0; 317 } 318 319 return fetchStackFrames(nextBatchSize); 320 } 321 322 /* 323 * This method traverses the next stack frame and returns the Class 324 * invoking that stack frame. 325 * 326 * This method can only be called during the walk method. This is intended 327 * to be used to walk the stack frames in one single invocation and 328 * this stack stream will be invalidated once walk is done. 329 * 330 * @see #tryNextFrame 331 */ 332 final Class<?> nextFrame() { 333 if (!hasNext()) { 334 return null; 335 } 336 337 Class<?> c = frameBuffer.next(); 338 depth++; 339 return c; 340 } 341 342 /* 343 * Returns true if there is next frame to be traversed. 344 * This skips hidden frames unless this StackWalker has 345 * {@link Option#SHOW_REFLECT_FRAMES} 346 */ 347 final boolean hasNext() { 348 return peekFrame() != null; 349 } 350 351 /** 352 * Begin stack walking - pass the allocated arrays to the VM to fill in 353 * stack frame information. 354 * 355 * VM first anchors the frame of the current thread. A traversable stream 356 * on this thread's stack will be opened. The VM will fetch the first batch 357 * of stack frames and call AbstractStackWalker::doStackWalk to invoke the 358 * stack walking function on each stack frame. 359 * 360 * If all fetched stack frames are traversed, AbstractStackWalker::fetchStackFrames will 361 * fetch the next batch of stack frames to continue. 362 */ 363 private R beginStackWalk() { 364 // initialize buffers for VM to fill the stack frame info 365 initFrameBuffer(); 366 367 return callStackWalk(mode, 0, 368 frameBuffer.curBatchFrameCount(), 369 frameBuffer.startIndex(), 370 frameBuffer.frames()); 371 } 372 373 /* 374 * Fetches stack frames. 375 * 376 * @params batchSize number of elements of the frame buffers for this batch 377 * @returns number of frames fetched in this batch 378 */ 379 private int fetchStackFrames(int batchSize) { 380 int startIndex = frameBuffer.startIndex(); 381 frameBuffer.resize(startIndex, batchSize); 382 383 int endIndex = fetchStackFrames(mode, anchor, batchSize, 384 startIndex, 385 frameBuffer.frames()); 386 if (isDebug) { 387 System.out.format(" more stack walk requesting %d got %d to %d frames%n", 388 batchSize, frameBuffer.startIndex(), endIndex); 389 } 390 int numFrames = endIndex - startIndex; 391 if (numFrames == 0) { 392 frameBuffer.freeze(); // done stack walking 393 } else { 394 frameBuffer.setBatch(depth, startIndex, endIndex); 395 } 396 return numFrames; 397 } 398 399 /** 400 * Begins stack walking. This method anchors this frame and invokes 401 * AbstractStackWalker::doStackWalk after fetching the first batch of stack frames. 402 * 403 * @param mode mode of stack walking 404 * @param skipframes number of frames to be skipped before filling the frame buffer. 405 * @param batchSize the batch size, max. number of elements to be filled in the frame buffers. 406 * @param startIndex start index of the frame buffers to be filled. 407 * @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY} 408 * or a {@link StackFrameInfo} (or derivative) array otherwise. 409 * @return Result of AbstractStackWalker::doStackWalk 410 */ 411 private native R callStackWalk(long mode, int skipframes, 412 int batchSize, int startIndex, 413 T[] frames); 414 415 /** 416 * Fetch the next batch of stack frames. 417 * 418 * @param mode mode of stack walking 419 * @param anchor 420 * @param batchSize the batch size, max. number of elements to be filled in the frame buffers. 421 * @param startIndex start index of the frame buffers to be filled. 422 * @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY} 423 * or a {@link StackFrameInfo} (or derivative) array otherwise. 424 * 425 * @return the end index to the frame buffers 426 */ 427 private native int fetchStackFrames(long mode, long anchor, 428 int batchSize, int startIndex, 429 T[] frames); 430 } 431 432 /* 433 * This StackFrameTraverser supports {@link Stream} traversal. 434 * 435 * This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance. 436 */ 437 static class StackFrameTraverser<T> extends AbstractStackWalker<T, StackFrameInfo> 438 implements Spliterator<StackFrame> 439 { 440 static { 441 stackWalkImplClasses.add(StackFrameTraverser.class); 442 } 443 private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE; 444 445 final class StackFrameBuffer extends FrameBuffer<StackFrameInfo> { 446 private StackFrameInfo[] stackFrames; 447 StackFrameBuffer(int initialBatchSize) { 448 super(initialBatchSize); 449 450 this.stackFrames = new StackFrameInfo[initialBatchSize]; 451 for (int i = START_POS; i < initialBatchSize; i++) { 452 stackFrames[i] = new StackFrameInfo(walker); 453 } 454 } 455 456 @Override 457 StackFrameInfo[] frames() { 458 return stackFrames; 459 } 460 461 @Override 462 void resize(int startIndex, int elements) { 463 if (!isActive()) 464 throw new IllegalStateException("inactive frame buffer can't be resized"); 465 466 assert startIndex == START_POS : 467 "bad start index " + startIndex + " expected " + START_POS; 468 469 int size = startIndex+elements; 470 if (stackFrames.length < size) { 471 StackFrameInfo[] newFrames = new StackFrameInfo[size]; 472 // copy initial magic... 473 System.arraycopy(stackFrames, 0, newFrames, 0, startIndex); 474 stackFrames = newFrames; 475 } 476 for (int i = startIndex; i < size; i++) { 477 stackFrames[i] = new StackFrameInfo(walker); 478 } 479 currentBatchSize = size; 480 } 481 482 @Override 483 StackFrameInfo nextStackFrame() { 484 if (isEmpty()) { 485 throw new NoSuchElementException("origin=" + origin + " fence=" + fence); 486 } 487 488 StackFrameInfo frame = stackFrames[origin]; 489 origin++; 490 return frame; 491 } 492 493 @Override 494 final Class<?> at(int index) { 495 return stackFrames[index].declaringClass(); 496 } 497 } 498 499 final Function<? super Stream<StackFrame>, ? extends T> function; // callback 500 501 StackFrameTraverser(StackWalker walker, 502 Function<? super Stream<StackFrame>, ? extends T> function) { 503 this(walker, function, DEFAULT_MODE); 504 } 505 StackFrameTraverser(StackWalker walker, 506 Function<? super Stream<StackFrame>, ? extends T> function, 507 int mode) { 508 super(walker, mode); 509 this.function = function; 510 } 511 512 /** 513 * Returns next StackFrame object in the current batch of stack frames; 514 * or null if no more stack frame. 515 */ 516 StackFrame nextStackFrame() { 517 if (!hasNext()) { 518 return null; 519 } 520 521 StackFrameInfo frame = frameBuffer.nextStackFrame(); 522 depth++; 523 return frame; 524 } 525 526 @Override 527 protected T consumeFrames() { 528 checkState(OPEN); 529 Stream<StackFrame> stream = StreamSupport.stream(this, false); 530 if (function != null) { 531 return function.apply(stream); 532 } else 533 throw new UnsupportedOperationException(); 534 } 535 536 @Override 537 protected void initFrameBuffer() { 538 this.frameBuffer = new StackFrameBuffer(getNextBatchSize()); 539 } 540 541 @Override 542 protected int batchSize(int lastBatchFrameCount) { 543 if (lastBatchFrameCount == 0) { 544 // First batch, use estimateDepth if not exceed the large batch size 545 // and not too small 546 int initialBatchSize = Math.max(walker.estimateDepth(), SMALL_BATCH); 547 return Math.min(initialBatchSize, LARGE_BATCH_SIZE); 548 } else { 549 if (lastBatchFrameCount > BATCH_SIZE) { 550 return lastBatchFrameCount; 551 } else { 552 return Math.min(lastBatchFrameCount*2, BATCH_SIZE); 553 } 554 } 555 } 556 557 // ------- Implementation of Spliterator 558 559 @Override 560 public Spliterator<StackFrame> trySplit() { 561 return null; // ordered stream and do not allow to split 562 } 563 564 @Override 565 public long estimateSize() { 566 return maxDepth; 567 } 568 569 @Override 570 public int characteristics() { 571 return CHARACTERISTICS; 572 } 573 574 @Override 575 public void forEachRemaining(Consumer<? super StackFrame> action) { 576 checkState(OPEN); 577 for (int n = 0; n < maxDepth; n++) { 578 StackFrame frame = nextStackFrame(); 579 if (frame == null) break; 580 581 action.accept(frame); 582 } 583 } 584 585 @Override 586 public boolean tryAdvance(Consumer<? super StackFrame> action) { 587 checkState(OPEN); 588 589 int index = frameBuffer.getIndex(); 590 if (hasNext()) { 591 StackFrame frame = nextStackFrame(); 592 action.accept(frame); 593 if (isDebug) { 594 System.err.println("tryAdvance: " + index + " " + frame); 595 } 596 return true; 597 } 598 if (isDebug) { 599 System.err.println("tryAdvance: " + index + " NO element"); 600 } 601 return false; 602 } 603 } 604 605 /* 606 * CallerClassFinder is specialized to return Class<?> for each stack frame. 607 * StackFrame is not requested. 608 */ 609 static final class CallerClassFinder extends AbstractStackWalker<Integer, Class<?>> { 610 static { 611 stackWalkImplClasses.add(CallerClassFinder.class); 612 } 613 614 private Class<?> caller; 615 616 CallerClassFinder(StackWalker walker) { 617 super(walker, FILL_CLASS_REFS_ONLY); 618 assert (mode & FILL_CLASS_REFS_ONLY) == FILL_CLASS_REFS_ONLY 619 : "mode should contain FILL_CLASS_REFS_ONLY"; 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 throw new InternalError("Not jdk.internal.reflect.MethodAccessor: " + c.toString()); 986 } 987 // ## should filter all @Hidden frames? 988 return c == Method.class || 989 MethodAccessor.class.isAssignableFrom(c) || 990 c.getName().startsWith("java.lang.invoke.LambdaForm"); 991 } 992 993 }