diff options
Diffstat (limited to 'cmds')
55 files changed, 2207 insertions, 949 deletions
diff --git a/cmds/incident_helper/src/ih_util.cpp b/cmds/incident_helper/src/ih_util.cpp index 0b51e66c2108..db4f586c7e31 100644 --- a/cmds/incident_helper/src/ih_util.cpp +++ b/cmds/incident_helper/src/ih_util.cpp @@ -30,22 +30,26 @@ bool isValidChar(char c) { || (v == (uint8_t)'_'); } -static std::string trim(const std::string& s, const std::string& chars) { - const auto head = s.find_first_not_of(chars); +std::string trim(const std::string& s, const std::string& charset) { + const auto head = s.find_first_not_of(charset); if (head == std::string::npos) return ""; - const auto tail = s.find_last_not_of(chars); + const auto tail = s.find_last_not_of(charset); return s.substr(head, tail - head + 1); } -static std::string trimDefault(const std::string& s) { +static inline std::string toLowerStr(const std::string& s) { + std::string res(s); + std::transform(res.begin(), res.end(), res.begin(), ::tolower); + return res; +} + +static inline std::string trimDefault(const std::string& s) { return trim(s, DEFAULT_WHITESPACE); } -static std::string trimHeader(const std::string& s) { - std::string res = trimDefault(s); - std::transform(res.begin(), res.end(), res.begin(), ::tolower); - return res; +static inline std::string trimHeader(const std::string& s) { + return toLowerStr(trimDefault(s)); } // This is similiar to Split in android-base/file.h, but it won't add empty string @@ -188,97 +192,106 @@ bool Reader::ok(std::string* error) { } // ============================================================================== -static int -lookupName(const char** names, const int size, const char* name) +Table::Table(const char* names[], const uint64_t ids[], const int count) + :mEnums(), + mEnumValuesByName() { - for (int i=0; i<size; i++) { - if (strcmp(name, names[i]) == 0) { - return i; - } + map<std::string, uint64_t> fields; + for (int i = 0; i < count; i++) { + fields[names[i]] = ids[i]; } - return -1; -} - -EnumTypeMap::EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount) - :mEnumNames(enumNames), - mEnumValues(enumValues), - mEnumCount(enumCount) -{ -} - -EnumTypeMap::~EnumTypeMap() -{ + mFields = fields; } -int -EnumTypeMap::parseValue(const std::string& value) +Table::~Table() { - int index = lookupName(mEnumNames, mEnumCount, value.c_str()); - if (index < 0) return mEnumValues[0]; // Assume value 0 is default - return mEnumValues[index]; } -Table::Table(const char* names[], const uint64_t ids[], const int count) - :mFieldNames(names), - mFieldIds(ids), - mFieldCount(count), - mEnums() +void +Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize) { -} + if (mFields.find(field) == mFields.end()) return; -Table::~Table() -{ + map<std::string, int> enu; + for (int i = 0; i < enumSize; i++) { + enu[enumNames[i]] = enumValues[i]; + } + mEnums[field] = enu; } void -Table::addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize) +Table::addEnumNameToValue(const char* enumName, const int enumValue) { - int index = lookupName(mFieldNames, mFieldCount, field); - if (index < 0) return; - - EnumTypeMap enu(enumNames, enumValues, enumSize); - mEnums[index] = enu; + mEnumValuesByName[enumName] = enumValue; } bool Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value) { - int index = lookupName(mFieldNames, mFieldCount, name.c_str()); - if (index < 0) return false; + if (mFields.find(name) == mFields.end()) return false; - uint64_t found = mFieldIds[index]; - switch (found & FIELD_TYPE_MASK) { - case FIELD_TYPE_DOUBLE: - case FIELD_TYPE_FLOAT: + uint64_t found = mFields[name]; + record_t repeats; // used for repeated fields + switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) { + case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE: + case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT: proto->write(found, toDouble(value)); break; - case FIELD_TYPE_STRING: - case FIELD_TYPE_BYTES: + case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING: + case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES: proto->write(found, value); break; - case FIELD_TYPE_INT64: - case FIELD_TYPE_SINT64: - case FIELD_TYPE_UINT64: - case FIELD_TYPE_FIXED64: - case FIELD_TYPE_SFIXED64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64: + case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64: proto->write(found, toLongLong(value)); break; - case FIELD_TYPE_BOOL: + case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL: + if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) { + proto->write(found, true); + break; + } + if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) { + proto->write(found, false); + break; + } return false; - case FIELD_TYPE_ENUM: - if (mEnums.find(index) == mEnums.end()) { - // forget to add enum type mapping + case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM: + // if the field has its own enum mapping, use this, otherwise use general name to value mapping. + if (mEnums.find(name) != mEnums.end()) { + if (mEnums[name].find(value) != mEnums[name].end()) { + proto->write(found, mEnums[name][value]); + } else { + proto->write(found, 0); // TODO: should get the default enum value (Unknown) + } + } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) { + proto->write(found, mEnumValuesByName[value]); + } else { return false; } - proto->write(found, mEnums[index].parseValue(value)); break; - case FIELD_TYPE_INT32: - case FIELD_TYPE_SINT32: - case FIELD_TYPE_UINT32: - case FIELD_TYPE_FIXED32: - case FIELD_TYPE_SFIXED32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32: + case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32: proto->write(found, toInt(value)); break; + // REPEATED TYPE below: + case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32: + repeats = parseRecord(value, COMMA_DELIMITER); + for (size_t i=0; i<repeats.size(); i++) { + proto->write(found, toInt(repeats[i])); + } + break; + case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING: + repeats = parseRecord(value, COMMA_DELIMITER); + for (size_t i=0; i<repeats.size(); i++) { + proto->write(found, repeats[i]); + } + break; default: return false; } diff --git a/cmds/incident_helper/src/ih_util.h b/cmds/incident_helper/src/ih_util.h index e8366fa599e2..4a5fe1dd7a42 100644 --- a/cmds/incident_helper/src/ih_util.h +++ b/cmds/incident_helper/src/ih_util.h @@ -37,6 +37,9 @@ const std::string COMMA_DELIMITER = ","; // returns true if c is a-zA-Z0-9 or underscore _ bool isValidChar(char c); +// trim the string with the given charset +std::string trim(const std::string& s, const std::string& charset); + /** * When a text has a table format like this * line 1: HeadA HeadB HeadC @@ -98,21 +101,6 @@ private: std::string mStatus; }; -class EnumTypeMap -{ -public: - EnumTypeMap() {}; - EnumTypeMap(const char* enumNames[], const uint32_t enumValues[], const int enumCount); - ~EnumTypeMap(); - - int parseValue(const std::string& value); - -private: - const char** mEnumNames; - const uint32_t* mEnumValues; - int mEnumCount; -}; - /** * The class contains a mapping between table headers to its field ids. * And allow users to insert the field values to proto based on its header name. @@ -124,14 +112,16 @@ public: ~Table(); // Add enum names to values for parsing purpose. - void addEnumTypeMap(const char* field, const char* enumNames[], const uint32_t enumValues[], const int enumSize); + void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize); + + // manually add enum names to values mapping, useful when an Enum type is used by a lot of fields, and there are no name conflicts + void addEnumNameToValue(const char* enumName, const int enumValue); bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value); private: - const char** mFieldNames; - const uint64_t* mFieldIds; - const int mFieldCount; - map<int, EnumTypeMap> mEnums; + map<std::string, uint64_t> mFields; + map<std::string, map<std::string, int>> mEnums; + map<std::string, int> mEnumValuesByName; }; #endif // INCIDENT_HELPER_UTIL_H diff --git a/cmds/incident_helper/src/main.cpp b/cmds/incident_helper/src/main.cpp index 5c9a468dea0b..c8a0883d493c 100644 --- a/cmds/incident_helper/src/main.cpp +++ b/cmds/incident_helper/src/main.cpp @@ -21,6 +21,7 @@ #include "parsers/KernelWakesParser.h" #include "parsers/PageTypeInfoParser.h" #include "parsers/ProcrankParser.h" +#include "parsers/SystemPropertiesParser.h" #include <android-base/file.h> #include <getopt.h> @@ -50,6 +51,8 @@ static TextParserBase* selectParser(int section) { return new ReverseParser(); /* ========================================================================= */ // IDs larger than 1 are section ids reserved in incident.proto + case 1000: + return new SystemPropertiesParser(); case 2000: return new ProcrankParser(); case 2001: diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp new file mode 100644 index 000000000000..ee5feb03242e --- /dev/null +++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 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. + */ +#define LOG_TAG "incident_helper" + +#include <android/util/ProtoOutputStream.h> + +#include "frameworks/base/core/proto/android/os/system_properties.proto.h" +#include "ih_util.h" +#include "SystemPropertiesParser.h" + +using namespace android::os; + +const string LINE_DELIMITER = "]: ["; + +// system properties' names sometimes are not valid proto field names, make the names valid. +static string convertToFieldName(const string& name) { + int len = (int)name.length(); + char cstr[len + 1]; + strcpy(cstr, name.c_str()); + for (int i = 0; i < len; i++) { + if (!isValidChar(cstr[i])) { + cstr[i] = '_'; + } + } + return string(cstr); +} + +status_t +SystemPropertiesParser::Parse(const int in, const int out) const +{ + Reader reader(in); + string line; + string name; // the name of the property + string value; // the string value of the property + + ProtoOutputStream proto; + Table table(SystemPropertiesProto::_FIELD_NAMES, SystemPropertiesProto::_FIELD_IDS, SystemPropertiesProto::_FIELD_COUNT); + table.addEnumNameToValue("running", SystemPropertiesProto::STATUS_RUNNING); + table.addEnumNameToValue("stopped", SystemPropertiesProto::STATUS_STOPPED); + + // parse line by line + while (reader.readLine(&line)) { + if (line.empty()) continue; + + line = line.substr(1, line.size() - 2); // trim [] + size_t index = line.find(LINE_DELIMITER); // split by "]: [" + if (index == string::npos) { + fprintf(stderr, "Bad Line %s\n", line.c_str()); + continue; + } + name = line.substr(0, index); + value = trim(line.substr(index + 4), DEFAULT_WHITESPACE); + if (value.empty()) continue; + + // if the property name couldn't be found in proto definition or the value has mistype, + // add to extra properties with its name and value + if (!table.insertField(&proto, convertToFieldName(name), value)) { + long long token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES); + proto.write(SystemPropertiesProto::Property::NAME, name); + proto.write(SystemPropertiesProto::Property::VALUE, value); + proto.end(token); + } + } + + if (!reader.ok(&line)) { + fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str()); + return -1; + } + + if (!proto.flush(out)) { + fprintf(stderr, "[%s]Error writing proto back\n", this->name.string()); + return -1; + } + fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size()); + return NO_ERROR; +} diff --git a/cmds/incident_helper/src/parsers/SystemPropertiesParser.h b/cmds/incident_helper/src/parsers/SystemPropertiesParser.h new file mode 100644 index 000000000000..c4016006a48a --- /dev/null +++ b/cmds/incident_helper/src/parsers/SystemPropertiesParser.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef SYSTEM_PROPERTIES_PARSER_H +#define SYSTEM_PROPERTIES_PARSER_H + +#include "TextParserBase.h" + +using namespace android; + +/** + * SystemProperties parser, parses text produced by command getprop. + */ +class SystemPropertiesParser : public TextParserBase { +public: + SystemPropertiesParser() : TextParserBase(String8("SystemPropertiesParser")) {}; + ~SystemPropertiesParser() {}; + + virtual status_t Parse(const int in, const int out) const; +}; + +#endif // SYSTEM_PROPERTIES_PARSER_H diff --git a/cmds/incident_helper/testdata/system_properties.txt b/cmds/incident_helper/testdata/system_properties.txt new file mode 100644 index 000000000000..57c07ee9d75e --- /dev/null +++ b/cmds/incident_helper/testdata/system_properties.txt @@ -0,0 +1,14 @@ +[aaudio.hw_burst_min_usec]: [2000] +[aaudio.mmap_exclusive_policy]: [2] +[dalvik.vm.appimageformat]: [lz4] +[gsm.operator.isroaming]: [false] +[init.svc.vendor.imsqmidaemon]: [running] +[init.svc.vendor.init-radio-sh]: [stopped] +[net.dns1]: [2001:4860:4860::8844] +[net.tcp.buffersize.wifi]: [524288,2097152,4194304,262144,524288,1048576] +[nfc.initialized]: [True] +[persist_radio_VT_ENABLE]: [1] +[ro.boot.boottime]: [1BLL:85,1BLE:898,2BLL:0,2BLE:862,SW:6739,KL:340] +[ro.bootimage.build.date.utc]: [1509394807] +[ro.bootimage.build.fingerprint]: [google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys] +[ro.wifi.channels]: []
\ No newline at end of file diff --git a/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp b/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp new file mode 100644 index 000000000000..23e292a512b9 --- /dev/null +++ b/cmds/incident_helper/tests/SystemPropertiesParser_test.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 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. + */ + +#include "SystemPropertiesParser.h" + +#include "frameworks/base/core/proto/android/os/system_properties.pb.h" + +#include <android-base/file.h> +#include <android-base/test_utils.h> +#include <gmock/gmock.h> +#include <google/protobuf/message.h> +#include <gtest/gtest.h> +#include <string.h> +#include <fcntl.h> + +using namespace android::base; +using namespace android::os; +using namespace std; +using ::testing::StrEq; +using ::testing::Test; +using ::testing::internal::CaptureStderr; +using ::testing::internal::CaptureStdout; +using ::testing::internal::GetCapturedStderr; +using ::testing::internal::GetCapturedStdout; + +class SystemPropertiesParserTest : public Test { +public: + virtual void SetUp() override { + ASSERT_TRUE(tf.fd != -1); + } + + string getSerializedString(::google::protobuf::Message& message) { + string expectedStr; + message.SerializeToFileDescriptor(tf.fd); + ReadFileToString(tf.path, &expectedStr); + return expectedStr; + } + +protected: + TemporaryFile tf; + + const string kTestPath = GetExecutableDirectory(); + const string kTestDataPath = kTestPath + "/testdata/"; +}; + +TEST_F(SystemPropertiesParserTest, HasSwapInfo) { + const string testFile = kTestDataPath + "system_properties.txt"; + SystemPropertiesParser parser; + SystemPropertiesProto expected; + + expected.set_aaudio_hw_burst_min_usec(2000); + expected.set_aaudio_mmap_exclusive_policy(2); + expected.set_dalvik_vm_appimageformat("lz4"); + expected.set_gsm_operator_isroaming(false); + expected.set_init_svc_vendor_imsqmidaemon(SystemPropertiesProto_Status_STATUS_RUNNING); + expected.set_init_svc_vendor_init_radio_sh(SystemPropertiesProto_Status_STATUS_STOPPED); + expected.set_net_dns1("2001:4860:4860::8844"); + expected.add_net_tcp_buffersize_wifi(524288); + expected.add_net_tcp_buffersize_wifi(2097152); + expected.add_net_tcp_buffersize_wifi(4194304); + expected.add_net_tcp_buffersize_wifi(262144); + expected.add_net_tcp_buffersize_wifi(524288); + expected.add_net_tcp_buffersize_wifi(1048576); + expected.set_nfc_initialized(true); + expected.set_persist_radio_vt_enable(1); + expected.add_ro_boot_boottime("1BLL:85"); + expected.add_ro_boot_boottime("1BLE:898"); + expected.add_ro_boot_boottime("2BLL:0"); + expected.add_ro_boot_boottime("2BLE:862"); + expected.add_ro_boot_boottime("SW:6739"); + expected.add_ro_boot_boottime("KL:340"); + expected.set_ro_bootimage_build_date_utc(1509394807LL); + expected.set_ro_bootimage_build_fingerprint("google/marlin/marlin:P/MASTER/jinyithu10301320:eng/dev-keys"); + + int fd = open(testFile.c_str(), O_RDONLY); + ASSERT_TRUE(fd != -1); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO)); + EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected)); + close(fd); +} diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index 699de94a0240..77e8efafa2a0 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -101,6 +101,8 @@ public final class Sm { runSetEmulateFbe(); } else if ("get-fbe-mode".equals(op)) { runGetFbeMode(); + } else if ("idle-maint".equals(op)) { + runIdleMaint(); } else if ("fstrim".equals(op)) { runFstrim(); } else if ("set-virtual-disk".equals(op)) { @@ -278,6 +280,15 @@ public final class Sm { StorageManager.DEBUG_VIRTUAL_DISK); } + public void runIdleMaint() throws RemoteException { + final boolean im_run = "run".equals(nextArg()); + if (im_run) { + mSm.runIdleMaintenance(); + } else { + mSm.abortIdleMaintenance(); + } + } + private String nextArg() { if (mNextArg >= mArgs.length) { return null; @@ -300,6 +311,7 @@ public final class Sm { System.err.println(" sm unmount VOLUME"); System.err.println(" sm format VOLUME"); System.err.println(" sm benchmark VOLUME"); + System.err.println(" sm idle-maint [run|abort]"); System.err.println(" sm fstrim"); System.err.println(""); System.err.println(" sm forget [UUID|all]"); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 4ebca8430cf4..5fcb8a1a0892 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -20,7 +20,7 @@ cc_library_host_shared { name: "libstats_proto_host", srcs: [ - "src/stats_events.proto", + "src/atoms.proto", ], shared_libs: [ diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index 0f6d868708bd..d8603636c7c0 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -20,7 +20,7 @@ statsd_common_src := \ ../../core/java/android/os/IStatsManager.aidl \ src/stats_log.proto \ src/statsd_config.proto \ - src/stats_events_copy.proto \ + src/atoms_copy.proto \ src/anomaly/AnomalyMonitor.cpp \ src/condition/CombinationConditionTracker.cpp \ src/condition/condition_util.cpp \ diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 8c70bb502174..abd2a35d76df 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -50,8 +50,8 @@ const int FIELD_ID_UID = 1; const int FIELD_ID_NAME = 2; StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, - const std::function<void(const vector<uint8_t>&)>& pushLog) - : mUidMap(uidMap), mPushLog(pushLog) { + const std::function<void(const ConfigKey&)>& sendBroadcast) + : mUidMap(uidMap), mSendBroadcast(sendBroadcast) { } StatsLogProcessor::~StatsLogProcessor() { @@ -66,7 +66,7 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { } // Hard-coded logic to update the isolated uid's in the uid-map. - // The field numbers need to be currently updated by hand with stats_events.proto + // The field numbers need to be currently updated by hand with atoms.proto if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) { status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR; bool is_create = msg.GetBool(3, &err); @@ -102,12 +102,27 @@ void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig } } -vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) { +size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) { auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { ALOGW("Config source %s does not exist", key.ToString().c_str()); - return vector<uint8_t>(); + return 0; } + return it->second->byteSize(); +} + +void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) { + auto it = mMetricsManagers.find(key); + if (it == mMetricsManagers.end()) { + ALOGW("Config source %s does not exist", key.ToString().c_str()); + return; + } + + // This allows another broadcast to be sent within the rate-limit period if we get close to + // filling the buffer again soon. + mBroadcastTimesMutex.lock(); + mLastBroadcastTimes.erase(key); + mBroadcastTimesMutex.unlock(); ProtoOutputStream proto; @@ -131,17 +146,18 @@ vector<uint8_t> StatsLogProcessor::onDumpReport(const ConfigKey& key) { uidMap.SerializeToArray(&uidMapBuffer[0], uidMapSize); proto.write(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP, uidMapBuffer, uidMapSize); - vector<uint8_t> buffer(proto.size()); - size_t pos = 0; - auto iter = proto.data(); - while (iter.readBuffer() != NULL) { - size_t toRead = iter.currentToRead(); - std::memcpy(&buffer[pos], iter.readBuffer(), toRead); - pos += toRead; - iter.rp()->move(toRead); + if (outData != nullptr) { + outData->clear(); + outData->resize(proto.size()); + size_t pos = 0; + auto iter = proto.data(); + while (iter.readBuffer() != NULL) { + size_t toRead = iter.currentToRead(); + std::memcpy(&((*outData)[pos]), iter.readBuffer(), toRead); + pos += toRead; + iter.rp()->move(toRead); + } } - - return buffer; } void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { @@ -151,42 +167,34 @@ void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) { mMetricsManagers.erase(it); mUidMap->OnConfigRemoved(key); } - auto flushTime = mLastFlushTimes.find(key); - if (flushTime != mLastFlushTimes.end()) { - mLastFlushTimes.erase(flushTime); - } + + std::lock_guard<std::mutex> lock(mBroadcastTimesMutex); + mLastBroadcastTimes.erase(key); } void StatsLogProcessor::flushIfNecessary(uint64_t timestampNs, const ConfigKey& key, const unique_ptr<MetricsManager>& metricsManager) { - auto lastFlushNs = mLastFlushTimes.find(key); - if (lastFlushNs != mLastFlushTimes.end()) { - if (timestampNs - lastFlushNs->second < kMinFlushPeriod) { - return; - } - } + std::lock_guard<std::mutex> lock(mBroadcastTimesMutex); size_t totalBytes = metricsManager->byteSize(); - if (totalBytes > kMaxSerializedBytes) { - flush(); - mLastFlushTimes[key] = std::move(timestampNs); + if (totalBytes > .9 * kMaxSerializedBytes) { // Send broadcast so that receivers can pull data. + auto lastFlushNs = mLastBroadcastTimes.find(key); + if (lastFlushNs != mLastBroadcastTimes.end()) { + if (timestampNs - lastFlushNs->second < kMinBroadcastPeriod) { + return; + } + } + mLastBroadcastTimes[key] = timestampNs; + ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str()); + mSendBroadcast(key); + } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data. + // We ignore the return value so we force each metric producer to clear its contents. + metricsManager->onDumpReport(); + ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str()); } } -void StatsLogProcessor::flush() { - // TODO: Take ConfigKey as an argument and flush metrics related to the - // ConfigKey. Also, create a wrapper that holds a repeated field of - // StatsLogReport's. - /* - StatsLogReport logReport; - const int numBytes = logReport.ByteSize(); - vector<uint8_t> logReportBuffer(numBytes); - logReport.SerializeToArray(&logReportBuffer[0], numBytes); - mPushLog(logReportBuffer); - */ -} - } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index f38d71550eda..2091774b108e 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -33,7 +33,7 @@ namespace statsd { class StatsLogProcessor : public ConfigListener { public: StatsLogProcessor(const sp<UidMap>& uidMap, - const std::function<void(const vector<uint8_t>&)>& pushLog); + const std::function<void(const ConfigKey&)>& sendBroadcast); virtual ~StatsLogProcessor(); virtual void OnLogEvent(const LogEvent& event); @@ -41,15 +41,16 @@ public: void OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config); void OnConfigRemoved(const ConfigKey& key); - vector<uint8_t> onDumpReport(const ConfigKey& key); + size_t GetMetricsSize(const ConfigKey& key); - /* Request a flush through a binder call. */ - void flush(); + void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData); private: + mutable mutex mBroadcastTimesMutex; + std::unordered_map<ConfigKey, std::unique_ptr<MetricsManager>> mMetricsManagers; - std::unordered_map<ConfigKey, long> mLastFlushTimes; + std::unordered_map<ConfigKey, long> mLastBroadcastTimes; sp<UidMap> mUidMap; // Reference to the UidMap to lookup app name and version for each uid. @@ -60,17 +61,18 @@ private: */ static const size_t kMaxSerializedBytes = 16 * 1024; - /* Check if the buffer size exceeds the max buffer size when the new entry is added, and flush - the logs to callback clients if true. */ + /* Check if we should send a broadcast if approaching memory limits and if we're over, we + * actually delete the data. */ void flushIfNecessary(uint64_t timestampNs, const ConfigKey& key, const unique_ptr<MetricsManager>& metricsManager); - std::function<void(const vector<uint8_t>&)> mPushLog; + // Function used to send a broadcast so that receiver for the config key can call getData + // to retrieve the stored data. + std::function<void(const ConfigKey& key)> mSendBroadcast; - /* Minimum period between two flushes in nanoseconds. Currently set to 10 - * minutes. */ - static const unsigned long long kMinFlushPeriod = 600 * NS_PER_SEC; + /* Minimum period between two broadcasts in nanoseconds. Currently set to 60 seconds. */ + static const unsigned long long kMinBroadcastPeriod = 60 * NS_PER_SEC; }; } // namespace statsd diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 11c5de172721..ef01ec7563f5 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 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. @@ -17,17 +17,20 @@ #define DEBUG true #include "Log.h" +#include "android-base/stringprintf.h" #include "StatsService.h" +#include "config/ConfigKey.h" +#include "config/ConfigManager.h" #include "storage/DropboxReader.h" #include <android-base/file.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> +#include <dirent.h> #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h> #include <private/android_filesystem_config.h> #include <utils/Looper.h> #include <utils/String16.h> - #include <stdio.h> #include <stdlib.h> #include <sys/system_properties.h> @@ -39,6 +42,9 @@ namespace android { namespace os { namespace statsd { +constexpr const char* kPermissionDump = "android.permission.DUMP"; +#define STATS_SERVICE_DIR "/data/system/stats-service" + // ====================================================================== /** * Watches for the death of the stats companion (system process). @@ -67,8 +73,18 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) { mUidMap = new UidMap(); mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor(mUidMap, [this](const vector<uint8_t>& log) { - pushLog(log); + mProcessor = new StatsLogProcessor(mUidMap, [this](const ConfigKey& key) { + auto sc = getStatsCompanionService(); + auto receiver = mConfigManager->GetConfigReceiver(key); + if (sc == nullptr) { + ALOGD("Could not find StatsCompanionService"); + } else if (receiver.first.size() == 0) { + ALOGD("Statscompanion could not find a broadcast receiver for %s", + key.ToString().c_str()); + } else { + sc->sendBroadcast(String16(receiver.first.c_str()), + String16(receiver.second.c_str())); + } }); mConfigManager->AddListener(mProcessor); @@ -198,6 +214,18 @@ status_t StatsService::command(FILE* in, FILE* out, FILE* err, Vector<String8>& if (!args[0].compare(String8("pull-source")) && args.size() > 1) { return cmd_print_pulled_metrics(out, args); } + + if (!args[0].compare(String8("send-broadcast"))) { + return cmd_trigger_broadcast(out, args); + } + + if (!args[0].compare(String8("print-stats"))) { + return cmd_print_stats(out); + } + + if (!args[0].compare(String8("clear-config"))) { + return cmd_remove_config_files(out); + } } print_cmd_help(out); @@ -215,7 +243,12 @@ void StatsService::print_cmd_help(FILE* out) { fprintf(out, " Prints the UID, app name, version mapping.\n"); fprintf(out, "\n"); fprintf(out, "\n"); - fprintf(out, "usage: adb shell cmds stats pull-source [int] \n"); + fprintf(out, "usage: adb shell cmd stats clear-config \n"); + fprintf(out, "\n"); + fprintf(out, " Removes all configs from disk.\n"); + fprintf(out, "\n"); + fprintf(out, "\n"); + fprintf(out, "usage: adb shell cmd stats pull-source [int] \n"); fprintf(out, "\n"); fprintf(out, " Prints the output of a pulled metrics source (int indicates source)\n"); fprintf(out, "\n"); @@ -238,6 +271,65 @@ void StatsService::print_cmd_help(FILE* out) { fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); fprintf(out, " calling uid is used.\n"); fprintf(out, " NAME The name of the configuration\n"); + fprintf(out, "\n"); + fprintf(out, "\n"); + fprintf(out, "usage: adb shell cmd stats send-broadcast [UID] NAME\n"); + fprintf(out, " Send a broadcast that triggers the subscriber to fetch metrics.\n"); + fprintf(out, " UID The uid of the configuration. It is only possible to pass\n"); + fprintf(out, " the UID parameter on eng builds. If UID is omitted the\n"); + fprintf(out, " calling uid is used.\n"); + fprintf(out, " NAME The name of the configuration\n"); + fprintf(out, "\n"); + fprintf(out, "\n"); + fprintf(out, "usage: adb shell cmd stats print-stats\n"); + fprintf(out, " Prints some basic stats.\n"); +} + +status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) { + string name; + bool good = false; + int uid; + const int argCount = args.size(); + if (argCount == 2) { + // Automatically pick the UID + uid = IPCThreadState::self()->getCallingUid(); + // TODO: What if this isn't a binder call? Should we fail? + name.assign(args[1].c_str(), args[1].size()); + good = true; + } else if (argCount == 3) { + // If it's a userdebug or eng build, then the shell user can + // impersonate other uids. + if (mEngBuild) { + const char* s = args[1].c_str(); + if (*s != '\0') { + char* end = NULL; + uid = strtol(s, &end, 0); + if (*end == '\0') { + name.assign(args[2].c_str(), args[2].size()); + good = true; + } + } + } else { + fprintf(out, + "The metrics can only be dumped for other UIDs on eng or userdebug " + "builds.\n"); + } + } + if (!good) { + print_cmd_help(out); + return UNKNOWN_ERROR; + } + auto receiver = mConfigManager->GetConfigReceiver(ConfigKey(uid, name)); + sp<IStatsCompanionService> sc = getStatsCompanionService(); + if (sc != nullptr) { + sc->sendBroadcast(String16(receiver.first.c_str()), String16(receiver.second.c_str())); + ALOGD("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(), + args[2].c_str()); + } else { + ALOGD("Could not access statsCompanion"); + } + + return NO_ERROR; } status_t StatsService::cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args) { @@ -341,7 +433,8 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String } } if (good) { - mProcessor->onDumpReport(ConfigKey(uid, name)); + vector<uint8_t> data; + mProcessor->onDumpReport(ConfigKey(uid, name), &data); // TODO: print the returned StatsLogReport to file instead of printing to logcat. fprintf(out, "Dump report for Config [%d,%s]\n", uid, name.c_str()); fprintf(out, "See the StatsLogReport in logcat...\n"); @@ -357,6 +450,15 @@ status_t StatsService::cmd_dump_report(FILE* out, FILE* err, const Vector<String } } +status_t StatsService::cmd_print_stats(FILE* out) { + vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys(); + for (const ConfigKey& key : configs) { + fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(), + mProcessor->GetMetricsSize(key)); + } + return NO_ERROR; +} + status_t StatsService::cmd_print_stats_log(FILE* out, const Vector<String8>& args) { long msec = 0; @@ -384,6 +486,27 @@ status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8> return UNKNOWN_ERROR; } +status_t StatsService::cmd_remove_config_files(FILE* out) { + fprintf(out, "Trying to remove config files...\n"); + unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir); + if (dir == NULL) { + fprintf(out, "No existing config files found exiting...\n"); + return NO_ERROR; + } + + dirent* de; + while ((de = readdir(dir.get()))) { + char* name = de->d_name; + if (name[0] == '.') continue; + string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name); + fprintf(out, "Deleting file %s\n", file_name.c_str()); + if (remove(file_name.c_str())) { + fprintf(out, "Error deleting file %s\n", file_name.c_str()); + } + } + return NO_ERROR; +} + Status StatsService::informAllUidData(const vector<int32_t>& uid, const vector<int32_t>& version, const vector<String16>& app) { if (DEBUG) ALOGD("StatsService::informAllUidData was called"); @@ -520,32 +643,53 @@ void StatsService::OnLogEvent(const LogEvent& event) { mProcessor->OnLogEvent(event); } -Status StatsService::requestPush() { - mProcessor->flush(); - return Status::ok(); +Status StatsService::getData(const String16& key, vector <uint8_t>* output) { + IPCThreadState* ipc = IPCThreadState::self(); + ALOGD("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), + ipc->getCallingUid()); + if (checkCallingPermission(String16(kPermissionDump))) { + string keyStr = string(String8(key).string()); + ConfigKey configKey(ipc->getCallingUid(), keyStr); + mProcessor->onDumpReport(configKey, output); + return Status::ok(); + } else { + return Status::fromExceptionCode(binder::Status::EX_SECURITY); + } } -Status StatsService::pushLog(const vector<uint8_t>& log) { - std::lock_guard<std::mutex> lock(mLock); - for (size_t i = 0; i < mCallbacks.size(); i++) { - mCallbacks[i]->onReceiveLogs((vector<uint8_t>*)&log); +Status StatsService::addConfiguration(const String16& key, + const vector <uint8_t>& config, + const String16& package, const String16& cls, + bool* success) { + IPCThreadState* ipc = IPCThreadState::self(); + if (checkCallingPermission(String16(kPermissionDump))) { + string keyString = string(String8(key).string()); + ConfigKey configKey(ipc->getCallingUid(), keyString); + StatsdConfig cfg; + cfg.ParseFromArray(&config[0], config.size()); + mConfigManager->UpdateConfig(configKey, cfg); + mConfigManager->SetConfigReceiver(configKey, string(String8(package).string()), + string(String8(cls).string())); + *success = true; + return Status::ok(); + } else { + return Status::fromExceptionCode(binder::Status::EX_SECURITY); } - return Status::ok(); } -Status StatsService::subscribeStatsLog(const sp<IStatsCallbacks>& callback) { - std::lock_guard<std::mutex> lock(mLock); - for (size_t i = 0; i < mCallbacks.size(); i++) { - if (mCallbacks[i] == callback) { - return Status::fromStatusT(-errno); - } +Status StatsService::removeConfiguration(const String16& key, bool* success) { + IPCThreadState* ipc = IPCThreadState::self(); + if (checkCallingPermission(String16(kPermissionDump))) { + string keyStr = string(String8(key).string()); + mConfigManager->RemoveConfig(ConfigKey(ipc->getCallingUid(), keyStr)); + return Status::ok(); + } else { + *success = false; + return Status::fromExceptionCode(binder::Status::EX_SECURITY); } - mCallbacks.add(callback); - IInterface::asBinder(callback)->linkToDeath(this); - return Status::ok(); } -void StatsService::binderDied(const wp<IBinder>& who) { +void StatsService::binderDied(const wp <IBinder>& who) { for (size_t i = 0; i < mCallbacks.size(); i++) { if (IInterface::asBinder(mCallbacks[i]) == who) { mCallbacks.removeAt(i); diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h index fa92f65d264d..888f97b6e954 100644 --- a/cmds/statsd/src/StatsService.h +++ b/cmds/statsd/src/StatsService.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 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. @@ -71,20 +71,22 @@ public: virtual void OnLogEvent(const LogEvent& event); /** - * Binder call to force trigger pushLog. This would be called by callback - * clients. + * Binder call for clients to request data for this configuration key. */ - virtual Status requestPush() override; + virtual Status getData(const String16& key, vector<uint8_t>* output) override; /** - * Pushes stats log entries from statsd to callback clients. + * Binder call to let clients send a configuration and indicate they're interested when they + * should requestData for this configuration. */ - Status pushLog(const vector<uint8_t>& log); + virtual Status addConfiguration(const String16& key, const vector <uint8_t>& config, + const String16& package, const String16& cls, bool* success) + override; /** - * Binder call to listen to statsd to send stats log entries. + * Binder call to allow clients to remove the specified configuration. */ - virtual Status subscribeStatsLog(const sp<IStatsCallbacks>& callbacks) override; + virtual Status removeConfiguration(const String16& key, bool* success) override; // TODO: public for testing since statsd doesn't run when system starts. Change to private // later. @@ -120,11 +122,21 @@ private: void print_cmd_help(FILE* out); /** + * Trigger a broadcast. + */ + status_t cmd_trigger_broadcast(FILE* out, Vector<String8>& args); + + /** * Handle the config sub-command. */ status_t cmd_config(FILE* in, FILE* out, FILE* err, Vector<String8>& args); /** + * Prints some basic stats to std out. + */ + status_t cmd_print_stats(FILE* out); + + /** * Print the event log. */ status_t cmd_print_stats_log(FILE* out, const Vector<String8>& args); @@ -145,6 +157,11 @@ private: status_t cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args); /** + * Removes all configs stored on disk. + */ + status_t cmd_remove_config_files(FILE* out); + + /** * Update a configuration. */ void set_config(int uid, const string& name, const StatsdConfig& config); diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/atoms.proto index 9ab07de96aa2..ba93febe8ad4 100644 --- a/cmds/statsd/src/stats_events.proto +++ b/cmds/statsd/src/atoms.proto @@ -19,10 +19,10 @@ syntax = "proto2"; // TODO: Not the right package and class name package android.os.statsd; option java_package = "com.android.os"; -option java_outer_classname = "StatsEventProto"; +option java_outer_classname = "AtomsProto"; /** - * The master event class. This message defines all of the available + * The master atom class. This message defines all of the available * raw stats log events from the Android system, also known as "atoms." * * This field contains a single oneof with all of the available messages. @@ -30,12 +30,12 @@ option java_outer_classname = "StatsEventProto"; * generates the android.util.StatsLog class, which contains the constants * and methods that Android uses to log. * - * This StatsEvent class is not actually built into the Android system. + * This Atom class is not actually built into the Android system. * Instead, statsd on Android constructs these messages synthetically, * in the format defined here and in stats_log.proto. */ -message StatsEvent { - // Pushed events start at 2. +message Atom { + // Pushed atoms start at 2. oneof pushed { // For StatsLog reasons, 1 is illegal and will not work. Must start at 2. BleScanStateChanged ble_scan_state_changed = 2; @@ -102,8 +102,7 @@ message WorkSource { /* * ***************************************************************************** - * Below are all of the individual atoms that are logged by Android via statsd - * and Westworld. + * Below are all of the individual atoms that are logged by Android via statsd. * * RULES: * - The field ids for each atom must start at 1, and count upwards by 1. @@ -114,7 +113,7 @@ message WorkSource { * - The types must be built-in protocol buffer types, namely, no sub-messages * are allowed (yet). The bytes type is also not allowed. * - The CamelCase name of the message type should match the - * underscore_separated name as defined in StatsEvent. + * underscore_separated name as defined in Atom. * - If an atom represents work that can be attributed to an app, there can * be exactly one WorkSource field. It must be field number 1. * - A field that is a uid should be a string field, tagged with the [xxx] @@ -397,7 +396,7 @@ message WakelockStateChanged { FULL = 1; WINDOW = 2; } - optional int32 type = 2; + optional Type type = 2; // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). optional string tag = 3; @@ -425,7 +424,7 @@ message UidWakelockStateChanged { FULL = 1; WINDOW = 2; } - optional int32 type = 2; + optional Type type = 2; enum State { OFF = 0; @@ -852,7 +851,6 @@ message PowerStateSubsystemSleepStatePulled { } /** -<<<<<<< HEAD * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky * behavior in its own uid. However, the metrics of these isolated uid's almost always should be * attributed back to the parent (host) uid. One example is Chrome. @@ -872,7 +870,6 @@ message IsolatedUidChanged { } /* -<<<<<<< HEAD * Pulls Cpu time per frequency. * Note: this should be pulled for gauge metric only, without condition. * The puller keeps internal state of last values. It should not be pulled by diff --git a/cmds/statsd/src/atoms_copy.proto b/cmds/statsd/src/atoms_copy.proto new file mode 100644 index 000000000000..58e225a317f0 --- /dev/null +++ b/cmds/statsd/src/atoms_copy.proto @@ -0,0 +1,910 @@ +/* + * Copyright (C) 2017 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. + */ + +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; + + +// TODO: Not the right package and class name +package android.os.statsd; +option java_package = "com.android.os"; +option java_outer_classname = "AtomsProto"; + +/** + * The master atom class. This message defines all of the available + * raw stats log events from the Android system, also known as "atoms." + * + * This field contains a single oneof with all of the available messages. + * The stats-log-api-gen tool runs as part of the Android build and + * generates the android.util.StatsLog class, which contains the constants + * and methods that Android uses to log. + * + * This Atom class is not actually built into the Android system. + * Instead, statsd on Android constructs these messages synthetically, + * in the format defined here and in stats_log.proto. + */ +message Atom { + // Pushed atoms start at 2. + oneof pushed { + // For StatsLog reasons, 1 is illegal and will not work. Must start at 2. + BleScanStateChanged ble_scan_state_changed = 2; + BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3; + BleScanResultReceived ble_scan_result_received = 4; + SensorStateChanged sensor_state_changed = 5; + GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested + SyncStateChanged sync_state_changed = 7; + ScheduledJobStateChanged scheduled_job_state_changed = 8; + ScreenBrightnessChanged screen_brightness_changed = 9; + // 10-20 are temporarily reserved for wakelocks etc. + WakelockStateChanged wakelock_state_changed = 10; + UidWakelockStateChanged uid_wakelock_state_changed = 11; + LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 12; + BatterySaverModeStateChanged battery_saver_mode_state_changed = 21; + DeviceIdleModeStateChanged device_idle_mode_state_changed = 22; + AudioStateChanged audio_state_changed = 23; + MediaCodecActivityChanged media_codec_activity_changed = 24; + CameraStateChanged camera_state_changed = 25; + FlashlightStateChanged flashlight_state_changed = 26; + UidProcessStateChanged uid_process_state_changed = 27; + ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28; + ScreenStateChanged screen_state_changed = 29; + BatteryLevelChanged battery_level_changed = 30; + ChargingStateChanged charging_state_changed = 31; + PluggedStateChanged plugged_state_changed = 32; + DeviceTemperatureReported device_temperature_reported = 33; + DeviceOnStatusChanged device_on_status_changed = 34; + WakeupAlarmOccurred wakeup_alarm_occurred = 35; + KernelWakeupReported kernel_wakeup_reported = 36; + WifiLockStateChanged wifi_lock_state_changed = 37; + WifiSignalStrengthChanged wifi_signal_strength_changed = 38; + WifiScanStateChanged wifi_scan_state_changed = 39; + PhoneSignalStrengthChanged phone_signal_strength_changed = 40; + SettingChanged setting_changed = 41; + ActivityForegroundStateChanged activity_foreground_state_changed = 42; + IsolatedUidChanged isolated_uid_changed = 43; + // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. + } + + // Pulled events will start at field 1000. + oneof pulled { + WifiBytesTransferred wifi_bytes_transferred = 1000; + WifiBytesTransferredByFgBg wifi_bytes_transferred_by_fg_bg = 1001; + MobileBytesTransferred mobile_bytes_transferred = 1002; + MobileBytesTransferredByFgBg mobile_bytes_transferred_by_fg_bg = 1003; + KernelWakelockPulled kernel_wakelock_pulled = 1004; + PowerStatePlatformSleepStatePulled power_state_platform_sleep_state_pulled = 1005; + PowerStateVoterPulled power_state_voter_pulled = 1006; + PowerStateSubsystemSleepStatePulled power_state_subsystem_sleep_state_pulled = 1007; + CpuTimePerFreqPulled cpu_time_per_freq_pulled = 1008; + CpuTimePerUidPulled cpu_time_per_uid_pulled = 1009; + CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010; + } +} + +/** + * A WorkSource represents the chained attribution of applications that + * resulted in a particular bit of work being done. + */ +message WorkSource { + // TODO +} + +/* + * ***************************************************************************** + * Below are all of the individual atoms that are logged by Android via statsd. + * + * RULES: + * - The field ids for each atom must start at 1, and count upwards by 1. + * Skipping field ids is not allowed. + * - These form an API, so renaming, renumbering or removing fields is + * not allowed between android releases. (This is not currently enforced, + * but there will be a tool to enforce this restriction). + * - The types must be built-in protocol buffer types, namely, no sub-messages + * are allowed (yet). The bytes type is also not allowed. + * - The CamelCase name of the message type should match the + * underscore_separated name as defined in Atom. + * - If an atom represents work that can be attributed to an app, there can + * be exactly one WorkSource field. It must be field number 1. + * - A field that is a uid should be a string field, tagged with the [xxx] + * annotation. The generated code on android will be represented by UIDs, + * and those UIDs will be translated in xxx to those strings. + * + * CONVENTIONS: + * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange. + * - If there is a UID, it goes first. Think in an object-oriented fashion. + * ***************************************************************************** + */ + +/** + * Logs when the screen state changes. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message ScreenStateChanged { + // TODO: Use the real screen state. + enum State { + STATE_UNKNOWN = 0; + STATE_OFF = 1; + STATE_ON = 2; + STATE_DOZE = 3; + STATE_DOZE_SUSPEND = 4; + STATE_VR = 5; + } + // New screen state. + optional State display_state = 1; +} + +/** + * Logs that the state of a process state, as per the activity manager, has changed. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message UidProcessStateChanged { + optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation + + // The state. + // TODO: Use the real (mapped) process states. + optional int32 state = 2; +} + +/** + * Logs that a process started, finished, crashed, or ANRed. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message ProcessLifeCycleStateChanged { + // TODO: Use the real (mapped) process states. + optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation + + // TODO: What is this? + optional string name = 2; + + // The state. + // TODO: Use an enum. + optional int32 event = 3; +} + + + +/** + * Logs when the ble scan state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message BleScanStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs when an unoptimized ble scan state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats). +message BleUnoptimizedScanStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs reporting of a ble scan finding results. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats). +message BleScanResultReceived { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Number of ble scan results returned. + optional int32 num_of_results = 2; +} + +/** + * Logs when a sensor state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message SensorStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // TODO: Is there a way to get the actual name of the sensor? + // The id (int) of the sensor. + optional int32 sensor_id = 2; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 3; +} + + +/** + * Logs when GPS state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message GpsScanStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + + +/** + * Logs when a sync manager sync state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message SyncStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Name of the sync (as named in the app) + optional string name = 2; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 3; +} + +/** + * Logs when a job scheduler job state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message ScheduledJobStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Name of the job (as named in the app) + optional string name = 2; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 3; + + // TODO: Consider adding the stopReason (int) +} + +/** + * Logs when the audio state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message AudioStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs when the video codec state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message MediaCodecActivityChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs when the flashlight state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message FlashlightStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs when the camera state changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message CameraStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs that the state of a wakelock (per app and per wakelock name) has changed. + * + * Logged from: + * TODO + */ +message WakelockStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Type of wakelock. + enum Type { + PARTIAL = 0; + FULL = 1; + WINDOW = 2; + } + optional Type type = 2; + + // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). + optional string tag = 3; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 4; +} + +/** + * Logs when an app is holding a wakelock, regardless of the wakelock's name. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message UidWakelockStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + // Type of wakelock. + enum Type { + PARTIAL = 0; + FULL = 1; + WINDOW = 2; + } + optional Type type = 2; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 3; +} + +/** + * Logs when a partial wakelock is considered 'long' (over 1 min). + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message LongPartialWakelockStateChanged { + // TODO: Add attribution instead of uid? + optional int32 uid = 1; + + // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). + optional string tag = 2; + + // TODO: I have no idea what this is. + optional string history_tag = 3; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 4; +} + +/** + * Logs Battery Saver state change. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message BatterySaverModeStateChanged { + enum State { + OFF = 0; + ON = 1; + } + optional State state = 1; +} + +/** + * Logs Doze mode state change. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message DeviceIdleModeStateChanged { + // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_. + optional int32 state = 1; +} + +/** + * Logs screen brightness level. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message ScreenBrightnessChanged { + // Screen brightness level. Should be in [-1, 255] according to PowerManager.java. + optional int32 level = 1; +} + +/** + * Logs battery level (percent full, from 0 to 100). + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message BatteryLevelChanged { + // Battery level. Should be in [0, 100]. + optional int32 battery_level = 1; +} + +/** + * Logs change in charging status of the device. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message ChargingStateChanged { + // TODO: Link directly to BatteryManager.java's constants (via a proto). + enum State { + BATTERY_STATUS_UNKNOWN = 1; + BATTERY_STATUS_CHARGING = 2; + BATTERY_STATUS_DISCHARGING = 3; + BATTERY_STATUS_NOT_CHARGING = 4; + BATTERY_STATUS_FULL = 5; + } + optional State charging_state = 1; +} + +/** + * Logs whether the device is plugged in, and what power source it is using. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message PluggedStateChanged { + // TODO: Link directly to BatteryManager.java's constants (via a proto). + enum State { + // Note that NONE is not in BatteryManager.java's constants. + BATTERY_PLUGGED_NONE = 0; + // Power source is an AC charger. + BATTERY_PLUGGED_AC = 1; + // Power source is a USB port. + BATTERY_PLUGGED_USB = 2; + // Power source is wireless. + BATTERY_PLUGGED_WIRELESS = 4; + } + optional State plugged_state = 1; +} + +/** + * Logs the temperature of the device, in tenths of a degree Celsius. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message DeviceTemperatureReported { + // Temperature in tenths of a degree C. + optional int32 temperature = 1; +} + +// TODO: Define this more precisely. +// TODO: Log the ON state somewhere. It isn't currently logged anywhere. +/** + * Logs when the device turns off or on. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message DeviceOnStatusChanged { + enum State { + OFF = 0; + ON = 1; + } + optional State state = 1; +} + +/** + * Logs when an app's wakeup alarm fires. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java + */ +message WakeupAlarmOccurred { + // TODO: Add attribution instead of uid? + optional int32 uid = 1; +} + +/** + * Logs kernel wakeup reasons and aborts. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java + */ +message KernelWakeupReported { + // Name of the kernel wakeup reason (or abort). + optional string wakeup_reason_name = 1; + + // Duration (in microseconds) for the wake-up interrupt to be serviced. + optional int64 duration_usec = 2; +} + +/** + * Logs wifi locks held by an app. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message WifiLockStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs wifi signal strength changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message WifiSignalStrengthChanged { + // TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states. + enum SignalStrength { + SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; + SIGNAL_STRENGTH_POOR = 1; + SIGNAL_STRENGTH_MODERATE = 2; + SIGNAL_STRENGTH_GOOD = 3; + SIGNAL_STRENGTH_GREAT = 4; + } + optional SignalStrength signal_strength = 1; +} + +/** + * Logs wifi scans performed by an app. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message WifiScanStateChanged { + // TODO: Add attribution instead of uid. + optional int32 uid = 1; + + enum State { + OFF = 0; + ON = 1; + } + optional State state = 2; +} + +/** + * Logs phone signal strength changes. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message PhoneSignalStrengthChanged { + // TODO: Reference the actual telephony/java/android/telephony/SignalStrength.java states. + enum SignalStrength { + SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; + SIGNAL_STRENGTH_POOR = 1; + SIGNAL_STRENGTH_MODERATE = 2; + SIGNAL_STRENGTH_GOOD = 3; + SIGNAL_STRENGTH_GREAT = 4; + } + optional SignalStrength signal_strength = 1; +} + +/** + * Logs that a setting was updated. + * Logged from: + * frameworks/base/core/java/android/provider/Settings.java + * The tag and is_default allow resetting of settings to default values based on the specified + * tag. See Settings#putString(ContentResolver, String, String, String, boolean) for more details. + */ +message SettingChanged { + // The name of the setting. + optional string setting = 1; + + // The change being imposed on this setting. May represent a number, eg "3". + optional string value = 2; + + // The new value of this setting. For most settings, this is same as value. For some settings, + // value is +X or -X where X represents an element in a set. For example, if the previous value + // is A,B,C and value is -B, then new_value is A,C and prev_value is A,B,C. + // The +/- feature is currently only used for location_providers_allowed. + optional string new_value = 3; + + // The previous value of this setting. + optional string prev_value = 4; + + // The tag used with the is_default for resetting sets of settings. This is generally null. + optional string tag = 5; + + // 1 indicates that this setting with tag should be resettable. + optional int32 is_default = 6; + + // The user ID associated. Defined in android/os/UserHandle.java + optional int32 user = 7; +} + +/* + * Logs activity going to foreground or background + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java + */ +message ActivityForegroundStateChanged { + enum Activity { + MOVE_TO_BACKGROUND = 0; + MOVE_TO_FOREGROUND = 1; + } + optional int32 uid = 1; + optional string pkg_name = 2; + optional string class_name = 3; + optional Activity activity = 4; +} + +/** + * Pulls bytes transferred via wifi (Sum of foreground and background usage). + * + * Pulled from: + * StatsCompanionService (using BatteryStats to get which interfaces are wifi) + */ +message WifiBytesTransferred { + optional int32 uid = 1; + + optional int64 rx_bytes = 2; + + optional int64 rx_packets = 3; + + optional int64 tx_bytes = 4; + + optional int64 tx_packets = 5; +} + +/** + * Pulls bytes transferred via wifi (separated by foreground and background usage). + * + * Pulled from: + * StatsCompanionService (using BatteryStats to get which interfaces are wifi) + */ +message WifiBytesTransferredByFgBg { + optional int32 uid = 1; + + // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats. + optional int32 is_foreground = 2; + + optional int64 rx_bytes = 3; + + optional int64 rx_packets = 4; + + optional int64 tx_bytes = 5; + + optional int64 tx_packets = 6; +} + +/** + * Pulls bytes transferred via mobile networks (Sum of foreground and background usage). + * + * Pulled from: + * StatsCompanionService (using BatteryStats to get which interfaces are mobile data) + */ +message MobileBytesTransferred { + optional int32 uid = 1; + + optional int64 rx_bytes = 2; + + optional int64 rx_packets = 3; + + optional int64 tx_bytes = 4; + + optional int64 tx_packets = 5; +} + +/** + * Pulls bytes transferred via mobile networks (separated by foreground and background usage). + * + * Pulled from: + * StatsCompanionService (using BatteryStats to get which interfaces are mobile data) + */ +message MobileBytesTransferredByFgBg { + optional int32 uid = 1; + + // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats. + optional int32 is_foreground = 2; + + optional int64 rx_bytes = 3; + + optional int64 rx_packets = 4; + + optional int64 tx_bytes = 5; + + optional int64 tx_packets = 6; +} + +/** + * Pulls the kernel wakelock durations. This atom is adapted from + * android/internal/os/KernelWakelockStats.java + * + * Pulled from: + * StatsCompanionService using KernelWakelockReader. + */ +message KernelWakelockPulled { + optional string name = 1; + + optional int32 count = 2; + + optional int32 version = 3; + + optional int64 time = 4; +} + +/* + * Pulls PowerStatePlatformSleepState. + * + * Definition here: + * hardware/interfaces/power/1.0/types.hal + */ +message PowerStatePlatformSleepStatePulled { + optional string name = 1; + optional uint64 residency_in_msec_since_boot = 2; + optional uint64 total_transitions = 3; + optional bool supported_only_in_suspend = 4; +} + +/** + * Pulls PowerStateVoter. + * + * Definition here: + * hardware/interfaces/power/1.0/types.hal + */ +message PowerStateVoterPulled { + optional string power_state_platform_sleep_state_name = 1; + optional string power_state_voter_name = 2; + optional uint64 total_time_in_msec_voted_for_since_boot = 3; + optional uint64 total_number_of_times_voted_since_boot = 4; +} + +/** + * Pulls PowerStateSubsystemSleepState. + * + * Definition here: + * hardware/interfaces/power/1.1/types.hal + */ +message PowerStateSubsystemSleepStatePulled { + optional string power_state_subsystem_name = 1; + optional string power_state_subsystem_sleep_state_name = 2; + optional uint64 residency_in_msec_since_boot = 3; + optional uint64 total_transitions = 4; + optional uint64 last_entry_timestamp_ms = 5; + optional bool supported_only_in_suspend = 6; +} + +/** + * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky + * behavior in its own uid. However, the metrics of these isolated uid's almost always should be + * attributed back to the parent (host) uid. One example is Chrome. + * + * Logged from: + * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java + */ +message IsolatedUidChanged { + // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid. + optional int32 parent_uid = 1; + + optional int32 isolated_uid = 2; + + // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to + // be removed before if it's used for another parent uid. + optional int32 is_create = 3; +} + +/* + * Pulls Cpu time per frequency. + * Note: this should be pulled for gauge metric only, without condition. + * The puller keeps internal state of last values. It should not be pulled by + * different metrics. + * The pulled data is delta of cpu time from last pull, calculated as + * following: + * if current time is larger than last value, take delta between the two. + * if current time is smaller than last value, there must be a cpu + * hotplug event, and the current time is taken as delta. + */ +message CpuTimePerFreqPulled { + optional uint32 cluster = 1; + optional uint32 freq_index = 2; + optional uint64 time = 3; +} + +/* + * Pulls Cpu Time Per Uid. + * Note that isolated process uid time should be attributed to host uids. + */ +message CpuTimePerUidPulled { + optional uint64 uid = 1; + optional uint64 user_time_ms = 2; + optional uint64 sys_time_ms = 3; +} + +/** + * Pulls Cpu Time Per Uid per frequency. + * Note that isolated process uid time should be attributed to host uids. + * For each uid, we order the time by descending frequencies. + */ +message CpuTimePerUidFreqPulled { + optional uint64 uid = 1; + optional uint64 freq_idx = 2; + optional uint64 time_ms = 3; +} diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index a694dbf754ef..60060fe60fbd 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -206,7 +206,7 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event, vector<bool>& conditionChangedCache) { if (conditionCache[mIndex] != ConditionState::kNotEvaluated) { // it has been evaluated. - VLOG("Yes, already evaluated, %s %d", mName.c_str(), mNonSlicedConditionState); + VLOG("Yes, already evaluated, %s %d", mName.c_str(), conditionCache[mIndex]); return; } @@ -230,8 +230,23 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event, } if (matchedState < 0) { + // The event doesn't match this condition. So we just report existing condition values. conditionChangedCache[mIndex] = false; - conditionCache[mIndex] = mNonSlicedConditionState; + if (mSliced) { + // if the condition result is sliced. metrics won't directly get value from the + // cache, so just set any value other than kNotEvaluated. + conditionCache[mIndex] = ConditionState::kUnknown; + } else if (mSlicedConditionState.find(DEFAULT_DIMENSION_KEY) == + mSlicedConditionState.end()) { + // condition not sliced, but we haven't seen the matched start or stop yet. so return + // initial value. + conditionCache[mIndex] = mInitialValue; + } else { + // return the cached condition. + conditionCache[mIndex] = mSlicedConditionState[DEFAULT_DIMENSION_KEY] > 0 + ? ConditionState::kTrue + : ConditionState::kFalse; + } return; } diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp index 2618a217c378..669a4b77f3b4 100644 --- a/cmds/statsd/src/condition/condition_util.cpp +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -86,6 +86,9 @@ ConditionState evaluateCombinationCondition(const std::vector<int>& children, case LogicalOperation::NOR: newCondition = hasTrue ? ConditionState::kFalse : ConditionState::kTrue; break; + case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED: + newCondition = ConditionState::kFalse; + break; } return newCondition; } diff --git a/cmds/statsd/src/config/ConfigKey.h b/cmds/statsd/src/config/ConfigKey.h index bbf20fd1acf7..3489c43c8052 100644 --- a/cmds/statsd/src/config/ConfigKey.h +++ b/cmds/statsd/src/config/ConfigKey.h @@ -78,7 +78,7 @@ inline ostream& operator<<(ostream& os, const ConfigKey& config) { /** * A hash function for ConfigKey so it can be used for unordered_map/set. - * Unfortunately this hast to go in std namespace because C++ is fun! + * Unfortunately this has to go in std namespace because C++ is fun! */ namespace std { diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index 1e1d88ec142a..21256097a766 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -18,16 +18,23 @@ #include "stats_util.h" -#include <vector> - +#include <android-base/file.h> +#include <dirent.h> #include <stdio.h> +#include <vector> +#include "android-base/stringprintf.h" namespace android { namespace os { namespace statsd { +#define STATS_SERVICE_DIR "/data/system/stats-service" + static StatsdConfig build_fake_config(); +using android::base::StringPrintf; +using std::unique_ptr; + ConfigManager::ConfigManager() { } @@ -35,11 +42,10 @@ ConfigManager::~ConfigManager() { } void ConfigManager::Startup() { - // TODO: Implement me -- read from storage and call onto all of the listeners. - // Instead, we'll just make a fake one. + readConfigFromDisk(); // this should be called from StatsService when it receives a statsd_config - UpdateConfig(ConfigKey(0, "fake"), build_fake_config()); + UpdateConfig(ConfigKey(1000, "fake"), build_fake_config()); } void ConfigManager::AddListener(const sp<ConfigListener>& listener) { @@ -52,7 +58,7 @@ void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& confi // Why doesn't this work? mConfigs.insert({key, config}); // Save to disk - update_saved_configs(); + update_saved_configs(key, config); // Tell everyone for (auto& listener : mListeners) { @@ -60,21 +66,47 @@ void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& confi } } +void ConfigManager::SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls) { + mConfigReceivers[key] = pair<string, string>(pkg, cls); +} + +void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) { + mConfigReceivers.erase(key); +} + void ConfigManager::RemoveConfig(const ConfigKey& key) { unordered_map<ConfigKey, StatsdConfig>::iterator it = mConfigs.find(key); if (it != mConfigs.end()) { // Remove from map mConfigs.erase(it); - // Save to disk - update_saved_configs(); - // Tell everyone for (auto& listener : mListeners) { listener->OnConfigRemoved(key); } } - // If we didn't find it, just quietly ignore it. + + // Remove from disk. There can still be a lingering file on disk so we check + // whether or not the config was on memory. + remove_saved_configs(key); +} + +void ConfigManager::remove_saved_configs(const ConfigKey& key) { + unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir); + if (dir == NULL) { + ALOGD("no default config on disk"); + return; + } + string prefix = StringPrintf("%d-%s", key.GetUid(), key.GetName().c_str()); + dirent* de; + while ((de = readdir(dir.get()))) { + char* name = de->d_name; + if (name[0] != '.' && strncmp(name, prefix.c_str(), prefix.size()) == 0) { + if (remove(StringPrintf("%s/%s", STATS_SERVICE_DIR, name).c_str()) != 0) { + ALOGD("no file found"); + } + } + } } void ConfigManager::RemoveConfigs(int uid) { @@ -85,6 +117,7 @@ void ConfigManager::RemoveConfigs(int uid) { if (it->first.GetUid() == uid) { removed.push_back(it->first); it = mConfigs.erase(it); + mConfigReceivers.erase(it->first); } else { it++; } @@ -99,18 +132,101 @@ void ConfigManager::RemoveConfigs(int uid) { } } +vector<ConfigKey> ConfigManager::GetAllConfigKeys() { + vector<ConfigKey> ret; + for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) { + ret.push_back(it->first); + } + return ret; +} + +const pair<string, string> ConfigManager::GetConfigReceiver(const ConfigKey& key) { + auto it = mConfigReceivers.find(key); + if (it == mConfigReceivers.end()) { + return pair<string,string>(); + } else { + return it->second; + } +} + void ConfigManager::Dump(FILE* out) { fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size()); fprintf(out, " uid name\n"); for (unordered_map<ConfigKey, StatsdConfig>::const_iterator it = mConfigs.begin(); it != mConfigs.end(); it++) { fprintf(out, " %6d %s\n", it->first.GetUid(), it->first.GetName().c_str()); + auto receiverIt = mConfigReceivers.find(it->first); + if (receiverIt != mConfigReceivers.end()) { + fprintf(out, " -> received by %s, %s\n", receiverIt->second.first.c_str(), + receiverIt->second.second.c_str()); + } // TODO: Print the contents of the config too. } } -void ConfigManager::update_saved_configs() { - // TODO: Implement me -- write to disk. +void ConfigManager::readConfigFromDisk() { + unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir); + if (dir == NULL) { + ALOGD("no default config on disk"); + return; + } + + dirent* de; + while ((de = readdir(dir.get()))) { + char* name = de->d_name; + if (name[0] == '.') continue; + ALOGD("file %s", name); + + int index = 0; + int uid = 0; + string configName; + char* substr = strtok(name, "-"); + // Timestamp lives at index 2 but we skip parsing it as it's not needed. + while (substr != nullptr && index < 2) { + if (index) { + uid = atoi(substr); + } else { + configName = substr; + } + index++; + } + if (index < 2) continue; + string file_name = StringPrintf("%s/%s", STATS_SERVICE_DIR, name); + ALOGD("full file %s", file_name.c_str()); + int fd = open(file_name.c_str(), O_RDONLY | O_CLOEXEC); + if (fd != -1) { + string content; + if (android::base::ReadFdToString(fd, &content)) { + StatsdConfig config; + if (config.ParseFromString(content)) { + mConfigs[ConfigKey(uid, configName)] = config; + ALOGD("map key uid=%d|name=%s", uid, name); + } + } + close(fd); + } + } +} + +void ConfigManager::update_saved_configs(const ConfigKey& key, const StatsdConfig& config) { + mkdir(STATS_SERVICE_DIR, S_IRWXU); + + // If there is a pre-existing config with same key we should first delete it. + remove_saved_configs(key); + + // Then we save the latest config. + string file_name = StringPrintf("%s/%d-%s-%ld", STATS_SERVICE_DIR, key.GetUid(), + key.GetName().c_str(), time(nullptr)); + int fd = open(file_name.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR); + if (fd != -1) { + const int numBytes = config.ByteSize(); + vector<uint8_t> buffer(numBytes); + config.SerializeToArray(&buffer[0], numBytes); + int result = write(fd, &buffer[0], numBytes); + close(fd); + bool wroteKey = (result == numBytes); + ALOGD("wrote to file %d", wroteKey); + } } static StatsdConfig build_fake_config() { @@ -152,7 +268,7 @@ static StatsdConfig build_fake_config() { metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); // Anomaly threshold for screen-on count. - Alert* alert = config.add_alerts(); + Alert* alert = config.add_alert(); alert->set_name("1"); alert->set_number_of_buckets(6); alert->set_trigger_if_sum_gt(10); @@ -167,7 +283,7 @@ static StatsdConfig build_fake_config() { keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY); // Anomaly threshold for background count. - alert = config.add_alerts(); + alert = config.add_alert(); alert->set_name("2"); alert->set_number_of_buckets(4); alert->set_trigger_if_sum_gt(30); @@ -199,11 +315,11 @@ static StatsdConfig build_fake_config() { DurationMetric* durationMetric = config.add_duration_metric(); durationMetric->set_name("5"); durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); keyMatcher = durationMetric->add_dimension(); keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID); durationMetric->set_what("WL_HELD_PER_APP_PER_NAME"); - durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON"); + durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); link = durationMetric->add_links(); link->set_condition("APP_IS_BACKGROUND"); link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID); @@ -213,11 +329,11 @@ static StatsdConfig build_fake_config() { durationMetric = config.add_duration_metric(); durationMetric->set_name("6"); durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); keyMatcher = durationMetric->add_dimension(); keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID); durationMetric->set_what("WL_HELD_PER_APP_PER_NAME"); - durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON"); + durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); link = durationMetric->add_links(); link->set_condition("APP_IS_BACKGROUND"); link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID); @@ -227,9 +343,9 @@ static StatsdConfig build_fake_config() { durationMetric = config.add_duration_metric(); durationMetric->set_name("7"); durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); durationMetric->set_what("WL_HELD_PER_APP_PER_NAME"); - durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON"); + durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); link = durationMetric->add_links(); link->set_condition("APP_IS_BACKGROUND"); link->add_key_in_main()->set_key(WAKE_LOCK_UID_KEY_ID); @@ -239,7 +355,7 @@ static StatsdConfig build_fake_config() { durationMetric = config.add_duration_metric(); durationMetric->set_name("8"); durationMetric->mutable_bucket()->set_bucket_size_millis(10 * 1000L); - durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM); + durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); durationMetric->set_what("SCREEN_IS_ON"); // Value metric to count KERNEL_WAKELOCK when screen turned on diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h index 5d73eaf3c7b6..01d7fb969230 100644 --- a/cmds/statsd/src/config/ConfigManager.h +++ b/cmds/statsd/src/config/ConfigManager.h @@ -32,6 +32,7 @@ using android::RefBase; using std::string; using std::unordered_map; using std::vector; +using std::pair; /** * Keeps track of which configurations have been set from various sources. @@ -45,9 +46,7 @@ public: virtual ~ConfigManager(); /** - * Call to load the saved configs from disk. - * - * TODO: Implement me + * Initialize ConfigListener by reading from disk and get updates. */ void Startup(); @@ -64,6 +63,26 @@ public: void UpdateConfig(const ConfigKey& key, const StatsdConfig& data); /** + * Sets the broadcast receiver for a configuration key. + */ + void SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls); + + /** + * Returns the package name and class name representing the broadcast receiver for this config. + */ + const pair<string, string> GetConfigReceiver(const ConfigKey& key); + + /** + * Returns all config keys registered. + */ + vector<ConfigKey> GetAllConfigKeys(); + + /** + * Erase any broadcast receiver associated with this config key. + */ + void RemoveConfigReceiver(const ConfigKey& key); + + /** * A configuration was removed. * * Reports this to listeners. @@ -84,17 +103,33 @@ private: /** * Save the configs to disk. */ - void update_saved_configs(); + void update_saved_configs(const ConfigKey& key, const StatsdConfig& config); + + /** + * Remove saved configs from disk. + */ + void remove_saved_configs(const ConfigKey& key); /** - * The Configs that have been set + * The Configs that have been set. Each config should */ unordered_map<ConfigKey, StatsdConfig> mConfigs; /** + * Each config key can be subscribed by up to one receiver, specified as the package name and + * class name. + */ + unordered_map<ConfigKey, pair<string, string>> mConfigReceivers; + + /** * The ConfigListeners that will be told about changes. */ vector<sp<ConfigListener>> mListeners; + + /** + * Call to load the saved configs from disk. + */ + void readConfigFromDisk(); }; } // namespace statsd diff --git a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp index e004d217a966..e2745d21af3d 100644 --- a/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidFreqPuller.cpp @@ -71,10 +71,9 @@ bool CpuTimePerUidFreqPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>> do { timeMs = std::stoull(pch); auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_FREQ_PULLED, timestamp); - auto elemList = ptr->GetAndroidLogEventList(); - *elemList << uid; - *elemList << idx; - *elemList << timeMs; + ptr->write(uid); + ptr->write(idx); + ptr->write(timeMs); ptr->init(); data->push_back(ptr); VLOG("uid %lld, freq idx %d, sys time %lld", (long long)uid, idx, (long long)timeMs); diff --git a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp index b84b877a0ee9..e0572dca679d 100644 --- a/cmds/statsd/src/external/CpuTimePerUidPuller.cpp +++ b/cmds/statsd/src/external/CpuTimePerUidPuller.cpp @@ -66,10 +66,9 @@ bool CpuTimePerUidPuller::Pull(const int tagId, vector<shared_ptr<LogEvent>>* da uint64_t sysTimeMs = std::stoull(pch); auto ptr = make_shared<LogEvent>(android::util::CPU_TIME_PER_UID_PULLED, timestamp); - auto elemList = ptr->GetAndroidLogEventList(); - *elemList << uid; - *elemList << userTimeMs; - *elemList << sysTimeMs; + ptr->write(uid); + ptr->write(userTimeMs); + ptr->write(sysTimeMs); ptr->init(); data->push_back(ptr); VLOG("uid %lld, user time %lld, sys time %lld", (long long)uid, (long long)userTimeMs, (long long)sysTimeMs); diff --git a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp index 319feef49b35..3ee636dfcde5 100644 --- a/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp +++ b/cmds/statsd/src/external/ResourcePowerManagerPuller.cpp @@ -93,11 +93,10 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven auto statePtr = make_shared<LogEvent>( android::util::POWER_STATE_PLATFORM_SLEEP_STATE_PULLED, timestamp); - auto elemList = statePtr->GetAndroidLogEventList(); - *elemList << state.name; - *elemList << state.residencyInMsecSinceBoot; - *elemList << state.totalTransitions; - *elemList << state.supportedOnlyInSuspend; + statePtr->write(state.name); + statePtr->write(state.residencyInMsecSinceBoot); + statePtr->write(state.totalTransitions); + statePtr->write(state.supportedOnlyInSuspend); statePtr->init(); data->push_back(statePtr); VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(), @@ -106,11 +105,10 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven for (auto voter : state.voters) { auto voterPtr = make_shared<LogEvent>(android::util::POWER_STATE_VOTER_PULLED, timestamp); - auto elemList = voterPtr->GetAndroidLogEventList(); - *elemList << state.name; - *elemList << voter.name; - *elemList << voter.totalTimeInMsecVotedForSinceBoot; - *elemList << voter.totalNumberOfTimesVotedSinceBoot; + voterPtr->write(state.name); + voterPtr->write(voter.name); + voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot); + voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot); voterPtr->init(); data->push_back(voterPtr); VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(), @@ -141,13 +139,12 @@ bool ResourcePowerManagerPuller::Pull(const int tagId, vector<shared_ptr<LogEven const PowerStateSubsystemSleepState& state = subsystem.states[j]; auto subsystemStatePtr = make_shared<LogEvent>( android::util::POWER_STATE_SUBSYSTEM_SLEEP_STATE_PULLED, timestamp); - auto elemList = subsystemStatePtr->GetAndroidLogEventList(); - *elemList << subsystem.name; - *elemList << state.name; - *elemList << state.residencyInMsecSinceBoot; - *elemList << state.totalTransitions; - *elemList << state.lastEntryTimestampMs; - *elemList << state.supportedOnlyInSuspend; + subsystemStatePtr->write(subsystem.name); + subsystemStatePtr->write(state.name); + subsystemStatePtr->write(state.residencyInMsecSinceBoot); + subsystemStatePtr->write(state.totalTransitions); + subsystemStatePtr->write(state.lastEntryTimestampMs); + subsystemStatePtr->write(state.supportedOnlyInSuspend); subsystemStatePtr->init(); data->push_back(subsystemStatePtr); VLOG("subsystemstate: %s, %s, %lld, %lld, %lld", diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index 913b906986c3..103213830914 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -29,21 +29,71 @@ using std::ostringstream; using std::string; using android::util::ProtoOutputStream; -// We need to keep a copy of the android_log_event_list owned by this instance so that the char* -// for strings is not cleared before we can read them. -LogEvent::LogEvent(log_msg& msg) : mList(msg) { - init(msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec, &mList); +LogEvent::LogEvent(log_msg& msg) { + mContext = + create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t)); + mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec; + init(mContext); } -LogEvent::LogEvent(int tag, uint64_t timestampNs) : mList(tag), mTimestampNs(timestampNs) { +LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) { + mTimestampNs = timestampNs; + mTagId = tagId; + mContext = create_android_logger(1937006964); // the event tag shared by all stats logs + if (mContext) { + android_log_write_int32(mContext, tagId); + } } -LogEvent::~LogEvent() { +void LogEvent::init() { + if (mContext) { + const char* buffer; + size_t len = android_log_write_list_buffer(mContext, &buffer); + // turns to reader mode + mContext = create_android_log_parser(buffer, len); + init(mContext); + } } -void LogEvent::init() { - mList.convert_to_reader(); - init(mTimestampNs, &mList); +bool LogEvent::write(int32_t value) { + if (mContext) { + return android_log_write_int32(mContext, value) >= 0; + } + return false; +} + +bool LogEvent::write(uint32_t value) { + if (mContext) { + return android_log_write_int32(mContext, value) >= 0; + } + return false; +} + +bool LogEvent::write(uint64_t value) { + if (mContext) { + return android_log_write_int64(mContext, value) >= 0; + } + return false; +} + +bool LogEvent::write(const string& value) { + if (mContext) { + return android_log_write_string8_len(mContext, value.c_str(), value.length()) >= 0; + } + return false; +} + +bool LogEvent::write(float value) { + if (mContext) { + return android_log_write_float32(mContext, value) >= 0; + } + return false; +} + +LogEvent::~LogEvent() { + if (mContext) { + android_log_destroy(&mContext); + } } /** @@ -51,22 +101,25 @@ void LogEvent::init() { * The goal is to do as little preprocessing as possible, because we read a tiny fraction * of the elements that are written to the log. */ -void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) { - mTimestampNs = timestampNs; - mTagId = reader->tag(); - +void LogEvent::init(android_log_context context) { mElements.clear(); android_log_list_element elem; - // TODO: The log is actually structured inside one list. This is convenient // because we'll be able to use it to put the attribution (WorkSource) block first // without doing our own tagging scheme. Until that change is in, just drop the // list-related log elements and the order we get there is our index-keyed data // structure. + int i = 0; do { - elem = android_log_read_next(reader->context()); + elem = android_log_read_next(context); switch ((int)elem.type) { case EVENT_TYPE_INT: + // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. If we add WorkSource, it would + // be the list starting at [2]. + if (i == 1) { + mTagId = elem.data.int32; + break; + } case EVENT_TYPE_FLOAT: case EVENT_TYPE_STRING: case EVENT_TYPE_LONG: @@ -81,13 +134,10 @@ void LogEvent::init(int64_t timestampNs, android_log_event_list* reader) { default: break; } + i++; } while ((elem.type != EVENT_TYPE_UNKNOWN) && !elem.complete); } -android_log_event_list* LogEvent::GetAndroidLogEventList() { - return &mList; -} - int64_t LogEvent::GetLong(size_t key, status_t* err) const { if (key < 1 || (key - 1) >= mElements.size()) { *err = BAD_INDEX; diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index 298494049628..7e8a96b2114e 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -21,6 +21,7 @@ #include <android/util/ProtoOutputStream.h> #include <log/log_event_list.h> #include <log/log_read.h> +#include <private/android_logger.h> #include <utils/Errors.h> #include <memory> @@ -45,12 +46,9 @@ public: explicit LogEvent(log_msg& msg); /** - * Constructs a LogEvent with the specified tag and creates an android_log_event_list in write - * mode. Obtain this list with the getter. Make sure to call init() before attempting to read - * any of the values. This constructor is useful for unit-testing since we can't pass in an - * android_log_event_list since there is no copy constructor or assignment operator available. + * Constructs a LogEvent with synthetic data for testing. Must call init() before reading. */ - explicit LogEvent(int tag, uint64_t timestampNs); + explicit LogEvent(int32_t tagId, uint64_t timestampNs); ~LogEvent(); @@ -76,6 +74,17 @@ public: float GetFloat(size_t key, status_t* err) const; /** + * Write test data to the LogEvent. This can only be used when the LogEvent is constructed + * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it. + */ + bool write(uint32_t value); + bool write(int32_t value); + bool write(uint64_t value); + bool write(int64_t value); + bool write(const string& value); + bool write(float value); + + /** * Return a string representation of this event. */ string ToString() const; @@ -91,13 +100,6 @@ public: KeyValuePair GetKeyValueProto(size_t key) const; /** - * A pointer to the contained log_event_list. - * - * @return The android_log_event_list contained within. - */ - android_log_event_list* GetAndroidLogEventList(); - - /** * Used with the constructor where tag is passed in. Converts the log_event_list to read mode * and prepares the list for reading. */ @@ -113,16 +115,11 @@ private: /** * Parses a log_msg into a LogEvent object. */ - void init(const log_msg& msg); - - /** - * Parses a log_msg into a LogEvent object. - */ - void init(int64_t timestampNs, android_log_event_list* reader); + void init(android_log_context context); vector<android_log_list_element> mElements; - // Need a copy of the android_log_event_list so the strings are not cleared. - android_log_event_list mList; + + android_log_context mContext; uint64_t mTimestampNs; diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp index a7402800630e..41b24bc5ebd6 100644 --- a/cmds/statsd/src/main.cpp +++ b/cmds/statsd/src/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 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. diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index cccc9b3317e7..f7352cd1190c 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -84,6 +84,9 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera } } break; + case LogicalOperation::LOGICAL_OPERATION_UNSPECIFIED: + matched = false; + break; } return matched; } @@ -112,6 +115,7 @@ bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEvent& e if (err == NO_ERROR && val != NULL) { if (!(cur.eq_string() == val)) { allMatched = false; + break; } } } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt || @@ -126,26 +130,30 @@ bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEvent& e if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt) { if (!(val == cur.eq_int())) { allMatched = false; + break; } } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt) { if (!(val < cur.lt_int())) { allMatched = false; + break; } } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtInt) { if (!(val > cur.gt_int())) { allMatched = false; + break; } } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLteInt) { if (!(val <= cur.lte_int())) { allMatched = false; + break; } } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGteInt) { if (!(val >= cur.gte_int())) { allMatched = false; + break; } } } - break; } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqBool) { // Boolean fields status_t err = NO_ERROR; @@ -153,21 +161,24 @@ bool matchesSimple(const SimpleLogEntryMatcher& simpleMatcher, const LogEvent& e if (err == NO_ERROR) { if (!(cur.eq_bool() == val)) { allMatched = false; + break; } } } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat || matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) { // Float fields status_t err = NO_ERROR; - bool val = event.GetFloat(key, &err); + float val = event.GetFloat(key, &err); if (err == NO_ERROR) { if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat) { - if (!(cur.lt_float() <= val)) { + if (!(val < cur.lt_float())) { allMatched = false; + break; } } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) { - if (!(cur.gt_float() >= val)) { + if (!(val > cur.gt_float())) { allMatched = false; + break; } } } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index aaf3ec2c298b..eba2e06035e5 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -63,6 +63,7 @@ const int FIELD_ID_DURATION = 3; DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric, const int conditionIndex, const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, + const bool nesting, const sp<ConditionWizard>& wizard, const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs) @@ -71,6 +72,7 @@ DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric, mStartIndex(startIndex), mStopIndex(stopIndex), mStopAllIndex(stopAllIndex), + mNested(nesting), mInternalDimension(internalDimension) { // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract // them in the base class, because the proto generated CountMetric, and DurationMetric are @@ -109,13 +111,13 @@ void DurationMetricProducer::startNewProtoOutputStream(long long startTime) { unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( vector<DurationBucket>& bucket) { - switch (mMetric.type()) { - case DurationMetric_AggregationType_DURATION_SUM: - return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex, + switch (mMetric.aggregation_type()) { + case DurationMetric_AggregationType_SUM: + return make_unique<OringDurationTracker>(mWizard, mConditionTrackerIndex, mNested, mCurrentBucketStartTimeNs, mBucketSizeNs, bucket); - case DurationMetric_AggregationType_DURATION_MAX_SPARSE: - return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex, + case DurationMetric_AggregationType_MAX_SPARSE: + return make_unique<MaxDurationTracker>(mWizard, mConditionTrackerIndex, mNested, mCurrentBucketStartTimeNs, mBucketSizeNs, bucket); } @@ -220,10 +222,8 @@ std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReport() { (long long)mCurrentBucketStartTimeNs); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); - startNewProtoOutputStream(endTime); - mPastBuckets.clear(); - + // TODO: Properly clear the old buckets. return buffer; } @@ -233,10 +233,12 @@ void DurationMetricProducer::flushIfNeeded(uint64_t eventTime) { } VLOG("flushing..........."); - for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end(); ++it) { + for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) { if (it->second->flushIfNeeded(eventTime)) { VLOG("erase bucket for key %s", it->first.c_str()); - mCurrentSlicedDuration.erase(it); + it = mCurrentSlicedDuration.erase(it); + } else { + ++it; } } @@ -268,7 +270,7 @@ void DurationMetricProducer::onMatchedLogEventInternal( if (matcherIndex == mStartIndex) { it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys); } else if (matcherIndex == mStopIndex) { - it->second->noteStop(atomKey, event.GetTimestampNs()); + it->second->noteStop(atomKey, event.GetTimestampNs(), false); } } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index eea00454d5e6..bb5d4d94dea8 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -39,7 +39,8 @@ class DurationMetricProducer : public MetricProducer { public: DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex, const size_t startIndex, const size_t stopIndex, - const size_t stopAllIndex, const sp<ConditionWizard>& wizard, + const size_t stopAllIndex, const bool nesting, + const sp<ConditionWizard>& wizard, const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs); virtual ~DurationMetricProducer(); @@ -80,6 +81,9 @@ private: // Index of the SimpleLogEntryMatcher which defines the stop all for all dimensions. const size_t mStopAllIndex; + // nest counting -- for the same key, stops must match the number of starts to make real stop + const bool mNested; + // The dimension from the atom predicate. e.g., uid, wakelock name. const vector<KeyMatcher> mInternalDimension; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index b2ffb2fc6ff5..9a94a0efa850 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -49,7 +49,7 @@ const int FIELD_ID_EVENT_METRICS = 4; const int FIELD_ID_DATA = 1; // for EventMetricData const int FIELD_ID_TIMESTAMP_NANOS = 1; -const int FIELD_ID_STATS_EVENTS = 2; +const int FIELD_ID_ATOMS = 2; EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex, const sp<ConditionWizard>& wizard, @@ -96,7 +96,6 @@ std::unique_ptr<std::vector<uint8_t>> EventMetricProducer::onDumpReport() { std::unique_ptr<std::vector<uint8_t>> buffer = serializeProto(); startNewProtoOutputStream(endTime); - mByteSize = 0; return buffer; } @@ -117,15 +116,14 @@ void EventMetricProducer::onMatchedLogEventInternal( long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); mProto->write(FIELD_TYPE_INT64 | FIELD_ID_TIMESTAMP_NANOS, (long long)event.GetTimestampNs()); - long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_STATS_EVENTS); + long long eventToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOMS); event.ToProto(*mProto); mProto->end(eventToken); mProto->end(wrapperToken); - // TODO: Find a proper way to derive the size of incoming LogEvent. } size_t EventMetricProducer::byteSize() { - return mByteSize; + return mProto->bytesWritten(); } } // namespace statsd diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 77406216f421..0dccdf493869 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -65,8 +65,6 @@ protected: private: const EventMetric mMetric; - - size_t mByteSize; }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index c0930e381ce3..c7982a837c14 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -61,12 +61,15 @@ public: // TODO: Pass a timestamp as a parameter in onDumpReport and update all its // implementations. + // onDumpReport returns the proto-serialized output and clears the previously stored contents. virtual std::unique_ptr<std::vector<uint8_t>> onDumpReport() = 0; virtual bool isConditionSliced() const { return mConditionSliced; }; + // Returns the memory in bytes currently used to store this metric's data. Does not change + // state. virtual size_t byteSize() = 0; protected: diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 39c79f99daca..fb167798a44f 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -46,6 +46,8 @@ public: // Config source owner can call onDumpReport() to get all the metrics collected. std::vector<std::unique_ptr<std::vector<uint8_t>>> onDumpReport(); + // Computes the total byte size of all metrics managed by a single config source. + // Does not change the state. size_t byteSize(); private: diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 5c76d0e6513b..18b334988d0d 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -34,6 +34,10 @@ enum DurationState { // Hold duration information for one atom level duration in current on-going bucket. struct DurationInfo { DurationState state; + + // the number of starts seen. + int32_t startCount; + // most recent start time. int64_t lastStartTime; // existing duration in current bucket. @@ -42,7 +46,7 @@ struct DurationInfo { // cache the HashableDimensionKeys we need to query the condition for this duration event. ConditionKey conditionKeys; - DurationInfo() : state(kStopped), lastStartTime(0), lastDuration(0){}; + DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){}; }; struct DurationBucket { @@ -53,18 +57,21 @@ struct DurationBucket { class DurationTracker { public: - DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, uint64_t currentBucketStartNs, - uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket) + DurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting, + uint64_t currentBucketStartNs, uint64_t bucketSizeNs, + std::vector<DurationBucket>& bucket) : mWizard(wizard), mConditionTrackerIndex(conditionIndex), - mCurrentBucketStartTimeNs(currentBucketStartNs), mBucketSizeNs(bucketSizeNs), + mNested(nesting), + mCurrentBucketStartTimeNs(currentBucketStartNs), mBucket(bucket), mDuration(0){}; virtual ~DurationTracker(){}; virtual void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) = 0; - virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) = 0; + virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime, + const bool stopAll) = 0; virtual void noteStopAll(const uint64_t eventTime) = 0; virtual void onSlicedConditionMayChange(const uint64_t timestamp) = 0; virtual void onConditionChanged(bool condition, const uint64_t timestamp) = 0; @@ -75,11 +82,13 @@ public: protected: sp<ConditionWizard> mWizard; - int mConditionTrackerIndex; + const int mConditionTrackerIndex; - uint64_t mCurrentBucketStartTimeNs; + const int64_t mBucketSizeNs; + + const bool mNested; - int64_t mBucketSizeNs; + uint64_t mCurrentBucketStartTimeNs; std::vector<DurationBucket>& mBucket; // where to write output diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index 43c21a830ecf..8c7bfb6e4462 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -23,10 +23,10 @@ namespace android { namespace os { namespace statsd { -MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, +MaxDurationTracker::MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket) - : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket) { + : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket) { } void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition, @@ -38,10 +38,10 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi switch (duration.state) { case kStarted: - // The same event is already started. Because we are not counting nesting, so ignore. + duration.startCount++; break; case kPaused: - // Safe to do nothing here. Paused means started but condition is false. + duration.startCount++; break; case kStopped: if (!condition) { @@ -51,11 +51,13 @@ void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool conditi duration.state = DurationState::kStarted; duration.lastStartTime = eventTime; } + duration.startCount = 1; break; } } -void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime) { +void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t eventTime, + bool forceStop) { VLOG("MaxDuration: key %s stop", key.c_str()); if (mInfos.find(key) == mInfos.end()) { // we didn't see a start event before. do nothing. @@ -68,16 +70,23 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_ // already stopped, do nothing. break; case DurationState::kStarted: { - duration.state = DurationState::kStopped; - int64_t durationTime = eventTime - duration.lastStartTime; - VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), (long long)duration.lastStartTime, - (long long)eventTime, (long long)durationTime); - duration.lastDuration = duration.lastDuration + durationTime; - VLOG(" record duration: %lld ", (long long)duration.lastDuration); + duration.startCount--; + if (forceStop || !mNested || duration.startCount <= 0) { + duration.state = DurationState::kStopped; + int64_t durationTime = eventTime - duration.lastStartTime; + VLOG("Max, key %s, Stop %lld %lld %lld", key.c_str(), + (long long)duration.lastStartTime, (long long)eventTime, + (long long)durationTime); + duration.lastDuration = duration.lastDuration + durationTime; + VLOG(" record duration: %lld ", (long long)duration.lastDuration); + } break; } case DurationState::kPaused: { - duration.state = DurationState::kStopped; + duration.startCount--; + if (forceStop || !mNested || duration.startCount <= 0) { + duration.state = DurationState::kStopped; + } break; } } @@ -88,11 +97,13 @@ void MaxDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_ } // Once an atom duration ends, we erase it. Next time, if we see another atom event with the // same name, they are still considered as different atom durations. - mInfos.erase(key); + if (duration.state == DurationState::kStopped) { + mInfos.erase(key); + } } void MaxDurationTracker::noteStopAll(const uint64_t eventTime) { for (auto& pair : mInfos) { - noteStop(pair.first, eventTime); + noteStop(pair.first, eventTime, true); } } diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index b095884876a8..167f81e414e1 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -28,12 +28,13 @@ namespace statsd { // they stop or bucket expires. class MaxDurationTracker : public DurationTracker { public: - MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, + MaxDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket); void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; - void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override; + void noteStop(const HashableDimensionKey& key, const uint64_t eventTime, + const bool stopAll) override; void noteStopAll(const uint64_t eventTime) override; bool flushIfNeeded(uint64_t timestampNs) override; void onSlicedConditionMayChange(const uint64_t timestamp) override; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index e4f1d2149f7c..faf5ce50b45f 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -20,10 +20,14 @@ namespace android { namespace os { namespace statsd { + +using std::pair; + OringDurationTracker::OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, - uint64_t currentBucketStartNs, uint64_t bucketSizeNs, + bool nesting, uint64_t currentBucketStartNs, + uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket) - : DurationTracker(wizard, conditionIndex, currentBucketStartNs, bucketSizeNs, bucket), + : DurationTracker(wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs, bucket), mStarted(), mPaused() { mLastStartTime = 0; @@ -36,9 +40,9 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi mLastStartTime = eventTime; VLOG("record first start...."); } - mStarted.insert(key); + mStarted[key]++; } else { - mPaused.insert(key); + mPaused[key]++; } if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) { @@ -48,11 +52,16 @@ void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condi VLOG("Oring: %s start, condition %d", key.c_str(), condition); } -void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp) { +void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint64_t timestamp, + const bool stopAll) { VLOG("Oring: %s stop", key.c_str()); auto it = mStarted.find(key); if (it != mStarted.end()) { - mStarted.erase(it); + (it->second)--; + if (stopAll || !mNested || it->second <= 0) { + mStarted.erase(it); + mConditionKeyMap.erase(key); + } if (mStarted.empty()) { mDuration += (timestamp - mLastStartTime); VLOG("record duration %lld, total %lld ", (long long)timestamp - mLastStartTime, @@ -60,8 +69,14 @@ void OringDurationTracker::noteStop(const HashableDimensionKey& key, const uint6 } } - mPaused.erase(key); - mConditionKeyMap.erase(key); + auto pausedIt = mPaused.find(key); + if (pausedIt != mPaused.end()) { + (pausedIt->second)--; + if (stopAll || !mNested || pausedIt->second <= 0) { + mPaused.erase(pausedIt); + mConditionKeyMap.erase(key); + } + } } void OringDurationTracker::noteStopAll(const uint64_t timestamp) { if (!mStarted.empty()) { @@ -118,11 +133,11 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { } void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) { - vector<HashableDimensionKey> startedToPaused; - vector<HashableDimensionKey> pausedToStarted; + vector<pair<HashableDimensionKey, int>> startedToPaused; + vector<pair<HashableDimensionKey, int>> pausedToStarted; if (!mStarted.empty()) { for (auto it = mStarted.begin(); it != mStarted.end();) { - auto key = *it; + const auto& key = it->first; if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) { VLOG("Key %s dont have condition key", key.c_str()); ++it; @@ -130,8 +145,8 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) } if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) != ConditionState::kTrue) { + startedToPaused.push_back(*it); it = mStarted.erase(it); - startedToPaused.push_back(key); VLOG("Key %s started -> paused", key.c_str()); } else { ++it; @@ -147,7 +162,7 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) if (!mPaused.empty()) { for (auto it = mPaused.begin(); it != mPaused.end();) { - auto key = *it; + const auto& key = it->first; if (mConditionKeyMap.find(key) == mConditionKeyMap.end()) { VLOG("Key %s dont have condition key", key.c_str()); ++it; @@ -155,8 +170,8 @@ void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) } if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) == ConditionState::kTrue) { + pausedToStarted.push_back(*it); it = mPaused.erase(it); - pausedToStarted.push_back(key); VLOG("Key %s paused -> started", key.c_str()); } else { ++it; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index b54dafaa1758..78760ba1a024 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -27,12 +27,13 @@ namespace statsd { // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted. class OringDurationTracker : public DurationTracker { public: - OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, + OringDurationTracker(sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, std::vector<DurationBucket>& bucket); void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; - void noteStop(const HashableDimensionKey& key, const uint64_t eventTime) override; + void noteStop(const HashableDimensionKey& key, const uint64_t eventTime, + const bool stopAll) override; void noteStopAll(const uint64_t eventTime) override; void onSlicedConditionMayChange(const uint64_t timestamp) override; void onConditionChanged(bool condition, const uint64_t timestamp) override; @@ -44,8 +45,8 @@ private: // 2) which keys are paused (started but condition was false) // 3) whenever a key stops, we remove it from the started set. And if the set becomes empty, // it means everything has stopped, we then record the end time. - std::set<HashableDimensionKey> mStarted; - std::set<HashableDimensionKey> mPaused; + std::map<HashableDimensionKey, int> mStarted; + std::map<HashableDimensionKey, int> mPaused; int64_t mLastStartTime; std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap; }; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index c2044d85ec9f..d83c144d5d8b 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -220,6 +220,11 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, metric.links(), allConditionTrackers, conditionIndex, conditionToMetricMap); + } else { + if (metric.links_size() > 0) { + ALOGW("metrics has a EventConditionLink but doesn't have a condition"); + return false; + } } sp<MetricProducer> countProducer = @@ -247,6 +252,8 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l const auto& simpleCondition = durationWhat.simple_condition(); + bool nesting = simpleCondition.count_nesting(); + int trackerIndices[3] = {-1, -1, -1}; if (!simpleCondition.has_start() || !handleMetricWithLogTrackers(simpleCondition.start(), metricIndex, @@ -276,15 +283,20 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l int conditionIndex = -1; - if (metric.has_predicate()) { - handleMetricWithConditions(metric.predicate(), metricIndex, conditionTrackerMap, + if (metric.has_condition()) { + handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, metric.links(), allConditionTrackers, conditionIndex, conditionToMetricMap); + } else { + if (metric.links_size() > 0) { + ALOGW("metrics has a EventConditionLink but doesn't have a condition"); + return false; + } } sp<MetricProducer> durationMetric = new DurationMetricProducer( metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2], - wizard, internalDimension, startTimeNs); + nesting, wizard, internalDimension, startTimeNs); allMetricProducers.push_back(durationMetric); } @@ -308,6 +320,11 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, metric.links(), allConditionTrackers, conditionIndex, conditionToMetricMap); + } else { + if (metric.links_size() > 0) { + ALOGW("metrics has a EventConditionLink but doesn't have a condition"); + return false; + } } sp<MetricProducer> eventMetric = @@ -349,6 +366,11 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, metric.links(), allConditionTrackers, conditionIndex, conditionToMetricMap); + } else { + if (metric.links_size() > 0) { + ALOGW("metrics has a EventConditionLink but doesn't have a condition"); + return false; + } } sp<MetricProducer> valueProducer = @@ -389,6 +411,11 @@ bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& l handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, metric.links(), allConditionTrackers, conditionIndex, conditionToMetricMap); + } else { + if (metric.links_size() > 0) { + ALOGW("metrics has a EventConditionLink but doesn't have a condition"); + return false; + } } sp<MetricProducer> gaugeProducer = diff --git a/cmds/statsd/src/packages/PackageInfoListener.h b/cmds/statsd/src/packages/PackageInfoListener.h index 13e776fb1f80..5aa3db573c70 100644 --- a/cmds/statsd/src/packages/PackageInfoListener.h +++ b/cmds/statsd/src/packages/PackageInfoListener.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 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. diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 239881402880..6c32d3e1b11e 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, versionCode 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index de68fbceb7a9..24eb96619527 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2017 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. diff --git a/cmds/statsd/src/stats_events_copy.proto b/cmds/statsd/src/stats_events_copy.proto deleted file mode 100644 index 898856b06ebc..000000000000 --- a/cmds/statsd/src/stats_events_copy.proto +++ /dev/null @@ -1,479 +0,0 @@ -/* - * Copyright (C) 2017 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. - */ - -// STOPSHIP: this is a duplicate of stats_event.proto with LITE_RUNTIME added -// to produce device side lite proto. We should move statsd to soong so that -// we can generate full and lite library from the same proto file. -syntax = "proto2"; -option optimize_for = LITE_RUNTIME; - -// TODO: Not the right package and class name -package android.os.statsd; -option java_package = "com.android.os"; -option java_outer_classname = "StatsEventProto"; - -/** - * The master event class. This message defines all of the available - * raw stats log events from the Android system, also known as "atoms." - * - * This field contains a single oneof with all of the available messages. - * The stats-log-api-gen tool runs as part of the Android build and - * generates the android.util.StatsLog class, which contains the constants - * and methods that Android uses to log. - * - * This StatsEvent class is not actually built into the Android system. - * Instead, statsd on Android constructs these messages synthetically, - * in the format defined here and in stats_log.proto. - */ -message StatsEvent { - oneof event { - // For StatsLog reasons, 1 is illegal and will not work. Must start at 2. - BleScanStateChanged ble_scan_state_changed = 2; - BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3; - BleScanResultReceived ble_scan_result_received = 4; - SensorStateChanged sensor_state_changed = 5; - GpsScanStateChanged gps_scan_state_changed = 6; // TODO: untested - SyncStateChanged sync_state_changed = 7; - ScheduledJobStateChanged scheduled_job_state_changed = 8; - ScreenBrightnessChanged screen_brightness_changed = 9; - // 10-20 are temporarily reserved for wakelocks etc. - UidWakelockStateChanged uid_wakelock_state_changed = 11; - LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 12; - BatterySaverModeStateChanged battery_saver_mode_state_changed = 21; - DeviceIdleModeStateChanged device_idle_mode_state_changed = 22; - AudioStateChanged audio_state_changed = 23; - MediaCodecActivityChanged media_codec_activity_changed = 24; - CameraStateChanged camera_state_changed = 25; - FlashlightStateChanged flashlight_state_changed = 26; - UidProcessStateChanged uid_process_state_changed = 27; - ProcessLifeCycleStateChanged process_life_cycle_state_changed = 28; - ScreenStateChanged screen_state_changed = 29; - DeviceTemperatureReported device_temperature_reported = 33; - // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15. - } -} - -/** - * A WorkSource represents the chained attribution of applications that - * resulted in a particular bit of work being done. - */ -message WorkSource { - // TODO -} - -/* - * ***************************************************************************** - * Below are all of the individual atoms that are logged by Android via statsd - * and Westworld. - * - * RULES: - * - The field ids for each atom must start at 1, and count upwards by 1. - * Skipping field ids is not allowed. - * - These form an API, so renaming, renumbering or removing fields is - * not allowed between android releases. (This is not currently enforced, - * but there will be a tool to enforce this restriction). - * - The types must be built-in protocol buffer types, namely, no sub-messages - * are allowed (yet). The bytes type is also not allowed. - * - The CamelCase name of the message type should match the - * underscore_separated name as defined in StatsEvent. - * - If an atom represents work that can be attributed to an app, there can - * be exactly one WorkSource field. It must be field number 1. - * - A field that is a uid should be a string field, tagged with the [xxx] - * annotation. The generated code on android will be represented by UIDs, - * and those UIDs will be translated in xxx to those strings. - * - * CONVENTIONS: - * - Events are past tense. e.g. ScreenStateChanged, not ScreenStateChange. - * - If there is a UID, it goes first. Think in an object-oriented fashion. - * ***************************************************************************** - */ - -/** - * Logs the temperature of the device, in tenths of a degree Celsius. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message DeviceTemperatureReported { - // Temperature in tenths of a degree C. - optional int32 temperature = 1; -} - -/** - * Logs when the screen state changes. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message ScreenStateChanged { - // TODO: Use the real screen state. - enum State { - STATE_UNKNOWN = 0; - STATE_OFF = 1; - STATE_ON = 2; - STATE_DOZE = 3; - STATE_DOZE_SUSPEND = 4; - STATE_VR = 5; - } - // New screen state. - optional State display_state = 1; -} - -/** - * Logs that the state of a process state, as per the activity manager, has changed. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message UidProcessStateChanged { - optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation - - // The state. - // TODO: Use the real (mapped) process states. - optional int32 state = 2; -} - -/** - * Logs that a process started, finished, crashed, or ANRed. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message ProcessLifeCycleStateChanged { - // TODO: Use the real (mapped) process states. - optional int32 uid = 1; // TODO: should be a string tagged w/ uid annotation - - // TODO: What is this? - optional string name = 2; - - // The state. - // TODO: Use an enum. - optional int32 event = 3; -} - - - -/** - * Logs when the ble scan state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message BleScanStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs when an unoptimized ble scan state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats). -message BleUnoptimizedScanStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs reporting of a ble scan finding results. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats). -message BleScanResultReceived { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - // Number of ble scan results returned. - optional int32 num_of_results = 2; -} - -/** - * Logs when a sensor state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message SensorStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - // TODO: Is there a way to get the actual name of the sensor? - // The id (int) of the sensor. - optional int32 sensor_id = 2; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 3; -} - - -/** - * Logs when GPS state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message GpsScanStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - - -/** - * Logs when a sync manager sync state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message SyncStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - // Name of the sync (as named in the app) - optional string name = 2; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 3; -} - -/** - * Logs when a job scheduler job state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message ScheduledJobStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - // Name of the job (as named in the app) - optional string name = 2; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 3; - - // TODO: Consider adding the stopReason (int) -} - -/** - * Logs when the audio state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message AudioStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs when the video codec state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message MediaCodecActivityChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs when the flashlight state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message FlashlightStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs when the camera state changes. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message CameraStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 2; -} - -/** - * Logs that the state of a wakelock (per app and per wakelock name) has changed. - * - * Logged from: - * TODO - */ -message WakelockChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - // Type of wakelock. - enum Type { - PARTIAL = 0; - FULL = 1; - WINDOW = 2; - } - optional int32 type = 2; - - // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). - optional string tag = 3; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 4; -} - -/** - * Logs when an app is holding a wakelock, regardless of the wakelock's name. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message UidWakelockStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; - - // Type of wakelock. - enum Type { - PARTIAL = 0; - FULL = 1; - WINDOW = 2; - } - optional int32 type = 2; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 3; -} - -/** - * Logs when a partial wakelock is considered 'long' (over 1 min). - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message LongPartialWakelockStateChanged { - // TODO: Add attribution instead of uid? - optional int32 uid = 1; - - // The wakelock tag (Called tag in the Java API, sometimes name elsewhere). - optional string tag = 2; - - // TODO: I have no idea what this is. - optional string history_tag = 3; - - enum State { - OFF = 0; - ON = 1; - } - optional State state = 4; -} - -/** - * Logs Battery Saver state change. - * - * Logged from: - * frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java - */ -message BatterySaverModeStateChanged { - enum State { - OFF = 0; - ON = 1; - } - optional State state = 1; -} - -/** - * Logs Doze mode state change. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message DeviceIdleModeStateChanged { - // TODO: Use the enum matching BatteryStats.DEVICE_IDLE_MODE_. - optional int32 state = 1; -} - -/** - * Logs screen brightness level. - * - * Logged from: - * frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java - */ -message ScreenBrightnessChanged { - // Screen brightness level. Should be in [-1, 255] according to PowerManager.java. - optional int32 level = 1; -}
\ No newline at end of file diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 1e37ff8c0aef..4f5df5512847 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -22,7 +22,7 @@ package android.os.statsd; option java_package = "com.android.os"; option java_outer_classname = "StatsLog"; -import "frameworks/base/cmds/statsd/src/stats_events_copy.proto"; +import "frameworks/base/cmds/statsd/src/atoms_copy.proto"; message KeyValuePair { optional int32 key = 1; @@ -38,7 +38,7 @@ message KeyValuePair { message EventMetricData { optional int64 timestamp_nanos = 1; - optional StatsEvent stats_events = 2; + optional Atom atom = 2; } message CountBucketInfo { diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 7648a9178a7c..d3b04ba5e32d 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -47,6 +47,7 @@ message KeyValueMatcher { } enum LogicalOperation { + LOGICAL_OPERATION_UNSPECIFIED = 0; AND = 1; OR = 2; NOT = 3; @@ -111,23 +112,12 @@ message Bucket { optional int64 bucket_size_millis = 1; } -message Alert { - optional string name = 1; - - optional string metric_name = 2; - - message IncidentdDetails { - repeated int32 section = 1; - } - optional IncidentdDetails incidentd_details = 3; - - optional int32 number_of_buckets = 4; - - optional int32 refractory_period_secs = 5; +message EventConditionLink { + optional string condition = 1; - optional int64 trigger_if_sum_gt = 6; + repeated KeyMatcher key_in_main = 2; - optional int32 refractory_period_in_buckets = 7; + repeated KeyMatcher key_in_condition = 3; } message EventMetric { @@ -151,9 +141,7 @@ message CountMetric { optional Bucket bucket = 5; - optional bool include_in_output = 6; - - repeated EventConditionLink links = 7; + repeated EventConditionLink links = 6; } message DurationMetric { @@ -161,20 +149,20 @@ message DurationMetric { optional string what = 2; - enum AggregationType { - DURATION_SUM = 1; + optional string condition = 3; - DURATION_MAX_SPARSE = 2; - } - optional AggregationType type = 3; + repeated EventConditionLink links = 4; - optional string predicate = 4; + enum AggregationType { + SUM = 1; - repeated KeyMatcher dimension = 5; + MAX_SPARSE = 2; + } + optional AggregationType aggregation_type = 5 [default = SUM]; - optional Bucket bucket = 6; + repeated KeyMatcher dimension = 6; - repeated EventConditionLink links = 7; + optional Bucket bucket = 7; } message GaugeMetric { @@ -208,16 +196,30 @@ message ValueMetric { repeated EventConditionLink links = 7; - enum Operation { SUM = 1; } - optional Operation operation = 9 [default = SUM]; + enum AggregationType { + SUM = 1; + } + optional AggregationType aggregation_type = 8 [default = SUM]; } -message EventConditionLink { - optional string condition = 1; +message Alert { + optional string name = 1; - repeated KeyMatcher key_in_main = 2; - repeated KeyMatcher key_in_condition = 3; -}; + optional string metric_name = 2; + + message IncidentdDetails { + repeated int32 section = 1; + } + optional IncidentdDetails incidentd_details = 3; + + optional int32 number_of_buckets = 4; + + optional int32 refractory_period_secs = 5; + + optional int64 trigger_if_sum_gt = 6; + + optional int32 refractory_period_in_buckets = 7; +} message StatsdConfig { optional string name = 1; @@ -236,5 +238,5 @@ message StatsdConfig { repeated Condition condition = 8; - repeated Alert alerts = 9; + repeated Alert alert = 9; } diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp index fad5de67ca1a..f570522dcd0a 100644 --- a/cmds/statsd/tests/ConfigManager_test.cpp +++ b/cmds/statsd/tests/ConfigManager_test.cpp @@ -85,7 +85,7 @@ TEST(ConfigManagerTest, TestAddUpdateRemove) { // TODO: Remove this when we get rid of the fake one, and make this // test loading one from disk somewhere. EXPECT_CALL(*(listener.get()), - OnConfigUpdated(ConfigKeyEq(0, "fake"), StatsdConfigEq("12345"))) + OnConfigUpdated(ConfigKeyEq(1000, "fake"), StatsdConfigEq("12345"))) .RetiresOnSaturation(); manager->Startup(); diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index d0898b0fa865..40c0e9d74c1d 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -27,7 +27,7 @@ using namespace android::os::statsd; using std::unordered_map; using std::vector; -const int TAG_ID = 123; +const int32_t TAG_ID = 123; const int FIELD_ID_1 = 1; const int FIELD_ID_2 = 2; const int FIELD_ID_3 = 2; @@ -43,8 +43,6 @@ TEST(LogEntryMatcherTest, TestSimpleMatcher) { simpleMatcher->set_tag(TAG_ID); LogEvent event(TAG_ID, 0); - - // Convert to a LogEvent event.init(); // Test @@ -63,10 +61,8 @@ TEST(LogEntryMatcherTest, TestBoolMatcher) { // Set up the event LogEvent event(TAG_ID, 0); - auto list = event.GetAndroidLogEventList(); - *list << true; - *list << false; - + event.write(true); + event.write(false); // Convert to a LogEvent event.init(); @@ -99,14 +95,44 @@ TEST(LogEntryMatcherTest, TestStringMatcher) { // Set up the event LogEvent event(TAG_ID, 0); - auto list = event.GetAndroidLogEventList(); - *list << "some value"; + event.write("some value"); + // Convert to a LogEvent + event.init(); + + // Test + EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); +} + +TEST(LogEntryMatcherTest, TestMultiFieldsMatcher) { + // Set up the matcher + LogEntryMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_log_entry_matcher(); + simpleMatcher->set_tag(TAG_ID); + auto keyValue1 = simpleMatcher->add_key_value_matcher(); + keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1); + auto keyValue2 = simpleMatcher->add_key_value_matcher(); + keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2); + + // Set up the event + LogEvent event(TAG_ID, 0); + event.write(2); + event.write(3); // Convert to a LogEvent event.init(); // Test + keyValue1->set_eq_int(2); + keyValue2->set_eq_int(3); EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + + keyValue1->set_eq_int(2); + keyValue2->set_eq_int(4); + EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + + keyValue1->set_eq_int(4); + keyValue2->set_eq_int(3); + EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); } TEST(LogEntryMatcherTest, TestIntComparisonMatcher) { @@ -120,9 +146,7 @@ TEST(LogEntryMatcherTest, TestIntComparisonMatcher) { // Set up the event LogEvent event(TAG_ID, 0); - auto list = event.GetAndroidLogEventList(); - *list << 11; - + event.write(11); event.init(); // Test @@ -168,8 +192,6 @@ TEST(LogEntryMatcherTest, TestIntComparisonMatcher) { EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); } -#if 0 - TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) { // Set up the matcher LogEntryMatcher matcher; @@ -179,22 +201,28 @@ TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) { auto keyValue = simpleMatcher->add_key_value_matcher(); keyValue->mutable_key_matcher()->set_key(FIELD_ID_1); - LogEvent event; - event.tagId = TAG_ID; - + LogEvent event1(TAG_ID, 0); keyValue->set_lt_float(10.0); - event.floatMap[FIELD_ID_1] = 10.1; - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); - event.floatMap[FIELD_ID_1] = 9.9; - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); - + event1.write(10.1f); + event1.init(); + EXPECT_FALSE(matchesSimple(*simpleMatcher, event1)); + + LogEvent event2(TAG_ID, 0); + event2.write(9.9f); + event2.init(); + EXPECT_TRUE(matchesSimple(*simpleMatcher, event2)); + + LogEvent event3(TAG_ID, 0); + event3.write(10.1f); + event3.init(); keyValue->set_gt_float(10.0); - event.floatMap[FIELD_ID_1] = 10.1; - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); - event.floatMap[FIELD_ID_1] = 9.9; - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(*simpleMatcher, event3)); + + LogEvent event4(TAG_ID, 0); + event4.write(9.9f); + event4.init(); + EXPECT_FALSE(matchesSimple(*simpleMatcher, event4)); } -#endif // Helper for the composite matchers. void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, int tag, int key, int val) { diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index c64719ee0c85..4c12b0397769 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -36,10 +36,9 @@ TEST(UidMapTest, TestIsolatedUID) { sp<UidMap> m = new UidMap(); StatsLogProcessor p(m, nullptr); LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); - android_log_event_list* list = addEvent.GetAndroidLogEventList(); - *list << 100; // parent UID - *list << 101; // isolated UID - *list << 1; // Indicates creation. + addEvent.write(100); // parent UID + addEvent.write(101); // isolated UID + addEvent.write(1); // Indicates creation. addEvent.init(); EXPECT_EQ(101, m->getParentUidOrSelf(101)); @@ -48,10 +47,9 @@ TEST(UidMapTest, TestIsolatedUID) { EXPECT_EQ(100, m->getParentUidOrSelf(101)); LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1); - list = removeEvent.GetAndroidLogEventList(); - *list << 100; // parent UID - *list << 101; // isolated UID - *list << 0; // Indicates removal. + removeEvent.write(100); // parent UID + removeEvent.write(101); // isolated UID + removeEvent.write(0); // Indicates removal. removeEvent.init(); p.OnLogEvent(removeEvent); EXPECT_EQ(101, m->getParentUidOrSelf(101)); diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index 05aad290e3fe..80a0068690f7 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -46,10 +46,9 @@ SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse, } void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) { - auto list = event->GetAndroidLogEventList(); - *list << uid; // uid - *list << wl; - *list << acquire; + event->write(uid); // uid + event->write(wl); + event->write(acquire); event->init(); } @@ -108,6 +107,18 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); EXPECT_TRUE(changedCache[0]); + // match nothing. + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + + conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache, + changedCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + EXPECT_FALSE(changedCache[0]); + // the case for match stop. matcherState.clear(); matcherState.push_back(MatchingState::kNotMatched); diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index d07a84d4c3d8..b7c9b406e0b9 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -138,15 +138,13 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { link->add_key_in_condition()->set_key(2); LogEvent event1(1, bucketStartTimeNs + 1); - auto list = event1.GetAndroidLogEventList(); - *list << "111"; // uid + event1.write("111"); // uid event1.init(); ConditionKey key1; key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|"; LogEvent event2(1, bucketStartTimeNs + 10); - auto list2 = event2.GetAndroidLogEventList(); - *list2 << "222"; // uid + event2.write("222"); // uid event2.init(); ConditionKey key2; key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|"; diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index 0971d2682d40..18d177cb9173 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -95,15 +95,13 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { link->add_key_in_condition()->set_key(2); LogEvent event1(1, bucketStartTimeNs + 1); - auto list = event1.GetAndroidLogEventList(); - *list << "111"; // uid + event1.write("111"); // uid event1.init(); ConditionKey key1; key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|"; LogEvent event2(1, bucketStartTimeNs + 10); - auto list2 = event2.GetAndroidLogEventList(); - *list2 << "222"; // uid + event2.write("222"); // uid event2.init(); ConditionKey key2; key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|"; diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 58bf1b30a9d0..9cc184a1a435 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -45,13 +45,13 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets); + MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets); tracker.noteStart("", true, bucketStartTimeNs, key1); - tracker.noteStop("", bucketStartTimeNs + 10); + tracker.noteStop("", bucketStartTimeNs + 10, false); tracker.noteStart("", true, bucketStartTimeNs + 20, key1); - tracker.noteStop("", bucketStartTimeNs + 40); + tracker.noteStop("", bucketStartTimeNs + 40, false); tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); EXPECT_EQ(1u, buckets.size()); @@ -67,7 +67,7 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(wizard, -1, bucketStartTimeNs, bucketSizeNs, buckets); + MaxDurationTracker tracker(wizard, -1, false, bucketStartTimeNs, bucketSizeNs, buckets); tracker.noteStart("", true, bucketStartTimeNs + 1, key1); tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1); @@ -77,6 +77,37 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration); } +TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + vector<DurationBucket> buckets; + ConditionKey key1; + + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + + MaxDurationTracker tracker(wizard, -1, true, bucketStartTimeNs, bucketSizeNs, buckets); + + // 2 starts + tracker.noteStart("", true, bucketStartTimeNs + 1, key1); + tracker.noteStart("", true, bucketStartTimeNs + 10, key1); + // one stop + tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/); + + tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1); + + EXPECT_EQ(2u, buckets.size()); + EXPECT_EQ((long long)(bucketSizeNs - 1), buckets[0].mDuration); + EXPECT_EQ((long long)bucketSizeNs, buckets[1].mDuration); + + // real stop now. + tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false); + tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1); + + EXPECT_EQ(3u, buckets.size()); + EXPECT_EQ(5, buckets[2].mDuration); +} + TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -93,13 +124,13 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t durationTimeNs = 2 * 1000; - MaxDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets); + MaxDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.onSlicedConditionMayChange(eventStartTimeNs + 5); - tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs); + tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); EXPECT_EQ(1u, buckets.size()); diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 74a6f11754f3..f495d6bc24b8 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -47,18 +47,43 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets); + OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl - tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs); + tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); EXPECT_EQ(1u, buckets.size()); EXPECT_EQ(durationTimeNs, buckets[0].mDuration); } +TEST(OringDurationTrackerTest, TestDurationNested) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + ConditionKey key1; + key1["APP_BACKGROUND"] = "1:maps|"; + + vector<DurationBucket> buckets; + + uint64_t bucketStartTimeNs = 10000000000; + uint64_t eventStartTimeNs = bucketStartTimeNs + 1; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + + OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets); + + tracker.noteStart("2:maps", true, eventStartTimeNs, key1); + tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl + + tracker.noteStop("2:maps", eventStartTimeNs + 2000, false); + tracker.noteStop("2:maps", eventStartTimeNs + 2003, false); + + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1u, buckets.size()); + EXPECT_EQ(2003, buckets[0].mDuration); +} + TEST(OringDurationTrackerTest, TestDurationConditionChange) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -75,18 +100,50 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(wizard, 1, bucketStartTimeNs, bucketSizeNs, buckets); + OringDurationTracker tracker(wizard, 1, false, bucketStartTimeNs, bucketSizeNs, buckets); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.onSlicedConditionMayChange(eventStartTimeNs + 5); - tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs); + tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); EXPECT_EQ(1u, buckets.size()); EXPECT_EQ(5, buckets[0].mDuration); } + +TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + ConditionKey key1; + key1["APP_BACKGROUND"] = "1:maps|"; + + EXPECT_CALL(*wizard, query(_, key1)) // #4 + .WillOnce(Return(ConditionState::kFalse)); + + vector<DurationBucket> buckets; + + uint64_t bucketStartTimeNs = 10000000000; + uint64_t eventStartTimeNs = bucketStartTimeNs + 1; + uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + + OringDurationTracker tracker(wizard, 1, true, bucketStartTimeNs, bucketSizeNs, buckets); + + tracker.noteStart("2:maps", true, eventStartTimeNs, key1); + tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1); + + tracker.noteStop("2:maps", eventStartTimeNs + 3, false); + + tracker.onSlicedConditionMayChange(eventStartTimeNs + 15); + + tracker.noteStop("2:maps", eventStartTimeNs + 2003, false); + + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); + EXPECT_EQ(1u, buckets.size()); + EXPECT_EQ(15, buckets[0].mDuration); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 72b4194d533e..cd647cb9c238 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -64,9 +64,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { vector<shared_ptr<LogEvent>> allData; allData.clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); - auto list = event->GetAndroidLogEventList(); - *list << 1; - *list << 11; + event->write(1); + event->write(11); event->init(); allData.push_back(event); @@ -89,9 +88,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { allData.clear(); event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); - list = event->GetAndroidLogEventList(); - *list << 1; - *list << 22; + event->write(1); + event->write(22); event->init(); allData.push_back(event); valueProducer.onDataPulled(allData); @@ -110,9 +108,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) { allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); - list = event->GetAndroidLogEventList(); - *list << 1; - *list << 33; + event->write(1); + event->write(33); event->init(); allData.push_back(event); valueProducer.onDataPulled(allData); @@ -159,9 +156,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; data->clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); - auto list = event->GetAndroidLogEventList(); - *list << 1; - *list << 100; + event->write(1); + event->write(100); event->init(); data->push_back(event); return true; @@ -174,9 +170,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; data->clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); - auto list = event->GetAndroidLogEventList(); - *list << 1; - *list << 120; + event->write(1); + event->write(120); event->init(); data->push_back(event); return true; @@ -201,9 +196,8 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { vector<shared_ptr<LogEvent>> allData; allData.clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); - auto list = event->GetAndroidLogEventList(); - *list << 1; - *list << 110; + event->write(1); + event->write(110); event->init(); allData.push_back(event); valueProducer.onDataPulled(allData); @@ -253,14 +247,12 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { bucketStartTimeNs, pullerManager); shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); - auto list = event1->GetAndroidLogEventList(); - *list << 1; - *list << 10; + event1->write(1); + event1->write(10); event1->init(); shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); - auto list2 = event2->GetAndroidLogEventList(); - *list2 << 1; - *list2 << 20; + event2->write(1); + event2->write(20); event2->init(); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1, false); // has one slice |