summaryrefslogtreecommitdiffstats
path: root/tools/dexfuzz/src/dexfuzz/executors/Device.java
diff options
context:
space:
mode:
Diffstat (limited to 'tools/dexfuzz/src/dexfuzz/executors/Device.java')
-rw-r--r--tools/dexfuzz/src/dexfuzz/executors/Device.java190
1 files changed, 173 insertions, 17 deletions
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Device.java b/tools/dexfuzz/src/dexfuzz/executors/Device.java
index 8c03103..736aaad 100644
--- a/tools/dexfuzz/src/dexfuzz/executors/Device.java
+++ b/tools/dexfuzz/src/dexfuzz/executors/Device.java
@@ -16,22 +16,36 @@
package dexfuzz.executors;
+import java.io.IOException;
+import java.util.Map;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.StreamConsumer;
+
/**
- * Handles execution either on a remote device, or locally.
- * Currently only remote execution, on an ADB-connected device, is supported.
+ * Handles execution either on a remote target device, or on a local host computer.
*/
public class Device {
- private boolean isLocal;
+ private boolean isHost;
private String deviceName;
private boolean usingSpecificDevice;
private boolean noBootImage;
+ private String androidHostOut;
+ private String androidProductOut;
+ private String androidData;
+
+ private boolean programPushed;
+
/**
- * The constructor for a local "device". Not yet supported.
+ * The constructor for a host "device".
*/
public Device() {
- this.isLocal = true;
- throw new UnsupportedOperationException("Currently local execution is not supported.");
+ this.isHost = true;
+ this.deviceName = "[HostDevice]";
+ setup();
}
/**
@@ -43,20 +57,68 @@ public class Device {
this.usingSpecificDevice = true;
}
this.noBootImage = noBootImage;
+ setup();
+ }
+
+ private String checkForEnvVar(Map<String, String> envVars, String key) {
+ if (!envVars.containsKey(key)) {
+ Log.errorAndQuit("Cannot run a fuzzed program if $" + key + " is not set!");
+ }
+ return envVars.get(key);
+ }
+
+ private void setup() {
+ programPushed = false;
+
+ Map<String, String> envVars = System.getenv();
+ androidProductOut = checkForEnvVar(envVars, "ANDROID_PRODUCT_OUT");
+ androidHostOut = checkForEnvVar(envVars, "ANDROID_HOST_OUT");
+
+ if (!isHost) {
+ // Create temporary consumers for the initial test.
+ StreamConsumer outputConsumer = new StreamConsumer();
+ outputConsumer.start();
+ StreamConsumer errorConsumer = new StreamConsumer();
+ errorConsumer.start();
+
+ // Check for ADB.
+ try {
+ ProcessBuilder pb = new ProcessBuilder();
+ pb.command("adb", "devices");
+ Process process = pb.start();
+ int exitValue = process.waitFor();
+ if (exitValue != 0) {
+ Log.errorAndQuit("Problem executing ADB - is it in your $PATH?");
+ }
+ } catch (IOException e) {
+ Log.errorAndQuit("IOException when executing ADB, is it working?");
+ } catch (InterruptedException e) {
+ Log.errorAndQuit("InterruptedException when executing ADB, is it working?");
+ }
+
+ // Check we can run something on ADB.
+ ExecutionResult result = executeCommand("true", true, outputConsumer, errorConsumer);
+ if (result.getFlattenedAll().contains("device not found")) {
+ Log.errorAndQuit("Couldn't connect to specified ADB device: " + deviceName);
+ }
+
+ outputConsumer.shutdown();
+ errorConsumer.shutdown();
+ } else {
+ androidData = checkForEnvVar(envVars, "ANDROID_DATA");
+ }
}
/**
* Get the name that would be provided to adb -s to communicate specifically with this device.
*/
public String getName() {
- if (isLocal) {
- return "LOCAL DEVICE";
- }
+ assert(!isHost);
return deviceName;
}
- public boolean isLocal() {
- return isLocal;
+ public boolean isHost() {
+ return isHost;
}
/**
@@ -72,20 +134,81 @@ public class Device {
* Get the command prefix for this device if we want to use adb shell.
*/
public String getExecutionShellPrefix() {
- if (isLocal) {
+ if (isHost) {
return "";
}
return getExecutionPrefixWithAdb("shell");
}
/**
- * Get the command prefix for this device if we want to use adb push.
+ * Get any extra flags required to execute ART on the host.
*/
- public String getExecutionPushPrefix() {
- if (isLocal) {
- return "";
+ public String getHostExecutionFlags() {
+ return String.format("-Xnorelocate -Ximage:%s/framework/core.art", androidHostOut);
+ }
+
+ public String getAndroidHostOut() {
+ return androidHostOut;
+ }
+
+ public String getAndroidProductOut() {
+ return androidProductOut;
+ }
+
+ public ExecutionResult executeCommand(String command, boolean captureOutput) {
+ assert(!captureOutput);
+ return executeCommand(command, captureOutput, null, null);
+ }
+
+ public ExecutionResult executeCommand(String command, boolean captureOutput,
+ StreamConsumer outputConsumer, StreamConsumer errorConsumer) {
+
+ ExecutionResult result = new ExecutionResult();
+
+ Log.info("Executing: " + command);
+
+ try {
+ ProcessBuilder processBuilder = new ProcessBuilder(command.split(" "));
+ processBuilder.environment().put("ANDROID_ROOT", androidHostOut);
+ if (Options.executeOnHost) {
+ processBuilder.environment().put("ANDROID_DATA", androidData);
+ }
+ Process process = processBuilder.start();
+
+ if (captureOutput) {
+ // Give the streams to the StreamConsumers.
+ outputConsumer.giveStreamAndStartConsuming(process.getInputStream());
+ errorConsumer.giveStreamAndStartConsuming(process.getErrorStream());
+ }
+
+ // Wait until the process is done - the StreamConsumers will keep the
+ // buffers drained, so this shouldn't block indefinitely.
+ // Get the return value as well.
+ result.returnValue = process.waitFor();
+
+ Log.info("Return value: " + result.returnValue);
+
+ if (captureOutput) {
+ // Tell the StreamConsumers to stop consuming, and wait for them to finish
+ // so we know we have all of the output.
+ outputConsumer.processFinished();
+ errorConsumer.processFinished();
+ result.output = outputConsumer.getOutput();
+ result.error = errorConsumer.getOutput();
+
+ // Always explicitly indicate the return code in the text output now.
+ // NB: adb shell doesn't actually return exit codes currently, but this will
+ // be useful if/when it does.
+ result.output.add("RETURN CODE: " + result.returnValue);
+ }
+
+ } catch (IOException e) {
+ Log.errorAndQuit("ExecutionResult.execute() caught an IOException");
+ } catch (InterruptedException e) {
+ Log.errorAndQuit("ExecutionResult.execute() caught an InterruptedException");
}
- return getExecutionPrefixWithAdb("push");
+
+ return result;
}
private String getExecutionPrefixWithAdb(String command) {
@@ -95,4 +218,37 @@ public class Device {
return String.format("adb %s ", command);
}
}
+
+ private String getCacheLocation(Architecture architecture) {
+ String cacheLocation = "";
+ if (isHost) {
+ cacheLocation = androidData + "/dalvik-cache/" + architecture.asString() + "/";
+ } else {
+ cacheLocation = "/data/dalvik-cache/" + architecture.asString() + "/";
+ }
+ return cacheLocation;
+ }
+
+ private String getOatFileName(String testLocation, String programName) {
+ // Converts e.g. /data/art-test/file.dex to data@art-test@file.dex
+ return (testLocation.replace("/", "@").substring(1) + "@" + programName);
+ }
+
+ public void cleanCodeCache(Architecture architecture, String testLocation, String programName) {
+ String command = "rm -f " + getCacheLocation(architecture)
+ + getOatFileName(testLocation, programName);
+ executeCommand(command, false);
+ }
+
+ public void pushProgramToDevice(String programName, String testLocation) {
+ assert(!isHost);
+ if (!programPushed) {
+ executeCommand(getExecutionPrefixWithAdb("push") + programName + " " + testLocation, false);
+ programPushed = true;
+ }
+ }
+
+ public void resetProgramPushed() {
+ programPushed = false;
+ }
}