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