package org.sef4j.callstack; import org.sef4j.callstack.CallStackElt.StackPopper; /** * a CallStack is a sub-list of a java thread Stack, but enriched with applicative info * <p/> * This class is managed from current thread local facade: LocalCallStack using push()/pop() on current thread. * <p/> * * There is NO exact mapping between java StackTraceElement and CallStackElt: * - only methodf with instrumented push-pop call to applicative "LocalCallStack" will maintain synchro between java stack and applicative stack * - when not creating applicative CallStackElt for a method => CallStackElt is a partial sub-list of java thread * - when creating extra "virtual" element => CallStackElt can be used to categorize calls, using parameter values * * <PRE> * Applicative CallStack Java Thread Stack * ----- * | * no sync --> | meth44() { .. } * +-------------+ no sync --> | meth43() { .. meth44(); ..} * | callElt Curr| <-- sync with push-pop --> | meth42() { .. meth43(); .. } * +-------------+ no sync --> | meth41() { .. meth42(); .. } * no sync --> | meth40() { .. meth41(); .. } * | * | * | * | meth24 { .. meth25(); .. } * +-------------+ | * | callElt 2 | <-- dummy Call(no sync) | * +-------------+ | * | meth23 { .. meth24(); .. } * | .. | | * | .. | | * | * +-------------+ | * | callElt 2 | <-- sync --> | * +-------------+ | * | * +-------------+ | * | callElt 1 | <-- sync --> | meth3() { .. meth4(); .. } * +-------------+ | meth2() { .. meth3(); .. } * | meth1() { .. meth2(); .. } * * </PRE> * * * <PRE> * <- "try { toPop=LocalCallStack().push()" * when entering new "method" : push new element on stack * \ * +-------------+ / * | callElt Curr| <-- curr stack position * +-------------+ \ * | / * | <- "} finally { toPop.close(); }" * | when exiting curr "method" : pop element on stack * | .. * | .. * +-------------+ * | callElt 2 | * +-------------+ * | callElt 1 | * +-------------+ * </PRE> * */ public class CallStack { private static final int DEFAULT_PREALLOC_STACK_LEN = 10; private static final int DEFAULT_ALLOC_INCR_STACK_LEN = 5; private CallStackElt curr; private CallStackElt[] stackElts; // ------------------------------------------------------------------------ public CallStack() { this.stackElts = new CallStackElt[1]; this.stackElts[0] = new CallStackElt(this, 0, null); reallocStackEltArray(DEFAULT_PREALLOC_STACK_LEN); this.curr = stackElts[0]; } // ------------------------------------------------------------------------ public CallStackElt curr() { return curr; } // internal // ------------------------------------------------------------------------ private void reallocStackEltArray(int stackLen) { CallStackElt[] prevStackElts = stackElts; CallStackElt[] newStackElts = new CallStackElt[stackLen]; System.arraycopy(prevStackElts, 0, newStackElts, 0, prevStackElts.length); for(int i = prevStackElts.length; i < stackLen; i++) { newStackElts[i] = new CallStackElt(this, i, newStackElts[i-1]); newStackElts[i - 1].pusher = new CallStackElt.StackPusher(newStackElts[i]); } this.stackElts = newStackElts; } /*pp*/ StackPopper doPush(CallStackElt pushedElt) { if (pushedElt.pusher == null) { reallocStackEltArray(this.stackElts.length + DEFAULT_ALLOC_INCR_STACK_LEN); } this.curr = pushedElt; pushedElt.onPush(); return pushedElt.popper; } /*pp*/ StackPopper doPushWithParentStartTime(CallStackElt pushedElt) { if (pushedElt.pusher == null) { reallocStackEltArray(this.stackElts.length + DEFAULT_ALLOC_INCR_STACK_LEN); } this.curr = pushedElt; pushedElt.onPushWithParentStartTime(); return pushedElt.popper; } /*pp*/ void doPop(CallStackElt poppedElt) { this.curr = poppedElt.getParentCallStackElt(); poppedElt.onPop(); } }