Fixing bugs and cleaning up: Continuing sub-graph executions when there is no matched...
[jpf-core.git] / src / main / gov / nasa / jpf / listener / StopWatchFuzzer.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.listener;
19
20 import gov.nasa.jpf.ListenerAdapter;
21 import gov.nasa.jpf.jvm.bytecode.LCMP;
22 import gov.nasa.jpf.jvm.bytecode.LSUB;
23 import gov.nasa.jpf.jvm.bytecode.NATIVERETURN;
24 import gov.nasa.jpf.vm.ClassInfo;
25 import gov.nasa.jpf.vm.Instruction;
26 import gov.nasa.jpf.vm.StackFrame;
27 import gov.nasa.jpf.vm.VM;
28 import gov.nasa.jpf.vm.MethodInfo;
29 import gov.nasa.jpf.vm.ThreadInfo;
30 import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
31
32
33 /**
34  * a listener that is used to explore all paths from a time-value comparison.
35  * 
36  * This works by creating a CG on LCMP instructions that involve 
37  * System.currentTimeMillis() obtained values, which are attribute tagged
38  * upon return from the native method, propagated on LSUB (duration computation),
39  * and finally used for LCMP interception if the tag attributes are present
40  * 
41  *   long t1 = System.currentTimeMillis();
42  *   doSomeComputation();
43  *   long t2 = System.currentTimeMillis();
44  *   if (t2 - t1 <= MAX_DURATION){
45  *     // all fine branch
46  *   } else {
47  *     // catastrophic failure branch
48  *   }
49  * 
50  * which boils down to a bytecode pattern like
51  * 
52  *   invokestatic #2; // System.currentTimeMillis()   <<< tag result with TimeVal attr
53  *   ..
54  *   lsub  <<< propagate if any of the operands has a TimeVal attr
55  *   ..
56  *   lcmp <<< register CG and skip if any of the operands has TimeVal attr
57  * 
58  */
59 public class StopWatchFuzzer extends ListenerAdapter {
60   
61   MethodInfo miCurrentTimeMillis;
62   
63   static class TimeVal {
64     // attribute for values obtained from System.currentTimeMillis()
65   }
66   static TimeVal timeValAttr = new TimeVal(); // we don't need separate instances
67   
68   static String CG_ID = "LCMP_fuzzer";
69
70   @Override
71   public void classLoaded(VM vm, ClassInfo ci){
72     if (miCurrentTimeMillis == null){
73       if (ci.getName().equals("java.lang.System")) {
74         miCurrentTimeMillis = ci.getMethod("currentTimeMillis()J", false); // its got to be there
75       }
76     }
77   }
78   
79   @Override
80   public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
81
82     if (executedInsn instanceof NATIVERETURN){
83       if (executedInsn.isCompleted(ti)){
84         if (((NATIVERETURN)executedInsn).getMethodInfo() == miCurrentTimeMillis){
85           // the two top operand slots hold the 'long' time value
86           StackFrame frame = ti.getModifiableTopFrame();
87           frame.addLongOperandAttr( timeValAttr);
88         }
89       }
90     }
91   }
92   
93   @Override
94   public void executeInstruction(VM vm, ThreadInfo ti, Instruction insnToExecute){
95
96     if (insnToExecute instanceof LSUB){  // propagate TimeVal attrs
97       StackFrame frame = ti.getTopFrame();
98       // check if any of the operands have TimeVal attributes
99       // attributes are stored on the first slot of a long val
100       if (frame.hasOperandAttr(1, TimeVal.class) || frame.hasOperandAttr(3, TimeVal.class)){      
101         // enter insn (this pops the 4 top operand slots and pushes the long result
102         ti.skipInstruction(insnToExecute.execute(ti));
103       
104         // propagate TimeVal attr, now we need a modifiable frame
105         frame = ti.getModifiableTopFrame();
106         frame.addLongOperandAttr(timeValAttr);
107       }
108        
109     } else if (insnToExecute instanceof LCMP){ // create and set CG if operand has TimeVal attr
110       
111       if (!ti.isFirstStepInsn()){ // this is the first time we see this insn
112         StackFrame frame = ti.getTopFrame();
113         
114         if (frame.hasOperandAttr(1, TimeVal.class) || frame.hasOperandAttr(3, TimeVal.class)){
115           IntChoiceFromSet cg = new IntChoiceFromSet( CG_ID, -1, 0, 1);
116           if (vm.setNextChoiceGenerator(cg)){
117             ti.skipInstruction(insnToExecute); // reexecute after breaking the transition
118           }
119         }
120         
121       } else { // it is the beginning of a transition, push the choice and proceed
122         IntChoiceFromSet cg = vm.getCurrentChoiceGenerator(CG_ID, IntChoiceFromSet.class);
123         if (cg != null){
124           int choice = cg.getNextChoice();
125           StackFrame frame = ti.getModifiableTopFrame();
126           
127           // pop the operands 
128           frame.popLong();
129           frame.popLong();
130           
131           frame.push(choice);
132           
133           ti.skipInstruction(insnToExecute.getNext());
134         }
135       }
136     }
137   }
138 }