// // Copyright (C) 2012 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 de.fosd.typechef.featureexpr.FeatureExpr; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPF; import gov.nasa.jpf.JPFConfigException; import gov.nasa.jpf.util.Misc; import gov.nasa.jpf.util.Predicate; import gov.nasa.jpf.vm.choice.BreakGenerator; import gov.nasa.jpf.vm.choice.ThreadChoiceFromSet; /** * @author Nastaran Shafiei <nastaran.shafiei@gmail.com> * * Includes the VM initialization for executing single Java process * * To use this jpf.properties includes, * vm.class = gov.nasa.jpf.vm.SingleProcessVM */ public class SingleProcessVM extends VM { protected ApplicationContext appCtx; // we only have one protected Predicate<ThreadInfo> runnablePredicate; protected Predicate<ThreadInfo> daemonRunnable; protected SingleProcessVM (){} public SingleProcessVM (JPF jpf, Config conf) { super(jpf, conf); appCtx = createApplicationContext(); // set predicates used to query from threadlist runnablePredicate = new Predicate<ThreadInfo>(){ public boolean isTrue (ThreadInfo ti){ return (ti.isRunnable()); } }; daemonRunnable = new Predicate<ThreadInfo>(){ public boolean isTrue (ThreadInfo ti){ return (ti.isDaemon() && ti.isRunnable()); } }; } protected ApplicationContext createApplicationContext (){ String clsName; String[] args = null; String[] freeArgs = config.getFreeArgs(); clsName = config.getProperty("target"); // explicit 'target' takes precedence if (clsName == null){ if (freeArgs != null){ // if it is non-null, there is at least one entry // note that application property filenames have been removed by Config since they are part of its initialization clsName = freeArgs[0]; if (freeArgs.length > 1){ // if there is no 'target' command line overrides 'target.args' args = Misc.arrayWithoutFirst(freeArgs, 1); } else { args = config.getStringArray("target.args"); } } } else { // since there was a 'target', 'target.args' override command line args = config.getStringArray("target.args"); if (args == null){ if (freeArgs != null){ args = freeArgs; } } } // sanity checks if (args == null){ args = EMPTY_ARGS; } if (clsName == null){ throw new JPFConfigException("no target class specified, terminating"); } if (!isValidClassName(clsName)){ throw new JPFConfigException("main class not a valid class name: " + clsName); } // can be any static method that has a (String[]), (String) or () signature String mainEntry = config.getProperty("target.entry", "main([Ljava/lang/String;)V"); String host = config.getString("target.host", "localhost"); SystemClassLoaderInfo sysCli = createSystemClassLoaderInfo(0); return new ApplicationContext( 0, clsName, mainEntry, args, host, sysCli); } @Override public boolean initialize(FeatureExpr ctx){ try { ThreadInfo tiMain = initializeMainThread(ctx, appCtx, 0); initializeFinalizerThread(ctx, appCtx, 1); if (tiMain == null) { return false; // bail out } initSystemState(tiMain); initialized = true; notifyVMInitialized(); return true; } catch (JPFConfigException cfe){ log.severe(cfe.getMessage()); return false; } catch (ClassInfoException cie){ log.severe(cie.getMessage()); return false; } // all other exceptions are JPF errors that should cause stack traces } @Override protected ChoiceGenerator<?> getInitialCG () { ThreadInfo[] runnables = getThreadList().getAllMatching(vm.getTimedoutRunnablePredicate()); return new ThreadChoiceFromSet("<root>", runnables, true); } @Override public int getNumberOfApplications(){ return 1; } @Override public String getSUTName() { return appCtx.mainClassName; } @Override public String getSUTDescription(){ StringBuilder sb = new StringBuilder(); sb.append(appCtx.mainClassName); sb.append('.'); sb.append(Misc.upToFirst( appCtx.mainEntry, '(')); sb.append('('); String[] args = appCtx.args; for (int i=0; i<args.length; i++){ if (i>0){ sb.append(','); } sb.append('"'); sb.append(args[i]); sb.append('"'); } sb.append(')'); return sb.toString(); } @Override public ApplicationContext getApplicationContext(int obj) { return appCtx; } @Override public ApplicationContext[] getApplicationContexts(){ return new ApplicationContext[] { appCtx }; } @Override public ApplicationContext getCurrentApplicationContext(){ ThreadInfo ti = ThreadInfo.getCurrentThread(); if (ti != null){ return ti.getApplicationContext(); } else { return appCtx; } } /** * The program is terminated if there are no alive threads, and there is no nonDaemon left. * * NOTE - this is only approximated in real life. Daemon threads can still run for a few cycles * after the last non-daemon died, which opens an interesting source of errors we * actually might want to check for */ @Override public boolean isEndState () { // note this uses 'alive', not 'runnable', hence isEndStateProperty won't // catch deadlocks - but that would be NoDeadlockProperty anyway boolean hasNonTerminatedDaemon = getThreadList().hasAnyMatching(getUserLiveNonDaemonPredicate()); boolean hasRunnable = getThreadList().hasAnyMatching(getUserTimedoutRunnablePredicate()); boolean isEndState = !(hasNonTerminatedDaemon && hasRunnable); if(processFinalizers) { if(isEndState) { if(getFinalizerThread().isRunnable()) { return false; } } } return isEndState; } @Override public boolean isDeadlocked () { boolean hasNonDaemons = false; boolean hasBlockedThreads = false; if (ss.isBlockedInAtomicSection()) { return true; // blocked in atomic section } ThreadInfo[] threads = getThreadList().getThreads(); for (int i = 0; i < threads.length; i++) { ThreadInfo ti = threads[i]; if (ti.isAlive()){ hasNonDaemons |= !ti.isDaemon(); // shortcut - if there is at least one runnable, we are not deadlocked if (ti.isTimeoutRunnable()) { // willBeRunnable() ? return false; } // means it is not NEW or TERMINATED, i.e. live & blocked hasBlockedThreads = true; } } return (hasNonDaemons && hasBlockedThreads); } @Override public void terminateProcess (ThreadInfo ti) { SystemState ss = getSystemState(); ThreadInfo[] liveThreads = getLiveThreads(); ThreadInfo finalizerTi = null; for (int i = 0; i < liveThreads.length; i++) { if(!liveThreads[i].isSystemThread()) { // keep the stack frames around, so that we can inspect the snapshot liveThreads[i].setTerminated(); } else { // FinalizerThread is not killed at this point. We need to keep it around in // case fianlizable objects are GCed after System.exit() returns. finalizerTi = liveThreads[i]; } } ss.setMandatoryNextChoiceGenerator( new BreakGenerator("exit", ti, true), "exit without break CG"); // if there is a finalizer thread, we have to run the last GC, to queue finalizable objects, if any if(finalizerTi != null) { assert finalizerTi.isAlive(); activateGC(); } } //---------- Predicates used to query threads from ThreadList ---------- // @Override public Predicate<ThreadInfo> getRunnablePredicate() { return runnablePredicate; } @Override public Predicate<ThreadInfo> getAppTimedoutRunnablePredicate() { return getRunnablePredicate(); } @Override public Predicate<ThreadInfo> getDaemonRunnablePredicate() { return daemonRunnable; } // ---------- Methods for handling finalizers ---------- // @Override void updateFinalizerQueues () { getFinalizerThread().processNewFinalizables(); } }