blob: fc7b778b7a860d158e68bf28be1fbd8710f11bfb [file] [log] [blame]
/*
* Copyright (C) 2018 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.statsd.shelltools.testdrive;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.EventMetric;
import com.android.internal.os.StatsdConfigProto.FieldFilter;
import com.android.internal.os.StatsdConfigProto.GaugeMetric;
import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
import com.android.os.AtomsProto.Atom;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.StatsLogReport;
import com.android.statsd.shelltools.Utils;
import com.google.common.io.Files;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
public class TestDrive {
private static final int METRIC_ID_BASE = 1111;
private static final long ATOM_MATCHER_ID_BASE = 1234567;
private static final int PULL_ATOM_START = 10000;
private static final long CONFIG_ID = 54321;
private static final String[] ALLOWED_LOG_SOURCES = {
"AID_GRAPHICS",
"AID_INCIDENTD",
"AID_STATSD",
"AID_RADIO",
"com.android.systemui",
"com.android.vending",
"AID_SYSTEM",
"AID_ROOT",
"AID_BLUETOOTH",
"AID_LMKD",
"com.android.managedprovisioning",
"AID_NETWORK_STACK"
};
private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());
private String mAdditionalAllowedPackage;
private final Set<Long> mTrackedMetrics = new HashSet<>();
public static void main(String[] args) {
TestDrive testDrive = new TestDrive();
Set<Integer> trackedAtoms = new HashSet<>();
Utils.setUpLogger(LOGGER, false);
String remoteConfigPath = null;
if (args.length < 1) {
LOGGER.log(Level.SEVERE, "Usage: ./test_drive [-p additional_allowed_package] "
+ "<atomId1> <atomId2> ... <atomIdN>");
return;
}
if (args.length >= 3 && args[0].equals("-p")) {
testDrive.mAdditionalAllowedPackage = args[1];
}
for (int i = testDrive.mAdditionalAllowedPackage == null ? 0 : 2; i < args.length; i++) {
try {
int atomId = Integer.valueOf(args[i]);
if (Atom.getDescriptor().findFieldByNumber(atomId) == null) {
LOGGER.log(Level.SEVERE, "No such atom found: " + args[i]);
continue;
}
trackedAtoms.add(atomId);
} catch (NumberFormatException e) {
LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]);
continue;
}
}
try {
StatsdConfig config = testDrive.createConfig(trackedAtoms);
if (config == null) {
LOGGER.log(Level.SEVERE, "Failed to create valid config.");
return;
}
remoteConfigPath = testDrive.pushConfig(config);
LOGGER.info("Pushed the following config to statsd:");
LOGGER.info(config.toString());
if (!hasPulledAtom(trackedAtoms)) {
LOGGER.info(
"Now please play with the device to trigger the event. All events should "
+ "be dumped after 1 min ...");
Thread.sleep(60_000);
} else {
// wait for 2 min
LOGGER.info("Now wait for 2 minutes ...");
Thread.sleep(120_000);
}
testDrive.dumpMetrics();
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
} finally {
testDrive.removeConfig();
if (remoteConfigPath != null) {
try {
Utils.runCommand(null, LOGGER, "adb", "shell", "rm", remoteConfigPath);
} catch (Exception e) {
LOGGER.log(Level.WARNING,
"Unable to remove remote config file: " + remoteConfigPath, e);
}
}
}
}
private void dumpMetrics() throws Exception {
ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER);
// We may get multiple reports. Take the last one.
ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
for (StatsLogReport statsLog : report.getMetricsList()) {
if (mTrackedMetrics.contains(statsLog.getMetricId())) {
LOGGER.info(statsLog.toString());
}
}
}
private StatsdConfig createConfig(Set<Integer> atomIds) {
long metricId = METRIC_ID_BASE;
long atomMatcherId = ATOM_MATCHER_ID_BASE;
ArrayList<String> allowedSources = new ArrayList<>();
Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES);
if (mAdditionalAllowedPackage != null) {
allowedSources.add(mAdditionalAllowedPackage);
}
StatsdConfig.Builder builder = StatsdConfig.newBuilder();
builder
.addAllAllowedLogSource(allowedSources)
.setHashStringsInMetricReport(false);
for (int atomId : atomIds) {
if (isPulledAtom(atomId)) {
builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder();
gaugeMetricBuilder
.setId(metricId)
.setWhat(atomMatcherId)
.setGaugeFieldsFilter(FieldFilter.newBuilder().setIncludeAll(true).build())
.setBucket(TimeUnit.ONE_MINUTE);
builder.addGaugeMetric(gaugeMetricBuilder.build());
} else {
EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
eventMetricBuilder
.setId(metricId)
.setWhat(atomMatcherId);
builder.addEventMetric(eventMetricBuilder.build());
builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
}
atomMatcherId++;
mTrackedMetrics.add(metricId++);
}
return builder.build();
}
private static AtomMatcher createAtomMatcher(int atomId, long matcherId) {
AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
atomMatcherBuilder
.setId(matcherId)
.setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder().setAtomId(atomId));
return atomMatcherBuilder.build();
}
private static String pushConfig(StatsdConfig config) throws IOException, InterruptedException {
File configFile = File.createTempFile("statsdconfig", ".config");
configFile.deleteOnExit();
Files.write(config.toByteArray(), configFile);
String remotePath = "/data/local/tmp/" + configFile.getName();
Utils.runCommand(null, LOGGER, "adb", "push", configFile.getAbsolutePath(), remotePath);
Utils.runCommand(null, LOGGER,
"adb", "shell", "cat", remotePath, "|", Utils.CMD_UPDATE_CONFIG,
String.valueOf(CONFIG_ID));
return remotePath;
}
private static void removeConfig() {
try {
Utils.runCommand(null, LOGGER,
"adb", "shell", Utils.CMD_REMOVE_CONFIG, String.valueOf(CONFIG_ID));
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to remove config: " + e.getMessage());
}
}
private static boolean isPulledAtom(int atomId) {
return atomId >= PULL_ATOM_START;
}
private static boolean hasPulledAtom(Set<Integer> atoms) {
for (Integer i : atoms) {
if (isPulledAtom(i)) {
return true;
}
}
return false;
}
}