//
// Copyright (C) 2010 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.test.mc.basic;
import org.junit.Test;
import cmu.conditional.One;
import de.fosd.typechef.featureexpr.FeatureExprFactory;
import gov.nasa.jpf.ListenerAdapter;
import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
import gov.nasa.jpf.jvm.bytecode.GETFIELD;
import gov.nasa.jpf.search.Search;
import gov.nasa.jpf.util.test.TestJPF;
import gov.nasa.jpf.vm.ChoiceGenerator;
import gov.nasa.jpf.vm.FieldInfo;
import gov.nasa.jpf.vm.Instruction;
import gov.nasa.jpf.vm.StackFrame;
import gov.nasa.jpf.vm.SystemState;
import gov.nasa.jpf.vm.ThreadInfo;
import gov.nasa.jpf.vm.VM;
import gov.nasa.jpf.vm.Verify;
import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
/**
* regression test for cascaded ChoiceGenerators
*/
public class CascadedCGTest extends TestJPF {
public static class IntChoiceCascader extends ListenerAdapter {
static int result;
@Override
public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
SystemState ss = vm.getSystemState();
if (executedInsn instanceof EXECUTENATIVE) { // break on native method exec
EXECUTENATIVE exec = (EXECUTENATIVE) executedInsn;
if (exec.getExecutedMethodName().equals("getInt")){// this insn did create a CG
if (!ti.isFirstStepInsn()){
result = 0;
IntIntervalGenerator cg = new IntIntervalGenerator("listenerCG", 3,4);
ss.setNextChoiceGenerator(cg);
System.out.println("# listener registered " + cg);
} else { // reexecution
ChoiceGenerator<?>[] curCGs = ss.getCurrentChoiceGenerators();
assert curCGs.length == 2;
IntIntervalGenerator cg = ss.getCurrentChoiceGenerator("listenerCG", IntIntervalGenerator.class);
assert cg != null : "no 'listenerCG' IntIntervalGenerator found";
int i = cg.getNextChoice();
System.out.println("# current listener CG choice: " + i);
cg = ss.getCurrentChoiceGenerator("verifyGetInt(II)", IntIntervalGenerator.class);
assert cg != null : "no 'verifyGetInt(II)' IntIntervalGenerator found";
int j = cg.getNextChoice();
System.out.println("# current insn CG choice: " + j);
result += i * j;
}
}
}
}
}
@Test
public void testCascadedIntIntervals () {
if (verifyNoPropertyViolation("+listener=.test.mc.basic.CascadedCGTest$IntChoiceCascader")){
int i = Verify.getInt( 1, 2);
System.out.print("i=");
System.out.println(i);
} else {
assert IntChoiceCascader.result == 21;
}
}
//--- mixed data and thread CG
// this listener replaces all GETFIELD "mySharedField" results with configured
// choice values (i.e. it is a simplified field Perturbator).
// The demo point is that it is not aware of that such GETFIELDs might also be
// scheduling points because of shared object field access, and it should work
// the same no matter if there also was a ThreadChoice/context switch or not
// NOTE: while the cascaded CG interface is easy to use (almost the same as the
// single CG interface), the context can be quite tricky because the cascaded
// CG (the scheduling point in this case) means the corresponding instruction
// is already rescheduled and might have been cut short in insn specific ways
// (in this case before pushing the field value on the operand stack). For this
// reason a simple ti.isFirstStepInsn() check is not sufficient. There might not
// have been a reschedule if there was only one thread, or even if this is the
// first step insn, the corresponding CG might have been not related to the
// getfield but some action in the preceeding thread (e.g. a terminate).
// In this case, the simple solution is based on that we want the data CG
// unconditionally, so we check if there is a corresponding current CG
// (which means this is not the first step insn)
public static class FieldAccessCascader extends ListenerAdapter {
@Override
public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
SystemState ss = vm.getSystemState();
if (executedInsn instanceof GETFIELD){
GETFIELD getInsn = (GETFIELD) executedInsn;
FieldInfo fi = getInsn.getFieldInfo(null);
if (fi.getName().equals("mySharedField")){
IntChoiceFromSet cg = ss.getCurrentChoiceGenerator("fieldReplace", IntChoiceFromSet.class);
if (cg == null){
// we might get here after a preceding rescheduling exec, i.e.
// partial execution (with successive re-execution), or after
// non-rescheduling exec has been completed (only one runnable thread).
// In the first case we have to restore the operand stack so that
// we can reexecute
if (!ti.willReExecuteInstruction()){
// restore old operand stack contents
StackFrame frame = ti.getModifiableTopFrame();
frame.pop(FeatureExprFactory.True());
frame.pushRef( FeatureExprFactory.True(), getInsn.getLastThis());
}
cg = new IntChoiceFromSet("fieldReplace", 42, 43);
ss.setNextChoiceGenerator(cg);
ti.reExecuteInstruction();
System.out.println("# listener registered: " + cg);
} else {
StackFrame frame = ti.getModifiableTopFrame();
int v = cg.getNextChoice();
int n = frame.pop(FeatureExprFactory.True()).getValue();
frame.push(FeatureExprFactory.True(), One.valueOf(v));
System.out.println("# listener replacing " + n + " with " + v);
}
}
}
}
//--- those are just for debugging purposes
@Override
public void stateBacktracked(Search search) {
System.out.println("#------ [" + search.getDepth() + "] backtrack: " + search.getStateId());
}
@Override
public void stateAdvanced(Search search){
System.out.println("#------ " + search.getStateId() + " isNew: " + search.isNewState() + ", isEnd: " + search.isEndState());
}
@Override
public void threadScheduled(VM vm, ThreadInfo ti){
System.out.println("# running thread: " + ti);
}
@Override
public void threadTerminated(VM vm, ThreadInfo ti){
System.out.println("# terminated thread: " + ti);
}
@Override
public void threadStarted(VM vm, ThreadInfo ti){
System.out.println("# started thread: " + ti);
}
@Override
public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {
System.out.println("# choice: " + currentCG);
}
}
int mySharedField = -1;
@Test
public void testMixedThreadDataCGs () {
if (verifyNoPropertyViolation("+listener=.test.mc.basic.CascadedCGTest$FieldAccessCascader")){
Thread t = new Thread(){
public void run() {
int n = mySharedField;
System.out.print("<thread> mySharedField read: ");
System.out.println( n);
assert n == 42 || n == 43; // regardless of main thread exec state
}
};
t.start();
mySharedField = 7;
System.out.println("<main> mySharedField write: 7");
}
}
}