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.*;
23 import gov.nasa.jpf.vm.*;
24 import gov.nasa.jpf.vm.bytecode.WriteInstruction;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
30 // TODO: Fix for Groovy's model-checking
31 // TODO: This is a listener created to detect device conflicts and global variable conflicts
33 * Simple listener tool to track conflicts between 2 apps.
34 * A conflict is defined as one app trying to change the state of a variable
35 * into its opposite value after being set by the other app,
36 * e.g., app1 attempts to change variable A to false after A has been set by app2 to true earlier.
38 public class VariableConflictTracker extends ListenerAdapter {
40 private final HashMap<String, VarChange> executionMap = new HashMap<>();
41 private final HashSet<String> conflictSet = new HashSet<>();
42 private final HashSet<String> appSet = new HashSet<>();
44 public VariableConflictTracker(Config config) {
45 String[] conflictVars = config.getStringArray("variables");
46 for(String var : conflictVars) {
49 String[] apps = config.getStringArray("apps");
50 for(String var : apps) {
56 public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
59 if (executedInsn instanceof WriteInstruction){
61 varId = ((WriteInstruction) executedInsn).getFieldInfo().getFullName();
62 for(String var : conflictSet) {
64 if (varId.contains(var)) {
66 byte type = getType(ti, executedInsn);
67 String value = getValue(ti, executedInsn, type);
68 String writer = getWriter(ti.getStack());
69 // Just return if the writer is not one of the listed apps in the .jpf file
73 if (executionMap.containsKey(var)) {
74 // Subsequent accesses to the variable
75 VarChange current = executionMap.get(var);
76 if (current.writer != writer) {
77 // Conflict is declared when:
78 // 1) Current writer != previous writer, e.g., App1 vs. App2
79 // 2) Current value != previous value, e.g., "locked" vs. "unlocked"
80 if (!current.value.equals(value)) {
82 String msg = "Conflict between apps " + current.writer + " and " +
83 writer + " for variable " + var;
84 Instruction nextIns = ti.createAndThrowException("java.lang.RuntimeException", msg);
85 ti.setNextPC(nextIns);
88 // No conflict is declared if this variable is accessed subsequently by the same writer
89 current.writer = writer;
90 current.value = value;
93 // First access to the variable
94 VarChange change = new VarChange(writer, value);
95 executionMap.put(var, change);
106 VarChange(String writer, String value) {
107 this.writer = writer;
112 // Find the variable writer
113 // It should be one of the apps listed in the .jpf file
114 private String getWriter(List<StackFrame> sfList) {
116 for(StackFrame sf : sfList) {
117 MethodInfo mi = sf.getMethodInfo();
118 if(!mi.isJPFInternal()) {
119 String method = mi.getStackTraceName();
120 // Check against the apps in the appSet
121 for(String app : appSet) {
122 if (method.contains(app)) {
132 private byte getType(ThreadInfo ti, Instruction inst) {
137 frame = ti.getTopFrame();
138 if ((frame.getTopPos() >= 0) && (frame.isOperandRef())) {
139 return (Types.T_REFERENCE);
144 if (inst instanceof JVMLocalVariableInstruction) {
145 type = ((JVMLocalVariableInstruction) inst).getLocalVariableType();
146 } else if (inst instanceof JVMFieldInstruction){
147 fi = ((JVMFieldInstruction) inst).getFieldInfo();
151 if (inst instanceof JVMArrayElementInstruction) {
152 return (getTypeFromInstruction(inst));
156 return (Types.T_VOID);
159 return (decodeType(type));
162 private final static byte getTypeFromInstruction(Instruction inst) {
163 if (inst instanceof JVMArrayElementInstruction)
164 return(getTypeFromInstruction((JVMArrayElementInstruction) inst));
166 return(Types.T_VOID);
169 private final static byte getTypeFromInstruction(JVMArrayElementInstruction inst) {
172 name = inst.getClass().getName();
173 name = name.substring(name.lastIndexOf('.') + 1);
175 switch (name.charAt(0)) {
176 case 'A': return(Types.T_REFERENCE);
177 case 'B': return(Types.T_BYTE); // Could be a boolean but it is better to assume a byte.
178 case 'C': return(Types.T_CHAR);
179 case 'F': return(Types.T_FLOAT);
180 case 'I': return(Types.T_INT);
181 case 'S': return(Types.T_SHORT);
182 case 'D': return(Types.T_DOUBLE);
183 case 'L': return(Types.T_LONG);
186 return(Types.T_VOID);
189 private final static String encodeType(byte type) {
191 case Types.T_BYTE: return("B");
192 case Types.T_CHAR: return("C");
193 case Types.T_DOUBLE: return("D");
194 case Types.T_FLOAT: return("F");
195 case Types.T_INT: return("I");
196 case Types.T_LONG: return("J");
197 case Types.T_REFERENCE: return("L");
198 case Types.T_SHORT: return("S");
199 case Types.T_VOID: return("V");
200 case Types.T_BOOLEAN: return("Z");
201 case Types.T_ARRAY: return("[");
207 private final static byte decodeType(String type) {
208 if (type.charAt(0) == '?'){
209 return(Types.T_REFERENCE);
211 return Types.getBuiltinType(type);
215 private String getValue(ThreadInfo ti, Instruction inst, byte type) {
219 frame = ti.getTopFrame();
221 if ((inst instanceof JVMLocalVariableInstruction) ||
222 (inst instanceof JVMFieldInstruction))
224 if (frame.getTopPos() < 0)
228 hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
230 return(decodeValue(type, lo, hi));
236 private final static String decodeValue(byte type, int lo, int hi) {
238 case Types.T_ARRAY: return(null);
239 case Types.T_VOID: return(null);
241 case Types.T_BOOLEAN: return(String.valueOf(Types.intToBoolean(lo)));
242 case Types.T_BYTE: return(String.valueOf(lo));
243 case Types.T_CHAR: return(String.valueOf((char) lo));
244 case Types.T_DOUBLE: return(String.valueOf(Types.intsToDouble(lo, hi)));
245 case Types.T_FLOAT: return(String.valueOf(Types.intToFloat(lo)));
246 case Types.T_INT: return(String.valueOf(lo));
247 case Types.T_LONG: return(String.valueOf(Types.intsToLong(lo, hi)));
248 case Types.T_SHORT: return(String.valueOf(lo));
250 case Types.T_REFERENCE:
251 ElementInfo ei = VM.getVM().getHeap().get(lo);
255 ClassInfo ci = ei.getClassInfo();
259 if (ci.getName().equals("java.lang.String"))
260 return('"' + ei.asString() + '"');
262 return(ei.toString());
265 System.err.println("Unknown type: " + type);