blob: 8ccba1acb749407d572a533ae9acf6197cd8c5a3 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.util;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* A utility class that resets and forces a flush of Java code coverage measurements from processes
* running on the device.
*/
public class JavaCodeCoverageFlusher {
private static final String COVERAGE_RESET_FORMAT =
"am attach-agent %s /system/lib/libdumpcoverage.so=reset";
private static final String COVERAGE_FLUSH_FORMAT =
"am attach-agent %s /system/lib/libdumpcoverage.so=dump:%s";
private static final String PACKAGE_PREFIX = "package:";
private static final long FLUSH_DELAY = 60 * 1000; // 1 minute
private IRunUtil mRunUtil = RunUtil.getDefault();
private final ITestDevice mDevice;
private final List<String> mProcessNames;
public JavaCodeCoverageFlusher(ITestDevice device, List<String> processNames) {
mDevice = device;
mProcessNames = ImmutableList.copyOf(processNames);
}
@VisibleForTesting
void setRunUtil(IRunUtil runUtil) {
mRunUtil = runUtil;
}
/** Retrieves the name of all running processes on the device. */
private List<String> getAllProcessNames() throws DeviceNotAvailableException {
List<ProcessInfo> processes = PsParser.getProcesses(mDevice.executeShellCommand("ps -e"));
List<String> names = new ArrayList<>();
for (ProcessInfo pinfo : processes) {
names.add(pinfo.getName());
}
return names;
}
/** Retrieves the set of all installed packages on the device. */
private Set<String> getAllPackages() throws DeviceNotAvailableException {
Splitter splitter = Splitter.on('\n').trimResults().omitEmptyStrings();
String pmListPackages = mDevice.executeShellCommand("pm list packages -a");
List<String> lines = splitter.splitToList(pmListPackages);
ImmutableSet.Builder<String> packages = ImmutableSet.builder();
// Strip "package:" prefix from the output.
for (String line : lines) {
if (line.startsWith(PACKAGE_PREFIX)) {
line = line.substring(PACKAGE_PREFIX.length());
}
packages.add(line);
}
return packages.build();
}
/**
* Resets the Java code coverage counters for the given processes.
*
* @throws DeviceNotAvailableException
*/
public void resetCoverage() throws DeviceNotAvailableException {
List<String> processNames = mProcessNames;
if (processNames.isEmpty()) {
processNames = getAllProcessNames();
}
Set<String> javaPackages = getAllPackages();
// Attempt to reset the coverage measurements for the given processes.
for (String processName :
Sets.intersection(javaPackages, ImmutableSet.copyOf(processNames))) {
String pid = mDevice.getProcessPid(processName);
if (pid != null) {
mDevice.executeShellCommand(String.format(COVERAGE_RESET_FORMAT, processName));
}
}
// Reset coverage for system_server.
mDevice.executeShellCommand("cmd coverage reset");
}
/**
* Forces a flush of Java coverage data from processes running on the device.
*
* @return a list of the coverage files generated on the device.
* @throws DeviceNotAvailableException
*/
public List<String> forceCoverageFlush() throws DeviceNotAvailableException {
List<String> processNames = mProcessNames;
if (processNames.isEmpty()) {
processNames = getAllProcessNames();
}
List<String> coverageFiles = new ArrayList<>();
Set<String> javaPackages = getAllPackages();
// Flush coverage for the given processes, if the process exists and is a Java process.
for (String processName :
Sets.intersection(javaPackages, ImmutableSet.copyOf(processNames))) {
String pid = mDevice.getProcessPid(processName);
if (pid != null) {
String outputPath = String.format("/data/misc/trace/%s-%s.ec", processName, pid);
mDevice.executeShellCommand(
String.format(COVERAGE_FLUSH_FORMAT, processName, outputPath));
coverageFiles.add(outputPath);
}
}
// Flush coverage for system_server.
mDevice.executeShellCommand("cmd coverage dump /data/misc/trace/system_server.ec");
coverageFiles.add("/data/misc/trace/system_server.ec");
// Allow time for the system to fully write coverage to disk before pulling measurements.
// This reduces the rate of incomplete coverage measurements received.
mRunUtil.sleep(FLUSH_DELAY);
return coverageFiles;
}
}