KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2003-2005 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 import edu.umd.cs.findbugs.ba.*;
25 import edu.umd.cs.findbugs.ba.npe.*;
26 import edu.umd.cs.findbugs.ba.vna.*;
27
28 import java.util.BitSet JavaDoc;
29 import org.apache.bcel.Constants;
30 import org.apache.bcel.classfile.*;
31 import org.apache.bcel.generic.*;
32
33 class Lock extends ResourceCreationPoint {
34     private ValueNumber lockValue;
35
36     public Lock(Location location, String JavaDoc lockClass, ValueNumber lockValue) {
37         super(location, lockClass);
38         this.lockValue = lockValue;
39     }
40
41     public ValueNumber getLockValue() {
42         return lockValue;
43     }
44 }
45
46 public class FindUnreleasedLock extends ResourceTrackingDetector<Lock, FindUnreleasedLock.LockResourceTracker> {
47     private static final boolean DEBUG = SystemProperties.getBoolean("ful.debug");
48     private int numAcquires = 0;
49     
50     private static final int JDK15_MAJOR = 48;
51     private static final int JDK15_MINOR = 0;
52
53     /* ----------------------------------------------------------------------
54      * Helper classes
55      * ---------------------------------------------------------------------- */

56
57     private static class LockFrameModelingVisitor extends ResourceValueFrameModelingVisitor {
58         private LockResourceTracker resourceTracker;
59         private Lock lock;
60         private ValueNumberDataflow vnaDataflow;
61         //private IsNullValueDataflow isNullDataflow;
62

63         public LockFrameModelingVisitor(
64                 ConstantPoolGen cpg,
65                 LockResourceTracker resourceTracker,
66                 Lock lock,
67                 ValueNumberDataflow vnaDataflow,
68                 IsNullValueDataflow isNullDataflow) {
69             super(cpg);
70             this.resourceTracker = resourceTracker;
71             this.lock = lock;
72             this.vnaDataflow = vnaDataflow;
73             //this.isNullDataflow = isNullDataflow;
74
}
75
76         @Override JavaDoc
77         public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock) throws DataflowAnalysisException {
78             final Instruction ins = handle.getInstruction();
79             final ConstantPoolGen cpg = getCPG();
80             final ResourceValueFrame frame = getFrame();
81
82             int status = -1;
83
84             if (DEBUG) System.out.println("PC : " + handle.getPosition() + " " + ins);
85             if (DEBUG && ins instanceof InvokeInstruction) {
86                 InvokeInstruction iins = (InvokeInstruction) ins;
87                 System.out.println(" " + ins.toString(cpg.getConstantPool()));
88             }
89             if (DEBUG) System.out.println("resource frame before instruction: " + frame.toString());
90
91             // Is a lock acquired or released by this instruction?
92
Location creationPoint = lock.getLocation();
93             if (handle == creationPoint.getHandle() && basicBlock == creationPoint.getBasicBlock()) {
94                 status = ResourceValueFrame.OPEN;
95                 if (DEBUG) System.out.println("OPEN");
96             } else if (resourceTracker.isResourceClose(basicBlock, handle, cpg, lock, frame)) {
97                 status = ResourceValueFrame.CLOSED;
98                 if (DEBUG) System.out.println("CLOSE");
99             }
100
101             // Model use of instance values in frame slots
102
analyzeInstruction(ins);
103
104             final int updatedNumSlots = frame.getNumSlots();
105
106             // Mark any appearances of the lock value in the ResourceValueFrame.
107
ValueNumberFrame vnaFrame = vnaDataflow.getFactAfterLocation(new Location(handle, basicBlock));
108             if (DEBUG) {
109                 System.out.println("vna frame after instruction: " + vnaFrame.toString());
110                 System.out.println("Lock value number: " + lock.getLockValue());
111                 if (lock.getLockValue().hasFlag(ValueNumber.RETURN_VALUE) )
112                     System.out.println("is return value");
113             }
114             
115             for (int i = 0; i < updatedNumSlots; ++i) {
116                 if (DEBUG) {
117                     System.out.println("Slot " + i);
118                     System.out.println(" Lock value number: " + vnaFrame.getValue(i));
119                     if (vnaFrame.getValue(i).hasFlag(ValueNumber.RETURN_VALUE) )
120                         System.out.println(" is return value");
121                 }
122                 if (vnaFrame.fuzzyMatch(lock.getLockValue(), vnaFrame.getValue(i)) ) {
123                     if (DEBUG) System.out.println("Saw lock value!");
124                     frame.setValue(i, ResourceValue.instance());
125                 }
126             }
127             
128             // If needed, update frame status
129
if (status != -1) {
130                 frame.setStatus(status);
131             }
132             if (DEBUG) System.out.println("resource frame after instruction: " + frame.toString());
133
134
135         }
136
137         @Override JavaDoc
138         protected boolean instanceEscapes(InvokeInstruction inv, int instanceArgNum) {
139             return false;
140         }
141     }
142
143      class LockResourceTracker implements ResourceTracker<Lock> {
144         private RepositoryLookupFailureCallback lookupFailureCallback;
145         private CFG cfg;
146         private ValueNumberDataflow vnaDataflow;
147         private IsNullValueDataflow isNullDataflow;
148
149         public LockResourceTracker(
150                 RepositoryLookupFailureCallback lookupFailureCallback,
151                 CFG cfg,
152                 ValueNumberDataflow vnaDataflow,
153                 IsNullValueDataflow isNullDataflow) {
154             this.lookupFailureCallback = lookupFailureCallback;
155             this.cfg = cfg;
156             this.vnaDataflow = vnaDataflow;
157             this.isNullDataflow = isNullDataflow;
158         }
159
160         public Lock isResourceCreation(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg)
161                 throws DataflowAnalysisException {
162
163             InvokeInstruction inv = toInvokeInstruction(handle.getInstruction());
164             if (inv == null)
165                 return null;
166
167             String JavaDoc className = inv.getClassName(cpg);
168             String JavaDoc methodName = inv.getName(cpg);
169             String JavaDoc methodSig = inv.getSignature(cpg);
170
171             try {
172                 if (methodName.equals("lock") &&
173                         methodSig.equals("()V") &&
174                         Hierarchy.isSubtype(className, "java.util.concurrent.locks.Lock")) {
175
176                     Location location = new Location(handle, basicBlock);
177                     ValueNumberFrame frame = vnaDataflow.getFactAtLocation(location);
178                     ValueNumber lockValue = frame.getTopValue();
179                     if (DEBUG) System.out.println("Lock value is " + lockValue.getNumber() + ", frame=" + frame.toString());
180                     if (DEBUG) ++numAcquires;
181                     return new Lock(location, className, lockValue);
182                 }
183             } catch (ClassNotFoundException JavaDoc e) {
184                 lookupFailureCallback.reportMissingClass(e);
185             }
186             return null;
187         }
188
189         public boolean isResourceClose(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg, Lock resource,
190                 ResourceValueFrame frame) throws DataflowAnalysisException {
191
192             InvokeInstruction inv = toInvokeInstruction(handle.getInstruction());
193             if (inv == null)
194                 return false;
195
196             String JavaDoc className = inv.getClassName(cpg);
197             String JavaDoc methodName = inv.getName(cpg);
198             String JavaDoc methodSig = inv.getSignature(cpg);
199
200             ResourceValue topValue = frame.getTopValue();
201             if (!topValue.isInstance())
202                 return false;
203
204             try {
205                 if (methodName.equals("unlock") &&
206                         methodSig.equals("()V") &&
207                         Hierarchy.isSubtype(className, "java.util.concurrent.locks.Lock")) {
208                     
209                     return true;
210                 }
211             } catch (ClassNotFoundException JavaDoc e) {
212                 lookupFailureCallback.reportMissingClass(e);
213             }
214
215             return false;
216         }
217
218         public ResourceValueFrameModelingVisitor createVisitor(Lock resource, ConstantPoolGen cpg) {
219             return new LockFrameModelingVisitor(cpg, this, resource, vnaDataflow, isNullDataflow);
220         }
221
222         public boolean ignoreImplicitExceptions(Lock resource) {
223             // JSR166 locks should be ALWAYS be released,
224
// including when implicit runtime exceptions are thrown
225
return false;
226         }
227
228         public boolean ignoreExceptionEdge(Edge edge, Lock resource, ConstantPoolGen cpg) {
229             
230             try {
231                 Location location = cfg.getExceptionThrowerLocation(edge);
232                 if (DEBUG) {
233                     System.out.println("Exception thrower location: " + location);
234                 }
235                 Instruction ins = location.getHandle().getInstruction();
236                 
237                 if (ins instanceof GETFIELD) {
238                     GETFIELD insGetfield = (GETFIELD)ins;
239                     String JavaDoc fieldName = insGetfield.getFieldName(cpg);
240                     if (DEBUG) {
241                         System.out.println("Inspecting GETFIELD of " + fieldName + " at " + location);
242                     }
243                     // Ignore exceptions from getfield instructions where the
244
// object referece is known not to be null
245
if (fieldName.equals("lock")) return true;
246                     IsNullValueFrame frame = isNullDataflow.getFactAtLocation(location);
247                     if (!frame.isValid())
248                         return false;
249                     IsNullValue receiver = frame.getInstance(ins, cpg);
250                     boolean notNull = receiver.isDefinitelyNotNull();
251                     if (DEBUG && notNull) {
252                         System.out.println("Ignoring exception from non-null GETFIELD");
253                     }
254                     return notNull;
255                 } else if (ins instanceof InvokeInstruction) {
256                     InvokeInstruction iins = (InvokeInstruction) ins;
257                     String JavaDoc methodName = iins.getMethodName(cpg);
258                     //System.out.println("Method " + methodName);
259
if (methodName.startsWith("access$")) return true;
260                     if (methodName.equals("readLock") || methodName.equals("writeLock")) return true;
261                     if (methodName.equals("lock") || methodName.equals("unlock")) return true;
262                 }
263                 if (DEBUG) {
264                     System.out.println("FOUND Exception thrower at: " + location);
265                 }
266             } catch (DataflowAnalysisException e) {
267                 // Report...
268
}
269             
270             return false;
271         }
272         
273         public boolean isParamInstance(Lock resource, int slot) {
274             // There is nothing special about Lock objects passed
275
// into the method as parameters.
276
return false;
277         }
278
279         private InvokeInstruction toInvokeInstruction(Instruction ins) {
280             short opcode = ins.getOpcode();
281             if (opcode != Constants.INVOKEVIRTUAL && opcode != Constants.INVOKEINTERFACE)
282                 return null;
283             return (InvokeInstruction) ins;
284         }
285     }
286
287     /* ----------------------------------------------------------------------
288      * Implementation
289      * ---------------------------------------------------------------------- */

290
291     public FindUnreleasedLock(BugReporter bugReporter) {
292         super(bugReporter);
293     }
294
295     /**
296      * Checks if classfile was compiled for pre 1.5 target
297      */

298     public static boolean preTiger(JavaClass jclass) {
299         return jclass.getMajor() < JDK15_MAJOR ||
300                 (jclass.getMajor() == JDK15_MAJOR && jclass.getMinor() < JDK15_MINOR);
301         
302     }
303     /* (non-Javadoc)
304      * @see edu.umd.cs.findbugs.Detector#visitClassContext(edu.umd.cs.findbugs.ba.ClassContext)
305      */

306     @Override JavaDoc
307     public void visitClassContext(ClassContext classContext) {
308         JavaClass jclass = classContext.getJavaClass();
309         
310         // We can ignore classes that were compiled for anything
311
// less than JDK 1.5. This should avoid lots of unnecessary work
312
// when analyzing code for older VM targets.
313
if (preTiger(jclass)) return;
314         
315         boolean sawUtilConcurrentLocks = false;
316         for(Constant c : jclass.getConstantPool().getConstantPool())
317             if (c instanceof ConstantMethodref) {
318                 ConstantMethodref m = (ConstantMethodref) c;
319                 ConstantClass cl = (ConstantClass) jclass.getConstantPool().getConstant(m.getClassIndex());
320                 ConstantUtf8 name = (ConstantUtf8) jclass.getConstantPool().getConstant(cl.getNameIndex());
321                 String JavaDoc nameAsString = name.getBytes();
322                 if (nameAsString.startsWith("java/util/concurrent/locks")) sawUtilConcurrentLocks = true;
323                 
324             }
325         if (sawUtilConcurrentLocks) super.visitClassContext(classContext);
326     }
327
328     
329     @Override JavaDoc
330     public boolean prescreen(ClassContext classContext, Method method) {
331         BitSet JavaDoc bytecodeSet = classContext.getBytecodeSet(method);
332         if (bytecodeSet == null) return false;
333         MethodGen methodGen = classContext.getMethodGen(method);
334         return methodGen != null && methodGen.getName().toLowerCase().indexOf("lock") == -1
335             && (bytecodeSet.get(Constants.INVOKEVIRTUAL)
336                 || bytecodeSet.get(Constants.INVOKEINTERFACE));
337     }
338
339     @Override JavaDoc
340     public LockResourceTracker getResourceTracker(ClassContext classContext, Method method)
341             throws CFGBuilderException, DataflowAnalysisException {
342         return new LockResourceTracker(
343                 bugReporter,
344                 classContext.getCFG(method),
345                 classContext.getValueNumberDataflow(method),
346                 classContext.getIsNullValueDataflow(method));
347     }
348
349     @Override JavaDoc
350     public void inspectResult(ClassContext classContext, MethodGen methodGen, CFG cfg,
351             Dataflow<ResourceValueFrame, ResourceValueAnalysis<Lock>> dataflow, Lock resource) {
352
353         JavaClass javaClass = classContext.getJavaClass();
354         
355         ResourceValueFrame exitFrame = dataflow.getResultFact(cfg.getExit());
356         if (DEBUG) {
357             System.out.println("Resource value at exit: " + exitFrame);
358         }
359         int exitStatus = exitFrame.getStatus();
360
361         if (exitStatus == ResourceValueFrame.OPEN || exitStatus == ResourceValueFrame.OPEN_ON_EXCEPTION_PATH) {
362             String JavaDoc bugType;
363             int priority;
364             if (exitStatus == ResourceValueFrame.OPEN) {
365                 bugType = "UL_UNRELEASED_LOCK";
366                 priority = HIGH_PRIORITY;
367             } else {
368                 bugType = "UL_UNRELEASED_LOCK_EXCEPTION_PATH";
369                 priority = NORMAL_PRIORITY;
370             }
371
372             String JavaDoc sourceFile = javaClass.getSourceFileName();
373             bugReporter.reportBug(new BugInstance(this, bugType, priority)
374                     .addClassAndMethod(methodGen, sourceFile)
375                     .addSourceLine(classContext, methodGen, sourceFile, resource.getLocation().getHandle()));
376         }
377     }
378
379     @Override JavaDoc
380     public void report() {
381         if (DEBUG) System.out.println("numAcquires=" + numAcquires);
382     }
383
384     /* ----------------------------------------------------------------------
385      * Test main() driver
386      * ---------------------------------------------------------------------- */

387
388     public static void main(String JavaDoc[] argv) throws Exception JavaDoc {
389         if (argv.length != 3) {
390             System.err.println("Usage: " + FindUnreleasedLock.class.getName() + " <class file> <method name> <bytecode offset>");
391             System.exit(1);
392         }
393
394         String JavaDoc classFile = argv[0];
395         String JavaDoc methodName = argv[1];
396         int offset = Integer.parseInt(argv[2]);
397         final FindUnreleasedLock detector = new FindUnreleasedLock(null);
398         
399         ResourceValueAnalysisTestDriver<Lock, LockResourceTracker> driver =
400             new ResourceValueAnalysisTestDriver<Lock, LockResourceTracker>() {
401                 @Override JavaDoc
402                 public LockResourceTracker createResourceTracker(ClassContext classContext, Method method)
403                         throws CFGBuilderException, DataflowAnalysisException {
404
405                     RepositoryLookupFailureCallback lookupFailureCallback = classContext.getLookupFailureCallback();
406
407                     return detector.new LockResourceTracker(
408                                 lookupFailureCallback,
409                                 classContext.getCFG(method),
410                                 classContext.getValueNumberDataflow(method),
411                                 classContext.getIsNullValueDataflow(method));
412                 }
413         };
414
415         driver.execute(classFile, methodName, offset);
416     }
417 }
418
419 // vim:ts=4
420
Popular Tags