[Fb-contrib-commit] SF.net SVN: fb-contrib:[1772] trunk/fb-contrib
Brought to you by:
dbrosius
|
From: <dbr...@us...> - 2014-05-24 04:08:33
|
Revision: 1772
http://sourceforge.net/p/fb-contrib/code/1772
Author: dbrosius
Date: 2014-05-24 04:08:28 +0000 (Sat, 24 May 2014)
Log Message:
-----------
prepared for version 5.2.1 release
Modified Paths:
--------------
trunk/fb-contrib/.classpath
trunk/fb-contrib/build.properties
trunk/fb-contrib/build.xml
trunk/fb-contrib/etc/bugrank.txt
trunk/fb-contrib/etc/findbugs.xml
trunk/fb-contrib/etc/messages.xml
trunk/fb-contrib/htdocs/index.shtml
trunk/fb-contrib/pom.xml
trunk/fb-contrib/samples/LSYC_Sample.java
trunk/fb-contrib/samples/ROOM_Sample.java
trunk/fb-contrib/samples/samples.fbp
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/collect/CollectStatistics.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/debug/OCSDebugger.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/InvalidConstantArgument.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/LiteralStringComparison.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/LocalSynchronizedCollection.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/PossiblyRedundantMethodCalls.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/SillynessPotPourri.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/UnusedParameter.java
trunk/fb-contrib/yank.xls
Added Paths:
-----------
trunk/fb-contrib/samples/CTU_Sample.java
trunk/fb-contrib/samples/HES_Sample.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConflictingTimeUnits.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/HangingExecutors.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/LocalTypeDetector.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/Unjitable.java
trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/utils/OpcodeUtils.java
Modified: trunk/fb-contrib/.classpath
===================================================================
--- trunk/fb-contrib/.classpath 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/.classpath 2014-05-24 04:08:28 UTC (rev 1772)
@@ -15,8 +15,8 @@
<classpathentry kind="lib" path="lib/findbugs-2.0.3.jar" sourcepath="/home/dave/.m2/repository/com/google/code/findbugs/findbugs/2.0.3/findbugs-2.0.3-sources.jar"/>
<classpathentry kind="lib" path="lib/annotations-2.0.3.jar"/>
<classpathentry kind="lib" path="lib/javax.servlet.jsp-api-2.3.1.jar"/>
- <classpathentry kind="lib" path="lib/guava-16.0.1.jar" sourcepath="lib/guava-16.0.1-sources.jar"/>
<classpathentry kind="lib" path="lib/commons-lang3-3.3.jar"/>
<classpathentry kind="lib" path="lib/slf4j-api-1.7.6.jar"/>
+ <classpathentry kind="lib" path="lib/guava-17.0.jar"/>
<classpathentry kind="output" path="classes"/>
</classpath>
Modified: trunk/fb-contrib/build.properties
===================================================================
--- trunk/fb-contrib/build.properties 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/build.properties 2014-05-24 04:08:28 UTC (rev 1772)
@@ -9,3 +9,5 @@
output.. = classes/
proxy.server =
+
+findbugs.dir=/home/dave/dev/findbugs/findbugs
Modified: trunk/fb-contrib/build.xml
===================================================================
--- trunk/fb-contrib/build.xml 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/build.xml 2014-05-24 04:08:28 UTC (rev 1772)
@@ -18,7 +18,7 @@
<property name="javac.deprecation" value="on" />
<property name="javac.debug" value="on" />
- <property name="fb-contrib.version" value="5.3.0" />
+ <property name="fb-contrib.version" value="5.2.1" />
<property name="sonatype.dir" value="${user.home}/.fb-contrib-${fb-contrib.version}-sonatype" />
@@ -38,7 +38,7 @@
<target name="yank" xmlns:yank="antlib:com.mebigfatguy.yank">
<mkdir dir="${lib.dir}" />
- <yank:yank yankFile="${basedir}/yank.xls" destination="${lib.dir}" proxyServer="${proxy.server}" source="true">
+ <yank:yank yankFile="${basedir}/yank.xls" destination="${lib.dir}" proxyServer="${proxy.server}" source="true" separateClassifierTypes="true">
<server url="http://repo1.maven.org/maven2" />
<generateVersions propertyFileName="${basedir}/version.properties" />
</yank:yank>
@@ -91,7 +91,8 @@
<target name="compile_samples" depends="-init" description="compiles sample problem files">
<javac srcdir="${samples.dir}" destdir="${samples.dir}" source="1.7" target="1.7" deprecation="${javac.deprecation}" debug="${javac.debug}" includeantruntime="false">
- <classpath refid="fb-contrib.classpath" />
+ <compilerarg value="-XDignore.symbol.file"/>
+ <classpath refid="fb-contrib.classpath" />
<classpath refid="fb-contrib.samples.classpath" />
</javac>
<delete file="${samples.dir}/SJVU_Sample.class" />
@@ -164,8 +165,7 @@
</target>
<target name="install" depends="build" description="installs the plugin into FindBugs">
- <property environment="env" />
- <copy todir="${env.FINDBUGS_HOME}/plugin">
+ <copy todir="${findbugs.dir}/plugin">
<fileset dir="${basedir}">
<include name="fb-contrib-${fb-contrib.version}.jar" />
</fileset>
Modified: trunk/fb-contrib/etc/bugrank.txt
===================================================================
--- trunk/fb-contrib/etc/bugrank.txt 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/etc/bugrank.txt 2014-05-24 04:08:28 UTC (rev 1772)
@@ -126,4 +126,7 @@
0 BugPattern NSE_NON_SYMMETRIC_EQUALS
0 BugPattern CVAA_CONTRAVARIANT_ARRAY_ASSIGNMENT
0 BugPattern NFF_NON_FUNCTIONAL_FIELD
-0 BugPattern CEBE_COMMONS_EQUAL_BUILDER_TOEQUALS
\ No newline at end of file
+0 BugPattern CEBE_COMMONS_EQUAL_BUILDER_TOEQUALS
+-2 BugPattern HE_LOCAL_EXECUTOR_SERVICE
+-4 BugPattern HE_EXECUTOR_NEVER_SHUTDOWN
+-2 BugPattern HE_EXECUTOR_OVERWRITTEN_WITHOUT_SHUTDOWN
\ No newline at end of file
Modified: trunk/fb-contrib/etc/findbugs.xml
===================================================================
--- trunk/fb-contrib/etc/findbugs.xml 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/etc/findbugs.xml 2014-05-24 04:08:28 UTC (rev 1772)
@@ -20,7 +20,7 @@
<!-- Detectors -->
-<!-- COMMENT OUT FOR RELEASE -->
+<!-- COMMENT OUT FOR RELEASE
<Detector class="com.mebigfatguy.fbcontrib.debug.OCSDebugger" speed="fast"/>
@@ -34,7 +34,7 @@
<Detector class="com.mebigfatguy.fbcontrib.detect.BloatedAssignmentScope" speed="fast" reports="BAS_BLOATED_ASSIGNMENT_SCOPE" hidden="true" />
-<!-- COMMENT OUT FOR RELEASE -->
+ COMMENT OUT FOR RELEASE -->
<Detector class="com.mebigfatguy.fbcontrib.collect.CollectStatistics" speed="fast" reports="" hidden="true" />
@@ -264,14 +264,21 @@
<Detector class="com.mebigfatguy.fbcontrib.collect.CollectMethodsReturningImmutableCollections" speed="fast" reports="" hidden="true" />
<Detector class="com.mebigfatguy.fbcontrib.detect.ModifyingUnmodifiableCollection" speed="fast" reports="MUC_MODIFYING_UNMODIFIABLE_COLLECTION"/>
+
- <!-- COMMENT OUT FOR POINT RELEASE -->
+ <!-- COMMENT OUT FOR POINT RELEASE
<Detector class="com.mebigfatguy.fbcontrib.detect.PresizeCollections" speed="fast" reports="PSC_PRESIZE_COLLECTIONS" />
<Detector class="com.mebigfatguy.fbcontrib.detect.ArrayIndexOutOfBounds" speed="fast" reports="AIOB_ARRAY_INDEX_OUT_OF_BOUNDS,AIOB_ARRAY_STORE_TO_NULL_REFERENCE" />
- <!-- COMMENT OUT FOR POINT RELEASE -->
+ <Detector class="com.mebigfatguy.fbcontrib.detect.Unjitable" speed="fast" reports="UJM_UNJITABLE_METHOD" />
+
+ <Detector class="com.mebigfatguy.fbcontrib.detect.HangingExecutors" speed="fast" reports="HES_EXECUTOR_NEVER_SHUTDOWN,HES_EXECUTOR_OVERWRITTEN_WITHOUT_SHUTDOWN,HES_LOCAL_EXECUTOR_SERVICE"/>
+ <Detector class="com.mebigfatguy.fbcontrib.detect.ConflictingTimeUnits" speed="fast" reports="CTU_CONFLICTING_TIME_UNITS" />
+
+ COMMENT OUT FOR POINT RELEASE -->
+
<!-- BugPattern -->
<BugPattern abbrev="ISB" type="ISB_INEFFICIENT_STRING_BUFFERING" category="PERFORMANCE" />
@@ -474,4 +481,9 @@
<BugPattern abbrev="UP" type="UP_UNUSED_PARAMETER" category="STYLE" experimental="true" />
<BugPattern abbrev="CD" type="CD_CIRCULAR_DEPENDENCY" category="CORRECTNESS" />
<BugPattern abbrev="MUC" type="MUC_MODIFYING_UNMODIFIABLE_COLLECTION" category="CORRECTNESS" experimental="true" />
+ <BugPattern abbrev="UJM" type="UJM_UNJITABLE_METHOD" category="PERFORMANCE" experimental="true" />
+ <BugPattern abbrev="HES" type="HES_EXECUTOR_NEVER_SHUTDOWN" category="CORRECTNESS" experimental="true" />
+ <BugPattern abbrev="HES" type="HES_EXECUTOR_OVERWRITTEN_WITHOUT_SHUTDOWN" category="CORRECTNESS" experimental="true" />
+ <BugPattern abbrev="HES" type="HES_LOCAL_EXECUTOR_SERVICE" category="CORRECTNESS" experimental="true" />
+ <BugPattern abbrev="CTU" type="CTU_CONFLICTING_TIME_UNITS" category="CORRECTNESS" experimental="true" />
</FindbugsPlugin>
Modified: trunk/fb-contrib/etc/messages.xml
===================================================================
--- trunk/fb-contrib/etc/messages.xml 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/etc/messages.xml 2014-05-24 04:08:28 UTC (rev 1772)
@@ -178,6 +178,12 @@
<p>Looks for methods that correctly do not write to a parameter. To help document this, and to perhaps
help the JVM optimize the invocation of this method, you should consider defining these parameters
as final.</p>
+
+ <p>Performance gains are debatable as "the final keyword does not appear in the class file for
+ local variables and parameters, thus it cannot impact the runtime performance. It's only use
+ is to clarify the coders intent that the variable not be changed (which many consider dubious
+ reason for its usage), and dealing with anonymous inner classes." - http://stackoverflow.com/a/266981/1447621</p>
+
<p>It is a slow detector</p>
]]>
</Details>
@@ -1384,7 +1390,7 @@
<Details>
<![CDATA[
<p>Looks for private or static methods that have parameters that aren't used. These parameters
- can be removed.</p>
+ can be removed, assuming the method isn't used through reflection.</p>
<p>It is fast detector</p>
]]>
</Details>
@@ -1409,6 +1415,38 @@
</Details>
</Detector>
+ <Detector class="com.mebigfatguy.fbcontrib.detect.HangingExecutors">
+ <Details>
+ <![CDATA[
+ <p>Single detector for hanging ExecutorServices, that is, ExecutorServices that never get a call to shutdown, which
+ can potentially cause the JVM to not exit.</p>
+ <p>It is a fast detector</p>
+ ]]>
+ </Details>
+ </Detector>
+
+ <Detector class="com.mebigfatguy.fbcontrib.detect.Unjitable">
+ <Details>
+ <![CDATA[
+ <p>This detector looks for methods that are longer than 8000 bytes. Methods this
+ long are automatically disqualified by the jit for compilation and will always be
+ emulated. Consider breaking this method up to avoid this, if performance is important.</p>
+ <p>It is a fast detector.</p>
+ ]]>
+ </Details>
+ </Detector>
+
+ <Detector class="com.mebigfatguy.fbcontrib.detect.ConflictingTimeUnits">
+ <Details>
+ <![CDATA[
+ <p>looks for methods that perform arithmetic operations on values representing time
+ where the time unit is incompatible, ie adding a millisecond value to a nanosecond value.
+ </p>
+ <p>It is a fast detector</p>
+ ]]>
+ </Details>
+ </Detector>
+
<Detector class="com.mebigfatguy.fbcontrib.debug.OCSDebugger">
<Details></Details>
</Detector>
@@ -1592,22 +1630,43 @@
<LongDescription>Class {0} defines two or more one for one associated lists or arrays</LongDescription>
<Details>
<![CDATA[
- <p>This class appears to maintain two or more lists or arrays who's contains is related one-for-one
- through the index of the list or array. Consider creating a separate class to hold all the related
+ <p>This class appears to maintain two or more lists or arrays whose contents are related in a parallel way. That is,
+ you have something like:<br/>
+ <code>
+ List<String> words = new ArrayList<String>();<br/>
+ List<Integer> wordCounts = new ArrayList<String>();<br/>
+ </code>
+ where the elements of the list at index 0 are related, the elements at index 1 are related and so on. </p>
+ <p>
+ Consider creating a separate class to hold all the related
pieces of information, and adding instances of this class to just one list or array, or if just two values, use
- a Map to associate one value with the other.</p>
+ a Map to associate one value with the other like:<br/>
+ <code>
+ private class WordAndCount{public String word; public int count}
+ List<WordAndCount> wordsAndCounts = new ArrayList<WordAndCount>();<br/>
+ <br/>
+ //or, for just two elements<br/>
+ Map<String,Integer> wordCounts = new HashMap<String,Integer>();<br/>
+ </code>
+
+ </p>
]]>
</Details>
</BugPattern>
<BugPattern type="FP_FINAL_PARAMETERS">
<ShortDescription>Method does not define a parameter as final, but could</ShortDescription>
- <LongDescription>Method {1} does not define a parameter as final, but could</LongDescription>
+ <LongDescription>Method {1} does not define one or more parameters as final, but could</LongDescription>
<Details>
<![CDATA[
- <p>This method correctly does not write to a parameter. To help document this, and to perhaps
+ <p>This method does not write to a parameter. To help document this, and to perhaps
help the JVM optimize the invocation of this method, you should consider defining these parameters
as final.</p>
+
+ <p>Performance gains are debatable as "the final keyword does not appear in the class file for
+ local variables and parameters, thus it cannot impact the runtime performance. It's only use
+ is to clarify the coders intent that the variable not be changed (which many consider dubious
+ reason for its usage), and dealing with anonymous inner classes." - http://stackoverflow.com/a/266981/1447621 </p>
]]>
</Details>
</BugPattern>
@@ -2041,13 +2100,33 @@
</BugPattern>
<BugPattern type="PMB_POSSIBLE_MEMORY_BLOAT">
- <ShortDescription>Class defines static field that appears to allow memory bloat</ShortDescription>
- <LongDescription>Class {0} defines static field that appears to allow memory bloat</LongDescription>
+ <ShortDescription>Potential memory bloat in static field</ShortDescription>
+ <LongDescription>Class {0} defines static field "{2}" which appears to allow memory bloat</LongDescription>
<Details>
<![CDATA[
- <p>This class defines static fields that are collections or StringBuffers that do not
- appear to have any way to clear or reduce their size. This is a potential cause of
- memory bloat.</p>
+ <p>This class defines static fields that are <code>Collection</code>s, <code>StringBuffer</code>s, or <code>StringBuilder</code>s
+ that do not appear to have any way to clear or reduce their size. That is, a collection is defined
+ and has method calls like <br/>
+ {<code>add()</code>, <code>append()</code>, <code>offer()</code>, <code>put()</code>, ...} <br/>
+ with no method calls to removal methods like<br/>
+ {<code>clear()</code>, <code>delete()</code>, <code>pop()</code>, <code>remove()</code>, ...}<br/>
+ This means that the collection in question can only ever increase in size, which is
+ a potential cause of memory bloat.</p>
+
+ <p>
+ If this collection is a list, set or otherwise of static things (e.g. a List<String> for month names), consider
+ adding all of the elements in a static initializer, which can only be called once:<br/>
+ <code>
+ private static List<String> monthNames = new ArrayList<String>();<br/>
+ static {<br/>
+ monthNames.add("January");<br/>
+ monthNames.add("February");<br/>
+ monthNames.add("March");<br/>
+ ...<br/>
+ }
+ </code>
+ </p>
+
]]>
</Details>
</BugPattern>
@@ -3954,7 +4033,110 @@
]]>
</Details>
</BugPattern>
+
+ <BugPattern type="HES_EXECUTOR_NEVER_SHUTDOWN">
+ <ShortDescription>ExecutorService field doesn't ever get shutdown</ShortDescription>
+ <LongDescription>ExecutorService {2} is instantiated, but never shutdown, potentially preventing the entire JVM from shutting down</LongDescription>
+ <Details>
+ <![CDATA[
+ <p>Most <code>ExecutorService</code> objects must be explicitly shutdown,
+ otherwise, their internal threads can prolong the running of the JVM, even when everything
+ else has stopped.</p>
+
+ <p>FindBugs has detected that there are no calls to either the <code>shutdown()</code> or <code>shutdownNow()</code>
+ method, and thus, the <code>ExecutorService</code> is not guaranteed to ever terminate. This is especially
+ problematic for <code>Executors.newFixedThreadPool()</code> and most of the other convenience methods in
+ the <code>Executors</code> class.</p>
+
+ <p>Even though there are some exceptions to this, particularly when a custom <code>ThreadFactory</code> is
+ provided, or for <code>ThreadPoolExecutor</code>s with <code>allowsCoreThreadTimeOut()</code> set to true,
+ it is good practice to explictly shutdown the <code>ExecutorService</code> when its utility is done.</p>
+ ]]>
+ </Details>
+ </BugPattern>
+
+ <BugPattern type="HES_LOCAL_EXECUTOR_SERVICE">
+ <ShortDescription>Suspicious Local Executor Service</ShortDescription>
+ <LongDescription>ExecutorService is created as a local variable, which is unusual</LongDescription>
+ <Details>
+ <![CDATA[
+ <p><code>ExecutorService</code>s are typically instantiated as fields so that many tasks can be executed on a controlled number of <code>Thread</code>s across many method calls. Therefore, it is unusual for <code>ExecutorService</code>s to be a local variable, where tasks will be added only one time, in the enclosing method. </p>
+
+ <p>Furthermore, when a local <code>ExecutorService</code> reaches the end of scope and goes up for garbage collection, the internal <code>Thread</code>s are not necessarily terminated and can prevent the JVM from ever shutting down.</p>
+
+ <p>Consider making this local variable a field and create a method that will explicitly shutdown the <code>ExecutorService</code></p>
+ ]]>
+ </Details>
+ </BugPattern>
+<BugPattern type="HES_EXECUTOR_OVERWRITTEN_WITHOUT_SHUTDOWN">
+ <ShortDescription>An ExecutorService isn't shutdown before the reference to it is lost</ShortDescription>
+ <LongDescription>ExecutorService {2} is replaced with another ExecutorService without being shutdown, potentially preventing the entire JVM from shutting down</LongDescription>
+ <Details>
+ <![CDATA[
+ <p>Most <code>ExecutorService</code> objects must be explicitly shutdown, otherwise, their internal threads can prevent the JVM from ever shutting down, even when everything else has stopped.</p>
+
+ <p>FindBugs has detected that something like the following is happening:<br/>
+ <code>
+ ExecutorService executor = ... //e.g. Executors.newCachedThreadPool();<br/>
+ ...<br/>
+ public void reset() {<br/>
+ this.executor = Executors.newCachedThreadPool(); <br/>
+ this.executor.execute(new SampleExecutable()); <br/>
+ }<br/>
+ </code>
+ For normal objects, losing the last reference to them like this would trigger the object to be cleaned up
+ in garbage collection. For <code>ExecutorService</code>s, this isn't enough to terminate the internal threads in the
+ thread pool, and the <code>ExecutorService</code> isn't guaranteed to shutdown, causing the JVM to never stop. <br/>
+ To fix this, simply add a call to <code>shutdown()</code> like this:<br/>
+ <code>
+ ExecutorService executor = ... //e.g. Executors.newCachedThreadPool();<br/>
+ ...<br/>
+ public void reset() {<br/>
+ <b> this.executor.shutDown();</b> <br/>
+ this.executor = Executors.newCachedThreadPool(); <br/>
+ this.executor.execute(new SampleExecutable()); <br/>
+ }
+ </code>
+ </p>
+
+ <p>Even though there are some exceptions to this, particularly when a custom <code>ThreadFactory</code> is
+ provided, or for <code>ThreadPoolExecutor</code>s with <code>allowsCoreThreadTimeOut()</code> set to true,
+ it is good practice to explictly shutdown the <code>ExecutorService</code> at the end of execution, or
+ when it is being replaced.</p>
+
+ <p><b>Note:</b> <code>ExecutorService</code>s are generally created once in a program's lifecycle. If you find yourself
+ replacing the <code>ExecutorService</code>, perhaps you may consider restructuring your code to use calls like
+ <code>awaitTermination()</code> or <code>Future</code>s/<code>Callable</code>s to avoid recreating the <code>ExecutorService</code>.</p>
+ ]]>
+ </Details>
+ </BugPattern>
+
+ <BugPattern type="UJM_UNJITABLE_METHOD">
+ <ShortDescription>This method is too long to be compiled by the JIT</ShortDescription>
+ <LongDescription>This method {1} is too long to be compiled by the JIT</LongDescription>
+ <Details>
+ <![CDATA[
+ <p>This method is longer than 8000 bytes. By default the JIT will not attempt to compile this method no matter
+ how hot it is, and so this method will always be interpreted. If performance is important, you should consider
+ breaking this method up in smaller chunks. (And probably a good idea for readability too!).
+ ]]>
+ </Details>
+ </BugPattern>
+
+ <BugPattern type="CTU_CONFLICTING_TIME_UNITS">
+ <ShortDescription>This method performs arithmetic operations on time values with different units</ShortDescription>
+ <LongDescription>This method performs arithmetic operations on time values with different units</LongDescription>
+ <Details>
+ <![CDATA[
+ <p>This method takes two values that appear to be representing time, and performs arithmetic operations on this
+ two values directly, even though it appears that the two values are representing different time units, such as
+ adding a millisecond value to a nanosecond value. You should convert the two values to the same time unit before
+ performing this calculation in order for it to be meaningful.
+ ]]>
+ </Details>
+ </BugPattern>
+
<!-- BugCode -->
<BugCode abbrev="ISB">Inefficient String Buffering</BugCode>
@@ -4074,4 +4256,7 @@
<BugCode abbrev="UP">Unused Parameter</BugCode>
<BugCode abbrev="CD">Circular Dependencies</BugCode>
<BugCode abbrev="MUC">Modifying Unmodifiable Collection</BugCode>
+ <BugCode abbrev="UJM">Unjitable method</BugCode>
+ <BugCode abbrev="HES">Hanging ExecutorService</BugCode>
+ <BugCode abbrev="CTU">Conflicting Time Units</BugCode>
</MessageCollection>
Modified: trunk/fb-contrib/htdocs/index.shtml
===================================================================
--- trunk/fb-contrib/htdocs/index.shtml 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/htdocs/index.shtml 2014-05-24 04:08:28 UTC (rev 1772)
@@ -89,7 +89,8 @@
on the garbage collector.
</li>
<li><b>[AIOB] Array Index Out of Bounds</b><br/>
- Looks for questionable load/stores to array elements.<ul>
+ Looks for questionable load/stores to array elements.
+ <ul>
<li>
Looks for accesses to array elements using literal values that are known to be outside the bounds of the array.
This mistake will cause an ArrayIndexOutOfBoundsException to occur at runtime.</li>
@@ -98,6 +99,17 @@
</li>
</ul>
</li>
+ <li><b>[UJM] Unjitable Methods</b><br/>
+ Looks for methods that are too big that the JIT will not compile them no matter how often they are run
+ </li>
+ <li><b>[HE] Hangable Executors</b><br/>
+ Looks for executors that are never shutdown, which will not allow the application to terminate<br/>
+ <span style="color: #0000FF;">--contributed by Kevin Lubick - THANKS!</span>
+ </li>
+ <li><b>[CTU] Conflicting Time Units</b><br/>
+ Looks for methods that perform arithmetic operations on values representing time
+ where the time unit is incompatible, ie adding a millisecond value to a nanosecond value.
+ </li>
</ul>
</div>
<hr/>
Modified: trunk/fb-contrib/pom.xml
===================================================================
--- trunk/fb-contrib/pom.xml 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/pom.xml 2014-05-24 04:08:28 UTC (rev 1772)
@@ -8,7 +8,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.mebigfatguy.fb-contrib</groupId>
<artifactId>fb-contrib</artifactId>
- <version>5.3.0</version>
+ <version>5.2.1</version>
<parent>
<groupId>org.sonatype.oss</groupId>
Added: trunk/fb-contrib/samples/CTU_Sample.java
===================================================================
--- trunk/fb-contrib/samples/CTU_Sample.java (rev 0)
+++ trunk/fb-contrib/samples/CTU_Sample.java 2014-05-24 04:08:28 UTC (rev 1772)
@@ -0,0 +1,26 @@
+import java.util.concurrent.TimeUnit;
+
+
+public class CTU_Sample {
+
+ public long simpleMillisAndNanos() {
+ long millis = System.currentTimeMillis();
+ long nanos = System.nanoTime();
+
+ return millis + nanos;
+ }
+
+ public long badUseOfConvert() {
+ long secs = TimeUnit.SECONDS.convert(1000, TimeUnit.MILLISECONDS);
+ long millis = System.currentTimeMillis();
+
+ return secs + millis;
+ }
+
+ public long fpHandConversions() {
+ long millis = System.currentTimeMillis();
+ long secs = TimeUnit.SECONDS.convert(1, TimeUnit.HOURS);
+
+ return millis * 1000 + secs;
+ }
+}
Property changes on: trunk/fb-contrib/samples/CTU_Sample.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/fb-contrib/samples/HES_Sample.java
===================================================================
--- trunk/fb-contrib/samples/HES_Sample.java (rev 0)
+++ trunk/fb-contrib/samples/HES_Sample.java 2014-05-24 04:08:28 UTC (rev 1772)
@@ -0,0 +1,370 @@
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
+//Expected bug count: 12
+//5 HE_EXECUTOR_NEVER_SHUTDOWN
+//4 HE_LOCAL_EXECUTOR_SERVICE
+//3 HE_EXECUTOR_OVERWRITTEN_WITHOUT_SHUTDOWN
+public class HES_Sample {
+
+
+ public static void main(String[] args) {
+ LocalExecutorProblem p = new LocalExecutorProblem();
+ p.task();
+
+ System.out.println("Should end");
+ }
+}
+
+class SampleExecutable implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("Hello");
+ }
+
+ //Dummy method with throws to simulate something potentially throwing exception
+ public static void methodThrows() throws Exception{
+ if (Math.random()<.5)
+ throw new Exception("There was a problem with the RNG");
+ }
+
+}
+
+class SingleThreadExecutorProblem {
+ //tag
+ private ExecutorService executor;
+
+ public SingleThreadExecutorProblem() {
+ this.executor = Executors.newSingleThreadExecutor();
+ }
+
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+
+}
+
+class SingleThreadExecutorGood {
+ //no tag
+ private ExecutorService executor;
+
+ public SingleThreadExecutorGood() {
+ this.executor = Executors.newSingleThreadExecutor();
+ }
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+ public void shutDown() {
+ executor.shutdown();
+ }
+}
+class SingleThreadExecutorGood1 {
+ //no tag
+ private ExecutorService executor;
+
+ public SingleThreadExecutorGood1() {
+ this.executor = Executors.newSingleThreadExecutor();
+ }
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+ public void shutDown() {
+ executor.shutdownNow();
+ }
+}
+class SingleThreadExecutorGood2 {
+ //no tag
+ private ExecutorService executor;
+
+ public SingleThreadExecutorGood2() {
+ this.executor = Executors.newSingleThreadExecutor();
+ }
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+ public void shutDown() {
+ try {
+ executor.awaitTermination(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ executor.shutdown();
+ }
+}
+
+class SingleThreadExecutorTryProblem {
+ //this won't get tagged as of version 2.2 If given more thought, this could be implemented
+ private ExecutorService executor;
+
+ public SingleThreadExecutorTryProblem() {
+ this.executor = Executors.newSingleThreadExecutor();
+ }
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+ public void shutDown() {
+ try {
+ executor.awaitTermination(30, TimeUnit.SECONDS);
+ executor.shutdown(); //this doesn't count as shutdown, so it should be tagged.
+ //probably with a different bug
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ }
+}
+
+class FixedThreadPoolProblem {
+ //tag
+ private ExecutorService executor;
+
+ public FixedThreadPoolProblem() {
+ this.executor = Executors.newFixedThreadPool(3);
+ }
+
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+
+}
+
+class CachedThreadPoolMehProblem {
+ //tag - this is bad practice, even though JVM will exit after 60 seconds
+ private ExecutorService executor;
+
+ public CachedThreadPoolMehProblem() {
+ this.executor = Executors.newCachedThreadPool();
+ }
+
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+
+}
+
+class SingleThreadExecutorThreadFactoryMehProblem {
+ //tag - this is bad practice, even though the threads will terminate
+ private ExecutorService executor;
+
+ public SingleThreadExecutorThreadFactoryMehProblem() {
+ this.executor = Executors.newSingleThreadExecutor(new ThreadFactory() {
+ @Override
+ public Thread newThread(Runnable arg0) {
+ Thread t = new Thread(arg0);
+ t.setDaemon(true);
+ return t;
+ }
+ });
+ }
+
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+
+}
+
+class ScheduledThreadPoolProblem {
+ //tag
+ private ExecutorService executor;
+
+ public ScheduledThreadPoolProblem() {
+ this.executor = Executors.newScheduledThreadPool(1);
+ }
+
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+
+}
+
+class ReplacementExecutorProblem {
+ private ExecutorService executor;
+
+ public ReplacementExecutorProblem() {
+ this.executor = Executors.newScheduledThreadPool(1);
+ executor.execute(new SampleExecutable());
+ }
+
+ public void reset() {
+
+ executor.execute(new SampleExecutable());
+ //tag (the old executor won't get picked up for garbage collection)
+ this.executor = Executors.newScheduledThreadPool(1);
+ executor.execute(new SampleExecutable());
+ try {
+ executor.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void test() {
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+
+ public void shutDown() {
+ executor.shutdownNow();
+ }
+
+}
+
+class ReplacementExecutorGood {
+ private ExecutorService executor;
+
+ public ReplacementExecutorGood() {
+ this.executor = Executors.newScheduledThreadPool(1);
+ executor.execute(new SampleExecutable());
+ }
+
+ public void reset() {
+ //no tag
+ this.executor.shutdown();
+
+ this.executor = Executors.newScheduledThreadPool(1);
+ executor.execute(new SampleExecutable());
+ try {
+ executor.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ executor.shutdown();
+ }
+
+}
+
+class ReplacementExecutorBad2 {
+ private ExecutorService executor;
+
+ public ReplacementExecutorBad2() {
+ this.executor = Executors.newScheduledThreadPool(1);
+ executor.execute(new SampleExecutable());
+ }
+
+ public void reset() {
+ //tag, because shutdown in other method isn't forced to be called
+ this.executor = Executors.newScheduledThreadPool(1);
+
+ }
+
+ public void shutDown() {
+ this.executor.shutdown();
+ }
+
+ public void task() {
+ executor.execute(new SampleExecutable());
+ try {
+ executor.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
+
+class ReplacementExecutorGood2 {
+ private ExecutorService executor;
+
+ public ReplacementExecutorGood2() {
+ this.executor = Executors.newScheduledThreadPool(1);
+ executor.execute(new SampleExecutable());
+ }
+
+ public void reset() {
+ System.out.println("Pretest");
+ if (executor == null) {
+ //no tag, the null check indicates some thought that another threadpool won't get left behind
+ this.executor = Executors.newScheduledThreadPool(1);
+ }
+ //tag (this one is no long under the "good graces" of the null check
+ this.executor = Executors.newCachedThreadPool();
+ }
+
+ public void shutDown() {
+ this.executor.shutdown();
+ //no tag
+ this.executor = null;
+ }
+
+ public void task() {
+ executor.execute(new SampleExecutable());
+ try {
+ executor.awaitTermination(1, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
+
+
+class LocalExecutorProblem {
+
+ public void task() {
+ //tag
+ ExecutorService executor = Executors.newSingleThreadExecutor();
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+
+}
+
+class LocalExecutorProblem1 {
+
+ public void task() {
+ //tag
+ ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactory() {
+
+ @Override
+ public Thread newThread(Runnable arg0) {
+ return new Thread(arg0);
+ }
+ });
+ executor.execute(new SampleExecutable());
+ executor.execute(new SampleExecutable());
+ }
+
+}
+
+class LocalExecutorProblem2 {
+
+ public void task() {
+ //tag (checking for mislabeled objects)
+ Object executor = Executors.newCachedThreadPool(new ThreadFactory() {
+
+ @Override
+ public Thread newThread(Runnable arg0) {
+ return new Thread(arg0);
+ }
+ });
+
+ System.out.println(executor);
+ }
+
+}
+
+class LocalExecutorProblem3 {
+
+ public void task() {
+ //tag
+ ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
+
+ System.out.println(executor);
+ }
+
+}
+
+
+
Property changes on: trunk/fb-contrib/samples/HES_Sample.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/fb-contrib/samples/LSYC_Sample.java
===================================================================
--- trunk/fb-contrib/samples/LSYC_Sample.java 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/samples/LSYC_Sample.java 2014-05-24 04:08:28 UTC (rev 1772)
@@ -1,3 +1,4 @@
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
@@ -28,6 +29,12 @@
List<String> a = Collections.synchronizedList(ls);
syncfield = a;
}
+
+ public List<String> getList() {
+ // don't report
+ return Collections.synchronizedList(new ArrayList<String>());
+
+ }
public Map<String, Map<String, String>> test4() {
// report as low
@@ -47,5 +54,10 @@
buffer.append("Findbugs ");
return buffer.toString();
}
+
+ public String printString2() {
+ //no tag, but probably should.
+ return new StringBuffer().append("Hello").append("World").toString();
+ }
}
Modified: trunk/fb-contrib/samples/ROOM_Sample.java
===================================================================
--- trunk/fb-contrib/samples/ROOM_Sample.java 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/samples/ROOM_Sample.java 2014-05-24 04:08:28 UTC (rev 1772)
@@ -1,32 +1,32 @@
import java.lang.reflect.Method;
public class ROOM_Sample {
- public static final Class[] STATIC_NO_ARGS = new Class[0];
- public final Class[] NO_ARGS = new Class[0];
+ public static final Class<?>[] STATIC_NO_ARGS = new Class[0];
+ public final Class<?>[] NO_ARGS = new Class[0];
public void testRoomWithLocals() throws Exception {
- Class c = Class.forName("java.lang.Object");
+ Class<?> c = Class.forName("java.lang.Object");
Method m = c.getMethod("equals", Object.class);
String s = (String) m.invoke(this, new ROOM_Sample());
}
public void testRoomWithField() throws Exception {
- Class c = Class.forName("java.lang.Object");
+ Class<?> c = Class.forName("java.lang.Object");
Method m = c.getMethod("toString", NO_ARGS);
String s = (String) m.invoke(this, (Object[]) null);
}
public void testRoomWithStatic() throws Exception {
- Class c = Class.forName("java.lang.Object");
+ Class<?> c = Class.forName("java.lang.Object");
Method m = c.getMethod("hashCode", STATIC_NO_ARGS);
String s = (String) m.invoke(this, (Object[]) null);
}
public void testRoomWithNull() throws Exception {
- Class c = Class.forName("java.lang.Object");
+ Class<?> c = Class.forName("java.lang.Object");
Method m = c.getMethod("notify", (Class[]) null);
String s = (String) m.invoke(this, (Object[]) null);
Modified: trunk/fb-contrib/samples/samples.fbp
===================================================================
--- trunk/fb-contrib/samples/samples.fbp 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/samples/samples.fbp 2014-05-24 04:08:28 UTC (rev 1772)
@@ -1,15 +1,15 @@
<Project projectName="sample">
- <Jar>././.</Jar>
- <AuxClasspathEntry>./../lib/backport-util-concurrent-3.1.jar</AuxClasspathEntry>
- <AuxClasspathEntry>./../lib/commons-collections-3.2.1.jar</AuxClasspathEntry>
- <AuxClasspathEntry>./../lib/guava-16.0.1.jar</AuxClasspathEntry>
- <AuxClasspathEntry>./../lib/javax.servlet-api-3.1.0.jar</AuxClasspathEntry>
- <AuxClasspathEntry>./../lib/javax.servlet.jsp-api-2.3.1.jar</AuxClasspathEntry>
- <AuxClasspathEntry>./../lib/junit-4.11.jar</AuxClasspathEntry>
- <AuxClasspathEntry>./../lib/log4j-1.2.17.jar</AuxClasspathEntry>
+ <Jar>.</Jar>
+ <AuxClasspathEntry>../lib/backport-util-concurrent-3.1.jar</AuxClasspathEntry>
+ <AuxClasspathEntry>../lib/commons-collections-3.2.1.jar</AuxClasspathEntry>
+ <AuxClasspathEntry>../lib/guava-16.0.1.jar</AuxClasspathEntry>
+ <AuxClasspathEntry>../lib/javax.servlet-api-3.1.0.jar</AuxClasspathEntry>
+ <AuxClasspathEntry>../lib/javax.servlet.jsp-api-2.3.1.jar</AuxClasspathEntry>
+ <AuxClasspathEntry>../lib/junit-4.11.jar</AuxClasspathEntry>
+ <AuxClasspathEntry>../lib/log4j-1.2.17.jar</AuxClasspathEntry>
<AuxClasspathEntry>../lib/commons-lang3-3.3.jar</AuxClasspathEntry>
<AuxClasspathEntry>../lib/slf4j-api-1.7.6.jar</AuxClasspathEntry>
- <SrcDir>././.</SrcDir>
+ <SrcDir>.</SrcDir>
<SuppressionFilter>
<LastVersion value="-1" relOp="NEQ"/>
</SuppressionFilter>
Modified: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/collect/CollectStatistics.java
===================================================================
--- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/collect/CollectStatistics.java 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/collect/CollectStatistics.java 2014-05-24 04:08:28 UTC (rev 1772)
@@ -28,7 +28,6 @@
{
private int numMethodCalls;
- @SuppressWarnings("unused")
public CollectStatistics(BugReporter bugReporter) {
Statistics.getStatistics().clear();
}
Modified: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/debug/OCSDebugger.java
===================================================================
--- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/debug/OCSDebugger.java 2014-04-13 01:38:42 UTC (rev 1771)
+++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/debug/OCSDebugger.java 2014-05-24 04:08:28 UTC (rev 1772)
@@ -42,7 +42,6 @@
private OpcodeStack stack = new OpcodeStack();
private PrintWriter pw = null;
- @SuppressWarnings("unused")
public OCSDebugger(BugReporter bugReporter) {
}
Added: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConflictingTimeUnits.java
===================================================================
--- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConflictingTimeUnits.java (rev 0)
+++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConflictingTimeUnits.java 2014-05-24 04:08:28 UTC (rev 1772)
@@ -0,0 +1,198 @@
+/*
+ * fb-contrib - Auxiliary detectors for Java programs
+ * Copyright (C) 2005-2014 Dave Brosius
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.mebigfatguy.fbcontrib.detect;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.bcel.classfile.Code;
+import org.apache.bcel.generic.Type;
+
+import edu.umd.cs.findbugs.BugInstance;
+import edu.umd.cs.findbugs.BugReporter;
+import edu.umd.cs.findbugs.BytecodeScanningDetector;
+import edu.umd.cs.findbugs.OpcodeStack;
+import edu.umd.cs.findbugs.OpcodeStack.CustomUserValue;
+import edu.umd.cs.findbugs.ba.ClassContext;
+
+/**
+ * looks for methods that perform arithmetic operations on values representing time
+ * where the time unit is incompatible, ie adding a millisecond value to a nanosecond value.
+ */
+@CustomUserValue
+public class ConflictingTimeUnits extends BytecodeScanningDetector {
+
+ private enum Units { NANOS, MICROS, MILLIS, SECONDS, MINUTES, HOURS, DAYS, CALLER };
+
+ private static Map<String, Units> TIME_UNIT_GENERATING_METHODS = new HashMap<String, Units>();
+ static {
+ TIME_UNIT_GENERATING_METHODS.put("java/lang/System.currentTimeMillis()J", Units.MILLIS);
+ TIME_UNIT_GENERATING_METHODS.put("java/lang/System.nanoTime()J", Units.NANOS);
+ TIME_UNIT_GENERATING_METHODS.put("java/sql/Timestamp.getTime()J", Units.MILLIS);
+ TIME_UNIT_GENERATING_METHODS.put("java/sql/Timestamp.getNanos()I", Units.NANOS);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/Date.getTime()J", Units.MILLIS);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/concurrent/TimeUnit.toNanos(J)J", Units.NANOS);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/concurrent/TimeUnit.toMicros(J)J", Units.MICROS);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/concurrent/TimeUnit.toSeconds(J)J", Units.SECONDS);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/concurrent/TimeUnit.toMinutes(J)J", Units.MINUTES);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/concurrent/TimeUnit.toHours(J)J", Units.HOURS);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/concurrent/TimeUnit.toDays(J)J", Units.DAYS);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/concurrent/TimeUnit.excessNanos(JJ)I", Units.NANOS);
+ TIME_UNIT_GENERATING_METHODS.put("java/util/concurrent/TimeUnit.convert(JLjava/util/concurrent/TimeUnit;)J", Units.CALLER);
+ TIME_UNIT_GENERATING_METHODS.put("edu/emory/matchcs/backport/java/util/concurrent/TimeUnit.toNanos(J)J", Units.NANOS);
+ TIME_UNIT_GENERATING_METHODS.put("edu/emory/matchcs/backport/java/util/concurrent/TimeUnit.toMicros(J)J", Units.MICROS);
+ TIME_UNIT_GENERATING_METHODS.put("edu/emory/matchcs/backport/java/util/concurrent/TimeUnit.toSeconds(J)J", Units.SECONDS);
+ TIME_UNIT_GENERATING_METHODS.put("edu/emory/matchcs/backport/java/util/concurrent/TimeUnit.toMinutes(J)J", Units.MINUTES);
+ TIME_UNIT_GENERATING_METHODS.put("edu/emory/matchcs/backport/java/util/concurrent/TimeUnit.toHours(J)J", Units.HOURS);
+ TIME_UNIT_GENERATING_METHODS.put("edu/emory/matchcs/backport/java/util/concurrent/TimeUnit.toDays(J)J", Units.DAYS);
+ TIME_UNIT_GENERATING_METHODS.put("edu/emory/matchcs/backport/java/util/concurrent/TimeUnit.excessNanos(JJ)I", Units.NANOS);
+ TIME_UNIT_GENERATING_METHODS.put("edu/emory/matchcs/backport/java/util/concurrent/TimeUnit.convert(JLjava/util/concurrent/TimeUnit;)J", Units.CALLER);
+ TIME_UNIT_GENERATING_METHODS.put("org/joda/time/base/BaseDuration.getMillis()J", Units.MILLIS);
+ TIME_UNIT_GENERATING_METHODS.put("org/joda/time/base/BaseInterval.getEndMillis()J", Units.MILLIS);
+ TIME_UNIT_GENERATING_METHODS.put("org/joda/time/base/BaseInterval.getStartMillis()J", Units.MILLIS);
+ }
+
+ private static Map<String, Units> TIMEUNIT_TO_UNITS = new HashMap<String, Units>();
+ static {
+ TIMEUNIT_TO_UNITS.put("NANOSECONDS", Units.NANOS);
+ TIMEUNIT_TO_UNITS.put("MICROSECONDS", Units.MICROS);
+ TIMEUNIT_TO_UNITS.put("MILLISECONDS", Units.MILLIS);
+ TIMEUNIT_TO_UNITS.put("SECONDS", Units.SECONDS);
+ TIMEUNIT_TO_UNITS.put("MINUTES", Units.MINUTES);
+ TIMEUNIT_TO_UNITS.put("HOURS", Units.HOURS);
+ TIMEUNIT_TO_UNITS.put("DAYS", Units.DAYS);
+ }
+
+ private BugReporter bugReporter;
+ private OpcodeStack stack;
+
+ /**
+ * constructs a CTU detector given the reporter to report bugs on
+ * @param bugReporter the sync of bug reports
+ */
+ public ConflictingTimeUnits(BugReporter bugReporter) {
+ this.bugReporter = bugReporter;
+ }
+
+ /**
+ * overrides the visitor to reset the stack
+ *
+ * @param classContext the context object of the currently parsed class
+ */
+ @Override
+ public void visitClassContext(ClassContext classContext) {
+ try {
+ stack = new OpcodeStack();
+ super.visitClassContext(classContext);
+ } finally {
+ stack = null;
+ }
+ }
+
+ /**
+ * overrides the visitor to resets the stack for this method.
+ *
+ * @param obj the context object for the currently parsed code block
+ */
+ @Override
+ public void visitCode(Code obj) {
+ stack.resetForMethodEntry(this);
+ super.visitCode(obj);
+ }
+
+ /**
+ * overrides the visitor to look for operations on two time unit values that are conflicting
+ */
+ @Override
+ public void sawOpcode(int seen) {
+ Units unit = null;
+ try {
+ stack.precomputation(this);
+
+ switch (seen) {
+ case INVOKEVIRTUAL:
+ case INVOKEINTERFACE:
+ case INVOKESTATIC:
+ String methodCall = getClassConstantOperand() + "." + getNameConstantOperand() + getSigConstantOperand();
+ unit = TIME_UNIT_GENERATING_METHODS.get(methodCall);
+ if (unit == Units.CALLER) {
+ int offset = Type.getArgumentTypes(getSigConstantOperand()).length;
+ if (stack.getStackDepth() > offset) {
+ OpcodeStack.Item item = stack.getStackItem(offset);
+ unit = (Units) item.getUserValue();
+ } else {
+ unit = null;
+ }
+ }
+ break;
+
+ case GETSTATIC:
+ String clsName = getClassConstantOperand();
+ if ("java/util/concurrent/TimeUnit".equals(clsName)
+ || "edu/emory/matchcs/backport/java/util/concurrent/TimeUnit".equals(clsName)) {
+ unit = TIMEUNIT_TO_UNITS.get(getNameConstantOperand());
+ }
+ break;
+
+
+ case L2I:
+ case I2L:
+ if (stack.getStackDepth() > 0) {
+ OpcodeStack.Item item = stack.getStackItem(0);
+ unit = (Units) item.getUserValue();
+ }
+ break;
+
+ case IADD:
+ case ISUB:
+ case IMUL:
+ case IDIV:
+ case IREM:
+ case LADD:
+ case LSUB:
+ case LMUL:
+ case LDIV:
+ case LREM:
+ if (stack.getStackDepth() > 1) {
+ OpcodeStack.Item arg1 = stack.getStackItem(0);
+ OpcodeStack.Item arg2 = stack.getStackItem(1);
+
+ Units u1 = (Units) arg1.getUserValue();
+ Units u2 = (Units) arg2.getUserValue();
+
+ if ((u1 != null) && (u2 != null) && (u1 != u2)) {
+ bugReporter.reportBug(new BugInstance(this, "CTU_CONFLICTING_TIME_UNITS", NORMAL_PRIORITY)
+ .addClass(this)
+ .addMethod(this)
+ .addSourceLine(this)
+ .addString(u1.toString())
+ .addString(u2.toString()));
+ }
+ }
+ break;
+ }
+ } finally {
+ stack.sawOpcode(this, seen);
+ if ((unit != null) && (stack.getStackDepth() > 0)) {
+ OpcodeStack.Item item = stack.getStackItem(0);
+ item.setUserValue(unit);
+ }
+ }
+ }
+}
Property changes on: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/ConflictingTimeUnits.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/HangingExecutors.java
===================================================================
--- trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/HangingExecutors.java (rev 0)
+++ trunk/fb-contrib/src/com/mebigfatguy/fbcontrib/detect/HangingExecutors.java 2014-05-24 04:08:28 UTC (rev 1772)
@@ -0,0 +1,312 @@
+/*
+ * fb-contrib - Auxiliary detectors for Java programs
+ * Copyright (C) 2005-2014 Kevin Lubick
+ * Copyright (C) 2005-2014 Dave Brosius
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package com.mebigfatguy.fbcontrib.detect;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.bcel.Constants;
+import org.apache.bcel.classfile.Code;
+import org.apache.bcel.classfile.Field;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.generic.Type;
+
+import edu.umd.cs.findbugs.BugInstance;
+import edu.umd.cs.findbugs.BugReporter;
+import edu.umd.cs.findbugs.BytecodeScanningDetector;
+import edu.umd.cs.findbugs.Detector;
+import edu.umd.cs.findbugs.FieldAnnotation;
+import edu.umd.cs.findbugs.OpcodeStack;
+import edu.umd.cs.findbugs.Priorities;
+import edu.umd.cs.findbugs.ba.ClassContext;
+import edu.umd.cs.findbugs.ba.XFactory;
+import edu.umd.cs.findbugs.ba.XField;
+
+/**
+ * looks for executors that are never shutdown, which will not allow the application to terminate
+ */
+public class HangingExecutors extends BytecodeScanningDetector {
+
+ private static final Set<String> hangableSig = new HashSet<String>();
+
+ static {
+ hangableSig.add("Ljava/util/concurrent/ExecutorService;");
+ }
+
+
+ private static final Set<String> shutdownMethods = new HashSet<String>();
+
+ static {
+ shutdownMethods.add("shutdown");
+ shutdownMethods.add("shutdownNow");
+ }
+
+
+ private final BugReporter bugReporter;
+ private Map<XField, FieldAnnotation> hangingFieldCandidates;
+ private Map<XField, Integer> exemptExecutors;
+ private OpcodeStack stack;
+ private String methodName;
+
+ private LocalHangingExecutor localHEDetector;
+
+
+
+ public HangingExecutors(BugReporter reporter) {
+ this.bugReporter=reporter;
+ this.localHEDetector = new LocalHangingExecutor(this, reporter);
+ }
+
+
+ /**
+ * finds ExecutorService objects that don't get a call to the terminating methods,
+ * and thus, never appear to be shutdown properly (the threads exist until shutdown is called)
+ *
+ * @param classContext the class context object of the currently parsed java class
+ */
+ @Override
+ public void visitClassContext(ClassContext classContext) {
+ localHEDetector.visitClassContext(classContext);
+ try {
+ hangingFieldCandidates = new HashMap<XField, FieldAnnotation>();
+ exemptExecutors = new HashMap<XField, Integer>();
+ parseFieldsForHangingCandidates(classContext);
+
+ if (!hangingFieldCandidates.isEmpty()) {
+ stack = new OpcodeStack();
+ super.visitClassContext(classContext);
+
+ reportHangingExecutorFieldBugs();
+ }
+ } finally {
+ stack = null;
+ hangingFieldCandidates = null;
+ exemptExecutors = null;
+ }
+
+ }
+
+ private void parseFieldsForHangingCandidates(ClassContext classContext) {
+ JavaClass cls = classContext.getJavaClass();
+ Field[] fields = cls.getFields();
+ for (Field f : fields) {
+ String sig = f.getSignature();
+ if (hangableSig.contains(sig)) {
+ hangingFieldCandidates.put(XFactory.createXField(cls.getClassName(), f.getName(), f.getSignature(), f.isStatic()), FieldAnnotation.fromBCELField(cls, f));
+ }
+ }
+ }
+
+ private void reportHangingExecutorFieldBugs() {
+ for (Entry<XField, FieldAnnotation> entry : hangingFieldCandidates.entrySet()) {
+ FieldAnnotation fieldAn = entry.getValue();
+ if (fieldAn != null) {
+ bugReporter.reportBug(new BugInstance(this, "HES_EXECUTOR_NEVER_SHUTDOWN", NORMAL_PRIORITY)
+ .addClass(this)
+ .addField(fieldAn)
+ .addField(entry.getKey()));
+ }
+ }
+ }
+
+ /**
+ * implements the visitor to reset the opcode stack
+ *
+ * @param obj the context object of the currently parsed code block
+ */
+ @Override
+ public void visitCode(Code obj) {
+ stack.resetForMethodEntry(this);
+ exemptExecutors.clear();
+ if ("<clinit>".equals(methodName) || "<init>".equals(methodName))
+ return;
+
+ if (!hangingFieldCandidates.isEmpty())
+ super.visitCode(obj);
+ }
+
+ /**
+ * implements the visitor to collect the method name
+ *
+ * @param obj the context object of the currently parsed method
+ */
+ @Override
+ public void visitMethod(Method obj) {
+ methodName = obj.getName();
+ }
+
+ /**
+ * Browses for calls to shutdown() and shutdownNow(), and if they happen, remove
+ * the hanging candidate, as there is a chance it will be called.
+ *
+ * @param seen the opcode of the currently parsed instruction
+ */
+ @Override
+ public void sawOpcode(int seen) {
+ try {
+ stack.precomputation(this);
+
+ if ((seen == INVOKEVIRTUAL) || (seen == INVOKEINTERFACE)) {
+ String sig = getSigConstantOperand();
+ int argCount = Type.getArgumentTypes(sig).length;
+ if (stack.getStackDepth() > argCount) {
+ OpcodeStack.Item invokeeItem = stack.getStackItem(argCount);
+ XField fieldOnWhichMethodIsInvoked = invokeeItem.getXField();
+ if (fieldOnWhichMethodIsInvoked != null) {
+ removeCandidateIfShutdownCalled(fieldOnWhichMethodIsInvoked);
+ addExemptionIfShutdownCalled(fieldOnWhichMethodIsInvoked);
+ }
+ }
+ }
+ //TODO Should not include private methods
+ else if (seen == ARETURN) {
+ removeFieldsThatGetReturned();
+ }
+ else if (seen == PUTFIELD) {
+ XField f = getXFieldOperand();
+ if ("Ljava/util/concurrent/ExecutorService;".equals(f.getSignature()) && !checkException(f)) {
+ bugReporter.reportBug(new BugInstance(this, "HES_EXECUTOR_OVERWRITTEN_WITHOUT_SHUTDOWN", Priorities.HIGH_PRIORITY)
+ .addClass(this)
+ .addMethod(this)
+ .addField(f)
+ .addSourceLine(this));
+ }
+ //after it's been replaced, it no longer uses its exemption.
+ exemptExecutors.remove(f);
+ }
+ else if (seen == IFNONNULL) {
+ OpcodeStack.Item nullCheckItem = stack.getStackItem(0);
+ XField fieldWhichWasNullChecked = nullCheckItem.getXField();
+ if (fieldWhichWasNullChecked != null) {
+ exemptExecutors.put(fieldWhichWasNullChecked, getPC() + getBranchOffset());
+ }
+ }
+ }
+ finally {
+ stack.sawOpcode(this, seen);
+ }
+ }
+
+
+ private boolean checkException(XField f) {
+ if (!exemptExecutors.containsKey(f))
+ return false;
+ int i = exemptExecutors.get(f).intValue();
+
+ return i == -1 || getPC() < i;
+ }
+
+ private void removeFieldsThatGetReturned() {
+ if (stack.getStackDepth() > 0) {
+ OpcodeStack.Item returnItem = stack.getStackItem(0);
+ XField field = returnItem.getXField();
+ if (field != null) {
+ hangingFieldCandidates.remove(field);
+ }
+ }
+ }
+
+ private void addExemptionIfShutdownCalled(XField fieldOnWhichMethodIsInvoked) {
+ String methodBeingInvoked = getNameConstantOperand();
+ if (shutdownMethods.contains(methodBeingInvoked)) {
+ exemptExecutors.put(fieldOnWhichMethodIsInvoked, -1);
+ }
+ }
+
+
+ private void removeCandidateIfShutdownCalled(XField fieldOnWhichMethodIsInvoked) {
+ if (hangingFieldCandidates.containsKey(fieldOnWhichMethodIsInvoked)) {
+ String methodBeingInvoked = getNameConstantOperand();
+ if (shutdownMethods.contains(methodBeingInvoked)) {
+ hangingFieldCandidates.remove(fieldOnWhichMethodIsInvoked);
+ }
+ }
+ }
+
+}
+
+
+class LocalHangingExecutor extends LocalTypeDetector {
+
+ private static final Map<String, Set<String>> watchedClassMethods = new HashMap<String, Set<String>>();
+ private static final Map<String, Integer> syncCtors = new HashMap<String, Integer>();
+ static {
+ Set<String> forExecutors = new HashSet<String>();
+ forExecutors.add("newCachedThreadPool");
+ forExecutors.add("newFixedThreadPool");
+ forExecutors.add("newScheduledThreadPool");
+ forExecutors.add("newSingleThreadExecutor");
+
+
+ watchedClassMethods.put("java/util/concurrent/Executors", forExecutors);
+
+
+ syncCtors.put("java/util/concurrent/ThreadPoolExecutor", Integer.valueOf(Constants.MAJOR_1_5));
+ syncCtors.put("java/util/concurrent/ScheduledThreadPoolExecutor", Integer.valueOf(Constants.MAJOR_1_5));
+ }
+
+ private BugReporter bugReporter;
+ private Detector delegatingDetector;
+
+ public LocalHangingExecutor(Detector delegatingDetector, BugReporter reporter) {
+ this.bugReporter = reporter;
+ this.delegatingDetector = delegatingDetector;
+ }
+
+ @Override
+ protected Map<String, Integer> getWatchedConstructors() {
+ return syncCtors;
+ }
+
+ @Override
+ protected Map<String, Set<String>> getWatchedClassMethods() {
+ return watchedClassMethods;
+ }
+
+ @Override
+ protected void reportBug(RegisterInfo cri) {
+ //very important to report the bug under the top, parent detector, otherwise it gets filtered out
+ bugReporter.reportBug(new BugInstance(delegatingDetector, "HES_LOCAL_EXECUTOR_SERVICE", Priorities.HIGH_PRIORITY)
+ .addClass(this)
+ .addMethod(this)
+ .addSourceLine(cri.getSourceLineAnnotation()));
+
+
+ }
+ @Override
+ public void visitClassContext(ClassContext classContext) {
+ super.visi...
[truncated message content] |