// // Copyright (C) 2007 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.listener; import java.io.PrintWriter; import java.util.HashMap; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPF; import gov.nasa.jpf.PropertyListenerAdapter; import gov.nasa.jpf.jvm.bytecode.PUTFIELD; import gov.nasa.jpf.jvm.bytecode.VirtualInvocation; import gov.nasa.jpf.search.Search; import gov.nasa.jpf.util.StringSetMatcher; import gov.nasa.jpf.vm.AnnotationInfo; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.FieldInfo; import gov.nasa.jpf.vm.InfoObject; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.VM; /** * listener that keeps track of all allocations, method calls, field updates * and deallocations of instances of a set of types * * we don't need a StateExtensionClient/Listener here because we only * keep live, typed object data. If we ever backtrack to a point * where a registered object didn't exist yet, this id can't be used * again (for a relevant object) w/o previous registration. Note this * might cause some discarded objects to stay around, but requires probably * less memory than storing a collection for every state id. */ public class ObjectTracker extends PropertyListenerAdapter { static class Record { ElementInfo ei; ThreadInfo tiCreate; Record (ElementInfo ei, ThreadInfo ti){ this.ei = ei; this.tiCreate = ti; } } PrintWriter out; StringSetMatcher includes, excludes; //--- our various report & check options boolean logLife; boolean logCall; boolean logPut; boolean logShared; boolean checkShared; boolean checkConst; HashMap<Integer,Record> trackedObjects; //--- property data class Violation { Record rec; String msg; ThreadInfo tiUse; InfoObject use; Instruction insn; Violation (Record rec, ThreadInfo tiUse, InfoObject use, Instruction insn) { this.rec = rec; this.tiUse = tiUse; this.use = use; this.insn = insn; } public void setSharedErrorMessage () { StringBuilder sb = new StringBuilder("@NonShared object violation: "); sb.append(rec.ei); sb.append("\n\tcreated in thread: "); sb.append(rec.tiCreate.getName()); sb.append("\n\tused in thread: "); sb.append(tiUse.getName()); sb.append("\n\tmethod: "); sb.append(insn.getSourceLocation()); msg = sb.toString(); } public void setConstErrorMessage () { MethodInfo mi = insn.getMethodInfo(); StringBuilder sb = new StringBuilder("@Const method violation: "); sb.append(mi.getFullName()); sb.append("\n\tfield: "); sb.append(((FieldInfo)use).getFullName()); sb.append("\n\tmethod: "); sb.append(insn.getSourceLocation()); msg = sb.toString(); } } Violation violation; //--- internal stuff public ObjectTracker (Config conf, JPF jpf) { out = new PrintWriter(System.out, true); includes = StringSetMatcher.getNonEmpty(conf.getStringArray("ot.include")); excludes = StringSetMatcher.getNonEmpty(conf.getStringArray("ot.exclude")); logLife = conf.getBoolean("ot.log_life", true); logCall = conf.getBoolean("ot.log_call", true); logPut = conf.getBoolean("ot.log_put", true); logShared = conf.getBoolean("ot.log_shared", true); checkShared = conf.getBoolean("ot.check_shared",false); checkConst = conf.getBoolean("ot.check_const",false); trackedObjects = new HashMap<Integer,Record>(); } boolean isTrackedClass (String clsName){ return StringSetMatcher.isMatch(clsName, includes, excludes); } boolean isTrackedObject (int ref){ return (trackedObjects.containsKey(ref)); } Record getRecord (int ref){ return trackedObjects.get(ref); } void log (ThreadInfo ti, String fmt, Object... args){ out.print(ti.getId()); out.print(": "); out.printf(fmt, args); out.println(); } boolean checkShared (Record rec, ThreadInfo ti, InfoObject use, Instruction insn){ if (checkShared){ AnnotationInfo ai = rec.ei.getClassInfo().getAnnotation("gov.nasa.jpf.NonShared"); if (ai != null && ti != rec.tiCreate){ violation = new Violation(rec, ti, use, insn); violation.setSharedErrorMessage(); ti.breakTransition("checkShared"); return false; } } return true; } boolean checkConst (Record rec, ThreadInfo ti, FieldInfo fi, Instruction insn){ if (checkConst){ AnnotationInfo ai = insn.getMethodInfo().getAnnotation("gov.nasa.jpf.Const"); if (ai != null){ violation = new Violation(rec, ti, fi, insn); violation.setConstErrorMessage(); ti.breakTransition("checkConst"); return false; } } return true; } //--- Property interface @Override public boolean check (Search search, VM vm) { if (violation != null){ return false; } return true; } @Override public void reset () { violation = null; } public String getErrorMessage() { if (violation != null){ return violation.msg; } else { return null; } } //--- VMListener interface @Override public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei) { ClassInfo ci = ei.getClassInfo(); if (isTrackedClass(ci.getName())){ trackedObjects.put(ei.getObjectRef(), new Record(ei, ti)); if (logLife){ log(ti, "created %1$s", ei); } } } @Override public void objectReleased (VM vm, ThreadInfo ti, ElementInfo ei) { int ref = ei.getObjectRef(); if (isTrackedObject(ref)){ trackedObjects.remove(ref); if (logLife){ log(ti, "released %1$s", ei); } } } @Override public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){ if (executedInsn instanceof VirtualInvocation){ if (nextInsn != executedInsn){ // otherwise we didn't enter VirtualInvocation call = (VirtualInvocation)executedInsn; int ref = call.getCalleeThis(ti); Record rec = getRecord(ref); if (rec != null){ MethodInfo mi = call.getInvokedMethod(FeatureExprFactory.True(), ti, ref); if (logCall){ log(ti, "invoke %1$s.%2$s", rec.ei, mi.getUniqueName()); } if (!checkShared(rec, ti, mi, executedInsn)){ return; } } } } else if (executedInsn instanceof PUTFIELD){ PUTFIELD storeInsn = (PUTFIELD) executedInsn; int ref = storeInsn.getLastThis(); Record rec = getRecord(ref); if (rec != null){ FieldInfo fi = storeInsn.getFieldInfo(null); if (logPut){ log(ti, "put %1$s.%2$s = <%3$d>", rec.ei, fi.getName(), storeInsn.getLastValue()); } if (!checkShared(rec, ti, fi, executedInsn)){ return; } if (!checkConst(rec,ti,fi,executedInsn)){ return; } } } } }