blob: 35286313b2562ea7d1ab5dae4d12589b805d9d31 [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.device.metric;
import com.android.tradefed.config.Option;
import com.android.tradefed.config.OptionClass;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
import com.android.tradefed.result.InputStreamSource;
import com.android.tradefed.result.LogDataType;
import com.android.tradefed.result.TestDescription;
import com.android.tradefed.util.statsd.ConfigUtil;
import com.android.tradefed.util.statsd.MetricUtil;
import com.google.common.annotations.VisibleForTesting;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* A {@link IMetricCollector} that collects statsd metrics from host side using statsd utility
* commands. It has basic push metrics and dump report functions. It can be extended by subclasses
* to process statsd metric report based on the needs.
*/
@OptionClass(alias = "host-statsd-collector")
public class HostStatsdMetricCollector extends BaseDeviceMetricCollector {
@Option(name = "binary-stats-config", description = "Path to the binary Statsd config file")
private File mBinaryConfig;
@Option(name = "per-run", description = "Collect Metrics at per-test level or per-run level")
private boolean mPerRun = true;
/** Map from device serial number to config ID on that device */
private Map<String, Long> mDeviceConfigIds = new HashMap<>();
/**
* Counting the test in the same run. It is used to distinguish the statsd result file from
* multiple tests
*/
private int mTestCount = 1;
private boolean mTestFailed = false;
@Override
public void onTestRunStart(DeviceMetricData runData) throws DeviceNotAvailableException {
if (mPerRun && mBinaryConfig != null) {
mDeviceConfigIds.clear();
startCollection();
}
}
@Override
public void onTestStart(DeviceMetricData testData) throws DeviceNotAvailableException {
if (!mPerRun && mBinaryConfig != null) {
mDeviceConfigIds.clear();
startCollection();
}
}
@Override
public void onTestFail(DeviceMetricData testData, TestDescription test) {
mTestFailed = true;
}
@Override
public void onTestEnd(
DeviceMetricData testData, final Map<String, Metric> currentTestCaseMetrics)
throws DeviceNotAvailableException {
if (!mPerRun && mBinaryConfig != null) {
stopCollection(testData, !mTestFailed);
}
mTestCount++;
mTestFailed = false;
}
@Override
public void onTestRunEnd(DeviceMetricData runData, final Map<String, Metric> currentRunMetrics)
throws DeviceNotAvailableException {
if (mPerRun && mBinaryConfig != null) {
stopCollection(runData, true);
}
}
@VisibleForTesting
InputStreamSource getReportByteStream(ITestDevice device, long configId)
throws DeviceNotAvailableException {
return MetricUtil.getReportByteStream(device, configId);
}
@VisibleForTesting
long pushBinaryStatsConfig(ITestDevice device, File configFile)
throws IOException, DeviceNotAvailableException {
return ConfigUtil.pushBinaryStatsConfig(device, configFile);
}
@VisibleForTesting
void removeConfig(ITestDevice device, long configId) throws DeviceNotAvailableException {
ConfigUtil.removeConfig(device, configId);
}
/**
* Subclasses can implement the method to process Statsd metric report if needed. It is called
* for metric report from a particular device
*
* @param device Test device where the statsd report is coming from
* @param dataStream Stats report as input stream
* @param runData The destination where the processed metrics will be stored
*/
protected void processStatsReport(
ITestDevice device, InputStreamSource dataStream, DeviceMetricData runData) {
// Empty method by default
}
private void startCollection() throws DeviceNotAvailableException {
for (ITestDevice device : getDevices()) {
String serialNumber = device.getSerialNumber();
try {
long configId = pushBinaryStatsConfig(device, mBinaryConfig);
CLog.d(
"Pushed binary stats config to device %s with config id: %d",
serialNumber, configId);
mDeviceConfigIds.put(serialNumber, configId);
} catch (IOException e) {
CLog.e("Failed to push stats config to device %s, error: %s", serialNumber, e);
}
}
}
private void stopCollection(DeviceMetricData metricData, boolean reportResult)
throws DeviceNotAvailableException {
for (ITestDevice device : getDevices()) {
String serialNumber = device.getSerialNumber();
if (mDeviceConfigIds.containsKey(serialNumber)) {
long configId = mDeviceConfigIds.get(serialNumber);
try (InputStreamSource dataStream = getReportByteStream(device, configId)) {
CLog.d(
"Retrieved stats report from device %s for config %d",
serialNumber, configId);
removeConfig(device, configId);
if (reportResult) {
String reportName =
mPerRun
? String.format("device_%s_stats_report", serialNumber)
: String.format(
"device_%s_stats_report_test_%d",
serialNumber, mTestCount);
testLog(reportName, LogDataType.PB, dataStream);
processStatsReport(device, dataStream, metricData);
}
}
}
}
}
}