1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dexfuzz.executors; 18 19 import java.io.IOException; 20 import java.io.File; 21 import java.util.Map; 22 23 import dexfuzz.ExecutionResult; 24 import dexfuzz.Log; 25 import dexfuzz.Options; 26 import dexfuzz.StreamConsumer; 27 28 /** 29 * Handles execution either on a remote target device, or on a local host computer. 30 */ 31 public class Device { 32 private boolean isHost; 33 private String deviceName; 34 private boolean usingSpecificDevice; 35 private boolean noBootImage; 36 37 private String androidHostOut; 38 private String androidProductOut; 39 private String androidData; 40 41 private boolean programPushed; 42 43 /** 44 * The constructor for a host "device". 45 */ Device()46 public Device() { 47 this.isHost = true; 48 this.deviceName = "[HostDevice]"; 49 setup(); 50 } 51 52 /** 53 * The constructor for an ADB connected device. 54 */ Device(String deviceName, boolean noBootImage)55 public Device(String deviceName, boolean noBootImage) { 56 if (!deviceName.isEmpty()) { 57 this.deviceName = deviceName; 58 this.usingSpecificDevice = true; 59 } 60 this.noBootImage = noBootImage; 61 setup(); 62 } 63 checkForEnvVar(Map<String, String> envVars, String key)64 private String checkForEnvVar(Map<String, String> envVars, String key) { 65 if (!envVars.containsKey(key)) { 66 Log.errorAndQuit("Cannot run a fuzzed program if $" + key + " is not set!"); 67 } 68 return envVars.get(key); 69 } 70 getHostCoreImagePath()71 private String getHostCoreImagePath() { 72 return androidHostOut + "/framework/core.art"; 73 } 74 setup()75 private void setup() { 76 programPushed = false; 77 78 Map<String, String> envVars = System.getenv(); 79 androidProductOut = checkForEnvVar(envVars, "ANDROID_PRODUCT_OUT"); 80 androidHostOut = checkForEnvVar(envVars, "ANDROID_HOST_OUT"); 81 82 if (Options.executeOnHost) { 83 File coreImage = new File(getHostCoreImagePath()); 84 if (!coreImage.exists()) { 85 Log.errorAndQuit("Host core image not found at " + coreImage.getPath() 86 + ". Did you forget to build it?"); 87 } 88 } 89 if (!isHost) { 90 // Create temporary consumers for the initial test. 91 StreamConsumer outputConsumer = new StreamConsumer(); 92 outputConsumer.start(); 93 StreamConsumer errorConsumer = new StreamConsumer(); 94 errorConsumer.start(); 95 96 // Check for ADB. 97 try { 98 ProcessBuilder pb = new ProcessBuilder(); 99 pb.command("adb", "devices"); 100 Process process = pb.start(); 101 int exitValue = process.waitFor(); 102 if (exitValue != 0) { 103 Log.errorAndQuit("Problem executing ADB - is it in your $PATH?"); 104 } 105 } catch (IOException e) { 106 Log.errorAndQuit("IOException when executing ADB, is it working?"); 107 } catch (InterruptedException e) { 108 Log.errorAndQuit("InterruptedException when executing ADB, is it working?"); 109 } 110 111 // Check we can run something on ADB. 112 ExecutionResult result = executeCommand("true", true, outputConsumer, errorConsumer); 113 if (result.getFlattenedAll().contains("device not found")) { 114 Log.errorAndQuit("Couldn't connect to specified ADB device: " + deviceName); 115 } 116 117 outputConsumer.shutdown(); 118 errorConsumer.shutdown(); 119 } else { 120 androidData = checkForEnvVar(envVars, "ANDROID_DATA"); 121 } 122 } 123 124 /** 125 * Get the name that would be provided to adb -s to communicate specifically with this device. 126 */ getName()127 public String getName() { 128 assert(!isHost); 129 return deviceName; 130 } 131 isHost()132 public boolean isHost() { 133 return isHost; 134 } 135 136 /** 137 * Certain AOSP builds of Android may not have a full boot.art built. This will be set if 138 * we use --no-boot-image, and is used by Executors when deciding the arguments for dalvikvm 139 * and dex2oat when performing host-side verification. 140 */ noBootImageAvailable()141 public boolean noBootImageAvailable() { 142 return noBootImage; 143 } 144 145 /** 146 * Get the command prefix for this device if we want to use adb shell. 147 */ getExecutionShellPrefix()148 public String getExecutionShellPrefix() { 149 if (isHost) { 150 return ""; 151 } 152 return getExecutionPrefixWithAdb("shell"); 153 } 154 155 /** 156 * Get any extra flags required to execute ART on the host. 157 */ getHostExecutionFlags()158 public String getHostExecutionFlags() { 159 return String.format("-Xnorelocate -Ximage:%s", getHostCoreImagePath()); 160 } 161 getAndroidHostOut()162 public String getAndroidHostOut() { 163 return androidHostOut; 164 } 165 getAndroidProductOut()166 public String getAndroidProductOut() { 167 return androidProductOut; 168 } 169 executeCommand(String command, boolean captureOutput)170 public ExecutionResult executeCommand(String command, boolean captureOutput) { 171 assert(!captureOutput); 172 return executeCommand(command, captureOutput, null, null); 173 } 174 executeCommand(String command, boolean captureOutput, StreamConsumer outputConsumer, StreamConsumer errorConsumer)175 public ExecutionResult executeCommand(String command, boolean captureOutput, 176 StreamConsumer outputConsumer, StreamConsumer errorConsumer) { 177 178 ExecutionResult result = new ExecutionResult(); 179 180 Log.info("Executing: " + command); 181 182 try { 183 ProcessBuilder processBuilder = new ProcessBuilder(command.split(" ")); 184 processBuilder.environment().put("ANDROID_ROOT", androidHostOut); 185 if (Options.executeOnHost) { 186 processBuilder.environment().put("ANDROID_DATA", androidData); 187 } 188 Process process = processBuilder.start(); 189 190 if (captureOutput) { 191 // Give the streams to the StreamConsumers. 192 outputConsumer.giveStreamAndStartConsuming(process.getInputStream()); 193 errorConsumer.giveStreamAndStartConsuming(process.getErrorStream()); 194 } 195 196 // Wait until the process is done - the StreamConsumers will keep the 197 // buffers drained, so this shouldn't block indefinitely. 198 // Get the return value as well. 199 result.returnValue = process.waitFor(); 200 201 Log.info("Return value: " + result.returnValue); 202 203 if (captureOutput) { 204 // Tell the StreamConsumers to stop consuming, and wait for them to finish 205 // so we know we have all of the output. 206 outputConsumer.processFinished(); 207 errorConsumer.processFinished(); 208 result.output = outputConsumer.getOutput(); 209 result.error = errorConsumer.getOutput(); 210 211 // Always explicitly indicate the return code in the text output now. 212 // NB: adb shell doesn't actually return exit codes currently, but this will 213 // be useful if/when it does. 214 result.output.add("RETURN CODE: " + result.returnValue); 215 } 216 217 } catch (IOException e) { 218 Log.errorAndQuit("ExecutionResult.execute() caught an IOException"); 219 } catch (InterruptedException e) { 220 Log.errorAndQuit("ExecutionResult.execute() caught an InterruptedException"); 221 } 222 223 return result; 224 } 225 getExecutionPrefixWithAdb(String command)226 private String getExecutionPrefixWithAdb(String command) { 227 if (usingSpecificDevice) { 228 return String.format("adb -s %s %s ", deviceName, command); 229 } else { 230 return String.format("adb %s ", command); 231 } 232 } 233 getCacheLocation(Architecture architecture)234 private String getCacheLocation(Architecture architecture) { 235 String cacheLocation = ""; 236 if (isHost) { 237 cacheLocation = androidData + "/dalvik-cache/" + architecture.asString() + "/"; 238 } else { 239 cacheLocation = "/data/dalvik-cache/" + architecture.asString() + "/"; 240 } 241 return cacheLocation; 242 } 243 getOatFileName(String testLocation, String programName)244 private String getOatFileName(String testLocation, String programName) { 245 // Converts e.g. /data/art-test/file.dex to data@art-test@file.dex 246 return (testLocation.replace("/", "@").substring(1) + "@" + programName); 247 } 248 cleanCodeCache(Architecture architecture, String testLocation, String programName)249 public void cleanCodeCache(Architecture architecture, String testLocation, String programName) { 250 String command = "rm -f " + getCacheLocation(architecture) 251 + getOatFileName(testLocation, programName); 252 executeCommand(command, false); 253 } 254 pushProgramToDevice(String programName, String testLocation)255 public void pushProgramToDevice(String programName, String testLocation) { 256 assert(!isHost); 257 if (!programPushed) { 258 executeCommand(getExecutionPrefixWithAdb("push") + programName + " " + testLocation, false); 259 programPushed = true; 260 } 261 } 262 resetProgramPushed()263 public void resetProgramPushed() { 264 programPushed = false; 265 } 266 } 267