2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
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
10 * http://www.apache.org/licenses/LICENSE-2.0.
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.
19 package gov.nasa.jpf.listener;
21 import gov.nasa.jpf.Config;
22 import gov.nasa.jpf.JPF;
23 import gov.nasa.jpf.ListenerAdapter;
24 import gov.nasa.jpf.jvm.bytecode.PUTFIELD;
25 import gov.nasa.jpf.report.ConsolePublisher;
26 import gov.nasa.jpf.report.Publisher;
27 import gov.nasa.jpf.util.IntSet;
28 import gov.nasa.jpf.util.SortedArrayIntSet;
29 import gov.nasa.jpf.util.StateExtensionClient;
30 import gov.nasa.jpf.util.StateExtensionListener;
31 import gov.nasa.jpf.util.StringSetMatcher;
32 import gov.nasa.jpf.vm.ClassInfo;
33 import gov.nasa.jpf.vm.ElementInfo;
34 import gov.nasa.jpf.vm.Instruction;
35 import gov.nasa.jpf.vm.StackFrame;
36 import gov.nasa.jpf.vm.VM;
37 import gov.nasa.jpf.vm.ThreadInfo;
38 import gov.nasa.jpf.vm.Types;
39 import gov.nasa.jpf.vm.bytecode.FieldInstruction;
40 import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction;
41 import gov.nasa.jpf.vm.bytecode.InstanceInvokeInstruction;
42 import gov.nasa.jpf.vm.bytecode.InvokeInstruction;
43 import java.io.PrintWriter;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.List;
50 * listener that keeps track of all operations on objects that are specified by
51 * reference value or types
53 public class ObjectTracker extends ListenerAdapter implements StateExtensionClient {
56 // nothing here, just a tag
59 static final Attr ATTR = new Attr(); // we need only one
61 enum OpType { NEW, CALL, PUT, GET, FREE };
63 static class LogRecord {
72 LogRecord (OpType opType, ElementInfo ei, ThreadInfo ti, Instruction insn, LogRecord prev){
78 this.stateId = ti.getVM().getStateId();
81 void printOn (PrintWriter pw){
82 if (prev != null && stateId != prev.stateId){
83 pw.printf("----------------------------------- [%d]\n", prev.stateId + 1);
89 pw.printf("%-4s ", opType.toString().toLowerCase());
94 if (insn instanceof FieldInstruction){
95 FieldInstruction finsn = (FieldInstruction)insn;
97 String fname = finsn.getFieldName();
100 } else if (insn instanceof InvokeInstruction){
101 InvokeInstruction call = (InvokeInstruction)insn;
103 String mthName = call.getInvokedMethodName();
105 pw.print( Types.getDequalifiedMethodSignature(mthName));
113 protected LogRecord log; // needs to be state restored
116 protected StringSetMatcher includeClasses, excludeClasses; // type name patterns
117 protected IntSet trackedRefs;
119 protected boolean logFieldAccess;
120 protected boolean logCalls;
126 public ObjectTracker (Config conf, JPF jpf) {
127 includeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("ot.include"));
128 excludeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("ot.exclude", new String[] { "*" }));
130 trackedRefs = new SortedArrayIntSet();
132 int[] refs = conf.getIntArray("ot.refs");
134 for (int i=0; i<refs.length; i++){
135 trackedRefs.add(refs[i]);
139 logCalls = conf.getBoolean("ot.log_calls", true);
140 logFieldAccess = conf.getBoolean("ot.log_fields", true);
142 registerListener(jpf);
143 jpf.addPublisherExtension(ConsolePublisher.class, this);
146 protected void log (OpType opType, ElementInfo ei, ThreadInfo ti, Instruction insn){
147 log = new LogRecord( opType, ei, ti, insn, log);
151 //--- Listener interface
154 public void classLoaded (VM vm, ClassInfo ci){
155 if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)){
161 public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei) {
162 ClassInfo ci = ei.getClassInfo();
163 int ref = ei.getObjectRef();
165 if (ci.hasAttr(Attr.class) || trackedRefs.contains(ref)){
166 // it's new, we don't need to call getModifiable
167 ei.addObjectAttr(ATTR);
168 log( OpType.NEW, ei, ti, ti.getPC());
173 public void objectReleased (VM vm, ThreadInfo ti, ElementInfo ei) {
174 if (ei.hasObjectAttr(Attr.class)){
175 log( OpType.FREE, ei, ti, ti.getPC());
180 public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
182 if (logCalls && executedInsn instanceof InstanceInvokeInstruction){
183 if (nextInsn != executedInsn){ // otherwise we didn't enter
184 InstanceInvokeInstruction call = (InstanceInvokeInstruction)executedInsn;
186 int ref = call.getCalleeThis(ti);
187 ElementInfo ei = ti.getElementInfo(ref);
189 if (ei.hasObjectAttr(Attr.class)) {
190 log( OpType.CALL, ei, ti, executedInsn);
194 } else if (logFieldAccess && executedInsn instanceof InstanceFieldInstruction){
195 if (nextInsn != executedInsn){ // otherwise we didn't enter
196 InstanceFieldInstruction finsn = (InstanceFieldInstruction) executedInsn;
198 StackFrame frame = ti.getTopFrame();
199 int idx = finsn.getObjectSlot(frame);
200 int ref = frame.getSlot(idx);
201 ElementInfo ei = ti.getElementInfo(ref);
203 if (ei.hasObjectAttr(Attr.class)) {
204 OpType op = (executedInsn instanceof PUTFIELD) ? OpType.PUT : OpType.GET;
205 log( op, ei, ti, executedInsn);
211 //--- state store/restore
214 public Object getStateExtension () {
219 public void restore (Object stateExtension) {
220 log = (LogRecord)stateExtension;
224 public void registerListener (JPF jpf) {
225 StateExtensionListener<Number> sel = new StateExtensionListener(this);
226 jpf.addSearchListener(sel);
233 public void publishPropertyViolation (Publisher publisher) {
234 if (log != null){ // otherwise we don't have anything to report
235 PrintWriter pw = publisher.getOut();
236 publisher.publishTopicStart("ObjectTracker " + publisher.getLastErrorId());
241 protected void printLogOn (PrintWriter pw){
242 // the log can be quite long so we can't use recursion (Java does not optimize tail recursion)
243 List<LogRecord> logRecs = new ArrayList<LogRecord>();
244 for (LogRecord lr = log; lr != null; lr = lr.prev){
248 Collections.reverse(logRecs);
250 for (LogRecord lr : logRecs){