SmartThings specific support to reduce state space
[jpf-core.git] / src / main / gov / nasa / jpf / vm / serialize / DynamicAbstractionSerializer.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.serialize;
19
20 import gov.nasa.jpf.Config;
21 import gov.nasa.jpf.JPF;
22 import gov.nasa.jpf.ListenerAdapter;
23 import gov.nasa.jpf.util.FieldSpec;
24 import gov.nasa.jpf.util.FinalBitSet;
25 import gov.nasa.jpf.util.JPFLogger;
26 import gov.nasa.jpf.util.StringSetMatcher;
27 import gov.nasa.jpf.vm.ArrayFields;
28 import gov.nasa.jpf.vm.ClassInfo;
29 import gov.nasa.jpf.vm.ClassLoaderInfo;
30 import gov.nasa.jpf.vm.ElementInfo;
31 import gov.nasa.jpf.vm.FieldInfo;
32 import gov.nasa.jpf.vm.Fields;
33 import gov.nasa.jpf.vm.MJIEnv;
34 import gov.nasa.jpf.vm.VM;
35 import gov.nasa.jpf.vm.MethodInfo;
36 import gov.nasa.jpf.vm.ReferenceArrayFields;
37 import gov.nasa.jpf.vm.StackFrame;
38 import gov.nasa.jpf.vm.StaticElementInfo;
39 import gov.nasa.jpf.vm.Statics;
40
41 import java.util.LinkedList;
42 import java.util.List;
43
44 /**
45  * a serializer that uses Abstraction objects stored as field attributes to
46  * obtain the values to hash. 
47  */
48 public class DynamicAbstractionSerializer extends FilteringSerializer {
49
50   static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.vm.serialize.DynamicAbstractionSerializer");
51
52   static class FieldAbstraction {
53     FieldSpec fspec;
54     Abstraction abstraction;
55
56     FieldAbstraction(FieldSpec f, Abstraction a) {
57       fspec = f;
58       abstraction = a;
59     }
60   }
61
62   public class Attributor extends ListenerAdapter {
63
64     @Override
65     public void classLoaded(VM vm, ClassInfo loadedClass) {
66
67       if (!loadedClass.isArray() && !loadedClass.isPrimitive()) {
68
69         if (!fieldAbstractions.isEmpty()) {
70           for (FieldInfo fi : loadedClass.getDeclaredInstanceFields()) {
71             for (FieldAbstraction fabs : fieldAbstractions) {
72               if (fabs.fspec.matches(fi)) {
73                 logger.info("setting instance field abstraction ", fabs.abstraction.getClass().getName(),
74                         " for field ", fi.getFullName());
75                 fi.addAttr(fabs.abstraction);
76               }
77             }
78           }
79
80           for (FieldInfo fi : loadedClass.getDeclaredStaticFields()) {
81             for (FieldAbstraction fabs : fieldAbstractions) {
82               if (fabs.fspec.matches(fi)) {
83                 logger.info("setting static field abstraction ", fabs.abstraction.getClass().getName(),
84                         " for field ", fi.getFullName());
85                 fi.addAttr(fabs.abstraction);
86               }
87             }
88           }
89         }
90       }
91     }
92   }
93   
94   protected StringSetMatcher includeClasses = null; //  means all
95   protected StringSetMatcher excludeClasses = null; //  means none
96   protected StringSetMatcher includeMethods = null;
97   protected StringSetMatcher excludeMethods = null;
98
99   List<FieldAbstraction> fieldAbstractions;
100
101   protected boolean processAllObjects;
102   protected boolean declaredFieldsOnly;
103
104   
105   public DynamicAbstractionSerializer(Config conf) {
106     processAllObjects = conf.getBoolean("das.all_objects", true);
107     declaredFieldsOnly = conf.getBoolean("das.declared_fields", false);
108
109     includeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("das.classes.include"));
110     excludeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("das.classes.exclude"));
111
112     includeMethods = StringSetMatcher.getNonEmpty(conf.getStringArray("das.methods.include"));
113     excludeMethods = StringSetMatcher.getNonEmpty(conf.getStringArray("das.methods.exclude"));
114
115     fieldAbstractions = getFieldAbstractions(conf);
116   }
117
118   
119   protected List<FieldAbstraction> getFieldAbstractions(Config conf){
120     List<FieldAbstraction> list = null;
121     
122     String[] fids = conf.getCompactTrimmedStringArray("das.fields");
123     for (String id : fids) {
124       String keyPrefix = "das." + id;
125       String fs = conf.getString(keyPrefix + ".field");
126       if (fs != null) {
127         FieldSpec fspec = FieldSpec.createFieldSpec(fs);
128         if (fspec != null) {
129           String aKey = keyPrefix + ".abstraction";
130           Abstraction abstraction = conf.getInstance(aKey, Abstraction.class);
131
132           logger.info("found field abstraction for ", fspec, " = ", abstraction.getClass().getName());
133           
134           if (list == null){
135             list = new LinkedList<FieldAbstraction>();
136           }
137           
138           list.add(new FieldAbstraction(fspec, abstraction));
139         }
140       } else {
141         logger.warning("no field spec for id: " + id);
142       }
143     }
144     
145     return list;
146   }
147   
148   @Override
149   public void attach (VM vm){
150     super.attach(vm);
151     
152     if (fieldAbstractions != null){
153       Attributor attributor = new Attributor();
154       vm.addListener(attributor);
155     }
156   }
157   
158   
159   // note that we don't add the reference value here
160   @Override
161   public void processReference(int objref) {
162     if (objref != MJIEnv.NULL) {
163       ElementInfo ei = heap.get(objref);
164       if (!ei.isMarked()) { // only add objects once
165         ei.setMarked();
166         refQueue.add(ei);
167       }
168     }
169     
170     // we DON'T add the reference value to the buffer here
171   }
172
173   protected void processField(Fields fields, int[] slotValues, FieldInfo fi, FinalBitSet filtered) {
174     int off = fi.getStorageOffset();
175     if (!filtered.get(off)) {
176       Abstraction a = fi.getAttr(Abstraction.class);
177       if (a != null) {
178         if (fi.is1SlotField()) {
179           if (fi.isReference()) {
180             int ref = fields.getReferenceValue(off);
181             buf.add(a.getAbstractObject(ref));
182
183             if (a.traverseObject(ref)) {
184               processReference(ref);
185             }
186
187           } else if (fi.isFloatField()) {
188             buf.add(a.getAbstractValue(fields.getFloatValue(off)));
189           } else {
190             buf.add(a.getAbstractValue(fields.getIntValue(off)));
191           }
192         } else { // double or long
193           if (fi.isLongField()) {
194             buf.add(a.getAbstractValue(fields.getLongValue(off)));
195           } else { // got to be double
196             buf.add(a.getAbstractValue(fields.getDoubleValue(off)));
197           }
198         }
199
200       } else { // no abstraction, fall back to concrete values
201         if (fi.is1SlotField()) {
202           if (fi.isReference()) {
203             int ref = slotValues[off];
204             buf.add(ref);
205             processReference(ref);
206
207           } else {
208             buf.add(slotValues[off]);
209           }
210
211         } else { // double or long
212           buf.add(slotValues[off]);
213           buf.add(slotValues[off + 1]);
214         }
215       }
216     }
217   }
218
219   // non-ignored class
220   @Override
221   protected void processArrayFields(ArrayFields fields) {
222     buf.add(fields.arrayLength());
223
224     if (fields.isReferenceArray()) {
225       int[] values = fields.asReferenceArray();
226       for (int i = 0; i < values.length; i++) {
227         processReference(values[i]);
228         buf.add(values[i]);
229       }
230     } else {
231       fields.appendTo(buf);
232     }
233   }
234
235   // for ignored class, to get all live objects
236   protected void processNamedInstanceReferenceFields(ClassInfo ci, Fields fields) {
237     FinalBitSet filtered = getInstanceFilterMask(ci);
238     FinalBitSet refs = getInstanceRefMask(ci);
239     int[] slotValues = fields.asFieldSlots(); // for non-attributed fields
240
241     for (int i = 0; i < slotValues.length; i++) {
242       if (!filtered.get(i)) {
243         if (refs.get(i)) {
244           processReference(slotValues[i]);
245         }
246       }
247     }
248   }
249
250   // for ignored class, to get all live objects
251   protected void processNamedStaticReferenceFields(ClassInfo ci, Fields fields) {
252     FinalBitSet filtered = getStaticFilterMask(ci);
253     FinalBitSet refs = getStaticRefMask(ci);
254     int[] slotValues = fields.asFieldSlots(); // for non-attributed fields
255
256     for (int i = 0; i < slotValues.length; i++) {
257       if (!filtered.get(i)) {
258         if (refs.get(i)) {
259           processReference(slotValues[i]);
260         }
261       }
262     }
263   }
264
265   // for ignored class, to get all live objects
266   protected void processReferenceArray(ReferenceArrayFields fields) {
267     int[] slotValues = fields.asReferenceArray();
268     for (int i = 0; i < slotValues.length; i++) {
269       processReference(slotValues[i]);
270     }
271   }
272
273   // non-ignored class
274   @Override
275   protected void processNamedFields(ClassInfo ci, Fields fields) {
276     FinalBitSet filtered = getInstanceFilterMask(ci);
277     int nFields = ci.getNumberOfInstanceFields();
278     int[] slotValues = fields.asFieldSlots(); // for non-attributed fields
279
280     for (int i = 0; i < nFields; i++) {
281       FieldInfo fi = ci.getInstanceField(i);
282
283       if (declaredFieldsOnly && fi.getClassInfo() != ci) {
284         continue;
285       }
286
287       processField(fields, slotValues, fi, filtered);
288     }
289   }
290
291   // <2do> this should also allow abstraction of whole objects, so that
292   // we can hash combinations/relations of field values
293   @Override
294   public void process (ElementInfo ei) {
295     Fields fields = ei.getFields();
296     ClassInfo ci = ei.getClassInfo();
297
298     if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)) {
299       buf.add(ci.getUniqueId());
300
301       if (fields instanceof ArrayFields) { // not filtered
302         processArrayFields((ArrayFields) fields);
303       } else { // named fields, potentially filtered & abstracted via attributes
304         processNamedFields(ci, fields);
305       }
306
307     } else { // ignored class
308       // we check for live non-ignored objects along all stack frames, so we should do the same for all objects
309       if (fields instanceof ArrayFields) {
310         if (fields instanceof ReferenceArrayFields) {
311           processReferenceArray((ReferenceArrayFields) fields);
312         }
313       } else {
314         processNamedInstanceReferenceFields(ci, fields);
315       }
316     }
317   }
318
319   @Override
320   protected void serializeFrame (StackFrame frame) {
321     MethodInfo mi = frame.getMethodInfo();
322     
323     if (StringSetMatcher.isMatch(mi.getFullName(), includeMethods, excludeMethods)){
324       // <2do> should do frame abstraction here
325       super.serializeFrame(frame);
326
327     } else {
328       if (processAllObjects) {
329         frame.visitReferenceSlots(this);
330       }
331     }
332   }
333
334   @Override
335   protected void serializeClass (StaticElementInfo sei) {
336     ClassInfo ci = sei.getClassInfo();
337     Fields fields = sei.getFields();
338
339     if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)) {
340       buf.add(sei.getStatus());
341
342       FinalBitSet filtered = getStaticFilterMask(ci);
343       int[] slotValues = fields.asFieldSlots();
344
345       for (FieldInfo fi : ci.getDeclaredStaticFields()) {
346         processField(fields, slotValues, fi, filtered);
347       }
348
349     } else {
350       // ignored class, but still process references to extract live objects
351       processNamedStaticReferenceFields(ci, fields);
352     }
353   }
354
355   @Override
356   protected void serializeClassLoaders(){
357     // we don't care about the number of classloaders
358     
359     for (ClassLoaderInfo cl : ks.classLoaders) {
360       if(cl.isAlive()) {
361         serializeStatics( cl.getStatics());
362       }
363     }
364   }
365
366   @Override
367   protected void serializeStatics(Statics statics){
368     // we don't care about the number of statics entries
369
370     for (StaticElementInfo sei : statics.liveStatics()) {
371       serializeClass(sei);
372     }
373   }
374 }