9fb12cf3dac2aa6a4769468a7966edf7a7d5e710
[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 HashSet<String> manualSet = new HashSet<String>(); // Writer classes with manual inputs to detect direct-direct(No Conflict) interactions
45   private final HashMap<Integer, Node> nodes = new HashMap<Integer, Node>(); // Nodes of a graph
46   private HashSet<NameValuePair> tempSetSet = new HashSet<NameValuePair>();
47   private long timeout;
48   private long startTime;
49   private Node parentNode = new Node(-2);
50   private String operation;
51   private String detail;
52   private String errorMessage;
53   private int depth;
54   private int id;
55   private boolean conflictFound = false;
56   private boolean isSet = false;
57   private boolean manual = false;
58  
59   
60   public ConflictTracker(Config config, JPF jpf) {
61     out = new PrintWriter(System.out, true);
62
63     String[] conflictVars = config.getStringArray("variables");
64     // We are not tracking anything if it is null
65     if (conflictVars != null) {
66       for (String var : conflictVars) {
67         conflictSet.add(var);
68       }
69     }
70     String[] apps = config.getStringArray("apps");
71     // We are not tracking anything if it is null
72     if (apps != null) {
73       for (String var : apps) {
74         appSet.add(var);
75       }
76     }
77     String[] manualClasses = config.getStringArray("manualClasses");
78     // We are not tracking anything if it is null
79     if (manualClasses != null) {
80       for (String var : manualClasses) {
81         manualSet.add(var);
82       }
83     }
84
85     // Timeout input from config is in minutes, so we need to convert into millis
86     timeout = config.getInt("timeout", 0) * 60 * 1000;
87     startTime = System.currentTimeMillis();
88   }
89
90   boolean propagateTheChange(Node currentNode) {
91         HashSet<Node> changed = new HashSet<Node>(currentNode.getSuccessors());
92
93         while(!changed.isEmpty()) {
94                 // Get the first element of HashSet and remove it from the changed set
95                 Node nodeToProcess = changed.iterator().next();
96                 changed.remove(nodeToProcess);
97                         
98                 // Update the sets, store the outSet to temp before its changes
99                 boolean isChanged = updateSets(nodeToProcess);
100
101                 // Check for a conflict
102                 if (checkForConflict(nodeToProcess))
103                         return true;
104                         
105                 // Checking if the out set has changed or not(Add its successors to the change list!)
106                 if (isChanged) {
107                         for (Node i : nodeToProcess.getSuccessors()) {
108                                 if (!changed.contains(i))
109                                         changed.add(i);
110                         }
111                 }
112         }
113
114         return false;
115   }
116
117   boolean setOutSet(Node currentNode) {
118         Integer prevSize = currentNode.getOutSet().size();
119
120         // Update based on setSet
121         for (NameValuePair i : currentNode.getSetSet()) {
122                 if (currentNode.getOutSet().contains(i))
123                         currentNode.getOutSet().remove(i);      
124                 currentNode.getOutSet().add(i);
125         }
126
127         // Add all the inSet
128         currentNode.getOutSet().addAll(currentNode.getInSet());
129
130         // Check if the outSet is changed
131         if (!prevSize.equals(currentNode.getOutSet().size()))
132                 return true;
133         
134         return false;
135   }
136
137   void setInSet(Node currentNode) {
138         for (Node i : currentNode.getPredecessors()) {
139                 currentNode.getInSet().addAll(i.getOutSet());
140         }
141   }
142
143   boolean checkForConflict(Node currentNode) {
144         HashMap<String, String> valueMap = new HashMap<String, String>(); // HashMap from varName to value
145         HashMap<String, Integer> writerMap = new HashMap<String, Integer>(); // HashMap from varName to appNum
146
147         for (NameValuePair i : currentNode.getSetSet()) {
148                 if (i.getIsManual()) // Manual input: we have no conflict
149                         continue;
150
151                 valueMap.put(i.getVarName(), i.getValue());
152                 if (writerMap.containsKey(i.getVarName()))
153                         writerMap.put(i.getVarName(), i.getAppNum()+writerMap.get(i.getVarName())); // We have two writers?
154                 else
155                         writerMap.put(i.getVarName(), i.getAppNum());
156         }
157
158         // Comparing the inSet and setSet to find the conflict
159         for (NameValuePair i : currentNode.getInSet()) {
160                 if (valueMap.containsKey(i.getVarName())) {
161                         if (!(valueMap.get(i.getVarName()).equals(i.getValue()))) // We have different values
162                                 if (!(writerMap.get(i.getVarName()).equals(i.getAppNum()))) {// We have different writers
163                                         errorMessage = "Conflict found between the two apps. App"+i.getAppNum()+" has written the value: "+i.getValue()+
164                                                         " to the variable: "+i.getVarName()+" while App"+writerMap.get(i.getVarName())+" is overwriting the value: "
165                                                         +valueMap.get(i.getVarName())+" to the same variable!";
166                                         return true;
167                                 }
168                 }
169         }
170
171         return false;
172   }
173
174   boolean updateSets(Node currentNode) {
175         // Set input set according to output set of pred states of current state
176         setInSet(currentNode);
177
178         // Set outSet according to inSet, and setSet of current node, check if there is a change
179         return setOutSet(currentNode);
180   }
181
182   static class Node {
183         Integer id;
184         HashSet<Node> predecessors = new HashSet<Node>();
185         HashSet<Node> successors = new HashSet<Node>();
186         HashSet<NameValuePair> inSet = new HashSet<NameValuePair>();
187         HashSet<NameValuePair> setSet = new HashSet<NameValuePair>();
188         HashSet<NameValuePair> outSet = new HashSet<NameValuePair>();
189
190         Node(Integer id) {
191                 this.id = id;
192         }
193
194         void addPredecessor(Node node) {
195                 predecessors.add(node);
196         }
197
198         void addSuccessor(Node node) {
199                 successors.add(node);
200         }
201
202         void setSetSet(HashSet<NameValuePair> setSet, boolean isManual) {
203                 if (isManual)
204                         this.setSet = new HashSet<NameValuePair>();
205                 for (NameValuePair i : setSet) {
206                         this.setSet.add(new NameValuePair(i.getAppNum(), i.getValue(), i.getVarName(), i.getIsManual()));
207                 }
208         }
209
210         Integer getId() {
211                 return id;
212         }
213
214         HashSet<Node> getPredecessors() {
215                 return predecessors;
216         }
217
218         HashSet<Node> getSuccessors() {
219                 return successors;
220         }
221
222         HashSet<NameValuePair> getInSet() {
223                 return inSet;
224         }
225
226         HashSet<NameValuePair> getSetSet() {
227                 return setSet;
228         }
229
230         HashSet<NameValuePair> getOutSet() {
231                 return outSet;
232         }
233   }
234
235   static class NameValuePair {
236         Integer appNum;
237         String value;
238         String varName;
239         boolean isManual;
240
241         NameValuePair(Integer appNum, String value, String varName, boolean isManual) {
242                 this.appNum = appNum;
243                 this.value = value;
244                 this.varName = varName;
245                 this.isManual = isManual;
246         }
247
248         void setAppNum(Integer appNum) {
249                 this.appNum = appNum;
250         }
251
252         void setValue(String value) {
253                 this.value = value;
254         }
255
256         void setVarName(String varName) {
257                 this.varName = varName;
258         }
259
260         void setIsManual(String varName) {
261                 this.isManual = isManual;
262         }
263
264         Integer getAppNum() {
265                 return appNum;
266         }
267
268         String getValue() {
269                 return value;
270         }
271
272         String getVarName() {
273                 return varName;
274         }
275
276         boolean getIsManual() {
277                 return isManual;
278         }
279
280         @Override
281         public boolean equals(Object o) {
282                 if (o instanceof NameValuePair) {
283                         NameValuePair other = (NameValuePair) o;
284                         if (varName.equals(other.getVarName()))
285                                 return appNum.equals(other.getAppNum());
286                 }
287                 return false;
288         }
289
290         @Override
291         public int hashCode() {
292                 return appNum.hashCode() * 31 + varName.hashCode();
293         }
294   }
295
296   @Override
297   public void stateRestored(Search search) {
298     id = search.getStateId();
299     depth = search.getDepth();
300     operation = "restored";
301     detail = null;
302
303     out.println("The state is restored to state with id: "+id+", depth: "+depth);
304   
305     // Update the parent node
306     if (nodes.containsKey(id)) {
307         parentNode = nodes.get(id);
308     } else {
309         parentNode = new Node(id);
310     }
311   }
312
313   @Override
314   public void searchStarted(Search search) {
315     out.println("----------------------------------- search started");
316   }
317  
318
319   @Override
320   public void stateAdvanced(Search search) {
321     String theEnd = null;
322     id = search.getStateId();
323     depth = search.getDepth();
324     operation = "forward";
325
326     // Add the node to the list of nodes        
327     nodes.put(id, new Node(id));
328     Node currentNode = nodes.get(id);
329
330     // Update the setSet for this new node
331     if (isSet) {
332       currentNode.setSetSet(tempSetSet, manual);
333       tempSetSet = new HashSet<NameValuePair>(); 
334       isSet = false;
335       manual = false;
336     }
337
338     if (search.isNewState()) {
339       detail = "new";
340     } else {
341       detail = "visited";
342     }
343
344     if (search.isEndState()) {
345       out.println("This is the last state!");
346       theEnd = "end";
347     }
348
349     out.println("The state is forwarded to state with id: "+id+", depth: "+depth+" which is "+detail+" state: "+"% "+theEnd);
350     
351     // Updating the predecessors for this node
352     // Check if parent node is already in successors of the current node or not
353     if (!(currentNode.getPredecessors().contains(parentNode)))
354         currentNode.addPredecessor(parentNode);
355
356     // Update the successors for this node
357     // Check if current node is already in successors of the parent node or not
358     if (!(parentNode.getSuccessors().contains(currentNode)))
359         parentNode.addSuccessor(currentNode);
360
361     // Update the sets, check if the outSet is changed or not
362     boolean isChanged = updateSets(currentNode);
363    
364     // Check for a conflict
365     conflictFound = checkForConflict(currentNode);
366
367     // Check if the outSet of this state has changed, update all of its successors' sets if any
368     if (isChanged)
369         conflictFound = conflictFound || propagateTheChange(currentNode);
370
371     // Update the parent node
372     if (nodes.containsKey(id)) {
373         parentNode = nodes.get(id);
374     } else {
375         parentNode = new Node(id);
376     }
377   }
378
379   @Override
380   public void stateBacktracked(Search search) {
381     id = search.getStateId();
382     depth = search.getDepth();
383     operation = "backtrack";
384     detail = null;
385
386     out.println("The state is backtracked to state with id: "+id+", depth: "+depth);
387
388     // Update the parent node
389     if (nodes.containsKey(id)) {
390         parentNode = nodes.get(id);
391     } else {
392         parentNode = new Node(id);
393     }
394   }
395
396   @Override
397   public void searchFinished(Search search) {
398     out.println("----------------------------------- search finished");
399   }
400
401   private String getValue(ThreadInfo ti, Instruction inst, byte type) {
402     StackFrame frame;
403     int lo, hi;
404
405     frame = ti.getTopFrame();
406
407     if ((inst instanceof JVMLocalVariableInstruction) ||
408         (inst instanceof JVMFieldInstruction))
409     {
410        if (frame.getTopPos() < 0)
411          return(null);
412
413        lo = frame.peek();
414        hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
415
416        return(decodeValue(type, lo, hi));
417     }
418
419     if (inst instanceof JVMArrayElementInstruction)
420       return(getArrayValue(ti, type));
421
422     return(null);
423   }
424
425   private final static String decodeValue(byte type, int lo, int hi) {
426     switch (type) {
427       case Types.T_ARRAY:   return(null);
428       case Types.T_VOID:    return(null);
429
430       case Types.T_BOOLEAN: return(String.valueOf(Types.intToBoolean(lo)));
431       case Types.T_BYTE:    return(String.valueOf(lo));
432       case Types.T_CHAR:    return(String.valueOf((char) lo));
433       case Types.T_DOUBLE:  return(String.valueOf(Types.intsToDouble(lo, hi)));
434       case Types.T_FLOAT:   return(String.valueOf(Types.intToFloat(lo)));
435       case Types.T_INT:     return(String.valueOf(lo));
436       case Types.T_LONG:    return(String.valueOf(Types.intsToLong(lo, hi)));
437       case Types.T_SHORT:   return(String.valueOf(lo));
438
439       case Types.T_REFERENCE:
440         ElementInfo ei = VM.getVM().getHeap().get(lo);
441         if (ei == null)
442           return(null);
443
444         ClassInfo ci = ei.getClassInfo();
445         if (ci == null)
446           return(null);
447
448         if (ci.getName().equals("java.lang.String"))
449           return('"' + ei.asString() + '"');
450
451         return(ei.toString());
452
453       default:
454         System.err.println("Unknown type: " + type);
455         return(null);
456      }
457   }
458
459   private String getArrayValue(ThreadInfo ti, byte type) {
460     StackFrame frame;
461     int lo, hi;
462
463     frame = ti.getTopFrame();
464     lo    = frame.peek();
465     hi    = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
466
467     return(decodeValue(type, lo, hi));
468   }
469
470   private byte getType(ThreadInfo ti, Instruction inst) {
471     StackFrame frame;
472     FieldInfo fi;
473     String type;
474
475     frame = ti.getTopFrame();
476     if ((frame.getTopPos() >= 0) && (frame.isOperandRef())) {
477       return (Types.T_REFERENCE);
478     }
479
480     type = null;
481
482     if (inst instanceof JVMLocalVariableInstruction) {
483       type = ((JVMLocalVariableInstruction) inst).getLocalVariableType();
484     } else if (inst instanceof JVMFieldInstruction){
485       fi = ((JVMFieldInstruction) inst).getFieldInfo();
486       type = fi.getType();
487     }
488
489     if (inst instanceof JVMArrayElementInstruction) {
490       return (getTypeFromInstruction(inst));
491     }
492
493     if (type == null) {
494       return (Types.T_VOID);
495     }
496
497     return (decodeType(type));
498   }
499
500   private final static byte getTypeFromInstruction(Instruction inst) {
501     if (inst instanceof JVMArrayElementInstruction)
502       return(getTypeFromInstruction((JVMArrayElementInstruction) inst));
503
504     return(Types.T_VOID);
505   }
506
507   private final static byte decodeType(String type) {
508     if (type.charAt(0) == '?'){
509       return(Types.T_REFERENCE);
510     } else {
511       return Types.getBuiltinType(type);
512     }
513   }
514
515   // Find the variable writer
516   // It should be one of the apps listed in the .jpf file
517   private String getWriter(List<StackFrame> sfList, HashSet<String> writerSet) {
518     // Start looking from the top of the stack backward
519     for(int i=sfList.size()-1; i>=0; i--) {
520       MethodInfo mi = sfList.get(i).getMethodInfo();
521       if(!mi.isJPFInternal()) {
522         String method = mi.getStackTraceName();
523         // Check against the writers in the writerSet
524         for(String writer : writerSet) {
525           if (method.contains(writer)) {
526             return writer;
527           }
528         }
529       }
530     }
531
532     return null;
533   }
534
535   @Override
536   public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
537     // Instantiate timeoutTimer
538     if (timeout > 0) {
539       if (System.currentTimeMillis() - startTime > timeout) {
540         StringBuilder sbTimeOut = new StringBuilder();
541         sbTimeOut.append("Execution timeout: " + (timeout / (60 * 1000)) + " minutes have passed!");
542         Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sbTimeOut.toString());
543         ti.setNextPC(nextIns);
544       }
545     }
546
547     if (conflictFound) {
548       StringBuilder sb = new StringBuilder();
549       sb.append(errorMessage);
550       Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sb.toString());
551       ti.setNextPC(nextIns);
552     } else if (executedInsn instanceof WriteInstruction) {
553       String varId = ((WriteInstruction) executedInsn).getFieldInfo().getFullName();
554       for(String var : conflictSet) {
555
556         if (varId.contains(var)) {
557           // Get variable info
558           byte type  = getType(ti, executedInsn);
559           String value = getValue(ti, executedInsn, type);
560           String writer = getWriter(ti.getStack(), appSet);
561           // Just return if the writer is not one of the listed apps in the .jpf file
562           if (writer == null)
563             return;
564
565           if (getWriter(ti.getStack(), manualSet) != null)
566                 manual = true;
567
568           // Update the temporary Set set.
569           if (writer.equals("App1"))
570                   tempSetSet.add(new NameValuePair(1, value, var, manual));
571           else if (writer.equals("App2"))
572                   tempSetSet.add(new NameValuePair(2, value, var, manual));
573           // Set isSet to true
574           isSet = true;
575                   
576        }
577      }
578
579       
580   }
581   
582  }
583
584 }