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.
18 package gov.nasa.jpf.listener;
20 import gov.nasa.jpf.Config;
21 import gov.nasa.jpf.ListenerAdapter;
22 import gov.nasa.jpf.jvm.bytecode.JVMArrayElementInstruction;
23 import gov.nasa.jpf.jvm.bytecode.ArrayLoadInstruction;
24 import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
25 import gov.nasa.jpf.jvm.bytecode.JVMLocalVariableInstruction;
26 import gov.nasa.jpf.vm.bytecode.StoreInstruction;
27 import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
28 import gov.nasa.jpf.util.StringSetMatcher;
29 import gov.nasa.jpf.vm.ClassInfo;
30 import gov.nasa.jpf.vm.ElementInfo;
31 import gov.nasa.jpf.vm.FieldInfo;
32 import gov.nasa.jpf.vm.Instruction;
33 import gov.nasa.jpf.vm.MJIEnv;
34 import gov.nasa.jpf.vm.VM;
35 import gov.nasa.jpf.vm.MethodInfo;
36 import gov.nasa.jpf.vm.StackFrame;
37 import gov.nasa.jpf.vm.Step;
38 import gov.nasa.jpf.vm.ThreadInfo;
39 import gov.nasa.jpf.vm.Types;
41 import java.util.HashMap;
44 * Simple listener tool to record the values of variables as they are accessed.
45 * Records the information in Step.setComment() so that when the trace is
46 * written the values can be written too.
48 public class VarRecorder extends ListenerAdapter {
50 private final HashMap<ThreadInfo, String> pendingComment = new HashMap<ThreadInfo, String>();
52 private final StringSetMatcher includes;
53 private final StringSetMatcher excludes;
54 private final boolean recordFields;
55 private final boolean recordLocals;
56 private final boolean recordArrays;
58 private ClassInfo lastClass;
59 private boolean recordClass;
61 public VarRecorder(Config config) {
62 includes = StringSetMatcher.getNonEmpty(config.getStringArray("var_recorder.include"));
63 excludes = StringSetMatcher.getNonEmpty(config.getStringArray("var_recorder.exclude"));
64 recordFields = config.getBoolean("var_recorder.fields", true);
65 recordLocals = config.getBoolean("var_recorder.locals", true);
66 recordArrays = config.getBoolean("var_recorder.arrays", true);
70 public void executeInstruction(VM vm, ThreadInfo ti, Instruction insnToExecute) {
74 if (!canRecord(vm, insnToExecute))
77 if (!(insnToExecute instanceof StoreInstruction))
78 if (!(insnToExecute instanceof ArrayLoadInstruction))
81 type = getType(ti, insnToExecute);
82 name = getName(ti, insnToExecute, type);
84 if (insnToExecute instanceof ArrayLoadInstruction) {
85 setComment(vm, ti, name, "", "", true);
86 saveVariableType(ti, type);
88 value = getValue(ti, insnToExecute, type);
89 setComment(vm, ti, name, " <- ", value, true);
94 public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
98 if (!canRecord(vm, executedInsn))
101 if (executedInsn instanceof StoreInstruction) {
102 name = pendingComment.remove(ti);
103 setComment(vm, ti, name, "", "", false);
107 type = getType(ti, executedInsn);
108 value = getValue(ti, executedInsn, type);
110 if (executedInsn instanceof ArrayLoadInstruction)
111 name = pendingComment.remove(ti);
113 name = getName(ti, executedInsn, type);
115 if (isArrayReference(vm, ti))
116 saveVariableName(ti, name);
118 saveVariableType(ti, type);
120 setComment(vm, ti, name, " -> ", value, false);
123 private final void setComment(VM vm, ThreadInfo ti, String name, String operator, String value, boolean pending) {
133 comment = name + operator + value;
136 pendingComment.put(ti, comment);
138 step = vm.getLastStep();
139 step.setComment(name + operator + value);
143 private final boolean canRecord(VM vm, Instruction inst) {
147 if (vm.getLastStep() == null)
150 if (!(inst instanceof LocalVariableInstruction))
151 if (!(inst instanceof JVMArrayElementInstruction))
154 mi = inst.getMethodInfo();
158 ci = mi.getClassInfo();
162 if (lastClass != ci) {
164 recordClass = StringSetMatcher.isMatch(ci.getName(), includes, excludes);
170 // <2do> general purpose listeners should not use anonymous attribute types such as String
172 private final void saveVariableName(ThreadInfo ti, String name) {
173 StackFrame frame = ti.getModifiableTopFrame();
174 frame.addOperandAttr(name);
177 private final void saveVariableType(ThreadInfo ti, byte type) {
181 frame = ti.getModifiableTopFrame();
182 if (frame.getTopPos() < 0) {
186 str = encodeType(type);
187 frame.addOperandAttr(str);
190 private final boolean isArrayReference(VM vm, ThreadInfo ti) {
191 StackFrame frame = ti.getTopFrame();
193 if (frame.getTopPos() < 0) {
197 if (!frame.isOperandRef()) {
201 int objRef = frame.peek();
202 if (objRef == MJIEnv.NULL) {
206 ElementInfo ei = ti.getElementInfo(objRef);
211 return(ei.isArray());
214 private byte getType(ThreadInfo ti, Instruction inst) {
219 frame = ti.getTopFrame();
220 if ((frame.getTopPos() >= 0) && (frame.isOperandRef())) {
221 return (Types.T_REFERENCE);
226 if (((recordLocals) && (inst instanceof JVMLocalVariableInstruction))
227 || ((recordFields) && (inst instanceof JVMFieldInstruction))) {
228 if (inst instanceof JVMLocalVariableInstruction) {
229 type = ((JVMLocalVariableInstruction) inst).getLocalVariableType();
231 fi = ((JVMFieldInstruction) inst).getFieldInfo();
236 if ((recordArrays) && (inst instanceof JVMArrayElementInstruction)) {
237 return (getTypeFromInstruction(inst));
241 return (Types.T_VOID);
244 return (decodeType(type));
247 private final static byte getTypeFromInstruction(Instruction inst) {
248 if (inst instanceof JVMArrayElementInstruction)
249 return(getTypeFromInstruction((JVMArrayElementInstruction) inst));
251 return(Types.T_VOID);
254 private final static byte getTypeFromInstruction(JVMArrayElementInstruction inst) {
257 name = inst.getClass().getName();
258 name = name.substring(name.lastIndexOf('.') + 1);
260 switch (name.charAt(0)) {
261 case 'A': return(Types.T_REFERENCE);
262 case 'B': return(Types.T_BYTE); // Could be a boolean but it is better to assume a byte.
263 case 'C': return(Types.T_CHAR);
264 case 'F': return(Types.T_FLOAT);
265 case 'I': return(Types.T_INT);
266 case 'S': return(Types.T_SHORT);
267 case 'D': return(Types.T_DOUBLE);
268 case 'L': return(Types.T_LONG);
271 return(Types.T_VOID);
274 private final static String encodeType(byte type) {
276 case Types.T_BYTE: return("B");
277 case Types.T_CHAR: return("C");
278 case Types.T_DOUBLE: return("D");
279 case Types.T_FLOAT: return("F");
280 case Types.T_INT: return("I");
281 case Types.T_LONG: return("J");
282 case Types.T_REFERENCE: return("L");
283 case Types.T_SHORT: return("S");
284 case Types.T_VOID: return("V");
285 case Types.T_BOOLEAN: return("Z");
286 case Types.T_ARRAY: return("[");
292 private final static byte decodeType(String type) {
293 if (type.charAt(0) == '?'){
294 return(Types.T_REFERENCE);
296 return Types.getBuiltinType(type);
300 private String getName(ThreadInfo ti, Instruction inst, byte type) {
305 if (((recordLocals) && (inst instanceof JVMLocalVariableInstruction)) ||
306 ((recordFields) && (inst instanceof JVMFieldInstruction))) {
307 name = ((LocalVariableInstruction) inst).getVariableId();
308 name = name.substring(name.lastIndexOf('.') + 1);
313 if ((recordArrays) && (inst instanceof JVMArrayElementInstruction)) {
314 store = inst instanceof StoreInstruction;
315 name = getArrayName(ti, type, store);
316 index = getArrayIndex(ti, type, store);
317 return(name + '[' + index + ']');
323 private String getValue(ThreadInfo ti, Instruction inst, byte type) {
327 frame = ti.getTopFrame();
329 if (((recordLocals) && (inst instanceof JVMLocalVariableInstruction)) ||
330 ((recordFields) && (inst instanceof JVMFieldInstruction)))
332 if (frame.getTopPos() < 0)
336 hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
338 return(decodeValue(type, lo, hi));
341 if ((recordArrays) && (inst instanceof JVMArrayElementInstruction))
342 return(getArrayValue(ti, type));
347 private String getArrayName(ThreadInfo ti, byte type, boolean store) {
351 offset = calcOffset(type, store) + 1;
352 // <2do> String is really not a good attribute type to retrieve!
353 StackFrame frame = ti.getTopFrame();
354 attr = frame.getOperandAttr( offset, String.class);
363 private int getArrayIndex(ThreadInfo ti, byte type, boolean store) {
366 offset = calcOffset(type, store);
368 return(ti.getTopFrame().peek(offset));
371 private final static int calcOffset(byte type, boolean store) {
375 return(Types.getTypeSize(type));
378 private String getArrayValue(ThreadInfo ti, byte type) {
382 frame = ti.getTopFrame();
384 hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
386 return(decodeValue(type, lo, hi));
389 private final static String decodeValue(byte type, int lo, int hi) {
391 case Types.T_ARRAY: return(null);
392 case Types.T_VOID: return(null);
394 case Types.T_BOOLEAN: return(String.valueOf(Types.intToBoolean(lo)));
395 case Types.T_BYTE: return(String.valueOf(lo));
396 case Types.T_CHAR: return(String.valueOf((char) lo));
397 case Types.T_DOUBLE: return(String.valueOf(Types.intsToDouble(lo, hi)));
398 case Types.T_FLOAT: return(String.valueOf(Types.intToFloat(lo)));
399 case Types.T_INT: return(String.valueOf(lo));
400 case Types.T_LONG: return(String.valueOf(Types.intsToLong(lo, hi)));
401 case Types.T_SHORT: return(String.valueOf(lo));
403 case Types.T_REFERENCE:
404 ElementInfo ei = VM.getVM().getHeap().get(lo);
408 ClassInfo ci = ei.getClassInfo();
412 if (ci.getName().equals("java.lang.String"))
413 return('"' + ei.asString() + '"');
415 return(ei.toString());
418 System.err.println("Unknown type: " + type);