You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
569 lines
16 KiB
569 lines
16 KiB
/*
|
|
* Copyright (c) 2013- Facebook.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
package utils;
|
|
|
|
import com.google.common.base.Joiner;
|
|
import com.google.common.collect.ImmutableList;
|
|
|
|
import org.junit.rules.TemporaryFolder;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.InputStreamReader;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
public class InferRunner {
|
|
|
|
public static final String BUGS_FILE_NAME = "report.csv";
|
|
|
|
public static final String DOT_FILE_NAME = "icfg.dot";
|
|
|
|
public static final String CAPTURED_FOLDER = "captured";
|
|
|
|
private static File bugsFile;
|
|
|
|
@Nullable
|
|
private static File dotFile;
|
|
|
|
private static File resultsDir;
|
|
|
|
private static final ImmutableList<String> EMPTY_ARGS = ImmutableList.of();
|
|
|
|
private static final String ANDROID =
|
|
"/infer/lib/java/android/android-19.jar";
|
|
|
|
private static final String[] LIBRARIES = {
|
|
"/infer/annotations/annotations.jar",
|
|
"/dependencies/java/guava/guava-10.0.1-fork.jar",
|
|
"/dependencies/java/jackson/jackson-2.2.3.jar",
|
|
};
|
|
|
|
private static final String IOS_ISYSROOT_SUFFIX =
|
|
"/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk";
|
|
|
|
private static HashMap<String, InferResults> inferResultsMap =
|
|
new HashMap<String, InferResults>();
|
|
|
|
private InferRunner() {
|
|
}
|
|
|
|
private static String getXcodeRoot() throws IOException, InterruptedException {
|
|
ProcessBuilder pb = new ProcessBuilder("xcode-select", "-p");
|
|
Process process = pb.start();
|
|
InputStream is = process.getInputStream();
|
|
InputStreamReader isr = new InputStreamReader(is);
|
|
BufferedReader br = new BufferedReader(isr);
|
|
String line = br.readLine();
|
|
process.waitFor();
|
|
return line;
|
|
}
|
|
|
|
private static ImmutableList<String> createInferJavaCommand(
|
|
TemporaryFolder folder,
|
|
ImmutableList<String> sourceFiles,
|
|
String analyzer,
|
|
ImmutableList<String> args) {
|
|
try {
|
|
File resultsDir = createResultsDir(folder);
|
|
String resultsDirName = resultsDir.getAbsolutePath();
|
|
InferRunner.bugsFile = new File(resultsDir, BUGS_FILE_NAME);
|
|
|
|
ImmutableList<String> javacCmd = createJavacCommand(
|
|
folder,
|
|
sourceFiles);
|
|
|
|
return new ImmutableList.Builder<String>()
|
|
.add("infer")
|
|
.add("--no-filtering")
|
|
.addAll(args)
|
|
.add("-o")
|
|
.add(resultsDirName)
|
|
.add("-a")
|
|
.add(analyzer)
|
|
.add("--")
|
|
.addAll(javacCmd)
|
|
.build();
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
System.exit(1);
|
|
return ImmutableList.of(); // unreachable
|
|
}
|
|
}
|
|
|
|
private static ImmutableList<String> createJavacCommand(
|
|
TemporaryFolder folder,
|
|
ImmutableList<String> sourceFiles)
|
|
throws IOException {
|
|
File classesDir = folder.newFolder("classes_out");
|
|
String classesDirName = classesDir.getAbsolutePath();
|
|
|
|
ImmutableList<String> javacCmd = new ImmutableList.Builder<String>()
|
|
.add("javac")
|
|
.add("-classpath")
|
|
.add(getClasspath())
|
|
.add("-bootclasspath")
|
|
.add(getBootClasspath())
|
|
.add("-d")
|
|
.add(classesDirName)
|
|
.addAll(sourceFiles).build();
|
|
return javacCmd;
|
|
}
|
|
|
|
public static ImmutableList<String> createJavaInferHarnessCommand(
|
|
TemporaryFolder folder,
|
|
ImmutableList<String> sourceFiles) {
|
|
ImmutableList<String> args = new ImmutableList.Builder<String>().build();
|
|
return createInferJavaCommand(folder, sourceFiles, "infer", args);
|
|
}
|
|
|
|
public static ImmutableList<String> createJavaInferHarnessCommand(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createJavaInferHarnessCommand(folder, ImmutableList.of(sourceFile));
|
|
}
|
|
|
|
public static ImmutableList<String> createJavaInferAngelicHarnessCommand(
|
|
TemporaryFolder folder,
|
|
ImmutableList<String> sourceFiles) {
|
|
ImmutableList<String> args = (new ImmutableList.Builder<String>()).build();
|
|
return createInferJavaCommand(folder, sourceFiles, "infer", args);
|
|
}
|
|
|
|
public static String getClangLangOption(Language lang) {
|
|
String langOption = "";
|
|
switch (lang) {
|
|
case C:
|
|
langOption = "c";
|
|
break;
|
|
|
|
case ObjC:
|
|
langOption = "objective-c";
|
|
break;
|
|
|
|
case CPP:
|
|
langOption = "c++";
|
|
break;
|
|
|
|
case ObjCPP:
|
|
langOption = "objective-c++";
|
|
break;
|
|
|
|
default:
|
|
throw new RuntimeException(
|
|
"It should be called only with the "
|
|
+ "languages (C, C++, ObjC, ObjC++)");
|
|
}
|
|
return langOption;
|
|
}
|
|
|
|
public static ImmutableList<String> createClangCommand(
|
|
String sourceFile,
|
|
Language lang,
|
|
@Nullable String isysroot,
|
|
boolean arc) {
|
|
ImmutableList.Builder<String> isysrootOption =
|
|
new ImmutableList.Builder<>();
|
|
if (isysroot != null) {
|
|
isysrootOption
|
|
.add("-isysroot")
|
|
.add(isysroot)
|
|
.add("-mios-simulator-version-min=8.2");
|
|
}
|
|
ImmutableList.Builder<String> arcOption =
|
|
new ImmutableList.Builder<>();
|
|
if (arc) {
|
|
arcOption.add("-fobjc-arc");
|
|
}
|
|
ImmutableList<String> clangCmd = new ImmutableList.Builder<String>()
|
|
.add("clang")
|
|
.add("-x")
|
|
.add(getClangLangOption(lang))
|
|
.addAll(isysrootOption.build())
|
|
.addAll(arcOption.build())
|
|
.add("-c")
|
|
.add(sourceFile)
|
|
.add("-o")
|
|
.add(sourceFile + ".o")
|
|
.build();
|
|
return clangCmd;
|
|
}
|
|
|
|
public static ImmutableList<String> createClangInferCommand(
|
|
TemporaryFolder folder,
|
|
String sourceFile,
|
|
Language lang,
|
|
boolean analyze,
|
|
@Nullable String isysroot,
|
|
@Nullable String ml_buckets,
|
|
boolean arc) {
|
|
File resultsDir = createResultsDir(folder);
|
|
String resultsDirName = resultsDir.getAbsolutePath();
|
|
InferRunner.bugsFile = new File(resultsDir, BUGS_FILE_NAME);
|
|
|
|
ImmutableList.Builder<String> analyzeOption =
|
|
new ImmutableList.Builder<>();
|
|
if (!analyze) {
|
|
analyzeOption
|
|
.add("--analyzer")
|
|
.add("capture");
|
|
}
|
|
ImmutableList.Builder<String> ml_bucketsOption =
|
|
new ImmutableList.Builder<>();
|
|
ml_bucketsOption
|
|
.add("--objc_ml_buckets")
|
|
.add(ml_buckets == null ? "all" : ml_buckets);
|
|
ImmutableList<String> inferCmd = new ImmutableList.Builder<String>()
|
|
.add("infer")
|
|
.add("--no-filtering")
|
|
.add("--out")
|
|
.add(resultsDirName)
|
|
.add("--testing_mode")
|
|
.addAll(analyzeOption.build())
|
|
.addAll(ml_bucketsOption.build())
|
|
.add("--")
|
|
.addAll(createClangCommand(sourceFile, lang, isysroot, arc))
|
|
.build();
|
|
return inferCmd;
|
|
}
|
|
|
|
public static ImmutableList<String> createCInferCommandFrontend(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.C,
|
|
false,
|
|
null,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createCPPInferCommandFrontend(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.CPP,
|
|
false,
|
|
null,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createObjCInferCommandFrontend(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjC,
|
|
false,
|
|
null,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createObjCInferCommandFrontendArc(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjC,
|
|
false,
|
|
null,
|
|
null,
|
|
true);
|
|
}
|
|
|
|
public static ImmutableList<String> createObjCPPInferCommandFrontend(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjCPP,
|
|
false,
|
|
null,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createCInferCommand(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.C,
|
|
true,
|
|
null,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createCPPInferCommand(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.CPP,
|
|
true,
|
|
null,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createObjCInferCommand(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjC,
|
|
true,
|
|
null,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createObjCInferCommandWithMLBuckets(
|
|
TemporaryFolder folder,
|
|
String sourceFile,
|
|
String ml_bucket,
|
|
boolean arc) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjC,
|
|
true,
|
|
null,
|
|
ml_bucket,
|
|
arc);
|
|
}
|
|
|
|
public static ImmutableList<String> createObjCPPInferCommand(
|
|
TemporaryFolder folder,
|
|
String sourceFile) {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjCPP,
|
|
true,
|
|
null,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createiOSInferCommandFrontend(
|
|
TemporaryFolder folder,
|
|
String sourceFile) throws IOException, InterruptedException {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjC,
|
|
false,
|
|
getXcodeRoot() + IOS_ISYSROOT_SUFFIX,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createiOSInferCommand(
|
|
TemporaryFolder folder,
|
|
String sourceFile) throws IOException, InterruptedException {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjC,
|
|
true,
|
|
getXcodeRoot() + IOS_ISYSROOT_SUFFIX,
|
|
null,
|
|
false);
|
|
}
|
|
|
|
public static ImmutableList<String> createiOSInferCommandWithMLBuckets(
|
|
TemporaryFolder folder,
|
|
String sourceFile,
|
|
String bucket,
|
|
boolean arc) throws IOException, InterruptedException {
|
|
return createClangInferCommand(
|
|
folder,
|
|
sourceFile,
|
|
Language.ObjC,
|
|
true,
|
|
getXcodeRoot() + IOS_ISYSROOT_SUFFIX,
|
|
bucket,
|
|
arc);
|
|
}
|
|
|
|
@Nullable
|
|
public static File runInferFrontend(ImmutableList<String> inferCmd)
|
|
throws IOException, InterruptedException, InferException {
|
|
runCommand(inferCmd, TestType.FRONTEND);
|
|
return dotFile;
|
|
}
|
|
|
|
private static InferResults runInfer(
|
|
ImmutableList<String> inferCmd,
|
|
Language lang)
|
|
throws IOException, InterruptedException, InferException {
|
|
String inferCmdString = Joiner.on(' ').join(inferCmd);
|
|
InferResults results = inferResultsMap.get(inferCmdString);
|
|
if (results == null) {
|
|
if (lang == Language.Java) checkLibraries();
|
|
|
|
runCommand(inferCmd, TestType.END_TO_END);
|
|
String errorString = getErrors(bugsFile);
|
|
results = new InferResults(inferCmd);
|
|
if (lang == Language.Java)
|
|
results.parseJavaInferResultsFromString(errorString);
|
|
else if (lang == Language.C || lang == Language.CPP || lang == Language.ObjCPP)
|
|
results.parseCInferResultsFromString(errorString);
|
|
else if (lang == Language.ObjC)
|
|
results.parseObjCInferResultsFromString(errorString);
|
|
inferResultsMap.put(inferCmdString, results);
|
|
}
|
|
return results;
|
|
}
|
|
|
|
public static InferResults runInferJava(ImmutableList<String> inferCmd)
|
|
throws IOException, InterruptedException, InferException {
|
|
return runInfer(inferCmd, Language.Java);
|
|
}
|
|
|
|
public static InferResults runInferC(ImmutableList<String> inferCmd)
|
|
throws IOException, InterruptedException, InferException {
|
|
return runInfer(inferCmd, Language.C);
|
|
}
|
|
|
|
public static InferResults runInferCPP(ImmutableList<String> inferCmd)
|
|
throws IOException, InterruptedException, InferException {
|
|
return runInfer(inferCmd, Language.CPP);
|
|
}
|
|
|
|
public static InferResults runInferObjC(ImmutableList<String> inferCmd)
|
|
throws IOException, InterruptedException, InferException {
|
|
return runInfer(inferCmd, Language.ObjC);
|
|
}
|
|
|
|
public static InferResults runInferObjCPP(ImmutableList<String> inferCmd)
|
|
throws IOException, InterruptedException, InferException {
|
|
return runInfer(inferCmd, Language.ObjCPP);
|
|
}
|
|
|
|
|
|
private static void runCommand(
|
|
ImmutableList<String> inferCmd,
|
|
TestType testType)
|
|
throws IOException, InterruptedException, InferException {
|
|
ProcessBuilder pb = new ProcessBuilder(inferCmd);
|
|
|
|
Map<String, String> env = pb.environment();
|
|
env.put("REPORT_ASSERTION_FAILURE", "1");
|
|
|
|
Process process = pb.start();
|
|
StringBuilder stderr = new StringBuilder();
|
|
try (BufferedReader bufferedReader =
|
|
new BufferedReader(
|
|
new InputStreamReader(
|
|
process.getErrorStream()))) {
|
|
String line = null;
|
|
while ((line = bufferedReader.readLine()) != null) {
|
|
stderr.append(line + "\n");
|
|
}
|
|
}
|
|
process.waitFor();
|
|
|
|
File file = null;
|
|
if (testType == TestType.END_TO_END) {
|
|
file = bugsFile;
|
|
} else if (testType == TestType.FRONTEND) {
|
|
getDotFile();
|
|
file = dotFile;
|
|
}
|
|
if (file == null || !file.exists()) {
|
|
String fileNotFoundMessage = file == null ?
|
|
"" : "\n\nFile " + file + " not found.";
|
|
throw new InferException(
|
|
"There was an error while calling Infer." +
|
|
"\n\nCommand output:\n" + stderr +
|
|
"\nCommand: " + Joiner.on(' ').join(inferCmd) +
|
|
fileNotFoundMessage +
|
|
"\n==========================================================================");
|
|
}
|
|
}
|
|
|
|
static String getErrors(File file) throws IOException {
|
|
String s = "";
|
|
String line = "";
|
|
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
|
|
br.readLine();
|
|
while ((line = br.readLine()) != null) {
|
|
s += line + "\n";
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
private static String getBootClasspath() {
|
|
String current_dir = System.getProperty("user.dir");
|
|
String bootclasspath = current_dir + ANDROID;
|
|
return bootclasspath;
|
|
}
|
|
|
|
private static String getClasspath() {
|
|
String current_dir = System.getProperty("user.dir");
|
|
StringBuilder classpath = new StringBuilder();
|
|
for (String library : LIBRARIES) {
|
|
classpath.append(current_dir);
|
|
classpath.append(library);
|
|
classpath.append(":");
|
|
}
|
|
classpath.deleteCharAt(classpath.length() - 1);
|
|
return classpath.toString();
|
|
}
|
|
|
|
private static void checkLibraries() throws InferException {
|
|
String current_dir = System.getProperty("user.dir");
|
|
for (String lib_file : LIBRARIES) {
|
|
File lib = new File(current_dir + lib_file);
|
|
if (!lib.exists()) {
|
|
throw new InferException("File " + lib_file + " not found.");
|
|
}
|
|
}
|
|
}
|
|
|
|
private static File createResultsDir(TemporaryFolder folder) {
|
|
try {
|
|
resultsDir = folder.newFolder("infer_out");
|
|
return resultsDir;
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
System.exit(1);
|
|
return null; // unreachable
|
|
}
|
|
}
|
|
|
|
private static void getDotFile() {
|
|
File captured = new File(resultsDir, CAPTURED_FOLDER);
|
|
if (captured.exists()) {
|
|
File f = new File(captured, captured.list()[0]);
|
|
dotFile = new File(f, DOT_FILE_NAME);
|
|
} else {
|
|
dotFile = null;
|
|
}
|
|
}
|
|
}
|