changeset 26:4eb191cbb68c

moved NonShared + checker listener over from old jpf-aprop. Note this is still a JVM specific listener but it could be VM agnostic
author Peter Mehlitz <Peter.C.Mehlitz@nasa.gov>
date Mon, 04 May 2015 22:10:33 -0700
parents 3517702bd768
children 8aded593a50f
files src/annotations/gov/nasa/jpf/annotation/NonShared.java src/main/gov/nasa/jpf/listener/NonSharedChecker.java
diffstat 2 files changed, 191 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/annotations/gov/nasa/jpf/annotation/NonShared.java	Mon May 04 22:10:33 2015 -0700
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015, 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.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * instances of @NonShared classes are not allowed to be referenced from
+ * more than one thread (but do allow hand-over)
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NonShared {
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/gov/nasa/jpf/listener/NonSharedChecker.java	Mon May 04 22:10:33 2015 -0700
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015, 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.ListenerAdapter;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.jvm.bytecode.ALOAD;
+import gov.nasa.jpf.jvm.bytecode.ARETURN;
+import gov.nasa.jpf.jvm.bytecode.ASTORE;
+import gov.nasa.jpf.jvm.bytecode.IFNONNULL;
+import gov.nasa.jpf.jvm.bytecode.IFNULL;
+import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.jvm.bytecode.MONITORENTER;
+import gov.nasa.jpf.jvm.bytecode.VirtualInvocation;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ *
+ */
+public class NonSharedChecker extends ListenerAdapter {
+
+  boolean throwOnCycle = false;
+
+  static class Access {
+    ThreadInfo ti;
+    Access prev;
+
+    Access(ThreadInfo ti, Access prev){
+      this.ti = ti;
+      this.prev = prev;
+    }
+
+    // <2do> get a better hashCode for state hashing
+    public int hashCode() {
+      int h = ti.getId();
+      for (Access p = prev; p!= null; p = p.prev){
+        h = 31*h + p.ti.getId();
+      }
+      return h;
+    }
+    // but we don't care for equals()
+  }
+
+  public NonSharedChecker (Config conf){
+    throwOnCycle = conf.getBoolean("nonshared.throw_on_cycle");
+  }
+
+  boolean isNonShared(ElementInfo ei){
+    ClassInfo ci = ei.getClassInfo();
+    return (ci.getAnnotation("gov.nasa.jpf.annotation.NonShared") != null);
+  }
+
+  boolean checkLiveCycles (ElementInfo ei, ThreadInfo ti, Access ac){
+    if (ti == ac.ti){
+      return true; // Ok, fine - no need to record
+
+    } else {
+      boolean foundLiveOne = false;
+      for (Access a = ac; a != null; a = a.prev){
+        ThreadInfo t = a.ti;
+        if (t == ti){ // cycle detected
+          return !foundLiveOne;
+        }
+        foundLiveOne = (foundLiveOne || t.isAlive()); // <2do> maybe we should check for non-blocked threads
+      }
+
+      // new one, record it in the access history of the object
+      ac = new Access(ti, ac);
+      ei = ei.getModifiableInstance();
+      ei.setObjectAttr(ac);
+    }
+
+    return true;
+  }
+
+
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insn){
+
+    ElementInfo ei = null;
+
+    if (ti.isFirstStepInsn()) {
+      return;
+    }
+
+    if (insn instanceof InstanceFieldInstruction){
+      ei = ((InstanceFieldInstruction)insn).peekElementInfo(ti);
+    } else if (insn instanceof VirtualInvocation){
+      ei = ((VirtualInvocation)insn).getThisElementInfo(ti);  // Outch - that's expensive
+    } else if (insn instanceof MONITORENTER ||
+               insn instanceof ASTORE ||
+               insn instanceof ARETURN ||
+               insn instanceof IFNONNULL ||
+               insn instanceof IFNULL) {
+      StackFrame frame = ti.getTopFrame();
+      int ref = frame.peek();
+      if (ref != -1){
+        ei = ti.getElementInfo(ref);
+      }
+    } else if (insn instanceof ALOAD){
+      StackFrame frame = ti.getTopFrame();
+      int ref = frame.getLocalVariable(((ALOAD)insn).getLocalVariableIndex());
+      if (ref != -1){
+        ei = ti.getElementInfo(ref);
+      }
+    }
+
+    if (ei != null){
+      Access ac = ei.getObjectAttr(Access.class);
+      if (ac != null){
+        if (!checkLiveCycles(ei,ti,ac)){
+          StringBuilder sb = new StringBuilder("NonShared object: ");
+          sb.append( ei);
+          sb.append(" accessed in live thread cycle: ");
+          sb.append( ti.getName());
+          for (Access a = ac; a != null; a = a.prev ){
+            sb.append(',');
+            sb.append(a.ti.getName());
+          }
+          String msg = sb.toString();
+
+          if (throwOnCycle){
+            ti.setNextPC( ti.createAndThrowException("java.lang.AssertionError", msg));
+          } else {
+            System.err.println("WARNING: " + msg);
+            System.err.println("\tat " + insn.getSourceLocation());
+          }
+          return;
+        }
+      }
+    }
+  }
+
+  @Override
+  public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei){
+    if (isNonShared(ei)){
+      ei.setObjectAttrNoClone(new Access(ti, null));
+    }
+  }
+}
+