2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package gov.nasa.jpf.vm;
20 import gov.nasa.jpf.vm.bytecode.ReturnInstruction;
21 import gov.nasa.jpf.Config;
22 import gov.nasa.jpf.JPF;
23 import gov.nasa.jpf.JPFException;
24 import gov.nasa.jpf.SystemAttribute;
25 import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
26 import gov.nasa.jpf.jvm.bytecode.INVOKESTATIC;
27 import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
28 import gov.nasa.jpf.util.HashData;
29 import gov.nasa.jpf.util.IntVector;
30 import gov.nasa.jpf.util.JPFLogger;
31 import gov.nasa.jpf.util.Predicate;
32 import gov.nasa.jpf.util.StringSetMatcher;
33 import gov.nasa.jpf.vm.choice.BreakGenerator;
35 import java.io.PrintWriter;
37 import java.util.ArrayList;
38 import java.util.Collections;
39 import java.util.HashMap;
40 import java.util.Iterator;
41 import java.util.LinkedList;
42 import java.util.List;
44 import java.util.NoSuchElementException;
45 import java.util.logging.Level;
49 * Represents a thread. It contains the state of the thread, static
50 * information about the thread, and the stack frames.
51 * Race detection and lock order also store some information
52 * in this data structure.
54 * Note that we preserve identities according to their associated java.lang.Thread object
55 * (oref). This esp. means along the same path, a ThreadInfo reference
58 * <2do> remove EXECUTENATIVE,INVOKESTATIC .bytecode dependencies
60 public class ThreadInfo extends InfoObject
61 implements Iterable<StackFrame>, Comparable<ThreadInfo>, Restorable<ThreadInfo> {
63 static JPFLogger log = JPF.getLogger("gov.nasa.jpf.vm.ThreadInfo");
64 static int[] emptyLockRefs = new int[0];
66 //--- our internal thread states
68 NEW, // means created but not yet started
70 BLOCKED, // waiting to acquire a lock
71 UNBLOCKED, // was BLOCKED but can acquire the lock now
72 WAITING, // waiting to be notified
74 NOTIFIED, // was WAITING and got notified, but is still blocked
75 INTERRUPTED, // was WAITING and got interrupted
76 TIMEDOUT, // was TIMEOUT_WAITING and timed out
81 static final int[] emptyRefArray = new int[0];
82 static final String MAIN_NAME = "main";
85 static ThreadInfo currentThread;
87 protected class StackIterator implements Iterator<StackFrame> {
88 StackFrame frame = top;
91 public boolean hasNext() {
96 public StackFrame next() {
98 StackFrame ret = frame;
99 frame = frame.getPrevious();
103 throw new NoSuchElementException();
108 public void remove() {
109 throw new UnsupportedOperationException("can't remove StackFrames");
113 protected class InvokedStackIterator extends StackIterator implements Iterator<StackFrame> {
114 InvokedStackIterator() {
115 frame = getLastInvokedStackFrame();
119 public StackFrame next() {
121 StackFrame ret = frame;
123 for (StackFrame f=ret.getPrevious(); f != null; f = f.getPrevious()){
124 if (!f.isDirectCallFrame()){
132 throw new NoSuchElementException();
138 //--- instance fields
140 // transient, not state stored. This is reset when backtracking or starting a new transition
141 protected ExceptionInfo pendingException;
143 // state managed data that is copy-on-first-write
144 protected ThreadData threadData;
147 //<2do> Hmm, why are these not in ThreadData?
148 // the top stack frame
149 protected StackFrame top = null;
151 // the current stack depth (number of frames)
152 protected int stackDepth;
155 // something that tells the ThreadList how to look this up efficiently (e.g. index)
156 // note - this is for internal purposes only, there is no public accessor
157 // (we don't want to expose/hardwire ThreadList implementation)
158 // note also that the ThreadList is allowed to move this thread around, in which
159 // case update is the ThreadLists responsibility
165 // we need this mostly for getting our SystemClassLoader
166 protected ApplicationContext appCtx;
168 // search global id, which is the basis for canonical order of threads
171 protected int objRef; // the java.lang.Thread object reference
172 protected ClassInfo ci; // the classinfo associated with the thread object
173 protected int targetRef; // the associated java.lang.Runnable
175 // which attributes are stored/restored
176 static final int ATTR_STORE_MASK = 0x0000ffff;
178 //--- the transient (un(re)stored) attributes
179 static final int ATTR_DATA_CHANGED = 0x10000;
180 static final int ATTR_STACK_CHANGED = 0x20000;
182 // allow CG in next re-exec
183 static final int ATTR_ENABLE_EMPTY_TRANSITION = 0x4000;
185 // don't call insn.execute()
186 static final int ATTR_SKIP_INSN_EXEC = 0x100000;
188 // don't store insn execution as transition step
189 static final int ATTR_SKIP_INSN_LOG = 0x200000;
192 static final int ATTR_ATTRIBUTE_CHANGED = 0x80000000;
194 //--- state stored/restored attributes
195 // this is a typical "orthogonal" thread state we have to remember, but
196 // that should not affect any locking, blocking, notifying or such
197 static final int ATTR_STOPPED = 0x0001;
200 static final int ATTR_ANY_CHANGED = (ATTR_DATA_CHANGED | ATTR_STACK_CHANGED | ATTR_ATTRIBUTE_CHANGED);
201 static final int ATTR_SET_STOPPED = (ATTR_STOPPED | ATTR_ATTRIBUTE_CHANGED);
203 protected int attributes;
206 /** counter for executed instructions along current transition */
207 protected int executedInstructions;
209 /** a listener or peer request for throwing an exception into the SUT, to be processed in executeInstruction */
210 protected SUTExceptionRequest pendingSUTExceptionRequest;
212 /** the last returned direct call frame */
213 protected DirectCallStackFrame returnedDirectCall;
215 /** the next insn to enter (null prior to execution) */
216 protected Instruction nextPc;
219 * not so nice we cross-couple the NativePeers with ThreadInfo,
220 * but to carry on with the JNI analogy, a MJIEnv is clearly
221 * owned by a thread (even though we don't have much ThreadInfo
222 * state dependency in here (yet), hence the simplistic init)
227 * the VM we are running on. Bad backlink, but then again, we can't really
228 * test a ThreadInfo outside a VM context anyways.
229 * <2do> If we keep 'list' as a field, 'vm' might be moved over there
230 * (all threads in the list share the same VM)
235 * !! this is a volatile object, i.e. it has to be re-computed explicitly
236 * !! after each backtrack (we don't want to duplicate state storage)
237 * list of lock objects currently held by this thread.
238 * unfortunately, we cannot organize this as a stack, since it might get
239 * restored (from the heap) in random order
241 int[] lockedObjectReferences;
244 * !! this is also volatile -> has to be reset after backtrack
245 * the reference of the object if this thread is blocked or waiting for
247 int lockRef = MJIEnv.NULL;
249 Memento<ThreadInfo> cachedMemento; // cache for unchanged ThreadInfos
252 static class TiMemento implements Memento<ThreadInfo> {
253 // note that we don't have to store the invariants (id, oref, runnableRef, ciException)
256 ThreadData threadData;
261 TiMemento (ThreadInfo ti){
264 threadData = ti.threadData; // no need to clone - it's copy on first write
265 top = ti.top; // likewise
266 stackDepth = ti.stackDepth; // we just copy this for efficiency reasons
267 attributes = (ti.attributes & ATTR_STORE_MASK);
274 public ThreadInfo restore(ThreadInfo ignored) {
277 ti.threadData = threadData;
279 ti.stackDepth = stackDepth;
280 ti.attributes = attributes;
289 // the following parameters are configurable. Would be nice if we could keep
290 // them on a per-instance basis, but there are a few locations
291 // (e.g. ThreadList) where we loose the init context, and it's questionable
292 // if we want to change this at runtime, or POR might make sense on a per-thread
295 /** do we halt on each throw, i.e. don't look for an exception handler?
296 * Useful to find empty handler blocks, or misusd exceptionHandlers
298 static StringSetMatcher haltOnThrow;
301 * do we delegate to Thread.UncaughtExceptionHandlers (in case there is any
302 * other than the standard ThreadGroup)
304 static boolean ignoreUncaughtHandlers;
307 * do we go on if we return from an UncaughtExceptionHandler, or do we still
308 * regard this as a NoUncaughtExceptionProperty violation
310 static boolean passUncaughtHandler;
314 * break the current transition after this number of instructions.
315 * This is a safeguard against paths that won't break because potentially
316 * shared fields are not yet accessed by a second thread (existence of such
317 * paths is the downside of our access tracking). Note that we only break on
318 * backjumps once this count gets exceeded, to give state matching a better
319 * chance and avoid interference with the IdleLoop listener
321 static int maxTransitionLength;
324 * reset ThreadInfo statics (e.g. to reinitialize JPF)
326 static boolean init (Config config) {
327 currentThread = null;
329 globalTids = new HashMap<Integer, Integer>();
331 String[] haltOnThrowSpecs = config.getStringArray("vm.halt_on_throw");
332 if (haltOnThrowSpecs != null){
333 haltOnThrow = new StringSetMatcher(haltOnThrowSpecs);
336 ignoreUncaughtHandlers = config.getBoolean( "vm.ignore_uncaught_handler", true);
337 passUncaughtHandler = config.getBoolean( "vm.pass_uncaught_handler", true);
339 maxTransitionLength = config.getInt("vm.max_transition_length", 5000);
344 //--- factory methods
345 // <2do> this is going to be a configurable factory method
348 * search global cache for dense ThreadInfo ids. We could just use oref since those are
349 * guaranteed to be global, but not dense. The ids are search global, i.e. there is no
350 * need to store/restore, but it needs to be (re)set during init()
352 static Map<Integer, Integer> globalTids; // initialized by init
355 protected int computeId (int objRef) {
356 Integer id = globalTids.get(objRef);
359 id = globalTids.size();
366 static void addId(int objRef, int id) {
367 globalTids.put(objRef, id);
371 * mainThread ctor called by the VM. Note we don't have a thread object yet (hen-and-egg problem
372 * since we can't allocate objects without a ThreadInfo)
374 protected ThreadInfo (VM vm, int id, ApplicationContext appCtx) {
376 this.appCtx = appCtx;
379 // we don't have the group yet, no Runnable or parent, and the name is fixed
380 // the thread is also not yet in the ThreadList
382 ci = appCtx.getSystemClassLoader().getThreadClassInfo();
383 targetRef = MJIEnv.NULL;
384 threadData.name = MAIN_NAME;
388 * the ctor for all explicitly (bytecode) created threads. At this point, there is at least
389 * a mainThread and we have a corresponding java.lang.Thread object
391 protected ThreadInfo (VM vm, int objRef, int groupRef, int runnableRef, int nameRef, ThreadInfo parent) {
392 id = computeId(objRef);
393 this.appCtx = parent.getApplicationContext();
395 init(vm); // this is only partial, we still have to link/set the references
397 ElementInfo ei = vm.getModifiableElementInfo(objRef);
398 ei.setExposed(parent, null); // all explicitly creatd threads are per se exposed
400 this.ci = ei.getClassInfo();
401 this.objRef = objRef;
402 this.targetRef = runnableRef;
404 threadData.name = vm.getElementInfo(nameRef).asString();
406 vm.getScheduler().initializeThreadSync(parent, this);
408 // note the thread is not yet in the ThreadList, we have to register from the caller
411 protected void init(VM vm){
412 // 'gid' is set by the factory method
413 // we can't set the 'id' field of the corresponding java.lang.Thread object until we have one
417 threadData = new ThreadData();
418 threadData.state = State.NEW;
419 threadData.priority = Thread.NORM_PRIORITY;
420 threadData.isDaemon = false;
421 threadData.lockCount = 0;
422 threadData.suspendCount = 0;
423 // threadData.name not yet known
425 // 'priority', 'name', 'target' and 'group' are not taken
426 // from the object, but set within the java.lang.Thread ctors
431 lockedObjectReferences = emptyLockRefs;
434 attributes |= ATTR_DATA_CHANGED;
435 env = new MJIEnv(this);
439 public Memento<ThreadInfo> getMemento(MementoFactory factory) {
440 return factory.getMemento(this);
443 public Memento<ThreadInfo> getMemento(){
444 return new TiMemento(this);
448 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()) {
453 //--- cached mementos are only supposed to be accessed from the Restorer
455 public Memento<ThreadInfo> getCachedMemento(){
456 return cachedMemento;
459 public void setCachedMemento(Memento<ThreadInfo> memento){
460 cachedMemento = memento;
463 public static ThreadInfo getCurrentThread() {
464 return currentThread;
467 public boolean isExecutingAtomically () {
468 return vm.getSystemState().isAtomic();
471 public boolean holdsLock (ElementInfo ei) {
472 int objRef = ei.getObjectRef();
474 for (int i=0; i<lockedObjectReferences.length; i++) {
475 if (lockedObjectReferences[i] == objRef) {
488 * is *this* transition allowed to be empty (i.e. allowed to set a CG
489 * during re-execution of the current insn)
490 * reset before each instruction.execute()
492 public boolean isEmptyTransitionEnabled (){
493 return (attributes & ATTR_ENABLE_EMPTY_TRANSITION) != 0;
496 public void enableEmptyTransition (){
497 attributes |= ATTR_ENABLE_EMPTY_TRANSITION;
500 public void resetEmptyTransition(){
501 attributes &= ~ATTR_ENABLE_EMPTY_TRANSITION;
505 * answers if is this the first instruction within the current transition.
506 * This is mostly used to tell the top- from the bottom-half of a native method
507 * or Instruction.enter(), so that only one (the top half) registers the CG
508 * (top = register CG and reschedule insn, bottom = re-enter insn and process choice
509 * at beginning of new transition)
511 * This can be used in both pre- and post-exec notification listeners,
512 * the executedInstructions number is incremented before notifying
513 * instructionExecuted()
515 * this method depends on the sequence of operations in ThreadInfo.executeInstruction,
518 * notify executeInstruction
519 * nextPc = insn.execute
520 * increment executedInstructions
521 * notify instructionExecuted
523 public boolean isFirstStepInsn() {
524 int nInsn = executedInstructions;
527 // that would be a break in execute() or instructionExecuted()
530 } else if (nInsn == 1 && nextPc != null) {
531 // that is for setting the CG in executeInsn and then testing in
532 // instructionExecuted. Note that nextPc is reset before pre-exec notification
533 // and hence should only be non-null from insn.execute() up to the next
534 // ThreadInfo.executeInstruction()
542 * get the number of instructions executed in the current transition. This
543 * gets incremented after calling Instruction.enter(), i.e. before
544 * notifying instructionExecuted() listeners
546 public int getExecutedInstructions(){
547 return executedInstructions;
551 * to be used from methods called from listeners, to find out if we are in a
552 * pre- or post-exec notification
554 public boolean isPreExec() {
555 return (nextPc == null);
559 //--- various thread state related methods
562 * Updates the status of the thread.
564 public void setState (State newStatus) {
565 State oldStatus = threadData.state;
567 if (oldStatus != newStatus) {
569 assert (oldStatus != State.TERMINATED) : "can't resurrect thread " + this + " to " + newStatus.name();
571 threadDataClone().state = newStatus;
575 break; // Hmm, shall we report a thread object creation?
577 // nothing. the notifyThreadStarted has to happen from
578 // Thread.start(), since the thread could have been blocked
579 // at the time with a sync run() method
580 // assert lockRef == MJIEnv.NULL;
583 vm.notifyThreadTerminated(this);
586 assert lockRef != MJIEnv.NULL;
587 vm.notifyThreadBlocked(this);
590 assert lockRef == MJIEnv.NULL;
591 break; // nothing to notify
593 assert lockRef != MJIEnv.NULL;
594 vm.notifyThreadWaiting(this);
597 vm.notifyThreadInterrupted(this);
600 assert lockRef != MJIEnv.NULL;
601 vm.notifyThreadNotified(this);
605 if (log.isLoggable(Level.FINE)){
606 log.fine("setStatus of " + getName() + " from "
607 + oldStatus.name() + " to " + newStatus.name());
612 void setBlockedState (int objref) {
614 State currentState = threadData.state;
615 switch (currentState){
620 setState(State.BLOCKED);
624 assert false : "thread " + this + "can't be blocked in state: " + currentState.name();
628 void setNotifiedState() {
629 State currentState = threadData.state;
630 switch (currentState){
632 case INTERRUPTED: // too late, we are already interrupted
634 // can happen in a Thread.join()
637 case TIMEOUT_WAITING:
638 setState(State.NOTIFIED);
642 assert false : "thread " + this + "can't be notified in state: " + currentState.name();
647 * Returns the current status of the thread.
649 public State getState () {
650 return threadData.state;
655 * Returns true if this thread is either RUNNING or UNBLOCKED
657 public boolean isRunnable () {
658 if (threadData.suspendCount != 0)
661 switch (threadData.state) {
666 return true; // that's arguable, but since we don't model time we treat it like runnable
668 return true; // would have been set to blocked if it couldn't reacquire the lock
674 public boolean willBeRunnable () {
675 if (threadData.suspendCount != 0)
678 switch (threadData.state) {
682 case TIMEOUT_WAITING: // it's not yet, but it will be at the time it gets scheduled
690 public boolean isNew () {
691 return (threadData.state == State.NEW);
694 public boolean isTimeoutRunnable () {
695 if (threadData.suspendCount != 0)
698 switch (threadData.state) {
705 case TIMEOUT_WAITING:
706 // depends on if we can re-acquire the lock
707 //assert lockRef != MJIEnv.NULL : "timeout waiting but no blocked object";
708 if (lockRef != MJIEnv.NULL){
709 ElementInfo ei = vm.getElementInfo(lockRef);
710 return ei.canLock(this);
720 public boolean isTimedOut() {
721 return (threadData.state == State.TIMEDOUT);
724 public boolean isTimeoutWaiting() {
725 return (threadData.state == State.TIMEOUT_WAITING);
728 public void setTimedOut() {
729 setState(State.TIMEDOUT);
732 public void setTerminated() {
733 setState(State.TERMINATED);
736 public void resetTimedOut() {
737 // should probably check for TIMEDOUT
738 setState(State.TIMEOUT_WAITING);
741 public void setSleeping() {
742 setState(State.SLEEPING);
745 public boolean isSleeping(){
746 return (threadData.state == State.SLEEPING);
749 public void setRunning() {
750 setState(State.RUNNING);
753 public void setStopped(int throwableRef){
755 // no need to kill twice
759 attributes |= ATTR_SET_STOPPED;
761 if (!hasBeenStarted()){
762 // that one is easy - just remember the state so that a subsequent start()
767 // for all other cases, we need to have a proper stopping Throwable that does not
768 // fall victim to GC, and that does not cause NoUncaughtExcceptionsProperty violations
769 if (throwableRef == MJIEnv.NULL){
770 // if no throwable was provided (the normal case), throw a java.lang.ThreadDeath Error
771 ClassInfo cix = ClassInfo.getInitializedSystemClassInfo("java.lang.ThreadDeath", this);
772 throwableRef = createException(cix, null, MJIEnv.NULL);
775 // now the tricky part - this thread is alive but might be blocked, notified
776 // or waiting. In any case, exception action should not take place before
777 // the thread becomes scheduled again, which
778 // means we are not allowed to fiddle with its state in any way that changes
779 // scheduling/locking behavior. On the other hand, if this is the currently
780 // executing thread, take immediate action
782 if (isCurrentThread()){ // we are suicidal
783 if (isInNativeMethod()){
784 // remember the exception to be thrown when we return from the native method
785 env.throwException(throwableRef);
787 Instruction nextPc = throwException(throwableRef);
791 } else { // this thread is not currently running, this is an external kill
793 // remember there was a pending exception that has to be thrown the next
794 // time this gets scheduled, and make sure the exception object does
795 // not get GCed prematurely
796 ElementInfo eit = getModifiableElementInfo(objRef);
797 eit.setReferenceField("stopException", throwableRef);
801 public boolean isCurrentThread(){
802 return this == currentThread;
805 public boolean isInCurrentThreadList(){
806 return vm.getThreadList().contains(this);
810 * An alive thread is anything but TERMINATED or NEW
812 public boolean isAlive () {
813 State state = threadData.state;
814 return (state != State.TERMINATED && state != State.NEW);
817 public boolean isWaiting () {
818 State state = threadData.state;
819 return (state == State.WAITING) || (state == State.TIMEOUT_WAITING);
822 public boolean isWaitingOrTimedOut (){
823 State state = threadData.state;
824 return (state == State.WAITING) || (state == State.TIMEOUT_WAITING) || (state == State.TIMEDOUT);
827 public boolean isNotified () {
828 return (threadData.state == State.NOTIFIED);
831 public boolean isUnblocked () {
832 State state = threadData.state;
833 return (state == State.UNBLOCKED) || (state == State.TIMEDOUT);
836 public boolean isBlocked () {
837 return (threadData.state == State.BLOCKED);
840 public boolean isTerminated () {
841 return (threadData.state == State.TERMINATED);
844 public boolean isAtomic (){
845 return vm.getSystemState().isAtomic();
848 public void setBlockedInAtomicSection (){
849 vm.getSystemState().setBlockedInAtomicSection();
852 MethodInfo getExitMethod() {
853 MethodInfo mi = getClassInfo().getMethod("exit()V", true);
857 public boolean isBlockedOrNotified() {
858 State state = threadData.state;
859 return (state == State.BLOCKED) || (state == State.NOTIFIED);
862 // this is just a state attribute
863 public boolean isStopped() {
864 return (attributes & ATTR_STOPPED) != 0;
867 public boolean isInNativeMethod(){
868 return top != null && top.isNative();
871 public boolean hasBeenStarted(){
872 return (threadData.state != State.NEW);
875 public String getStateName () {
876 return threadData.getState().name();
880 public Iterator<StackFrame> iterator () {
881 return new StackIterator();
884 public Iterable<StackFrame> invokedStackFrames () {
885 return new Iterable<StackFrame>() {
887 public Iterator<StackFrame> iterator() {
888 return new InvokedStackIterator();
894 * this returns a copy of the StackFrames in reverse order. Note this is
895 * redundant because the frames are linked explicitly
896 * @deprecated - use Iterable<StackFrame>
899 public List<StackFrame> getStack() {
900 ArrayList<StackFrame> list = new ArrayList<StackFrame>(stackDepth);
902 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
906 Collections.reverse(list);
912 * returns StackFrames which have been entered through a corresponding
913 * invoke instruction (in top first order)
915 public List<StackFrame> getInvokedStackFrames() {
916 ArrayList<StackFrame> list = new ArrayList<StackFrame>(stackDepth);
918 int i = stackDepth-1;
919 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
920 if (!frame.isDirectCallFrame()){
924 Collections.reverse(list);
929 public Scheduler getScheduler(){
930 return vm.getScheduler();
933 public int getStackDepth() {
937 public MethodInfo getEntryMethod(){
938 return appCtx.getEntryMethod();
941 public StackFrame getCallerStackFrame (int offset){
943 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
954 public StackFrame getLastInvokedStackFrame() {
955 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
956 if (!frame.isDirectCallFrame()){
964 public StackFrame getLastNonSyntheticStackFrame (){
965 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
966 if (!frame.isSynthetic()){
974 // this is ugly - it can modify deeper stack frames
975 public StackFrame getModifiableLastNonSyntheticStackFrame (){
976 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
977 if (!frame.isSynthetic()){
978 if (frame.isFrozen()) {
979 StackFrame newFrame = frame.clone();
986 // Ughh, now we have to clone all frozen frames above
987 StackFrame fLast = null;
988 for (StackFrame f = getModifiableTopFrame(); f != frame; f = f
993 fLast.setPrevious(f);
999 fLast.setPrevious(newFrame);
1015 * Returns the this pointer of the callee from the stack.
1017 public int getCalleeThis (MethodInfo mi) {
1018 return top.getCalleeThis(mi);
1022 * Returns the this pointer of the callee from the stack.
1024 public int getCalleeThis (int size) {
1025 return top.getCalleeThis(size);
1028 public ClassInfo getClassInfo (int objref) {
1029 return env.getClassInfo(objref);
1032 public boolean isCalleeThis (ElementInfo r) {
1033 if (top == null || r == null) {
1037 Instruction pc = getPC();
1040 !(pc instanceof JVMInvokeInstruction) ||
1041 pc instanceof INVOKESTATIC) {
1045 JVMInvokeInstruction call = (JVMInvokeInstruction) pc;
1047 return getCalleeThis(Types.getArgumentsSize(call.getInvokedMethodSignature()) + 1) == r.getObjectRef();
1050 public ApplicationContext getApplicationContext(){
1054 public SystemClassLoaderInfo getSystemClassLoaderInfo(){
1055 return appCtx.sysCl;
1059 * Returns the class information.
1061 public ClassInfo getClassInfo () {
1065 public MJIEnv getEnv() {
1069 public boolean isInterrupted (boolean resetStatus) {
1070 ElementInfo ei = getElementInfo(getThreadObjectRef());
1071 boolean status = ei.getBooleanField("interrupted");
1073 if (resetStatus && status) {
1074 ei = ei.getModifiableInstance();
1075 ei.setBooleanField("interrupted", false);
1082 * path local unique id for live threads. This is what we use for the
1083 * public java.lang.Thread.getId() that can be called from SUT code. It is
1084 * NOT what we use for our canonical root set
1086 public int getId () {
1091 * this is our internal, search global id that is used for the
1092 * canonical root set
1094 public int getGlobalId(){
1100 * record what this thread is being blocked on.
1102 void setLockRef (int objref) {
1104 assert ((lockRef == MJIEnv.NULL) || (lockRef == objref)) :
1105 "attempt to overwrite lockRef: " + vm.getHeap().get(lockRef) +
1106 " with: " + vm.getHeap().get(objref);
1112 * thread is not blocked anymore
1113 * needs to be public since we have to use it from INVOKECLINIT (during call skipping)
1115 public void resetLockRef () {
1116 lockRef = MJIEnv.NULL;
1119 public int getLockRef() {
1123 public ElementInfo getLockObject () {
1124 if (lockRef == MJIEnv.NULL) {
1127 return vm.getElementInfo(lockRef);
1132 * Returns the line number of the program counter of the top stack frame.
1134 public int getLine () {
1138 return top.getLine();
1142 //--- suspend/resume modeling
1143 // modeling this with a count is an approximation. In reality it behaves
1144 // rather like a race that /sometimes/ causes the resume to fail, but its
1145 // Ok if we overapproximate on the safe side, since suspend/resume is such
1146 // an inherently unsafe thing. What we *do* want to preserve faithfully is
1147 // that locks held by the suspended thread are not released
1150 * set suspension status
1151 * @return true if thread was not suspended
1153 public boolean suspend() {
1154 return threadDataClone().suspendCount++ == 0;
1158 * unset suspension status
1159 * @return true if thread was suspended
1161 public boolean resume() {
1162 return (threadData.suspendCount > 0) && (--threadDataClone().suspendCount == 0);
1165 public boolean isSuspended() {
1166 return threadData.suspendCount > 0;
1173 * Sets the number of locks held at the time of a wait.
1175 public void setLockCount (int l) {
1176 if (threadData.lockCount != l) {
1177 threadDataClone().lockCount = l;
1182 * Returns the number of locks in the last wait.
1184 public int getLockCount () {
1185 return threadData.lockCount;
1188 // avoid use in performance critical code
1189 public List<ElementInfo> getLockedObjects () {
1190 List<ElementInfo> lockedObjects = new LinkedList<ElementInfo>();
1191 Heap heap = vm.getHeap();
1193 for (int i=0; i<lockedObjectReferences.length; i++) {
1194 ElementInfo ei = heap.get(lockedObjectReferences[i]);
1195 lockedObjects.add(ei);
1198 return lockedObjects;
1201 public boolean hasLockedObjects() {
1202 return lockedObjectReferences.length > 0;
1205 public int[] getLockedObjectReferences () {
1206 return lockedObjectReferences;
1209 public boolean isLockOwner (ElementInfo ei){
1210 return ei.getLockingThread() == this;
1214 * returns the current method in the top stack frame, which is always a
1215 * bytecode method (executed by JPF)
1217 public MethodInfo getTopFrameMethodInfo () {
1219 return top.getMethodInfo();
1226 * return the ClassInfo of the topmost stackframe that is not a direct call
1228 public ClassInfo getExecutingClassInfo(){
1229 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
1230 MethodInfo miExecuting = frame.getMethodInfo();
1231 ClassInfo ciExecuting = miExecuting.getClassInfo();
1232 if (ciExecuting != null){
1242 public ClassInfo resolveReferencedClass (String clsName){
1243 ClassInfo ciTop = top.getClassInfo();
1244 return ciTop.resolveReferencedClass(clsName);
1246 //return ClassLoaderInfo.getCurrentClassLoader(this).getResolvedClassInfo(clsName);
1249 public boolean isInCtor () {
1250 // <2do> - hmm, if we don't do this the whole stack, we miss factored
1252 MethodInfo mi = getTopFrameMethodInfo();
1260 public boolean isCtorOnStack (int objRef){
1261 for (StackFrame f = top; f != null; f = f.getPrevious()){
1262 if (f.getThis() == objRef && f.getMethodInfo().isCtor()){
1270 public boolean isClinitOnStack (ClassInfo ci){
1271 for (StackFrame f = top; f != null; f = f.getPrevious()){
1272 MethodInfo mi = f.getMethodInfo();
1273 if (mi.isClinit(ci)){
1282 public String getName () {
1283 return threadData.name;
1289 * Returns the object reference.
1291 public int getThreadObjectRef () {
1295 public ElementInfo getThreadObject(){
1296 return getElementInfo(objRef);
1299 public ElementInfo getModifiableThreadObject() {
1300 return getModifiableElementInfo(objRef);
1305 * Sets the program counter of the top stack frame.
1307 public void setPC (Instruction pc) {
1308 getModifiableTopFrame().setPC(pc);
1311 public void advancePC () {
1312 getModifiableTopFrame().advancePC();
1316 * Returns the program counter of the top stack frame.
1318 public Instruction getPC () {
1326 public Instruction getNextPC () {
1332 * get the current stack trace of this thread
1333 * this is called during creation of a Throwable, hence we should skip
1334 * all throwable ctors in here
1335 * <2do> this is only a partial solution,since we don't catch exceptionHandlers
1336 * in Throwable ctors yet
1338 public String getStackTrace () {
1339 StringBuilder sb = new StringBuilder(256);
1341 for (StackFrame sf = top; sf != null; sf = sf.getPrevious()){
1342 MethodInfo mi = sf.getMethodInfo();
1345 ClassInfo ci = mi.getClassInfo();
1346 if (ci.isInstanceOf("java.lang.Throwable")) {
1352 sb.append(sf.getStackTraceInfo());
1356 return sb.toString();
1361 * Returns the information necessary to store.
1363 * <2do> pcm - not clear to me how lower stack frames can contribute to
1364 * a different threadinfo state hash - only the current one can be changed
1365 * by the executing method
1367 public void dumpStoringData (IntVector v) {
1368 v = null; // Get rid of IDE warnings
1372 * Returns the object reference of the target.
1374 public int getRunnableRef () {
1379 * Returns the pointer to the object reference of the executing method
1381 public int getThis () {
1382 return top.getThis();
1385 public ElementInfo getThisElementInfo(){
1386 return getElementInfo(getThis());
1389 public boolean isThis (ElementInfo ei) {
1398 if (getTopFrameMethodInfo().isStatic()) {
1401 int thisRef = top.getThis();
1402 return ei.getObjectRef() == thisRef;
1406 public boolean atMethod (String mname) {
1407 return top != null && getTopFrameMethodInfo().getFullName().equals(mname);
1410 public boolean atPosition (int position) {
1414 Instruction pc = getPC();
1415 return pc != null && pc.getPosition() == position;
1419 public boolean atReturn () {
1423 Instruction pc = getPC();
1424 return pc instanceof ReturnInstruction;
1430 * reset any information that has to be re-computed in a backtrack
1431 * (i.e. hasn't been stored explicitly)
1433 void resetVolatiles () {
1434 // resetting lock sets goes here
1435 lockedObjectReferences = emptyLockRefs;
1437 // the ref of the object we are blocked on or waiting for
1438 lockRef = MJIEnv.NULL;
1440 pendingException = null;
1444 * this is used when restoring states
1446 void updateLockedObject (ElementInfo ei) {
1447 int n = lockedObjectReferences.length;
1448 int[] a = new int[n+1];
1449 System.arraycopy(lockedObjectReferences, 0, a, 0, n);
1450 a[n] = ei.getObjectRef();
1451 lockedObjectReferences = a;
1453 // don't notify here, it's just a restore
1456 void addLockedObject (ElementInfo ei) {
1457 int n = lockedObjectReferences.length;
1458 int[] a = new int[n+1];
1459 System.arraycopy(lockedObjectReferences, 0, a, 0, n);
1460 a[n] = ei.getObjectRef();
1461 lockedObjectReferences = a;
1463 vm.notifyObjectLocked(this, ei);
1466 void removeLockedObject (ElementInfo ei) {
1467 int objRef = ei.getObjectRef();
1468 int n = lockedObjectReferences.length;
1471 assert lockedObjectReferences[0] == objRef;
1472 lockedObjectReferences = emptyLockRefs;
1475 int[] a = new int[n - 1];
1477 for (int i = 0, j = 0; i < n; i++) {
1478 if (lockedObjectReferences[i] != objRef) {
1479 a[j++] = lockedObjectReferences[i];
1482 lockedObjectReferences = a;
1485 vm.notifyObjectUnlocked(this, ei);
1490 public Object clone() {
1492 // threadData and top StackFrame are copy-on-write, so we should not have to clone them
1493 // lockedObjects are state-volatile and restored explicitly after a backtrack
1494 return super.clone();
1496 } catch (CloneNotSupportedException cnsx) {
1502 * Returns the number of stack frames.
1504 public int countStackFrames () {
1509 * get a stack snapshot that consists of an array of {mthId,pc} pairs.
1510 * strip stackframes that enter instance methods of the exception object
1512 public int[] getSnapshot (int xObjRef) {
1513 StackFrame frame = top;
1516 if (xObjRef != MJIEnv.NULL){ // filter out exception method frames
1517 for (;frame != null; frame = frame.getPrevious()){
1518 if (frame.getThis() != xObjRef){
1526 int[] snap = new int[n*2];
1528 for (; frame != null; frame = frame.getPrevious()){
1529 snap[j++] = frame.getMethodInfo().getGlobalId();
1530 snap[j++] = frame.getPC().getInstructionIndex();
1537 * turn a snapshot into an JPF array of StackTraceElements, which means
1538 * a lot of objects. Do this only on demand
1540 public int createStackTraceElements (int[] snapshot) {
1541 int n = snapshot.length/2;
1543 StackTraceElement[] list = new StackTraceElement[n];
1544 for (int i=0, j=0; i<n; i++){
1545 int methodId = snapshot[j++];
1546 int pcOffset = snapshot[j++];
1547 StackTraceElement ste = new StackTraceElement( methodId, pcOffset);
1549 list[nVisible++] = ste;
1553 Heap heap = vm.getHeap();
1554 ElementInfo eiArray = heap.newArray("Ljava/lang/StackTraceElement;", nVisible, this);
1555 for (int i=0; i<nVisible; i++){
1556 int eref = list[i].createJPFStackTraceElement();
1557 eiArray.setReferenceElement( i, eref);
1560 return eiArray.getObjectRef();
1563 void print (PrintWriter pw, String s) {
1571 public void printStackTrace (int objRef) {
1572 printStackTrace(null, objRef);
1575 public void printPendingExceptionOn (PrintWriter pw) {
1576 if (pendingException != null) {
1577 printStackTrace( pw, pendingException.getExceptionReference());
1582 * the reason why this is kind of duplicated (there is also a StackFrame.getPositionInfo)
1583 * is that this might be working off a StackTraceElement[] that is created when the exception
1584 * is created. At the time printStackTrace() is called, the StackFrames in question
1585 * are most likely already be unwinded
1587 public void printStackTrace (PrintWriter pw, int objRef) {
1588 // 'env' usage is not ideal, since we don't know from what context we are called, and
1589 // hence the MJIEnv calling context might not be set (no Method or ClassInfo)
1590 // on the other hand, we don't want to re-implement all the MJIEnv accessor methods
1592 print(pw, env.getClassInfo(objRef).getName());
1593 int msgRef = env.getReferenceField(objRef,"detailMessage");
1594 if (msgRef != MJIEnv.NULL) {
1596 print(pw, env.getStringObject(msgRef));
1600 // try the 'stackTrace' field first, it might have been set explicitly
1601 int aRef = env.getReferenceField(objRef, "stackTrace"); // StackTrace[]
1602 if (aRef != MJIEnv.NULL) {
1603 int len = env.getArrayLength(aRef);
1604 for (int i=0; i<len; i++) {
1605 int steRef = env.getReferenceArrayElement(aRef, i);
1606 if (steRef != MJIEnv.NULL){ // might be ignored (e.g. direct call)
1607 StackTraceElement ste = new StackTraceElement(steRef);
1612 } else { // fall back to use the snapshot stored in the exception object
1613 aRef = env.getReferenceField(objRef, "snapshot");
1614 int[] snapshot = env.getIntArrayObject(aRef);
1615 int len = snapshot.length/2;
1617 for (int i=0, j=0; i<len; i++){
1618 int methodId = snapshot[j++];
1619 int pcOffset = snapshot[j++];
1620 StackTraceElement ste = new StackTraceElement( methodId, pcOffset);
1625 int causeRef = env.getReferenceField(objRef, "cause");
1626 if ((causeRef != objRef) && (causeRef != MJIEnv.NULL)){
1627 print(pw, "Caused by: ");
1628 printStackTrace(pw, causeRef);
1632 class StackTraceElement {
1633 String clsName, mthName, fileName;
1638 StackTraceElement (int methodId, int pcOffset) {
1639 if (methodId == MethodInfo.DIRECT_CALL) {
1643 MethodInfo mi = MethodInfo.getMethodInfo(methodId);
1645 clsName = mi.getClassName();
1646 mthName = mi.getName();
1648 fileName = mi.getStackTraceSource();
1650 // See ThreadStopTest.threadDeathWhileRunstart
1651 // <2do> remove when RUNSTART is gone
1654 line = mi.getLineNumber(mi.getInstruction(pcOffset));
1656 } else { // this sounds like a bug
1665 StackTraceElement (int sRef){
1666 clsName = env.getStringObject(env.getReferenceField(sRef, "clsName"));
1667 mthName = env.getStringObject(env.getReferenceField(sRef, "mthName"));
1668 fileName = env.getStringObject(env.getReferenceField(sRef, "fileName"));
1669 line = env.getIntField(sRef, "line");
1672 int createJPFStackTraceElement() {
1677 Heap heap = vm.getHeap();
1678 ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.StackTraceElement");
1679 ElementInfo ei = heap.newObject(ci, ThreadInfo.this);
1681 ei.setReferenceField("clsName", heap.newString(clsName, ThreadInfo.this).getObjectRef());
1682 ei.setReferenceField("mthName", heap.newString(mthName, ThreadInfo.this).getObjectRef());
1684 String fname = fileName != null ? fileName : "Unknown Source";
1685 ei.setReferenceField("fileName", heap.newString(fname, ThreadInfo.this).getObjectRef());
1687 ei.setIntField("line", line);
1689 return ei.getObjectRef();
1693 void printOn (PrintWriter pw){
1695 // the usual behavior is to print only the filename, strip the path
1696 if (fileName != null){
1697 int idx = fileName.lastIndexOf(File.separatorChar);
1699 fileName = fileName.substring(idx+1);
1704 if (clsName != null){
1707 } else { // some synthetic methods don't belong to classes
1708 print(pw, "[no class] ");
1712 if (fileName != null){
1714 print(pw, fileName);
1717 print(pw, Integer.toString(line));
1721 //print(pw, "<no source>");
1730 * this is a helper class to store listener generated exception requests that are checked before and after
1731 * calling Instruction.execute(). This is a safe way to raise SUT exceptions from listener code without compromising
1732 * consistency of executes() that are not prepared to cut short by means of re-execution or host VM exceptions
1734 static class SUTExceptionRequest {
1738 SUTExceptionRequest (String xClsName, String details){
1739 this.xClsName = xClsName;
1740 this.details = details;
1743 public String getExceptionClassName(){
1747 public String getDetails(){
1752 public void requestSUTException (String exceptionClsName, String details){
1753 pendingSUTExceptionRequest = new SUTExceptionRequest( exceptionClsName, details);
1754 if (nextPc == null){ // this is pre-exec, skip the execute()
1755 attributes |= ATTR_SKIP_INSN_EXEC;
1759 protected void processPendingSUTExceptionRequest (){
1760 if (pendingSUTExceptionRequest != null){
1761 // <2do> we could do more specific checks for ClassNotFoundExceptions here
1762 nextPc = createAndThrowException( pendingSUTExceptionRequest.getExceptionClassName(), pendingSUTExceptionRequest.getDetails());
1763 pendingSUTExceptionRequest = null;
1769 * <2do> pcm - this is only valid for java.* and our own Throwables that don't
1770 * need ctor execution since we only initializeSharednessPolicy the Throwable fields. This method
1771 * is here to avoid round trips in case of exceptions
1773 int createException (ClassInfo ci, String details, int causeRef){
1774 int[] snap = getSnapshot(MJIEnv.NULL);
1775 return vm.getHeap().newSystemThrowable(ci, details, snap, causeRef, this, 0).getObjectRef();
1779 * Creates and throws an exception. This is what is used if the exception is
1780 * thrown by the VM (or a listener)
1782 public Instruction createAndThrowException (ClassInfo ci, String details) {
1783 //if (ci.initializeClass(this)) {
1786 ci.initializeClassAtomic(this);
1788 int objref = createException(ci,details, MJIEnv.NULL);
1789 return throwException(objref);
1793 * Creates an exception and throws it.
1795 public Instruction createAndThrowException (String cname) {
1796 return createAndThrowException(cname, null);
1799 public Instruction createAndThrowException (String cname, String details) {
1801 ClassInfo ci = null;
1803 ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cname);
1804 } catch(ClassInfoException cie) {
1805 // the non-system class loader couldn't find the class,
1806 if(cie.getExceptionClass().equals("java.lang.ClassNotFoundException") &&
1807 !ClassLoaderInfo.getCurrentClassLoader().isSystemClassLoader()) {
1808 ci = ClassLoaderInfo.getSystemResolvedClassInfo(cname);
1813 return createAndThrowException(ci, details);
1815 } catch (ClassInfoException cie){
1816 if(!cname.equals(cie.getExceptionClass())) {
1817 ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cie.getExceptionClass());
1818 return createAndThrowException(ci, cie.getMessage());
1826 * can be used by instructions to break long transitions (preferably on
1827 * backjumps so that state matching could terminate the search)
1829 public boolean maxTransitionLengthExceeded(){
1830 return executedInstructions >= maxTransitionLength;
1834 * enter instructions until there is none left or somebody breaks
1835 * the transition (e.g. by registering a CG)
1837 * this is the inner interpreter loop of JPF
1839 protected void executeTransition (SystemState ss) throws JPFException {
1842 while ((pc = getPC()) != null){
1843 Instruction nextPc = null;
1845 currentThread = this;
1846 executedInstructions = 0;
1847 pendingException = null;
1850 pc = throwStopException();
1854 // this constitutes the main transition loop. It gobbles up
1855 // insns until someone registered a ChoiceGenerator, there are no insns left,
1856 // the transition was explicitly marked as ignored, or we have reached a
1857 // max insn count and preempt the thread upon the next available backjump
1858 while (pc != null) {
1859 nextPc = executeInstruction();
1861 if (ss.breakTransition()) {
1862 if (ss.extendTransition()){
1866 if (executedInstructions == 0){ // a CG from a re-executed insn
1867 if (isEmptyTransitionEnabled()){ // treat as a new state if empty transitions are enabled
1882 protected void resetTransientAttributes(){
1883 attributes &= ~(ATTR_SKIP_INSN_EXEC | ATTR_SKIP_INSN_LOG | ATTR_ENABLE_EMPTY_TRANSITION);
1887 * Execute next instruction.
1889 public Instruction executeInstruction () {
1890 Instruction pc = getPC();
1891 SystemState ss = vm.getSystemState();
1893 resetTransientAttributes();
1896 // note that we don't reset pendingSUTExceptionRequest since it could be set outside executeInstruction()
1898 if (log.isLoggable(Level.FINER)) {
1899 log.fine( pc.getMethodInfo().getFullName() + " " + pc.getPosition() + " : " + pc);
1902 // this is the pre-execution notification, during which a listener can perform
1903 // on-the-fly instrumentation or even replace the instruction alltogether
1904 vm.notifyExecuteInstruction(this, pc);
1906 if ((pendingSUTExceptionRequest == null) && ((attributes & ATTR_SKIP_INSN_EXEC) == 0)){
1908 nextPc = pc.execute(this);
1909 } catch (ClassInfoException cie) {
1910 nextPc = this.createAndThrowException(cie.getExceptionClass(), cie.getMessage());
1914 // we also count the skipped ones
1915 executedInstructions++;
1917 if ((attributes & ATTR_SKIP_INSN_LOG) == 0) {
1918 ss.recordExecutionStep(pc);
1921 // here we have our post exec bytecode exec observation point
1922 vm.notifyInstructionExecuted(this, pc, nextPc);
1924 // since this is part of the inner execution loop, it is a convenient place to check for probes
1925 vm.getSearch().checkAndResetProbeRequest();
1927 // clean up whatever might have been stored by enter
1928 pc.cleanupTransients();
1930 if (pendingSUTExceptionRequest != null){
1931 processPendingSUTExceptionRequest();
1934 // set+return the next insn to enter if we did not return from the last stack frame.
1935 // Note that 'nextPc' might have been set by a listener, and/or 'top' might have
1936 // been changed by executing an invoke, return or throw (handler), or by
1937 // pushing overlay calls on the stack
1939 // <2do> this is where we would have to handle general insn repeat
1948 * enter instruction hidden from any listeners, and do not
1949 * record it in the path
1951 public Instruction executeInstructionHidden () {
1952 Instruction pc = getPC();
1953 SystemState ss = vm.getSystemState();
1954 KernelState ks = vm.getKernelState();
1956 nextPc = null; // reset in case pc.execute() blows (this could be behind an exception firewall)
1958 if (log.isLoggable(Level.FINE)) {
1959 log.fine( pc.getMethodInfo().getFullName() + " " + pc.getPosition() + " : " + pc);
1963 nextPc = pc.execute(this);
1964 } catch (ClassInfoException cie) {
1965 nextPc = this.createAndThrowException(cie.getExceptionClass(), cie.getMessage());
1968 // we also count the hidden ones since isFirstStepInsn depends on it
1969 executedInstructions++;
1971 // since this is part of the inner execution loop, it is a convenient place to check probe notifications
1972 vm.getSearch().checkAndResetProbeRequest();
1974 // we did not return from the last frame stack
1975 if (top != null) { // <2do> should probably bomb otherwise
1983 * is this after calling Instruction.enter()
1984 * used by instructions and listeners
1986 public boolean isPostExec() {
1987 return (nextPc != null);
1990 public void reExecuteInstruction() {
1994 public boolean willReExecuteInstruction() {
1995 return (getPC() == nextPc);
1999 * skip the next bytecode. To be used by listeners to on-the-fly replace
2002 public void skipInstruction (Instruction nextInsn) {
2003 attributes |= ATTR_SKIP_INSN_EXEC;
2005 //assert nextInsn != null;
2010 * skip and continue with the following instruction. This is deprecated because
2011 * callers should explicitly provide the next instruction (depending on the
2015 public void skipInstruction(){
2016 skipInstruction(getPC().getNext());
2019 public boolean isInstructionSkipped() {
2020 return (attributes & ATTR_SKIP_INSN_EXEC) != 0;
2023 public void skipInstructionLogging () {
2024 attributes |= ATTR_SKIP_INSN_LOG;
2028 * explicitly set the next insn to enter. To be used by listeners that
2029 * replace bytecode exec (during 'executeInstruction' notification
2031 * Note this is dangerous because you have to make sure the operand stack is
2032 * in a consistent state. This also will fail if someone already ordered
2033 * reexecution of the current instruction
2035 public boolean setNextPC (Instruction insn) {
2036 if (nextPc == null){
2037 // this is pre-execution, if we don't skip then the next insn.execute() is going
2038 // to override what we set here
2039 attributes |= ATTR_SKIP_INSN_EXEC;
2044 if (top != null && nextPc != top.getPC()){ // this needs to be re-executed
2054 * Executes a method call. Be aware that it executes the whole method as one atomic
2055 * step. Arguments have to be already on the provided stack
2057 * This only works for non-native methods, and does not allow any choice points,
2058 * so you have to know very well what you are doing.
2060 * Instructions executed by this method are still fully observable and stored in
2063 public void executeMethodAtomic (StackFrame frame) {
2066 int depth = countStackFrames();
2067 Instruction pc = frame.getPC();
2068 SystemState ss = vm.getSystemState();
2070 ss.incAtomic(); // to shut off avoidable context switches (MONITOR_ENTER and wait() can still block)
2072 while (depth <= countStackFrames()) {
2073 Instruction nextPC = executeInstruction();
2075 if (ss.getNextChoiceGenerator() != null) {
2076 // BANG - we can't have CG's here
2077 // should be rather an ordinary exception
2078 // createAndThrowException("java.lang.AssertionError", "choice point in sync executed method: " + frame);
2079 throw new JPFException("choice point in atomic method execution: " + frame);
2085 vm.getSystemState().decAtomic();
2089 // the frame was already removed by the RETURN insn of the frame's method
2093 * enter method atomically, but also hide it from listeners and do NOT add
2094 * executed instructions to the path.
2096 * this can be even more confusing than executeMethodAtomic(), since
2097 * nothing prevents such a method from changing the program state, and we
2098 * wouldn't know for what reason by looking at the trace
2100 * this method should only be used if we have to enter test application code
2101 * like hashCode() or equals() from native code, e.g. to silently check property
2104 * executeMethodHidden also acts as an exception firewall, since we don't want
2105 * any silently executed code fall back into the visible path (for
2106 * no observable reason)
2108 public void executeMethodHidden (StackFrame frame) {
2112 int depth = countStackFrames(); // this includes the DirectCallStackFrame
2113 Instruction pc = frame.getPC();
2115 vm.getSystemState().incAtomic(); // to shut off avoidable context switches (MONITOR_ENTER and wait() can still block)
2117 while (depth <= countStackFrames()) {
2118 Instruction nextPC = executeInstructionHidden();
2120 if (pendingException != null) {
2124 // BANG - we can't have CG's here
2125 // should be rather an ordinary exception
2126 // createAndThrowException("java.lang.AssertionError", "choice point in sync executed method: " + frame);
2127 throw new JPFException("choice point in hidden method execution: " + frame);
2134 vm.getSystemState().decAtomic();
2138 // the frame was already removed by the RETURN insn of the frame's method
2141 public Heap getHeap () {
2142 return vm.getHeap();
2145 public ElementInfo getElementInfo (int objRef) {
2146 Heap heap = vm.getHeap();
2147 return heap.get(objRef);
2150 public ElementInfo getModifiableElementInfo (int ref) {
2151 Heap heap = vm.getHeap();
2152 return heap.getModifiable(ref);
2155 public ElementInfo getBlockedObject (MethodInfo mi, boolean isBeforeCall, boolean isModifiable) {
2157 ElementInfo ei = null;
2159 if (mi.isSynchronized()) {
2160 if (mi.isStatic()) {
2161 objref = mi.getClassInfo().getClassObjectRef();
2163 // NOTE 'inMethod' doesn't work for natives, because getThis() pulls 'this' from the stack frame,
2164 // which we don't have (and don't need) for natives
2165 objref = isBeforeCall ? getCalleeThis(mi) : getThis();
2168 ei = (isModifiable) ? getModifiableElementInfo(objref) : getElementInfo(objref);
2170 assert (ei != null) : ("inconsistent stack, no object or class ref: " +
2171 mi.getFullName() + " (" + objref +")");
2177 //--- convenience methods for call sites that don't have direct access to the VM
2179 public boolean setNextChoiceGenerator (ChoiceGenerator<?> cg){
2180 return vm.setNextChoiceGenerator(cg);
2183 public boolean hasNextChoiceGenerator(){
2184 return vm.hasNextChoiceGenerator();
2187 public void checkNextChoiceGeneratorSet (String msg){
2188 if (!vm.hasNextChoiceGenerator()){
2189 throw new JPFException(msg);
2193 //--- call processing
2196 * note - this assumes the stackframe of the method to enter is already initialized and on top (pushed)
2198 public void enter (){
2199 MethodInfo mi = top.getMethodInfo();
2201 if (!mi.isJPFExecutable()){
2202 //printStackTrace();
2203 throw new JPFException("method is not JPF executable: " + mi);
2206 if (mi.isSynchronized()){
2207 int oref = mi.isStatic() ? mi.getClassInfo().getClassObjectRef() : top.getThis();
2208 ElementInfo ei = getModifiableElementInfo( oref);
2213 vm.notifyMethodEntered(this, mi);
2217 * note - this assumes the stackframe is still on top (not yet popped)
2219 * return true if any threads became unblocked due to a return from a sync method
2221 public boolean leave(){
2222 boolean didUnblock = false;
2223 MethodInfo mi = top.getMethodInfo();
2225 // <2do> - that's not really enough, we might have suspicious bytecode that fails
2226 // to release locks acquired by monitor_enter (e.g. by not having a handler that
2227 // monitor_exits & re-throws). That's probably shifted into the bytecode verifier
2228 // in the future (i.e. outside JPF), but maybe we should add an explicit test here
2229 // and report an error if the code does asymmetric locking (according to the specs,
2230 // VMs are allowed to silently fix this, so it might run on some and fail on others)
2232 if (mi.isSynchronized()) {
2233 int oref = mi.isStatic() ? mi.getClassInfo().getClassObjectRef() : top.getThis();
2234 ElementInfo ei = getElementInfo( oref);
2236 ei = ei.getModifiableInstance();
2237 didUnblock = ei.unlock(this);
2241 vm.notifyMethodExited(this, mi);
2247 * this should only be called from the top half of the last DIRECTCALLRETURN of
2249 * @return true - if the thread is done, false - if instruction has to be re-executed
2251 public boolean exit(){
2252 int objref = getThreadObjectRef();
2253 ElementInfo ei = getModifiableElementInfo(objref); // we are going to modify it no matter what
2254 SystemState ss = vm.getSystemState();
2255 Scheduler scheduler = getScheduler();
2257 enableEmptyTransition();
2259 // if this is the last non-daemon and there are only daemons left (which
2260 // would be killed by our termination) we have to give them a chance to
2261 // run BEFORE we terminate, to catch errors in those daemons we might have
2262 // triggered in our last transition. Even if a daemon has a proper CG
2263 // on the trigger that would expose the error subsequently, it would not be
2264 // scheduled anymore but hard terminated. This is even true if the trigger
2265 // is the last operation in the daemon since a host VM might preempt
2266 // on every instruction, not just CG insns (see .test.mc.DaemonTest)
2267 if (vm.getThreadList().hasOnlyMatchingOtherThan(this, vm.getDaemonRunnablePredicate())) {
2268 if (scheduler.setsRescheduleCG(this, "daemonTermination")) {
2273 // beware - this notifies all waiters for this thread (e.g. in a join())
2274 // hence it has to be able to acquire the lock
2275 if (!ei.canLock(this)) {
2276 // if we can't acquire the lock, it means there needs to be another live thread,
2277 // but it might not be runnable (deadlock) and we don't want to mask that error
2279 // block first, so that we don't get this thread in the list of CGs
2281 if (!scheduler.setsBlockedThreadCG(this, ei)){
2282 throw new JPFException("blocking thread termination without transition break");
2284 return false; // come back once we can obtain the lock to notify our waiters
2287 // we have to be able to acquire the group lock since we are going to remove
2288 // the thread from the group
2289 int grpRef = getThreadGroupRef();
2290 ElementInfo eiGrp = getModifiableElementInfo(grpRef);
2292 if (!eiGrp.canLock(this)){
2294 if (!scheduler.setsBlockedThreadCG(this, eiGrp)){
2295 throw new JPFException("blocking thread termination on group without transition break");
2297 return false; // come back once we can obtain the lock to update the group
2301 // Ok, we can get the lock, time to die
2303 // this simulates the ThreadGroup.remove(), which would cause a lot
2304 // of states if done in bytecode. Since we don't have a ThreadGroup peer
2305 // we keep all this code in ThreadInfo.
2306 // This might cause the ThreadGroup to notify waiters, which has to
2307 // happen before the notification on the thread object
2309 cleanupThreadGroup(grpRef, objref);
2313 // notify waiters on thread termination
2314 if (!holdsLock(ei)) {
2315 // we only need to increase the lockcount if we don't own the lock yet,
2316 // as is the case for synchronized run() in anonymous threads (otherwise
2317 // we have a lockcount > 1 and hence do not unlock upon return)
2321 ei.notifiesAll(); // watch out, this might change the runnable count
2324 setState(State.TERMINATED);
2326 // we don't unregister this thread yet, this happens when the corresponding
2327 // thread object is collected
2329 cleanupThreadObject(ei);
2330 vm.activateGC(); // stack is gone, so reachability might change
2332 // give the thread tracking policy a chance to remove this thread from
2333 // object/class thread sets
2334 scheduler.cleanupThreadTermination(this);
2336 if (vm.getThreadList().hasAnyMatchingOtherThan(this, getRunnableNonDaemonPredicate())) {
2337 if (!scheduler.setsTerminationCG(this)){
2338 throw new JPFException("thread termination without transition break");
2342 popFrame(); // we need to do this *after* setting the CG (so that we still have a CG insn)
2347 Predicate<ThreadInfo> getRunnableNonDaemonPredicate() {
2348 return new Predicate<ThreadInfo>() {
2350 public boolean isTrue (ThreadInfo ti) {
2351 return (ti.isRunnable() && !ti.isDaemon());
2357 * this is called upon ThreadInfo.exit() and corresponds to the private Thread.exit()
2359 void cleanupThreadObject (ElementInfo ei) {
2360 // ideally, this should be done by calling Thread.exit(), but this
2361 // does a ThreadGroup.remove(), which does a lot of sync stuff on the shared
2362 // ThreadGroup object, which might create lots of states. So we just nullify
2363 // the Thread fields and remove it from the ThreadGroup from here
2364 int grpRef = ei.getReferenceField("group");
2365 cleanupThreadGroup(grpRef, ei.getObjectRef());
2367 ei.setReferenceField("group", MJIEnv.NULL);
2368 ei.setReferenceField("threadLocals", MJIEnv.NULL);
2370 ei.setReferenceField("uncaughtExceptionHandler", MJIEnv.NULL);
2374 * this is called upon ThreadInfo.exit() and corresponds to ThreadGroup.remove(t), which is called from Thread.exit()
2376 * ideally this should be in the ThreadGroup peer, but we don't want to reference concrete peers from core (which is the
2378 * Since we already depend on ThreadGroup fields during VM initialization we just keep all Thread/ThreadGroup
2379 * related methods here
2381 void cleanupThreadGroup (int grpRef, int threadRef) {
2382 if (grpRef != MJIEnv.NULL) {
2383 ElementInfo eiGrp = getModifiableElementInfo(grpRef);
2384 int threadsRef = eiGrp.getReferenceField("threads");
2385 if (threadsRef != MJIEnv.NULL) {
2386 ElementInfo eiThreads = getModifiableElementInfo(threadsRef);
2387 if (eiThreads.isArray()) {
2388 int nthreads = eiGrp.getIntField("nthreads");
2390 for (int i=0; i<nthreads; i++) {
2391 int tref = eiThreads.getReferenceElement(i);
2393 if (tref == threadRef) { // compact the threads array
2394 int n1 = nthreads-1;
2395 for (int j=i; j<n1; j++) {
2396 eiThreads.setReferenceElement(j, eiThreads.getReferenceElement(j+1));
2398 eiThreads.setReferenceElement(n1, MJIEnv.NULL);
2400 eiGrp.setIntField("nthreads", n1);
2403 eiGrp.notifiesAll();
2407 // <2do> we should probably also check if we have to set it destroyed
2417 * this is called by the VM upon initialization of the main thread. At this point, we have a tiMain and a java.lang.Thread
2418 * object, but there is no ThreadGroup yet
2420 * This method is here to keep all Thread/ThreadGroup field dependencies in one place. The downside of not keeping this in
2421 * VM is that we can't override in order to have specialized ThreadInfos, but there is no factory for them anyways
2423 protected void createMainThreadObject (SystemClassLoaderInfo sysCl){
2424 //--- now create & initializeSharednessPolicy all the related JPF objects
2425 Heap heap = getHeap();
2427 ClassInfo ciThread = sysCl.threadClassInfo;
2428 ElementInfo eiThread = heap.newObject( ciThread, this);
2429 objRef = eiThread.getObjectRef();
2431 ElementInfo eiName = heap.newString(MAIN_NAME, this);
2432 int nameRef = eiName.getObjectRef();
2433 eiThread.setReferenceField("name", nameRef);
2435 ElementInfo eiGroup = createMainThreadGroup(sysCl);
2436 eiThread.setReferenceField("group", eiGroup.getObjectRef());
2438 eiThread.setIntField("priority", Thread.NORM_PRIORITY);
2440 ClassInfo ciPermit = sysCl.getResolvedClassInfo("java.lang.Thread$Permit");
2441 ElementInfo eiPermit = heap.newObject( ciPermit, this);
2442 eiPermit.setBooleanField("blockPark", true);
2443 eiThread.setReferenceField("permit", eiPermit.getObjectRef());
2445 addToThreadGroup(eiGroup);
2449 //--- set the thread running
2450 setState(ThreadInfo.State.RUNNING);
2455 * this creates and inits the main ThreadGroup object, which we have to do explicitly since
2456 * we can't execute bytecode yet
2458 protected ElementInfo createMainThreadGroup (SystemClassLoaderInfo sysCl) {
2459 Heap heap = getHeap();
2461 ClassInfo ciGroup = sysCl.getResolvedClassInfo("java.lang.ThreadGroup");
2462 ElementInfo eiThreadGrp = heap.newObject( ciGroup, this);
2464 ElementInfo eiGrpName = heap.newString("main", this);
2465 eiThreadGrp.setReferenceField("name", eiGrpName.getObjectRef());
2467 eiThreadGrp.setIntField("maxPriority", java.lang.Thread.MAX_PRIORITY);
2469 // 'threads' and 'nthreads' will be set later from createMainThreadObject
2475 * this is used for all thread objects, not just main
2477 protected void addToThreadGroup (ElementInfo eiGroup){
2478 FieldInfo finThreads = eiGroup.getFieldInfo("nthreads");
2479 int nThreads = eiGroup.getIntField(finThreads);
2481 if (eiGroup.getBooleanField("destroyed")){
2482 env.throwException("java.lang.IllegalThreadStateException");
2485 FieldInfo fiThreads = eiGroup.getFieldInfo("threads");
2486 int threadsRef = eiGroup.getReferenceField(fiThreads);
2488 if (threadsRef == MJIEnv.NULL){
2489 threadsRef = env.newObjectArray("Ljava/lang/Thread;", 1);
2490 env.setReferenceArrayElement(threadsRef, 0, objRef);
2491 eiGroup.setReferenceField(fiThreads, threadsRef);
2494 int newThreadsRef = env.newObjectArray("Ljava/lang/Thread;", nThreads+1);
2495 ElementInfo eiNewThreads = env.getElementInfo(newThreadsRef);
2496 ElementInfo eiThreads = env.getElementInfo(threadsRef);
2498 for (int i=0; i<nThreads; i++){
2499 int tr = eiThreads.getReferenceElement(i);
2500 eiNewThreads.setReferenceElement(i, tr);
2503 eiNewThreads.setReferenceElement(nThreads, objRef);
2504 eiGroup.setReferenceField(fiThreads, newThreadsRef);
2507 eiGroup.setIntField(finThreads, nThreads+1);
2509 /** <2do> we don't model this yet
2510 FieldInfo finUnstartedThreads = eiGroup.getFieldInfo("nUnstartedThreads");
2511 int nUnstarted = eiGroup.getIntField(finUnstartedThreads);
2512 eiGroup.setIntField(finUnstartedThreads, nUnstarted-1);
2518 public void hash (HashData hd) {
2519 threadData.hash(hd);
2521 for (StackFrame f = top; f != null; f = f.getPrevious()){
2526 public void interrupt () {
2527 ElementInfo eiThread = getModifiableElementInfo(getThreadObjectRef());
2529 State status = getState();
2537 // just set interrupt flag
2538 eiThread.setBooleanField("interrupted", true);
2542 case TIMEOUT_WAITING:
2543 eiThread.setBooleanField("interrupted", true);
2544 setState(State.INTERRUPTED);
2546 // since this is potentially called w/o owning the wait lock, we
2547 // have to check if the waiter goes directly to UNBLOCKED
2548 ElementInfo eiLock = getElementInfo(lockRef);
2549 if (eiLock.canLock(this)) {
2551 setState(State.UNBLOCKED);
2553 // we cannot yet remove this thread from the Monitor lock contender list
2554 // since it still has to re-acquire the lock before becoming runnable again
2556 // NOTE: this can lead to attempts to reenter the same thread to the
2557 // lock contender list if the interrupt handler of the interrupted thread
2558 // tries to wait/block/park again
2559 //eiLock.setMonitorWithoutLocked(this);
2574 * mark all objects during gc phase1 which are reachable from this threads
2575 * root set (Thread object, Runnable, stack)
2578 void markRoots (Heap heap) {
2580 // 1. mark the Thread object itself
2581 heap.markThreadRoot(objRef, id);
2583 // 2. and its runnable
2584 if (targetRef != MJIEnv.NULL) {
2585 heap.markThreadRoot(targetRef,id);
2588 // 3. if we have a pending exception that wasn't handled, make sure the exception object is not collected before we match states
2589 if (pendingException != null){
2590 heap.markThreadRoot(pendingException.getExceptionReference(), id);
2593 // 4. now all references on the stack
2594 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
2595 frame.markThreadRoots(heap, id);
2601 * replace the top frame - this is a dangerous method that should only
2602 * be used from Restoreres and to restore operators and locals in post-execution notifications
2603 * to their pre-execution contents
2605 public void setTopFrame (StackFrame frame) {
2608 // since we have swapped the top frame, the stackDepth might have changed
2610 for (StackFrame f = frame; f != null; f = f.getPrevious()){
2617 * Adds a new stack frame for a new called method.
2619 public void pushFrame (StackFrame frame) {
2621 frame.setPrevious(top);
2626 // a new frame cannot have been stored yet, so we don't need to clone on the next mod
2627 // note this depends on not pushing a frame in the top half of a CG method
2630 returnedDirectCall = null;
2634 * Removes a stack frame
2636 public void popFrame() {
2637 StackFrame frame = top;
2639 //--- do our housekeeping
2640 if (frame.hasAnyRef()) {
2641 vm.getSystemState().activateGC();
2644 // there always is one since we start all threads through directcalls
2645 top = frame.getPrevious();
2649 public StackFrame popAndGetModifiableTopFrame() {
2652 if (top.isFrozen()) {
2659 public StackFrame popAndGetTopFrame(){
2665 * removing DirectCallStackFrames is a bit different (only happens from
2666 * DIRECTCALLRETURN insns)
2668 public StackFrame popDirectCallFrame() {
2669 //assert top instanceof DirectCallStackFrame;
2671 returnedDirectCall = (DirectCallStackFrame) top;
2673 if (top.hasFrameAttr( UncaughtHandlerAttr.class)){
2674 return popUncaughtHandlerFrame();
2677 top = top.getPrevious();
2684 public boolean hasReturnedFromDirectCall () {
2685 // this is reset each time we push a new frame
2686 return (returnedDirectCall != null);
2689 public boolean hasReturnedFromDirectCall(String directCallId){
2690 return (returnedDirectCall != null &&
2691 returnedDirectCall.getMethodName().equals(directCallId));
2694 public DirectCallStackFrame getReturnedDirectCall () {
2695 return returnedDirectCall;
2699 public String getStateDescription () {
2700 StringBuilder sb = new StringBuilder("thread ");
2701 sb.append(getThreadObjectClassInfo().getName());
2705 sb.append(threadData.getFieldValues());
2708 return sb.toString();
2711 public ClassInfo getThreadObjectClassInfo() {
2712 return getThreadObject().getClassInfo();
2716 * Prints the content of the stack
2718 public void printStackContent () {
2719 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
2720 frame.printStackContent();
2725 * Prints current stacktrace information
2727 public void printStackTrace () {
2728 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
2729 frame.printStackTrace();
2733 protected boolean haltOnThrow (String exceptionClassName){
2734 if ((haltOnThrow != null) && (haltOnThrow.matchesAny(exceptionClassName))){
2741 protected Instruction throwStopException (){
2742 // <2do> maybe we should do a little sanity check first
2743 ElementInfo ei = getModifiableThreadObject();
2745 int xRef = ei.getReferenceField("stopException");
2746 ei.setReferenceField("stopException", MJIEnv.NULL);
2747 attributes &= ~ATTR_SET_STOPPED; // otherwise we would throw again if thread gets still executed
2749 Instruction insn = getPC();
2750 if (insn instanceof EXECUTENATIVE){
2751 // we only get here if there was a CG in a native method and we might
2752 // have to reacquire a lock to go on
2754 // <2do> it would be better if we could avoid to enter the native method
2755 // since it might have side effects like overwriting the exception or
2756 // doing roundtrips in its bottom half, but we don't know which lock that
2757 // is (lockRef might be already reset)
2759 env.throwException(xRef);
2763 return throwException(xRef);
2767 * this is basically a side-effect free version of throwException to determine if a given
2768 * exception will be handled.
2770 public HandlerContext getHandlerContextFor (ClassInfo ciException){
2771 ExceptionHandler matchingHandler = null; // the matching handler we found (if any)
2773 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()) {
2774 // that means we have to turn the exception into an InvocationTargetException
2775 if (frame.isReflection()) {
2776 ciException = ClassInfo.getInitializedSystemClassInfo("java.lang.reflect.InvocationTargetException", this);
2779 matchingHandler = frame.getHandlerFor( ciException);
2780 if (matchingHandler != null){
2781 return new HandlerContext( this, ciException, frame, matchingHandler);
2785 if (!ignoreUncaughtHandlers && !isUncaughtHandlerOnStack()) {
2787 if ((uchRef = getInstanceUncaughtHandler()) != MJIEnv.NULL) {
2788 return new HandlerContext( this, ciException, HandlerContext.UncaughtHandlerType.INSTANCE, uchRef);
2791 int grpRef = getThreadGroupRef();
2792 if ((uchRef = getThreadGroupUncaughtHandler(grpRef)) != MJIEnv.NULL) {
2793 return new HandlerContext( this, ciException, HandlerContext.UncaughtHandlerType.GROUP, uchRef);
2796 if ((uchRef = getGlobalUncaughtHandler()) != MJIEnv.NULL) {
2797 return new HandlerContext( this, ciException, HandlerContext.UncaughtHandlerType.GLOBAL, uchRef);
2805 * unwind stack frames until we find a matching handler for the exception object
2807 public Instruction throwException (int exceptionObjRef) {
2808 Heap heap = vm.getHeap();
2809 ElementInfo eiException = heap.get(exceptionObjRef);
2810 ClassInfo ciException = eiException.getClassInfo();
2811 String exceptionName = ciException.getName();
2812 StackFrame handlerFrame = null; // the stackframe that has a matching handler (if any)
2813 ExceptionHandler matchingHandler = null; // the matching handler we found (if any)
2815 // first, give the VM a chance to intercept (we do this before changing anything)
2816 Instruction insn = vm.handleException(this, exceptionObjRef);
2821 // we don't have to store the stacktrace explicitly anymore, since that is now
2822 // done in the Throwable ctor (more specifically the native fillInStackTrace)
2823 pendingException = new ExceptionInfo(this, eiException);
2825 vm.notifyExceptionThrown(this, eiException);
2827 if (haltOnThrow(exceptionName)) {
2828 // shortcut - we don't try to find a handler for this one but bail immediately
2829 //NoUncaughtExceptionsProperty.setExceptionInfo(pendingException);
2830 throw new UncaughtException(this, exceptionObjRef);
2833 // check if we find a matching handler, and if we do store it. Leave the
2834 // stack untouched so that listeners can still inspect it
2835 for (StackFrame frame = top; (frame != null) && (handlerFrame == null); frame = frame.getPrevious()) {
2836 // that means we have to turn the exception into an InvocationTargetException
2837 if (frame.isReflection()) {
2838 // make sure it's in the startup class set since we are not able to re-execute here
2839 ciException = ClassInfo.getInitializedSystemClassInfo("java.lang.reflect.InvocationTargetException", this);
2840 exceptionObjRef = createException(ciException, exceptionName, exceptionObjRef);
2841 exceptionName = ciException.getName();
2842 eiException = heap.get(exceptionObjRef);
2843 pendingException = new ExceptionInfo(this, eiException);
2846 matchingHandler = frame.getHandlerFor( ciException);
2847 if (matchingHandler != null){
2848 handlerFrame = frame;
2852 if ((handlerFrame == null) && frame.isFirewall()) {
2853 // this method should not let exceptionHandlers pass into lower level stack frames
2854 // (e.g. for <clinit>, or hidden direct calls)
2855 // <2do> if this is a <clinit>, we should probably turn into an
2856 // ExceptionInInitializerError first
2858 //NoUncaughtExceptionsProperty.setExceptionInfo(pendingException);
2859 throw new UncaughtException(this, exceptionObjRef);
2863 if (handlerFrame == null) {
2864 // we still have to check if there is a Thread.UncaughtExceptionHandler in effect,
2865 // and if we already enter within one, in which case we don't reenter it
2866 if (!ignoreUncaughtHandlers && !isUncaughtHandlerOnStack()) {
2867 // we use a direct call instead of exception handlers within the run()/main()
2868 // direct call methods because we want to preserve the whole stack in case
2869 // we treat returned (report-only) handlers as NoUncaughtExceptionProperty
2870 // violations (passUncaughtHandler=false)
2871 insn = callUncaughtHandler(pendingException);
2873 // we only do this if there is a UncaughtHandler other than the standard
2874 // ThreadGroup, hence we have to check for the return value. If there is
2875 // only ThreadGroup.uncaughtException(), we put the system out of its misery
2880 // there was no overridden uncaughtHandler, or we already executed it
2881 if ("java.lang.ThreadDeath".equals(exceptionName)) { // gracefully shut down
2882 unwindToFirstFrame();
2883 pendingException = null;
2884 return top.getPC().getNext(); // the final DIRECTCALLRETURN
2886 } else { // we have a NoUncaughtPropertyViolation
2887 //NoUncaughtExceptionsProperty.setExceptionInfo(pendingException);
2888 throw new UncaughtException(this, exceptionObjRef);
2891 } else { // we found a matching handler
2893 unwindTo(handlerFrame);
2895 // according to the VM spec, before transferring control to the handler we have
2896 // to reset the operand stack to contain only the exception reference
2897 // (4.9.2 - "4. merge the state of the operand stack..")
2898 handlerFrame = getModifiableTopFrame();
2899 handlerFrame.setExceptionReference(exceptionObjRef);
2901 // jump to the exception handler and set pc so that listeners can see it
2902 int handlerOffset = matchingHandler.getHandler();
2903 insn = handlerFrame.getMethodInfo().getInstructionAt(handlerOffset);
2904 handlerFrame.setPC(insn);
2906 // notify before we reset the pendingException
2907 vm.notifyExceptionHandled(this);
2909 pendingException = null; // handled, no need to keep it
2916 * is there any UncaughHandler in effect for this thread?
2917 * NOTE - this doesn't check if we are already executing one (i.e. it would still handle an exception)
2918 * or if uncaughtHandlers are enabled within JPF
2920 public boolean hasUncaughtHandler (){
2921 if (getInstanceUncaughtHandler() != MJIEnv.NULL){
2925 int grpRef = getThreadGroupRef();
2926 if (getThreadGroupUncaughtHandler(grpRef) != MJIEnv.NULL){
2930 if (getGlobalUncaughtHandler() != MJIEnv.NULL){
2938 * this explicitly models the standard ThreadGroup.uncaughtException(), but we want
2939 * to save us a roundtrip if that's the only handler we got. If we would use
2940 * a handler block in the run()/main() direct call stubs that just delegate to
2941 * the standard ThreadGroup.uncaughtException(), we would have trouble mapping
2942 * this to NoUncaughtExceptionProperty violations (which is just a normal
2943 * printStackTrace() in there).
2945 protected Instruction callUncaughtHandler (ExceptionInfo xi){
2946 Instruction insn = null;
2948 // 1. check if this thread has its own uncaughtExceptionHandler set. If not,
2949 // hand it over to ThreadGroup.uncaughtException()
2950 int hRef = getInstanceUncaughtHandler();
2951 if (hRef != MJIEnv.NULL){
2952 insn = callUncaughtHandler(xi, hRef, "[threadUncaughtHandler]");
2955 // 2. check if any of the ThreadGroup chain has an overridden uncaughtException
2956 int grpRef = getThreadGroupRef();
2957 hRef = getThreadGroupUncaughtHandler(grpRef);
2959 if (hRef != MJIEnv.NULL){
2960 insn = callUncaughtHandler(xi, hRef, "[threadGroupUncaughtHandler]");
2963 // 3. as a last measure, check if there is a global handler
2964 hRef = getGlobalUncaughtHandler();
2965 if (hRef != MJIEnv.NULL){
2966 insn = callUncaughtHandler(xi, hRef, "[globalUncaughtHandler]");
2974 protected boolean isUncaughtHandlerOnStack(){
2975 for (StackFrame frame = top; frame != null; frame = frame.getPrevious()) {
2976 if (frame.hasFrameAttr(UncaughtHandlerAttr.class)){
2984 protected int getInstanceUncaughtHandler (){
2985 ElementInfo ei = getElementInfo(objRef);
2986 int handlerRef = ei.getReferenceField("uncaughtExceptionHandler");
2990 protected int getThreadGroupRef() {
2991 ElementInfo ei = getElementInfo(objRef);
2992 int groupRef = ei.getReferenceField("group");
2996 protected int getThreadGroupUncaughtHandler (int grpRef){
2998 // get the first overridden uncaughtException() implementation in the group chain
2999 while (grpRef != MJIEnv.NULL){
3000 ElementInfo eiGrp = getElementInfo(grpRef);
3001 ClassInfo ciGrp = eiGrp.getClassInfo();
3002 MethodInfo miHandler = ciGrp.getMethod("uncaughtException(Ljava/lang/Thread;Ljava/lang/Throwable;)V", true);
3003 ClassInfo ciHandler = miHandler.getClassInfo();
3004 if (!ciHandler.getName().equals("java.lang.ThreadGroup")) {
3005 return eiGrp.getObjectRef();
3008 grpRef = eiGrp.getReferenceField("parent");
3011 // no overridden uncaughtHandler found
3015 protected int getGlobalUncaughtHandler(){
3016 ElementInfo ei = getElementInfo(objRef);
3017 ClassInfo ci = ei.getClassInfo();
3018 FieldInfo fi = ci.getStaticField("defaultUncaughtExceptionHandler");
3020 // the field is in our java.lang.Thread, but the concrete thread object class might differ
3021 ClassInfo fci = fi.getClassInfo();
3022 return fci.getStaticElementInfo().getReferenceField(fi);
3026 * using an attribute to mark DirectCallStackFrames executing uncaughtHandlers is not
3027 * ideal, but the case is so exotic that we don't want another concrete StackFrame subclass that
3028 * would have to be created through the ClassInfo factory. Apart from retrieving the
3029 * ExceptionInfo this is just a normal DirectCallStackFrame.
3030 * We could directly use ExceptionInfo, but it seems more advisable to have a dedicated,
3031 * private type. This could also store execution state
3033 class UncaughtHandlerAttr implements SystemAttribute {
3034 ExceptionInfo xInfo;
3036 UncaughtHandlerAttr (ExceptionInfo xInfo){
3040 ExceptionInfo getExceptionInfo(){
3045 protected Instruction callUncaughtHandler (ExceptionInfo xi, int handlerRef, String id){
3046 ElementInfo eiHandler = getElementInfo(handlerRef);
3047 ClassInfo ciHandler = eiHandler.getClassInfo();
3048 MethodInfo miHandler = ciHandler.getMethod("uncaughtException(Ljava/lang/Thread;Ljava/lang/Throwable;)V", true);
3050 // we have to clear this here in case there is a CG while executing the uncaughtHandler
3051 pendingException = null;
3053 DirectCallStackFrame frame = miHandler.createDirectCallStackFrame(this, 0);
3054 int argOffset = frame.setReferenceArgument( 0, handlerRef, null);
3055 argOffset = frame.setReferenceArgument( argOffset, objRef, null);
3056 frame.setReferenceArgument( argOffset, xi.getExceptionReference(), null);
3058 UncaughtHandlerAttr uchContext = new UncaughtHandlerAttr( xi);
3059 frame.setFrameAttr( uchContext);
3062 return frame.getPC();
3065 protected StackFrame popUncaughtHandlerFrame(){
3066 // we return from an overridden uncaughtException() direct call, but
3067 // its debatable if this counts as 'handled'. For handlers that just do
3068 // reporting this is probably false and we want JPF to report the defect.
3069 // If this is a fail-safe handler that tries to clean up so that other threads can
3070 // take over, we want to be able to go on and properly shut down the
3071 // thread without property violation
3073 if (passUncaughtHandler) {
3074 // gracefully shutdown this thread
3075 unwindToFirstFrame(); // this will take care of notifying
3077 getModifiableTopFrame().advancePC();
3078 assert top.getPC() instanceof ReturnInstruction : "topframe PC not a ReturnInstruction: " + top.getPC();
3082 // treat this still as an NoUncaughtExceptionProperty violation
3083 UncaughtHandlerAttr ctx = returnedDirectCall.getFrameAttr(UncaughtHandlerAttr.class);
3084 pendingException = ctx.getExceptionInfo();
3085 //NoUncaughtExceptionsProperty.setExceptionInfo(pendingException);
3086 throw new UncaughtException(this, pendingException.getExceptionReference());
3091 protected void unwindTo (StackFrame newTopFrame){
3092 for (StackFrame frame = top; (frame != null) && (frame != newTopFrame); frame = frame.getPrevious()) {
3093 leave(); // that takes care of releasing locks
3094 vm.notifyExceptionBailout(this); // notify before we pop the frame
3099 protected StackFrame unwindToFirstFrame(){
3102 for (frame = top; frame.getPrevious() != null; frame = frame.getPrevious()) {
3103 leave(); // that takes care of releasing locks
3104 vm.notifyExceptionBailout(this); // notify before we pop the frame
3111 public ExceptionInfo getPendingException () {
3112 return pendingException;
3116 * watch out - just clearing it might cause an infinite loop
3117 * if we don't drop frames and/or advance the pc
3119 public void clearPendingException () {
3120 //NoUncaughtExceptionsProperty.setExceptionInfo(null);
3121 pendingException = null;
3125 * Returns a clone of the thread data. To be called every time we change some ThreadData field
3126 * (which unfortunately includes lock counts, hence this should be changed)
3128 protected ThreadData threadDataClone () {
3129 if ((attributes & ATTR_DATA_CHANGED) != 0) {
3130 // already cloned, so we don't have to clone
3132 // reset, so that next storage request would recompute tdIndex
3134 vm.kernelStateChanged();
3136 threadData = threadData.clone();
3142 public void restoreThreadData(ThreadData td) {
3148 * this is a generic request to reschedule that is not based on instruction type
3149 * Note that we check for a mandatory CG, i.e. the policy has to return a CG to make sure
3150 * there is a transition break. We still go through the policy object for selection
3151 * of scheduling choices though
3154 public boolean reschedule (String reason){
3155 return getScheduler().setsRescheduleCG(this, reason);
3159 * this is a version that unconditionally breaks the current transition
3160 * without really adding choices. It only goes on with the same thread
3161 * (to avoid state explosion). Since we require a break, and there is no
3162 * choice (current thread is supposed to continue), there is no point
3163 * going through the SyncPolicy
3165 * if the current transition is already marked as ignored, this method does nothing
3167 public boolean breakTransition(String reason) {
3168 SystemState ss = vm.getSystemState();
3170 // no need to set a CG if this transition is already marked as ignored
3171 // (which will also break executeTransition)
3172 BreakGenerator cg = new BreakGenerator(reason, this, false);
3173 return ss.setNextChoiceGenerator(cg); // this breaks the transition
3177 * this breaks the current transition with a CG that forces an end state (i.e.
3179 * this only takes effect if the current transition is not already marked
3182 public boolean breakTransition(boolean isTerminator) {
3183 SystemState ss = vm.getSystemState();
3185 if (!ss.isIgnored()){
3186 BreakGenerator cg = new BreakGenerator( "breakTransition", this, isTerminator);
3187 return ss.setNextChoiceGenerator(cg); // this breaks the transition
3193 public boolean hasOtherRunnables () {
3194 return vm.getThreadList().hasAnyMatchingOtherThan(this, vm.getRunnablePredicate());
3197 protected void markUnchanged() {
3198 attributes &= ~ATTR_ANY_CHANGED;
3201 protected void markTfChanged(StackFrame frame) {
3202 attributes |= ATTR_STACK_CHANGED;
3203 vm.kernelStateChanged();
3206 protected void markTdChanged() {
3207 attributes |= ATTR_DATA_CHANGED;
3208 vm.kernelStateChanged();
3211 public StackFrame getCallerStackFrame() {
3213 return top.getPrevious();
3219 public int mixinExecutionStateHash(int h) {
3220 for (StackFrame frame = top; frame != null; frame = frame.prev) {
3221 if (!frame.isNative()) {
3222 h = frame.mixinExecutionStateHash(h);
3229 public boolean hasDataChanged() {
3230 return (attributes & ATTR_DATA_CHANGED) != 0;
3233 public boolean hasStackChanged() {
3234 return (attributes & ATTR_STACK_CHANGED) != 0;
3237 public boolean hasChanged() {
3238 return (attributes & ATTR_ANY_CHANGED) != 0;
3242 * Returns a clone of the top stack frame.
3244 public StackFrame getModifiableTopFrame () {
3245 if (top.isFrozen()) {
3253 * Returns the top stack frame.
3255 public StackFrame getTopFrame () {
3260 * this replaces all frames up from 'frame' to 'top' with modifiable ones.
3262 * NOTE - you can't use this AFTER getModifiableTopFrame() since it changes top. Because
3263 * it is inherently unsafe, it should be used with care and you have to make sure nothing
3264 * else has stored top references, or respective references are updated after this call.
3266 * NOTE also that we assume there is no frozen frame on top of an unfrozen one
3267 * <2do> that should probably be reported as an error since it is a stack consistency violation
3269 public StackFrame getModifiableFrame (StackFrame frame){
3270 StackFrame newTop = null;
3271 StackFrame last = null;
3272 boolean done = false;
3274 for (StackFrame f = top; f != null; f = f.getPrevious()){
3275 done = (f == frame);
3279 if (newTop == null){
3282 last.setPrevious(f);
3289 if (newTop != null){
3297 return null; // it wasn't on the callstack
3301 * note that we don't provide a modifiable version of this. All modifications
3302 * should only happen in the executing (top) frame
3304 public StackFrame getStackFrameExecuting (Instruction insn, int offset){
3306 StackFrame frame = top;
3308 for (; (n > 0) && frame != null; frame = frame.getPrevious()){
3312 for(; frame != null; frame = frame.getPrevious()){
3313 if (frame.getPC() == insn){
3322 public String toString() {
3323 return "ThreadInfo [name=" + getName() +
3325 ",state=" + getStateName() +
3326 // ",obj=" + Integer.toHexString(getThreadObjectRef()) +
3330 void setDaemon (boolean isDaemon) {
3331 threadDataClone().isDaemon = isDaemon;
3334 public boolean isDaemon () {
3335 return threadData.isDaemon;
3338 public MJIEnv getMJIEnv () {
3342 void setName (String newName) {
3343 threadDataClone().name = newName;
3345 // see 'setPriority()', only that it's more serious here, because the
3346 // java.lang.Thread name is stored as a char[]
3349 public void setPriority (int newPrio) {
3350 if (threadData.priority != newPrio) {
3351 threadDataClone().priority = newPrio;
3353 // note that we don't update the java.lang.Thread object, but
3354 // use our threadData value (which works because the object
3355 // values are just used directly from the Thread ctors (from where we pull
3356 // it out in our ThreadInfo ctor), and henceforth only via our intercepted
3361 public int getPriority () {
3362 return threadData.priority;
3366 * Comparison for sorting based on index.
3369 public int compareTo (ThreadInfo that) {
3370 return this.id - that.id;
3375 * only for debugging purposes
3377 public void checkConsistency(boolean isStore){
3378 checkAssertion(threadData != null, "no thread data");
3380 // if the thread is runnable, it can't be blocked
3382 checkAssertion(lockRef == MJIEnv.NULL, "runnable thread with non-null lockRef: " + lockRef) ;
3385 // every thread that has been started and is not terminated has to have a stackframe with a next pc
3386 if (!isTerminated() && !isNew()){
3387 checkAssertion( stackDepth > 0, "empty stack " + getState());
3388 checkAssertion( top != null, "no top frame");
3389 checkAssertion( top.getPC() != null, "no top PC");
3392 // if we are timedout, the top pc has to be either on a native Object.wait() or Unsafe.park()
3394 Instruction insn = top.getPC();
3395 checkAssertion( insn instanceof EXECUTENATIVE, "thread timedout outside of native method");
3397 // this is a bit dangerous in case we introduce new timeout points
3398 MethodInfo mi = ((EXECUTENATIVE)insn).getExecutedMethod();
3399 String mname = mi.getUniqueName();
3400 checkAssertion( mname.equals("wait(J") || mname.equals("park(ZJ"), "timedout thread outside timeout method: " + mi.getFullName());
3403 List<ElementInfo> lockedObjects = getLockedObjects();
3405 if (lockRef != MJIEnv.NULL){
3406 // object we are blocked on has to exist
3407 ElementInfo ei = this.getElementInfo(lockRef);
3408 checkAssertion( ei != null, "thread locked on non-existing object: " + lockRef);
3410 // we have to be in the lockedThreads list of that objects monitor
3411 checkAssertion( ei.isLocking(this), "thread blocked on non-locking object: " + ei);
3413 // can't be blocked on a lock we own (but could be in waiting before giving it up)
3414 if (!isWaiting() && lockedObjectReferences.length > 0){
3415 for (ElementInfo lei : lockedObjects){
3416 checkAssertion( lei.getObjectRef() != lockRef, "non-waiting thread blocked on owned lock: " + lei);
3420 // the state has to be BLOCKED, NOTIFIED, WAITING or TIMEOUT_WAITING
3421 checkAssertion( isWaiting() || isBlockedOrNotified(), "locked thread not waiting, blocked or notified");
3423 } else { // no lockRef set
3424 checkAssertion( !isWaiting() && !isBlockedOrNotified(), "non-locked thread is waiting, blocked or notified");
3427 // if we have locked objects, we have to be the locking thread of each of them
3428 if (lockedObjects != null && !lockedObjects.isEmpty()){
3429 for (ElementInfo ei : lockedObjects){
3430 ThreadInfo lti = ei.getLockingThread();
3432 checkAssertion(lti == this, "not the locking thread for locked object: " + ei + " owned by " + lti);
3434 // can happen for a nested monitor lockout
3441 protected void checkAssertion(boolean cond, String failMsg){
3443 System.out.println("!!!!!! failed thread consistency: " + this + ": " + failMsg);
3444 vm.dumpThreadStates();
3449 public boolean isSystemThread() {