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

Commit 183bdcf1 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "statsd local tool"

parents 267d6ac1 3e906582
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -424,13 +424,14 @@ void StatsService::print_cmd_help(int out) {
    dprintf(out, "\n                     be removed from memory and disk!\n");
    dprintf(out, "\n");
    dprintf(out,
            "usage: adb shell cmd stats dump-report [UID] NAME [--include_current_bucket] "
            "[--proto]\n");
            "usage: adb shell cmd stats dump-report [UID] NAME [--keep_data] "
            "[--include_current_bucket] [--proto]\n");
    dprintf(out, "  Dump all metric data for a configuration.\n");
    dprintf(out, "  UID           The uid of the configuration. It is only possible to pass\n");
    dprintf(out, "                the UID parameter on eng builds. If UID is omitted the\n");
    dprintf(out, "                calling uid is used.\n");
    dprintf(out, "  NAME          The name of the configuration\n");
    dprintf(out, "  --keep_data   Do NOT erase the data upon dumping it.\n");
    dprintf(out, "  --proto       Print proto binary.\n");
    dprintf(out, "\n");
    dprintf(out, "\n");
@@ -590,6 +591,7 @@ status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) {
        bool good = false;
        bool proto = false;
        bool includeCurrentBucket = false;
        bool eraseData = true;
        int uid;
        string name;
        if (!std::strcmp("--proto", args[argCount-1].c_str())) {
@@ -600,6 +602,10 @@ status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) {
            includeCurrentBucket = true;
            argCount -= 1;
        }
        if (!std::strcmp("--keep_data", args[argCount-1].c_str())) {
            eraseData = false;
            argCount -= 1;
        }
        if (argCount == 2) {
            // Automatically pick the UID
            uid = IPCThreadState::self()->getCallingUid();
@@ -627,7 +633,7 @@ status_t StatsService::cmd_dump_report(int out, const Vector<String8>& args) {
        if (good) {
            vector<uint8_t> data;
            mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
                                     includeCurrentBucket, true /* erase_data */, ADB_DUMP, &data);
                                     includeCurrentBucket, eraseData, ADB_DUMP, &data);
            if (proto) {
                for (size_t i = 0; i < data.size(); i ++) {
                    dprintf(out, "%c", data[i]);
+43 −0
Original line number Diff line number Diff line
@@ -240,6 +240,49 @@ TEST(StatsLogProcessorTest, TestReportIncludesSubConfig) {
    EXPECT_EQ(2, report.annotation(0).field_int32());
}

TEST(StatsLogProcessorTest, TestOnDumpReportEraseData) {
    // Setup a simple config.
    StatsdConfig config;
    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
    *config.add_atom_matcher() = wakelockAcquireMatcher;

    auto countMetric = config.add_count_metric();
    countMetric->set_id(123456);
    countMetric->set_what(wakelockAcquireMatcher.id());
    countMetric->set_bucket(FIVE_MINUTES);

    ConfigKey cfgKey;
    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);

    std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
    auto event = CreateAcquireWakelockEvent(attributions1, "wl1", 2);
    processor->OnLogEvent(event.get());

    vector<uint8_t> bytes;
    ConfigMetricsReportList output;

    // Dump report WITHOUT erasing data.
    processor->onDumpReport(cfgKey, 3, true, false /* Do NOT erase data. */, ADB_DUMP, &bytes);
    output.ParseFromArray(bytes.data(), bytes.size());
    EXPECT_EQ(output.reports_size(), 1);
    EXPECT_EQ(output.reports(0).metrics_size(), 1);
    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);

    // Dump report WITH erasing data. There should be data since we didn't previously erase it.
    processor->onDumpReport(cfgKey, 4, true, true /* DO erase data. */, ADB_DUMP, &bytes);
    output.ParseFromArray(bytes.data(), bytes.size());
    EXPECT_EQ(output.reports_size(), 1);
    EXPECT_EQ(output.reports(0).metrics_size(), 1);
    EXPECT_EQ(output.reports(0).metrics(0).count_metrics().data_size(), 1);

    // Dump report again. There should be no data since we erased it.
    processor->onDumpReport(cfgKey, 5, true, true /* DO erase data. */, ADB_DUMP, &bytes);
    output.ParseFromArray(bytes.data(), bytes.size());
    bool noData = (output.reports_size() == 0) || (output.reports(0).metrics_size() == 0);
    EXPECT_TRUE(noData);
}

#else
GTEST_LOG_(INFO) << "This test does nothing.\n";
#endif
+25 −0
Original line number Diff line number Diff line
java_binary_host {
    name: "statsd_localdrive",
    manifest: "localdrive_manifest.txt",
    srcs: [
        "src/com/android/statsd/shelltools/localdrive/*.java",
        "src/com/android/statsd/shelltools/Utils.java",
    ],
    static_libs: [
        "platformprotos",
        "guava",
    ],
}

java_binary_host {
    name: "statsd_testdrive",
    manifest: "testdrive_manifest.txt",
    srcs: [
        "src/com/android/statsd/shelltools/testdrive/*.java",
        "src/com/android/statsd/shelltools/Utils.java",
    ],
    static_libs: [
        "platformprotos",
        "guava",
    ],
}
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
Main-class: com.android.statsd.shelltools.localdrive.LocalDrive
+119 −0
Original line number Diff line number Diff line
/*
 * 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;

import com.android.os.StatsLog.ConfigMetricsReportList;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * Utilities for local use of statsd.
 */
public class Utils {

    public static final String CMD_UPDATE_CONFIG = "cmd stats config update";
    public static final String CMD_DUMP_REPORT = "cmd stats dump-report";
    public static final String CMD_REMOVE_CONFIG = "cmd stats config remove";

    public static final String SHELL_UID = "2000"; // Use shell, even if rooted.

    /**
     * Runs adb shell command with output directed to outputFile if non-null.
     */
    public static void runCommand(File outputFile, Logger logger, String... commands)
            throws IOException, InterruptedException {
        ProcessBuilder pb = new ProcessBuilder(commands);
        if (outputFile != null && outputFile.exists() && outputFile.canWrite()) {
            pb.redirectOutput(outputFile);
        }
        Process process = pb.start();

        // Capture any errors
        StringBuilder err = new StringBuilder();
        BufferedReader br = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        for (String line = br.readLine(); line != null; line = br.readLine()) {
            err.append(line).append('\n');
        }
        logger.severe(err.toString());

        // Check result
        if (process.waitFor() == 0) {
            logger.fine("Adb command successful.");
        } else {
            logger.severe("Abnormal adb shell cmd termination for: " + String.join(",", commands));
            throw new RuntimeException("Error running adb command: " + err.toString());
        }
    }

    /**
     * Dumps the report from the device and converts it to a ConfigMetricsReportList.
     * Erases the data if clearData is true.
     */
    public static ConfigMetricsReportList getReportList(long configId, boolean clearData,
            Logger logger) throws IOException, InterruptedException {
        try {
            File outputFile = File.createTempFile("statsdret", ".bin");
            outputFile.deleteOnExit();
            runCommand(
                    outputFile,
                    logger,
                    "adb",
                    "shell",
                    CMD_DUMP_REPORT,
                    SHELL_UID,
                    String.valueOf(configId),
                    clearData ? "" : "--keep_data",
                    "--include_current_bucket",
                    "--proto");
            ConfigMetricsReportList reportList =
                    ConfigMetricsReportList.parseFrom(new FileInputStream(outputFile));
            return reportList;
        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
            logger.severe("Failed to fetch and parse the statsd output report. "
                            + "Perhaps there is not a valid statsd config for the requested "
                            + "uid=" + SHELL_UID
                            + ", configId=" + configId
                            + ".");
            throw (e);
        }
    }

    public static void setUpLogger(Logger logger, boolean debug) {
        ConsoleHandler handler = new ConsoleHandler();
        handler.setFormatter(new LocalToolsFormatter());
        logger.setUseParentHandlers(false);
        if (debug) {
            handler.setLevel(Level.ALL);
            logger.setLevel(Level.ALL);
        }
        logger.addHandler(handler);
    }

    public static class LocalToolsFormatter extends Formatter {
        public String format(LogRecord record) {
            return record.getMessage() + "\n";
        }
    }
}
Loading