KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > detect > LockedFields


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003,2004 University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

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 privateMethod;
41
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 JavaDoc[] 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 (privateMethod) return;
68
if (!publicMethod && !protectedMethod) {
69             if (mode == READ_UNLOCKED || mode == WRITTEN_UNLOCKED) return;
70         }
71         /*
72         if (!publicMethod) {
73             if (mode == READ_UNLOCKED || mode == WRITTEN_UNLOCKED) return;
74             }
75         */

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 JavaDoc
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 JavaDoc
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         /*
131         privateMethod = (flags & ACC_PRIVATE) != 0
132                 || methodName.startsWith("access$");
133         */

134
135     }
136
137
138     @Override JavaDoc
139          public void visit(Code obj) {
140         if (inConstructor) return;
141         thisOnTopOfStack = false;
142         thisLocked = false;
143         super.visit(obj);
144         // System.out.println("End of method, state = " + state);
145
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 JavaDoc
155          public void sawOpcode(int seen) {
156         // state: 0 - unlocked
157
// state: 1 - locked
158
// state: 2 - saw unlocked, but might still be locked
159

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             // System.out.println("monitorexit " + thisLocked);
179
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             // System.out.println("monitorenter " + thisLocked);
188
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                 // System.out.println("putfield " + f + ", state = " + state);
198
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                 // System.out.println("getfield " + f);
207
fieldsRead.add(f);
208                 /*
209                 System.out.println("After read of "
210                     + classConstant + "." + nameConstant
211                     + ", next PC is " + (PC+3)
212                     );
213                 System.out.println("After read of "
214                     + classConstant + "." + nameConstant
215                     + ", next opcode is " + OPCODE_NAMES[next]
216                     + " (" + next + ")"
217                     );
218                 */

219             }
220             // OPCODE_NAMES
221
break;
222         }
223         thisOnTopOfStack = false;
224         doubleThisOnTopOfStack = false;
225     }
226
227     @Override JavaDoc
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 (Iterator<Map.Entry<FieldAnnotation, int[]>> i = stats.entrySet().iterator(); i.hasNext();) {
239
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