--- /dev/null
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package gov.nasa.jpf.vm;
+
+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.vm.FinalizerThreadInfo;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.IntTable;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.Predicate;
+
+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 java.util.Map;
+
+
+/**
+ * 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;
+
+ /** ThreadChoiceGenerator management related to data races and shared objects */
+ protected Scheduler scheduler;
+
+
+ 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);
+
+ scheduler = config.getEssentialInstance("vm.scheduler.class", Scheduler.class);
+
+ 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);
+ 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 (SystemClassLoaderInfo sysCl, ThreadInfo tiMain){
+ LinkedList<ClassInfo> list = new LinkedList<ClassInfo>();
+
+ try {
+ for (String clsName : getStartupSystemClassNames()) {
+ ClassInfo ci = sysCl.getResolvedClassInfo(clsName);
+ ci.registerStartupClass( tiMain, list); // takes care of superclasses and interfaces
+ }
+ } catch (ClassInfoException e){
+ e.printStackTrace();
+ 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(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 (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( ciCl, tiMain);
+ //ei.setReferenceField("parent", MJIEnv.NULL);
+ heap.registerPinDown(ei.getObjectRef());
+
+ sysCl.setClassLoaderObject(ei);
+ }
+
+ protected void pushMainEntryArgs (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("Ljava/lang/String;", args.length, tiMain);
+ for (int i = 0; i < args.length; i++) {
+ ElementInfo eiArg = heap.newString(args[i], tiMain);
+ eiArgs.setReferenceElement(i, eiArg.getObjectRef());
+ }
+ frame.setReferenceArgument( 0, eiArgs.getObjectRef(), null);
+
+ } else if (sig.contains("(Ljava/lang/String;)")){
+ if (args.length > 1){
+ ElementInfo eiArg = heap.newString(args[0], tiMain);
+ frame.setReferenceArgument( 0, eiArg.getObjectRef(), null);
+ } else {
+ frame.setReferenceArgument( 0, MJIEnv.NULL, null);
+ }
+
+ } else if (!sig.contains("()")){
+ throw new JPFException("unsupported main entry signature: " + miMain.getFullName());
+ }
+ }
+
+ protected void pushMainEntry (MethodInfo miMain, String[] args, ThreadInfo tiMain) {
+ DirectCallStackFrame frame = miMain.createDirectCallStackFrame(tiMain, 0);
+ pushMainEntryArgs( 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 (List<ClassInfo> startupClasses, ThreadInfo tiMain) {
+ for (ClassInfo ci : startupClasses){
+ MethodInfo mi = ci.getMethod("<clinit>()V", false);
+ if (mi != null) {
+ DirectCallStackFrame frame = mi.createDirectCallStackFrame(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 ();
+
+ /**
+ * 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 (ApplicationContext appCtx, int tid){
+ SystemClassLoaderInfo sysCl = appCtx.sysCl;
+
+ ThreadInfo tiMain = createMainThreadInfo(tid, appCtx);
+ List<ClassInfo> startupClasses = getStartupSystemClassInfos(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(sysCl, tiMain);
+ for (ClassInfo ci : startupClasses) {
+ ci.createAndLinkStartupClassObject(tiMain);
+ }
+ tiMain.createMainThreadObject(sysCl);
+ registerThread(tiMain);
+
+ // note that StackFrames have to be pushed in reverse order
+ MethodInfo miMain = getMainEntryMethodInfo(appCtx.mainEntry, ciMain);
+ appCtx.setEntryMethod(miMain);
+ pushMainEntry(miMain, appCtx.args, tiMain);
+ Collections.reverse(startupClasses);
+ pushClinits(startupClasses, tiMain);
+
+ registerThreadListCleanup(sysCl.getThreadClassInfo());
+
+ return tiMain;
+ }
+
+ protected void initializeFinalizerThread (ApplicationContext appCtx, int tid) {
+ if(processFinalizers) {
+ ApplicationContext app = getCurrentApplicationContext();
+ FinalizerThreadInfo finalizerTi = app.getFinalizerThread();
+
+ finalizerTi = (FinalizerThreadInfo) createFinalizerThreadInfo(tid, app);
+ finalizerTi.createFinalizerThreadObject(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();
+ }
+ }
+ });
+ }
+
+
+ /**
+ * override this if the concrete VM needs a special root CG
+ */
+ protected void setRootCG(){
+ scheduler.setRootCG();
+ }
+
+ protected void initSystemState (ThreadInfo mainThread){
+ ss.setStartThread(mainThread);
+
+ ss.recordSteps(hasToRecordSteps());
+
+ if (!pathOutput) { // don't override if explicitly requested
+ pathOutput = hasToRecordPathOutput();
+ }
+
+ setRootCG(); // this has to be guaranteed to register a CG
+ if (!hasNextChoiceGenerator()){
+ throw new JPFException("scheduler failed to set ROOT choice generator: " + scheduler);
+ }
+
+ 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++) {
+ listeners[i].choiceGeneratorRegistered(this, cg, ti, ti.getPC());
+ }
+ } 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(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);
+ }
+ }
+
+ 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();
+ }
+
+ /**
+ * 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 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 Scheduler getScheduler(){
+ return scheduler;
+ }
+
+ 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 hasNextChoiceGenerator(){
+ return (ss.getNextChoiceGenerator() != null);
+ }
+
+ 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 ChoiceGenerator<?> getLastChoiceGeneratorInThread (ThreadInfo ti){
+ return ss.getLastChoiceGeneratorInThread(ti);
+ }
+
+ 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;
+ }
+
+ private void printCG (ChoiceGenerator<?> cg, int n){
+ ChoiceGenerator cgPrev = cg.getPreviousChoiceGenerator();
+ if (cgPrev != null){
+ printCG( cgPrev, --n);
+ }
+
+ System.out.printf("[%d] ", n);
+ System.out.println(cg);
+ }
+
+ // for debugging purposes
+ public void printChoiceGeneratorStack(){
+ ChoiceGenerator<?> cg = getChoiceGenerator();
+ if (cg != null){
+ int n = cg.getNumberOfParents();
+ printCG(cg, n);
+ }
+ }
+
+ 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);
+ }
+
+ /**
+ * 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);
+
+ // this is invoked by the heap (see GenericHeap.newInternString()) upon creating
+ // the very first intern string
+ public abstract Map<Integer,IntTable<String>> getInitialInternStringsMap();
+
+ // ---------- 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 ---------- //
+
+ 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();
+ }
+ }
+ }
+}