Home | History | Annotate | Download | only in targetprep
      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
    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.i("Coverage disabled.");
    118             return;
    119         }
    120         if (sancovEnabled) {
    121             CLog.i("Sanitizer coverage processing enabled.");
    122         }
    123         if (gcovEnabled) {
    124             CLog.i("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
    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      */
    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      */
    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      */
    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
    293     File createTempDir(ITestDevice device) throws IOException {
    294         return FileUtil.createTempDir(device.getSerialNumber());
    295     }
    296 
    297     @VisibleForTesting
    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
    310     CompatibilityBuildHelper createBuildHelper(IBuildInfo buildInfo) {
    311         return new CompatibilityBuildHelper(buildInfo);
    312     }
    313 }
    314