// // Copyright (C) 2006 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.vm; import java.io.PrintWriter; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPF; import gov.nasa.jpf.JPFConfigException; import gov.nasa.jpf.JPFException; import gov.nasa.jpf.JPFListenerException; import gov.nasa.jpf.jvm.ClassFile; import gov.nasa.jpf.jvm.bytecode.FieldInstruction; import gov.nasa.jpf.search.Search; import gov.nasa.jpf.util.JPFLogger; import gov.nasa.jpf.util.Misc; import gov.nasa.jpf.util.Predicate; /** * This class represents the virtual machine. The virtual machine is able to * move backward and forward one transition at a time. */ public abstract class VM { /** * this is a debugging aid to control compilation of expensive consistency checks * (we don't control these with class-wise assertion enabling since we do use * unconditional assertions for mandatory consistency checks) */ public static final boolean CHECK_CONSISTENCY = false; protected static final String[] EMPTY_ARGS = new String[0]; protected static JPFLogger log = JPF.getLogger("vm"); /** * our execution context */ protected JPF jpf; /** * The number of errors saved so far. * Used to generate the name of the error trail file. */ protected static int error_id; /** * <2do> - this is a hack to be removed once there are no static references * anymore */ protected static VM vm; static { initStaticFields(); } protected SystemState ss; protected FunctionObjectFactory funcObjFactory = new FunctionObjectFactory(); // <2do> - if you are confused about the various pieces of state and its // storage/backtrack structures, I'm with you. It's mainly an attempt to // separate non-policy VM state (objects), policy VM state (Scheduler) // and general JPF execution state, with special support for stack oriented // state restoration (backtracking). // this needs to be cleaned up and the principle reinstated protected Path path; /** execution path to current state */ protected StringBuilder out; /** buffer to store output along path execution */ /** * various caches for VMListener state acquisition. NOTE - these are only * valid during notification * * <2do> get rid of the 'lasts' in favor of queries on the insn, the executing * thread, and the VM. This is superfluous work to for every notification * (even if there are no listeners using it) that can easily lead to inconsistencies */ protected Transition lastTrailInfo; protected boolean isTraceReplay; // can be set by listeners to indicate this is a replay /** the repository we use to find out if we already have seen a state */ protected StateSet stateSet; /** this was the last stateId - note this is also used for stateless model checking */ protected int newStateId; /** the structure responsible for storing and restoring backtrack info */ protected Backtracker backtracker; /** optional serializer/restorer to support backtracker */ protected StateRestorer<?> restorer; /** optional serializer to support stateSet */ protected StateSerializer serializer; /** potential execution listeners. We keep them in a simple array to avoid creating objects on each notification */ protected VMListener[] listeners = new VMListener[0]; /** did we get a new transition */ protected boolean transitionOccurred; /** how we model execution time */ protected TimeModel timeModel; protected Config config; // that's for the options we use only once // VM options we use frequently protected boolean runGc; protected boolean treeOutput; protected boolean pathOutput; protected boolean indentOutput; protected boolean processFinalizers; // <2do> there are probably many places where this should be used protected boolean isBigEndian; protected boolean initialized; //thread filters protected Predicate<ThreadInfo> userliveNonDaemonPredicate; protected Predicate<ThreadInfo> timedoutRunnablePredicate; protected Predicate<ThreadInfo> alivePredicate; protected Predicate<ThreadInfo> userTimedoutRunnablePredicate; // a list of actions to be run post GC. This is a bit redundant to VMListener, // but in addition to avoid the per-instruction execution overhead of a VMListener // we want a (internal) mechanism that is on-demand only, i.e. processed // actions are removed from the list protected ArrayList<Runnable> postGcActions = new ArrayList<Runnable>(); /** * be prepared this might throw JPFConfigExceptions */ public VM (JPF jpf, Config conf) { this.jpf = jpf; // so that we know who instantiated us // <2do> that's really a bad hack and should be removed once we // have cleaned up the reference chains vm = this; config = conf; runGc = config.getBoolean("vm.gc", true); treeOutput = config.getBoolean("vm.tree_output", true); // we have to defer setting pathOutput until we have a reporter registered indentOutput = config.getBoolean("vm.indent_output",false); processFinalizers = config.getBoolean("vm.process_finalizers", false); isBigEndian = getPlatformEndianness(config); initialized = false; initTimeModel(config); initSubsystems(config); initFields(config); // set predicates used to query from threadlist userliveNonDaemonPredicate = new Predicate<ThreadInfo>() { @Override public boolean isTrue (ThreadInfo ti) { return (!ti.isDaemon() && !ti.isTerminated() && !ti.isSystemThread()); } }; timedoutRunnablePredicate = new Predicate<ThreadInfo>() { @Override public boolean isTrue (ThreadInfo ti) { return (ti.isTimeoutRunnable()); } }; userTimedoutRunnablePredicate = new Predicate<ThreadInfo>() { @Override public boolean isTrue (ThreadInfo ti) { return (ti.isTimeoutRunnable() && !ti.isSystemThread()); } }; alivePredicate = new Predicate<ThreadInfo>() { @Override public boolean isTrue (ThreadInfo ti) { return (ti.isAlive()); } }; } /** * just here for unit test mockups, don't use as implicit base ctor in * VM derived classes */ protected VM (){} public JPF getJPF() { return jpf; } public void initFields (Config config) { path = new Path("fix-this!"); out = null; ss = new SystemState(config, this); stateSet = config.getInstance("vm.storage.class", StateSet.class); if (stateSet != null) { stateSet.attach(this); } backtracker = config.getEssentialInstance("vm.backtracker.class", Backtracker.class); backtracker.attach(this); newStateId = -1; } protected void initSubsystems (Config config) { ClassLoaderInfo.init(config); ClassInfo.init(config); ThreadInfo.init(config); ElementInfo.init(config); MethodInfo.init(config); NativePeer.init(config); FieldInstruction.init(config); ChoiceGeneratorBase.init(config); // peer classes get initialized upon NativePeer creation } protected void initTimeModel (Config config){ Class<?>[] argTypes = { VM.class, Config.class }; Object[] args = { this, config }; timeModel = config.getEssentialInstance("vm.time.class", TimeModel.class, argTypes, args); } /** * called after the JPF run is finished. Shouldn't be public, but is called by JPF */ public void cleanUp(){ // nothing yet } protected boolean getPlatformEndianness (Config config){ String endianness = config.getString("vm.endian"); if (endianness == null) { return ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; } else if (endianness.equalsIgnoreCase("big")) { return true; } else if (endianness.equalsIgnoreCase("little")) { return false; } else { config.exception("illegal vm.endian value: " + endianness); return false; // doesn't matter } } public boolean isBigEndianPlatform(){ return isBigEndian; } public boolean finalizersEnabled() { return processFinalizers; } public boolean isInitialized() { return initialized; } public boolean isSingleProcess() { return true; } /** * do we see our model classes? Some of them cannot be used from the standard CLASSPATH, because they * are tightly coupled with the JPF core (e.g. java.lang.Class, java.lang.Thread, * java.lang.StackTraceElement etc.) * Our strategy here is kind of lame - we just look into java.lang.Class if we find the 'uniqueId' field * (that's a true '42') */ static boolean checkSystemClassCompatibility (SystemClassLoaderInfo systemLoader) { ClassInfo ci = systemLoader.getClassClassInfo(); return ci.checkIfValidClassClassInfo(); } static boolean isValidClassName (String clsName) { if ( !clsName.matches("[a-zA-Z_$][a-zA-Z_$0-9.]*")) { return false; } // well, those two could be part of valid class names, but // in all likeliness somebody specified a filename instead of // a classname if (clsName.endsWith(".java")) { return false; } if (clsName.endsWith(".class")) { return false; } return true; } //--- ThreadInfo factory methods protected ThreadInfo createMainThreadInfo (int id, ApplicationContext appCtx){ ThreadInfo tiMain = new ThreadInfo( this, id, appCtx); ThreadInfo.currentThread = tiMain; // we still need this for listeners that process startup class loading events registerThread(tiMain); return tiMain; } protected ThreadInfo createThreadInfo (int objRef, int groupRef, int runnableRef, int nameRef){ ThreadInfo tiCurrent = ThreadInfo.getCurrentThread(); ThreadInfo tiNew = new ThreadInfo( this, objRef, groupRef, runnableRef, nameRef, tiCurrent); // note that we have to register here so that subsequent native peer calls can use the objRef // to lookup the ThreadInfo. This is a bit premature since the thread is not runnable yet, // but chances are it will be started soon, so we don't waste another data structure to do the mapping registerThread( tiNew); return tiNew; } // created if the option "vm.process_finalizers" is set to true protected ThreadInfo createFinalizerThreadInfo (int id, ApplicationContext appCtx){ FinalizerThreadInfo finalizerTi = new FinalizerThreadInfo( this, appCtx, id); registerThread(finalizerTi); return finalizerTi; } /** * the minimal set of system classes we need for initialization */ protected List<String> getStartupSystemClassNames() { ArrayList<String> startupClasses = new ArrayList<String>(64); // bare essentials startupClasses.add("java.lang.Object"); startupClasses.add("java.lang.Class"); startupClasses.add("java.lang.ClassLoader"); // the builtin types (and their arrays) startupClasses.add("boolean"); startupClasses.add("[Z"); startupClasses.add("byte"); startupClasses.add("[B"); startupClasses.add("char"); startupClasses.add("[C"); startupClasses.add("short"); startupClasses.add("[S"); startupClasses.add("int"); startupClasses.add("[I"); startupClasses.add("long"); startupClasses.add("[J"); startupClasses.add("float"); startupClasses.add("[F"); startupClasses.add("double"); startupClasses.add("[D"); startupClasses.add("void"); // the box types startupClasses.add("java.lang.Boolean"); startupClasses.add("java.lang.Character"); startupClasses.add("java.lang.Short"); startupClasses.add("java.lang.Integer"); startupClasses.add("java.lang.Long"); startupClasses.add("java.lang.Float"); startupClasses.add("java.lang.Double"); startupClasses.add("java.lang.Byte"); // the cache for box types startupClasses.add("gov.nasa.jpf.BoxObjectCaches"); // standard system classes startupClasses.add("java.lang.String"); startupClasses.add("java.lang.Thread"); startupClasses.add("java.lang.ThreadGroup"); startupClasses.add("java.lang.Thread$State"); startupClasses.add("java.lang.Thread$Permit"); startupClasses.add("java.io.PrintStream"); startupClasses.add("java.io.InputStream"); startupClasses.add("java.lang.System"); startupClasses.add("java.lang.ref.Reference"); startupClasses.add("java.lang.ref.WeakReference"); startupClasses.add("java.lang.Enum"); startupClasses.add("gov.nasa.jpf.FinalizerThread"); // we could be more fancy and use wildcard patterns and the current classpath // to specify extra classes, but this could be VERY expensive. Projected use // is mostly to avoid static init of single classes during the search String[] extraStartupClasses = config.getStringArray("vm.extra_startup_classes"); if (extraStartupClasses != null) { for (String extraCls : extraStartupClasses) { startupClasses.add(extraCls); } } // the main class has to be handled separately since it might be VM specific return startupClasses; } /** * return a list of ClassInfos for essential system types * * If system classes are not found, or are not valid JPF model classes, we throw * a JPFConfigException and exit * * returned ClassInfos are not yet registered in Statics and don't have class objects */ protected List<ClassInfo> getStartupSystemClassInfos (FeatureExpr ctx, SystemClassLoaderInfo sysCl, ThreadInfo tiMain){ LinkedList<ClassInfo> list = new LinkedList<ClassInfo>(); try { for (String clsName : getStartupSystemClassNames()) { ClassInfo ci = sysCl.getResolvedClassInfo(ctx, clsName); ci.registerStartupClass( tiMain, list); // takes care of superclasses and interfaces } } catch (ClassInfoException e){ throw new JPFConfigException("cannot load system class " + e.getFailedClass()); } return list; } /** * this adds the application main class and its supers to the list of startup classes */ protected ClassInfo getMainClassInfo (SystemClassLoaderInfo sysCl, String mainClassName, ThreadInfo tiMain, List<ClassInfo> list){ try { ClassInfo ciMain = sysCl.getResolvedClassInfo(null, mainClassName); ciMain.registerStartupClass(tiMain, list); // this might add a couple more return ciMain; } catch (ClassInfoException e){ throw new JPFConfigException("cannot load application class " + e.getFailedClass()); } } /* * these are called when creating ApplicationContexts and can be overridden by concrete VM types */ protected SystemClassLoaderInfo createSystemClassLoaderInfo (int appId) { Class<?>[] argTypes = { VM.class, int.class }; Object[] args = { this, Integer.valueOf(appId)}; SystemClassLoaderInfo sysCli = config.getEssentialInstance("vm.classloader.class", SystemClassLoaderInfo.class, argTypes, args); return sysCli; } protected void createSystemClassLoaderObject (FeatureExpr ctx, SystemClassLoaderInfo sysCl, ThreadInfo tiMain) { Heap heap = getHeap(); // create ClassLoader object for the ClassLoader type defined by this SystemClassLoaderInfo // NOTE - this requires the SystemClassLoaderInfo cache to be initialized ClassInfo ciCl = sysCl.getClassLoaderClassInfo(); ElementInfo ei = heap.newObject(ctx, ciCl, tiMain); //ei.setReferenceField("parent", MJIEnv.NULL); heap.registerPinDown(ei.getObjectRef()); sysCl.setClassLoaderObject(ei); } protected void pushMainEntryArgs (FeatureExpr ctx, MethodInfo miMain, String[] args, ThreadInfo tiMain, DirectCallStackFrame frame){ String sig = miMain.getSignature(); Heap heap = getHeap(); if (sig.contains("([Ljava/lang/String;)")){ ElementInfo eiArgs = heap.newArray(FeatureExprFactory.True(), "Ljava/lang/String;", args.length, tiMain); for (int i = 0; i < args.length; i++) { ElementInfo eiArg = heap.newString(FeatureExprFactory.True(), args[i], tiMain); eiArgs.setReferenceElement(FeatureExprFactory.True(), i, new One<>(eiArg.getObjectRef())); } frame.setReferenceArgument( ctx, 0, eiArgs.getObjectRef(), null); } else if (sig.contains("(Ljava/lang/String;)")){ if (args.length > 1){ ElementInfo eiArg = heap.newString(FeatureExprFactory.True(), args[0], tiMain); frame.setReferenceArgument( ctx, 0, eiArg.getObjectRef(), null); } else { frame.setReferenceArgument( ctx, 0, MJIEnv.NULL, null); } } else if (!sig.contains("()")){ throw new JPFException("unsupported main entry signature: " + miMain.getFullName()); } } protected void pushMainEntry (FeatureExpr ctx, MethodInfo miMain, String[] args, ThreadInfo tiMain) { DirectCallStackFrame frame = miMain.createDirectCallStackFrame(ctx, tiMain, 0); pushMainEntryArgs( ctx, miMain, args, tiMain, frame); tiMain.pushFrame(frame); } protected MethodInfo getMainEntryMethodInfo (String mthName, ClassInfo ciMain) { MethodInfo miMain = ciMain.getMethod(mthName, true); //--- do some sanity checks if this is a valid entry method if (miMain == null || !miMain.isStatic()) { throw new JPFConfigException("no static entry method: " + ciMain.getName() + '.' + mthName); } return miMain; } protected void pushClinits (FeatureExpr ctx, List<ClassInfo> startupClasses, ThreadInfo tiMain) { for (ClassInfo ci : startupClasses){ MethodInfo mi = ci.getMethod("<clinit>()V", false); if (mi != null) { DirectCallStackFrame frame = mi.createDirectCallStackFrame(ctx, tiMain, 0); tiMain.pushFrame(frame); } else { ci.setInitialized(); } } } /** * this is the main initialization point that sets up startup objects threads and callstacks. * If this returns false VM initialization cannot proceed and JPF will terminate */ public abstract boolean initialize (FeatureExpr ctx); /** * create and initialize the main thread for the given ApplicationContext. * This is called from VM.initialize() implementations, the caller has to handle exceptions that should be reported * differently (JPFConfigException, ClassInfoException) */ protected ThreadInfo initializeMainThread (FeatureExpr ctx, ApplicationContext appCtx, int tid){ SystemClassLoaderInfo sysCl = appCtx.sysCl; ThreadInfo tiMain = createMainThreadInfo(tid, appCtx); List<ClassInfo> startupClasses = getStartupSystemClassInfos(ctx, sysCl, tiMain); ClassInfo ciMain = getMainClassInfo(sysCl, appCtx.mainClassName, tiMain, startupClasses); if (!checkSystemClassCompatibility( sysCl)){ throw new JPFConfigException("non-JPF system classes, check classpath"); } // create essential objects (we can't call ctors yet) createSystemClassLoaderObject(ctx, sysCl, tiMain); for (ClassInfo ci : startupClasses) { ci.createAndLinkStartupClassObject(ctx, tiMain); } tiMain.createMainThreadObject(ctx, sysCl); registerThread(tiMain); // note that StackFrames have to be pushed in reverse order MethodInfo miMain = getMainEntryMethodInfo(appCtx.mainEntry, ciMain); appCtx.setEntryMethod(miMain); pushMainEntry(ctx, miMain, appCtx.args, tiMain); Collections.reverse(startupClasses); pushClinits(ctx, startupClasses, tiMain); registerThreadListCleanup(sysCl.getThreadClassInfo()); return tiMain; } protected void initializeFinalizerThread (FeatureExpr ctx, ApplicationContext appCtx, int tid) { if(processFinalizers) { ApplicationContext app = getCurrentApplicationContext(); FinalizerThreadInfo finalizerTi = app.getFinalizerThread(); finalizerTi = (FinalizerThreadInfo) createFinalizerThreadInfo(tid, app); finalizerTi.createFinalizerThreadObject(ctx, app.getSystemClassLoader()); appCtx.setFinalizerThread(finalizerTi); } } protected void registerThreadListCleanup (ClassInfo ciThread){ assert ciThread != null : "java.lang.Thread not loaded yet"; ciThread.addReleaseAction( new ReleaseAction(){ @Override public void release (ElementInfo ei) { ThreadList tl = getThreadList(); int objRef = ei.getObjectRef(); ThreadInfo ti = tl.getThreadInfoForObjRef(objRef); if (tl.remove(ti)){ vm.getKernelState().changed(); } } }); } protected abstract ChoiceGenerator<?> getInitialCG(); protected void initSystemState (ThreadInfo mainThread){ ss.setStartThread(mainThread); // the first transition probably doesn't have much choice (unless there were // threads started in the static init), but we want to keep it uniformly anyways ChoiceGenerator<?> cg = getInitialCG(); ss.setMandatoryNextChoiceGenerator(cg, "no root CG"); ss.recordSteps(hasToRecordSteps()); if (!pathOutput) { // don't override if explicitly requested pathOutput = hasToRecordPathOutput(); } transitionOccurred = true; } public void addPostGcAction (Runnable r){ postGcActions.add(r); } /** * to be called from the Heap after GC is completed (i.e. only live objects remain) */ public void processPostGcActions(){ if (!postGcActions.isEmpty()){ for (Runnable r : postGcActions){ r.run(); } postGcActions.clear(); } } public void addListener (VMListener newListener) { log.info("VMListener added: ", newListener); listeners = Misc.appendElement(listeners, newListener); } public boolean hasListenerOfType (Class<?> listenerCls) { return Misc.hasElementOfType(listeners, listenerCls); } public <T> T getNextListenerOfType(Class<T> type, T prev){ return Misc.getNextElementOfType(listeners, type, prev); } public void removeListener (VMListener removeListener) { listeners = Misc.removeElement(listeners, removeListener); } public void setTraceReplay (boolean isReplay) { isTraceReplay = isReplay; } public boolean isTraceReplay() { return isTraceReplay; } public boolean hasToRecordSteps() { // we have to record if there either is a reporter that has // a 'trace' topic, or there is an explicit request return jpf.getReporter().hasToReportTrace() || config.getBoolean("vm.store_steps"); } public void recordSteps( boolean cond) { // <2do> not ideal - it might be already too late when this is called config.setProperty("vm.store_steps", cond ? "true" : "false"); if (ss != null){ ss.recordSteps(cond); } } public boolean hasToRecordPathOutput() { if (config.getBoolean("vm.path_output")){ // explicitly requested return true; } else { return jpf.getReporter().hasToReportOutput(); // implicilty required } } //--- VM listener notifications /* * while some of these can be called from various places, the calls that happen from within Instruction.execute() should * happen right before return since listeners might do things such as ThreadInfo.createAndThrowException(..), i.e. cause * side effects that would violate consistency requirements of successive operations (e.g. by assuming we are still executing * in the same StackFrame - after throwing an exception) */ protected void notifyVMInitialized () { try { for (int i = 0; i < listeners.length; i++) { listeners[i].vmInitialized(this); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during vmInitialized() notification", t); } } protected void notifyChoiceGeneratorRegistered (ChoiceGenerator<?>cg, ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { // This is awkward, if we change the signature of this method, we need to // also change a bunch of listeners, most of which are useless to us. // FIXME if (ti.getPC() instanceof One){ listeners[i].choiceGeneratorRegistered(this, cg, ti, ti.getPC().getValue()); } else{ System.err.println("___________________________________________________"); System.err.println("[WARN] Get value of choice called: " + this); for (StackTraceElement e : Thread.currentThread().getStackTrace()) { System.err.println(e); } System.err.println("---------------------------------------------------"); // Let's wait for a NullPointerException listeners[i].choiceGeneratorRegistered(this, cg, ti, null); } } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during choiceGeneratorRegistered() notification", t); } } protected void notifyChoiceGeneratorSet (ChoiceGenerator<?>cg) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].choiceGeneratorSet(this, cg); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during choiceGeneratorSet() notification", t); } } protected void notifyChoiceGeneratorAdvanced (ChoiceGenerator<?>cg) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].choiceGeneratorAdvanced(this, cg); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during choiceGeneratorAdvanced() notification", t); } } protected void notifyChoiceGeneratorProcessed (ChoiceGenerator<?>cg) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].choiceGeneratorProcessed(this, cg); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during choiceGeneratorProcessed() notification", t); } } protected void notifyExecuteInstruction (ThreadInfo ti, Instruction insn) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].executeInstruction(FeatureExprFactory.True(), this, ti, insn); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during executeInstruction() notification", t); } } public void resetInstructionCounter() { for (int i = 0; i < listeners.length; i++) { listeners[i].resetInstructionCounter(); } } protected void notifyInstructionExecuted (ThreadInfo ti, Instruction insn, Instruction nextInsn) { try { //listener.instructionExecuted(this); for (int i = 0; i < listeners.length; i++) { listeners[i].instructionExecuted(this, ti, nextInsn, insn); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during instructionExecuted() notification", t); } } protected void notifyThreadStarted (ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].threadStarted(this, ti); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during threadStarted() notification", t); } } // NOTE: the supplied ThreadInfo does NOT have to be the running thread, as this // notification can occur as a result of a lock operation in the current thread protected void notifyThreadBlocked (ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].threadBlocked(this, ti, ti.getLockObject()); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during threadBlocked() notification", t); } } protected void notifyThreadWaiting (ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].threadWaiting(this, ti); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during threadWaiting() notification", t); } } protected void notifyThreadNotified (ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].threadNotified(this, ti); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during threadNotified() notification", t); } } protected void notifyThreadInterrupted (ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].threadInterrupted(this, ti); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during threadInterrupted() notification", t); } } protected void notifyThreadTerminated (ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].threadTerminated(this, ti); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during threadTerminated() notification", t); } } protected void notifyThreadScheduled (ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].threadScheduled(this, ti); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during threadScheduled() notification", t); } } protected void notifyLoadClass (ClassFile cf){ try { for (int i = 0; i < listeners.length; i++) { listeners[i].loadClass(this, cf); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during classLoaded() notification", t); } } protected void notifyClassLoaded(ClassInfo ci) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].classLoaded(this, ci); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during classLoaded() notification", t); } } protected void notifyObjectCreated(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectCreated(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectCreated() notification", t); } } protected void notifyObjectReleased(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectReleased(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectReleased() notification", t); } } protected void notifyObjectLocked(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectLocked(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectLocked() notification", t); } } protected void notifyObjectUnlocked(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectUnlocked(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectUnlocked() notification", t); } } protected void notifyObjectWait(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectWait(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectWait() notification", t); } } protected void notifyObjectExposed(ThreadInfo ti, ElementInfo eiShared, ElementInfo eiExposed) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectExposed(this, ti, eiShared, eiExposed); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectExposed() notification", t); } } protected void notifyObjectShared(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectShared(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectShared() notification", t); } } protected void notifyObjectNotifies(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectNotify(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectNotifies() notification", t); } } protected void notifyObjectNotifiesAll(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].objectNotifyAll(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during objectNotifiesAll() notification", t); } } protected void notifyGCBegin() { try { for (int i = 0; i < listeners.length; i++) { listeners[i].gcBegin(this); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during gcBegin() notification", t); } } protected void notifyGCEnd() { try { for (int i = 0; i < listeners.length; i++) { listeners[i].gcEnd(this); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during gcEnd() notification", t); } } protected void notifyExceptionThrown(ThreadInfo ti, ElementInfo ei) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].exceptionThrown(this, ti, ei); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during exceptionThrown() notification", t); } } protected void notifyExceptionBailout(ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].exceptionBailout(this, ti); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during exceptionBailout() notification", t); } } protected void notifyExceptionHandled(ThreadInfo ti) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].exceptionHandled(this, ti); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during exceptionHandled() notification", t); } } protected void notifyMethodEntered(ThreadInfo ti, MethodInfo mi) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].methodEntered(this, ti, mi); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during methodEntered() notification", t); } } protected void notifyMethodExited(ThreadInfo ti, MethodInfo mi) { try { for (int i = 0; i < listeners.length; i++) { listeners[i].methodExited(this, ti, mi); } } catch (UncaughtException x) { throw x; } catch (JPF.ExitException x) { throw x; } catch (Throwable t) { throw new JPFListenerException("exception during methodExited() notification", t); } } // VMListener acquisition public String getThreadName () { ThreadInfo ti = ThreadInfo.getCurrentThread(); return ti.getName(); } // VMListener acquisition public Instruction getInstruction () { ThreadInfo ti = ThreadInfo.getCurrentThread(); return ti.getPC().getValue(); } /** * note this is gone after backtracking or starting the next exception */ public ExceptionInfo getPendingException () { ThreadInfo ti = ThreadInfo.getCurrentThread(); if (ti != null){ return ti.getPendingException(); } else { return null; } } public Step getLastStep () { Transition trail = ss.getTrail(); if (trail != null) { return trail.getLastStep(); } return null; } public Transition getLastTransition () { if (path.size() == 0) { return null; } return path.get(path.size() - 1); } public ClassInfo getClassInfo (int objref) { if (objref != MJIEnv.NULL) { return getElementInfo(objref).getClassInfo(); } else { return null; } } /** * NOTE: only use this locally, since the path is getting modified by the VM * * The path only contains all states when queried from a stateAdvanced() notification. * If this is called from an instructionExecuted() (or other VMListener), and you need * the ongoing transition in it, you have to call updatePath() first */ public Path getPath () { return path; } /** * this is the ongoing transition. Note that it is not yet stored in the path * if this is called from a VMListener notification */ public Transition getCurrentTransition() { return ss.getTrail(); } /** * use that one if you have to store the path for subsequent use * * NOTE: without a prior call to updatePath(), this does NOT contain the * ongoing transition. See getPath() for usage from a VMListener */ public Path getClonedPath () { return path.clone(); } public int getPathLength () { return path.size(); } public ThreadList getThreadList () { return getKernelState().getThreadList(); } public ClassLoaderList getClassLoaderList() { return getKernelState().getClassLoaderList(); } /** * Bundles up the state of the system for export */ public RestorableVMState getRestorableState () { return new RestorableVMState(this); } /** * Gets the system state. */ public SystemState getSystemState () { return ss; } public KernelState getKernelState () { return ss.getKernelState(); } public void kernelStateChanged(){ ss.getKernelState().changed(); } public Config getConfig() { return config; } public SchedulerFactory getSchedulerFactory(){ return ss.getSchedulerFactory(); } public Backtracker getBacktracker() { return backtracker; } @SuppressWarnings("unchecked") public <T> StateRestorer<T> getRestorer() { if (restorer == null) { if (serializer instanceof StateRestorer) { restorer = (StateRestorer<?>) serializer; } else if (stateSet instanceof StateRestorer) { restorer = (StateRestorer<?>) stateSet; } else { // config read only if serializer is not also a restorer restorer = config.getInstance("vm.restorer.class", StateRestorer.class); } restorer.attach(this); } return (StateRestorer<T>) restorer; } public StateSerializer getSerializer() { if (serializer == null) { serializer = config.getEssentialInstance("vm.serializer.class", StateSerializer.class); serializer.attach(this); } return serializer; } public void setSerializer (StateSerializer newSerializer){ serializer = newSerializer; serializer.attach(this); } /** * Returns the stateSet if states are being matched. */ public StateSet getStateSet() { return stateSet; } public FunctionObjectFactory getFunctionObjectFacotry() { return funcObjFactory; } /** * return the last registered SystemState's ChoiceGenerator object * NOTE: there might be more than one ChoiceGenerator associated with the * current transition (ChoiceGenerators can be cascaded) */ public ChoiceGenerator<?> getChoiceGenerator () { return ss.getChoiceGenerator(); } public ChoiceGenerator<?> getNextChoiceGenerator() { return ss.getNextChoiceGenerator(); } public boolean setNextChoiceGenerator (ChoiceGenerator<?> cg){ return ss.setNextChoiceGenerator(cg); } public void setMandatoryNextChoiceGenerator (ChoiceGenerator<?> cg, String failMsg){ ss.setMandatoryNextChoiceGenerator(cg, failMsg); } /** * return the latest registered ChoiceGenerator used in this transition * that matches the provided 'id' and is of 'cgType'. * * This should be the main getter for clients that are cascade aware */ public <T extends ChoiceGenerator<?>> T getCurrentChoiceGenerator (String id, Class<T> cgType) { return ss.getCurrentChoiceGenerator(id,cgType); } /** * returns all ChoiceGenerators in current path */ public ChoiceGenerator<?>[] getChoiceGenerators() { return ss.getChoiceGenerators(); } public <T extends ChoiceGenerator<?>> T[] getChoiceGeneratorsOfType (Class<T> cgType) { return ss.getChoiceGeneratorsOfType(cgType); } public <T extends ChoiceGenerator<?>> T getLastChoiceGeneratorOfType (Class<T> cgType){ return ss.getLastChoiceGeneratorOfType(cgType); } public void print (String s) { if (treeOutput) { System.out.print(s); } if (pathOutput) { appendOutput(s); } } public void println (String s) { if (treeOutput) { if (indentOutput){ StringBuilder indent = new StringBuilder(); int i; for (i = 0;i<=path.size();i++) { indent.append('|').append(i); } indent.append("|").append(s); System.out.println(indent); } else { System.out.println(s); } } if (pathOutput) { appendOutput(s); appendOutput('\n'); } } public void print (boolean b) { if (treeOutput) { System.out.print(b); } if (pathOutput) { appendOutput(Boolean.toString(b)); } } public void print (char c) { if (treeOutput) { System.out.print(c); } if (pathOutput) { appendOutput(c); } } public void print (int i) { if (treeOutput) { System.out.print(i); } if (pathOutput) { appendOutput(Integer.toString(i)); } } public void print (long l) { if (treeOutput) { System.out.print(l); } if (pathOutput) { appendOutput(Long.toString(l)); } } public void print (double d) { if (treeOutput) { System.out.print(d); } if (pathOutput) { appendOutput(Double.toString(d)); } } public void print (float f) { if (treeOutput) { System.out.print(f); } if (pathOutput) { appendOutput(Float.toString(f)); } } public void println () { if (treeOutput) { System.out.println(); } if (pathOutput) { appendOutput('\n'); } } void appendOutput (String s) { if (out == null) { out = new StringBuilder(); } out.append(s); } void appendOutput (char c) { if (out == null) { out = new StringBuilder(); } out.append(c); } /** * get the pending output (not yet stored in the path) */ public String getPendingOutput() { if (out != null && out.length() > 0){ return out.toString(); } else { return null; } } /** * this is here so that we can intercept it in subclassed VMs */ public Instruction handleException (ThreadInfo ti, int xObjRef){ // ti = null; // Get rid of IDE warning // xObjRef = 0; return null; } public void storeTrace (String fileName, String comment, boolean verbose) { ChoicePoint.storeTrace(fileName, getSUTName(), comment, ss.getChoiceGenerators(), verbose); } public void storePathOutput () { pathOutput = true; } public ThreadInfo[] getLiveThreads () { return getThreadList().getThreads(); } /** * print call stacks of all live threads * this is also used for debugging purposes, so we can't move it to the Reporter system * (it's also using a bit too many internals for that) */ public void printLiveThreadStatus (PrintWriter pw) { int nThreads = ss.getThreadCount(); ThreadInfo[] threads = getThreadList().getThreads(); int n=0; for (int i = 0; i < nThreads; i++) { ThreadInfo ti = threads[i]; if (ti.getStackDepth() > 0){ n++; //pw.print("Thread: "); //pw.print(tiMain.getName()); pw.println(ti.getStateDescription()); List<ElementInfo> locks = ti.getLockedObjects(); if (!locks.isEmpty()) { pw.print(" owned locks:"); boolean first = true; for (ElementInfo e : locks) { if (first) { first = false; } else { pw.print(","); } pw.print(e); } pw.println(); } ElementInfo ei = ti.getLockObject(); if (ei != null) { if (ti.getState() == ThreadInfo.State.WAITING) { pw.print( " waiting on: "); } else { pw.print( " blocked on: "); } pw.println(ei); } pw.println(" call stack:"); for (StackFrame frame : ti){ if (!frame.isDirectCallFrame()) { pw.print("\tat "); pw.println(frame.getStackTraceInfo()); } } pw.println(); } } if (n==0) { pw.println("no live threads"); } } // just a debugging aid public void dumpThreadStates () { java.io.PrintWriter pw = new java.io.PrintWriter(System.out, true); printLiveThreadStatus(pw); pw.flush(); } /** * Moves one step backward. This method and forward() are the main methods * used by the search object. * Note this is called with the state that caused the backtrack still being on * the stack, so we have to remove that one first (i.e. popping two states * and restoring the second one) */ public boolean backtrack () { transitionOccurred = false; boolean success = backtracker.backtrack(); if (success) { if (CHECK_CONSISTENCY) { checkConsistency(false); } // restore the path path.removeLast(); lastTrailInfo = path.getLast(); return true; } else { return false; } } /** * store the current SystemState's Trail in our path, after updating it * with whatever annotations the VM wants to add. * This is supposed to be called after each transition we want to keep */ public void updatePath () { Transition t = ss.getTrail(); Transition tLast = path.getLast(); // NOTE: don't add the transition twice, this is public and might get called // from listeners, so the transition object might get changed if (tLast != t) { // <2do> we should probably store the output directly in the TrailInfo, // but this might not be our only annotation in the future // did we have output during the last transition? If yes, add it if ((out != null) && (out.length() > 0)) { t.setOutput( out.toString()); out.setLength(0); } path.add(t); } } /** * advance the program state * * forward() and backtrack() are the two primary interfaces towards the Search * driver. note that the caller still has to check if there is a next state, * and if the executed instruction sequence led into a new or already visited state * * @return 'true' if there was an un-executed sequence out of the current state, * 'false' if it was completely explored * */ public boolean forward () { // the reason we split up CG initialization and transition execution // is that program state storage is not required if the CG initialization // does not produce a new choice since we have to backtrack in that case // anyways. This can be caused by complete enumeration of CGs and/or by // CG listener intervention (i.e. not just after backtracking). For a large // number of matched or end states and ignored transitions this can be a // huge saving. // The downside is that CG notifications are NOT allowed anymore to change the // KernelState (modify fields or thread states) since those changes would // happen before storing the KernelState, and hence would make backtracking // inconsistent. This is advisable anyways since all program state changes // should take place during transitions, but the real snag is that this // cannot be easily enforced. // actually, it hasn't occurred yet, but will transitionOccurred = ss.initializeNextTransition(this); if (transitionOccurred){ if (CHECK_CONSISTENCY) { checkConsistency(true); // don't push an inconsistent state } backtracker.pushKernelState(); // cache this before we enter (and increment) the next insn(s) lastTrailInfo = path.getLast(); try { ss.executeNextTransition(vm); } catch (UncaughtException e) { // we don't pass this up since it means there were insns executed and we are // in a consistent state } // every other exception goes upwards backtracker.pushSystemState(); updatePath(); if (!isIgnoredState()) { // if this is ignored we are going to backtrack anyways // matching states out of ignored transitions is also not a good idea // because this transition is usually incomplete if (runGc && !hasPendingException()) { if(ss.gcIfNeeded()) { processFinalizers(); } } if (stateSet != null) { newStateId = stateSet.size(); int id = stateSet.addCurrent(); ss.setId(id); } else { // this is 'state-less' model checking, i.e. we don't match states ss.setId(++newStateId); // but we still should have states numbered in case listeners use the id } } return true; } else { return false; // no transition occurred } } /** * Prints the current stack trace. Just for debugging purposes */ public void printCurrentStackTrace () { ThreadInfo th = ThreadInfo.getCurrentThread(); if (th != null) { th.printStackTrace(); } } public void restoreState (RestorableVMState state) { if (state.path == null) { throw new JPFException("tried to restore partial VMState: " + state); } backtracker.restoreState(state.getBkState()); path = state.path.clone(); } public void activateGC () { ss.activateGC(); } //--- various state attribute getters and setters (mostly forwarding to SystemState) public void retainStateAttributes (boolean isRetained){ ss.retainAttributes(isRetained); } public void forceState () { ss.setForced(true); } /** * override the state matching - ignore this state, no matter if we changed * the heap or stacks. * use this with care, since it prunes whole search subtrees */ public void ignoreState (boolean cond) { ss.setIgnored(cond); } public void ignoreState(){ ignoreState(true); } /** * imperatively break the transition to enable state matching */ public void breakTransition (String reason) { ThreadInfo ti = ThreadInfo.getCurrentThread(); ti.breakTransition(reason); } public boolean transitionOccurred(){ return transitionOccurred; } /** * answers if the current state already has been visited. This is mainly * used by the searches (to control backtracking), but could also be useful * for observers to build up search graphs (based on the state ids) * * this returns true if no state has been produced yet, and false if * no transition occurred after a forward call */ public boolean isNewState() { if (!transitionOccurred){ return false; } if (stateSet != null) { if (ss.isForced()){ return true; } else if (ss.isIgnored()){ return false; } else { return (newStateId == ss.getId()); } } else { // stateless model checking - each transition leads to a new state return true; } } /** * We made this to be overriden by Single/MultiprcessesVM implementations, * since for MultiprcessesVM one can decide when to terminate (after the * the termination of all processes or only one process). * todo - that needs to be specified through the properties file */ public abstract boolean isEndState (); public boolean isVisitedState(){ return !isNewState(); } public boolean isIgnoredState(){ return ss.isIgnored(); } public boolean isInterestingState () { return ss.isInteresting(); } public boolean isBoringState () { return ss.isBoring(); } public boolean hasPendingException () { return (getPendingException() != null); } public abstract boolean isDeadlocked (); public Exception getException () { return ss.getUncaughtException(); } /** * get the numeric id for the current state * Note: this can be called several times (by the search and observers) for * every forward()/backtrack(), so we want to cache things a bit */ public int getStateId() { return ss.getId(); } public int getStateCount() { return newStateId; } /** * <2do> this is a band aid to bundle all these legacy reference chains * from JPFs past. The goal is to replace them with proper accessors (normally * through ThreadInfo, MJIEnv or VM, which all act as facades) wherever possible, * and use VM.getVM() where there is no access to such a facade. Once this * has been completed, we can start refactoring the users of VM.getVM() to * get access to a suitable facade. */ public static VM getVM () { return vm; } /** * not ideal to have this here since it is kind of a backlink, but it's not * any better if listeners have to dig this out from JPF * Note - this isn't set during initialization, since the VM object is created first */ public Search getSearch() { return jpf.getSearch(); } /** * pushClinit all our static fields. Called from <clinit> and reset */ static void initStaticFields () { error_id = 0; } /** * given an object reference, it returns the ApplicationContext of the process to which * this object belongs */ public abstract ApplicationContext getCurrentApplicationContext(); public abstract ApplicationContext getApplicationContext(int objRef); public abstract ApplicationContext[] getApplicationContexts(); public abstract String getSUTName(); public abstract String getSUTDescription(); public abstract int getNumberOfApplications(); public Heap getHeap() { return ss.getHeap(); } public ElementInfo getElementInfo(int objref){ return ss.getHeap().get(objref); } public ElementInfo getModifiableElementInfo(int objref){ return ss.getHeap().getModifiable(objref); } public ThreadInfo getCurrentThread () { return ThreadInfo.currentThread; } public void registerClassLoader(ClassLoaderInfo cl) { this.getKernelState().addClassLoader(cl); } public int registerThread (ThreadInfo ti){ getKernelState().changed(); return getThreadList().add(ti); } public boolean isAtomic() { return ss.isAtomic(); } /** * Returns the ClassLoader with the given globalId */ protected ClassLoaderInfo getClassLoader(int gid) { return ss.ks.getClassLoader(gid); } /** * <2do> this is where we will hook in a better time model */ public long currentTimeMillis () { return timeModel.currentTimeMillis(); } /** * <2do> this is where we will hook in a better time model */ public long nanoTime() { return timeModel.nanoTime(); } public void resetNextCG() { if (ss.nextCg != null) { ss.nextCg.reset(); } } /** * only for debugging, this is expensive * * If this is a store (forward) this is called before the state is stored. * * If this is a restore (visited forward or backtrack), this is called after * the state got restored */ public void checkConsistency(boolean isStateStore) { getThreadList().checkConsistency( isStateStore); getHeap().checkConsistency( isStateStore); } public abstract void terminateProcess (ThreadInfo ti); // ---------- Predicates used to query threads from ThreadList ---------- // public abstract Predicate<ThreadInfo> getRunnablePredicate(); public abstract Predicate<ThreadInfo> getDaemonRunnablePredicate(); public abstract Predicate<ThreadInfo> getAppTimedoutRunnablePredicate(); public Predicate<ThreadInfo> getUserTimedoutRunnablePredicate () { return userTimedoutRunnablePredicate; } public Predicate<ThreadInfo> getUserLiveNonDaemonPredicate() { return userliveNonDaemonPredicate; } public Predicate<ThreadInfo> getTimedoutRunnablePredicate () { return timedoutRunnablePredicate; } public Predicate<ThreadInfo> getAlivePredicate () { return alivePredicate; } // ---------- Methods for handling finalizers ---------- // /** * Add a given finalizable object to the finalizeQueue array of java.lang.ref.Finalizer * model class. This is invoked from the sweep() phase of the garbage collection. */ public void addToFinalizeQueue(ElementInfo ei) { ApplicationContext app = getApplicationContext(ei.getObjectRef()); app.getFinalizerThread().addToFinalizeQueue(ei); } public FinalizerThreadInfo getFinalizerThread() { return getCurrentApplicationContext().getFinalizerThread(); } abstract void updateFinalizerQueues(); public void processFinalizers() { if(processFinalizers) { updateFinalizerQueues(); ChoiceGenerator<?> cg = getNextChoiceGenerator(); if(cg==null || (cg.isSchedulingPoint() && !cg.isCascaded())) { getFinalizerThread().scheduleFinalizer(); } } } }