Mercurial > hg > Members > kono > jpf-core
view src/main/gov/nasa/jpf/listener/ObjectTracker.java @ 0:61d41facf527
initial v8 import (history reset)
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Fri, 23 Jan 2015 10:14:01 -0800 |
parents | |
children |
line wrap: on
line source
/* * Copyright (C) 2014, United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. * * The Java Pathfinder core (jpf-core) platform is licensed under the * Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gov.nasa.jpf.listener; import gov.nasa.jpf.Config; import gov.nasa.jpf.JPF; import gov.nasa.jpf.ListenerAdapter; import gov.nasa.jpf.jvm.bytecode.PUTFIELD; import gov.nasa.jpf.report.ConsolePublisher; import gov.nasa.jpf.report.Publisher; import gov.nasa.jpf.util.IntSet; import gov.nasa.jpf.util.SortedArrayIntSet; import gov.nasa.jpf.util.StateExtensionClient; import gov.nasa.jpf.util.StateExtensionListener; import gov.nasa.jpf.util.StringSetMatcher; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.VM; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.Types; import gov.nasa.jpf.vm.bytecode.FieldInstruction; import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction; import gov.nasa.jpf.vm.bytecode.InstanceInvokeInstruction; import gov.nasa.jpf.vm.bytecode.InvokeInstruction; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * listener that keeps track of all operations on objects that are specified by * reference value or types */ public class ObjectTracker extends ListenerAdapter implements StateExtensionClient { static class Attr { // nothing here, just a tag } static final Attr ATTR = new Attr(); // we need only one enum OpType { NEW, CALL, PUT, GET, FREE }; static class LogRecord { ElementInfo ei; ThreadInfo ti; Instruction insn; OpType opType; int stateId; LogRecord prev; LogRecord (OpType opType, ElementInfo ei, ThreadInfo ti, Instruction insn, LogRecord prev){ this.opType = opType; this.ei = ei; this.ti = ti; this.insn = insn; this.prev = prev; this.stateId = ti.getVM().getStateId(); } void printOn (PrintWriter pw){ if (prev != null && stateId != prev.stateId){ pw.printf("----------------------------------- [%d]\n", prev.stateId + 1); } pw.print(ti.getId()); pw.print(": "); pw.printf("%-4s ", opType.toString().toLowerCase()); pw.print(ei); pw.print('.'); if (insn != null){ if (insn instanceof FieldInstruction){ FieldInstruction finsn = (FieldInstruction)insn; String fname = finsn.getFieldName(); pw.print(fname); } else if (insn instanceof InvokeInstruction){ InvokeInstruction call = (InvokeInstruction)insn; String mthName = call.getInvokedMethodName(); pw.print( Types.getDequalifiedMethodSignature(mthName)); } } pw.println(); } } protected LogRecord log; // needs to be state restored //--- log options protected StringSetMatcher includeClasses, excludeClasses; // type name patterns protected IntSet trackedRefs; protected boolean logFieldAccess; protected boolean logCalls; //--- internal stuff public ObjectTracker (Config conf, JPF jpf) { includeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("ot.include")); excludeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("ot.exclude", new String[] { "*" })); trackedRefs = new SortedArrayIntSet(); int[] refs = conf.getIntArray("ot.refs"); if (refs != null){ for (int i=0; i<refs.length; i++){ trackedRefs.add(refs[i]); } } logCalls = conf.getBoolean("ot.log_calls", true); logFieldAccess = conf.getBoolean("ot.log_fields", true); registerListener(jpf); jpf.addPublisherExtension(ConsolePublisher.class, this); } protected void log (OpType opType, ElementInfo ei, ThreadInfo ti, Instruction insn){ log = new LogRecord( opType, ei, ti, insn, log); } //--- Listener interface @Override public void classLoaded (VM vm, ClassInfo ci){ if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)){ ci.addAttr(ATTR); } } @Override public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei) { ClassInfo ci = ei.getClassInfo(); int ref = ei.getObjectRef(); if (ci.hasAttr(Attr.class) || trackedRefs.contains(ref)){ // it's new, we don't need to call getModifiable ei.addObjectAttr(ATTR); log( OpType.NEW, ei, ti, ti.getPC()); } } @Override public void objectReleased (VM vm, ThreadInfo ti, ElementInfo ei) { if (ei.hasObjectAttr(Attr.class)){ log( OpType.FREE, ei, ti, ti.getPC()); } } @Override public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){ if (logCalls && executedInsn instanceof InstanceInvokeInstruction){ if (nextInsn != executedInsn){ // otherwise we didn't enter InstanceInvokeInstruction call = (InstanceInvokeInstruction)executedInsn; int ref = call.getCalleeThis(ti); ElementInfo ei = ti.getElementInfo(ref); if (ei.hasObjectAttr(Attr.class)) { log( OpType.CALL, ei, ti, executedInsn); } } } else if (logFieldAccess && executedInsn instanceof InstanceFieldInstruction){ if (nextInsn != executedInsn){ // otherwise we didn't enter InstanceFieldInstruction finsn = (InstanceFieldInstruction) executedInsn; StackFrame frame = ti.getTopFrame(); int idx = finsn.getObjectSlot(frame); int ref = frame.getSlot(idx); ElementInfo ei = ti.getElementInfo(ref); if (ei.hasObjectAttr(Attr.class)) { OpType op = (executedInsn instanceof PUTFIELD) ? OpType.PUT : OpType.GET; log( op, ei, ti, executedInsn); } } } } //--- state store/restore @Override public Object getStateExtension () { return log; } @Override public void restore (Object stateExtension) { log = (LogRecord)stateExtension; } @Override public void registerListener (JPF jpf) { StateExtensionListener<Number> sel = new StateExtensionListener(this); jpf.addSearchListener(sel); } //--- reporting @Override public void publishPropertyViolation (Publisher publisher) { if (log != null){ // otherwise we don't have anything to report PrintWriter pw = publisher.getOut(); publisher.publishTopicStart("ObjectTracker " + publisher.getLastErrorId()); printLogOn(pw); } } protected void printLogOn (PrintWriter pw){ // the log can be quite long so we can't use recursion (Java does not optimize tail recursion) List<LogRecord> logRecs = new ArrayList<LogRecord>(); for (LogRecord lr = log; lr != null; lr = lr.prev){ logRecs.add(lr); } Collections.reverse(logRecs); for (LogRecord lr : logRecs){ lr.printOn(pw); } } }