Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0a8a0b1b authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Extend & refactor Statsd TestDrive tool." into rvc-dev am: 4502a6b1

Change-Id: Ief08be1d7ede467c9dc62f28f6d333e7f8361e30
parents 9a70f81b 4502a6b1
Loading
Loading
Loading
Loading
+25 −4
Original line number Diff line number Diff line
@@ -11,9 +11,8 @@ java_binary_host {
    ],
}

java_binary_host {
    name: "statsd_testdrive",
    manifest: "testdrive_manifest.txt",
java_library_host {
    name: "statsd_testdrive_lib",
    srcs: [
        "src/com/android/statsd/shelltools/testdrive/*.java",
        "src/com/android/statsd/shelltools/Utils.java",
@@ -23,3 +22,25 @@ java_binary_host {
        "guava",
    ],
}


java_binary_host {
    name: "statsd_testdrive",
    manifest: "testdrive_manifest.txt",
    static_libs: [
        "statsd_testdrive_lib",
    ],
}

java_test_host {
    name: "statsd_testdrive_test",
    test_suites: ["general-tests"],
    srcs: ["test/com/android/statsd/shelltools/testdrive/*.java"],
    static_libs: [
        "statsd_testdrive_lib",
        "junit",
        "platformprotos",
        "guava",
    ],
}
+8 −0
Original line number Diff line number Diff line
{
  "presubmit": [
    {
      "name": "statsd_testdrive_test",
      "host": true
    }
  ]
}
+52 −0
Original line number Diff line number Diff line
@@ -229,4 +229,56 @@ public class Utils {
        }
        return null;
    }

    /**
     * Returns ANDROID_SERIAL environment variable, or null if that is undefined or unavailable.
     * @param logger Destination of error messages.
     * @return String value of ANDROID_SERIAL environment variable, or null.
     */
    public static String getDefaultDevice(Logger logger) {
        try {
            return System.getenv("ANDROID_SERIAL");
        } catch (Exception ex) {
            logger.log(Level.SEVERE, "Failed to check ANDROID_SERIAL environment variable.",
                    ex);
        }
        return null;
    }

    /**
     * Returns the device to use if one can be deduced, or null.
     * @param device Command-line specified device, or null.
     * @param connectedDevices List of all connected devices.
     * @param defaultDevice Environment-variable specified device, or null.
     * @param logger Destination of error messages.
     * @return Device to use, or null.
     */
    public static String chooseDevice(String device, List<String> connectedDevices,
            String defaultDevice, Logger logger) {
        if (connectedDevices == null || connectedDevices.isEmpty()) {
            logger.severe("No connected device.");
            return null;
        }
        if (device != null) {
            if (connectedDevices.contains(device)) {
                return device;
            }
            logger.severe("Device not connected: " + device);
            return null;
        }
        if (connectedDevices.size() == 1) {
            return connectedDevices.get(0);
        }
        if (defaultDevice != null) {
            if (connectedDevices.contains(defaultDevice)) {
                return defaultDevice;
            } else {
                logger.severe("ANDROID_SERIAL device is not connected: " + defaultDevice);
                return null;
            }
        }
        logger.severe("More than one device is connected. Choose one"
                + " with -s DEVICE_SERIAL or environment variable ANDROID_SERIAL.");
        return null;
    }
}
+2 −11
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
@@ -112,17 +111,9 @@ public class LocalDrive {
        }

        List<String> connectedDevices = Utils.getDeviceSerials(sLogger);
        if (connectedDevices == null || connectedDevices.size() == 0) {
            sLogger.log(Level.SEVERE, "No device connected.");
            return;
        }
        if (connectedDevices.size() == 1 && deviceSerial == null) {
            deviceSerial = connectedDevices.get(0);
        }

        deviceSerial = Utils.chooseDevice(deviceSerial, connectedDevices,
                Utils.getDefaultDevice(sLogger), sLogger);
        if (deviceSerial == null) {
            sLogger.log(Level.SEVERE, "More than one devices connected. Please specify"
                    + " with -s DEVICE_SERIAL");
            return;
        }

+207 −128
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.statsd.shelltools.testdrive;

import com.android.internal.os.StatsdConfigProto;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.EventMetric;
import com.android.internal.os.StatsdConfigProto.FieldFilter;
@@ -29,6 +30,7 @@ import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.StatsLogReport;
import com.android.statsd.shelltools.Utils;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.io.Files;

import java.io.File;
@@ -39,6 +41,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;

@@ -73,148 +76,181 @@ public class TestDrive {
    };
    private static final Logger LOGGER = Logger.getLogger(TestDrive.class.getName());

    private String mAdditionalAllowedPackage;
    private String mDeviceSerial;
    private final Set<Long> mTrackedMetrics = new HashSet<>();
    @VisibleForTesting
    String mDeviceSerial = null;

    public static void main(String[] args) {
        final Configuration configuration = new Configuration();

        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] "
                    + "[-s DEVICE_SERIAL_NUMBER]"
                    + "<atomId1> <atomId2> ... <atomIdN>");
        if (!testDrive.processArgs(configuration, args,
                Utils.getDeviceSerials(LOGGER), Utils.getDefaultDevice(LOGGER))) {
            return;
        }

        List<String> connectedDevices = Utils.getDeviceSerials(LOGGER);
        if (connectedDevices == null || connectedDevices.size() == 0) {
            LOGGER.log(Level.SEVERE, "No device connected.");
            return;
        final ConfigMetricsReportList reports = testDrive.testDriveAndGetReports(
                configuration.createConfig(), configuration.hasPulledAtoms(),
                configuration.hasPushedAtoms());
        if (reports != null) {
            configuration.dumpMetrics(reports);
        }

        int arg_index = 0;
        while (arg_index < args.length) {
            String arg = args[arg_index];
            if (arg.equals("-p")) {
                testDrive.mAdditionalAllowedPackage = args[++arg_index];
            } else if (arg.equals("-s")) {
                testDrive.mDeviceSerial = args[++arg_index];
            } else {
                break;
    }
            arg_index++;

    boolean processArgs(Configuration configuration, String[] args, List<String> connectedDevices,
            String defaultDevice) {
        if (args.length < 1) {
            LOGGER.severe("Usage: ./test_drive [-one] "
                    + "[-p additional_allowed_package] "
                    + "[-s DEVICE_SERIAL_NUMBER] "
                    + "<atomId1> <atomId2> ... <atomIdN>");
            return false;
        }

        if (connectedDevices.size() == 1 && testDrive.mDeviceSerial == null) {
            testDrive.mDeviceSerial = connectedDevices.get(0);
        int first_arg = 0;
        // Consume all flags, which must precede all atoms
        for (; first_arg < args.length; ++first_arg) {
            String arg = args[first_arg];
            int remaining_args = args.length - first_arg;
            if (remaining_args >= 2 && arg.equals("-one")) {
                LOGGER.info("Creating one event metric to catch all pushed atoms.");
                configuration.mOnePushedAtomEvent = true;
            } else if (remaining_args >= 3 && arg.equals("-p")) {
                configuration.mAdditionalAllowedPackage = args[++first_arg];
            } else if (remaining_args >= 3 && arg.equals("-s")) {
                mDeviceSerial = args[++first_arg];
            } else {
                break;  // Found the atom list
            }
        }

        if (testDrive.mDeviceSerial == null) {
            LOGGER.log(Level.SEVERE, "More than one devices connected. Please specify"
                    + " with -s DEVICE_SERIAL");
            return;
        mDeviceSerial = Utils.chooseDevice(mDeviceSerial, connectedDevices, defaultDevice, LOGGER);
        if (mDeviceSerial == null) {
            return false;
        }

        for (int i = arg_index; i < args.length; i++) {
        for ( ; first_arg < args.length; ++first_arg) {
            String atom = args[first_arg];
            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);
                configuration.addAtom(Integer.valueOf(atom));
            } catch (NumberFormatException e) {
                LOGGER.log(Level.SEVERE, "Bad atom id provided: " + args[i]);
                continue;
                LOGGER.severe("Bad atom id provided: " + atom);
            }
        }

        try {
            StatsdConfig config = testDrive.createConfig(trackedAtoms);
        return configuration.hasPulledAtoms() || configuration.hasPushedAtoms();
    }

    private ConfigMetricsReportList testDriveAndGetReports(StatsdConfig config,
            boolean hasPulledAtoms, boolean hasPushedAtoms) {
        if (config == null) {
                LOGGER.log(Level.SEVERE, "Failed to create valid config.");
                return;
            LOGGER.severe("Failed to create valid config.");
            return null;
        }
            remoteConfigPath = testDrive.pushConfig(config, testDrive.mDeviceSerial);
            LOGGER.info("Pushed the following config to statsd:");

        String remoteConfigPath = null;
        try {
            remoteConfigPath = pushConfig(config, mDeviceSerial);
            LOGGER.info("Pushed the following config to statsd on device '" + mDeviceSerial
                    + "':");
            LOGGER.info(config.toString());
            if (!hasPulledAtom(trackedAtoms)) {
            if (hasPushedAtoms) {
                LOGGER.info("Now please play with the device to trigger the event.");
            }
            if (!hasPulledAtoms) {
                LOGGER.info(
                        "Now please play with the device to trigger the event. All events should "
                                + "be dumped after 1 min ...");
                        "All events should be dumped after 1 min ...");
                Thread.sleep(60_000);
            } else {
                LOGGER.info("Now wait for 1.5 minutes ...");
                LOGGER.info("All events should be dumped after 1.5 minutes ...");
                Thread.sleep(15_000);
                Utils.logAppBreadcrumb(0, 0, LOGGER, testDrive.mDeviceSerial);
                Utils.logAppBreadcrumb(0, 0, LOGGER, mDeviceSerial);
                Thread.sleep(75_000);
            }
            testDrive.dumpMetrics();
            return Utils.getReportList(CONFIG_ID, true, false, LOGGER,
                    mDeviceSerial);
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Failed to test drive: " + e.getMessage(), e);
        } finally {
            testDrive.removeConfig(testDrive.mDeviceSerial);
            removeConfig(mDeviceSerial);
            if (remoteConfigPath != null) {
                try {
                    Utils.runCommand(null, LOGGER,
                            "adb", "-s", testDrive.mDeviceSerial, "shell", "rm", remoteConfigPath);
                            "adb", "-s", mDeviceSerial, "shell", "rm",
                            remoteConfigPath);
                } catch (Exception e) {
                    LOGGER.log(Level.WARNING,
                            "Unable to remove remote config file: " + remoteConfigPath, e);
                }
            }
        }
        return null;
    }

    private void dumpMetrics() throws Exception {
        ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, LOGGER,
                mDeviceSerial);
    static class Configuration {
        boolean mOnePushedAtomEvent = false;
        @VisibleForTesting
        Set<Integer> mPushedAtoms = new TreeSet<>();
        @VisibleForTesting
        Set<Integer> mPulledAtoms = new TreeSet<>();
        @VisibleForTesting
        String mAdditionalAllowedPackage = null;
        private final Set<Long> mTrackedMetrics = new HashSet<>();

        private void dumpMetrics(ConfigMetricsReportList reportList) {
            // 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())) {
                if (isTrackedMetric(statsLog.getMetricId())) {
                    LOGGER.info(statsLog.toString());
                }
            }
        }

    private StatsdConfig createConfig(Set<Integer> atomIds) {
        long metricId = METRIC_ID_BASE;
        long atomMatcherId = ATOM_MATCHER_ID_BASE;
        boolean isTrackedMetric(long metricId) {
            return mTrackedMetrics.contains(metricId);
        }

        ArrayList<String> allowedSources = new ArrayList<>();
        Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES);
        if (mAdditionalAllowedPackage != null) {
            allowedSources.add(mAdditionalAllowedPackage);
        static boolean isPulledAtom(int atomId) {
            return atomId >= PULL_ATOM_START && atomId <= MAX_PLATFORM_ATOM_TAG
                    || atomId >= VENDOR_PULLED_ATOM_START_TAG;
        }

        StatsdConfig.Builder builder = StatsdConfig.newBuilder();
        builder
            .addAllAllowedLogSource(allowedSources)
            .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES))
            .addPullAtomPackages(PullAtomPackages.newBuilder()
                    .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER)
                    .addPackages("AID_GPU_SERVICE"))
            .addPullAtomPackages(PullAtomPackages.newBuilder()
                    .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER)
                    .addPackages("AID_GPU_SERVICE"))
            .addPullAtomPackages(PullAtomPackages.newBuilder()
                    .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER)
                    .addPackages("AID_STATSD"))
            .setHashStringsInMetricReport(false);
        void addAtom(Integer atom) {
            if (Atom.getDescriptor().findFieldByNumber(atom) == null) {
                LOGGER.severe("No such atom found: " + atom);
                return;
            }
            if (isPulledAtom(atom)) {
                mPulledAtoms.add(atom);
            } else {
                mPushedAtoms.add(atom);
            }
        }

        if (hasPulledAtom(atomIds)) {
        private boolean hasPulledAtoms() {
            return !mPulledAtoms.isEmpty();
        }

        private boolean hasPushedAtoms() {
            return !mPushedAtoms.isEmpty();
        }

        StatsdConfig createConfig() {
            long metricId = METRIC_ID_BASE;
            long atomMatcherId = ATOM_MATCHER_ID_BASE;

            StatsdConfig.Builder builder = baseBuilder();

            if (hasPulledAtoms()) {
                builder.addAtomMatcher(
                        createAtomMatcher(
                            Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, APP_BREADCRUMB_MATCHER_ID));
                                Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER,
                                APP_BREADCRUMB_MATCHER_ID));
            }

        for (int atomId : atomIds) {
            if (isPulledAtom(atomId)) {
            for (int atomId : mPulledAtoms) {
                builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
                GaugeMetric.Builder gaugeMetricBuilder = GaugeMetric.newBuilder();
                gaugeMetricBuilder
@@ -226,17 +262,39 @@ public class TestDrive {
                        .setSamplingType(GaugeMetric.SamplingType.FIRST_N_SAMPLES)
                        .setMaxNumGaugeAtomsPerBucket(100);
                builder.addGaugeMetric(gaugeMetricBuilder.build());
                atomMatcherId++;
                mTrackedMetrics.add(metricId++);
            }

            // A simple atom matcher for each pushed atom.
            List<AtomMatcher> simpleAtomMatchers = new ArrayList<>();
            for (int atomId : mPushedAtoms) {
                final AtomMatcher atomMatcher = createAtomMatcher(atomId, atomMatcherId++);
                simpleAtomMatchers.add(atomMatcher);
                builder.addAtomMatcher(atomMatcher);
            }

            if (mOnePushedAtomEvent) {
                // Create a union event metric, using an matcher that matches all pulled atoms.
                AtomMatcher unionAtomMatcher = createUnionMatcher(simpleAtomMatchers,
                        atomMatcherId);
                builder.addAtomMatcher(unionAtomMatcher);
                EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
                eventMetricBuilder.setId(metricId).setWhat(unionAtomMatcher.getId());
                builder.addEventMetric(eventMetricBuilder.build());
                mTrackedMetrics.add(metricId++);
            } else {
                // Create multiple event metrics, one per pulled atom.
                for (AtomMatcher atomMatcher : simpleAtomMatchers) {
                    EventMetric.Builder eventMetricBuilder = EventMetric.newBuilder();
                    eventMetricBuilder
                            .setId(metricId)
                    .setWhat(atomMatcherId);
                            .setWhat(atomMatcher.getId());
                    builder.addEventMetric(eventMetricBuilder.build());
                builder.addAtomMatcher(createAtomMatcher(atomId, atomMatcherId));
            }
            atomMatcherId++;
                    mTrackedMetrics.add(metricId++);
                }
            }

            return builder.build();
        }

@@ -248,6 +306,41 @@ public class TestDrive {
            return atomMatcherBuilder.build();
        }

        private AtomMatcher createUnionMatcher(List<AtomMatcher> simpleAtomMatchers,
                long atomMatcherId) {
            AtomMatcher.Combination.Builder combinationBuilder =
                    AtomMatcher.Combination.newBuilder();
            combinationBuilder.setOperation(StatsdConfigProto.LogicalOperation.OR);
            for (AtomMatcher matcher : simpleAtomMatchers) {
                combinationBuilder.addMatcher(matcher.getId());
            }
            AtomMatcher.Builder atomMatcherBuilder = AtomMatcher.newBuilder();
            atomMatcherBuilder.setId(atomMatcherId).setCombination(combinationBuilder.build());
            return atomMatcherBuilder.build();
        }

        private StatsdConfig.Builder baseBuilder() {
            ArrayList<String> allowedSources = new ArrayList<>();
            Collections.addAll(allowedSources, ALLOWED_LOG_SOURCES);
            if (mAdditionalAllowedPackage != null) {
                allowedSources.add(mAdditionalAllowedPackage);
            }
            return StatsdConfig.newBuilder()
                    .addAllAllowedLogSource(allowedSources)
                    .addAllDefaultPullPackages(Arrays.asList(DEFAULT_PULL_SOURCES))
                    .addPullAtomPackages(PullAtomPackages.newBuilder()
                            .setAtomId(Atom.GPU_STATS_GLOBAL_INFO_FIELD_NUMBER)
                            .addPackages("AID_GPU_SERVICE"))
                    .addPullAtomPackages(PullAtomPackages.newBuilder()
                            .setAtomId(Atom.GPU_STATS_APP_INFO_FIELD_NUMBER)
                            .addPackages("AID_GPU_SERVICE"))
                    .addPullAtomPackages(PullAtomPackages.newBuilder()
                            .setAtomId(Atom.TRAIN_INFO_FIELD_NUMBER)
                            .addPackages("AID_STATSD"))
                    .setHashStringsInMetricReport(false);
        }
    }

    private static String pushConfig(StatsdConfig config, String deviceSerial)
            throws IOException, InterruptedException {
        File configFile = File.createTempFile("statsdconfig", ".config");
@@ -267,21 +360,7 @@ public class TestDrive {
            Utils.runCommand(null, LOGGER, "adb", "-s", deviceSerial,
                    "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 && atomId <= MAX_PLATFORM_ATOM_TAG
                || atomId >= VENDOR_PULLED_ATOM_START_TAG;
            LOGGER.severe("Failed to remove config: " + e.getMessage());
        }

    private static boolean hasPulledAtom(Set<Integer> atoms) {
        for (Integer i : atoms) {
            if (isPulledAtom(i)) {
                return true;
            }
        }
        return false;
    }
}
Loading