Loading cmds/statsd/src/StatsService.cpp +9 −3 Original line number Diff line number Diff line Loading @@ -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"); Loading Loading @@ -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())) { Loading @@ -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(); Loading Loading @@ -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]); Loading cmds/statsd/tests/StatsLogProcessor_test.cpp +43 −0 Original line number Diff line number Diff line Loading @@ -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 Loading cmds/statsd/tools/localtools/Android.bp 0 → 100644 +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 cmds/statsd/tools/localtools/localdrive_manifest.txt 0 → 100644 +1 −0 Original line number Diff line number Diff line Main-class: com.android.statsd.shelltools.localdrive.LocalDrive cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java 0 → 100644 +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
cmds/statsd/src/StatsService.cpp +9 −3 Original line number Diff line number Diff line Loading @@ -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"); Loading Loading @@ -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())) { Loading @@ -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(); Loading Loading @@ -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]); Loading
cmds/statsd/tests/StatsLogProcessor_test.cpp +43 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
cmds/statsd/tools/localtools/Android.bp 0 → 100644 +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
cmds/statsd/tools/localtools/localdrive_manifest.txt 0 → 100644 +1 −0 Original line number Diff line number Diff line Main-class: com.android.statsd.shelltools.localdrive.LocalDrive
cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java 0 → 100644 +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"; } } }