• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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