1 19 20 package edu.umd.cs.findbugs; 21 22 import java.io.DataInputStream ; 23 import java.io.File ; 24 import java.io.FileFilter ; 25 import java.io.FileInputStream ; 26 import java.io.IOException ; 27 import java.io.InputStream ; 28 import java.lang.reflect.Constructor ; 29 import java.net.URL ; 30 import java.net.URLConnection ; 31 import java.util.ArrayList ; 32 import java.util.Collection ; 33 import java.util.HashMap ; 34 import java.util.HashSet ; 35 import java.util.Iterator ; 36 import java.util.LinkedList ; 37 import java.util.List ; 38 import java.util.Map ; 39 import java.util.Set ; 40 import java.util.StringTokenizer ; 41 import java.util.zip.ZipEntry ; 42 import java.util.zip.ZipInputStream ; 43 44 import org.apache.bcel.Repository; 45 import org.apache.bcel.classfile.ClassFormatException; 46 import org.apache.bcel.classfile.ClassParser; 47 import org.apache.bcel.classfile.JavaClass; 48 import org.apache.bcel.util.ClassPath; 49 50 import edu.umd.cs.findbugs.annotations.SuppressWarnings; 51 import edu.umd.cs.findbugs.ba.AbstractClassMember; 52 import edu.umd.cs.findbugs.ba.AnalysisContext; 53 import edu.umd.cs.findbugs.ba.AnalysisException; 54 import edu.umd.cs.findbugs.ba.AnalysisFeatures; 55 import edu.umd.cs.findbugs.ba.ClassContext; 56 import edu.umd.cs.findbugs.ba.URLClassPath; 57 import edu.umd.cs.findbugs.ba.URLClassPathRepository; 58 import edu.umd.cs.findbugs.classfile.ClassDescriptor; 59 import edu.umd.cs.findbugs.classfile.IClassObserver; 60 import edu.umd.cs.findbugs.config.AnalysisFeatureSetting; 61 import edu.umd.cs.findbugs.config.CommandLine; 62 import edu.umd.cs.findbugs.config.UserPreferences; 63 import edu.umd.cs.findbugs.config.CommandLine.HelpRequestedException; 64 import edu.umd.cs.findbugs.filter.Filter; 65 import edu.umd.cs.findbugs.filter.FilterException; 66 import edu.umd.cs.findbugs.plan.AnalysisPass; 67 import edu.umd.cs.findbugs.plan.ExecutionPlan; 68 import edu.umd.cs.findbugs.plan.OrderingConstraintException; 69 import edu.umd.cs.findbugs.util.Archive; 70 import edu.umd.cs.findbugs.util.ClassName; 71 import edu.umd.cs.findbugs.visitclass.Constants2; 72 73 81 public class FindBugs implements Constants2, ExitCodes, IFindBugsEngine { 82 85 86 90 private static class NoCloseInputStream extends DataInputStream { 91 95 public NoCloseInputStream(InputStream in) { 96 super(in); 97 } 98 99 @Override 100 public void close() { 101 } 102 } 103 104 108 private static class ArchiveWorkListItem { 109 private String fileName; 110 private boolean explicit; 111 112 120 public ArchiveWorkListItem(String fileName, boolean explicit) { 121 this.fileName = fileName; 122 this.explicit = explicit; 123 } 124 125 128 public String getFileName() { 129 return fileName; 130 } 131 132 136 public boolean isExplicit() { 137 return explicit; 138 } 139 } 140 141 144 private interface ClassProducer { 145 152 public JavaClass getNextClass() throws IOException , InterruptedException ; 153 154 157 public boolean containsSourceFiles(); 158 159 163 public long getLastModificationTime(); 164 167 public void close(); 168 } 169 170 173 private class SingleClassProducer implements ClassProducer { 174 private URL url; 175 long time = 0; 176 181 public SingleClassProducer(URL url) { 182 this.url = url; 183 } 184 185 public JavaClass getNextClass() throws IOException , InterruptedException { 186 if (url == null) 187 return null; 188 if (Thread.interrupted()) 189 throw new InterruptedException (); 190 191 URL urlToParse = url; 192 url = null; 194 if (!classScreener.matches(urlToParse.toString())) 196 return null; 197 198 try { 199 URLConnection u = urlToParse.openConnection(); 200 time = u.getLastModified(); 201 return parseFromStream(u.getInputStream(), urlToParse.toString()); 202 } catch (ClassFormatException e) { 203 throw new ClassFormatException("Invalid class file format for " + 204 urlToParse.toString() + ": " + e.getMessage()); 205 } 206 } 207 208 209 public boolean containsSourceFiles() { 210 return false; 211 } 212 213 public void close() { 214 } 216 217 220 public long getLastModificationTime() { 221 return time; 222 } 223 } 224 225 228 private class ZipClassProducer implements ClassProducer { 229 private URL url; 230 private LinkedList <ArchiveWorkListItem> archiveWorkList; 231 private List <String > additionalAuxClasspathEntryList; 232 private ZipInputStream zipInputStream; 233 private boolean containsSourceFiles; 234 private long time = 0; 235 private long zipTime = 0; 236 237 public ZipClassProducer(URL url, LinkedList <ArchiveWorkListItem> archiveWorkList, 238 List <String > additionalAuxClasspathEntryList) 239 throws IOException { 240 this.url = url; 241 this.archiveWorkList = archiveWorkList; 242 this.additionalAuxClasspathEntryList = additionalAuxClasspathEntryList; 243 if (DEBUG) System.out.println("Opening jar/zip input stream for " + url.toString()); 244 URLConnection u = url.openConnection(); 245 this.zipTime = u.getLastModified(); 246 this.zipInputStream = new ZipInputStream (u.getInputStream()); 247 this.containsSourceFiles = false; 248 } 249 250 251 public JavaClass getNextClass() throws IOException , InterruptedException { 252 for (;;) { 253 if (Thread.interrupted()) 254 throw new InterruptedException (); 255 256 ZipEntry zipEntry = zipInputStream.getNextEntry(); 257 if (zipEntry == null) 258 return null; 259 260 try { 261 String entryName = zipEntry.getName(); 262 263 if (!classScreener.matches(entryName)) { 265 if (!additionalAuxClasspathEntryList.contains(url.toString())) { 267 additionalAuxClasspathEntryList.add(url.toString()); 269 } 270 continue; 271 } 272 273 String fileExtension = URLClassPath.getFileExtension(entryName); 274 if (fileExtension != null) { 275 if (fileExtension.equals(".class")) { 276 long modTime = zipEntry.getTime(); 277 if (modTime > time) time = modTime; 278 return parseClass(url.toString(), new NoCloseInputStream(zipInputStream), entryName); 279 } else if (Archive.ARCHIVE_EXTENSION_SET.contains(fileExtension)) { 280 if (url.toString().indexOf("!/") < 0) { 282 ArchiveWorkListItem nestedItem = 283 new ArchiveWorkListItem("jar:" + url.toString() + "!/" + entryName, false); 284 archiveWorkList.addFirst(nestedItem); 285 } 286 } else if (fileExtension.equals(".java")) { 287 containsSourceFiles = true; 288 } 289 } 290 } finally { 291 zipInputStream.closeEntry(); 292 } 293 } 294 } 295 296 public boolean containsSourceFiles() { 297 return containsSourceFiles; 298 } 299 300 public void close() { 301 if (zipInputStream != null) { 302 try { 303 zipInputStream.close(); 304 } catch (IOException ignore) { 305 } 307 } 308 } 309 static final long millisecondsInAYear = 31556926000L; 310 313 public long getLastModificationTime() { 314 if (time + millisecondsInAYear > zipTime) return time; 315 return zipTime; 316 } 317 } 318 319 323 private class DirectoryClassProducer implements ClassProducer { 324 private String dirName; 325 private List <String > additionalAuxClasspathEntryList; 326 private Iterator <String > rfsIter; 327 private boolean containsSourceFiles; 328 private long time; 329 330 public DirectoryClassProducer(String dirName, 331 List <String > additionalAuxClasspathEntryList) throws InterruptedException { 332 this.dirName = dirName; 333 this.additionalAuxClasspathEntryList = additionalAuxClasspathEntryList; 334 335 FileFilter filter = new FileFilter () { 336 public boolean accept(File file) { 337 String fileName = file.getName(); 338 if (file.isDirectory() || fileName.endsWith(".class")) 339 return true; 340 if (fileName.endsWith(".java")) 341 containsSourceFiles = true; 342 return false; 343 } 344 }; 345 346 RecursiveFileSearch rfs = new RecursiveFileSearch(dirName, filter).search(); 349 this.rfsIter = rfs.fileNameIterator(); 350 this.containsSourceFiles = false; 351 } 352 353 public JavaClass getNextClass() throws IOException , InterruptedException { 354 String fileName; 355 for (;;) { 356 if (!rfsIter.hasNext()) 357 return null; 358 fileName = rfsIter.next(); 359 if (classScreener.matches(fileName)) { 360 break; 361 } else { 362 String dirURL= "file:" + dirName; 364 if (!additionalAuxClasspathEntryList.contains(dirURL)) { 365 additionalAuxClasspathEntryList.add(dirURL); 367 } 368 } 369 } 370 try { 371 long modTime = new File (fileName).lastModified(); 372 if (time < modTime) time = modTime; 373 return parseClass(new URL ("file:" + fileName)); 374 } catch (ClassFormatException e) { 375 throw new ClassFormatException("Invalid class file format for " + 376 fileName + ": " + e.getMessage()); 377 } 378 } 379 380 public boolean containsSourceFiles() { 381 return containsSourceFiles; 382 } 383 384 public void close() { 385 } 387 388 391 public long getLastModificationTime() { 392 return time; 393 } 394 } 395 396 public static final AnalysisFeatureSetting[] MIN_EFFORT = new AnalysisFeatureSetting[]{ 397 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, true), 398 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, false), 399 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, false), 400 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true), 401 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, false), 402 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false), 403 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false), 404 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false), 405 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), 406 }; 407 408 public static final AnalysisFeatureSetting[] LESS_EFFORT = new AnalysisFeatureSetting[]{ 409 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false), 410 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true), 411 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true), 412 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true), 413 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true), 414 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false), 415 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false), 416 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false), 417 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), 418 }; 419 420 public static final AnalysisFeatureSetting[] DEFAULT_EFFORT = new AnalysisFeatureSetting[]{ 421 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false), 422 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true), 423 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true), 424 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true), 425 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true), 426 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true), 427 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true), 428 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true), 429 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), 430 }; 431 432 public static final AnalysisFeatureSetting[] MORE_EFFORT = new AnalysisFeatureSetting[]{ 433 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false), 434 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true), 435 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true), 436 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true), 437 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true), 438 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true), 439 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true), 440 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true), 441 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), 442 }; 443 public static final AnalysisFeatureSetting[] MAX_EFFORT = new AnalysisFeatureSetting[]{ 444 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false), 445 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true), 446 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true), 447 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, false), 448 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true), 449 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true), 450 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true), 451 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true), 452 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, true), 453 }; 454 455 public static final boolean DEBUG = SystemProperties.getBoolean("findbugs.debug"); 456 public static final boolean TIMEDEBUG = SystemProperties.getBoolean("findbugs.time"); 457 public static final int TIMEQUANTUM = SystemProperties.getInteger("findbugs.time.quantum", 1000); 458 459 462 private static String home; 463 464 469 static public final Set <String > knownURLProtocolSet = new HashSet <String >(); 470 static { 471 knownURLProtocolSet.add("file"); 472 knownURLProtocolSet.add("http"); 473 knownURLProtocolSet.add("https"); 474 knownURLProtocolSet.add("jar"); 475 } 476 477 private ErrorCountingBugReporter bugReporter; 478 private boolean relaxedReportingMode; 479 private Project project; 480 private DetectorFactoryCollection detectorFactoryCollection; 481 private UserPreferences userPreferences; 482 private List <IClassObserver> classObserverList; 483 private ExecutionPlan executionPlan; 484 private FindBugsProgress progressCallback; 485 private IClassScreener classScreener; 486 private AnalysisContext analysisContext; 487 private String currentClass; 488 private Map <String ,Long > detectorTimings; 489 private boolean useTrainingInput; 490 private boolean emitTrainingOutput; 491 private String trainingInputDir; 492 private String trainingOutputDir; 493 private AnalysisFeatureSetting[] settingList = DEFAULT_EFFORT; 494 private String releaseName; 495 496 private int passCount; 497 private String sourceInfoFile; 498 499 502 503 508 public FindBugs() { 509 510 this.relaxedReportingMode = false; 511 512 this.classObserverList = new LinkedList <IClassObserver>(); 513 514 this.progressCallback = new NoOpFindBugsProgress(); 516 517 this.classScreener = new ClassScreener(); 519 } 520 521 530 public FindBugs(BugReporter bugReporter, Project project) { 531 this(); 532 533 if (bugReporter == null) 534 throw new IllegalArgumentException ("null bugReporter"); 535 if (project == null) 536 throw new IllegalArgumentException ("null project"); 537 538 setBugReporter(bugReporter); 539 setProject(project); 540 } 541 542 545 public void setDetectorFactoryCollection(DetectorFactoryCollection detectorFactoryCollection) { 546 this.detectorFactoryCollection = detectorFactoryCollection; 547 } 548 549 552 public BugReporter getBugReporter() { 553 return bugReporter; 554 } 555 556 559 public void setBugReporter(BugReporter bugReporter) { 560 this.bugReporter = new ErrorCountingBugReporter(bugReporter); 561 addClassObserver(bugReporter); 562 } 563 564 567 public void setProject(Project project) { 568 this.project = project.duplicate(); 569 } 570 571 574 public Project getProject() { 575 return project; 576 } 577 578 581 public void setProgressCallback(FindBugsProgress progressCallback) { 582 this.progressCallback = progressCallback; 583 } 584 585 588 public void addFilter(String filterFileName, boolean include) throws IOException , FilterException { 589 configureFilter(bugReporter, filterFileName, include); 590 } 591 592 595 public void setUserPreferences(UserPreferences userPreferences) { 596 this.userPreferences = userPreferences; 597 } 598 599 602 public void addClassObserver(IClassObserver classObserver) { 603 classObserverList.add(classObserver); 604 } 605 606 609 public void setClassScreener(IClassScreener classScreener) { 610 this.classScreener = classScreener; 611 } 612 613 616 public void setRelaxedReportingMode(boolean relaxedReportingMode) { 617 this.relaxedReportingMode = relaxedReportingMode; 618 } 619 620 623 public void enableTrainingOutput(String trainingOutputDir) { 624 this.emitTrainingOutput = true; 625 this.trainingOutputDir = trainingOutputDir; 626 } 627 628 631 public void enableTrainingInput(String trainingInputDir) { 632 this.useTrainingInput = true; 633 this.trainingInputDir = trainingInputDir; 634 } 635 636 639 public void setAnalysisFeatureSettings(AnalysisFeatureSetting[] settingList) { 640 if (settingList != null) 641 this.settingList = settingList; 642 } 643 644 647 public String getReleaseName() { 648 return releaseName; 649 } 650 651 654 public void setReleaseName(String releaseName) { 655 this.releaseName = releaseName; 656 } 657 658 661 public void setSourceInfoFile(String sourceInfoFile) { 662 this.sourceInfoFile = sourceInfoFile; 663 } 664 665 668 public void execute() throws java.io.IOException , InterruptedException { 669 analysisContext = AnalysisContext.create(bugReporter); 671 analysisContext.setSourcePath(project.getSourceDirList()); 673 if (sourceInfoFile != null) { 674 analysisContext.getSourceInfoMap().read(new FileInputStream (sourceInfoFile)); 675 } 676 677 FindBugsAnalysisFeatures.setRelaxedMode(relaxedReportingMode); 679 680 configureTrainingDatabases(this); 682 683 configureAnalysisFeatures(); 685 686 configureBugCollection(this); 688 689 try { 691 createExecutionPlan(); 692 } catch (OrderingConstraintException e) { 693 IOException ioe = new IOException ("Invalid detector ordering constraints"); 694 ioe.initCause(e); 695 throw ioe; 696 } 697 698 analysisContext.clearRepository(); 700 701 LinkedList <ArchiveWorkListItem> archiveWorkList = new LinkedList <ArchiveWorkListItem>(); 703 for (String fileName : project.getFileList()) { 704 archiveWorkList.add(new ArchiveWorkListItem(fileName, true)); 705 } 706 707 progressCallback.reportNumberOfArchives(archiveWorkList.size()); 710 711 List <String > repositoryClassList = new LinkedList <String >(); 713 714 setRepositoryClassPath(); 716 717 List <String > additionalAuxClasspathEntryList = new LinkedList <String >(); 722 723 while (!archiveWorkList.isEmpty()) { 725 ArchiveWorkListItem item = archiveWorkList.removeFirst(); 726 scanArchiveOrDirectory(item, archiveWorkList, repositoryClassList, 727 additionalAuxClasspathEntryList); 728 } 729 730 addCollectionToClasspath(additionalAuxClasspathEntryList); 733 734 analysisContext.initDatabases(); 736 737 742 if (DEBUG) 743 detectorTimings = new HashMap <String ,Long >(); 744 745 Iterator <AnalysisPass> i = executionPlan.passIterator(); 746 if (i.hasNext()) { 747 AnalysisPass firstPass = i.next(); 748 Set <JavaClass> allReferencedClasses = analysisContext.getSubtypes().getAllClasses(); 750 ArrayList <String > listOfReferencedClasses = new ArrayList <String >(allReferencedClasses.size()); 751 for(JavaClass c : allReferencedClasses) 752 listOfReferencedClasses.add(c.getClassName()); 753 executeAnalysisPass(firstPass, listOfReferencedClasses); 754 755 analysisContext.clearClassContextCache(); 756 } 757 else if (DEBUG) System.err.println("execution plan has no passes"); 758 759 760 while (i.hasNext()) { 762 AnalysisPass analysisPass = i.next(); 763 executeAnalysisPass(analysisPass, repositoryClassList); 764 765 if (false) 766 System.out.println("Class content stats: " + analysisContext.getClassContextStats()); 767 analysisContext.clearClassContextCache(); 770 } 771 772 bugReporter.finish(); 774 775 bugReporter.reportQueuedErrors(); 777 778 analysisContext.clearRepository(); 780 781 if (false) 782 System.out.println(analysisContext.getClassContextStats()); 783 } 784 785 788 public String getCurrentClass() { 789 return currentClass; 790 } 791 792 795 public int getBugCount() { 796 return bugReporter.getBugCount(); 797 } 798 799 802 public int getErrorCount() { 803 return bugReporter.getErrorCount(); 804 } 805 806 809 public int getMissingClassCount() { 810 return bugReporter.getMissingClassCount(); 811 } 812 813 816 public boolean emitTrainingOutput() { 817 return emitTrainingOutput; 818 } 819 820 823 public UserPreferences getUserPreferences() { 824 if (userPreferences == null) 825 userPreferences = UserPreferences.createDefaultUserPreferences(); 826 return userPreferences; 827 } 828 829 832 public String getTrainingInputDir() { 833 return trainingInputDir; 834 } 835 836 839 public String getTrainingOutputDir() { 840 return trainingOutputDir; 841 } 842 843 846 public boolean useTrainingInput() { 847 return useTrainingInput; 848 } 849 850 853 public void setScanNestedArchives(boolean scanNestedArchives) { 854 } 856 857 860 public static void setHome(String home) { 861 FindBugs.home = home; 862 } 863 864 867 public static String getHome() { 868 if (home == null) { 869 home = SystemProperties.getProperty("findbugs.home"); 870 if (home == null) { 871 System.err.println("Error: The findbugs.home property is not set!"); 872 } 873 } 874 return home; 875 } 876 877 880 881 884 private void configureAnalysisFeatures() { 885 for (AnalysisFeatureSetting setting : settingList) { 886 setting.configure(analysisContext); 887 } 888 } 889 890 895 public static void configureTrainingDatabases(IFindBugsEngine findBugs) throws IOException { 896 if (findBugs.emitTrainingOutput()) { 897 String trainingOutputDir = findBugs.getTrainingOutputDir(); 898 899 if (!new File (trainingOutputDir).isDirectory()) 900 throw new IOException ("Training output directory " + trainingOutputDir + " does not exist"); 901 AnalysisContext.currentAnalysisContext().setDatabaseOutputDir(trainingOutputDir); 902 System.setProperty("findbugs.checkreturn.savetraining", new File (trainingOutputDir, "checkReturn.db").getPath()); 904 } 905 if (findBugs.useTrainingInput()) { 906 String trainingInputDir = findBugs.getTrainingInputDir(); 907 908 if (!new File (trainingInputDir).isDirectory()) 909 throw new IOException ("Training input directory " + trainingInputDir + " does not exist"); 910 AnalysisContext.currentAnalysisContext().setDatabaseInputDir(trainingInputDir); 911 AnalysisContext.currentAnalysisContext().loadInterproceduralDatabases(); 912 System.setProperty("findbugs.checkreturn.loadtraining", new File (trainingInputDir, "checkReturn.db").getPath()); 914 } 915 else { 916 AnalysisContext.currentAnalysisContext().loadDefaultInterproceduralDatabases(); 917 } 918 } 919 920 925 private void createExecutionPlan() throws OrderingConstraintException { 926 executionPlan = new ExecutionPlan(); 927 928 executionPlan.setDetectorFactoryChooser(new DetectorFactoryChooser() { 930 public boolean choose(DetectorFactory factory) { 931 boolean enabled = isDetectorEnabled(FindBugs.this, factory); 932 return enabled; 936 } 937 }); 938 939 for (Iterator <Plugin> i = detectorFactoryCollection.pluginIterator(); i.hasNext();) { 941 Plugin plugin = i.next(); 942 executionPlan.addPlugin(plugin); 943 } 944 945 executionPlan.build(); 947 } 948 949 956 public static boolean isDetectorEnabled(IFindBugsEngine findBugs, DetectorFactory factory) { 957 if (!factory.getPlugin().isEnabled()) 958 return false; 959 960 if (!findBugs.getUserPreferences().isDetectorEnabled(factory)) 961 return false; 962 963 if (!factory.isEnabledForCurrentJRE()) 964 return false; 965 966 if (!AnalysisContext.currentAnalysisContext().getBoolProperty(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS) 968 && factory.isDetectorClassSubtypeOf(InterproceduralFirstPassDetector.class)) 969 return false; 970 971 boolean isTrainingDetector = factory.isDetectorClassSubtypeOf(TrainingDetector.class); 973 boolean isNonReportingDetector = factory.isDetectorClassSubtypeOf(NonReportingDetector.class); 974 if (findBugs.emitTrainingOutput()) { 975 return isTrainingDetector || isNonReportingDetector; 976 } 977 978 if (isTrainingDetector) return false; 979 980 return true; 981 } 982 983 987 private void setRepositoryClassPath() { 988 addCollectionToClasspath(project.getAuxClasspathEntryList()); 990 991 addCollectionToClasspath(project.getImplicitClasspathEntryList()); 993 994 String systemClassPath = ClassPath.getClassPath(); 996 StringTokenizer tok = new StringTokenizer (systemClassPath, File.pathSeparator); 997 while (tok.hasMoreTokens()) { 998 String entry = tok.nextToken(); 999 try { 1000 analysisContext.addClasspathEntry(entry); 1001 } catch (IOException e) { 1002 bugReporter.logError("Warning: could not add URL " + 1003 entry + " to classpath", e); 1004 } 1005 } 1006 } 1007 1008 1016 private void addCollectionToClasspath(Collection <String > collection) { 1017 for (String entry : collection) { 1018 try { 1019 analysisContext.addClasspathEntry(entry); 1021 } catch (IOException e) { 1022 bugReporter.logError("Warning: could not add URL " + 1023 entry + " to classpath", e); 1024 } 1025 } 1026 } 1027 1028 1040 private void scanArchiveOrDirectory(ArchiveWorkListItem item, 1041 LinkedList <ArchiveWorkListItem> archiveWorkList, List <String > repositoryClassList, 1042 List <String > additionalAuxClasspathEntryList) 1043 throws IOException , InterruptedException { 1044 1045 String fileName = item.getFileName(); 1046 ClassProducer classProducer = null; 1047 1048 try { 1049 String protocol = URLClassPath.getURLProtocol(fileName); 1053 if (protocol == null) { 1054 protocol = "file"; 1055 fileName = "file:" + fileName; 1056 } 1057 URL url = new URL (fileName); 1058 1059 String fileExtension = null; 1061 int lastDot = fileName.lastIndexOf('.'); 1062 if (lastDot >= 0) { 1063 fileExtension = fileName.substring(lastDot); 1064 } 1065 1066 if (fileExtension != null && URLClassPath.isArchiveExtension(fileExtension)) 1068 classProducer = new ZipClassProducer(url, archiveWorkList, additionalAuxClasspathEntryList); 1069 else if (fileExtension != null && fileExtension.equals(".class")) 1070 classProducer = new SingleClassProducer(url); 1071 else if (protocol.equals("file")) { 1072 fileName = fileName.substring("file:".length()); 1074 File dir = new File (fileName); 1075 if (!dir.isDirectory()) 1076 throw new IOException ("Path " + fileName + " is not an archive, class file, or directory"); 1077 classProducer = new DirectoryClassProducer(fileName, additionalAuxClasspathEntryList); 1078 } else 1079 throw new IOException ("URL " + fileName + " is not an archive, class file, or directory"); 1080 1081 if (DEBUG || URLClassPathRepository.DEBUG) { 1082 System.out.println("Scanning " + url + " for classes"); 1083 } 1084 1085 for (; ;) { 1087 if (Thread.interrupted()) 1088 throw new InterruptedException (); 1089 try { 1090 JavaClass jclass = classProducer.getNextClass(); 1091 if (jclass == null) 1092 break; 1093 if (DEBUG) System.out.println("Scanned " + jclass.getClassName()); 1094 analysisContext.addApplicationClassToRepository(jclass); 1095 repositoryClassList.add(jclass.getClassName()); 1096 } catch (ClassFormatException e) { 1097 if (DEBUG) e.printStackTrace(); 1098 bugReporter.logError("Invalid classfile format", e); 1099 } 1100 } 1101 1102 if (item.isExplicit()) 1103 progressCallback.finishArchive(); 1104 1105 if (classProducer.containsSourceFiles()) 1108 project.addSourceDir(fileName); 1109 project.addTimestamp(classProducer.getLastModificationTime()); 1110 1111 } catch (IOException e) { 1112 IOException ioe = new IOException ("Could not analyze " + fileName); 1115 ioe.initCause(e); 1116 throw ioe; 1117 } finally { 1118 if (classProducer != null) { 1119 classProducer.close(); 1120 } 1121 } 1122 } 1123 1124 1131 private void executeAnalysisPass(AnalysisPass analysisPass, List <String > repositoryClassList) throws InterruptedException { 1132 progressCallback.startAnalysis(repositoryClassList.size()); 1134 1135 int thisPass = passCount++; 1136 if (ExecutionPlan.DEBUG) { 1137 System.out.println("************* Analysis pass " + thisPass + " *************"); 1138 for (Iterator <DetectorFactory> i = analysisPass.iterator(); i.hasNext();) { 1139 DetectorFactory factory = i.next(); 1140 System.out.println("\t" + factory.getFullName()); 1141 } 1142 } 1143 1144 Detector[] detectors = analysisPass.instantiateDetectorsInPass(bugReporter); 1147 1148 Set <String > examinedClassSet = new HashSet <String >(); 1150 for (String className : repositoryClassList) { 1151 if (examinedClassSet.add(className)) 1152 examineClass(detectors, className); 1153 } 1154 1155 if (DEBUG) { 1156 long total = 0; 1157 for (Long aLong : detectorTimings.values()) { 1158 total += aLong.longValue(); 1159 } 1160 System.out.println(); 1161 System.out.println("Detector Timings"); 1162 for (Map.Entry <String , Long > entry : detectorTimings.entrySet()) { 1163 String detectorName = entry.getKey(); 1164 long detectorTime = entry.getValue().longValue(); 1165 System.out.println(detectorName + ": " + detectorTime + " ms -> (" + (detectorTime * 100.0f / (float) total) + ") %"); 1166 } 1167 System.out.println(); 1168 detectorTimings = new HashMap <String ,Long >(); 1169 } 1170 1171 progressCallback.finishPerClassAnalysis(); 1173 1174 this.reportFinal(detectors); 1177 1178 AnalysisContext.currentAnalysisContext().updateDatabases(thisPass); 1179 } 1180 1181 1187 private void examineClass(Detector[] detectors, String className) throws InterruptedException { 1188 if (DEBUG) System.out.println("Examining class " + className); 1189 long entireClassAnalysisStart = 0; 1190 1191 1192 if (TIMEDEBUG || DEBUG) { 1193 entireClassAnalysisStart = System.currentTimeMillis(); 1194 } 1195 this.currentClass = className; 1196 1197 try { 1198 JavaClass javaClass = Repository.lookupClass(className); 1199 1200 for (IClassObserver aClassObserver : classObserverList) { 1202 ClassDescriptor classDescriptor = 1203 new ClassDescriptor(ClassName.toSlashedClassName(javaClass.getClassName())); 1204 aClassObserver.observeClass(classDescriptor); 1205 } 1206 1207 ClassContext classContext = analysisContext.getClassContext(javaClass); 1209 1210 for (Detector detector1 : detectors) { 1212 if (Thread.interrupted()) 1213 throw new InterruptedException (); 1214 Detector detector = detector1; 1215 if (false && detector instanceof StatelessDetector) { 1217 try { 1218 detector = (Detector) ((StatelessDetector) detector).clone(); 1219 } catch (CloneNotSupportedException e) { 1220 throw new AssertionError (e); 1221 } 1222 } 1223 1224 1225 try { 1226 long start = 0, end; 1227 1228 1229 if (TIMEDEBUG || DEBUG) { 1230 start = System.currentTimeMillis(); 1231 if (DEBUG) { 1232 System.out.println(" running " + detector.getClass().getName()); 1233 1234 } 1235 } 1236 detector.visitClassContext(classContext); 1237 1238 if (TIMEDEBUG || DEBUG) { 1239 end = System.currentTimeMillis(); 1240 long delta = end - start; 1241 entireClassAnalysisStart += delta; 1242 if (delta > TIMEQUANTUM) 1243 System.out.println("TIME: " + detector.getClass().getName() + " " + className + " " + delta); 1244 if (DEBUG) { 1245 String detectorName = detector.getClass().getName(); 1246 Long total = detectorTimings.get(detectorName); 1247 if (total == null) 1248 total = (Long )(delta); 1249 else 1250 total = (Long )(total.longValue() + delta); 1251 detectorTimings.put(detectorName, total); 1252 } 1253 } 1254 } catch (AnalysisException e) { 1255 reportRecoverableDetectorException(className, detector, e); 1256 } catch (ArrayIndexOutOfBoundsException e) { 1257 reportRecoverableDetectorException(className, detector, e); 1258 } catch (ClassCastException e) { 1259 reportRecoverableDetectorException(className, detector, e); 1260 } 1261 } 1262 } catch (ClassNotFoundException e) { 1263 bugReporter.reportMissingClass(e); 1265 reportRecoverableException(className, e); 1266 } catch (ClassFormatException e) { 1267 reportRecoverableException(className, e); 1268 } 1269 catch (RuntimeException re) { 1270 RuntimeException annotatedEx; 1271 try { 1272 String sep = SystemProperties.getProperty("line.separator"); 1273 Constructor <? extends RuntimeException > c = re.getClass().getConstructor(new Class [] { String .class }); 1274 String msg = re.getMessage(); 1275 msg = sep + "While finding bugs in class: " + className + ((msg == null) ? "" : (sep + msg)); 1276 annotatedEx = c.newInstance(new Object [] {msg}); 1277 annotatedEx.setStackTrace(re.getStackTrace()); 1278 } catch (RuntimeException e) { 1279 throw re; 1280 } catch (Exception e) { 1281 throw re; 1282 } 1283 throw annotatedEx; 1284 } 1285 if (TIMEDEBUG || DEBUG) { 1286 long classSetupTime = System.currentTimeMillis() - entireClassAnalysisStart; 1287 if (classSetupTime > TIMEQUANTUM) 1288 System.out.println("TIME: setup " + className + " " + classSetupTime); 1289 } 1290 progressCallback.finishClass(); 1291 } 1292 1293 private void reportRecoverableException(String className, Exception e) { 1294 if (DEBUG) { 1295 e.printStackTrace(); 1296 } 1297 bugReporter.logError("Exception analyzing " + className, e); 1298 } 1299 1300 private void reportRecoverableDetectorException(String className, Detector detector, Exception e) { 1301 if (DEBUG) { 1302 e.printStackTrace(); 1303 } 1304 bugReporter.logError("Exception analyzing " + className + 1305 " using detector " + detector.getClass().getName(), e); 1306 } 1307 1308 1312 private void reportFinal(Detector[] detectors) throws InterruptedException { 1313 for (Detector detector : detectors) { 1314 if (Thread.interrupted()) 1315 throw new InterruptedException (); 1316 detector.report(); 1317 } 1318 } 1319 1320 1323 private static JavaClass parseClass(String archiveName, InputStream in, String fileName) 1324 throws IOException { 1325 if (DEBUG) System.out.println("About to parse " + fileName + " in " + archiveName); 1326 return parseFromStream(in, fileName); 1327 } 1328 1329 1332 private static JavaClass parseClass(URL url) throws IOException { 1333 if (DEBUG) System.out.println("About to parse " + url.toString()); 1334 InputStream in = null; 1335 try { 1336 in = url.openStream(); 1337 return parseFromStream(in, url.toString()); 1338 } finally { 1339 if (in != null) 1340 in.close(); 1341 } 1342 } 1343 1344 1349 private static JavaClass parseFromStream(InputStream in, String fileName) throws IOException { 1350 try { 1351 return new ClassParser(in, fileName).parse(); 1352 } finally { 1353 try { 1354 in.close(); 1355 } catch (IOException ignore) { 1356 } 1358 } 1359 } 1360 1361 1362 1371 static Set <String > handleBugCategories(UserPreferences userPreferences, String categories) { 1372 Set <String > categorySet = new HashSet <String >(); 1374 StringTokenizer tok = new StringTokenizer (categories, ","); 1375 while (tok.hasMoreTokens()) { 1376 categorySet.add(tok.nextToken()); 1377 } 1378 1379 1410 return categorySet; 1411 } 1412 1413 1416 1417 public static void main(String [] argv) { 1418 try { 1419 TextUICommandLine commandLine = new TextUICommandLine(); 1420 FindBugs findBugs = createEngine(commandLine, argv); 1421 1422 try { 1423 runMain(findBugs, commandLine); 1424 } catch (RuntimeException e) { 1425 System.err.println("Fatal exception: " + e.toString()); 1426 String currentClass = findBugs.getCurrentClass(); 1427 if (currentClass != null) { 1428 System.err.println("\tWhile analyzing " + currentClass); 1429 } 1430 e.printStackTrace(); 1431 System.err.println("Please report the failure to " + Version.SUPPORT_EMAIL); 1432 System.exit(1); 1433 } 1434 1435 } catch (java.io.IOException e) { 1436 if (DEBUG) { 1438 e.printStackTrace(); 1439 } 1440 System.err.println("IO Error: " + e.getMessage()); 1441 System.exit(1); 1442 } catch (FilterException e) { 1443 System.err.println("Filter exception: " + e.getMessage()); 1444 } catch (IllegalArgumentException e) { 1445 System.err.println("Illegal argument: " + e.getMessage()); 1447 System.exit(1); 1448 } 1449 } 1450 1451 private static FindBugs createEngine(TextUICommandLine commandLine, String [] argv) 1452 throws java.io.IOException , FilterException { 1453 1454 FindBugs findBugs = new FindBugs(); 1455 processCommandLine(commandLine, argv, findBugs); 1456 return findBugs; 1457 } 1458 1459 1468 public static void processCommandLine(TextUICommandLine commandLine, String [] argv, IFindBugsEngine findBugs) throws IOException , FilterException { 1469 argv = CommandLine.expandOptionFiles(argv, true, true); 1476 1477 int argCount = 0; 1478 try { 1479 argCount = commandLine.parse(argv); 1480 } catch (IllegalArgumentException e) { 1481 System.out.println(e.getMessage()); 1482 showHelp(commandLine); 1483 } catch (HelpRequestedException e) { 1484 showHelp(commandLine); 1485 } 1486 1487 Project project = commandLine.getProject(); 1488 for (int i = argCount; i < argv.length; ++i) 1489 project.addFile(argv[i]); 1490 commandLine.handleXArgs(); 1491 1492 if (project.getFileCount() == 0) { 1493 showHelp(commandLine); 1494 } 1495 1496 commandLine.configureEngine(findBugs); 1497 } 1498 1499 @SuppressWarnings ("DM_EXIT") 1500 public static void showHelp(TextUICommandLine commandLine) { 1501 showSynopsis(); 1502 ShowHelp.showGeneralOptions(); 1503 FindBugs.showCommandLineOptions(commandLine); 1504 System.exit(1); 1505 } 1506 1507 @SuppressWarnings ("DM_EXIT") 1508 public static void runMain(IFindBugsEngine findBugs, TextUICommandLine commandLine) 1509 throws java.io.IOException , RuntimeException { 1510 try { 1511 findBugs.execute(); 1512 } catch (InterruptedException e) { 1513 } 1515 1516 int bugCount = findBugs.getBugCount(); 1517 int missingClassCount = findBugs.getMissingClassCount(); 1518 int errorCount = findBugs.getErrorCount(); 1519 1520 if (!commandLine.quiet() || commandLine.setExitCode()) { 1521 if (bugCount > 0) 1522 System.err.println("Warnings generated: " + bugCount); 1523 if (missingClassCount > 0) 1524 System.err.println("Missing classes: " + missingClassCount); 1525 if (errorCount > 0) 1526 System.err.println("Analysis errors: " + errorCount); 1527 } 1528 1529 if (commandLine.setExitCode()) { 1530 int exitCode = 0; 1531 if (errorCount > 0) 1532 exitCode |= ERROR_FLAG; 1533 if (missingClassCount > 0) 1534 exitCode |= MISSING_CLASS_FLAG; 1535 if (bugCount > 0) 1536 exitCode |= BUGS_FOUND_FLAG; 1537 1538 System.exit(exitCode); 1539 } 1540 } 1541 1542 1545 public static void showCommandLineOptions() { 1546 showCommandLineOptions(new TextUICommandLine()); 1547 } 1548 1549 public static void showCommandLineOptions(TextUICommandLine commandLine) { 1550 System.out.println("Command line options:"); 1551 commandLine.printUsage(System.out); 1552 } 1553 1554 public static void showSynopsis() { 1555 System.out.println("Usage: findbugs [general options] -textui [command line options...] [jar/zip/class files, directories...]"); 1556 } 1557 1558 public static void configureFilter(DelegatingBugReporter bugReporter, String filterFileName, boolean include) 1559 throws IOException , FilterException { 1560 Filter filter = new Filter (filterFileName); 1561 BugReporter origBugReporter = bugReporter.getDelegate(); 1562 BugReporter filterBugReporter = new FilterBugReporter(origBugReporter, filter, include); 1563 bugReporter.setDelegate(filterBugReporter); 1564 } 1565 1566 1572 public static void configureBugCollection(IFindBugsEngine findBugs) { 1573 BugReporter realBugReporter = findBugs.getBugReporter().getRealBugReporter(); 1574 1575 if (realBugReporter instanceof BugCollectionBugReporter) { 1576 BugCollectionBugReporter bugCollectionBugReporter = 1577 (BugCollectionBugReporter) realBugReporter; 1578 1579 bugCollectionBugReporter = (BugCollectionBugReporter) realBugReporter; 1580 1581 bugCollectionBugReporter.getBugCollection().setReleaseName(findBugs.getReleaseName()); 1582 1583 Project project = findBugs.getProject(); 1584 1585 if (project.getTimestamp() != 0) { 1586 bugCollectionBugReporter.getBugCollection().setTimestamp(project.getTimestamp()); 1587 bugCollectionBugReporter.getBugCollection().getProjectStats().setTimestamp(project.getTimestamp()); 1588 } 1589 1590 } 1591 } 1592} 1593 1594 | Popular Tags |