/**
* Analyze the classes in the application codebase.
*/
private void analyzeApplication() throws InterruptedException {
int passCount = 0;
Profiler profiler = bugReporter.getProjectStats().getProfiler();
profiler.start(this.getClass());
AnalysisContext.currentXFactory().canonicalizeAll();
try {
boolean multiplePasses = executionPlan.getNumPasses() > 1;
if (executionPlan.getNumPasses() == 0) {
throw new AssertionError("no analysis passes");
}
int[] classesPerPass = new int[executionPlan.getNumPasses()];
classesPerPass[0] = referencedClassSet.size();
for (int i = 0; i < classesPerPass.length; i++) {
classesPerPass[i] = i == 0 ? referencedClassSet.size() : appClassList.size();
}
progress.predictPassCount(classesPerPass);
XFactory factory = AnalysisContext.currentXFactory();
Collection<ClassDescriptor> badClasses = new LinkedList<ClassDescriptor>();
for (ClassDescriptor desc : referencedClassSet) {
try {
XClass info = Global.getAnalysisCache().getClassAnalysis(XClass.class, desc);
factory.intern(info);
} catch (CheckedAnalysisException e) {
AnalysisContext.logError("Couldn't get class info for " + desc, e);
badClasses.add(desc);
} catch (RuntimeException e) {
AnalysisContext.logError("Couldn't get class info for " + desc, e);
badClasses.add(desc);
}
}
if (!badClasses.isEmpty()) {
referencedClassSet = new LinkedHashSet<ClassDescriptor>(referencedClassSet);
referencedClassSet.removeAll(badClasses);
}
long startTime = System.currentTimeMillis();
bugReporter.getProjectStats().setReferencedClasses(referencedClassSet.size());
for (Iterator<AnalysisPass> passIterator = executionPlan.passIterator(); passIterator.hasNext();) {
AnalysisPass pass = passIterator.next();
yourkitController.advanceGeneration("Pass " + passCount);
// The first pass is generally a non-reporting pass which
// gathers information about referenced classes.
boolean isNonReportingFirstPass = multiplePasses && passCount == 0;
// Instantiate the detectors
Detector2[] detectorList = pass.instantiateDetector2sInPass(bugReporter);
// If there are multiple passes, then on the first pass,
// we apply detectors to all classes referenced by the
// application classes.
// On subsequent passes, we apply detector only to application
// classes.
Collection<ClassDescriptor> classCollection = (isNonReportingFirstPass) ? referencedClassSet : appClassList;
AnalysisContext.currentXFactory().canonicalizeAll();
if (PROGRESS || LIST_ORDER) {
System.out.printf("%6d : Pass %d: %d classes%n", (System.currentTimeMillis() - startTime)/1000, passCount, classCollection.size());
if (DEBUG) {
XFactory.profile();
}
}
if (!isNonReportingFirstPass) {
OutEdges<ClassDescriptor> outEdges = new OutEdges<ClassDescriptor>() {
@Override
public Collection<ClassDescriptor> getOutEdges(ClassDescriptor e) {
try {
XClass classNameAndInfo = Global.getAnalysisCache().getClassAnalysis(XClass.class, e);
return classNameAndInfo.getCalledClassDescriptors();
} catch (CheckedAnalysisException e2) {
AnalysisContext.logError("error while analyzing " + e.getClassName(), e2);
return Collections.emptyList();
}
}
};
classCollection = sortByCallGraph(classCollection, outEdges);
}
if (LIST_ORDER) {
System.out.println("Analysis order:");
for (ClassDescriptor c : classCollection) {
System.out.println(" " + c);
}
}
AnalysisContext currentAnalysisContext = AnalysisContext.currentAnalysisContext();
currentAnalysisContext.updateDatabases(passCount);
progress.startAnalysis(classCollection.size());
int count = 0;
Global.getAnalysisCache().purgeAllMethodAnalysis();
Global.getAnalysisCache().purgeClassAnalysis(FBClassReader.class);
for (ClassDescriptor classDescriptor : classCollection) {
long classStartNanoTime = 0;
if (PROGRESS) {
classStartNanoTime = System.nanoTime();
System.out.printf("%6d %d/%d %d/%d %s%n", (System.currentTimeMillis() - startTime)/1000,
passCount, executionPlan.getNumPasses(), count,
classCollection.size(), classDescriptor);
}
count++;
if (!isNonReportingFirstPass && count % 1000 == 0) {
yourkitController.advanceGeneration(String.format("Pass %d.%02d", passCount, count/1000));
}
// Check to see if class is excluded by the class screener.
// In general, we do not want to screen classes from the
// first pass, even if they would otherwise be excluded.
if ((SCREEN_FIRST_PASS_CLASSES || !isNonReportingFirstPass)
&& !classScreener.matches(classDescriptor.toResourceName())) {
if (DEBUG) {
System.out.println("*** Excluded by class screener");
}
continue;
}
boolean isHuge = currentAnalysisContext.isTooBig(classDescriptor);
if (isHuge && currentAnalysisContext.isApplicationClass(classDescriptor)) {
bugReporter.reportBug(new BugInstance("SKIPPED_CLASS_TOO_BIG", Priorities.NORMAL_PRIORITY)
.addClass(classDescriptor));
}
currentClassName = ClassName.toDottedClassName(classDescriptor.getClassName());
notifyClassObservers(classDescriptor);
profiler.startContext(currentClassName);
currentAnalysisContext.setClassBeingAnalyzed(classDescriptor);
try {
for (Detector2 detector : detectorList) {
if (Thread.interrupted()) {
throw new InterruptedException();
}
if (isHuge && !FirstPassDetector.class.isAssignableFrom(detector.getClass())) {
continue;
}
if (DEBUG) {
System.out.println("Applying " + detector.getDetectorClassName() + " to " + classDescriptor);
// System.out.println("foo: " +
// NonReportingDetector.class.isAssignableFrom(detector.getClass())
// + ", bar: " + detector.getClass().getName());
}
try {
profiler.start(detector.getClass());
detector.visitClass(classDescriptor);
} catch (ClassFormatException e) {
logRecoverableException(classDescriptor, detector, e);
} catch (MissingClassException e) {
Global.getAnalysisCache().getErrorLogger().reportMissingClass(e.getClassDescriptor());
} catch (CheckedAnalysisException e) {
logRecoverableException(classDescriptor, detector, e);
} catch (RuntimeException e) {
logRecoverableException(classDescriptor, detector, e);
} finally {
profiler.end(detector.getClass());
}
}
} finally {
progress.finishClass();
profiler.endContext(currentClassName);
currentAnalysisContext.clearClassBeingAnalyzed();
if (PROGRESS) {
long usecs = (System.nanoTime() - classStartNanoTime)/1000;
if (usecs > 15000) {
int classSize = currentAnalysisContext.getClassSize(classDescriptor);
long speed = usecs /classSize;
if (speed > 15) {
System.out.printf(" %6d usecs/byte %6d msec %6d bytes %d pass %s%n", speed, usecs/1000, classSize, passCount,
classDescriptor);
}
}
}
}
}
if (!passIterator.hasNext()) {
yourkitController.captureMemorySnapshot();
}
// Call finishPass on each detector
for (Detector2 detector : detectorList) {
detector.finishPass();
}
progress.finishPerClassAnalysis();
passCount++;
}
} finally {
bugReporter.finish();
bugReporter.reportQueuedErrors();
profiler.end(this.getClass());
if (PROGRESS) {
System.out.println("Analysis completed");
}
}