diff src/main/gov/nasa/jpf/listener/OverlappingMethodAnalyzer.java @ 0:61d41facf527

initial v8 import (history reset)
author Peter Mehlitz <Peter.C.Mehlitz@nasa.gov>
date Fri, 23 Jan 2015 10:14:01 -0800
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/gov/nasa/jpf/listener/OverlappingMethodAnalyzer.java	Fri Jan 23 10:14:01 2015 -0800
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * this is a specialized MethodAnalyzer that looks for overlapping method
+ * calls on the same object from different threads.
+ * 
+ * <2do> transition reporting does not work yet
+ */
+public class OverlappingMethodAnalyzer extends MethodAnalyzer {
+
+  public OverlappingMethodAnalyzer (Config config, JPF jpf){
+    super(config,jpf);
+  }
+
+  MethodOp getReturnOp (MethodOp op, boolean withinSameThread){
+    MethodInfo mi = op.mi;
+    int stateId = op.stateId;
+    int stackDepth = op.stackDepth;
+    ElementInfo ei = op.ei;
+    ThreadInfo ti = op.ti;
+
+    for (MethodOp o = op.p; o != null; o = o.p){
+      if (withinSameThread && o.ti != ti){
+        break;
+      }
+
+      if ((o.mi == mi) && (o.ti == ti) && (o.stackDepth == stackDepth) && (o.ei == ei)){
+        return o;
+      }
+    }
+
+    return null;
+  }
+
+  // check if there is an open exec from another thread for the same ElementInfo
+  boolean isOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
+    ThreadInfo ti = op.ti;
+    ElementInfo ei = op.ei;
+
+    for (Map.Entry<ThreadInfo, Deque<MethodOp>> e : openExecs.entrySet()) {
+      if (e.getKey() != ti) {
+        Deque<MethodOp> s = e.getValue();
+        for (Iterator<MethodOp> it = s.descendingIterator(); it.hasNext();) {
+          MethodOp o = it.next();
+          if (o.ei == ei) {
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+  // clean up (if necessary) - both RETURNS and exceptions
+  void cleanUpOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
+    ThreadInfo ti = op.ti;
+    int stackDepth = op.stackDepth;
+
+    Deque<MethodOp> stack = openExecs.get(ti);
+    if (stack != null && !stack.isEmpty()) {
+      for (MethodOp o = stack.peek(); o != null && o.stackDepth >= stackDepth; o = stack.peek()) {
+        stack.pop();
+      }
+    }
+  }
+
+  void addOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
+    ThreadInfo ti = op.ti;
+    Deque<MethodOp> stack = openExecs.get(ti);
+
+    if (stack == null){
+      stack = new ArrayDeque<MethodOp>();
+      stack.push(op);
+      openExecs.put(ti, stack);
+
+    } else {
+      stack.push(op);
+    }
+  }
+
+  @Override
+  void printOn (PrintWriter pw) {
+    MethodOp start = firstOp;
+
+    HashMap<ThreadInfo,Deque<MethodOp>> openExecs = new HashMap<ThreadInfo,Deque<MethodOp>>();
+
+    int lastStateId  = -1;
+    int lastTid = start.ti.getId();
+
+    for (MethodOp op = start; op != null; op = op.p) {
+
+      cleanUpOpenExec(openExecs, op);
+
+      if (op.isMethodEnter()) {  // EXEC or CALL_EXEC
+        MethodOp retOp = getReturnOp(op, true);
+        if (retOp != null) { // completed, skip
+          if (!isOpenExec(openExecs, op)) {
+            op = retOp;
+            lastStateId = op.stateId;
+            continue;
+          }
+        } else { // this is an open method exec, record it
+          addOpenExec(openExecs, op);
+        }
+      }
+
+      op = consolidateOp(op);
+      
+      if (showTransition) {
+        if (op.stateId != lastStateId) {
+          if (lastStateId >= 0){
+            pw.print("------------------------------------------ #");
+            pw.println(lastStateId);
+          }
+        }
+        lastStateId = op.stateId;
+        
+      } else {
+        int tid = op.ti.getId();
+        if (tid != lastTid) {
+          lastTid = tid;
+          pw.println("------------------------------------------");
+        }
+      }
+      
+      op.printOn(pw, this);
+      pw.println();
+    }
+  }
+
+  MethodOp consolidateOp (MethodOp op){
+    for (MethodOp o = op.p; o != null; o = o.p){
+      if (showTransition && (o.stateId != op.stateId)){
+        break;
+      }
+      if (o.isSameMethod(op)){
+        switch (o.type) {
+          case RETURN:
+            switch (op.type){
+              case CALL_EXECUTE:
+                op = o.clone(OpType.CALL_EXEC_RETURN); break;
+              case EXECUTE:
+                op = o.clone(OpType.EXEC_RETURN); break;
+            }
+            break;
+          case EXEC_RETURN:
+            switch (op.type){
+              case CALL:
+                op = o.clone(OpType.CALL_EXEC_RETURN); break;
+            }
+            break;
+          case CALL_EXECUTE:  // simple loop
+            switch (op.type){
+              case CALL_EXEC_RETURN:
+                op = o;
+            }
+            break;
+        }
+      } else {
+        break;
+      }
+    }
+
+    return op;
+  }
+}