1 /* 2 * Copyright (C) 2017 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 com.android.tradefed.targetprep; 18 19 import com.android.annotations.VisibleForTesting; 20 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; 21 import com.android.tradefed.build.IBuildInfo; 22 import com.android.tradefed.config.OptionClass; 23 import com.android.tradefed.config.Option; 24 import com.android.tradefed.device.DeviceNotAvailableException; 25 import com.android.tradefed.device.ITestDevice; 26 import com.android.tradefed.log.LogUtil.CLog; 27 import com.android.tradefed.targetprep.TargetSetupError; 28 import com.android.tradefed.util.CommandResult; 29 import com.android.tradefed.util.CommandStatus; 30 import com.android.tradefed.util.FileUtil; 31 import com.android.tradefed.util.IRunUtil; 32 import com.android.tradefed.util.RunUtil; 33 import com.android.tradefed.util.VtsVendorConfigFileUtil; 34 35 import java.io.File; 36 import java.io.FileNotFoundException; 37 import java.io.IOException; 38 /** 39 * Preparer class for sanitizer and gcov coverage. 40 * 41 * <p>For devices instrumented with sanitizer coverage, this preparer fetches the unstripped 42 * binaries and copies them to a temporary directory whose path is passed down with the test 43 * IBuildInfo object. For gcov-instrumented devices, the zip containing gcno files is retrieved 44 * along with the build info artifact. 45 */ 46 @OptionClass(alias = "vts-coverage-preparer") 47 public class VtsCoveragePreparer implements ITargetPreparer, ITargetCleaner { 48 static final long BASE_TIMEOUT = 1000 * 60 * 20; // timeout for fetching artifacts 49 static final String BUILD_INFO_ARTIFACT = "BUILD_INFO"; // name of build info artifact 50 static final String GCOV_PROPERTY = "ro.vts.coverage"; // indicates gcov when val is 1 51 static final String GCOV_ARTIFACT = "%s-coverage-%s.zip"; // gcov coverage artifact 52 static final String GCOV_FILE_NAME = "gcov.zip"; // gcov zip file to pass to VTS 53 54 static final String SELINUX_DISABLED = "Disabled"; // selinux disabled 55 static final String SELINUX_ENFORCING = "Enforcing"; // selinux enforcing mode 56 static final String SELINUX_PERMISSIVE = "Permissive"; // selinux permissive mode 57 58 static final String SYMBOLS_ARTIFACT = "%s-symbols-%s.zip"; // symbolized binary zip 59 static final String SYMBOLS_FILE_NAME = "symbols.zip"; // sancov zip to pass to VTS 60 static final String SANCOV_FLAVOR = "_asan_coverage"; // sancov device build flavor 61 62 // Path to store coverage data on the target. 63 static final String COVERAGE_DATA_PATH = "/data/misc/trace/"; 64 // Path to store coverage report. 65 static final String COVERAGE_REPORT_PATH = "coverage_report_path"; 66 67 // Build key for gcov resources 68 static final String GCOV_RESOURCES_KEY = "gcov-resources-path-%s"; 69 70 // Buid key for sancov resources 71 static final String SANCOV_RESOURCES_KEY = "sancov-resources-path-%s"; 72 73 // Relative path to coverage configure binary in VTS package 74 static final String COVERAGE_CONFIGURE_SRC = "DATA/bin/vts_coverage_configure"; 75 76 // Target path for coverage configure binary, will be removed in teardown 77 static final String COVERAGE_CONFIGURE_DST = "/data/local/tmp/vts_coverage_configure"; 78 79 // Default path to store coverage reports. 80 static final String DEFAULT_COVRAGE_REPORT_PATH = "/tmp/vts-coverage-report/"; 81 82 // Default path to store coverage resource files locally. 83 static final String DEFAULT_LOCAL_COVERAGE_RESOURCE_PATH = "/tmp/coverage/"; 84 85 private File mDeviceInfoPath = null; // host path where coverage device artifacts are stored 86 private String mEnforcingState = null; // start state for selinux enforcement 87 private IRunUtil mRunUtil = null; 88 89 @Option(name = "use-local-artifects", description = "Whether to use local artifacts.") 90 private boolean mUseLocalArtifects = false; 91 92 @Option(name = "local-coverage-resource-path", 93 description = "Path to look for local artifacts.") 94 private String mLocalCoverageResourcePath = DEFAULT_LOCAL_COVERAGE_RESOURCE_PATH; 95 96 @Option(name = "coverage-report-dir", description = "Local directory to store coverage report.") 97 private String mCoverageReportDir = null; 98 99 /** {@inheritDoc} */ 100 @Override setUp(ITestDevice device, IBuildInfo buildInfo)101 public void setUp(ITestDevice device, IBuildInfo buildInfo) 102 throws DeviceNotAvailableException, TargetSetupError { 103 String flavor = device.getBuildFlavor(); 104 String buildId = device.getBuildId(); 105 106 if (buildId == null) { 107 CLog.w("Missing build ID. Coverage disabled."); 108 return; 109 } 110 111 boolean sancovEnabled = (flavor != null) && flavor.contains(SANCOV_FLAVOR); 112 113 String coverageProperty = device.getProperty(GCOV_PROPERTY); 114 boolean gcovEnabled = (coverageProperty != null) && coverageProperty.equals("1"); 115 116 if (!sancovEnabled && !gcovEnabled) { 117 CLog.d("Coverage disabled."); 118 return; 119 } 120 if (sancovEnabled) { 121 CLog.d("Sanitizer coverage processing enabled."); 122 } 123 if (gcovEnabled) { 124 CLog.d("Gcov coverage processing enabled."); 125 } 126 127 if (mRunUtil == null) { 128 mRunUtil = new RunUtil(); 129 } 130 131 CompatibilityBuildHelper buildHelper = createBuildHelper(buildInfo); 132 if (!mUseLocalArtifects) { 133 // Load the vendor configuration 134 String artifactFetcher = getArtifactFetcher(buildInfo); 135 if (artifactFetcher == null) { 136 throw new TargetSetupError( 137 "Vendor configuration with build_artifact_fetcher required."); 138 } 139 140 try { 141 // Create a temporary coverage directory 142 mDeviceInfoPath = createTempDir(device); 143 } catch (IOException e) { 144 cleanupCoverageData(device); 145 throw new TargetSetupError( 146 "Failed to create temp dir to store coverage resource files."); 147 } 148 149 if (sancovEnabled) { 150 // Fetch the symbolized binaries 151 String artifactName = String.format( 152 SYMBOLS_ARTIFACT, flavor.substring(0, flavor.lastIndexOf("-")), buildId); 153 File artifactFile = new File(mDeviceInfoPath, SYMBOLS_FILE_NAME); 154 155 String cmdString = String.format(artifactFetcher, buildId, flavor, artifactName, 156 artifactFile.getAbsolutePath().toString()); 157 String[] cmd = cmdString.split("\\s+"); 158 CommandResult commandResult = mRunUtil.runTimedCmd(BASE_TIMEOUT, cmd); 159 if (commandResult == null || commandResult.getStatus() != CommandStatus.SUCCESS 160 || !artifactFile.exists()) { 161 cleanupCoverageData(device); 162 throw new TargetSetupError("Could not fetch unstripped binaries."); 163 } 164 } 165 166 if (gcovEnabled) { 167 // Fetch the symbolized binaries 168 String artifactName = String.format( 169 GCOV_ARTIFACT, flavor.substring(0, flavor.lastIndexOf("-")), buildId); 170 File artifactFile = new File(mDeviceInfoPath, GCOV_FILE_NAME); 171 172 String cmdString = String.format(artifactFetcher, buildId, flavor, artifactName, 173 artifactFile.getAbsolutePath().toString()); 174 String[] cmd = cmdString.split("\\s+"); 175 CommandResult commandResult = mRunUtil.runTimedCmd(BASE_TIMEOUT, cmd); 176 if (commandResult == null || commandResult.getStatus() != CommandStatus.SUCCESS 177 || !artifactFile.exists()) { 178 cleanupCoverageData(device); 179 throw new TargetSetupError("Could not fetch gcov build artifacts."); 180 } 181 } 182 183 // Fetch the device build information file 184 String cmdString = String.format(artifactFetcher, buildId, flavor, BUILD_INFO_ARTIFACT, 185 mDeviceInfoPath.getAbsolutePath().toString()); 186 String[] cmd = cmdString.split("\\s+"); 187 CommandResult commandResult = mRunUtil.runTimedCmd(BASE_TIMEOUT, cmd); 188 File artifactFile = new File(mDeviceInfoPath, BUILD_INFO_ARTIFACT); 189 if (commandResult == null || commandResult.getStatus() != CommandStatus.SUCCESS 190 || !artifactFile.exists()) { 191 cleanupCoverageData(device); 192 throw new TargetSetupError("Could not fetch build info."); 193 } 194 } else { 195 mDeviceInfoPath = new File(mLocalCoverageResourcePath); 196 String fileName = sancovEnabled ? SYMBOLS_FILE_NAME : GCOV_FILE_NAME; 197 File artifactFile = new File(mDeviceInfoPath, fileName); 198 if (!artifactFile.exists()) { 199 cleanupCoverageData(device); 200 throw new TargetSetupError(String.format("Could not find %s under %s.", 201 GCOV_FILE_NAME, mDeviceInfoPath.getAbsolutePath())); 202 } 203 File buildInfoArtifact = new File(mDeviceInfoPath, BUILD_INFO_ARTIFACT); 204 if (!buildInfoArtifact.exists()) { 205 cleanupCoverageData(device); 206 throw new TargetSetupError(String.format("Could not find %s under %s.", 207 BUILD_INFO_ARTIFACT, mDeviceInfoPath.getAbsolutePath())); 208 } 209 } 210 211 try { 212 // Push the coverage configure tool 213 File configureSrc = new File(buildHelper.getTestsDir(), COVERAGE_CONFIGURE_SRC); 214 device.pushFile(configureSrc, COVERAGE_CONFIGURE_DST); 215 } catch (FileNotFoundException e) { 216 cleanupCoverageData(device); 217 throw new TargetSetupError("Failed to push the vts coverage configure tool."); 218 } 219 220 if (mCoverageReportDir != null) { 221 try { 222 File resultDir = buildHelper.getResultDir(); 223 File coverageDir = new File(resultDir, mCoverageReportDir); 224 buildInfo.addBuildAttribute(COVERAGE_REPORT_PATH, coverageDir.getAbsolutePath()); 225 } catch (FileNotFoundException e) { 226 cleanupCoverageData(device); 227 throw new TargetSetupError("Failed to get coverageDir."); 228 } 229 } 230 231 device.executeShellCommand(String.format("rm -rf %s/*", COVERAGE_DATA_PATH)); 232 mEnforcingState = device.executeShellCommand("getenforce"); 233 if (!mEnforcingState.equals(SELINUX_DISABLED) 234 && !mEnforcingState.equals(SELINUX_PERMISSIVE)) { 235 device.executeShellCommand("setenforce " + SELINUX_PERMISSIVE); 236 } 237 238 if (sancovEnabled) { 239 buildInfo.setFile( 240 getSancovResourceDirKey(device), mDeviceInfoPath, buildInfo.getBuildId()); 241 } 242 243 if (gcovEnabled) { 244 buildInfo.setFile( 245 getGcovResourceDirKey(device), mDeviceInfoPath, buildInfo.getBuildId()); 246 } 247 } 248 249 /** {@inheritDoc} */ 250 @Override tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)251 public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e) 252 throws DeviceNotAvailableException { 253 if (mEnforcingState != null && !mEnforcingState.equals(SELINUX_DISABLED)) { 254 device.executeShellCommand("setenforce " + mEnforcingState); 255 } 256 cleanupCoverageData(device); 257 } 258 259 /** 260 * Get the key of the symbolized binary directory for the specified device. 261 * 262 * @param device the target device whose sancov resources directory to get. 263 * @return the (String) key name of the device's sancov resources directory. 264 */ getSancovResourceDirKey(ITestDevice device)265 public static String getSancovResourceDirKey(ITestDevice device) { 266 return String.format(SANCOV_RESOURCES_KEY, device.getSerialNumber()); 267 } 268 269 /** 270 * Get the key of the gcov resources for the specified device. 271 * 272 * @param device the target device whose sancov resources directory to get. 273 * @return the (String) key name of the device's gcov resources directory. 274 */ getGcovResourceDirKey(ITestDevice device)275 public static String getGcovResourceDirKey(ITestDevice device) { 276 return String.format(GCOV_RESOURCES_KEY, device.getSerialNumber()); 277 } 278 279 /** 280 * Cleanup the coverage data on target and host. 281 * 282 * @param device the target device. 283 */ cleanupCoverageData(ITestDevice device)284 private void cleanupCoverageData(ITestDevice device) throws DeviceNotAvailableException { 285 if (mDeviceInfoPath != null) { 286 FileUtil.recursiveDelete(mDeviceInfoPath); 287 } 288 device.executeShellCommand("rm -rf " + COVERAGE_CONFIGURE_DST); 289 device.executeShellCommand(String.format("rm -rf %s/*", COVERAGE_DATA_PATH)); 290 } 291 292 @VisibleForTesting createTempDir(ITestDevice device)293 File createTempDir(ITestDevice device) throws IOException { 294 return FileUtil.createTempDir(device.getSerialNumber()); 295 } 296 297 @VisibleForTesting getArtifactFetcher(IBuildInfo buildInfo)298 String getArtifactFetcher(IBuildInfo buildInfo) { 299 VtsVendorConfigFileUtil configFileUtil = new VtsVendorConfigFileUtil(); 300 if (configFileUtil.LoadVendorConfig(buildInfo)) { 301 return configFileUtil.GetVendorConfigVariable("build_artifact_fetcher"); 302 } 303 return null; 304 } 305 306 /** 307 * Create and return a {@link CompatibilityBuildHelper} to use during the preparer. 308 */ 309 @VisibleForTesting createBuildHelper(IBuildInfo buildInfo)310 CompatibilityBuildHelper createBuildHelper(IBuildInfo buildInfo) { 311 return new CompatibilityBuildHelper(buildInfo); 312 } 313 } 314