Adding the old tracker variable for debugging/testing purposes.
[jpf-core.git] / src / main / gov / nasa / jpf / listener / ConflictTrackerOld.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 import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
31
32 import java.io.PrintWriter;
33
34 import java.util.*;
35
36 /**
37  * Listener using data flow analysis to find conflicts between smartApps.
38  **/
39
40 public class ConflictTrackerOld extends ListenerAdapter {
41   // Public graph: to allow the StateReducer class to access it
42   public static final HashMap<Integer, Node> nodes = new HashMap<Integer, Node>(); // Nodes of a graph
43   // Private
44   private final PrintWriter out;
45   private final HashSet<String> conflictSet = new HashSet<String>(); // Variables we want to track
46   private final HashSet<String> appSet = new HashSet<String>(); // Apps we want to find their conflicts
47   private final HashSet<String> manualSet = new HashSet<String>(); // Writer classes with manual inputs to detect direct-direct(No Conflict) interactions
48   private HashSet<Transition> transitions = new HashSet<Transition>();
49   private ArrayList<Update> currUpdates = new ArrayList<Update>();
50   private long timeout;
51   private long startTime;
52   private Node parentNode = new Node(-2);
53   private String operation;
54   private String detail;
55   private String errorMessage;
56   private int depth;
57   private int id;
58   private boolean manual = false;
59   private boolean conflictFound = false;
60   private int currentEvent = -1;
61   private boolean debugMode = false;
62
63   private final String SET_LOCATION_METHOD = "setLocationMode";
64   private final String LOCATION_VAR = "locationMode";
65
66   public ConflictTrackerOld(Config config, JPF jpf) {
67     out = new PrintWriter(System.out, true);
68     debugMode = config.getBoolean("debug_mode", false);
69
70     String[] conflictVars = config.getStringArray("variables");
71     // We are not tracking anything if it is null
72     if (conflictVars != null) {
73       for (String var : conflictVars) {
74         conflictSet.add(var);
75       }
76     }
77     String[] apps = config.getStringArray("apps");
78     // We are not tracking anything if it is null
79     if (apps != null) {
80       for (String var : apps) {
81         appSet.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   void propagateChange(Edge newEdge) {
91     HashSet<Edge> changed = new HashSet<Edge>();
92
93     // Add the current node to the changed set
94     changed.add(newEdge);
95
96     while(!changed.isEmpty()) {
97       // Get the first element of the changed set and remove it
98       Edge edgeToProcess = changed.iterator().next();
99       changed.remove(edgeToProcess);
100
101       //If propagating change on edge causes change, enqueue all the target node's out edges
102       if (propagateEdge(edgeToProcess)) {
103         Node dst = edgeToProcess.getDst();
104         for (Edge e : dst.getOutEdges()) {
105           changed.add(e);
106         }
107       }
108     }
109   }
110
111   boolean propagateEdge(Edge e) {
112     HashMap<IndexObject, HashSet<Update>> srcUpdates = e.getSrc().getLastUpdates();
113     HashMap<IndexObject, HashSet<Update>> dstUpdates = e.getDst().getLastUpdates();
114     ArrayList<Update> edgeUpdates = e.getUpdates();
115     HashMap<IndexObject, Update> lastupdate = new HashMap<IndexObject, Update>();
116     boolean changed = false;
117
118     //Go through each update on the current transition
119     for(int i=0; i<edgeUpdates.size(); i++) {
120       Update u = edgeUpdates.get(i);
121       IndexObject io = u.getIndex();
122       HashSet<Update> confupdates = null;
123
124       //See if we have already updated this device attribute
125       if (lastupdate.containsKey(io)) {
126         confupdates = new HashSet<Update>();
127         confupdates.add(lastupdate.get(io));
128       } else if (srcUpdates.containsKey(io)){
129         confupdates = srcUpdates.get(io);
130       }
131
132       //Check for conflict with the appropriate update set if we are not a manual transition
133       //If this is debug mode, then we do not report any conflicts
134       if (!debugMode && confupdates != null && !u.isManual()) {
135         for(Update u2: confupdates) {
136           if (conflicts(u, u2)) {
137             //throw new RuntimeException(createErrorMessage(u, u2));
138             conflictFound = true;
139             errorMessage = createErrorMessage(u, u2);
140           }
141         }
142       }
143       lastupdate.put(io, u);
144     }
145     for(IndexObject io: srcUpdates.keySet()) {
146       //Only propagate src changes if the transition doesn't update the device attribute
147       if (!lastupdate.containsKey(io)) {
148         //Make sure destination has hashset in map
149         if (!dstUpdates.containsKey(io))
150           dstUpdates.put(io, new HashSet<Update>());
151
152         changed |= dstUpdates.get(io).addAll(srcUpdates.get(io));
153       }
154     }
155     for(IndexObject io: lastupdate.keySet()) {
156       //Make sure destination has hashset in map
157       if (!dstUpdates.containsKey(io))
158         dstUpdates.put(io, new HashSet<Update>());
159
160       changed |= dstUpdates.get(io).add(lastupdate.get(io));
161     }
162     return changed;
163   }
164
165   //Method to check for conflicts between two updates
166   //Have conflict if same device, same attribute, different app, different vaalue
167   boolean conflicts(Update u, Update u2) {
168     return (!u.getApp().equals(u2.getApp())) &&
169             u.getDeviceId().equals(u2.getDeviceId()) &&
170             u.getAttribute().equals(u2.getAttribute()) &&
171             (!u.getValue().equals(u2.getValue()));
172   }
173
174   String createErrorMessage(Update u, Update u2) {
175     String message = "Conflict found between the two apps. "+u.getApp()+
176             " has written the value: "+u.getValue()+
177             " to the attribute: "+u.getAttribute()+" while "
178             +u2.getApp()+" is writing the value: "
179             +u2.getValue()+" to the same variable!";
180     System.out.println(message);
181     return message;
182   }
183
184   Edge createEdge(Node parent, Node current, Transition transition, int evtNum) {
185     //Check if this transition is explored.  If so, just skip everything
186     if (transitions.contains(transition))
187       return null;
188
189     //Create edge
190     Edge e = new Edge(parent, current, transition, evtNum, currUpdates);
191     parent.addOutEdge(e);
192
193     //Mark transition as explored
194     transitions.add(transition);
195     return e;
196   }
197
198   static class Node {
199     Integer id;
200     Vector<Edge> outEdges = new Vector<Edge>();
201     HashMap<IndexObject, HashSet<Update>> lastUpdates = new HashMap<IndexObject,  HashSet<Update>>();
202
203     Node(Integer id) {
204       this.id = id;
205     }
206
207     Integer getId() {
208       return id;
209     }
210
211     Vector<Edge> getOutEdges() {
212       return outEdges;
213     }
214
215     void addOutEdge(Edge e) {
216       outEdges.add(e);
217     }
218
219     HashMap<IndexObject, HashSet<Update>> getLastUpdates() {
220       return lastUpdates;
221     }
222   }
223
224   //Each Edge corresponds to a transition
225   static class Edge {
226     Node source, destination;
227     Transition transition;
228     int eventNumber;
229     ArrayList<Update> updates = new ArrayList<Update>();
230
231     Edge(Node src, Node dst, Transition t, int evtNum, ArrayList<Update> _updates) {
232       this.source = src;
233       this.destination = dst;
234       this.transition = t;
235       this.eventNumber = evtNum;
236       this.updates.addAll(_updates);
237     }
238
239     Node getSrc() {
240       return source;
241     }
242
243     Node getDst() {
244       return destination;
245     }
246
247     Transition getTransition() {
248       return transition;
249     }
250
251     int getEventNumber() { return eventNumber; }
252
253     ArrayList<Update> getUpdates() {
254       return updates;
255     }
256   }
257
258   static class Update {
259     String appName;
260     String deviceId;
261     String attribute;
262     String value;
263     boolean ismanual;
264
265     Update(String _appName, String _deviceId, String _attribute, String _value, boolean _ismanual) {
266       this.appName = _appName;
267       this.deviceId = _deviceId;
268       this.attribute = _attribute;
269       this.value = _value;
270       this.ismanual = _ismanual;
271     }
272
273     boolean isManual() {
274       return ismanual;
275     }
276
277     String getApp() {
278       return appName;
279     }
280
281     String getDeviceId() {
282       return deviceId;
283     }
284
285     String getAttribute() {
286       return attribute;
287     }
288
289     String getValue() {
290       return value;
291     }
292
293     //Gets an index object for indexing updates by just device and attribute
294     IndexObject getIndex() {
295       return new IndexObject(this);
296     }
297
298     public boolean equals(Object o) {
299       if (!(o instanceof Update))
300         return false;
301       Update u=(Update)o;
302       return (getDeviceId().equals(u.getDeviceId()) &&
303               getAttribute().equals(u.getAttribute()) &&
304               getApp().equals(u.getApp()) &&
305               getValue().equals(u.getValue()));
306     }
307
308     public int hashCode() {
309       return (getDeviceId().hashCode() << 3) ^
310               (getAttribute().hashCode() << 2) ^
311               (getApp().hashCode() << 1) ^
312               getValue().hashCode();
313     }
314
315     public String toString() {
316       return "<"+getAttribute()+", "+getValue()+", "+getApp()+", "+ismanual+">";
317     }
318   }
319
320   static class IndexObject {
321     Update u;
322     IndexObject(Update _u) {
323       this.u = _u;
324     }
325
326     public boolean equals(Object o) {
327       if (!(o instanceof IndexObject))
328         return false;
329       IndexObject io=(IndexObject)o;
330       return (u.getDeviceId().equals(io.u.getDeviceId()) &&
331               u.getAttribute().equals(io.u.getAttribute()));
332     }
333     public int hashCode() {
334       return (u.getDeviceId().hashCode() << 1) ^ u.getAttribute().hashCode();
335     }
336   }
337
338   @Override
339   public void stateRestored(Search search) {
340     id = search.getStateId();
341     depth = search.getDepth();
342     operation = "restored";
343     detail = null;
344
345     out.println("The state is restored to state with id: "+id+", depth: "+depth);
346
347     // Update the parent node
348     parentNode = getNode(id);
349   }
350
351   @Override
352   public void searchStarted(Search search) {
353     out.println("----------------------------------- search started");
354   }
355
356   private Node getNode(Integer id) {
357     if (!nodes.containsKey(id))
358       nodes.put(id, new Node(id));
359     return nodes.get(id);
360   }
361
362   public void printGraph() {
363     System.out.println("digraph testgraph {");
364     for(Integer i : nodes.keySet()) {
365       Node n = nodes.get(i);
366       System.out.print("N"+i+"[label=\"");
367
368       for(IndexObject io:n.lastUpdates.keySet()) {
369         for(Update u:n.lastUpdates.get(io)) {
370           System.out.print(u.toString().replace("\"", "\\\"")+", ");
371         }
372       }
373       System.out.println("\"];");
374       for(Edge e:n.outEdges) {
375         System.out.print("N"+e.getSrc().getId()+"->N"+e.getDst().getId()+"[label=\"");
376         for(Update u:e.getUpdates()) {
377           System.out.print(u.toString().replace("\"", "\\\"")+", ");
378         }
379         System.out.println("\"];");
380       }
381     }
382
383     System.out.println("}");
384   }
385
386   @Override
387   public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator<?> currentCG) {
388
389     if (currentCG instanceof IntChoiceFromSet) {
390       IntChoiceFromSet icsCG = (IntChoiceFromSet) currentCG;
391       currentEvent = icsCG.getNextChoice();
392     }
393   }
394
395   @Override
396   public void stateAdvanced(Search search) {
397     String theEnd = null;
398     Transition transition = search.getTransition();
399     id = search.getStateId();
400     depth = search.getDepth();
401     operation = "forward";
402
403     // Add the node to the list of nodes
404     Node currentNode = getNode(id);
405
406     // Create an edge based on the current transition
407     Edge newEdge = createEdge(parentNode, currentNode, transition, currentEvent);
408
409     // Reset the temporary variables and flags
410     currUpdates.clear();
411     manual = false;
412
413     // If we have a new Edge, check for conflicts
414     if (newEdge != null)
415       propagateChange(newEdge);
416
417     if (search.isNewState()) {
418       detail = "new";
419     } else {
420       detail = "visited";
421     }
422
423     if (search.isEndState()) {
424       out.println("This is the last state!");
425       theEnd = "end";
426     }
427
428     out.println("The state is forwarded to state with id: "+id+", depth: "+depth+" which is "+detail+" state: "+"% "+theEnd);
429
430     // Update the parent node
431     parentNode = currentNode;
432   }
433
434   @Override
435   public void stateBacktracked(Search search) {
436     id = search.getStateId();
437     depth = search.getDepth();
438     operation = "backtrack";
439     detail = null;
440
441     out.println("The state is backtracked to state with id: "+id+", depth: "+depth);
442
443     // Update the parent node
444     parentNode = getNode(id);
445   }
446
447   @Override
448   public void searchFinished(Search search) {
449     out.println("----------------------------------- search finished");
450
451     //Comment out the following line to print the explored graph
452     printGraph();
453   }
454
455   private String getValue(ThreadInfo ti, Instruction inst, byte type) {
456     StackFrame frame;
457     int lo, hi;
458
459     frame = ti.getTopFrame();
460
461     if ((inst instanceof JVMLocalVariableInstruction) ||
462             (inst instanceof JVMFieldInstruction))
463     {
464       if (frame.getTopPos() < 0)
465         return(null);
466
467       lo = frame.peek();
468       hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
469
470       // TODO: Fix for integer values (need to dig deeper into the stack frame to find the right value other than 0)
471       // TODO: Seems to be a problem since this is Groovy (not Java)
472       if (type == Types.T_INT || type == Types.T_LONG || type == Types.T_SHORT) {
473         int offset = 0;
474         while (lo == 0) {
475           lo = frame.peek(offset);
476           offset++;
477         }
478       }
479
480       return(decodeValue(type, lo, hi));
481     }
482
483     if (inst instanceof JVMArrayElementInstruction)
484       return(getArrayValue(ti, type));
485
486     return(null);
487   }
488
489   private final static String decodeValue(byte type, int lo, int hi) {
490     switch (type) {
491       case Types.T_ARRAY:   return(null);
492       case Types.T_VOID:    return(null);
493
494       case Types.T_BOOLEAN: return(String.valueOf(Types.intToBoolean(lo)));
495       case Types.T_BYTE:    return(String.valueOf(lo));
496       case Types.T_CHAR:    return(String.valueOf((char) lo));
497       case Types.T_DOUBLE:  return(String.valueOf(Types.intsToDouble(lo, hi)));
498       case Types.T_FLOAT:   return(String.valueOf(Types.intToFloat(lo)));
499       case Types.T_INT:     return(String.valueOf(lo));
500       case Types.T_LONG:    return(String.valueOf(Types.intsToLong(lo, hi)));
501       case Types.T_SHORT:   return(String.valueOf(lo));
502
503       case Types.T_REFERENCE:
504         ElementInfo ei = VM.getVM().getHeap().get(lo);
505         if (ei == null)
506           return(null);
507
508         ClassInfo ci = ei.getClassInfo();
509         if (ci == null)
510           return(null);
511
512         if (ci.getName().equals("java.lang.String"))
513           return('"' + ei.asString() + '"');
514
515         return(ei.toString());
516
517       default:
518         System.err.println("Unknown type: " + type);
519         return(null);
520     }
521   }
522
523   private String getArrayValue(ThreadInfo ti, byte type) {
524     StackFrame frame;
525     int lo, hi;
526
527     frame = ti.getTopFrame();
528     lo    = frame.peek();
529     hi    = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
530
531     return(decodeValue(type, lo, hi));
532   }
533
534   private byte getType(ThreadInfo ti, Instruction inst) {
535     StackFrame frame;
536     FieldInfo fi;
537     String type;
538
539     frame = ti.getTopFrame();
540     if ((frame.getTopPos() >= 0) && (frame.isOperandRef())) {
541       return (Types.T_REFERENCE);
542     }
543
544     type = null;
545
546     if (inst instanceof JVMLocalVariableInstruction) {
547       type = ((JVMLocalVariableInstruction) inst).getLocalVariableType();
548     } else if (inst instanceof JVMFieldInstruction){
549       fi = ((JVMFieldInstruction) inst).getFieldInfo();
550       type = fi.getType();
551     }
552
553     if (inst instanceof JVMArrayElementInstruction) {
554       return (getTypeFromInstruction(inst));
555     }
556
557     if (type == null) {
558       return (Types.T_VOID);
559     }
560
561     return (decodeType(type));
562   }
563
564   private final static byte getTypeFromInstruction(Instruction inst) {
565     if (inst instanceof JVMArrayElementInstruction)
566       return(getTypeFromInstruction((JVMArrayElementInstruction) inst));
567
568     return(Types.T_VOID);
569   }
570
571   private final static byte decodeType(String type) {
572     if (type.charAt(0) == '?'){
573       return(Types.T_REFERENCE);
574     } else {
575       return Types.getBuiltinType(type);
576     }
577   }
578
579   // Find the variable writer
580   // It should be one of the apps listed in the .jpf file
581   private String getWriter(List<StackFrame> sfList, HashSet<String> writerSet) {
582     // Start looking from the top of the stack backward
583     for(int i=sfList.size()-1; i>=0; i--) {
584       MethodInfo mi = sfList.get(i).getMethodInfo();
585       if(!mi.isJPFInternal()) {
586         String method = mi.getStackTraceName();
587         // Check against the writers in the writerSet
588         for(String writer : writerSet) {
589           if (method.contains(writer)) {
590             return writer;
591           }
592         }
593       }
594     }
595
596     return null;
597   }
598
599   private void writeWriterAndValue(String writer, String attribute, String value) {
600     Update u = new Update(writer, "DEVICE", attribute, value, manual);
601     currUpdates.add(u);
602   }
603
604   @Override
605   public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
606     if (timeout > 0) {
607       if (System.currentTimeMillis() - startTime > timeout) {
608         StringBuilder sbTimeOut = new StringBuilder();
609         sbTimeOut.append("Execution timeout: " + (timeout / (60 * 1000)) + " minutes have passed!");
610         Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sbTimeOut.toString());
611         ti.setNextPC(nextIns);
612       }
613     }
614
615     if (conflictFound) {
616
617       StringBuilder sb = new StringBuilder();
618       sb.append(errorMessage);
619       Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", sb.toString());
620       ti.setNextPC(nextIns);
621     } else {
622
623       if (executedInsn instanceof WriteInstruction) {
624         String varId = ((WriteInstruction) executedInsn).getFieldInfo().getFullName();
625         // Check if we have an update to isManualTransaction to update manual field
626         if (varId.contains("isManualTransaction")) {
627           byte type = getType(ti, executedInsn);
628           String value = getValue(ti, executedInsn, type);
629           System.out.println();
630           manual = (value.equals("true"))?true:false;
631         }
632         for (String var : conflictSet) {
633           if (varId.contains(var)) {
634             // Get variable info
635             byte type = getType(ti, executedInsn);
636             String value = getValue(ti, executedInsn, type);
637             String writer = getWriter(ti.getStack(), appSet);
638
639             // Just return if the writer is not one of the listed apps in the .jpf file
640             if (writer == null)
641               return;
642
643             // Update the current updates
644             writeWriterAndValue(writer, var, value);
645           }
646         }
647       }
648     }
649   }
650 }