blob: 1e5d4be1a270e8611dcc912d37fc5ba62d20846d [file] [log] [blame]
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dexfuzz.executors;
import dexfuzz.ExecutionResult;
import dexfuzz.Options;
import dexfuzz.StreamConsumer;
import dexfuzz.listeners.BaseListener;
/**
* Base class containing the common methods for executing a particular backend of ART.
*/
public abstract class Executor {
private StreamConsumer outputConsumer;
private StreamConsumer errorConsumer;
protected ExecutionResult executionResult;
protected String executeClass;
// Set by subclasses.
protected String name;
protected int timeout;
protected BaseListener listener;
protected String testLocation;
protected Architecture architecture;
protected Device device;
private boolean needsCleanCodeCache;
protected Executor(String name, int timeout, BaseListener listener, Architecture architecture,
Device device, boolean needsCleanCodeCache) {
executeClass = Options.executeClass;
if (Options.shortTimeouts) {
this.timeout = 2;
} else {
this.timeout = timeout;
}
this.name = name;
this.listener = listener;
this.architecture = architecture;
this.device = device;
this.needsCleanCodeCache = needsCleanCodeCache;
if (Options.executeOnHost) {
this.testLocation = System.getProperty("user.dir");
} else {
this.testLocation = Options.executeDirectory;
}
outputConsumer = new StreamConsumer();
outputConsumer.start();
errorConsumer = new StreamConsumer();
errorConsumer.start();
}
/**
* Called by subclass Executors in their execute() implementations.
*/
protected ExecutionResult executeCommandWithTimeout(String command, boolean captureOutput) {
String timeoutString = "timeout " + timeout + " ";
return device.executeCommand(timeoutString + device.getExecutionShellPrefix() + command,
captureOutput, outputConsumer, errorConsumer);
}
/**
* Call this to make sure the StreamConsumer threads are stopped.
*/
public void shutdown() {
outputConsumer.shutdown();
errorConsumer.shutdown();
}
/**
* Called by the Fuzzer after each execution has finished, to clear the results.
*/
public void reset() {
executionResult = null;
}
/**
* Called by the Fuzzer to verify the mutated program using the host-side dex2oat.
*/
public boolean verifyOnHost(String programName) {
StringBuilder commandBuilder = new StringBuilder();
commandBuilder.append("dex2oat ");
commandBuilder.append("--instruction-set=").append(architecture.asString());
commandBuilder.append(" --instruction-set-features=default ");
// Select the correct boot image.
commandBuilder.append("--boot-image=").append(device.getAndroidProductOut());
if (device.noBootImageAvailable()) {
commandBuilder.append("/data/art-test/core.art ");
} else {
commandBuilder.append("/system/framework/boot.art ");
}
commandBuilder.append("--oat-file=output.oat ");
commandBuilder.append("--android-root=").append(device.getAndroidHostOut()).append(" ");
commandBuilder.append("--runtime-arg -classpath ");
commandBuilder.append("--runtime-arg ").append(programName).append(" ");
commandBuilder.append("--dex-file=").append(programName).append(" ");
commandBuilder.append("--compiler-filter=interpret-only --runtime-arg -Xnorelocate ");
ExecutionResult verificationResult = device.executeCommand(commandBuilder.toString(), true,
outputConsumer, errorConsumer);
boolean success = true;
if (verificationResult.isSigabort()) {
listener.handleHostVerificationSigabort(verificationResult);
success = false;
}
if (success) {
// Search for a keyword that indicates verification was not successful.
// TODO: Determine if dex2oat crashed?
for (String line : verificationResult.error) {
if (line.contains("Verification error")
|| line.contains("Failure to verify dex file")) {
success = false;
}
if (Options.dumpVerify) {
// Strip out the start of the log lines.
listener.handleDumpVerify(line.replaceFirst(".*(cc|h):\\d+] ", ""));
}
}
}
if (!success) {
listener.handleFailedHostVerification(verificationResult);
}
device.executeCommand("rm output.oat", false);
return success;
}
/**
* Called by the Fuzzer to upload the program to the target device.
*/
public void prepareProgramForExecution(String programName) {
if (!Options.executeOnHost) {
device.pushProgramToDevice(programName, testLocation);
}
if (needsCleanCodeCache) {
// Get the device to clean the code cache
device.cleanCodeCache(architecture, testLocation, programName);
}
}
/**
* Executor subclasses need to override this, to construct their arguments for dalvikvm
* invocation correctly.
*/
public abstract void execute(String programName);
/**
* Fuzzer.checkForArchitectureSplit() will use this determine the architecture of the Executor.
*/
public Architecture getArchitecture() {
return architecture;
}
/**
* Used by the Fuzzer to get result of execution.
*/
public ExecutionResult getResult() {
return executionResult;
}
/**
* Because dex2oat can accept a program with soft errors on the host, and then fail after
* performing hard verification on the target, we need to check if the Executor detected
* a target verification failure, before doing anything else with the resulting output.
* Used by the Fuzzer.
*/
public boolean didTargetVerify() {
// TODO: Remove this once host-verification can be forced to always fail?
String output = executionResult.getFlattenedAll();
if (output.contains("VerifyError") || output.contains("Verification failed on class")) {
return false;
}
return true;
}
public String getName() {
return name;
}
public void finishedWithProgramOnDevice() {
device.resetProgramPushed();
}
}