1 /* 2 * Copyright (C) 2019 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.util; 18 19 import static com.google.common.base.Preconditions.checkState; 20 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.log.LogUtil.CLog; 24 import com.android.tradefed.testtype.coverage.CoverageOptions; 25 26 import com.google.common.collect.ImmutableList; 27 28 import java.util.List; 29 import java.util.StringJoiner; 30 31 /** 32 * A utility class that clears native coverage measurements and forces a flush of native coverage 33 * data from processes on the device. 34 */ 35 public final class NativeCodeCoverageFlusher { 36 37 private static final String EXTRACT_SIGCGT_FORMAT = 38 "cat /proc/%d/status | grep SigCgt | awk '{ print $2 }'"; 39 private static final long SIGNAL_37_BIT = 1L << (37 - 1); 40 private static final String COVERAGE_FLUSH_COMMAND_FORMAT = "kill -37 %s"; 41 private static final String CLEAR_CLANG_COVERAGE_FILES_FORMAT = 42 "find %s -name '*.profraw' -delete"; 43 private static final String CLEAR_GCOV_COVERAGE_FILES_FORMAT = "find %s -name '*.gcda' -delete"; 44 private static final long FLUSH_DELAY_MS = 2 * 60 * 1000; // 2 minutes 45 46 private final ITestDevice mDevice; 47 private final CoverageOptions mCoverageOptions; 48 private IRunUtil mRunUtil = RunUtil.getDefault(); 49 NativeCodeCoverageFlusher(ITestDevice device, CoverageOptions coverageOptions)50 public NativeCodeCoverageFlusher(ITestDevice device, CoverageOptions coverageOptions) { 51 mDevice = device; 52 mCoverageOptions = coverageOptions; 53 } 54 setRunUtil(IRunUtil runUtil)55 public void setRunUtil(IRunUtil runUtil) { 56 mRunUtil = runUtil; 57 } 58 59 /** 60 * Resets native coverage counters for processes running on the device and clears any existing 61 * coverage measurements from disk. Device must be in adb root. 62 * 63 * @throws DeviceNotAvailableException 64 */ resetCoverage()65 public void resetCoverage() throws DeviceNotAvailableException { 66 forceCoverageFlush(); 67 deleteCoverageMeasurements(); 68 } 69 70 /** 71 * Deletes coverage measurements from the device. Device must be in adb root. 72 * 73 * @throws DeviceNotAvailableException 74 */ deleteCoverageMeasurements()75 public void deleteCoverageMeasurements() throws DeviceNotAvailableException { 76 for (String devicePath : mCoverageOptions.getDeviceCoveragePaths()) { 77 mDevice.executeShellCommand( 78 String.format(CLEAR_CLANG_COVERAGE_FILES_FORMAT, devicePath)); 79 mDevice.executeShellCommand( 80 String.format(CLEAR_GCOV_COVERAGE_FILES_FORMAT, devicePath)); 81 } 82 } 83 84 /** 85 * Forces a flush of native coverage data from processes running on the device. Device must be 86 * in adb root. 87 * 88 * @throws DeviceNotAvailableException 89 */ forceCoverageFlush()90 public void forceCoverageFlush() throws DeviceNotAvailableException { 91 checkState(mDevice.isAdbRoot(), "adb root is required to flush native coverage data."); 92 93 List<Integer> signalHandlingPids = 94 findSignalHandlingPids(mCoverageOptions.getCoverageProcesses()); 95 StringJoiner pidString = new StringJoiner(" "); 96 97 CLog.d("Signal handling pids: %s", signalHandlingPids.toString()); 98 99 for (Integer pid : signalHandlingPids) { 100 pidString.add(pid.toString()); 101 } 102 103 if (pidString.length() > 0) { 104 mDevice.executeShellCommand( 105 String.format(COVERAGE_FLUSH_COMMAND_FORMAT, pidString.toString())); 106 } 107 108 // Wait 2 minutes for the device to complete flushing coverage data. 109 mRunUtil.sleep(FLUSH_DELAY_MS); 110 mDevice.waitForDeviceAvailable(); 111 } 112 113 /** Finds processes that handle the native coverage flush signal (37). */ findSignalHandlingPids(List<String> processNames)114 private List<Integer> findSignalHandlingPids(List<String> processNames) 115 throws DeviceNotAvailableException { 116 // Get a list of all running pids. 117 List<ProcessInfo> allProcessInfo = 118 PsParser.getProcesses(mDevice.executeShellCommand("ps -e")); 119 ImmutableList.Builder<Integer> signalHandlingPids = ImmutableList.builder(); 120 121 // Check SigCgt from /proc/<pid>/status to see if the bit for signal 37 is set. 122 for (ProcessInfo processInfo : allProcessInfo) { 123 CommandResult result = 124 mDevice.executeShellV2Command( 125 String.format(EXTRACT_SIGCGT_FORMAT, processInfo.getPid())); 126 127 if (!result.getStatus().equals(CommandStatus.SUCCESS) || (result.getExitCode() != 0)) { 128 CLog.w( 129 "Failed to read /proc/%d/status for %s", 130 processInfo.getPid(), processInfo.getName()); 131 } else if (result.getStdout().trim().isEmpty()) { 132 CLog.w( 133 "Empty string when retrieving SigCgt for %s (pid %d)", 134 processInfo.getName(), processInfo.getPid()); 135 } else { 136 long sigCgt = Long.parseUnsignedLong(result.getStdout().trim(), 16); 137 138 // Check the signal bit is set and either no processes are set, or this specific 139 // process is in the process list. 140 if ((sigCgt & SIGNAL_37_BIT) == SIGNAL_37_BIT 141 && (processNames.isEmpty() 142 || processNames.contains(processInfo.getName()))) { 143 signalHandlingPids.add(processInfo.getPid()); 144 } 145 } 146 } 147 148 return signalHandlingPids.build(); 149 } 150 } 151