/**
* This file is licensed under the University of Illinois/NCSA Open Source License. See LICENSE.TXT for details.
*/
package edu.illinois.keshmesh.detector;
import java.io.IOException;
import java.io.Writer;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.eclipse.jdt.core.IJavaProject;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.ibm.wala.analysis.pointers.BasicHeapGraph;
import com.ibm.wala.analysis.pointers.HeapGraph;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.propagation.HeapModel;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.util.WalaException;
import com.ibm.wala.util.io.FileProvider;
import edu.illinois.keshmesh.config.ConfigurationOptions;
import edu.illinois.keshmesh.constants.Constants;
import edu.illinois.keshmesh.detector.bugs.BugInstances;
import edu.illinois.keshmesh.detector.bugs.BugPattern;
import edu.illinois.keshmesh.detector.bugs.BugPatterns;
import edu.illinois.keshmesh.detector.exception.Exceptions;
import edu.illinois.keshmesh.detector.exception.Exceptions.WALAInitializationException;
import edu.illinois.keshmesh.detector.util.AnalysisUtils;
import edu.illinois.keshmesh.detector.util.DisplayUtils;
import edu.illinois.keshmesh.report.FileWriterFactory;
import edu.illinois.keshmesh.report.KeyValuePair;
import edu.illinois.keshmesh.report.Reporter;
import edu.illinois.keshmesh.report.StringWriterFactory;
import edu.illinois.keshmesh.walaconfig.KeshmeshCGModel;
/**
*
* @author Mohsen Vakilian
* @author Stas Negara
*
*/
public class Main {
private static boolean hasShownGraphs = false;
private final static StringWriterFactory stringWriterFactory = new StringWriterFactory();
public static BugInstances initAndPerformAnalysis(IJavaProject javaProject, Reporter reporter, ConfigurationOptions configurationOptions) throws WALAInitializationException {
BugInstances bugInstances = new BugInstances();
int objectSensitivityLevel = configurationOptions.getObjectSensitivityLevel();
reporter.report(new KeyValuePair("OBJECT_SENSITIVITY_LEVEL", String.valueOf(objectSensitivityLevel)));
BasicAnalysisData basicAnalysisData = initBytecodeAnalysis(javaProject, reporter, configurationOptions);
if (configurationOptions.shouldDumpCallGraph()) {
dumpCallGraph(basicAnalysisData.callGraph);
}
Iterator<BugPattern> bugPatternsIterator = BugPatterns.iterator();
while (bugPatternsIterator.hasNext()) {
BugPattern bugPattern = bugPatternsIterator.next();
BugPatternDetector bugPatternDetector = bugPattern.createBugPatternDetector();
Stopwatch stopWatch = Stopwatch.createStarted();
BugInstances instancesOfCurrentBugPattern = bugPatternDetector.performAnalysis(javaProject, basicAnalysisData);
stopWatch.stop();
reporter.report(new KeyValuePair("BUG_PATTERN_" + bugPattern.getName() + "_DETECTION_TIME_IN_MILLISECONDS", String.valueOf(stopWatch.elapsed(TimeUnit.MILLISECONDS))));
bugInstances.addAll(instancesOfCurrentBugPattern);
reporter.report(new KeyValuePair("NUMBER_OF_INSTANCES_OF_BUG_PATTERN_" + bugPattern.getName(), String.valueOf(instancesOfCurrentBugPattern.size())));
}
reporter.close();
return bugInstances;
}
private static BasicAnalysisData initBytecodeAnalysis(IJavaProject javaProject, Reporter reporter, ConfigurationOptions configurationOptions) throws WALAInitializationException {
KeshmeshCGModel model;
try {
String exclusionsFileName = FileProvider.getFileFromPlugin(Activator.getDefault(), "EclipseDefaultExclusions.txt").getAbsolutePath();
model = new KeshmeshCGModel(javaProject, exclusionsFileName, configurationOptions.getObjectSensitivityLevel());
Stopwatch stopWatch = Stopwatch.createStarted();
model.buildGraph();
stopWatch.stop();
reporter.report(new KeyValuePair("CALL_GRAPH_CONSTRUCTION_TIME_IN_MILLISECONDS", String.valueOf(stopWatch.elapsed(TimeUnit.MILLISECONDS))));
reportEntryPointStatistics(reporter, model.getEntryPoints());
dumpEntryPoints(model.getEntryPoints());
} catch (Exception e) {
throw new Exceptions.WALAInitializationException(e);
}
CallGraph callGraph = model.getGraph();
reportCallGraphStatistics(reporter, callGraph);
PointerAnalysis pointerAnalysis = model.getPointerAnalysis();
HeapModel heapModel = pointerAnalysis.getHeapModel();
BasicHeapGraph heapGraph = new BasicHeapGraph(pointerAnalysis, callGraph);
if (configurationOptions.shouldDumpHeapGraph()) {
dumpHeapGraph(heapGraph);
}
reporter.report(new KeyValuePair("NUMBER_OF_NODES_OF_HEAP_GRAPH", String.valueOf(heapGraph.getNumberOfNodes())));
if (!hasShownGraphs) {
try {
DisplayUtils.displayGraph(callGraph);
DisplayUtils.displayGraph(heapGraph);
hasShownGraphs = true;
} catch (WalaException e) {
throw new WALAInitializationException(e);
}
}
IClassHierarchy classHierarchy = model.getClassHierarchy();
reporter.report(new KeyValuePair("NUMBER_OF_CLASSES", String.valueOf(classHierarchy.getNumberOfClasses())));
return new BasicAnalysisData(classHierarchy, callGraph, pointerAnalysis, heapModel, heapGraph);
}
private static void reportEntryPointStatistics(Reporter reporter, Iterable<Entrypoint> entryPoints) {
reporter.report(new KeyValuePair("NUMBER_OF_ENTRY_POINTS", String.valueOf(Iterables.size(entryPoints))));
}
private static void dumpEntryPoints(Iterable<Entrypoint> entryPoints) {
Writer writer = new FileWriterFactory(Constants.KESHMESH_ENTRY_POINTS_FILE_NAME, stringWriterFactory).create();
List<Entrypoint> sortedEntryPoints = sortedCopy(entryPoints);
try {
writer.write(Joiner.on(Constants.LINE_SEPARATOR).join(sortedEntryPoints));
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static List<Entrypoint> sortedCopy(Iterable<Entrypoint> entryPoints) {
Ordering<Entrypoint> ordering = Ordering.natural().onResultOf(new Function<Entrypoint, String>() {
@Override
public String apply(Entrypoint entryPoint) {
return entryPoint.toString();
}
});
return ordering.sortedCopy(entryPoints);
}
private static void reportCallGraphStatistics(Reporter reporter, CallGraph callGraph) {
reporter.report(new KeyValuePair("NUMBER_OF_NODES_OF_CALL_GRAPH", String.valueOf(callGraph.getNumberOfNodes())));
Iterable<CGNode> primordialCGNodes = Iterables.filter(callGraph, new Predicate<CGNode>() {
@Override
public boolean apply(CGNode cgNode) {
return AnalysisUtils.isJDKClass(cgNode.getMethod().getDeclaringClass());
}
});
reporter.report(new KeyValuePair("NUMBER_OF_PRIMORDIAL_NODES_OF_CALL_GRAPH", String.valueOf(Iterables.size(primordialCGNodes))));
Iterable<CGNode> extensionCGNodes = Iterables.filter(callGraph, new Predicate<CGNode>() {
@Override
public boolean apply(CGNode cgNode) {
return AnalysisUtils.isLibraryClass(cgNode.getMethod().getDeclaringClass());
}
});
reporter.report(new KeyValuePair("NUMBER_OF_EXTENSION_NODES_OF_CALL_GRAPH", String.valueOf(Iterables.size(extensionCGNodes))));
Iterable<CGNode> applicationCGNodes = Iterables.filter(callGraph, new Predicate<CGNode>() {
@Override
public boolean apply(CGNode cgNode) {
return AnalysisUtils.isApplicationClass(cgNode.getMethod().getDeclaringClass());
}
});
reporter.report(new KeyValuePair("NUMBER_OF_APPLICATION_NODES_OF_CALL_GRAPH", String.valueOf(Iterables.size(applicationCGNodes))));
}
private static void dumpCallGraph(CallGraph callGraph) {
Preconditions.checkNotNull(callGraph);
Writer writer = new FileWriterFactory(Constants.KESHMESH_CALL_GRAPH_FILE_NAME, stringWriterFactory).create();
Iterator<CGNode> cgNodesIter = callGraph.iterator();
try {
while (cgNodesIter.hasNext()) {
CGNode cgNode = cgNodesIter.next();
IMethod method = cgNode.getMethod();
if (AnalysisUtils.isJDKClass(method.getDeclaringClass()))
continue;
writer.write("**CGNode:** " + cgNode + "\n");
IR ir = cgNode.getIR();
if (ir != null) {
writer.write("**IR:** " + ir + "\n");
}
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static void dumpHeapGraph(HeapGraph heapGraph) {
Preconditions.checkNotNull(heapGraph);
Writer writer = new FileWriterFactory(Constants.KESHMESH_HEAP_GRAPH_FILE_NAME, stringWriterFactory).create();
Preconditions.checkNotNull(heapGraph);
Iterator<Object> cgNodesIter = heapGraph.iterator();
try {
while (cgNodesIter.hasNext()) {
writer.write(cgNodesIter.next() + "\n");
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}