< prev index next >
jdk/src/java.base/share/classes/java/lang/StackStreamFactory.java
Print this page
@@ -22,26 +22,20 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
-import jdk.internal.misc.VM;
-
-import java.io.PrintStream;
import java.lang.StackWalker.Option;
import java.lang.StackWalker.StackFrame;
import java.lang.annotation.Native;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
-import java.util.Arrays;
-import java.util.EnumSet;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.Objects;
-import java.util.Optional;
import java.util.Set;
import java.util.Spliterator;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;
@@ -58,12 +52,11 @@
*
* AbstractStackWalker subclass is specialized for a specific kind of stack traversal
* to avoid overhead of Stream/Lambda
* 1. Support traversing Stream<StackFrame>
* 2. StackWalker::getCallerClass
- * 3. Throwable::init and Throwable::getStackTrace
- * 4. AccessControlContext getting ProtectionDomain
+ * 3. AccessControlContext getting ProtectionDomain
*/
final class StackStreamFactory {
private StackStreamFactory() {}
// Stack walk implementation classes to be excluded during stack walking
@@ -76,72 +69,60 @@
private static final int MIN_BATCH_SIZE = SMALL_BATCH;
// These flags must match the values maintained in the VM
@Native private static final int DEFAULT_MODE = 0x0;
@Native private static final int FILL_CLASS_REFS_ONLY = 0x2;
- @Native private static final int FILTER_FILL_IN_STACKTRACE = 0x10;
@Native private static final int SHOW_HIDDEN_FRAMES = 0x20; // LambdaForms are hidden by the VM
@Native private static final int FILL_LIVE_STACK_FRAMES = 0x100;
-
/*
* For Throwable to use StackWalker, set useNewThrowable to true.
* Performance work and extensive testing is needed to replace the
* VM built-in backtrace filled in Throwable with the StackWalker.
*/
- final static boolean useNewThrowable = getProperty("stackwalk.newThrowable", false);
final static boolean isDebug = getProperty("stackwalk.debug", false);
static <T> StackFrameTraverser<T>
makeStackTraverser(StackWalker walker, Function<? super Stream<StackFrame>, ? extends T> function)
{
if (walker.hasLocalsOperandsOption())
- return new LiveStackInfoTraverser<T>(walker, function);
+ return new LiveStackInfoTraverser<>(walker, function);
else
- return new StackFrameTraverser<T>(walker, function);
+ return new StackFrameTraverser<>(walker, function);
}
/**
* Gets a stack stream to find caller class.
*/
static CallerClassFinder makeCallerFinder(StackWalker walker) {
return new CallerClassFinder(walker);
}
- static boolean useStackTrace(Throwable t) {
- if (t instanceof VirtualMachineError)
- return false;
-
- return VM.isBooted() && StackStreamFactory.useNewThrowable;
- }
-
- /*
- * This should only be used by Throwable::<init>.
- */
- static StackTrace makeStackTrace(Throwable ex) {
- return StackTrace.dump(ex);
- }
-
- /*
- * This creates StackTrace for Thread::dumpThread to use.
- */
- static StackTrace makeStackTrace() {
- return StackTrace.dump();
- }
-
enum WalkerState {
NEW, // the stream is new and stack walking has not started
OPEN, // the stream is open when it is being traversed.
CLOSED; // the stream is closed when the stack walking is done
}
- static abstract class AbstractStackWalker<T> {
+ /**
+ * Subclass of AbstractStackWalker implements a specific stack walking logic.
+ * It needs to set up the frame buffer and stack walking mode.
+ *
+ * It initiates the VM stack walking via the callStackWalk method that serves
+ * as the anchored frame and VM will call up to AbstractStackWalker::doStackWalk.
+ *
+ * @param <R> the type of the result returned from stack walking
+ * @param <T> the type of the data gathered for each frame.
+ * For example, StackFrameInfo for StackWalker::walk or
+ * Class<?> for StackWalker::getCallerClass
+ */
+ static abstract class AbstractStackWalker<R, T> {
protected final StackWalker walker;
protected final Thread thread;
protected final int maxDepth;
protected final long mode;
protected int depth; // traversed stack depth
- protected FrameBuffer frameBuffer; // buffer for VM to fill in
+ protected FrameBuffer<? extends T> frameBuffer;
protected long anchor;
// buffers to fill in stack frame information
protected AbstractStackWalker(StackWalker walker, int mode) {
this(walker, mode, Integer.MAX_VALUE);
@@ -155,20 +136,17 @@
}
private int toStackWalkMode(StackWalker walker, int mode) {
int newMode = mode;
if (walker.hasOption(Option.SHOW_HIDDEN_FRAMES) &&
- !fillCallerClassOnly(newMode) /* don't show hidden frames for getCallerClass */)
+ (mode & FILL_CLASS_REFS_ONLY) != FILL_CLASS_REFS_ONLY)
newMode |= SHOW_HIDDEN_FRAMES;
if (walker.hasLocalsOperandsOption())
newMode |= FILL_LIVE_STACK_FRAMES;
return newMode;
}
- private boolean fillCallerClassOnly(int mode) {
- return (mode|FILL_CLASS_REFS_ONLY) != FILL_CLASS_REFS_ONLY;
- }
/**
* A callback method to consume the stack frames. This method is invoked
* once stack walking begins (i.e. it is only invoked when walkFrames is called).
*
* Each specialized AbstractStackWalker subclass implements the consumeFrames method
@@ -177,11 +155,11 @@
* 2. reuse or expand the allocated buffers
* 3. create specialized StackFrame objects
*
* @return the number of consumed frames
*/
- protected abstract T consumeFrames();
+ protected abstract R consumeFrames();
/**
* Initialize FrameBuffer. Subclass should implement this method to
* create its custom frame buffers.
*/
@@ -250,11 +228,11 @@
/*
* Walks stack frames until {@link #consumeFrames} is done consuming
* the frames it is interested in.
*/
- final T walk() {
+ final R walk() {
checkState(NEW);
try {
// VM will need to stablize the stack before walking. It will invoke
// the AbstractStackWalker::doStackWalk method once it fetches the first batch.
// the callback will be invoked within the scope of the callStackWalk frame.
@@ -316,11 +294,11 @@
System.err.format("doStackWalk: skip %d start %d end %d%n",
skipFrames, bufStartIndex, bufEndIndex);
}
this.anchor = anchor; // set anchor for this bulk stack frame traversal
- frameBuffer.setBatch(bufStartIndex, bufEndIndex);
+ frameBuffer.setBatch(depth, bufStartIndex, bufEndIndex);
// traverse all frames and perform the action on the stack frames, if specified
return consumeFrames();
}
@@ -379,19 +357,18 @@
* stack walking function on each stack frame.
*
* If all fetched stack frames are traversed, AbstractStackWalker::fetchStackFrames will
* fetch the next batch of stack frames to continue.
*/
- private T beginStackWalk() {
+ private R beginStackWalk() {
// initialize buffers for VM to fill the stack frame info
initFrameBuffer();
return callStackWalk(mode, 0,
frameBuffer.curBatchFrameCount(),
frameBuffer.startIndex(),
- frameBuffer.classes,
- frameBuffer.stackFrames);
+ frameBuffer.frames());
}
/*
* Fetches stack frames.
*
@@ -402,275 +379,122 @@
int startIndex = frameBuffer.startIndex();
frameBuffer.resize(startIndex, batchSize);
int endIndex = fetchStackFrames(mode, anchor, batchSize,
startIndex,
- frameBuffer.classes,
- frameBuffer.stackFrames);
+ frameBuffer.frames());
if (isDebug) {
System.out.format(" more stack walk requesting %d got %d to %d frames%n",
batchSize, frameBuffer.startIndex(), endIndex);
}
int numFrames = endIndex - startIndex;
if (numFrames == 0) {
frameBuffer.freeze(); // done stack walking
} else {
- frameBuffer.setBatch(startIndex, endIndex);
+ frameBuffer.setBatch(depth, startIndex, endIndex);
}
return numFrames;
}
/**
* Begins stack walking. This method anchors this frame and invokes
- * AbstractStackWalker::doStackWalk after fetching the firt batch of stack frames.
+ * AbstractStackWalker::doStackWalk after fetching the first batch of stack frames.
*
* @param mode mode of stack walking
* @param skipframes number of frames to be skipped before filling the frame buffer.
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
* @param startIndex start index of the frame buffers to be filled.
- * @param classes Classes buffer of the stack frames
- * @param frames StackFrame buffer, or null
+ * @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY}
+ * or a {@link StackFrameInfo} (or derivative) array otherwise.
* @return Result of AbstractStackWalker::doStackWalk
*/
- private native T callStackWalk(long mode, int skipframes,
+ private native R callStackWalk(long mode, int skipframes,
int batchSize, int startIndex,
- Class<?>[] classes,
- StackFrame[] frames);
+ T[] frames);
/**
* Fetch the next batch of stack frames.
*
* @param mode mode of stack walking
* @param anchor
* @param batchSize the batch size, max. number of elements to be filled in the frame buffers.
* @param startIndex start index of the frame buffers to be filled.
- * @param classes Classes buffer of the stack frames
- * @param frames StackFrame buffer, or null
+ * @param frames Either a Class<?> array, if mode is {@link #FILL_CLASS_REFS_ONLY}
+ * or a {@link StackFrameInfo} (or derivative) array otherwise.
*
* @return the end index to the frame buffers
*/
private native int fetchStackFrames(long mode, long anchor,
int batchSize, int startIndex,
- Class<?>[] classes,
- StackFrame[] frames);
-
-
- /*
- * Frame buffer
- *
- * Each specialized AbstractStackWalker subclass may subclass the FrameBuffer.
- */
- class FrameBuffer {
- static final int START_POS = 2; // 0th and 1st elements are reserved
-
- // buffers for VM to fill stack frame info
- int currentBatchSize; // current batch size
- Class<?>[] classes; // caller class for fast path
-
- StackFrame[] stackFrames;
-
- int origin; // index to the current traversed stack frame
- int fence; // index to the last frame in the current batch
-
- FrameBuffer(int initialBatchSize) {
- if (initialBatchSize < MIN_BATCH_SIZE) {
- throw new IllegalArgumentException(initialBatchSize + " < minimum batch size: " + MIN_BATCH_SIZE);
- }
- this.origin = START_POS;
- this.fence = 0;
- this.currentBatchSize = initialBatchSize;
- this.classes = new Class<?>[currentBatchSize];
- }
-
- int curBatchFrameCount() {
- return currentBatchSize-START_POS;
- }
-
- /*
- * Tests if this frame buffer is empty. All frames are fetched.
- */
- final boolean isEmpty() {
- return origin >= fence || (origin == START_POS && fence == 0);
- }
-
- /*
- * Freezes this frame buffer. The stack stream source is done fetching.
- */
- final void freeze() {
- origin = 0;
- fence = 0;
- }
-
- /*
- * Tests if this frame buffer is active. It is inactive when
- * it is done for traversal. All stack frames have been traversed.
- */
- final boolean isActive() {
- return origin > 0 && (fence == 0 || origin < fence || fence == currentBatchSize);
- }
-
- /**
- * Gets the class at the current frame and move to the next frame.
- */
- final Class<?> next() {
- if (isEmpty()) {
- throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
- }
- Class<?> c = classes[origin++];
- if (isDebug) {
- int index = origin-1;
- System.out.format(" next frame at %d: %s (origin %d fence %d)%n", index,
- Objects.toString(c), index, fence);
- }
- return c;
- }
-
- /**
- * Gets the class at the current frame.
- */
- final Class<?> get() {
- if (isEmpty()) {
- throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
- }
- return classes[origin];
- }
-
- /*
- * Returns the index of the current frame.
- */
- final int getIndex() {
- return origin;
- }
-
- /*
- * Set the start and end index of a new batch of stack frames that have
- * been filled in this frame buffer.
- */
- final void setBatch(int startIndex, int endIndex) {
- if (startIndex <= 0 || endIndex <= 0)
- throw new IllegalArgumentException("startIndex=" + startIndex + " endIndex=" + endIndex);
-
- this.origin = startIndex;
- this.fence = endIndex;
- if (depth == 0 && fence > 0) {
- // filter the frames due to the stack stream implementation
- for (int i = START_POS; i < fence; i++) {
- Class<?> c = classes[i];
- if (isDebug) System.err.format(" frame %d: %s%n", i, c);
- if (filterStackWalkImpl(c)) {
- origin++;
- } else {
- break;
- }
- }
- }
- }
-
- /*
- * Checks if the origin is the expected start index.
- */
- final void check(int skipFrames) {
- int index = skipFrames + START_POS;
- if (origin != index) {
- // stack walk must continue with the previous frame depth
- throw new IllegalStateException("origin " + origin + " != " + index);
- }
- }
-
- // ------ subclass may override the following methods -------
- /**
- * Resizes the buffers for VM to fill in the next batch of stack frames.
- * The next batch will start at the given startIndex with the maximum number
- * of elements.
- *
- * <p> Subclass may override this method to manage the allocated buffers.
- *
- * @param startIndex the start index for the first frame of the next batch to fill in.
- * @param elements the number of elements for the next batch to fill in.
- *
- */
- void resize(int startIndex, int elements) {
- if (!isActive())
- throw new IllegalStateException("inactive frame buffer can't be resized");
-
- int size = startIndex+elements;
- if (classes.length < size) {
- // copy the elements in classes array to the newly allocated one.
- // classes[0] is a Thread object
- Class<?>[] prev = classes;
- classes = new Class<?>[size];
- System.arraycopy(prev, 0, classes, 0, START_POS);
- }
- currentBatchSize = size;
- }
-
- /*
- * Returns the start index for this frame buffer is refilled.
- *
- * This implementation reuses the allocated buffer for the next batch
- * of stack frames. For subclass to retain the fetched stack frames,
- * it should override this method to return the index at which the frame
- * should be filled in for the next batch.
- */
- int startIndex() {
- return START_POS;
- }
-
- /**
- * Returns next StackFrame object in the current batch of stack frames
- */
- StackFrame nextStackFrame() {
- throw new InternalError("should not reach here");
- }
- }
+ T[] frames);
}
/*
* This StackFrameTraverser supports {@link Stream} traversal.
*
* This class implements Spliterator::forEachRemaining and Spliterator::tryAdvance.
*/
- static class StackFrameTraverser<T> extends AbstractStackWalker<T>
+ static class StackFrameTraverser<T> extends AbstractStackWalker<T, StackFrameInfo>
implements Spliterator<StackFrame>
{
static {
stackWalkImplClasses.add(StackFrameTraverser.class);
}
private static final int CHARACTERISTICS = Spliterator.ORDERED | Spliterator.IMMUTABLE;
- class Buffer extends FrameBuffer {
- Buffer(int initialBatchSize) {
+
+ final class StackFrameBuffer extends FrameBuffer<StackFrameInfo> {
+ private StackFrameInfo[] stackFrames;
+ StackFrameBuffer(int initialBatchSize) {
super(initialBatchSize);
- this.stackFrames = new StackFrame[initialBatchSize];
+ this.stackFrames = new StackFrameInfo[initialBatchSize];
for (int i = START_POS; i < initialBatchSize; i++) {
stackFrames[i] = new StackFrameInfo(walker);
}
}
@Override
+ StackFrameInfo[] frames() {
+ return stackFrames;
+ }
+
+ @Override
void resize(int startIndex, int elements) {
- super.resize(startIndex, elements);
+ if (!isActive())
+ throw new IllegalStateException("inactive frame buffer can't be resized");
+
+ assert startIndex == START_POS :
+ "bad start index " + startIndex + " expected " + START_POS;
int size = startIndex+elements;
if (stackFrames.length < size) {
- stackFrames = new StackFrame[size];
+ StackFrameInfo[] newFrames = new StackFrameInfo[size];
+ // copy initial magic...
+ System.arraycopy(stackFrames, 0, newFrames, 0, startIndex);
+ stackFrames = newFrames;
}
- for (int i = startIndex(); i < size; i++) {
+ for (int i = startIndex; i < size; i++) {
stackFrames[i] = new StackFrameInfo(walker);
}
+ currentBatchSize = size;
}
@Override
- StackFrame nextStackFrame() {
+ StackFrameInfo nextStackFrame() {
if (isEmpty()) {
throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
}
- StackFrame frame = stackFrames[origin];
+ StackFrameInfo frame = stackFrames[origin];
origin++;
return frame;
}
+
+ @Override
+ final Class<?> at(int index) {
+ return stackFrames[index].declaringClass;
+ }
}
final Function<? super Stream<StackFrame>, ? extends T> function; // callback
StackFrameTraverser(StackWalker walker,
@@ -691,11 +515,11 @@
StackFrame nextStackFrame() {
if (!hasNext()) {
return null;
}
- StackFrame frame = frameBuffer.nextStackFrame();
+ StackFrameInfo frame = frameBuffer.nextStackFrame();
depth++;
return frame;
}
@Override
@@ -708,11 +532,11 @@
throw new UnsupportedOperationException();
}
@Override
protected void initFrameBuffer() {
- this.frameBuffer = new Buffer(getNextBatchSize());
+ this.frameBuffer = new StackFrameBuffer(getNextBatchSize());
}
@Override
protected int batchSize(int lastBatchFrameCount) {
if (lastBatchFrameCount == 0) {
@@ -779,19 +603,67 @@
/*
* CallerClassFinder is specialized to return Class<?> for each stack frame.
* StackFrame is not requested.
*/
- static class CallerClassFinder extends AbstractStackWalker<Integer> {
+ static final class CallerClassFinder extends AbstractStackWalker<Integer, Class<?>> {
static {
stackWalkImplClasses.add(CallerClassFinder.class);
}
private Class<?> caller;
CallerClassFinder(StackWalker walker) {
super(walker, FILL_CLASS_REFS_ONLY);
+ assert (mode & FILL_CLASS_REFS_ONLY) == FILL_CLASS_REFS_ONLY
+ : "mode should contain FILL_CLASS_REFS_ONLY";
+ }
+
+ final class ClassBuffer extends FrameBuffer<Class<?>> {
+ Class<?>[] classes; // caller class for fast path
+ ClassBuffer(int batchSize) {
+ super(batchSize);
+ classes = new Class<?>[batchSize];
+ }
+
+ @Override
+ Class<?>[] frames() { return classes;}
+
+ @Override
+ final Class<?> at(int index) { return classes[index];}
+
+
+ // ------ subclass may override the following methods -------
+ /**
+ * Resizes the buffers for VM to fill in the next batch of stack frames.
+ * The next batch will start at the given startIndex with the maximum number
+ * of elements.
+ *
+ * <p> Subclass may override this method to manage the allocated buffers.
+ *
+ * @param startIndex the start index for the first frame of the next batch to fill in.
+ * @param elements the number of elements for the next batch to fill in.
+ *
+ */
+ @Override
+ void resize(int startIndex, int elements) {
+ if (!isActive())
+ throw new IllegalStateException("inactive frame buffer can't be resized");
+
+ assert startIndex == START_POS :
+ "bad start index " + startIndex + " expected " + START_POS;
+
+ int size = startIndex+elements;
+ if (classes.length < size) {
+ // copy the elements in classes array to the newly allocated one.
+ // classes[0] is a Thread object
+ Class<?>[] prev = classes;
+ classes = new Class<?>[size];
+ System.arraycopy(prev, 0, classes, 0, startIndex);
+ }
+ currentBatchSize = size;
+ }
}
Class<?> findCaller() {
walk();
return caller;
@@ -808,19 +680,19 @@
// 2: caller class
while (n < 2 && (caller = nextFrame()) != null) {
if (isMethodHandleFrame(caller)) continue;
frames[n++] = caller;
}
-
- if (frames[1] == null)
+ if (frames[1] == null) {
throw new IllegalStateException("no caller frame");
+ }
return n;
}
@Override
protected void initFrameBuffer() {
- this.frameBuffer = new FrameBuffer(getNextBatchSize());
+ this.frameBuffer = new ClassBuffer(getNextBatchSize());
}
@Override
protected int batchSize(int lastBatchFrameCount) {
return MIN_BATCH_SIZE;
@@ -830,229 +702,254 @@
protected int getNextBatchSize() {
return MIN_BATCH_SIZE;
}
}
- /*
- * StackTrace caches all frames in the buffer. StackTraceElements are
- * created lazily when Throwable::getStackTrace is called.
- */
- static class StackTrace extends AbstractStackWalker<Integer> {
+ static final class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> {
static {
- stackWalkImplClasses.add(StackTrace.class);
+ stackWalkImplClasses.add(LiveStackInfoTraverser.class);
}
-
- class GrowableBuffer extends FrameBuffer {
- GrowableBuffer(int initialBatchSize) {
+ // VM will fill in all method info and live stack info directly in StackFrameInfo
+ final class LiveStackFrameBuffer extends FrameBuffer<LiveStackFrameInfo> {
+ private LiveStackFrameInfo[] stackFrames;
+ LiveStackFrameBuffer(int initialBatchSize) {
super(initialBatchSize);
-
- this.stackFrames = new StackFrame[initialBatchSize];
+ this.stackFrames = new LiveStackFrameInfo[initialBatchSize];
for (int i = START_POS; i < initialBatchSize; i++) {
- stackFrames[i] = new StackFrameInfo(walker);
+ stackFrames[i] = new LiveStackFrameInfo(walker);
}
}
- /*
- * Returns the next index to fill
- */
@Override
- int startIndex() {
- return origin;
+ LiveStackFrameInfo[] frames() {
+ return stackFrames;
}
- /**
- * Initialize the buffers for VM to fill in the stack frame information.
- * The next batch will start at the given startIndex to
- * the length of the buffer.
- */
@Override
void resize(int startIndex, int elements) {
- // Expand the frame buffer.
- // Do not call super.resize that will reuse the filled elements
- // in this frame buffer
- int size = startIndex+elements;
- if (classes.length < size) {
- // resize the frame buffer
- classes = Arrays.copyOf(classes, size);
- stackFrames = Arrays.copyOf(stackFrames, size);
- }
- for (int i = startIndex; i < size; i++) {
- stackFrames[i] = new StackFrameInfo(walker);
- }
- currentBatchSize = size;
+ if (!isActive()) {
+ throw new IllegalStateException("inactive frame buffer can't be resized");
}
+ assert startIndex == START_POS :
+ "bad start index " + startIndex + " expected " + START_POS;
- StackTraceElement get(int index) {
- return new StackTraceElement(classes[index].getName(), "unknown", null, -1);
+ int size = startIndex + elements;
+ if (stackFrames.length < size) {
+ LiveStackFrameInfo[] newFrames = new LiveStackFrameInfo[size];
+ // copy initial magic...
+ System.arraycopy(stackFrames, 0, newFrames, 0, startIndex);
+ stackFrames = newFrames;
}
- /**
- * Returns an array of StackTraceElement for all stack frames cached in
- * this StackTrace object.
- * <p>
- * This method is intended for Throwable::getOurStackTrace use only.
- */
- StackTraceElement[] toStackTraceElements() {
- int startIndex = START_POS;
- for (int i = startIndex; i < classes.length; i++) {
- if (classes[i] != null && filterStackWalkImpl(classes[i])) {
- startIndex++;
- } else {
- break;
- }
+ for (int i = startIndex(); i < size; i++) {
+ stackFrames[i] = new LiveStackFrameInfo(walker);
}
- // VM fills in the method name, filename, line number info
- StackFrameInfo.fillInStackFrames(0, stackFrames, startIndex, startIndex + depth);
+ currentBatchSize = size;
+ }
- StackTraceElement[] stes = new StackTraceElement[depth];
- for (int i = startIndex, j = 0; i < classes.length && j < depth; i++, j++) {
- if (isDebug) {
- System.err.println("StackFrame: " + i + " " + stackFrames[i]);
+ @Override
+ LiveStackFrameInfo nextStackFrame() {
+ if (isEmpty()) {
+ throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
}
- stes[j] = stackFrames[i].toStackTraceElement();
+
+ LiveStackFrameInfo frame = stackFrames[origin];
+ origin++;
+ return frame;
}
- return stes;
+
+ @Override
+ final Class<?> at(int index) {
+ return stackFrames[index].declaringClass;
}
}
- private static final int MAX_STACK_FRAMES = 1024;
- private static final StackWalker STACKTRACE_WALKER =
- StackWalker.newInstanceNoCheck(EnumSet.of(Option.SHOW_REFLECT_FRAMES));
-
- private StackTraceElement[] stes;
- static StackTrace dump() {
- return new StackTrace();
+ LiveStackInfoTraverser(StackWalker walker,
+ Function<? super Stream<StackFrame>, ? extends T> function) {
+ super(walker, function, DEFAULT_MODE);
}
- static StackTrace dump(Throwable ex) {
- return new StackTrace(ex);
+ @Override
+ protected void initFrameBuffer() {
+ this.frameBuffer = new LiveStackFrameBuffer(getNextBatchSize());
}
-
- private StackTrace() {
- this(STACKTRACE_WALKER, DEFAULT_MODE);
}
/*
- * Throwable::fillInStackTrace and <init> of Throwable and subclasses
- * are filtered in the VM.
+ * Frame buffer
+ *
+ * Each specialized AbstractStackWalker subclass may subclass the FrameBuffer.
*/
- private StackTrace(Throwable ex) {
- this(STACKTRACE_WALKER, FILTER_FILL_IN_STACKTRACE); // skip Throwable::init frames
- if (isDebug) {
- System.err.println("dump stack for " + ex.getClass().getName());
- }
- }
-
- StackTrace(StackWalker walker, int mode) {
- super(walker, mode, MAX_STACK_FRAMES);
+ static abstract class FrameBuffer<F> {
+ static final int START_POS = 2; // 0th and 1st elements are reserved
- // snapshot the stack trace
- walk();
- }
+ // buffers for VM to fill stack frame info
+ int currentBatchSize; // current batch size
+ int origin; // index to the current traversed stack frame
+ int fence; // index to the last frame in the current batch
- @Override
- protected Integer consumeFrames() {
- // traverse all frames and perform the action on the stack frames, if specified
- int n = 0;
- while (n < maxDepth && nextFrame() != null) {
- n++;
+ FrameBuffer(int initialBatchSize) {
+ if (initialBatchSize < MIN_BATCH_SIZE) {
+ throw new IllegalArgumentException(initialBatchSize +
+ " < minimum batch size: " + MIN_BATCH_SIZE);
}
- return n;
+ this.origin = START_POS;
+ this.fence = 0;
+ this.currentBatchSize = initialBatchSize;
}
- @Override
- protected void initFrameBuffer() {
- this.frameBuffer = new GrowableBuffer(getNextBatchSize());
- }
+ /**
+ * Returns an array of frames that may be used to store frame objects
+ * when walking the stack.
+ *
+ * May be an array of {@code Class<?>} if the {@code AbstractStackWalker}
+ * mode is {@link #FILL_CLASS_REFS_ONLY}, or an array of
+ * {@link StackFrameInfo} (or derivative) array otherwise.
+ *
+ * @return An array of frames that may be used to store frame objects
+ * when walking the stack. Must not be null.
+ */
+ abstract F[] frames(); // must not return null
- // TODO: implement better heuristic
- @Override
- protected int batchSize(int lastBatchFrameCount) {
- // chunk size of VM backtrace is 32
- return lastBatchFrameCount == 0 ? 32 : 32;
- }
+ /**
+ * Resizes the buffers for VM to fill in the next batch of stack frames.
+ * The next batch will start at the given startIndex with the maximum number
+ * of elements.
+ *
+ * <p> Subclass may override this method to manage the allocated buffers.
+ *
+ * @param startIndex the start index for the first frame of the next batch to fill in.
+ * @param elements the number of elements for the next batch to fill in.
+ *
+ */
+ abstract void resize(int startIndex, int elements);
/**
- * Returns an array of StackTraceElement for all stack frames cached in
- * this StackTrace object.
- * <p>
- * This method is intended for Throwable::getOurStackTrace use only.
+ * Return the class at the given position in the current batch.
+ * @param index the position of the frame.
+ * @return the class at the given position in the current batch.
*/
- synchronized StackTraceElement[] getStackTraceElements() {
- if (stes == null) {
- stes = ((GrowableBuffer) frameBuffer).toStackTraceElements();
- // release the frameBuffer memory
- frameBuffer = null;
- }
- return stes;
- }
+ abstract Class<?> at(int index);
+
+ // ------ subclass may override the following methods -------
/*
- * Prints stack trace to the given PrintStream.
+ * Returns the start index for this frame buffer is refilled.
*
- * Further implementation could skip creating StackTraceElement objects
- * print directly to the PrintStream.
+ * This implementation reuses the allocated buffer for the next batch
+ * of stack frames. For subclass to retain the fetched stack frames,
+ * it should override this method to return the index at which the frame
+ * should be filled in for the next batch.
*/
- void printStackTrace(PrintStream s) {
- StackTraceElement[] stes = getStackTraceElements();
- synchronized (s) {
- s.println("Stack trace");
- for (StackTraceElement traceElement : stes)
- s.println("\tat " + traceElement);
- }
- }
+ int startIndex() {
+ return START_POS;
}
- static class LiveStackInfoTraverser<T> extends StackFrameTraverser<T> {
- static {
- stackWalkImplClasses.add(LiveStackInfoTraverser.class);
+ /**
+ * Returns next StackFrame object in the current batch of stack frames
+ */
+ F nextStackFrame() {
+ throw new InternalError("should not reach here");
}
- // VM will fill in all method info and live stack info directly in StackFrameInfo
- class Buffer extends FrameBuffer {
- Buffer(int initialBatchSize) {
- super(initialBatchSize);
- this.stackFrames = new StackFrame[initialBatchSize];
- for (int i = START_POS; i < initialBatchSize; i++) {
- stackFrames[i] = new LiveStackFrameInfo(walker);
+
+ // ------ FrameBuffer implementation ------
+
+ final int curBatchFrameCount() {
+ return currentBatchSize-START_POS;
}
+
+ /*
+ * Tests if this frame buffer is empty. All frames are fetched.
+ */
+ final boolean isEmpty() {
+ return origin >= fence || (origin == START_POS && fence == 0);
}
- @Override
- void resize(int startIndex, int elements) {
- super.resize(startIndex, elements);
- int size = startIndex + elements;
+ /*
+ * Freezes this frame buffer. The stack stream source is done fetching.
+ */
+ final void freeze() {
+ origin = 0;
+ fence = 0;
+ }
- if (stackFrames.length < size) {
- this.stackFrames = new StackFrame[size];
+ /*
+ * Tests if this frame buffer is active. It is inactive when
+ * it is done for traversal. All stack frames have been traversed.
+ */
+ final boolean isActive() {
+ return origin > 0 && (fence == 0 || origin < fence || fence == currentBatchSize);
}
- for (int i = startIndex(); i < size; i++) {
- stackFrames[i] = new LiveStackFrameInfo(walker);
+ /**
+ * Gets the class at the current frame and move to the next frame.
+ */
+ final Class<?> next() {
+ if (isEmpty()) {
+ throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
}
+ Class<?> c = at(origin);
+ origin++;
+ if (isDebug) {
+ int index = origin-1;
+ System.out.format(" next frame at %d: %s (origin %d fence %d)%n", index,
+ Objects.toString(c), index, fence);
+ }
+ return c;
}
- @Override
- StackFrame nextStackFrame() {
+ /**
+ * Gets the class at the current frame.
+ */
+ final Class<?> get() {
if (isEmpty()) {
throw new NoSuchElementException("origin=" + origin + " fence=" + fence);
}
+ return at(origin);
+ }
+
+ /*
+ * Returns the index of the current frame.
+ */
+ final int getIndex() {
+ return origin;
+ }
+
+ /*
+ * Set the start and end index of a new batch of stack frames that have
+ * been filled in this frame buffer.
+ */
+ final void setBatch(int depth, int startIndex, int endIndex) {
+ if (startIndex <= 0 || endIndex <= 0)
+ throw new IllegalArgumentException("startIndex=" + startIndex + " endIndex=" + endIndex);
- StackFrame frame = stackFrames[origin];
+ this.origin = startIndex;
+ this.fence = endIndex;
+ if (depth == 0 && fence > 0) {
+ // filter the frames due to the stack stream implementation
+ for (int i = START_POS; i < fence; i++) {
+ Class<?> c = at(i);
+ if (isDebug) System.err.format(" frame %d: %s%n", i, c);
+ if (filterStackWalkImpl(c)) {
origin++;
- return frame;
+ } else {
+ break;
+ }
}
}
-
- LiveStackInfoTraverser(StackWalker walker,
- Function<? super Stream<StackFrame>, ? extends T> function) {
- super(walker, function, DEFAULT_MODE);
}
- @Override
- protected void initFrameBuffer() {
- this.frameBuffer = new Buffer(getNextBatchSize());
+ /*
+ * Checks if the origin is the expected start index.
+ */
+ final void check(int skipFrames) {
+ int index = skipFrames + START_POS;
+ if (origin != index) {
+ // stack walk must continue with the previous frame depth
+ throw new IllegalStateException("origin " + origin + " != " + index);
+ }
}
}
private static native boolean checkStackWalkModes();
< prev index next >