// // Copyright (C) 2010 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.Iterator; import java.util.function.BiConsumer; import de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.MJIEnv; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.ThreadList; /** * a FilteringSerializer that performs on-the-fly heap canonicalization to * achieve heap symmetry. It does so by storing the order in which * objects are referenced, not the reference values themselves. * * Use this serializer if the Heap implementation does not provide * sufficient symmetry, i.e. reference values depend on the order of * thread scheduling. * * Ad hoc heap symmetry is hard to achieve in the heap because of static initialization. * Each time a thread loads a class all the static init (at least the class object and * its fields) are associated with this thread, hence thread reference * values depend on which classes are already loaded by other threads. Associating * all allocations from inside of clinits to one address range doesn't help either * because then this range will experience scheduling dependent orders. A hybrid * approach in which only this segment is canonicalized might work, but it is * questionable if the overhead is worth the effort. */ public class CFSerializer extends FilteringSerializer { // we flip this on every serialization, which helps us to avoid passes // over the serialized objects to reset their sids. This works by resetting // the sid to 0 upon backtrack, and counting either upwards from 1 or downwards // from -1, but store the absolute value in the serialization stream boolean positiveSid; int sidCount; @Override protected void initReferenceQueue() { super.initReferenceQueue(); if (positiveSid){ positiveSid = false; sidCount = -1; } else { positiveSid = true; sidCount = 1; } } // might be overriden in subclasses to conditionally queue objects protected void queueReference(ElementInfo ei){ refQueue.add(ei); } @Override public void processReference(int objref) { if (objref == MJIEnv.NULL) { buf.add(MJIEnv.NULL); } else { ElementInfo ei = heap.get(objref); int sid = ei.getSid(); if (positiveSid){ // count sid upwards from 1 if (sid <= 0){ // not seen before in this serialization run sid = sidCount++; ei.setSid(sid); queueReference(ei); } } else { // count sid downwards from -1 if (sid >= 0){ // not seen before in this serialization run sid = sidCount--; ei.setSid(sid); queueReference(ei); } sid = -sid; } // note that we always add the absolute sid value buf.add(sid); } } @Override protected void serializeStackFrames() { ThreadList tl = ks.getThreadList(); for (Iterator<ThreadInfo> it = tl.canonicalLiveIterator(); it.hasNext(); ) { serializeStackFrames(it.next()); } } @Override protected void serializeFrame(final StackFrame frame){ buf.add(frame.getMethodInfo().getGlobalId()); // TODO Jens make conditional Instruction pc = null; int min = Integer.MAX_VALUE; for (Instruction i : frame.getPC().toList()) { if (i != null && i.getPosition() < min) { min = i.getPosition(); pc = i; } } final Instruction finalPC = pc; frame.getPC().mapf(FeatureExprFactory.True(), new BiConsumer<FeatureExpr, Instruction>() { @Override public void accept(FeatureExpr f, Instruction x) { if (x == finalPC) { buf.add( finalPC != null ? finalPC.getInstructionIndex() : -1); int len = frame.nLocals;//frame.getTopPos(f) + 1;TODO buf.add(len); // unfortunately we can't do this as a block operation because that // would use concrete reference values as hash data, i.e. break heap symmetry int[] slots = frame.stack.getSlots(f); for (int i = 0; i < len; i++) { if (frame.stack.isRefLocal(f, i)) { processReference(slots[i]); } else { buf.add(slots[i]); } } } } }); } @Override protected void processReferenceQueue() { refQueue.process(this); } @Override protected int getSerializedReferenceValue (ElementInfo ei){ return Math.abs(ei.getSid()); } }