1 19 20 package edu.umd.cs.findbugs.detect; 21 22 23 import edu.umd.cs.findbugs.*; 24 25 import java.util.*; 26 import org.apache.bcel.classfile.*; 27 28 public class LockedFields extends BytecodeScanningDetector { 29 private static final boolean DEBUG = SystemProperties.getBoolean("lockedfields.debug"); 30 31 Set<FieldAnnotation> volatileOrFinalFields = new HashSet<FieldAnnotation>(); 32 Set<FieldAnnotation> fieldsWritten = new HashSet<FieldAnnotation>(); 33 Set<FieldAnnotation> fieldsRead = new HashSet<FieldAnnotation>(); 34 Set<FieldAnnotation> localLocks = new HashSet<FieldAnnotation>(); 35 Set<FieldAnnotation> publicFields = new HashSet<FieldAnnotation>(); 36 Set<FieldAnnotation> writtenOutsideOfConstructor = new HashSet<FieldAnnotation>(); 37 boolean synchronizedMethod; 38 boolean publicMethod; 39 boolean protectedMethod; 40 boolean inConstructor; 42 Map<FieldAnnotation, int[]> stats = new TreeMap<FieldAnnotation, int[]>(); 43 int state; 44 boolean thisOnTopOfStack; 45 boolean doubleThisOnTopOfStack; 46 boolean thisLocked; 47 boolean thisLockingOnly = true; 48 49 private BugReporter bugReporter; 50 51 static final int READ_LOCKED = 0; 52 static final int WRITTEN_LOCKED = 1; 53 static final int READ_UNLOCKED = 2; 54 static final int WRITTEN_UNLOCKED = 3; 55 56 static final String [] names = { 57 "R/L", 58 "W/L", 59 "R/U", 60 "W/U"}; 61 62 public LockedFields(BugReporter bugReporter) { 63 this.bugReporter = bugReporter; 64 } 65 66 private void updateStats(Set<FieldAnnotation> fields, int mode) { 67 if (!publicMethod && !protectedMethod) { 69 if (mode == READ_UNLOCKED || mode == WRITTEN_UNLOCKED) return; 70 } 71 76 for (FieldAnnotation f : fields) { 77 if (f.getClassName().equals(getDottedClassName()) && mode <= WRITTEN_LOCKED) 78 localLocks.add(f); 79 int[] theseStats = stats.get(f); 80 if (theseStats == null) { 81 theseStats = new int[4]; 82 stats.put(f, theseStats); 83 } 84 if (DEBUG) 85 System.out.println(names[mode] 86 + " " 87 + getFullyQualifiedMethodName() 88 + " " 89 + f.toString()); 90 91 theseStats[mode]++; 92 } 93 } 94 95 @Override 96 public void visit(Field obj) { 97 super.visit(obj); 98 99 FieldAnnotation f = FieldAnnotation.fromVisitedField(this); 100 101 int flags = obj.getAccessFlags(); 102 boolean publicField = (flags & ACC_PUBLIC) != 0; 103 boolean volatileField = (flags & ACC_VOLATILE) != 0; 104 boolean finalField = (flags & ACC_FINAL) != 0; 105 if (publicField) 106 publicFields.add(f); 107 if (volatileField || finalField) 108 volatileOrFinalFields.add(f); 109 } 110 111 @Override 112 public void visit(Method obj) { 113 super.visit(obj); 114 int flags = obj.getAccessFlags(); 115 publicMethod = (flags & ACC_PUBLIC) != 0; 116 protectedMethod = (flags & ACC_PROTECTED) != 0; 117 synchronizedMethod = (flags & ACC_SYNCHRONIZED) != 0; 118 if (synchronizedMethod) 119 state = 1; 120 else 121 state = 0; 122 fieldsWritten.clear(); 123 fieldsRead.clear(); 124 inConstructor = getMethodName().equals("<init>") 125 || getMethodName().equals("<clinit>") 126 || getMethodName().equals("readObject") 127 || getMethodName().equals("clone") 128 || getMethodName().equals("close") 129 || getMethodName().equals("finalize"); 130 134 135 } 136 137 138 @Override 139 public void visit(Code obj) { 140 if (inConstructor) return; 141 thisOnTopOfStack = false; 142 thisLocked = false; 143 super.visit(obj); 144 if (state == 1) { 146 updateStats(fieldsWritten, WRITTEN_LOCKED); 147 updateStats(fieldsRead, READ_LOCKED); 148 } else if (obj.getCode().length > 6) { 149 updateStats(fieldsWritten, WRITTEN_UNLOCKED); 150 updateStats(fieldsRead, READ_UNLOCKED); 151 } 152 } 153 154 @Override 155 public void sawOpcode(int seen) { 156 160 switch (seen) { 161 case ASTORE_1: 162 case ASTORE_2: 163 case ASTORE_3: 164 case ASTORE: 165 thisOnTopOfStack = doubleThisOnTopOfStack; 166 return; 167 case ALOAD_0: 168 thisOnTopOfStack = true; 169 return; 170 case DUP: 171 doubleThisOnTopOfStack = thisOnTopOfStack; 172 return; 173 case MONITOREXIT: 174 if (thisLockingOnly && !thisLocked) break; 175 updateStats(fieldsWritten, WRITTEN_LOCKED); 176 updateStats(fieldsRead, READ_LOCKED); 177 state = 2; 178 fieldsWritten.clear(); 180 fieldsRead.clear(); 181 break; 182 case MONITORENTER: 183 thisLocked = thisOnTopOfStack; 184 if (thisLockingOnly && !thisLocked) break; 185 updateStats(fieldsWritten, WRITTEN_UNLOCKED); 186 updateStats(fieldsRead, READ_UNLOCKED); 187 state = 1; 189 fieldsWritten.clear(); 190 fieldsRead.clear(); 191 break; 192 case PUTFIELD: 193 { 194 FieldAnnotation f = FieldAnnotation.fromReferencedField(this); 195 writtenOutsideOfConstructor.add(f); 196 if (!getClassName().equals(getClassConstantOperand())) break; 197 fieldsWritten.add(f); 199 } 200 break; 201 case GETFIELD: 202 int next = codeBytes[getPC() + 3] & 0xff; 203 if (!thisOnTopOfStack) break; 204 if (next != IFNULL && next != IFNONNULL) { 205 FieldAnnotation f = FieldAnnotation.fromReferencedField(this); 206 fieldsRead.add(f); 208 219 } 220 break; 222 } 223 thisOnTopOfStack = false; 224 doubleThisOnTopOfStack = false; 225 } 226 227 @Override 228 public void report() { 229 230 int noLocked = 0; 231 int noUnlocked = 0; 232 int isPublic = 0; 233 int couldBeFinal = 0; 234 int noLocalLocks = 0; 235 int volatileOrFinalCount = 0; 236 int mostlyUnlocked = 0; 237 238 for (FieldAnnotation f : stats.keySet()) { 240 int[] theseStats = stats.get(f); 241 242 int locked = theseStats[READ_LOCKED] + theseStats[WRITTEN_LOCKED]; 243 int biasedLocked = theseStats[READ_LOCKED] + 2 * theseStats[WRITTEN_LOCKED]; 244 int unlocked = theseStats[READ_UNLOCKED] + theseStats[WRITTEN_UNLOCKED]; 245 int biasedUnlocked = theseStats[READ_UNLOCKED] + 2 * theseStats[WRITTEN_UNLOCKED]; 246 int writes = theseStats[WRITTEN_LOCKED] + theseStats[WRITTEN_UNLOCKED]; 247 if (locked == 0) { 248 noLocked++; 249 continue; 250 } 251 if (unlocked == 0) { 252 noUnlocked++; 253 continue; 254 } 255 if (theseStats[READ_UNLOCKED] > 0 && 2 * biasedUnlocked > biasedLocked) { 256 if (DEBUG) System.out.println("Mostly unlocked for " + f + ":"); 257 int freq = (100 * locked) / (locked + unlocked); 258 if (DEBUG) { 259 System.out.print(freq 260 + " "); 261 for (int j = 0; j < 4; j++) 262 System.out.print(theseStats[j] + " "); 263 System.out.println(f); 264 } 265 mostlyUnlocked++; 266 continue; 267 } 268 if (publicFields.contains(f)) { 269 isPublic++; 270 continue; 271 } 272 if (volatileOrFinalFields.contains(f)) { 273 volatileOrFinalCount++; 274 continue; 275 } 276 if (!writtenOutsideOfConstructor.contains(f)) { 277 couldBeFinal++; 278 continue; 279 } 280 if (!localLocks.contains(f)) { 281 if (DEBUG) System.out.println("No local locks of " + f); 282 noLocalLocks++; 283 continue; 284 } 285 int freq = (100 * locked) / (locked + unlocked); 286 bugReporter.reportBug(new BugInstance(this, "IS_INCONSISTENT_SYNC", NORMAL_PRIORITY) 287 .addClass(f.getClassName()) 288 .addField(f) 289 .addInt(freq).describe(IntAnnotation.INT_SYNC_PERCENT)); 290 if (DEBUG) { 291 System.out.print(freq 292 + " "); 293 for (int j = 0; j < 4; j++) 294 System.out.print(theseStats[j] + " "); 295 System.out.println(f); 296 } 297 } 298 if (DEBUG) { 299 int total = stats.size(); 300 System.out.println(" Total fields: " + total); 301 System.out.println(" No locked accesses: " + noLocked); 302 System.out.println("No unlocked accesses: " + noUnlocked); 303 System.out.println(" Mostly unlocked: " + mostlyUnlocked); 304 System.out.println(" public fields: " + isPublic); 305 if (couldBeFinal > 0) 306 System.out.println(" could be Final: " + couldBeFinal); 307 System.out.println(" volatile or final: " + volatileOrFinalCount); 308 System.out.println(" no local locks: " + noLocalLocks); 309 System.out.println(" questionable fields: " + (total - noLocked - noUnlocked - isPublic - volatileOrFinalCount - couldBeFinal - noLocalLocks - mostlyUnlocked)); 310 } 311 } 312 313 314 } 315 | Popular Tags |