1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package edu.umd.cs.findbugs;
21
22 import java.io.FileInputStream;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.HashSet;
28 import java.util.Iterator;
29 import java.util.LinkedHashSet;
30 import java.util.LinkedList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.Set;
35
36 import javax.annotation.CheckForNull;
37 import javax.annotation.Nonnull;
38
39 import org.apache.bcel.classfile.ClassFormatException;
40 import org.dom4j.DocumentException;
41
42 import edu.umd.cs.findbugs.asm.FBClassReader;
43 import edu.umd.cs.findbugs.ba.AnalysisContext;
44 import edu.umd.cs.findbugs.ba.AnalysisFeatures;
45 import edu.umd.cs.findbugs.ba.ObjectTypeFactory;
46 import edu.umd.cs.findbugs.ba.SourceInfoMap;
47 import edu.umd.cs.findbugs.ba.XClass;
48 import edu.umd.cs.findbugs.ba.XFactory;
49 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation;
50 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierApplications;
51 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue;
52 import edu.umd.cs.findbugs.bugReporter.BugReporterDecorator;
53 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
54 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
55 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
56 import edu.umd.cs.findbugs.classfile.Global;
57 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
58 import edu.umd.cs.findbugs.classfile.IAnalysisEngineRegistrar;
59 import edu.umd.cs.findbugs.classfile.IClassFactory;
60 import edu.umd.cs.findbugs.classfile.IClassObserver;
61 import edu.umd.cs.findbugs.classfile.IClassPath;
62 import edu.umd.cs.findbugs.classfile.IClassPathBuilder;
63 import edu.umd.cs.findbugs.classfile.ICodeBase;
64 import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
65 import edu.umd.cs.findbugs.classfile.MissingClassException;
66 import edu.umd.cs.findbugs.classfile.impl.ClassFactory;
67 import edu.umd.cs.findbugs.config.AnalysisFeatureSetting;
68 import edu.umd.cs.findbugs.config.UserPreferences;
69 import edu.umd.cs.findbugs.detect.NoteSuppressedWarnings;
70 import edu.umd.cs.findbugs.filter.FilterException;
71 import edu.umd.cs.findbugs.log.Profiler;
72 import edu.umd.cs.findbugs.plan.AnalysisPass;
73 import edu.umd.cs.findbugs.plan.ExecutionPlan;
74 import edu.umd.cs.findbugs.plan.OrderingConstraintException;
75 import edu.umd.cs.findbugs.util.ClassName;
76 import edu.umd.cs.findbugs.util.TopologicalSort.OutEdges;
77
78
79
80
81
82
83
84 public class FindBugs2 implements IFindBugsEngine {
85 private static final boolean LIST_ORDER = SystemProperties.getBoolean("findbugs.listOrder");
86
87 private static final boolean VERBOSE = SystemProperties.getBoolean("findbugs.verbose");
88
89 public static final boolean DEBUG = VERBOSE || SystemProperties.getBoolean("findbugs.debug");
90
91 public static final boolean PROGRESS = DEBUG || SystemProperties.getBoolean("findbugs.progress");
92
93 private static final boolean SCREEN_FIRST_PASS_CLASSES = SystemProperties.getBoolean("findbugs.screenFirstPass");
94
95 public static final String PROP_FINDBUGS_HOST_APP = "findbugs.hostApp";
96 public static final String PROP_FINDBUGS_HOST_APP_VERSION = "findbugs.hostAppVersion";
97
98 private int rankThreshold;
99
100 private List<IClassObserver> classObserverList;
101
102 private BugReporter bugReporter;
103
104 private ErrorCountingBugReporter errorCountingBugReporter;
105
106 private Project project;
107
108 private IClassFactory classFactory;
109
110 private IClassPath classPath;
111
112 private List<ClassDescriptor> appClassList;
113
114 private Collection<ClassDescriptor> referencedClassSet;
115
116 private DetectorFactoryCollection detectorFactoryCollection;
117
118 private ExecutionPlan executionPlan;
119
120 private String currentClassName;
121
122 private FindBugsProgress progress;
123
124 private IClassScreener classScreener;
125
126 private final AnalysisOptions analysisOptions = new AnalysisOptions(true);
127
128
129
130
131 public FindBugs2() {
132 this.classObserverList = new LinkedList<>();
133 this.analysisOptions.analysisFeatureSettingList = FindBugs.DEFAULT_EFFORT;
134 this.progress = new NoOpFindBugsProgress();
135
136
137 this.classScreener = new IClassScreener() {
138 @Override
139 public boolean matches(String fileName) {
140 return true;
141 }
142
143 @Override
144 public boolean vacuous() {
145 return true;
146 }
147 };
148
149 String hostApp = System.getProperty(PROP_FINDBUGS_HOST_APP);
150 String hostAppVersion = null;
151 if (hostApp == null || hostApp.trim().length() <= 0) {
152 hostApp = "FindBugs TextUI";
153 hostAppVersion = System.getProperty(PROP_FINDBUGS_HOST_APP_VERSION);
154 }
155 if (hostAppVersion == null) {
156 hostAppVersion = "";
157 }
158 Version.registerApplication(hostApp, hostAppVersion);
159
160
161 this.analysisOptions.scanNestedArchives = false;
162
163
164 rankThreshold = BugRanker.VISIBLE_RANK_MAX;
165 }
166
167
168
169
170
171
172
173
174 @Override
175 public void setDetectorFactoryCollection(DetectorFactoryCollection detectorFactoryCollection) {
176 this.detectorFactoryCollection = detectorFactoryCollection;
177 }
178
179
180
181
182
183
184
185
186
187 @Override
188 public void execute() throws IOException, InterruptedException {
189
190 if (FindBugs.isNoAnalysis()) {
191 throw new UnsupportedOperationException("This FindBugs invocation was started without analysis capabilities");
192 }
193
194 Profiler profiler = bugReporter.getProjectStats().getProfiler();
195
196 try {
197 try {
198
199 classFactory = ClassFactory.instance();
200
201
202 createClassPath();
203
204 progress.reportNumberOfArchives(project.getFileCount() + project.getNumAuxClasspathEntries());
205 profiler.start(this.getClass());
206
207
208 createAnalysisCache();
209
210
211 createAnalysisContext(project, appClassList, analysisOptions.sourceInfoFileName);
212
213
214
215 buildClassPath();
216
217
218
219 buildReferencedClassSet();
220
221
222 setAppClassList(appClassList);
223
224
225 FindBugs.configureBugCollection(this);
226
227
228 FindBugsAnalysisFeatures.setRelaxedMode(analysisOptions.relaxedReportingMode);
229 FindBugsDisplayFeatures.setAbridgedMessages(analysisOptions.abridgedMessages);
230
231
232 FindBugs.configureTrainingDatabases(this);
233
234
235 configureAnalysisFeatures();
236
237
238 createExecutionPlan();
239
240 for (Plugin p : detectorFactoryCollection.plugins()) {
241 for (ComponentPlugin<BugReporterDecorator> brp
242 : p.getComponentPlugins(BugReporterDecorator.class)) {
243 if (brp.isEnabledByDefault() && !brp.isNamed(explicitlyDisabledBugReporterDecorators)
244 || brp.isNamed(explicitlyEnabledBugReporterDecorators)) {
245 bugReporter = BugReporterDecorator.construct(brp, bugReporter);
246 }
247 }
248 }
249 if (!classScreener.vacuous()) {
250 bugReporter = new DelegatingBugReporter(bugReporter) {
251
252 @Override
253 public void reportBug(@Nonnull BugInstance bugInstance) {
254 String className = bugInstance.getPrimaryClass().getClassName();
255 String resourceName = className.replace('.', '/') + ".class";
256 if (classScreener.matches(resourceName)) {
257 this.getDelegate().reportBug(bugInstance);
258 }
259 }
260 };
261 }
262
263 if (executionPlan.isActive(NoteSuppressedWarnings.class)) {
264 SuppressionMatcher m = AnalysisContext.currentAnalysisContext().getSuppressionMatcher();
265 bugReporter = new FilterBugReporter(bugReporter, m, false);
266 }
267
268 if (appClassList.size() == 0) {
269 Map<String, ICodeBaseEntry> codebase = classPath.getApplicationCodebaseEntries();
270 if (analysisOptions.noClassOk) {
271 System.err.println("No classfiles specified; output will have no warnings");
272 } else if (codebase.isEmpty()) {
273 throw new IOException("No files to analyze could be opened");
274 } else {
275 throw new NoClassesFoundToAnalyzeException(classPath);
276 }
277 }
278
279
280 analyzeApplication();
281 } catch (CheckedAnalysisException e) {
282 IOException ioe = new IOException("IOException while scanning codebases");
283 ioe.initCause(e);
284 throw ioe;
285 } catch (OutOfMemoryError e) {
286 System.err.println("Out of memory");
287 System.err.println("Total memory: " + Runtime.getRuntime().maxMemory() / 1000000 + "M");
288 System.err.println(" free memory: " + Runtime.getRuntime().freeMemory() / 1000000 + "M");
289
290 for (String s : project.getFileList()) {
291 System.err.println("Analyzed: " + s);
292 }
293 for (String s : project.getAuxClasspathEntryList()) {
294 System.err.println(" Aux: " + s);
295 }
296 throw e;
297 } finally {
298 clearCaches();
299 profiler.end(this.getClass());
300 profiler.report();
301 }
302 } catch (IOException e) {
303 bugReporter.reportQueuedErrors();
304 throw e;
305 }
306 }
307
308
309
310
311 protected void clearCaches() {
312 DescriptorFactory.clearInstance();
313 ObjectTypeFactory.clearInstance();
314 TypeQualifierApplications.clearInstance();
315 TypeQualifierAnnotation.clearInstance();
316 TypeQualifierValue.clearInstance();
317
318 AnalysisContext.removeCurrentAnalysisContext();
319 Global.removeAnalysisCacheForCurrentThread();
320 if (classPath != null) {
321 classPath.close();
322 }
323 }
324
325
326
327
328
329 public void dispose() {
330 if (executionPlan != null) {
331 executionPlan.dispose();
332 }
333 if (appClassList != null) {
334 appClassList.clear();
335 }
336 if (classObserverList != null) {
337 classObserverList.clear();
338 }
339 if (referencedClassSet != null) {
340 referencedClassSet.clear();
341 }
342 analysisOptions.analysisFeatureSettingList = null;
343 bugReporter = null;
344 classFactory = null;
345 classPath = null;
346 classScreener = null;
347 detectorFactoryCollection = null;
348 executionPlan = null;
349 progress = null;
350 project = null;
351 analysisOptions.userPreferences = null;
352 }
353
354 @Override
355 public BugReporter getBugReporter() {
356 return bugReporter;
357 }
358
359 @Override
360 public Project getProject() {
361 return project;
362 }
363
364 @Override
365 public void addClassObserver(IClassObserver classObserver) {
366 classObserverList.add(classObserver);
367 }
368
369 @Override
370 public void addFilter(String filterFileName, boolean include) throws IOException, FilterException {
371 bugReporter = FindBugs.configureFilter(bugReporter, filterFileName, include);
372 }
373
374 @Override
375 public void excludeBaselineBugs(String baselineBugs) throws IOException, DocumentException {
376 bugReporter = FindBugs.configureBaselineFilter(bugReporter, baselineBugs);
377 }
378
379 @Override
380 public void enableTrainingInput(String trainingInputDir) {
381 this.analysisOptions.trainingInputDir = trainingInputDir;
382 }
383
384 @Override
385 public void enableTrainingOutput(String trainingOutputDir) {
386 this.analysisOptions.trainingOutputDir = trainingOutputDir;
387 }
388
389 @Override
390 public int getBugCount() {
391 return errorCountingBugReporter.getBugCount();
392 }
393
394 @Override
395 public String getCurrentClass() {
396 return currentClassName;
397 }
398
399 @Override
400 public int getErrorCount() {
401 return errorCountingBugReporter.getErrorCount();
402 }
403
404 @Override
405 public int getMissingClassCount() {
406 return errorCountingBugReporter.getMissingClassCount();
407 }
408
409 @Override
410 public String getReleaseName() {
411 return analysisOptions.releaseName;
412 }
413
414 @Override
415 public String getProjectName() {
416 return analysisOptions.projectName;
417 }
418
419 @Override
420 public void setProjectName(String name) {
421 analysisOptions.projectName = name;
422 }
423
424 @Override
425 public void setAnalysisFeatureSettings(AnalysisFeatureSetting[] settingList) {
426 this.analysisOptions.analysisFeatureSettingList = settingList;
427 }
428
429 @Override
430 public void setBugReporter(BugReporter bugReporter) {
431 this.bugReporter = this.errorCountingBugReporter = new ErrorCountingBugReporter(bugReporter);
432
433 addClassObserver(bugReporter);
434 }
435
436 @Override
437 public void setClassScreener(IClassScreener classScreener) {
438 this.classScreener = classScreener;
439 }
440
441 @Override
442 public void setProgressCallback(FindBugsProgress progressCallback) {
443 this.progress = progressCallback;
444 }
445
446 @Override
447 public void setProject(Project project) {
448 this.project = project;
449 }
450
451 @Override
452 public void setRelaxedReportingMode(boolean relaxedReportingMode) {
453 this.analysisOptions.relaxedReportingMode = relaxedReportingMode;
454 }
455
456 @Override
457 public void setReleaseName(String releaseName) {
458 this.analysisOptions.releaseName = releaseName;
459 }
460
461 @Override
462 public void setSourceInfoFile(String sourceInfoFile) {
463 this.analysisOptions.sourceInfoFileName = sourceInfoFile;
464 }
465
466 @Override
467 public void setUserPreferences(UserPreferences userPreferences) {
468 this.analysisOptions.userPreferences = userPreferences;
469
470
471
472 configureFilters(userPreferences);
473 }
474
475 protected void configureFilters(UserPreferences userPreferences) {
476 IllegalArgumentException deferredError = null;
477 Set<Entry<String, Boolean>> excludeBugFiles = userPreferences.getExcludeBugsFiles().entrySet();
478 for (Entry<String, Boolean> entry : excludeBugFiles) {
479 if (entry.getValue() == null || !entry.getValue()) {
480 continue;
481 }
482 try {
483 excludeBaselineBugs(entry.getKey());
484 } catch (Exception e) {
485 String message = "Unable to read filter: " + entry.getKey() + " : " + e.getMessage();
486 if (getBugReporter() != null) {
487 getBugReporter().logError(message, e);
488 } else if (deferredError == null){
489 deferredError = new IllegalArgumentException(message, e);
490 }
491 }
492 }
493 Set<Entry<String, Boolean>> includeFilterFiles = userPreferences.getIncludeFilterFiles().entrySet();
494 for (Entry<String, Boolean> entry : includeFilterFiles) {
495 if (entry.getValue() == null || !entry.getValue()) {
496 continue;
497 }
498 try {
499 addFilter(entry.getKey(), true);
500 } catch (Exception e) {
501 String message = "Unable to read filter: " + entry.getKey() + " : " + e.getMessage();
502 if (getBugReporter() != null) {
503 getBugReporter().logError(message, e);
504 } else if (deferredError == null){
505 deferredError = new IllegalArgumentException(message, e);
506 }
507 }
508 }
509 Set<Entry<String, Boolean>> excludeFilterFiles = userPreferences.getExcludeFilterFiles().entrySet();
510
511 for (Entry<String, Boolean> entry : excludeFilterFiles) {
512 Boolean value = entry.getValue();
513 if (value == null || !value) {
514 continue;
515 }
516 String excludeFilterFile = entry.getKey();
517 try {
518 addFilter(excludeFilterFile, false);
519 } catch (Exception e) {
520 String message = "Unable to read filter: " + excludeFilterFile + " : " + e.getMessage();
521 if (getBugReporter() != null) {
522 getBugReporter().logError(message, e);
523 } else if (deferredError == null){
524 deferredError = new IllegalArgumentException(message, e);
525 }
526 }
527 }
528 if (deferredError != null) {
529 throw deferredError;
530 }
531 }
532
533 @Override
534 public boolean emitTrainingOutput() {
535 return analysisOptions.trainingOutputDir != null;
536 }
537
538 @Override
539 public UserPreferences getUserPreferences() {
540 return analysisOptions.userPreferences;
541 }
542
543
544
545
546 private void createClassPath() {
547 classPath = classFactory.createClassPath();
548 }
549
550 @Override
551 public String getTrainingInputDir() {
552 return analysisOptions.trainingInputDir;
553 }
554
555 @Override
556 public String getTrainingOutputDir() {
557 return analysisOptions.trainingOutputDir;
558 }
559
560 @Override
561 public boolean useTrainingInput() {
562 return analysisOptions.trainingInputDir != null;
563 }
564
565 @Override
566 public void setScanNestedArchives(boolean scanNestedArchives) {
567 this.analysisOptions.scanNestedArchives = scanNestedArchives;
568 }
569
570 @Override
571 public void setNoClassOk(boolean noClassOk) {
572 this.analysisOptions.noClassOk = noClassOk;
573 }
574
575
576
577
578
579
580
581
582
583
584 protected IAnalysisCache createAnalysisCache() throws IOException {
585 IAnalysisCache analysisCache = ClassFactory.instance().createAnalysisCache(classPath, bugReporter);
586
587
588 registerBuiltInAnalysisEngines(analysisCache);
589
590
591 registerPluginAnalysisEngines(detectorFactoryCollection, analysisCache);
592
593
594 analysisCache.eagerlyPutDatabase(DetectorFactoryCollection.class, detectorFactoryCollection);
595
596 Global.setAnalysisCacheForCurrentThread(analysisCache);
597 return analysisCache;
598 }
599
600
601
602
603
604
605 public static void registerBuiltInAnalysisEngines(IAnalysisCache analysisCache) {
606 new edu.umd.cs.findbugs.classfile.engine.EngineRegistrar().registerAnalysisEngines(analysisCache);
607 new edu.umd.cs.findbugs.classfile.engine.asm.EngineRegistrar().registerAnalysisEngines(analysisCache);
608 new edu.umd.cs.findbugs.classfile.engine.bcel.EngineRegistrar().registerAnalysisEngines(analysisCache);
609 }
610
611
612
613
614
615
616
617
618
619
620
621 public static void registerPluginAnalysisEngines(DetectorFactoryCollection detectorFactoryCollection,
622 IAnalysisCache analysisCache) throws IOException {
623 for (Iterator<Plugin> i = detectorFactoryCollection.pluginIterator(); i.hasNext();) {
624 Plugin plugin = i.next();
625
626 Class<? extends IAnalysisEngineRegistrar> engineRegistrarClass = plugin.getEngineRegistrarClass();
627 if (engineRegistrarClass != null) {
628 try {
629 IAnalysisEngineRegistrar engineRegistrar = engineRegistrarClass.newInstance();
630 engineRegistrar.registerAnalysisEngines(analysisCache);
631 } catch (InstantiationException e) {
632 IOException ioe = new IOException("Could not create analysis engine registrar for plugin "
633 + plugin.getPluginId());
634 ioe.initCause(e);
635 throw ioe;
636 } catch (IllegalAccessException e) {
637 IOException ioe = new IOException("Could not create analysis engine registrar for plugin "
638 + plugin.getPluginId());
639 ioe.initCause(e);
640 throw ioe;
641 }
642 }
643 }
644 }
645
646
647
648
649
650
651
652
653
654
655 private void buildClassPath() throws InterruptedException, IOException, CheckedAnalysisException {
656 IClassPathBuilder builder = classFactory.createClassPathBuilder(bugReporter);
657
658 {
659 HashSet<String> seen = new HashSet<>();
660 for (String path : project.getFileArray()) {
661 if (seen.add(path)) {
662 builder.addCodeBase(classFactory.createFilesystemCodeBaseLocator(path), true);
663 }
664 }
665 for (String path : project.getAuxClasspathEntryList()) {
666 if (seen.add(path)) {
667 builder.addCodeBase(classFactory.createFilesystemCodeBaseLocator(path), false);
668 }
669 }
670 }
671
672 builder.scanNestedArchives(analysisOptions.scanNestedArchives);
673
674 builder.build(classPath, progress);
675
676 appClassList = builder.getAppClassList();
677
678 if (PROGRESS) {
679 System.out.println(appClassList.size() + " classes scanned");
680 }
681
682
683
684
685
686 List<String> pathNames = new ArrayList<>();
687 for (Iterator<? extends ICodeBase> i = classPath.appCodeBaseIterator(); i.hasNext();) {
688 ICodeBase appCodeBase = i.next();
689
690 if (appCodeBase.containsSourceFiles()) {
691 String pathName = appCodeBase.getPathName();
692 if (pathName != null) {
693 pathNames.add(pathName);
694 }
695 }
696
697 project.addTimestamp(appCodeBase.getLastModifiedTime());
698 }
699
700 project.addSourceDirs(pathNames);
701 }
702
703 private void buildReferencedClassSet() throws InterruptedException {
704
705
706 if (PROGRESS) {
707 System.out.println("Adding referenced classes");
708 }
709 Set<String> referencedPackageSet = new HashSet<>();
710
711 LinkedList<ClassDescriptor> workList = new LinkedList<>();
712 workList.addAll(appClassList);
713
714 Set<ClassDescriptor> seen = new HashSet<>();
715 Set<ClassDescriptor> appClassSet = new HashSet<>(appClassList);
716
717 Set<ClassDescriptor> badAppClassSet = new HashSet<>();
718 HashSet<ClassDescriptor> knownDescriptors = new HashSet<>(DescriptorFactory.instance()
719 .getAllClassDescriptors());
720 int count = 0;
721 Set<ClassDescriptor> addedToWorkList = new HashSet<>(appClassList);
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750 while (!workList.isEmpty()) {
751 if (Thread.interrupted()) {
752 throw new InterruptedException();
753 }
754 ClassDescriptor classDesc = workList.removeFirst();
755
756 if (seen.contains(classDesc)) {
757 continue;
758 }
759 seen.add(classDesc);
760
761 if (!knownDescriptors.contains(classDesc)) {
762 count++;
763 if (PROGRESS && count % 5000 == 0) {
764 System.out.println("Adding referenced class " + classDesc);
765 }
766 }
767
768 referencedPackageSet.add(classDesc.getPackageName());
769
770
771
772 try {
773 XClass classNameAndInfo = Global.getAnalysisCache().getClassAnalysis(XClass.class, classDesc);
774
775 ClassDescriptor superclassDescriptor = classNameAndInfo.getSuperclassDescriptor();
776 if (superclassDescriptor != null && addedToWorkList.add(superclassDescriptor)) {
777 workList.addLast(superclassDescriptor);
778 }
779
780 for (ClassDescriptor ifaceDesc : classNameAndInfo.getInterfaceDescriptorList()) {
781 if (addedToWorkList.add(ifaceDesc)) {
782 workList.addLast(ifaceDesc);
783 }
784 }
785
786 ClassDescriptor enclosingClass = classNameAndInfo.getImmediateEnclosingClass();
787 if (enclosingClass != null && addedToWorkList.add(enclosingClass)) {
788 workList.addLast(enclosingClass);
789 }
790
791 } catch (RuntimeException e) {
792 bugReporter.logError("Error scanning " + classDesc + " for referenced classes", e);
793 if (appClassSet.contains(classDesc)) {
794 badAppClassSet.add(classDesc);
795 }
796 } catch (MissingClassException e) {
797
798 bugReporter.reportMissingClass(e.getClassDescriptor());
799 if (appClassSet.contains(classDesc)) {
800 badAppClassSet.add(classDesc);
801 }
802 } catch (CheckedAnalysisException e) {
803
804
805 bugReporter.logError("Error scanning " + classDesc + " for referenced classes", e);
806 if (appClassSet.contains(classDesc)) {
807 badAppClassSet.add(classDesc);
808 }
809 }
810 }
811
812 appClassList.removeAll(badAppClassSet);
813 DescriptorFactory.instance().purge(badAppClassSet);
814
815 for (ClassDescriptor d : DescriptorFactory.instance().getAllClassDescriptors()) {
816 referencedPackageSet.add(d.getPackageName());
817 }
818 referencedClassSet = new ArrayList<>(DescriptorFactory.instance().getAllClassDescriptors());
819
820
821
822 if (PROGRESS) {
823 referencedPackageSet.remove("");
824 System.out.println("Added " + count + " referenced classes");
825 System.out.println("Total of " + referencedPackageSet.size() + " packages");
826 for (ClassDescriptor d : referencedClassSet) {
827 System.out.println(" " + d);
828 }
829 }
830 }
831
832 public List<ClassDescriptor> sortByCallGraph(Collection<ClassDescriptor> classList, OutEdges<ClassDescriptor> outEdges) {
833 List<ClassDescriptor> evaluationOrder = edu.umd.cs.findbugs.util.TopologicalSort.sortByCallGraph(classList, outEdges);
834 edu.umd.cs.findbugs.util.TopologicalSort.countBadEdges(evaluationOrder, outEdges);
835 return evaluationOrder;
836
837 }
838
839 public static void clearAnalysisContext() {
840 AnalysisContext.removeCurrentAnalysisContext();
841 }
842
843
844
845
846
847
848
849
850
851
852
853
854 public static void createAnalysisContext(Project project, List<ClassDescriptor> appClassList,
855 @CheckForNull String sourceInfoFileName) throws IOException {
856 AnalysisContext analysisContext = new AnalysisContext(project);
857
858
859 AnalysisContext.setCurrentAnalysisContext(analysisContext);
860
861
862
863 analysisContext.clearRepository();
864
865
866 if (sourceInfoFileName != null) {
867 SourceInfoMap sourceInfoMap = analysisContext.getSourceInfoMap();
868 sourceInfoMap.read(new FileInputStream(sourceInfoFileName));
869 }
870 }
871
872 public static void setAppClassList(List<ClassDescriptor> appClassList) {
873 AnalysisContext analysisContext = AnalysisContext
874 .currentAnalysisContext();
875
876 analysisContext.setAppClassList(appClassList);
877 }
878
879
880
881
882 private void configureAnalysisFeatures() {
883 for (AnalysisFeatureSetting setting : analysisOptions.analysisFeatureSettingList) {
884 setting.configure(AnalysisContext.currentAnalysisContext());
885 }
886 AnalysisContext.currentAnalysisContext().setBoolProperty(AnalysisFeatures.MERGE_SIMILAR_WARNINGS,
887 analysisOptions.mergeSimilarWarnings);
888 }
889
890
891
892
893
894
895
896 private void createExecutionPlan() throws OrderingConstraintException {
897 executionPlan = new ExecutionPlan();
898
899
900 DetectorFactoryChooser detectorFactoryChooser = new DetectorFactoryChooser() {
901 HashSet<DetectorFactory> forcedEnabled = new HashSet<>();
902
903 @Override
904 public boolean choose(DetectorFactory factory) {
905 boolean result = FindBugs.isDetectorEnabled(FindBugs2.this, factory, rankThreshold) || forcedEnabled.contains(factory);
906 if (ExecutionPlan.DEBUG) {
907 System.out.printf(" %6s %s %n", result, factory.getShortName());
908 }
909 return result;
910 }
911
912 @Override
913 public void enable(DetectorFactory factory) {
914 forcedEnabled.add(factory);
915 factory.setEnabledButNonReporting(true);
916 }
917
918 };
919 executionPlan.setDetectorFactoryChooser(detectorFactoryChooser);
920
921 if (ExecutionPlan.DEBUG) {
922 System.out.println("rank threshold is " + rankThreshold);
923 }
924
925 for (Iterator<Plugin> i = detectorFactoryCollection.pluginIterator(); i.hasNext();) {
926 Plugin plugin = i.next();
927 if (DEBUG) {
928 System.out.println("Adding plugin " + plugin.getPluginId() + " to execution plan");
929 }
930 executionPlan.addPlugin(plugin);
931 }
932
933
934 executionPlan.build();
935
936
937 Global.getAnalysisCache().eagerlyPutDatabase(ExecutionPlan.class, executionPlan);
938
939 if (PROGRESS) {
940 System.out.println(executionPlan.getNumPasses() + " passes in execution plan");
941 }
942 }
943
944
945
946
947 private void analyzeApplication() throws InterruptedException {
948 int passCount = 0;
949 Profiler profiler = bugReporter.getProjectStats().getProfiler();
950 profiler.start(this.getClass());
951 AnalysisContext.currentXFactory().canonicalizeAll();
952 try {
953 boolean multiplePasses = executionPlan.getNumPasses() > 1;
954 if (executionPlan.getNumPasses() == 0) {
955 throw new AssertionError("no analysis passes");
956 }
957 int[] classesPerPass = new int[executionPlan.getNumPasses()];
958 classesPerPass[0] = referencedClassSet.size();
959 for (int i = 0; i < classesPerPass.length; i++) {
960 classesPerPass[i] = i == 0 ? referencedClassSet.size() : appClassList.size();
961 }
962 progress.predictPassCount(classesPerPass);
963 XFactory factory = AnalysisContext.currentXFactory();
964 Collection<ClassDescriptor> badClasses = new LinkedList<>();
965 for (ClassDescriptor desc : referencedClassSet) {
966 try {
967 XClass info = Global.getAnalysisCache().getClassAnalysis(XClass.class, desc);
968 factory.intern(info);
969 } catch (CheckedAnalysisException e) {
970 AnalysisContext.logError("Couldn't get class info for " + desc, e);
971 badClasses.add(desc);
972 } catch (RuntimeException e) {
973 AnalysisContext.logError("Couldn't get class info for " + desc, e);
974 badClasses.add(desc);
975 }
976 }
977 if (!badClasses.isEmpty()) {
978 referencedClassSet = new LinkedHashSet<>(referencedClassSet);
979 referencedClassSet.removeAll(badClasses);
980 }
981
982 long startTime = System.currentTimeMillis();
983 bugReporter.getProjectStats().setReferencedClasses(referencedClassSet.size());
984 for (Iterator<AnalysisPass> passIterator = executionPlan.passIterator(); passIterator.hasNext();) {
985 AnalysisPass pass = passIterator.next();
986
987
988 boolean isNonReportingFirstPass = multiplePasses && passCount == 0;
989
990
991 Detector2[] detectorList = pass.instantiateDetector2sInPass(bugReporter);
992
993
994
995
996
997
998 Collection<ClassDescriptor> classCollection = (isNonReportingFirstPass) ? referencedClassSet : appClassList;
999 AnalysisContext.currentXFactory().canonicalizeAll();
1000 if (PROGRESS || LIST_ORDER) {
1001 System.out.printf("%6d : Pass %d: %d classes%n", (System.currentTimeMillis() - startTime)/1000, passCount, classCollection.size());
1002 if (DEBUG) {
1003 XFactory.profile();
1004 }
1005 }
1006 if (!isNonReportingFirstPass) {
1007 OutEdges<ClassDescriptor> outEdges = e -> {
1008 try {
1009 XClass classNameAndInfo = Global.getAnalysisCache().getClassAnalysis(XClass.class, e);
1010 return classNameAndInfo.getCalledClassDescriptors();
1011 } catch (CheckedAnalysisException e2) {
1012 AnalysisContext.logError("error while analyzing " + e.getClassName(), e2);
1013 return Collections.emptyList();
1014
1015 }
1016 };
1017
1018 classCollection = sortByCallGraph(classCollection, outEdges);
1019 }
1020 if (LIST_ORDER) {
1021 System.out.println("Analysis order:");
1022 for (ClassDescriptor c : classCollection) {
1023 System.out.println(" " + c);
1024 }
1025 }
1026 AnalysisContext currentAnalysisContext = AnalysisContext.currentAnalysisContext();
1027 currentAnalysisContext.updateDatabases(passCount);
1028
1029 progress.startAnalysis(classCollection.size());
1030 int count = 0;
1031 Global.getAnalysisCache().purgeAllMethodAnalysis();
1032 Global.getAnalysisCache().purgeClassAnalysis(FBClassReader.class);
1033 for (ClassDescriptor classDescriptor : classCollection) {
1034 long classStartNanoTime = 0;
1035 if (PROGRESS) {
1036 classStartNanoTime = System.nanoTime();
1037 System.out.printf("%6d %d/%d %d/%d %s%n", (System.currentTimeMillis() - startTime)/1000,
1038 passCount, executionPlan.getNumPasses(), count,
1039 classCollection.size(), classDescriptor);
1040 }
1041 count++;
1042
1043
1044
1045
1046 if ((SCREEN_FIRST_PASS_CLASSES || !isNonReportingFirstPass)
1047 && !classScreener.matches(classDescriptor.toResourceName())) {
1048 if (DEBUG) {
1049 System.out.println("*** Excluded by class screener");
1050 }
1051 continue;
1052 }
1053 boolean isHuge = currentAnalysisContext.isTooBig(classDescriptor);
1054 if (isHuge && currentAnalysisContext.isApplicationClass(classDescriptor)) {
1055 bugReporter.reportBug(new BugInstance("SKIPPED_CLASS_TOO_BIG", Priorities.NORMAL_PRIORITY)
1056 .addClass(classDescriptor));
1057 }
1058 currentClassName = ClassName.toDottedClassName(classDescriptor.getClassName());
1059 notifyClassObservers(classDescriptor);
1060 profiler.startContext(currentClassName);
1061 currentAnalysisContext.setClassBeingAnalyzed(classDescriptor);
1062
1063 try {
1064 for (Detector2 detector : detectorList) {
1065 if (Thread.interrupted()) {
1066 throw new InterruptedException();
1067 }
1068 if (isHuge && !FirstPassDetector.class.isAssignableFrom(detector.getClass())) {
1069 continue;
1070 }
1071 if (DEBUG) {
1072 System.out.println("Applying " + detector.getDetectorClassName() + " to " + classDescriptor);
1073
1074
1075
1076 }
1077 try {
1078 profiler.start(detector.getClass());
1079 detector.visitClass(classDescriptor);
1080 } catch (ClassFormatException e) {
1081 logRecoverableException(classDescriptor, detector, e);
1082 } catch (MissingClassException e) {
1083 Global.getAnalysisCache().getErrorLogger().reportMissingClass(e.getClassDescriptor());
1084 } catch (CheckedAnalysisException e) {
1085 logRecoverableException(classDescriptor, detector, e);
1086 } catch (RuntimeException e) {
1087 logRecoverableException(classDescriptor, detector, e);
1088 } finally {
1089 profiler.end(detector.getClass());
1090 }
1091 }
1092 } finally {
1093
1094 progress.finishClass();
1095 profiler.endContext(currentClassName);
1096 currentAnalysisContext.clearClassBeingAnalyzed();
1097 if (PROGRESS) {
1098 long usecs = (System.nanoTime() - classStartNanoTime)/1000;
1099 if (usecs > 15000) {
1100 int classSize = currentAnalysisContext.getClassSize(classDescriptor);
1101 long speed = usecs /classSize;
1102 if (speed > 15) {
1103 System.out.printf(" %6d usecs/byte %6d msec %6d bytes %d pass %s%n", speed, usecs/1000, classSize, passCount,
1104 classDescriptor);
1105 }
1106 }
1107
1108 }
1109 }
1110 }
1111
1112
1113 for (Detector2 detector : detectorList) {
1114 detector.finishPass();
1115 }
1116
1117 progress.finishPerClassAnalysis();
1118
1119 passCount++;
1120 }
1121
1122
1123 } finally {
1124
1125 bugReporter.finish();
1126 bugReporter.reportQueuedErrors();
1127 profiler.end(this.getClass());
1128 if (PROGRESS) {
1129 System.out.println("Analysis completed");
1130 }
1131 }
1132
1133 }
1134
1135
1136
1137
1138
1139
1140
1141 private void notifyClassObservers(ClassDescriptor classDescriptor) {
1142 for (IClassObserver observer : classObserverList) {
1143 observer.observeClass(classDescriptor);
1144 }
1145 }
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158 private void logRecoverableException(ClassDescriptor classDescriptor, Detector2 detector, Throwable e) {
1159 bugReporter.logError(
1160 "Exception analyzing " + classDescriptor.toDottedClassName() + " using detector "
1161 + detector.getDetectorClassName(), e);
1162 }
1163
1164 public static void main(String[] args) throws Exception {
1165
1166 if (!CheckBcel.check()) {
1167 System.exit(1);
1168 }
1169
1170
1171 FindBugs2 findBugs = new FindBugs2();
1172
1173
1174 TextUICommandLine commandLine = new TextUICommandLine();
1175 FindBugs.processCommandLine(commandLine, args, findBugs);
1176
1177
1178 boolean justPrintConfiguration = commandLine.justPrintConfiguration();
1179 if (justPrintConfiguration || commandLine.justPrintVersion()) {
1180 Version.printVersion(justPrintConfiguration);
1181
1182 return;
1183 }
1184
1185
1186
1187 FindBugs.runMain(findBugs, commandLine);
1188
1189 }
1190
1191
1192 @Override
1193 public void setAbridgedMessages(boolean xmlWithAbridgedMessages) {
1194 analysisOptions.abridgedMessages = xmlWithAbridgedMessages;
1195 }
1196
1197 @Override
1198 public void setMergeSimilarWarnings(boolean mergeSimilarWarnings) {
1199 this.analysisOptions.mergeSimilarWarnings = mergeSimilarWarnings;
1200 }
1201
1202 @Override
1203 public void setApplySuppression(boolean applySuppression) {
1204 this.analysisOptions.applySuppression = applySuppression;
1205 }
1206
1207 @Override
1208 public void setRankThreshold(int rankThreshold) {
1209 this.rankThreshold = rankThreshold;
1210 }
1211
1212 @Override
1213 public void finishSettings() {
1214 if (analysisOptions.applySuppression) {
1215 bugReporter = new FilterBugReporter(bugReporter, getProject().getSuppressionFilter(), false);
1216 }
1217 }
1218
1219 @Nonnull
1220 Set<String> explicitlyEnabledBugReporterDecorators = Collections.emptySet();
1221
1222 @Nonnull
1223 Set<String> explicitlyDisabledBugReporterDecorators = Collections.emptySet();
1224
1225 @Override
1226 public void setBugReporterDecorators(Set<String> explicitlyEnabled, Set<String> explicitlyDisabled) {
1227 explicitlyEnabledBugReporterDecorators = explicitlyEnabled;
1228 explicitlyDisabledBugReporterDecorators = explicitlyDisabled;
1229 }
1230
1231 }