Make the program cleaner, easy to understand
[jpf-core.git] / src / main / gov / nasa / jpf / listener / ConflictTracker.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.Config;
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.ListenerAdapter;
23 import gov.nasa.jpf.search.Search;
24 import gov.nasa.jpf.jvm.bytecode.*;
25 import gov.nasa.jpf.vm.*;
26 import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
27 import gov.nasa.jpf.vm.bytecode.ReadInstruction;
28 import gov.nasa.jpf.vm.bytecode.StoreInstruction;
29 import gov.nasa.jpf.vm.bytecode.WriteInstruction;
30
31 import java.io.PrintWriter;
32
33 import java.util.*;
34
35 /**
36  * Listener using data flow analysis to find conflicts between smartApps.
37  **/
38
39 public class ConflictTracker extends ListenerAdapter {
40
41   private final PrintWriter out;
42   private final HashSet<String> conflictSet = new HashSet<String>(); // Variables we want to track
43   private final HashSet<String> appSet = new HashSet<String>(); // Apps we want to find their conflicts
44   private final HashMap<Integer, Node> nodes = new HashMap<Integer, Node>(); // Nodes of a graph
45   private final DataSet tempSetSet = new DataSet(false, "NA", false, "NA");
46   volatile private Node parentNode = new Node(-2);
47   volatile private String operation;
48   volatile private String detail;
49   volatile private int depth;
50   volatile private int id;
51   volatile private boolean conflictFound = false;
52   volatile private boolean isSet = false;
53   Transition transition;
54   Object annotation;
55   String label;
56  
57   
58   public ConflictTracker(Config conf, JPF jpf) {
59     out = new PrintWriter(System.out, true);
60
61     String[] conflictVars = conf.getStringArray("variables");
62     // We are not tracking anything if it is null
63     if (conflictVars != null) {
64       for (String var : conflictVars) {
65         conflictSet.add(var);
66       }
67     }
68     String[] apps = conf.getStringArray("apps");
69     // We are not tracking anything if it is null
70     if (apps != null) {
71       for (String var : apps) {
72         appSet.add(var);
73       }
74     }
75   }
76
77   public void setOutSet(Node currentNode) {
78         if ((currentNode.getSetSet().getDataList1().getAppSet())&&(currentNode.getSetSet().getDataList2().getAppSet())) { //App1 & App2 are writers
79                 currentNode.getOutSet().setDataList1(currentNode.getSetSet().getDataList1().getAppSet(), currentNode.getSetSet().getDataList1().getValue());
80                 currentNode.getOutSet().setDataList2(currentNode.getSetSet().getDataList2().getAppSet(), currentNode.getSetSet().getDataList2().getValue());
81         } else if (currentNode.getSetSet().getDataList1().getAppSet()) { //App1 is a writer
82                 currentNode.getOutSet().setDataList1(currentNode.getSetSet().getDataList1().getAppSet(), currentNode.getSetSet().getDataList1().getValue());
83                 currentNode.getOutSet().setDataList2(currentNode.getInSet().getDataList2().getAppSet(), currentNode.getInSet().getDataList2().getValue());
84         } else if (currentNode.getSetSet().getDataList2().getAppSet()) { //App2 is a writer
85                 currentNode.getOutSet().setDataList1(currentNode.getInSet().getDataList1().getAppSet(), currentNode.getInSet().getDataList1().getValue());
86                 currentNode.getOutSet().setDataList2(currentNode.getSetSet().getDataList2().getAppSet(), currentNode.getSetSet().getDataList2().getValue());
87         } else { //No writer for this node => outSet = inSet
88                 currentNode.getOutSet().setDataList1(currentNode.getInSet().getDataList1().getAppSet(), currentNode.getInSet().getDataList1().getValue());
89                 currentNode.getOutSet().setDataList2(currentNode.getInSet().getDataList2().getAppSet(), currentNode.getInSet().getDataList2().getValue());
90         }
91   }
92
93   public void setInSet(Node currentNode) {
94         for (Node i : currentNode.getPredecessors()) {
95                 //Check to see if dataList1 of predNode(i) is valid or not: if valid get its value
96                 if (i.getOutSet().getDataList1().getAppSet()) {
97                         currentNode.getInSet().setDataList1(true, i.getOutSet().getDataList1().getValue());
98                 }
99
100                 //Check to see if dataList2 of predNode(i) is valid or not: if valid get its value
101                 if (i.getOutSet().getDataList2().getAppSet()) {
102                         currentNode.getInSet().setDataList2(true, i.getOutSet().getDataList2().getValue());
103                 }
104         }
105   }
106
107   boolean checkOutSetChange(Node currentNode, DataSet tempOutSet) {
108         return ((currentNode.getOutSet().getDataList1().getAppSet() != tempOutSet.getDataList1().getAppSet())||
109                 !(currentNode.getOutSet().getDataList1().getValue().equals(tempOutSet.getDataList1().getValue()))||
110                 (currentNode.getOutSet().getDataList2().getAppSet() != tempOutSet.getDataList2().getAppSet())||
111                 !(currentNode.getOutSet().getDataList2().getValue().equals(tempOutSet.getDataList2().getValue())));
112   }
113
114   public void checkForConflict(Node currentNode) {
115         if ((currentNode.getOutSet().getDataList1().getAppSet() == true)&&(currentNode.getOutSet().getDataList2().getAppSet() == true)) { //Both apps have set the device
116                 if (!(currentNode.getOutSet().getDataList1().getValue().equals(currentNode.getOutSet().getDataList2().getValue()))) //Values set by the apps are not the same, and we have a conflict!
117                         conflictFound = true;
118         }
119   }
120
121   class Node {
122         Integer id;
123         ArrayList<Node> predecessors = new ArrayList<Node>();
124         ArrayList<Node> successors = new ArrayList<Node>();
125         DataSet inSet = new DataSet(false, "NA", false, "NA");
126         DataSet setSet = new DataSet(false, "NA", false, "NA");
127         DataSet outSet = new DataSet(false, "NA", false, "NA");
128
129         Node(Integer id) {
130                 this.id = id;
131         }
132
133         void addPredecessor(Node node) {
134                 predecessors.add(node);
135         }
136
137         void addSuccessor(Node node) {
138                 successors.add(node);
139         }
140
141         void emptyInSet() {
142                 this.inSet = new DataSet(false, "NA", false, "NA");
143         }
144
145         void setInSet(DataSet inSet) {
146                 this.inSet.setDataList1(inSet.getDataList1().getAppSet(), inSet.getDataList1().getValue());
147                 this.inSet.setDataList2(inSet.getDataList2().getAppSet(), inSet.getDataList2().getValue());
148         }
149
150         void setSetSet(DataSet setSet) {
151                 this.setSet.setDataList1(setSet.getDataList1().getAppSet(), setSet.getDataList1().getValue());
152                 this.setSet.setDataList2(setSet.getDataList2().getAppSet(), setSet.getDataList2().getValue());
153         }
154
155         void setOutSet(DataSet outSet) {
156                 this.outSet.setDataList1(outSet.getDataList1().getAppSet(), outSet.getDataList1().getValue());
157                 this.outSet.setDataList2(outSet.getDataList2().getAppSet(), outSet.getDataList2().getValue());
158         }
159
160         Integer getId() {
161                 return id;
162         }
163
164         ArrayList<Node> getPredecessors() {
165                 return predecessors;
166         }
167
168         ArrayList<Node> getSuccessors() {
169                 return successors;
170         }
171
172         DataSet getInSet() {
173                 return inSet;
174         }
175
176         DataSet getOutSet() {
177                 return outSet;
178         }
179
180         DataSet getSetSet() {
181                 return setSet;
182         }
183   }
184
185   class DataList {
186         boolean appSet;
187         String value;
188
189         DataList(boolean appSet, String value) {
190                 this.appSet = appSet;
191                 this.value = value;
192         }
193
194         void setAppSet(boolean appSet) {
195                 this.appSet = appSet;
196         }
197
198         void setValue(String value) {
199                 this.value = value;
200         }
201
202         boolean getAppSet() {
203                 return appSet;
204         }
205
206         String getValue() {
207                 return value;
208         }
209   }
210
211   class DataSet {
212         DataList dataList1 = new DataList(false, "NA");
213         DataList dataList2 = new DataList(false, "NA"); 
214
215         DataSet(boolean appSet1, String value1, boolean appSet2, String value2) {
216                 dataList1.setAppSet(appSet1);
217                 dataList1.setValue(value1);
218                 dataList2.setAppSet(appSet2);
219                 dataList2.setValue(value2);
220         }
221
222         void setDataList1(boolean appSet, String value) {
223                 dataList1.setAppSet(appSet);
224                 dataList1.setValue(value);
225         }
226
227         void setDataList2(boolean appSet, String value) {
228                 dataList2.setAppSet(appSet);
229                 dataList2.setValue(value);
230         }
231
232         DataList getDataList1() {
233                 return dataList1;
234         }
235
236         DataList getDataList2() {
237                 return dataList2;
238         }
239   }
240
241   @Override
242   public void stateRestored(Search search) {
243     id = search.getStateId();
244     depth = search.getDepth();
245     operation = "restored";
246     detail = null;
247
248     out.println("The state is restored to state with id: "+id+", depth: "+depth);
249   
250     //Update the parent node
251     parentNode = new Node(id);
252   }
253
254   @Override
255   public void searchStarted(Search search) {
256     out.println("----------------------------------- search started");
257   }
258  
259
260   @Override
261   public void stateAdvanced(Search search) {
262     String theEnd = null;
263     id = search.getStateId();
264     depth = search.getDepth();
265     operation = "forward";
266
267     //Add the node to the list of nodes 
268     nodes.put(id, new Node(id));
269     Node currentNode = nodes.get(id);    
270
271     if (search.isNewState()) {
272       //Add this new node
273       detail = "new";
274       //Update the setSet for this new node
275       if (isSet) {
276         currentNode.setSetSet(tempSetSet);
277         isSet = false;
278       }
279     } else {
280       detail = "visited";
281     }
282
283     if (search.isEndState()) {
284       out.println("This is the last state!");
285       theEnd = "end";
286     }
287
288     out.println("The state is forwarded to state with id: "+id+", depth: "+depth+" which is "+detail+" state: "+"% "+theEnd);
289     
290     //Updating the predecessors for this node
291     //Check if parent node is already in successors of the current node or not
292     if (!(currentNode.getPredecessors().contains(parentNode)))
293         currentNode.addPredecessor(parentNode);
294
295     //Update the successors for this node
296     //Check if current node is already in successors of the parent node or not
297     if (!(parentNode.getSuccessors().contains(currentNode)))
298         parentNode.addSuccessor(currentNode);
299     
300     
301     //Set the input set of this state to empty
302     currentNode.emptyInSet();
303
304
305     //Store the out set of this state to the temporary data set
306     DataSet tempOutSet = new DataSet(currentNode.getOutSet().getDataList1().getAppSet(),
307                                      currentNode.getOutSet().getDataList1().getValue(),
308                                      currentNode.getOutSet().getDataList2().getAppSet(),
309                                      currentNode.getOutSet().getDataList2().getValue());
310
311     
312     //Set input set according to output set of pred states of current state
313     setInSet(currentNode);
314     
315
316
317     //Set dataLists of outSet to dataLists of setSet if it is valid, otherwise to dataLists of inSet
318     setOutSet(currentNode);
319
320     
321     //Check for a conflict
322     checkForConflict(currentNode);
323     
324     
325
326     //Check if the outSet of this state has changed, update all of its successors' sets if any
327     if (checkOutSetChange(currentNode, tempOutSet)) {
328                 ArrayList<Node> changed = new ArrayList<Node>(currentNode.getSuccessors());
329                 while(!changed.isEmpty()) {
330                         //Get a random node inside the changed list and remove it from the list
331                         Integer rnd = new Random().nextInt(changed.size());
332                         Node nodeToProcess  = changed.get(rnd);
333                         changed.remove(nodeToProcess);
334
335                         //Initialize the empty input set for current node
336                         nodeToProcess.emptyInSet();
337
338                         //Store the out set of this state to the temporary data set
339                         tempOutSet = new DataSet(nodeToProcess.getOutSet().getDataList1().getAppSet(),
340                                                  nodeToProcess.getOutSet().getDataList1().getValue(),
341                                                  nodeToProcess.getOutSet().getDataList2().getAppSet(),
342                                                  nodeToProcess.getOutSet().getDataList2().getValue());
343
344                                 
345                         //Set input set according to output set of pred states of current state
346                         setInSet(nodeToProcess);
347
348                         
349                         //Set outSet to setSet if it is valid, otherwise to inSet
350                         setOutSet(nodeToProcess);
351
352                         
353                         //Checking if the out set has changed or not(Add its successors to the change list!)
354                         if (checkOutSetChange(nodeToProcess, tempOutSet)) {
355                                 for (Node i : nodeToProcess.getSuccessors()) {
356                                         if (!changed.contains(i))
357                                                 changed.add(i);
358                                 }
359                         }
360                 }
361     }
362
363     //Update the parent node
364     parentNode = new Node(id);
365         
366   }
367
368   @Override
369   public void stateBacktracked(Search search) {
370     id = search.getStateId();
371     depth = search.getDepth();
372     operation = "backtrack";
373     detail = null;
374
375     out.println("The state is backtracked to state with id: "+id+", depth: "+depth);
376
377     //Update the parent node
378     parentNode = new Node(id);
379   }
380
381   @Override
382   public void searchFinished(Search search) {
383     out.println("----------------------------------- search finished");
384   }
385
386   private String getValue(ThreadInfo ti, Instruction inst, byte type) {
387     StackFrame frame;
388     int lo, hi;
389
390     frame = ti.getTopFrame();
391
392     if ((inst instanceof JVMLocalVariableInstruction) ||
393         (inst instanceof JVMFieldInstruction))
394     {
395        if (frame.getTopPos() < 0)
396          return(null);
397
398        lo = frame.peek();
399        hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
400
401        return(decodeValue(type, lo, hi));
402     }
403
404     if (inst instanceof JVMArrayElementInstruction)
405       return(getArrayValue(ti, type));
406
407     return(null);
408   }
409
410   private final static String decodeValue(byte type, int lo, int hi) {
411     switch (type) {
412       case Types.T_ARRAY:   return(null);
413       case Types.T_VOID:    return(null);
414
415       case Types.T_BOOLEAN: return(String.valueOf(Types.intToBoolean(lo)));
416       case Types.T_BYTE:    return(String.valueOf(lo));
417       case Types.T_CHAR:    return(String.valueOf((char) lo));
418       case Types.T_DOUBLE:  return(String.valueOf(Types.intsToDouble(lo, hi)));
419       case Types.T_FLOAT:   return(String.valueOf(Types.intToFloat(lo)));
420       case Types.T_INT:     return(String.valueOf(lo));
421       case Types.T_LONG:    return(String.valueOf(Types.intsToLong(lo, hi)));
422       case Types.T_SHORT:   return(String.valueOf(lo));
423
424       case Types.T_REFERENCE:
425         ElementInfo ei = VM.getVM().getHeap().get(lo);
426         if (ei == null)
427           return(null);
428
429         ClassInfo ci = ei.getClassInfo();
430         if (ci == null)
431           return(null);
432
433         if (ci.getName().equals("java.lang.String"))
434           return('"' + ei.asString() + '"');
435
436         return(ei.toString());
437
438       default:
439         System.err.println("Unknown type: " + type);
440         return(null);
441      }
442   }
443
444   private String getArrayValue(ThreadInfo ti, byte type) {
445     StackFrame frame;
446     int lo, hi;
447
448     frame = ti.getTopFrame();
449     lo    = frame.peek();
450     hi    = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
451
452     return(decodeValue(type, lo, hi));
453   }
454
455   private byte getType(ThreadInfo ti, Instruction inst) {
456     StackFrame frame;
457     FieldInfo fi;
458     String type;
459
460     frame = ti.getTopFrame();
461     if ((frame.getTopPos() >= 0) && (frame.isOperandRef())) {
462       return (Types.T_REFERENCE);
463     }
464
465     type = null;
466
467     if (inst instanceof JVMLocalVariableInstruction) {
468       type = ((JVMLocalVariableInstruction) inst).getLocalVariableType();
469     } else if (inst instanceof JVMFieldInstruction){
470       fi = ((JVMFieldInstruction) inst).getFieldInfo();
471       type = fi.getType();
472     }
473
474     if (inst instanceof JVMArrayElementInstruction) {
475       return (getTypeFromInstruction(inst));
476     }
477
478     if (type == null) {
479       return (Types.T_VOID);
480     }
481
482     return (decodeType(type));
483   }
484
485   private final static byte getTypeFromInstruction(Instruction inst) {
486     if (inst instanceof JVMArrayElementInstruction)
487       return(getTypeFromInstruction((JVMArrayElementInstruction) inst));
488
489     return(Types.T_VOID);
490   }
491
492   private final static byte decodeType(String type) {
493     if (type.charAt(0) == '?'){
494       return(Types.T_REFERENCE);
495     } else {
496       return Types.getBuiltinType(type);
497     }
498   }
499
500   // Find the variable writer
501   // It should be one of the apps listed in the .jpf file
502   private String getWriter(List<StackFrame> sfList) {
503     // Start looking from the top of the stack backward
504     for(int i=sfList.size()-1; i>=0; i--) {
505       MethodInfo mi = sfList.get(i).getMethodInfo();
506       if(!mi.isJPFInternal()) {
507         String method = mi.getStackTraceName();
508         // Check against the apps in the appSet
509         for(String app : appSet) {
510           // There is only one writer at a time but we need to always
511           // check all the potential writers in the list
512           if (method.contains(app)) {
513             return app;
514           }
515         }
516       }
517     }
518
519     return null;
520   }
521
522   @Override
523   public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
524     if (conflictFound) {
525       StringBuilder sb = new StringBuilder();
526       sb.append("Conflict found between two apps!");
527       Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sb.toString());
528       ti.setNextPC(nextIns);
529     } else if (executedInsn instanceof WriteInstruction) {
530       String varId = ((WriteInstruction) executedInsn).getFieldInfo().getFullName();
531       for(String var : conflictSet) {
532
533         if (varId.contains(var)) {
534           // Get variable info
535           byte type  = getType(ti, executedInsn);
536           String value = getValue(ti, executedInsn, type);
537           String writer = getWriter(ti.getStack());
538           // Just return if the writer is not one of the listed apps in the .jpf file
539           if (writer == null)
540             return;
541
542         //Update the temporary Set set.
543         if (writer.equals("App1"))
544                 tempSetSet.setDataList1(true, value);
545         else if (writer.equals("App2"))
546                 tempSetSet.setDataList2(true, value);
547         //Set isSet to 1        
548         isSet = true;
549                   
550        }
551       }
552
553       
554     }
555   
556   }
557
558 }