// // Copyright (C) 2011 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.vm.serialize; import java.util.LinkedList; import java.util.List; import cmu.conditional.Conditional; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPF; import gov.nasa.jpf.ListenerAdapter; import gov.nasa.jpf.util.FieldSpec; import gov.nasa.jpf.util.FinalBitSet; import gov.nasa.jpf.util.JPFLogger; import gov.nasa.jpf.util.StringSetMatcher; import gov.nasa.jpf.vm.ArrayFields; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ClassLoaderInfo; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.FieldInfo; import gov.nasa.jpf.vm.Fields; import gov.nasa.jpf.vm.MJIEnv; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.ReferenceArrayFields; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.StaticElementInfo; import gov.nasa.jpf.vm.Statics; import gov.nasa.jpf.vm.VM; /** * a serializer that uses Abstraction objects stored as field attributes to * obtain the values to hash. */ public class DynamicAbstractionSerializer extends FilteringSerializer { static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.vm.serialize.DynamicAbstractionSerializer"); static class FieldAbstraction { FieldSpec fspec; Abstraction abstraction; FieldAbstraction(FieldSpec f, Abstraction a) { fspec = f; abstraction = a; } } public class Attributor extends ListenerAdapter { @Override public void classLoaded(VM vm, ClassInfo loadedClass) { if (!loadedClass.isArray() && !loadedClass.isPrimitive()) { if (!fieldAbstractions.isEmpty()) { for (FieldInfo fi : loadedClass.getDeclaredInstanceFields()) { for (FieldAbstraction fabs : fieldAbstractions) { if (fabs.fspec.matches(fi)) { logger.info("setting instance field abstraction ", fabs.abstraction.getClass().getName(), " for field ", fi.getFullName()); fi.addAttr(fabs.abstraction); } } } for (FieldInfo fi : loadedClass.getDeclaredStaticFields()) { for (FieldAbstraction fabs : fieldAbstractions) { if (fabs.fspec.matches(fi)) { logger.info("setting static field abstraction ", fabs.abstraction.getClass().getName(), " for field ", fi.getFullName()); fi.addAttr(fabs.abstraction); } } } } } } } protected StringSetMatcher includeClasses = null; // means all protected StringSetMatcher excludeClasses = null; // means none protected StringSetMatcher includeMethods = null; protected StringSetMatcher excludeMethods = null; List<FieldAbstraction> fieldAbstractions; protected boolean processAllObjects; protected boolean declaredFieldsOnly; public DynamicAbstractionSerializer(Config conf) { processAllObjects = conf.getBoolean("das.all_objects", true); declaredFieldsOnly = conf.getBoolean("das.declared_fields", false); includeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("das.classes.include")); excludeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("das.classes.exclude")); includeMethods = StringSetMatcher.getNonEmpty(conf.getStringArray("das.methods.include")); excludeMethods = StringSetMatcher.getNonEmpty(conf.getStringArray("das.methods.exclude")); fieldAbstractions = getFieldAbstractions(conf); } protected List<FieldAbstraction> getFieldAbstractions(Config conf){ List<FieldAbstraction> list = null; String[] fids = conf.getCompactTrimmedStringArray("das.fields"); for (String id : fids) { String keyPrefix = "das." + id; String fs = conf.getString(keyPrefix + ".field"); if (fs != null) { FieldSpec fspec = FieldSpec.createFieldSpec(fs); if (fspec != null) { String aKey = keyPrefix + ".abstraction"; Abstraction abstraction = conf.getInstance(aKey, Abstraction.class); logger.info("found field abstraction for ", fspec, " = ", abstraction.getClass().getName()); if (list == null){ list = new LinkedList<FieldAbstraction>(); } list.add(new FieldAbstraction(fspec, abstraction)); } } else { logger.warning("no field spec for id: " + id); } } return list; } @Override public void attach (VM vm){ super.attach(vm); if (fieldAbstractions != null){ Attributor attributor = new Attributor(); vm.addListener(attributor); } } // note that we don't add the reference value here public void processReference(int objref) { if (objref != MJIEnv.NULL) { ElementInfo ei = heap.get(objref); if (!ei.isMarked()) { // only add objects once ei.setMarked(); refQueue.add(ei); } } // we DON'T add the reference value to the buffer here } protected void processField(Fields fields, int[] slotValues, FieldInfo fi, FinalBitSet filtered) { int off = fi.getStorageOffset(); if (!filtered.get(off)) { Abstraction a = fi.getAttr(Abstraction.class); if (a != null) { if (fi.is1SlotField()) { if (fi.isReference()) { Conditional<Integer> ref = fields.getReferenceValue(off); buf.add(a.getAbstractObject(ref.getValue())); if (a.traverseObject(ref.getValue())) { processReference(ref.getValue()); } } else if (fi.isFloatField()) { buf.add(a.getAbstractValue(fields.getFloatValue(off).getValue())); } else { buf.add(a.getAbstractValue(fields.getIntValue(off).getValue())); } } else { // double or long if (fi.isLongField()) { buf.add(a.getAbstractValue(fields.getLongValue(off).getValue())); } else { // got to be double buf.add(a.getAbstractValue(fields.getDoubleValue(off).getValue())); } } } else { // no abstraction, fall back to concrete values if (fi.is1SlotField()) { if (fi.isReference()) { int ref = slotValues[off]; buf.add(ref); processReference(ref); } else { buf.add(slotValues[off]); } } else { // double or long buf.add(slotValues[off]); buf.add(slotValues[off + 1]); } } } } // non-ignored class protected void processArrayFields(ArrayFields fields) { buf.add(fields.arrayLength()); if (fields.isReferenceArray()) { Conditional<Integer>[] values = fields.asReferenceArray(); for (int i = 0; i < values.length; i++) { processReference(values[i].getValue()); buf.add(values[i]); } } else { fields.appendTo(buf); } } // for ignored class, to get all live objects protected void processNamedInstanceReferenceFields(ClassInfo ci, Fields fields) { FinalBitSet filtered = getInstanceFilterMask(ci); FinalBitSet refs = getInstanceRefMask(ci); int[] slotValues = fields.asFieldSlots(); // for non-attributed fields for (int i = 0; i < slotValues.length; i++) { if (!filtered.get(i)) { if (refs.get(i)) { processReference(slotValues[i]); } } } } // for ignored class, to get all live objects protected void processNamedStaticReferenceFields(ClassInfo ci, Fields fields) { FinalBitSet filtered = getStaticFilterMask(ci); FinalBitSet refs = getStaticRefMask(ci); int[] slotValues = fields.asFieldSlots(); // for non-attributed fields for (int i = 0; i < slotValues.length; i++) { if (!filtered.get(i)) { if (refs.get(i)) { processReference(slotValues[i]); } } } } // for ignored class, to get all live objects protected void processReferenceArray(ReferenceArrayFields fields) { Conditional<Integer>[] slotValues = fields.asReferenceArray(); for (int i = 0; i < slotValues.length; i++) { processReference(slotValues[i].getValue()); } } // non-ignored class protected void processNamedFields(ClassInfo ci, Fields fields) { FinalBitSet filtered = getInstanceFilterMask(ci); int nFields = ci.getNumberOfInstanceFields(); int[] slotValues = fields.asFieldSlots(); // for non-attributed fields for (int i = 0; i < nFields; i++) { FieldInfo fi = ci.getInstanceField(i); if (declaredFieldsOnly && fi.getClassInfo() != ci) { continue; } processField(fields, slotValues, fi, filtered); } } // <2do> this should also allow abstraction of whole objects, so that // we can hash combinations/relations of field values public void process (ElementInfo ei) { Fields fields = ei.getFields(); ClassInfo ci = ei.getClassInfo(); if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)) { buf.add(ci.getUniqueId()); if (fields instanceof ArrayFields) { // not filtered processArrayFields((ArrayFields) fields); } else { // named fields, potentially filtered & abstracted via attributes processNamedFields(ci, fields); } } else { // ignored class // we check for live non-ignored objects along all stack frames, so we should do the same for all objects if (fields instanceof ArrayFields) { if (fields instanceof ReferenceArrayFields) { processReferenceArray((ReferenceArrayFields) fields); } } else { processNamedInstanceReferenceFields(ci, fields); } } } @Override protected void serializeFrame (StackFrame frame) { MethodInfo mi = frame.getMethodInfo(); if (StringSetMatcher.isMatch(mi.getFullName(), includeMethods, excludeMethods)){ // <2do> should do frame abstraction here super.serializeFrame(frame); } else { if (processAllObjects) { frame.visitReferenceSlots(this); } } } protected void serializeClass (StaticElementInfo sei) { ClassInfo ci = sei.getClassInfo(); Fields fields = sei.getFields(); if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)) { buf.add(sei.getStatus()); FinalBitSet filtered = getStaticFilterMask(ci); int[] slotValues = fields.asFieldSlots(); for (FieldInfo fi : ci.getDeclaredStaticFields()) { processField(fields, slotValues, fi, filtered); } } else { // ignored class, but still process references to extract live objects processNamedStaticReferenceFields(ci, fields); } } protected void serializeClassLoaders(){ // we don't care about the number of classloaders for (ClassLoaderInfo cl : ks.classLoaders) { if(cl.isAlive()) { serializeStatics( cl.getStatics()); } } } protected void serializeStatics(Statics statics){ // we don't care about the number of statics entries for (StaticElementInfo sei : statics.liveStatics()) { serializeClass(sei); } } }