Fixes null captured parameters
[jpf-core.git] / src / main / gov / nasa / jpf / vm / HashedAllocationContext.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.vm;
19
20 // see mixinJPFStack() comments
21 import sun.misc.SharedSecrets;
22 import sun.misc.JavaLangAccess;
23
24 import gov.nasa.jpf.Config;
25 import static gov.nasa.jpf.util.OATHash.*;
26
27 /**
28  * an AllocationContext that uses a hash value for comparison. This is
29  * lossy - heap implementations using this class have to check/handle
30  * collisions.
31  * 
32  * However, given that we have very good hash data (search global object
33  * references), the probability of collisions is low enough that heap
34  * implementations might simply report this as a problem requiring a
35  * non-lossy AllocationContext.
36  * 
37  * Ideally, we would like to hash the host VM thread context too (esp.
38  * for system allocations), but host VM stack traces are expensive, and it is
39  * arguable if would be too strict (e.g. when using a dedicated allocator
40  * method called from alternative branches of the caller) 
41  * 
42  * note - this is a HashMap key type which has to obey the hashCode/equals contract
43  */
44 public class HashedAllocationContext implements AllocationContext {
45     
46   static final Throwable throwable = new Throwable(); // to avoid frequent allocations
47     
48   static int mixinSUTStack (int h, ThreadInfo ti) {
49     h = hashMixin( h, ti.getId());
50
51     // we don't want to mixin the stack slots (locals and operands) because this would
52     // cause state leaks (different hash) if there are changed slot values that do not
53     // relate to the allocation
54     
55     for (StackFrame frame = ti.getTopFrame(); frame != null; frame = frame.getPrevious() ) {
56       if (!(frame instanceof DirectCallStackFrame)) {
57         Instruction insn = frame.getPC();
58         
59         //h = hashMixin(h, insn.hashCode()); // this is the Instruction object system hash - not reproducible between runs
60         
61         h = hashMixin( h, insn.getMethodInfo().getGlobalId()); // the method
62         h = hashMixin( h, insn.getInstructionIndex()); // the position within the method code
63         h = hashMixin( h, insn.getByteCode()); // the instruction type
64       }
65     }
66     
67     return h;
68   }
69   
70   /*
71    * this is an optimization to cut down on host VM StackTrace acquisition, since we just need one
72    * element.
73    * 
74    * NOTE: this is more fragile than Throwable.getStackTrace() and String.equals() since it assumes
75    * availability of the sun.misc.JavaLangAccess SharedSecret and invariance of classname strings.
76    * 
77    * The robust version would be
78    *   ..
79    *   throwable.fillInStackTrace();
80    *   StackTraceElement[] ste = throwable.getStackTrace();
81    *   StackTraceElement e = ste[4];
82    *   if (e.getClassName().equals("gov.nasa.jpf.vm.MJIEnv") && e.getMethodName().startsWith("new")){ ..
83    */ 
84   
85    static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
86    static final String ENV_CLSNAME = MJIEnv.class.getName();
87   
88   // <2do> this method is problematic - we should not assume a fixed stack position
89   // but we can't just mixin the whole stack since this would cause different class object
90   // allocation contexts (registerClass can happen from lots of locations).
91   // At the other end of the spectrum, MJIEnv.newXX() is not differentiating enough since
92   // those are convenience methods used from a gazillion of places that might share
93    // the same SUT state
94   static int mixinJPFStack (int h) {
95     throwable.fillInStackTrace();
96     
97     // we know the callstack is at least 4 levels deep:
98     //   0: mixinJPFStack
99     //   1: getXAllocationContext
100     //   2: heap.getXAllocationContext
101     //   3: heap.newObject/newArray/newString
102     //   4: <allocating method>
103     //   ...
104
105     // note that it is not advisable to mixin more than the immediate newX() caller since
106     // this would create state leaks for allocations that are triggered by SUT threads and
107     // have different native paths (e.g. Class object creation caused by different SUT thread context)
108     
109     StackTraceElement e = JLA.getStackTraceElement(throwable, 4); // see note below regarding fixed call depth fragility
110     // <2do> this sucks - MJIEnv.newObject/newArray/newString are used from a gazillion of places that might not differ in SUT state
111     if (e.getClassName() == ENV_CLSNAME && e.getMethodName().startsWith("new")){
112       // there is not much use to loop, since we don't have a good end condition
113       e = JLA.getStackTraceElement(throwable, 5);
114     }
115           
116     // NOTE - this is fragile since it is implementation dependent and differs
117     // between JPF runs
118     // the names are interned string from the class object
119     // h = hashMixin( h, System.identityHashCode(e.getClassName()));
120     // h = hashMixin( h, System.identityHashCode(e.getMethodName()));
121
122     // this should be reproducible, but the string hash is bad
123     h = hashMixin(h, e.getClassName().hashCode());
124     h = hashMixin(h, e.getMethodName().hashCode());
125     h = hashMixin(h, e.getLineNumber());
126     
127     return h;
128   }
129   
130   /*
131    * !! NOTE: these always have to be at a fixed call distance of the respective Heap.newX() call:
132    * 
133    *  ConcreteHeap.newX()
134    *    ConcreteHeap.getXAllocationContext()
135    *      ConcreteAllocationContext.getXAllocationContext()
136    *      
137    * that means the allocation site is at stack depth 4. This is not nice, but there is no
138    * good heuristic we could use instead, other than assuming there is a newObject/newArray/newString
139    * call on the stack
140    */
141   
142   /**
143    * this one is for allocations that should depend on the SUT thread context (such as all
144    * explicit NEW executions)
145    */
146   public static AllocationContext getSUTAllocationContext (ClassInfo ci, ThreadInfo ti) {
147     int h = 0;
148     
149     //--- the type that gets allocated
150     h = hashMixin(h, ci.getUniqueId()); // ClassInfo instances can change upon backtrack
151     
152     //--- the SUT execution context (allocating ThreadInfo and its stack)
153     h = mixinSUTStack( h, ti);
154     
155     //--- the JPF execution context (from where in the JPF code the allocation happens)
156     h = mixinJPFStack( h);
157     
158     h = hashFinalize(h);
159     HashedAllocationContext ctx = new HashedAllocationContext(h);
160
161     return ctx;
162   }
163   
164   /**
165    * this one is for allocations that should NOT depend on the SUT thread context (such as
166    * automatic allocation of java.lang.Class objects by the VM)
167    * 
168    * @param anchor a value that can be used to provide a context that is heap graph specific (such as
169    * a classloader or class object reference)
170    */
171   public static AllocationContext getSystemAllocationContext (ClassInfo ci, ThreadInfo ti, int anchor) {
172     int h = 0;
173     
174     h = hashMixin(h, ci.getUniqueId()); // ClassInfo instances can change upon backtrack
175     
176     // in lieu of the SUT stack, add some magic salt and the anchor
177     h = hashMixin(h, 0x14040118);
178     h = hashMixin(h, anchor);
179     
180     //--- the JPF execution context (from where in the JPF code the allocation happens)
181     h = mixinJPFStack( h);
182     
183     h = hashFinalize(h);
184     HashedAllocationContext ctx = new HashedAllocationContext(h);
185
186     return ctx;
187   }
188
189   public static boolean init (Config conf) {
190     //pool = new SparseObjVector<HashedAllocationContext>();
191     return true;
192   }
193   
194   //--- instance data
195   
196   // rolled up hash value for all context components
197   protected final int id;
198
199   
200   //--- instance methods
201   
202   protected HashedAllocationContext (int id) {
203     this.id = id;
204   }
205   
206   @Override
207   public boolean equals (Object o) {
208     if (o instanceof HashedAllocationContext) {
209       HashedAllocationContext other = (HashedAllocationContext)o;
210       return id == other.id; 
211     }
212     
213     return false;
214   }
215   
216   /**
217    * @pre: must be the same for two objects that result in equals() returning true
218    */
219   @Override
220   public int hashCode() {
221     return id;
222   }
223   
224   // for automatic field init allocations
225   @Override
226   public AllocationContext extend (ClassInfo ci, int anchor) {
227     //int h = hash( id, anchor, ci.hashCode());
228     int h = hashMixin(id, anchor);
229     h = hashMixin(h, ci.getUniqueId());
230     h = hashFinalize(h);
231     
232     return new HashedAllocationContext(h);
233   }
234 }