diff options
80 files changed, 4450 insertions, 1174 deletions
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index c392540882c2..a365f54bfdf0 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -20,6 +20,9 @@ statsd_common_src := \ src/stats_log.proto \ src/statsd_config.proto \ src/atoms.proto \ + src/field_util.cpp \ + src/stats_log_util.cpp \ + src/dimension.cpp \ src/anomaly/AnomalyMonitor.cpp \ src/anomaly/AnomalyTracker.cpp \ src/anomaly/DurationAnomalyTracker.cpp \ @@ -163,6 +166,7 @@ LOCAL_SRC_FILES := \ tests/indexed_priority_queue_test.cpp \ tests/LogEntryMatcher_test.cpp \ tests/LogReader_test.cpp \ + tests/LogEvent_test.cpp \ tests/MetricsManager_test.cpp \ tests/StatsLogProcessor_test.cpp \ tests/UidMap_test.cpp \ @@ -176,7 +180,10 @@ LOCAL_SRC_FILES := \ tests/metrics/ValueMetricProducer_test.cpp \ tests/metrics/GaugeMetricProducer_test.cpp \ tests/guardrail/StatsdStats_test.cpp \ - tests/metrics/metrics_test_helper.cpp + tests/metrics/metrics_test_helper.cpp \ + tests/statsd_test_util.cpp \ + tests/e2e/WakelockDuration_e2e_test.cpp \ + tests/e2e/MetricConditionLink_e2e_test.cpp LOCAL_STATIC_LIBRARIES := \ $(statsd_common_static_libraries) \ @@ -198,4 +205,4 @@ statsd_common_shared_libraries:= ############################## -include $(call all-makefiles-under,$(LOCAL_PATH)) +include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index 0b6f8f2b335d..6da12438926c 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -13,92 +13,104 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include "HashableDimensionKey.h" +#include "dimension.h" namespace android { namespace os { namespace statsd { +android::hash_t hashDimensionsValue(const DimensionsValue& value) { + android::hash_t hash = 0; + hash = android::JenkinsHashMix(hash, android::hash_type(value.field())); + + hash = android::JenkinsHashMix(hash, android::hash_type((int)value.value_case())); + switch (value.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + hash = android::JenkinsHashMix( + hash, + static_cast<uint32_t>(std::hash<std::string>()(value.value_str()))); + break; + case DimensionsValue::ValueCase::kValueInt: + hash = android::JenkinsHashMix(hash, android::hash_type(value.value_int())); + break; + case DimensionsValue::ValueCase::kValueLong: + hash = android::JenkinsHashMix( + hash, android::hash_type(static_cast<int64_t>(value.value_long()))); + break; + case DimensionsValue::ValueCase::kValueBool: + hash = android::JenkinsHashMix(hash, android::hash_type(value.value_bool())); + break; + case DimensionsValue::ValueCase::kValueFloat: { + float floatVal = value.value_float(); + hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float)); + break; + } + case DimensionsValue::ValueCase::kValueTuple: { + hash = android::JenkinsHashMix(hash, android::hash_type( + value.value_tuple().dimensions_value_size())); + for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { + hash = android::JenkinsHashMix( + hash, + hashDimensionsValue(value.value_tuple().dimensions_value(i))); + } + break; + } + case DimensionsValue::ValueCase::VALUE_NOT_SET: + break; + } + return JenkinsHashWhiten(hash); +} + using std::string; + string HashableDimensionKey::toString() const { string flattened; - for (const auto& pair : mKeyValuePairs) { - flattened += std::to_string(pair.key()); - flattened += ":"; - switch (pair.value_case()) { - case KeyValuePair::ValueCase::kValueStr: - flattened += pair.value_str(); - break; - case KeyValuePair::ValueCase::kValueInt: - flattened += std::to_string(pair.value_int()); - break; - case KeyValuePair::ValueCase::kValueLong: - flattened += std::to_string(pair.value_long()); - break; - case KeyValuePair::ValueCase::kValueBool: - flattened += std::to_string(pair.value_bool()); - break; - case KeyValuePair::ValueCase::kValueFloat: - flattened += std::to_string(pair.value_float()); - break; - default: - break; - } - flattened += "|"; - } + DimensionsValueToString(getDimensionsValue(), &flattened); return flattened; } -bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const { - const auto& keyValue2 = that.getKeyValuePairs(); - if (mKeyValuePairs.size() != keyValue2.size()) { +bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) { + if (s1.field() != s2.field()) { return false; } - - for (size_t i = 0; i < keyValue2.size(); i++) { - const auto& kv1 = mKeyValuePairs[i]; - const auto& kv2 = keyValue2[i]; - if (kv1.key() != kv2.key()) { - return false; - } - - if (kv1.value_case() != kv2.value_case()) { - return false; - } - - switch (kv1.value_case()) { - case KeyValuePair::ValueCase::kValueStr: - if (kv1.value_str() != kv2.value_str()) { - return false; - } - break; - case KeyValuePair::ValueCase::kValueInt: - if (kv1.value_int() != kv2.value_int()) { - return false; - } - break; - case KeyValuePair::ValueCase::kValueLong: - if (kv1.value_long() != kv2.value_long()) { - return false; - } - break; - case KeyValuePair::ValueCase::kValueBool: - if (kv1.value_bool() != kv2.value_bool()) { + if (s1.value_case() != s1.value_case()) { + return false; + } + switch (s1.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + return (s1.value_str() == s2.value_str()); + case DimensionsValue::ValueCase::kValueInt: + return s1.value_int() == s2.value_int(); + case DimensionsValue::ValueCase::kValueLong: + return s1.value_long() == s2.value_long(); + case DimensionsValue::ValueCase::kValueBool: + return s1.value_bool() == s2.value_bool(); + case DimensionsValue::ValueCase::kValueFloat: + return s1.value_float() == s2.value_float(); + case DimensionsValue::ValueCase::kValueTuple: + { + if (s1.value_tuple().dimensions_value_size() != + s2.value_tuple().dimensions_value_size()) { return false; } - break; - case KeyValuePair::ValueCase::kValueFloat: { - if (kv1.value_float() != kv2.value_float()) { - return false; + bool allMatched = true; + for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) { + allMatched &= compareDimensionsValue(s1.value_tuple().dimensions_value(i), + s2.value_tuple().dimensions_value(i)); } - break; + return allMatched; } - case KeyValuePair::ValueCase::VALUE_NOT_SET: - break; - } + case DimensionsValue::ValueCase::VALUE_NOT_SET: + default: + return true; } - return true; +} + +bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const { + return compareDimensionsValue(getDimensionsValue(), that.getDimensionsValue()); }; bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const { diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index 85215552b777..3a4ffce1aadd 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -25,20 +25,20 @@ namespace statsd { class HashableDimensionKey { public: - explicit HashableDimensionKey(const std::vector<KeyValuePair>& keyValuePairs) - : mKeyValuePairs(keyValuePairs){}; + explicit HashableDimensionKey(const DimensionsValue& dimensionsValue) + : mDimensionsValue(dimensionsValue){}; HashableDimensionKey(){}; HashableDimensionKey(const HashableDimensionKey& that) - : mKeyValuePairs(that.getKeyValuePairs()){}; + : mDimensionsValue(that.getDimensionsValue()){}; HashableDimensionKey& operator=(const HashableDimensionKey& from) = default; std::string toString() const; - inline const std::vector<KeyValuePair>& getKeyValuePairs() const { - return mKeyValuePairs; + inline const DimensionsValue& getDimensionsValue() const { + return mDimensionsValue; } bool operator==(const HashableDimensionKey& that) const; @@ -50,9 +50,11 @@ public: } private: - std::vector<KeyValuePair> mKeyValuePairs; + DimensionsValue mDimensionsValue; }; +android::hash_t hashDimensionsValue(const DimensionsValue& value); + } // namespace statsd } // namespace os } // namespace android @@ -60,42 +62,11 @@ private: namespace std { using android::os::statsd::HashableDimensionKey; -using android::os::statsd::KeyValuePair; template <> struct hash<HashableDimensionKey> { std::size_t operator()(const HashableDimensionKey& key) const { - android::hash_t hash = 0; - for (const auto& pair : key.getKeyValuePairs()) { - hash = android::JenkinsHashMix(hash, android::hash_type(pair.key())); - hash = android::JenkinsHashMix( - hash, android::hash_type(static_cast<int32_t>(pair.value_case()))); - switch (pair.value_case()) { - case KeyValuePair::ValueCase::kValueStr: - hash = android::JenkinsHashMix( - hash, - static_cast<uint32_t>(std::hash<std::string>()(pair.value_str()))); - break; - case KeyValuePair::ValueCase::kValueInt: - hash = android::JenkinsHashMix(hash, android::hash_type(pair.value_int())); - break; - case KeyValuePair::ValueCase::kValueLong: - hash = android::JenkinsHashMix( - hash, android::hash_type(static_cast<int64_t>(pair.value_long()))); - break; - case KeyValuePair::ValueCase::kValueBool: - hash = android::JenkinsHashMix(hash, android::hash_type(pair.value_bool())); - break; - case KeyValuePair::ValueCase::kValueFloat: { - float floatVal = pair.value_float(); - hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float)); - break; - } - case KeyValuePair::ValueCase::VALUE_NOT_SET: - break; - } - } - return hash; + return hashDimensionsValue(key.getDimensionsValue()); } }; diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index 5f9b53a88af3..13f332ed34c3 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -63,11 +63,12 @@ const int FIELD_ID_UID_MAP = 2; StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor, + const long timeBaseSec, const std::function<void(const ConfigKey&)>& sendBroadcast) : mUidMap(uidMap), mAnomalyMonitor(anomalyMonitor), mSendBroadcast(sendBroadcast), - mTimeBaseSec(time(nullptr)) { + mTimeBaseSec(timeBaseSec) { // On each initialization of StatsLogProcessor, check stats-data directory to see if there is // any left over data to be read. StorageManager::sendBroadcast(STATS_DATA_DIR, mSendBroadcast); @@ -97,7 +98,6 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { pair.second->onLogEvent(msg); flushIfNecessary(msg.GetTimestampNs(), pair.first, *(pair.second)); } - // Hard-coded logic to update the isolated uid's in the uid-map. // The field numbers need to be currently updated by hand with atoms.proto if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) { @@ -117,9 +117,7 @@ void StatsLogProcessor::OnLogEvent(const LogEvent& msg) { void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) { ALOGD("Updated configuration for key %s", key.ToString().c_str()); - sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap); - auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) { ALOGE("Can't accept more configs!"); @@ -152,6 +150,19 @@ size_t StatsLogProcessor::GetMetricsSize(const ConfigKey& key) const { return it->second->byteSize(); } +void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report) { + auto it = mMetricsManagers.find(key); + if (it == mMetricsManagers.end()) { + ALOGW("Config source %s does not exist", key.ToString().c_str()); + return; + } + report->mutable_config_key()->set_uid(key.GetUid()); + report->mutable_config_key()->set_name(key.GetName()); + ConfigMetricsReport* configMetricsReport = report->add_reports(); + it->second->onDumpReport(dumpTimeStampNs, configMetricsReport); + // TODO: dump uid mapping. +} + void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) { auto it = mMetricsManagers.find(key); if (it == mMetricsManagers.end()) { diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 1e5c426efd02..f62fc4e31c0a 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef STATS_LOG_PROCESSOR_H -#define STATS_LOG_PROCESSOR_H +#pragma once + +#include <gtest/gtest_prod.h> #include "config/ConfigListener.h" #include "logd/LogReader.h" #include "metrics/MetricsManager.h" @@ -33,6 +34,7 @@ namespace statsd { class StatsLogProcessor : public ConfigListener { public: StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor, + const long timeBaseSec, const std::function<void(const ConfigKey&)>& sendBroadcast); virtual ~StatsLogProcessor(); @@ -44,6 +46,7 @@ public: size_t GetMetricsSize(const ConfigKey& key) const; void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData); + void onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report); /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */ void onAnomalyAlarmFired( @@ -81,10 +84,12 @@ private: FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize); FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast); FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); + FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); + FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); + }; } // namespace statsd } // namespace os } // namespace android - -#endif // STATS_LOG_PROCESSOR_H diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 76e2e48a0379..e8b0bd284570 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -75,7 +75,7 @@ StatsService::StatsService(const sp<Looper>& handlerLooper) { mUidMap = new UidMap(); mConfigManager = new ConfigManager(); - mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, [this](const ConfigKey& key) { + mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) { sp<IStatsCompanionService> sc = getStatsCompanionService(); auto receiver = mConfigManager->GetConfigReceiver(key); if (sc == nullptr) { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 1c6d9b09a839..1ee86f05ca29 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -104,24 +104,18 @@ message Atom { } /** - * An attribution represents an application or module that is part of process where a particular bit - * of work is done. + * This proto represents a node of an attribution chain. + * Note: All attribution chains are represented as a repeated field of type + * AttributionNode. It is understood that in such arrays, the order is that + * of calls, that is [A, B, C] if A calls B that calls C. */ -message Attribution { - // The uid for an application or module. +message AttributionNode { + // The uid for a given element in the attribution chain. optional int32 uid = 1; - // The string tag for the attribution node. - optional string tag = 2; -} -/** - * An attribution chain represents the chained attributions of applications or modules that - * resulted in a particular bit of work being done. - * The ordering of the attributions is that of calls, that is uid = [A, B, C] if A calls B that - * calls C. - */ -message AttributionChain { - repeated Attribution attribution = 1; + // The (optional) string tag for an element in the attribution chain. If the + // element has no tag, it is encoded as an empty string. + optional string tag = 2; } /* @@ -151,7 +145,7 @@ message AttributionChain { */ message AttributionChainDummyAtom { - optional AttributionChain attribution_chain = 1; + repeated AttributionNode attribution_node = 1; optional int32 value = 2; } @@ -421,8 +415,7 @@ message CameraStateChanged { * TODO */ message WakelockStateChanged { - // TODO: Add attribution instead of uid. - optional int32 uid = 1; + repeated AttributionNode attribution_node = 1; // Type of wakelock. enum Type { @@ -780,13 +773,14 @@ message SettingChanged { * frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java */ message ActivityForegroundStateChanged { + optional int32 uid = 1; + optional string pkg_name = 2; + optional string class_name = 3; + 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; } diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp index bb4b817a820d..52b83d875caa 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp +++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp @@ -103,7 +103,7 @@ bool CombinationConditionTracker::init(const vector<Predicate>& allConditionConf } void CombinationConditionTracker::isConditionMet( - const map<string, HashableDimensionKey>& conditionParameters, + const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, vector<ConditionState>& conditionCache) const { for (const int childIndex : mChildren) { diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 93369144ecd6..894283323c78 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -41,7 +41,7 @@ public: std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache) override; - void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters, + void isConditionMet(const ConditionKey& conditionParameters, const std::vector<sp<ConditionTracker>>& allConditions, std::vector<ConditionState>& conditionCache) const override; diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 6f66ad685ccf..1154b6fed177 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -42,6 +42,8 @@ public: virtual ~ConditionTracker(){}; + inline const string& getName() { return mName; } + // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also // be done in the constructor, but we do it separately because (1) easy to return a bool to // indicate whether the initialization is successful. (2) makes unit test easier. @@ -83,7 +85,7 @@ public: // done recursively // [conditionCache]: the cache holding the condition evaluation values. virtual void isConditionMet( - const std::map<std::string, HashableDimensionKey>& conditionParameters, + const ConditionKey& conditionParameters, const std::vector<sp<ConditionTracker>>& allConditions, std::vector<ConditionState>& conditionCache) const = 0; diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp index 411f7e510c3b..d99c2ccd1fda 100644 --- a/cmds/statsd/src/condition/ConditionWizard.cpp +++ b/cmds/statsd/src/condition/ConditionWizard.cpp @@ -24,7 +24,7 @@ using std::string; using std::vector; ConditionState ConditionWizard::query(const int index, - const map<string, HashableDimensionKey>& parameters) { + const ConditionKey& parameters) { vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated); mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache); diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index 30a368412c8e..4ff5c07e210f 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -19,6 +19,7 @@ #include "ConditionTracker.h" #include "condition_util.h" +#include "stats_util.h" namespace android { namespace os { @@ -40,7 +41,7 @@ public: // the conditionParameters contains the parameters for it's children SimpleConditionTrackers. virtual ConditionState query( const int conditionIndex, - const std::map<std::string, HashableDimensionKey>& conditionParameters); + const ConditionKey& conditionParameters); private: std::vector<sp<ConditionTracker>> mAllConditions; diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp index a63bc042284a..1803cbb6f500 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp +++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp @@ -76,10 +76,9 @@ SimpleConditionTracker::SimpleConditionTracker( mStopAllLogMatcherIndex = -1; } - mOutputDimension.insert(mOutputDimension.begin(), simplePredicate.dimension().begin(), - simplePredicate.dimension().end()); + mOutputDimensions = simplePredicate.dimensions(); - if (mOutputDimension.size() > 0) { + if (mOutputDimensions.child_size() > 0) { mSliced = true; } @@ -150,6 +149,14 @@ void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& ou bool matchStart, std::vector<ConditionState>& conditionCache, std::vector<bool>& conditionChangedCache) { + if ((int)conditionChangedCache.size() <= mIndex) { + ALOGE("handleConditionEvent: param conditionChangedCache not initialized."); + return; + } + if ((int)conditionCache.size() <= mIndex) { + ALOGE("handleConditionEvent: param conditionCache not initialized."); + return; + } bool changed = false; auto outputIt = mSlicedConditionState.find(outputKey); ConditionState newCondition; @@ -278,36 +285,64 @@ void SimpleConditionTracker::evaluateCondition(const LogEvent& event, return; } - // outputKey is the output key values. e.g, uid:1234 - const HashableDimensionKey outputKey(getDimensionKey(event, mOutputDimension)); - handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache); + // outputKey is the output values. e.g, uid:1234 + const std::vector<DimensionsValue> outputValues = getDimensionKeys(event, mOutputDimensions); + if (outputValues.size() == 0) { + // The original implementation would generate an empty string dimension hash when condition + // is not sliced. + handleConditionEvent( + DEFAULT_DIMENSION_KEY, matchedState == 1, conditionCache, conditionChangedCache); + } else if (outputValues.size() == 1) { + handleConditionEvent(HashableDimensionKey(outputValues[0]), matchedState == 1, + conditionCache, conditionChangedCache); + } else { + // If this event has multiple nodes in the attribution chain, this log event probably will + // generate multiple dimensions. If so, we will find if the condition changes for any + // dimension and ask the corresponding metric producer to verify whether the actual sliced + // condition has changed or not. + // A high level assumption is that a predicate is either sliced or unsliced. We will never + // have both sliced and unsliced version of a predicate. + for (const DimensionsValue& outputValue : outputValues) { + vector<ConditionState> dimensionalConditionCache(conditionCache.size(), + ConditionState::kNotEvaluated); + vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false); + + handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1, + dimensionalConditionCache, dimensionalConditionChangedCache); + + OrConditionState(dimensionalConditionCache, &conditionCache); + OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache); + } + } } void SimpleConditionTracker::isConditionMet( - const map<string, HashableDimensionKey>& conditionParameters, + const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions, vector<ConditionState>& conditionCache) const { const auto pair = conditionParameters.find(mName); - HashableDimensionKey key = - (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second; - if (pair == conditionParameters.end() && mOutputDimension.size() > 0) { + if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) { ALOGE("Predicate %s output has dimension, but it's not specified in the query!", mName.c_str()); conditionCache[mIndex] = mInitialValue; return; } - - VLOG("simplePredicate %s query key: %s", mName.c_str(), key.c_str()); - - auto startedCountIt = mSlicedConditionState.find(key); - if (startedCountIt == mSlicedConditionState.end()) { - conditionCache[mIndex] = mInitialValue; - } else { - conditionCache[mIndex] = - startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse; + std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY}; + const std::vector<HashableDimensionKey> &keys = + (pair == conditionParameters.end()) ? defaultKeys : pair->second; + + ConditionState conditionState = ConditionState::kNotEvaluated; + for (const auto& key : keys) { + auto startedCountIt = mSlicedConditionState.find(key); + if (startedCountIt != mSlicedConditionState.end()) { + conditionState = conditionState | + (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse); + } else { + conditionState = conditionState | mInitialValue; + } } - + conditionCache[mIndex] = conditionState; VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]); } diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index 644d84c76591..50486358db66 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -46,7 +46,7 @@ public: std::vector<ConditionState>& conditionCache, std::vector<bool>& changedCache) override; - void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters, + void isConditionMet(const ConditionKey& conditionParameters, const std::vector<sp<ConditionTracker>>& allConditions, std::vector<ConditionState>& conditionCache) const override; @@ -66,7 +66,7 @@ private: ConditionState mInitialValue; - std::vector<KeyMatcher> mOutputDimension; + FieldMatcher mOutputDimensions; std::map<HashableDimensionKey, int> mSlicedConditionState; diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp index 53ef9d58b889..a5aee73ce84f 100644 --- a/cmds/statsd/src/condition/condition_util.cpp +++ b/cmds/statsd/src/condition/condition_util.cpp @@ -27,6 +27,7 @@ #include "ConditionTracker.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" +#include "dimension.h" namespace android { namespace os { @@ -93,23 +94,106 @@ ConditionState evaluateCombinationCondition(const std::vector<int>& children, return newCondition; } -HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event, - const MetricConditionLink& link) { - vector<KeyMatcher> eventKey; - eventKey.reserve(link.key_in_what().size()); +ConditionState operator|(ConditionState l, ConditionState r) { + return l >= r ? l : r; +} + +void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored) { + if (ref.size() != ored->size()) { + return; + } + for (size_t i = 0; i < ored->size(); ++i) { + ored->at(i) = ored->at(i) | ref.at(i); + } +} - for (const auto& key : link.key_in_what()) { - eventKey.push_back(key); +void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored) { + if (ref.size() != ored->size()) { + return; + } + for (size_t i = 0; i < ored->size(); ++i) { + ored->at(i) = ored->at(i) | ref.at(i); + } +} + +void getFieldsFromFieldMatcher(const FieldMatcher& matcher, const Field& parentField, + std::vector<Field> *allFields) { + Field newParent = parentField; + Field* leaf = getSingleLeaf(&newParent); + leaf->set_field(matcher.field()); + if (matcher.child_size() == 0) { + allFields->push_back(newParent); + return; + } + for (int i = 0; i < matcher.child_size(); ++i) { + leaf->add_child(); + getFieldsFromFieldMatcher(matcher.child(i), newParent, allFields); } +} + +void getFieldsFromFieldMatcher(const FieldMatcher& matcher, std::vector<Field> *allFields) { + Field parentField; + getFieldsFromFieldMatcher(matcher, parentField, allFields); +} - vector<KeyValuePair> dimensionKey = getDimensionKey(event, eventKey); +void flattenValueLeaves(const DimensionsValue& value, + std::vector<DimensionsValue> *allLaves) { + switch (value.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + case DimensionsValue::ValueCase::kValueInt: + case DimensionsValue::ValueCase::kValueLong: + case DimensionsValue::ValueCase::kValueBool: + case DimensionsValue::ValueCase::kValueFloat: + case DimensionsValue::ValueCase::VALUE_NOT_SET: + allLaves->push_back(value); + break; + case DimensionsValue::ValueCase::kValueTuple: + for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { + flattenValueLeaves(value.value_tuple().dimensions_value(i), allLaves); + } + break; + } +} - for (int i = 0; i < link.key_in_what_size(); i++) { - auto& kv = dimensionKey[i]; - kv.set_key(link.key_in_condition(i).key()); +std::vector<HashableDimensionKey> getDimensionKeysForCondition( + const LogEvent& event, const MetricConditionLink& link) { + std::vector<Field> whatFields; + getFieldsFromFieldMatcher(link.dimensions_in_what(), &whatFields); + std::vector<Field> conditionFields; + getFieldsFromFieldMatcher(link.dimensions_in_condition(), &conditionFields); + + std::vector<HashableDimensionKey> hashableDimensionKeys; + + // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and + // directly construct the full condition value tree. + std::vector<DimensionsValue> whatValues = getDimensionKeys(event, link.dimensions_in_what()); + + for (size_t i = 0; i < whatValues.size(); ++i) { + std::vector<DimensionsValue> whatLeaves; + flattenValueLeaves(whatValues[i], &whatLeaves); + if (whatLeaves.size() != whatFields.size() || + whatLeaves.size() != conditionFields.size()) { + ALOGE("Dimensions between what and condition not equal."); + return hashableDimensionKeys; + } + FieldValueMap conditionValueMap; + for (size_t j = 0; j < whatLeaves.size(); ++j) { + if (!setFieldInLeafValueProto(conditionFields[j], &whatLeaves[j])) { + ALOGE("Not able to reset the field for condition leaf value."); + return hashableDimensionKeys; + } + conditionValueMap.insert(std::make_pair(conditionFields[j], whatLeaves[j])); + } + std::vector<DimensionsValue> conditionValues; + findDimensionsValues(conditionValueMap, link.dimensions_in_condition(), &conditionValues); + if (conditionValues.size() != 1) { + ALOGE("Not able to find unambiguous field value in condition atom."); + continue; + } + hashableDimensionKeys.push_back(HashableDimensionKey(conditionValues[0])); } - return HashableDimensionKey(dimensionKey); + return hashableDimensionKeys; } } // namespace statsd diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h index 934c2076ddfa..598027b7e366 100644 --- a/cmds/statsd/src/condition/condition_util.h +++ b/cmds/statsd/src/condition/condition_util.h @@ -32,12 +32,16 @@ enum ConditionState { kTrue = 1, }; +ConditionState operator|(ConditionState l, ConditionState r); +void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored); +void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored); + ConditionState evaluateCombinationCondition(const std::vector<int>& children, const LogicalOperation& operation, const std::vector<ConditionState>& conditionCache); -HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event, - const MetricConditionLink& link); +std::vector<HashableDimensionKey> getDimensionKeysForCondition( + const LogEvent& event, const MetricConditionLink& link); } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp index cb3f3d634695..addc1111a93d 100644 --- a/cmds/statsd/src/config/ConfigManager.cpp +++ b/cmds/statsd/src/config/ConfigManager.cpp @@ -209,7 +209,7 @@ StatsdConfig build_fake_config() { int WAKE_LOCK_ACQUIRE_VALUE = 1; int WAKE_LOCK_RELEASE_VALUE = 0; - int APP_USAGE_ID = 12345; + int APP_USAGE_TAG_ID = 12345; int APP_USAGE_UID_KEY_ID = 1; int APP_USAGE_STATE_KEY = 2; int APP_USAGE_FOREGROUND = 1; @@ -259,8 +259,9 @@ StatsdConfig build_fake_config() { metric->set_name("METRIC_2"); metric->set_what("PROCESS_STATE_CHANGE"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - KeyMatcher* keyMatcher = metric->add_dimension(); - keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY); + FieldMatcher* dimensions = metric->mutable_dimensions(); + dimensions->set_field(UID_PROCESS_STATE_TAG_ID); + dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY); // Anomaly threshold for background count. // TODO(b/70627390): Uncomment once the bug is fixed. @@ -280,8 +281,10 @@ StatsdConfig build_fake_config() { metric->set_name("METRIC_3"); metric->set_what("PROCESS_STATE_CHANGE"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - keyMatcher = metric->add_dimension(); - keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY); + + dimensions = metric->mutable_dimensions(); + dimensions->set_field(UID_PROCESS_STATE_TAG_ID); + dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY); metric->set_condition("SCREEN_IS_OFF"); // Count wake lock, slice by uid, while SCREEN_IS_ON and app in background @@ -289,41 +292,52 @@ StatsdConfig build_fake_config() { metric->set_name("METRIC_4"); metric->set_what("APP_GET_WL"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - keyMatcher = metric->add_dimension(); - keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID); + dimensions = metric->mutable_dimensions(); + dimensions->set_field(WAKE_LOCK_TAG_ID); + dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + + metric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); MetricConditionLink* link = metric->add_links(); link->set_condition("APP_IS_BACKGROUND"); - link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID); - link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID); + link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); + link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); + link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); // Duration of an app holding any wl, while screen on and app in background, slice by uid DurationMetric* durationMetric = config.add_duration_metric(); durationMetric->set_name("METRIC_5"); durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM); - keyMatcher = durationMetric->add_dimension(); - keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID); + dimensions = durationMetric->mutable_dimensions(); + dimensions->set_field(WAKE_LOCK_TAG_ID); + dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); durationMetric->set_what("WL_HELD_PER_APP_PER_NAME"); durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); link = durationMetric->add_links(); link->set_condition("APP_IS_BACKGROUND"); - link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID); - link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID); + link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); + link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); + link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); // max Duration of an app holding any wl, while screen on and app in background, slice by uid durationMetric = config.add_duration_metric(); durationMetric->set_name("METRIC_6"); durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); - keyMatcher = durationMetric->add_dimension(); - keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID); + dimensions = durationMetric->mutable_dimensions(); + dimensions->set_field(WAKE_LOCK_TAG_ID); + dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); durationMetric->set_what("WL_HELD_PER_APP_PER_NAME"); durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); link = durationMetric->add_links(); link->set_condition("APP_IS_BACKGROUND"); - link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID); - link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID); + link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); + link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); + link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); // Duration of an app holding any wl, while screen on and app in background durationMetric = config.add_duration_metric(); @@ -334,8 +348,11 @@ StatsdConfig build_fake_config() { durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON"); link = durationMetric->add_links(); link->set_condition("APP_IS_BACKGROUND"); - link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID); - link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID); + link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID); + link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID); + link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID); + // Duration of screen on time. durationMetric = config.add_duration_metric(); @@ -362,8 +379,9 @@ StatsdConfig build_fake_config() { valueMetric->set_what("KERNEL_WAKELOCK"); valueMetric->set_value_field(KERNEL_WAKELOCK_COUNT_KEY); valueMetric->set_condition("SCREEN_IS_ON"); - keyMatcher = valueMetric->add_dimension(); - keyMatcher->set_key(KERNEL_WAKELOCK_NAME_KEY); + dimensions = valueMetric->mutable_dimensions(); + dimensions->set_field(KERNEL_WAKELOCK_TAG_ID); + dimensions->add_child()->set_field(KERNEL_WAKELOCK_NAME_KEY); // This is for testing easier. We should never set bucket size this small. valueMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L); @@ -376,73 +394,75 @@ StatsdConfig build_fake_config() { GaugeMetric* gaugeMetric = config.add_gauge_metric(); gaugeMetric->set_name("METRIC_10"); gaugeMetric->set_what("DEVICE_TEMPERATURE"); - gaugeMetric->mutable_gauge_fields()->add_field_num(DEVICE_TEMPERATURE_KEY); + auto gaugeFieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(DEVICE_TEMPERATURE_TAG_ID); + gaugeFieldMatcher->add_child()->set_field(DEVICE_TEMPERATURE_KEY); gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L); - // Event matchers............ + // Event matchers. AtomMatcher* temperatureAtomMatcher = config.add_atom_matcher(); temperatureAtomMatcher->set_name("DEVICE_TEMPERATURE"); - temperatureAtomMatcher->mutable_simple_atom_matcher()->set_tag( + temperatureAtomMatcher->mutable_simple_atom_matcher()->set_atom_id( DEVICE_TEMPERATURE_TAG_ID); AtomMatcher* eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("SCREEN_TURNED_ON"); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(SCREEN_EVENT_TAG_ID); - KeyValueMatcher* keyValueMatcher = simpleAtomMatcher->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key(SCREEN_EVENT_STATE_KEY); - keyValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE); + simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID); + FieldValueMatcher* fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); + fieldValueMatcher->set_field(SCREEN_EVENT_STATE_KEY); + fieldValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("SCREEN_TURNED_OFF"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(SCREEN_EVENT_TAG_ID); - keyValueMatcher = simpleAtomMatcher->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key(SCREEN_EVENT_STATE_KEY); - keyValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE); + simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID); + fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); + fieldValueMatcher->set_field(SCREEN_EVENT_STATE_KEY); + fieldValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("PROCESS_STATE_CHANGE"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(UID_PROCESS_STATE_TAG_ID); + simpleAtomMatcher->set_atom_id(UID_PROCESS_STATE_TAG_ID); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("APP_GOES_BACKGROUND"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(APP_USAGE_ID); - keyValueMatcher = simpleAtomMatcher->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key(APP_USAGE_STATE_KEY); - keyValueMatcher->set_eq_int(APP_USAGE_BACKGROUND); + simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID); + fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); + fieldValueMatcher->set_field(APP_USAGE_STATE_KEY); + fieldValueMatcher->set_eq_int(APP_USAGE_BACKGROUND); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("APP_GOES_FOREGROUND"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(APP_USAGE_ID); - keyValueMatcher = simpleAtomMatcher->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key(APP_USAGE_STATE_KEY); - keyValueMatcher->set_eq_int(APP_USAGE_FOREGROUND); + simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID); + fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); + fieldValueMatcher->set_field(APP_USAGE_STATE_KEY); + fieldValueMatcher->set_eq_int(APP_USAGE_FOREGROUND); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("APP_GET_WL"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(WAKE_LOCK_TAG_ID); - keyValueMatcher = simpleAtomMatcher->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key(WAKE_LOCK_STATE_KEY); - keyValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE); + simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID); + fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); + fieldValueMatcher->set_field(WAKE_LOCK_STATE_KEY); + fieldValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("APP_RELEASE_WL"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(WAKE_LOCK_TAG_ID); - keyValueMatcher = simpleAtomMatcher->add_key_value_matcher(); - keyValueMatcher->mutable_key_matcher()->set_key(WAKE_LOCK_STATE_KEY); - keyValueMatcher->set_eq_int(WAKE_LOCK_RELEASE_VALUE); + simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID); + fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher(); + fieldValueMatcher->set_field(WAKE_LOCK_STATE_KEY); + fieldValueMatcher->set_eq_int(WAKE_LOCK_RELEASE_VALUE); // pulled events eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("KERNEL_WAKELOCK"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(KERNEL_WAKELOCK_TAG_ID); + simpleAtomMatcher->set_atom_id(KERNEL_WAKELOCK_TAG_ID); // Predicates............. Predicate* predicate = config.add_predicate(); @@ -464,8 +484,9 @@ StatsdConfig build_fake_config() { simplePredicate = predicate->mutable_simple_predicate(); simplePredicate->set_start("APP_GOES_BACKGROUND"); simplePredicate->set_stop("APP_GOES_FOREGROUND"); - KeyMatcher* predicate_dimension1 = simplePredicate->add_dimension(); - predicate_dimension1->set_key(APP_USAGE_UID_KEY_ID); + FieldMatcher* predicate_dimension1 = simplePredicate->mutable_dimensions(); + predicate_dimension1->set_field(APP_USAGE_TAG_ID); + predicate_dimension1->add_child()->set_field(APP_USAGE_UID_KEY_ID); simplePredicate->set_count_nesting(false); predicate = config.add_predicate(); @@ -480,10 +501,10 @@ StatsdConfig build_fake_config() { simplePredicate = predicate->mutable_simple_predicate(); simplePredicate->set_start("APP_GET_WL"); simplePredicate->set_stop("APP_RELEASE_WL"); - KeyMatcher* predicate_dimension = simplePredicate->add_dimension(); - predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID); - predicate_dimension = simplePredicate->add_dimension(); - predicate_dimension->set_key(WAKE_LOCK_NAME_KEY); + FieldMatcher* predicate_dimension = simplePredicate->mutable_dimensions(); + predicate_dimension1->set_field(WAKE_LOCK_TAG_ID); + predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); + predicate_dimension->add_child()->set_field(WAKE_LOCK_NAME_KEY); simplePredicate->set_count_nesting(true); predicate = config.add_predicate(); @@ -492,8 +513,9 @@ StatsdConfig build_fake_config() { simplePredicate->set_start("APP_GET_WL"); simplePredicate->set_stop("APP_RELEASE_WL"); simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE); - predicate_dimension = simplePredicate->add_dimension(); - predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID); + predicate_dimension = simplePredicate->mutable_dimensions(); + predicate_dimension->set_field(WAKE_LOCK_TAG_ID); + predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID); simplePredicate->set_count_nesting(true); return config; diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp new file mode 100644 index 000000000000..886a33bf540f --- /dev/null +++ b/cmds/statsd/src/dimension.cpp @@ -0,0 +1,356 @@ +/* + * 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 "Log.h" + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "dimension.h" +#include "field_util.h" + + +namespace android { +namespace os { +namespace statsd { + +const DimensionsValue* getSingleLeafValue(const DimensionsValue* value) { + if (value->value_case() == DimensionsValue::ValueCase::kValueTuple) { + return getSingleLeafValue(&value->value_tuple().dimensions_value(0)); + } else { + return value; + } +} + +DimensionsValue getSingleLeafValue(const DimensionsValue& value) { + const DimensionsValue* leafValue = getSingleLeafValue(&value); + return *leafValue; +} + +void appendLeafNodeToParent(const Field& field, + const DimensionsValue& value, + DimensionsValue* parentValue) { + if (field.child_size() <= 0) { + *parentValue = value; + parentValue->set_field(field.field()); + return; + } + parentValue->set_field(field.field()); + int idx = -1; + for (int i = 0; i < parentValue->mutable_value_tuple()->dimensions_value_size(); ++i) { + if (parentValue->mutable_value_tuple()->dimensions_value(i).field() == + field.child(0).field()) { + idx = i; + } + } + if (idx < 0) { + parentValue->mutable_value_tuple()->add_dimensions_value(); + idx = parentValue->mutable_value_tuple()->dimensions_value_size() - 1; + } + appendLeafNodeToParent( + field.child(0), value, + parentValue->mutable_value_tuple()->mutable_dimensions_value(idx)); +} + +void addNodeToRootDimensionsValues(const Field& field, + const DimensionsValue& node, + std::vector<DimensionsValue>* rootValues) { + if (rootValues == nullptr) { + return; + } + if (rootValues->empty()) { + DimensionsValue rootValue; + appendLeafNodeToParent(field, node, &rootValue); + rootValues->push_back(rootValue); + } else { + for (size_t i = 0; i < rootValues->size(); ++i) { + appendLeafNodeToParent(field, node, &rootValues->at(i)); + } + } +} + +namespace { + +void findDimensionsValues( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + const Field& field, + std::vector<DimensionsValue>* rootDimensionsValues); + +void findNonRepeatedDimensionsValues( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + const Field& field, + std::vector<DimensionsValue>* rootValues) { + if (matcher.child_size() > 0) { + for (const auto& childMatcher : matcher.child()) { + Field childField = field; + appendLeaf(&childField, childMatcher.field()); + findDimensionsValues(fieldValueMap, childMatcher, childField, rootValues); + } + } else { + auto ret = fieldValueMap.equal_range(field); + int found = 0; + for (auto it = ret.first; it != ret.second; ++it) { + found++; + } + // Not found. + if (found <= 0) { + return; + } + if (found > 1) { + ALOGE("Found multiple values for optional field."); + return; + } + addNodeToRootDimensionsValues(field, ret.first->second, rootValues); + } +} + +void findRepeatedDimensionsValues(const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + const Field& field, + std::vector<DimensionsValue>* rootValues) { + if (matcher.position() == Position::FIRST) { + Field first_field = field; + setPositionForLeaf(&first_field, 0); + findNonRepeatedDimensionsValues(fieldValueMap, matcher, first_field, rootValues); + } else { + auto itLower = fieldValueMap.lower_bound(field); + if (itLower == fieldValueMap.end()) { + return; + } + Field next_field = field; + getNextField(&next_field); + auto itUpper = fieldValueMap.lower_bound(next_field); + + switch (matcher.position()) { + case Position::LAST: + { + itUpper--; + if (itUpper != fieldValueMap.end()) { + Field last_field = field; + int last_index = getPositionByReferenceField(field, itUpper->first); + if (last_index < 0) { + return; + } + setPositionForLeaf(&last_field, last_index); + findNonRepeatedDimensionsValues( + fieldValueMap, matcher, last_field, rootValues); + } + } + break; + case Position::ANY: + { + std::set<int> indexes; + for (auto it = itLower; it != itUpper; ++it) { + int index = getPositionByReferenceField(field, it->first); + if (index >= 0) { + indexes.insert(index); + } + } + if (!indexes.empty()) { + Field any_field = field; + std::vector<DimensionsValue> allValues; + for (const int index : indexes) { + setPositionForLeaf(&any_field, index); + std::vector<DimensionsValue> newValues = *rootValues; + findNonRepeatedDimensionsValues( + fieldValueMap, matcher, any_field, &newValues); + allValues.insert(allValues.end(), newValues.begin(), newValues.end()); + } + rootValues->clear(); + rootValues->insert(rootValues->end(), allValues.begin(), allValues.end()); + } + } + break; + default: + break; + } + } +} + +void findDimensionsValues( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + const Field& field, + std::vector<DimensionsValue>* rootDimensionsValues) { + if (!matcher.has_position()) { + findNonRepeatedDimensionsValues(fieldValueMap, matcher, field, rootDimensionsValues); + } else { + findRepeatedDimensionsValues(fieldValueMap, matcher, field, rootDimensionsValues); + } +} + +} // namespace + +void findDimensionsValues( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + std::vector<DimensionsValue>* rootDimensionsValues) { + findDimensionsValues(fieldValueMap, matcher, + buildSimpleAtomField(matcher.field()), rootDimensionsValues); +} + +FieldMatcher buildSimpleAtomFieldMatcher(const int tagId) { + FieldMatcher matcher; + matcher.set_field(tagId); + return matcher; +} + +FieldMatcher buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum) { + FieldMatcher matcher; + matcher.set_field(tagId); + matcher.add_child()->set_field(atomFieldNum); + return matcher; +} + +constexpr int ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO = 1; +constexpr int UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 1; +constexpr int TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 2; + +void buildAttributionUidFieldMatcher(const int tagId, const Position position, + FieldMatcher *matcher) { + matcher->set_field(tagId); + matcher->add_child()->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); + FieldMatcher* child = matcher->mutable_child(0)->add_child(); + child->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); +} + +void buildAttributionTagFieldMatcher(const int tagId, const Position position, + FieldMatcher *matcher) { + matcher->set_field(tagId); + FieldMatcher* child = matcher->add_child(); + child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); + child->set_position(position); + child = child->add_child(); + child->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); +} + +void buildAttributionFieldMatcher(const int tagId, const Position position, + FieldMatcher *matcher) { + matcher->set_field(tagId); + FieldMatcher* child = matcher->add_child(); + child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO); + child->set_position(position); + child = child->add_child(); + child->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); + child->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO); +} + +void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) { + *flattened += std::to_string(value.field()); + *flattened += ":"; + switch (value.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + *flattened += value.value_str(); + break; + case DimensionsValue::ValueCase::kValueInt: + *flattened += std::to_string(value.value_int()); + break; + case DimensionsValue::ValueCase::kValueLong: + *flattened += std::to_string(value.value_long()); + break; + case DimensionsValue::ValueCase::kValueBool: + *flattened += std::to_string(value.value_bool()); + break; + case DimensionsValue::ValueCase::kValueFloat: + *flattened += std::to_string(value.value_float()); + break; + case DimensionsValue::ValueCase::kValueTuple: + { + *flattened += "{"; + for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { + DimensionsValueToString(value.value_tuple().dimensions_value(i), flattened); + *flattened += "|"; + } + *flattened += "}"; + } + break; + case DimensionsValue::ValueCase::VALUE_NOT_SET: + break; + } +} + +void getDimensionsValueLeafNodes( + const DimensionsValue& value, std::vector<DimensionsValue> *leafNodes) { + switch (value.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + case DimensionsValue::ValueCase::kValueInt: + case DimensionsValue::ValueCase::kValueLong: + case DimensionsValue::ValueCase::kValueBool: + case DimensionsValue::ValueCase::kValueFloat: + leafNodes->push_back(value); + break; + case DimensionsValue::ValueCase::kValueTuple: + for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) { + getDimensionsValueLeafNodes(value.value_tuple().dimensions_value(i), leafNodes); + } + break; + case DimensionsValue::ValueCase::VALUE_NOT_SET: + break; + default: + break; + } +} + +std::string DimensionsValueToString(const DimensionsValue& value) { + std::string flatten; + DimensionsValueToString(value, &flatten); + return flatten; +} + +bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub) { + if (dimension.field() != sub.field()) { + return false; + } + if (dimension.value_case() != sub.value_case()) { + return false; + } + switch (dimension.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + return dimension.value_str() == sub.value_str(); + case DimensionsValue::ValueCase::kValueInt: + return dimension.value_int() == sub.value_int(); + case DimensionsValue::ValueCase::kValueLong: + return dimension.value_long() == sub.value_long(); + case DimensionsValue::ValueCase::kValueBool: + return dimension.value_bool() == sub.value_bool(); + case DimensionsValue::ValueCase::kValueFloat: + return dimension.value_float() == sub.value_float(); + case DimensionsValue::ValueCase::kValueTuple: { + if (dimension.value_tuple().dimensions_value_size() < sub.value_tuple().dimensions_value_size()) { + return false; + } + bool allSub = true; + for (int i = 0; i < sub.value_tuple().dimensions_value_size(); ++i) { + bool isSub = false; + for (int j = 0; !isSub && j < dimension.value_tuple().dimensions_value_size(); ++j) { + isSub |= IsSubDimension(dimension.value_tuple().dimensions_value(j), + sub.value_tuple().dimensions_value(i)); + } + allSub &= isSub; + } + return allSub; + } + break; + case DimensionsValue::ValueCase::VALUE_NOT_SET: + return false; + default: + return false; + } +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h new file mode 100644 index 000000000000..c866958fdaae --- /dev/null +++ b/cmds/statsd/src/dimension.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#pragma once + +#include <log/logprint.h> +#include <set> +#include <vector> +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "field_util.h" + +namespace android { +namespace os { +namespace statsd { + + +// Returns the leaf node from the DimensionsValue proto. It assume that the input has only one +// leaf node at most. +const DimensionsValue* getSingleLeafValue(const DimensionsValue* value); +DimensionsValue getSingleLeafValue(const DimensionsValue& value); + +// Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto +// represents a tree. When the input proto has repeated fields and the input "dimensions" wants +// "ANY" locations, it will return multiple trees. +void findDimensionsValues( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + std::vector<DimensionsValue>* rootDimensionsValues); + +// Utils to build FieldMatcher proto for simple one-depth atoms. +FieldMatcher buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum); +FieldMatcher buildSimpleAtomFieldMatcher(const int tagId); + +// Utils to build FieldMatcher proto for attribution nodes. +FieldMatcher buildAttributionUidFieldMatcher(const int tagId, const Position position); +FieldMatcher buildAttributionTagFieldMatcher(const int tagId, const Position position); +FieldMatcher buildAttributionFieldMatcher(const int tagId, const Position position); + +// Utils to print pretty string for DimensionsValue proto. +std::string DimensionsValueToString(const DimensionsValue& value); +void DimensionsValueToString(const DimensionsValue& value, std::string *flattened); + +bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/field_util.cpp b/cmds/statsd/src/field_util.cpp new file mode 100644 index 000000000000..d10e1670ddf6 --- /dev/null +++ b/cmds/statsd/src/field_util.cpp @@ -0,0 +1,318 @@ +/* + * 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 "Log.h" +#include "field_util.h" + +#include <set> +#include <vector> + +namespace android { +namespace os { +namespace statsd { + +// This function is to compare two Field trees where each node has at most one child. +bool CompareField(const Field& a, const Field& b) { + if (a.field() < b.field()) { + return true; + } + if (a.field() > b.field()) { + return false; + } + if (a.position_index() < b.position_index()) { + return true; + } + if (a.position_index() > b.position_index()) { + return false; + } + if (a.child_size() < b.child_size()) { + return true; + } + if (a.child_size() > b.child_size()) { + return false; + } + if (a.child_size() == 0 && b.child_size() == 0) { + return false; + } + return CompareField(a.child(0), b.child(0)); +} + +const Field* getSingleLeaf(const Field* field) { + if (field->child_size() <= 0) { + return field; + } else { + return getSingleLeaf(&field->child(0)); + } +} + +Field* getSingleLeaf(Field* field) { + if (field->child_size() <= 0) { + return field; + } else { + return getSingleLeaf(field->mutable_child(0)); + } +} + +void FieldToString(const Field& field, std::string *flattened) { + *flattened += std::to_string(field.field()); + if (field.has_position_index()) { + *flattened += "["; + *flattened += std::to_string(field.position_index()); + *flattened += "]"; + } + if (field.child_size() <= 0) { + return; + } + *flattened += "."; + *flattened += "{"; + for (int i = 0 ; i < field.child_size(); ++i) { + *flattened += FieldToString(field.child(i)); + } + *flattened += "},"; +} + +std::string FieldToString(const Field& field) { + std::string flatten; + FieldToString(field, &flatten); + return flatten; +} + +bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue) { + if (field.child_size() <= 0) { + leafValue->set_field(field.field()); + return true; + } else if (field.child_size() == 1) { + return setFieldInLeafValueProto(field.child(0), leafValue); + } else { + ALOGE("Not able to set the 'field' in leaf value for multiple children."); + return false; + } +} + +Field buildAtomField(const int tagId, const Field &atomField) { + Field field; + *field.add_child() = atomField; + field.set_field(tagId); + return field; +} + +Field buildSimpleAtomField(const int tagId, const int atomFieldNum) { + Field field; + field.set_field(tagId); + field.add_child()->set_field(atomFieldNum); + return field; +} + +Field buildSimpleAtomField(const int tagId) { + Field field; + field.set_field(tagId); + return field; +} + +void appendLeaf(Field *parent, int node_field_num) { + if (!parent->has_field()) { + parent->set_field(node_field_num); + } else if (parent->child_size() <= 0) { + parent->add_child()->set_field(node_field_num); + } else { + appendLeaf(parent->mutable_child(0), node_field_num); + } +} + +void appendLeaf(Field *parent, int node_field_num, int position) { + if (!parent->has_field()) { + parent->set_field(node_field_num); + parent->set_position_index(position); + } else if (parent->child_size() <= 0) { + auto child = parent->add_child(); + child->set_field(node_field_num); + child->set_position_index(position); + } else { + appendLeaf(parent->mutable_child(0), node_field_num, position); + } +} + + +void getNextField(Field* field) { + if (field->child_size() <= 0) { + field->set_field(field->field() + 1); + return; + } + if (field->child_size() != 1) { + return; + } + getNextField(field->mutable_child(0)); +} + +void increasePosition(Field *field) { + if (!field->has_position_index()) { + field->set_position_index(0); + } else { + field->set_position_index(field->position_index() + 1); + } +} + +int getPositionByReferenceField(const Field& ref, const Field& field_with_index) { + if (ref.child_size() <= 0) { + return field_with_index.position_index(); + } + if (ref.child_size() != 1 || + field_with_index.child_size() != 1) { + return -1; + } + return getPositionByReferenceField(ref.child(0), field_with_index.child(0)); +} + +void setPositionForLeaf(Field *field, int index) { + if (field->child_size() <= 0) { + field->set_position_index(index); + } else { + setPositionForLeaf(field->mutable_child(0), index); + } +} + +void findFields( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + const Field& field, + std::vector<Field>* rootFields); + +void findNonRepeatedFields( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + const Field& field, + std::vector<Field>* rootFields) { + if (matcher.child_size() > 0) { + for (const auto& childMatcher : matcher.child()) { + Field childField = field; + appendLeaf(&childField, childMatcher.field()); + findFields(fieldValueMap, childMatcher, childField, rootFields); + } + } else { + auto ret = fieldValueMap.equal_range(field); + int found = 0; + for (auto it = ret.first; it != ret.second; ++it) { + found++; + } + // Not found. + if (found <= 0) { + return; + } + if (found > 1) { + ALOGE("Found multiple values for optional field."); + return; + } + rootFields->push_back(ret.first->first); + } +} + +void findRepeatedFields(const FieldValueMap& fieldValueMap, const FieldMatcher& matcher, + const Field& field, std::vector<Field>* rootFields) { + if (matcher.position() == Position::FIRST) { + Field first_field = field; + setPositionForLeaf(&first_field, 0); + findNonRepeatedFields(fieldValueMap, matcher, first_field, rootFields); + } else { + auto itLower = fieldValueMap.lower_bound(field); + if (itLower == fieldValueMap.end()) { + return; + } + Field next_field = field; + getNextField(&next_field); + auto itUpper = fieldValueMap.lower_bound(next_field); + + switch (matcher.position()) { + case Position::LAST: + { + itUpper--; + if (itUpper != fieldValueMap.end()) { + Field last_field = field; + int last_index = getPositionByReferenceField(field, itUpper->first); + if (last_index < 0) { + return; + } + setPositionForLeaf(&last_field, last_index); + findNonRepeatedFields( + fieldValueMap, matcher, last_field, rootFields); + } + } + break; + case Position::ANY: + { + std::set<int> indexes; + for (auto it = itLower; it != itUpper; ++it) { + int index = getPositionByReferenceField(field, it->first); + if (index >= 0) { + indexes.insert(index); + } + } + if (!indexes.empty()) { + Field any_field = field; + for (const int index : indexes) { + setPositionForLeaf(&any_field, index); + findNonRepeatedFields( + fieldValueMap, matcher, any_field, rootFields); + } + } + } + break; + default: + break; + } + } +} + +void findFields( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + const Field& field, + std::vector<Field>* rootFields) { + if (!matcher.has_position()) { + findNonRepeatedFields(fieldValueMap, matcher, field, rootFields); + } else { + findRepeatedFields(fieldValueMap, matcher, field, rootFields); + } +} + +void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap) { + std::vector<Field> rootFields; + findFields(*fieldValueMap, matcher, buildSimpleAtomField(matcher.field()), &rootFields); + std::set<Field, FieldCmp> rootFieldSet(rootFields.begin(), rootFields.end()); + auto it = fieldValueMap->begin(); + while (it != fieldValueMap->end()) { + if (rootFieldSet.find(it->first) == rootFieldSet.end()) { + it = fieldValueMap->erase(it); + } else { + it++; + } + } +} + +bool hasLeafNode(const FieldMatcher& matcher) { + if (!matcher.has_field()) { + return false; + } + for (int i = 0; i < matcher.child_size(); ++i) { + if (hasLeafNode(matcher.child(i))) { + return true; + } + } + return true; +} + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/field_util.h b/cmds/statsd/src/field_util.h new file mode 100644 index 000000000000..5907e17d666b --- /dev/null +++ b/cmds/statsd/src/field_util.h @@ -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. + */ + +#pragma once + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" + +#include <unordered_map> + +namespace android { +namespace os { +namespace statsd { + +// Function to sort the Field protos. +bool CompareField(const Field& a, const Field& b); +struct FieldCmp { + bool operator()(const Field& a, const Field& b) const { + return CompareField(a, b); + } +}; + +// Flattened dimensions value map. To save space, usually the key contains the tree structure info +// and value field is only leaf node. +typedef std::map<Field, DimensionsValue, FieldCmp> FieldValueMap; + +// Util function to print the Field proto. +std::string FieldToString(const Field& field); + +// Util function to find the leaf node from the input Field proto and set it in the corresponding +// value proto. +bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue); + +// Returns the leaf node from the Field proto. It assume that the input has only one +// leaf node at most. +const Field* getSingleLeaf(const Field* field); +Field* getSingleLeaf(Field* field); + +// Append a node to the current leaf. It assumes that the input "parent" has one leaf node at most. +void appendLeaf(Field *parent, int node_field_num); +void appendLeaf(Field *parent, int node_field_num, int position); + +// Given the field sorting logic, this function is to increase the "field" at the leaf node. +void getNextField(Field* field); + +// Increase the position index for the node. If the "position_index" is not set, set it as 0. +void increasePosition(Field *field); + +// Finds the leaf node and set the index there. +void setPositionForLeaf(Field *field, int index); + +// Returns true if the matcher has specified at least one leaf node. +bool hasLeafNode(const FieldMatcher& matcher); + +// The two input Field proto are describing the same tree structure. Both contain one leaf node at +// most. This is find the position index info for the leaf node at "reference" stored in the +// "field_with_index" tree. +int getPositionByReferenceField(const Field& reference, const Field& field_with_index); + +// Utils to build the Field proto for simple atom fields. +Field buildAtomField(const int tagId, const Field &atomField); +Field buildSimpleAtomField(const int tagId, const int atomFieldNum); +Field buildSimpleAtomField(const int tagId); + +// Find out all the fields specified by the matcher. +void findFields( + const FieldValueMap& fieldValueMap, + const FieldMatcher& matcher, + std::vector<Field>* rootFields); + +// Filter out the fields not in the field matcher. +void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap); + +} // namespace statsd +} // namespace os +} // namespace android diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp index d660b5f9fe02..49a6e330c590 100644 --- a/cmds/statsd/src/logd/LogEvent.cpp +++ b/cmds/statsd/src/logd/LogEvent.cpp @@ -17,8 +17,14 @@ #define DEBUG true // STOPSHIP if true #include "logd/LogEvent.h" +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" + +#include <set> #include <sstream> -#include "stats_util.h" + +#include "field_util.h" +#include "dimension.h" +#include "stats_log_util.h" namespace android { namespace os { @@ -30,16 +36,20 @@ using std::string; using android::util::ProtoOutputStream; LogEvent::LogEvent(log_msg& msg) { - mContext = + android_log_context context = 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; mLogUid = msg.entry_v4.uid; - init(mContext); + init(context); + if (context) { + android_log_destroy(&context); + } } LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) { mTimestampNs = timestampNs; mTagId = tagId; + mLogUid = 0; mContext = create_android_logger(1937006964); // the event tag shared by all stats logs if (mContext) { android_log_write_int32(mContext, tagId); @@ -53,6 +63,14 @@ void LogEvent::init() { // turns to reader mode mContext = create_android_log_parser(buffer, len); init(mContext); + // destroy the context to save memory. + android_log_destroy(&mContext); + } +} + +LogEvent::~LogEvent() { + if (mContext) { + android_log_destroy(&mContext); } } @@ -98,19 +116,72 @@ bool LogEvent::write(float value) { return false; } -LogEvent::~LogEvent() { +bool LogEvent::write(const std::vector<AttributionNode>& nodes) { if (mContext) { - android_log_destroy(&mContext); + if (android_log_write_list_begin(mContext) < 0) { + return false; + } + for (size_t i = 0; i < nodes.size(); ++i) { + if (!write(nodes[i])) { + return false; + } + } + if (android_log_write_list_end(mContext) < 0) { + return false; + } + return true; + } + return false; +} + +bool LogEvent::write(const AttributionNode& node) { + if (mContext) { + if (android_log_write_list_begin(mContext) < 0) { + return false; + } + if (android_log_write_int32(mContext, node.uid()) < 0) { + return false; + } + if (android_log_write_string8(mContext, node.tag().c_str()) < 0) { + return false; + } + if (android_log_write_int32(mContext, node.uid()) < 0) { + return false; + } + if (android_log_write_list_end(mContext) < 0) { + return false; + } + return true; } + return false; } +namespace { + +void increaseField(Field *field, bool is_child) { + if (is_child) { + if (field->child_size() <= 0) { + field->add_child(); + } + } else { + field->clear_child(); + } + Field* curr = is_child ? field->mutable_child(0) : field; + if (!curr->has_field()) { + curr->set_field(1); + } else { + curr->set_field(curr->field() + 1); + } +} + +} // namespace + /** * The elements of each log event are stored as a vector of android_log_list_elements. * 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(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 @@ -118,24 +189,79 @@ void LogEvent::init(android_log_context context) { // list-related log elements and the order we get there is our index-keyed data // structure. int i = 0; + + int seenListStart = 0; + + Field field; do { 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]. + // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. if (i == 1) { mTagId = elem.data.int32; - break; + } else { + increaseField(&field, seenListStart > 0/* is_child */); + DimensionsValue dimensionsValue; + dimensionsValue.set_value_int(elem.data.int32); + setFieldInLeafValueProto(field, &dimensionsValue); + mFieldValueMap.insert( + std::make_pair(buildAtomField(mTagId, field), dimensionsValue)); } + break; case EVENT_TYPE_FLOAT: + { + increaseField(&field, seenListStart > 0/* is_child */); + DimensionsValue dimensionsValue; + dimensionsValue.set_value_float(elem.data.float32); + setFieldInLeafValueProto(field, &dimensionsValue); + mFieldValueMap.insert( + std::make_pair(buildAtomField(mTagId, field), dimensionsValue)); + } + break; case EVENT_TYPE_STRING: + { + increaseField(&field, seenListStart > 0/* is_child */); + DimensionsValue dimensionsValue; + dimensionsValue.set_value_str(string(elem.data.string, elem.len).c_str()); + setFieldInLeafValueProto(field, &dimensionsValue); + mFieldValueMap.insert( + std::make_pair(buildAtomField(mTagId, field), dimensionsValue)); + } + break; case EVENT_TYPE_LONG: - mElements.push_back(elem); + { + increaseField(&field, seenListStart > 0 /* is_child */); + DimensionsValue dimensionsValue; + dimensionsValue.set_value_long(elem.data.int64); + setFieldInLeafValueProto(field, &dimensionsValue); + mFieldValueMap.insert( + std::make_pair(buildAtomField(mTagId, field), dimensionsValue)); + } break; case EVENT_TYPE_LIST: + if (i >= 1) { + if (seenListStart > 0) { + increasePosition(&field); + } else { + increaseField(&field, false /* is_child */); + } + seenListStart++; + if (seenListStart >= 3) { + ALOGE("Depth > 2. Not supported!"); + return; + } + } break; case EVENT_TYPE_LIST_STOP: + seenListStart--; + if (seenListStart == 0) { + field.clear_position_index(); + } else { + if (field.child_size() > 0) { + field.mutable_child(0)->clear_field(); + } + } break; case EVENT_TYPE_UNKNOWN: break; @@ -147,142 +273,145 @@ void LogEvent::init(android_log_context context) { } int64_t LogEvent::GetLong(size_t key, status_t* err) const { - if (key < 1 || (key - 1) >= mElements.size()) { + DimensionsValue value; + if (!GetSimpleAtomDimensionsValueProto(key, &value)) { *err = BAD_INDEX; return 0; } - key--; - const android_log_list_element& elem = mElements[key]; - if (elem.type == EVENT_TYPE_INT) { - return elem.data.int32; - } else if (elem.type == EVENT_TYPE_LONG) { - return elem.data.int64; - } else if (elem.type == EVENT_TYPE_FLOAT) { - return (int64_t)elem.data.float32; - } else { - *err = BAD_TYPE; - return 0; + const DimensionsValue* leafValue = getSingleLeafValue(&value); + switch (leafValue->value_case()) { + case DimensionsValue::ValueCase::kValueInt: + return (int64_t)leafValue->value_int(); + case DimensionsValue::ValueCase::kValueLong: + return leafValue->value_long(); + case DimensionsValue::ValueCase::kValueBool: + return leafValue->value_bool() ? 1 : 0; + case DimensionsValue::ValueCase::kValueFloat: + return (int64_t)leafValue->value_float(); + case DimensionsValue::ValueCase::kValueTuple: + case DimensionsValue::ValueCase::kValueStr: + case DimensionsValue::ValueCase::VALUE_NOT_SET: { + *err = BAD_TYPE; + return 0; + } } } const char* LogEvent::GetString(size_t key, status_t* err) const { - if (key < 1 || (key - 1) >= mElements.size()) { + DimensionsValue value; + if (!GetSimpleAtomDimensionsValueProto(key, &value)) { *err = BAD_INDEX; - return NULL; + return 0; } - key--; - const android_log_list_element& elem = mElements[key]; - if (elem.type != EVENT_TYPE_STRING) { - *err = BAD_TYPE; - return NULL; + const DimensionsValue* leafValue = getSingleLeafValue(&value); + switch (leafValue->value_case()) { + case DimensionsValue::ValueCase::kValueStr: + return leafValue->value_str().c_str(); + case DimensionsValue::ValueCase::kValueInt: + case DimensionsValue::ValueCase::kValueLong: + case DimensionsValue::ValueCase::kValueBool: + case DimensionsValue::ValueCase::kValueFloat: + case DimensionsValue::ValueCase::kValueTuple: + case DimensionsValue::ValueCase::VALUE_NOT_SET: { + *err = BAD_TYPE; + return 0; + } } - // Need to add the '/0' at the end by specifying the length of the string. - return string(elem.data.string, elem.len).c_str(); } bool LogEvent::GetBool(size_t key, status_t* err) const { - if (key < 1 || (key - 1) >= mElements.size()) { + DimensionsValue value; + if (!GetSimpleAtomDimensionsValueProto(key, &value)) { *err = BAD_INDEX; return 0; } - key--; - const android_log_list_element& elem = mElements[key]; - if (elem.type == EVENT_TYPE_INT) { - return elem.data.int32 != 0; - } else if (elem.type == EVENT_TYPE_LONG) { - return elem.data.int64 != 0; - } else if (elem.type == EVENT_TYPE_FLOAT) { - return elem.data.float32 != 0; - } else { - *err = BAD_TYPE; - return 0; + const DimensionsValue* leafValue = getSingleLeafValue(&value); + switch (leafValue->value_case()) { + case DimensionsValue::ValueCase::kValueInt: + return leafValue->value_int() != 0; + case DimensionsValue::ValueCase::kValueLong: + return leafValue->value_long() != 0; + case DimensionsValue::ValueCase::kValueBool: + return leafValue->value_bool(); + case DimensionsValue::ValueCase::kValueFloat: + return leafValue->value_float() != 0; + case DimensionsValue::ValueCase::kValueTuple: + case DimensionsValue::ValueCase::kValueStr: + case DimensionsValue::ValueCase::VALUE_NOT_SET: { + *err = BAD_TYPE; + return 0; + } } } float LogEvent::GetFloat(size_t key, status_t* err) const { - if (key < 1 || (key - 1) >= mElements.size()) { + DimensionsValue value; + if (!GetSimpleAtomDimensionsValueProto(key, &value)) { *err = BAD_INDEX; return 0; } - key--; - const android_log_list_element& elem = mElements[key]; - if (elem.type == EVENT_TYPE_INT) { - return (float)elem.data.int32; - } else if (elem.type == EVENT_TYPE_LONG) { - return (float)elem.data.int64; - } else if (elem.type == EVENT_TYPE_FLOAT) { - return elem.data.float32; - } else { - *err = BAD_TYPE; - return 0; + const DimensionsValue* leafValue = getSingleLeafValue(&value); + switch (leafValue->value_case()) { + case DimensionsValue::ValueCase::kValueInt: + return (float)leafValue->value_int(); + case DimensionsValue::ValueCase::kValueLong: + return (float)leafValue->value_long(); + case DimensionsValue::ValueCase::kValueBool: + return leafValue->value_bool() ? 1.0f : 0.0f; + case DimensionsValue::ValueCase::kValueFloat: + return leafValue->value_float(); + case DimensionsValue::ValueCase::kValueTuple: + case DimensionsValue::ValueCase::kValueStr: + case DimensionsValue::ValueCase::VALUE_NOT_SET: { + *err = BAD_TYPE; + return 0; + } } } -KeyValuePair LogEvent::GetKeyValueProto(size_t key) const { - KeyValuePair pair; - pair.set_key(key); - // If the value is not valid, return the KeyValuePair without assigning the value. - // Caller can detect the error by checking the enum for "one of" proto type. - if (key < 1 || (key - 1) >= mElements.size()) { - return pair; - } - key--; - - const android_log_list_element& elem = mElements[key]; - if (elem.type == EVENT_TYPE_INT) { - pair.set_value_int(elem.data.int32); - } else if (elem.type == EVENT_TYPE_LONG) { - pair.set_value_long(elem.data.int64); - } else if (elem.type == EVENT_TYPE_STRING) { - pair.set_value_str(elem.data.string); - } else if (elem.type == EVENT_TYPE_FLOAT) { - pair.set_value_float(elem.data.float32); +void LogEvent::GetAtomDimensionsValueProtos(const FieldMatcher& matcher, + std::vector<DimensionsValue> *dimensionsValues) const { + findDimensionsValues(mFieldValueMap, matcher, dimensionsValues); +} + +bool LogEvent::GetAtomDimensionsValueProto(const FieldMatcher& matcher, + DimensionsValue* dimensionsValue) const { + std::vector<DimensionsValue> rootDimensionsValues; + findDimensionsValues(mFieldValueMap, matcher, &rootDimensionsValues); + if (rootDimensionsValues.size() != 1) { + return false; } - return pair; + *dimensionsValue = rootDimensionsValues.front(); + return true; +} + +bool LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField, + DimensionsValue* dimensionsValue) const { + return GetAtomDimensionsValueProto( + buildSimpleAtomFieldMatcher(mTagId, atomField), dimensionsValue); +} + +DimensionsValue LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField) const { + DimensionsValue dimensionsValue; + GetSimpleAtomDimensionsValueProto(atomField, &dimensionsValue); + return dimensionsValue; } string LogEvent::ToString() const { ostringstream result; result << "{ " << mTimestampNs << " (" << mTagId << ")"; - const size_t N = mElements.size(); - for (size_t i=0; i<N; i++) { - result << " "; - result << (i + 1); + for (const auto& itr : mFieldValueMap) { + result << FieldToString(itr.first); result << "->"; - const android_log_list_element& elem = mElements[i]; - if (elem.type == EVENT_TYPE_INT) { - result << elem.data.int32; - } else if (elem.type == EVENT_TYPE_LONG) { - result << elem.data.int64; - } else if (elem.type == EVENT_TYPE_FLOAT) { - result << elem.data.float32; - } else if (elem.type == EVENT_TYPE_STRING) { - // Need to add the '/0' at the end by specifying the length of the string. - result << string(elem.data.string, elem.len).c_str(); - } + result << DimensionsValueToString(itr.second); + result << " "; } result << " }"; return result.str(); } -void LogEvent::ToProto(ProtoOutputStream& proto) const { - long long atomToken = proto.start(FIELD_TYPE_MESSAGE | mTagId); - const size_t N = mElements.size(); - for (size_t i=0; i<N; i++) { - const int key = i + 1; - - const android_log_list_element& elem = mElements[i]; - if (elem.type == EVENT_TYPE_INT) { - proto.write(FIELD_TYPE_INT32 | key, elem.data.int32); - } else if (elem.type == EVENT_TYPE_LONG) { - proto.write(FIELD_TYPE_INT64 | key, (long long)elem.data.int64); - } else if (elem.type == EVENT_TYPE_FLOAT) { - proto.write(FIELD_TYPE_FLOAT | key, elem.data.float32); - } else if (elem.type == EVENT_TYPE_STRING) { - proto.write(FIELD_TYPE_STRING | key, elem.data.string); - } - } - proto.end(atomToken); +void LogEvent::ToProto(ProtoOutputStream& protoOutput) const { + writeFieldValueTreeToStream(getFieldValueMap(), &protoOutput); } } // namespace statsd diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h index d3f38deef886..8f3dedfd1825 100644 --- a/cmds/statsd/src/logd/LogEvent.h +++ b/cmds/statsd/src/logd/LogEvent.h @@ -16,6 +16,7 @@ #pragma once +#include "field_util.h" #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include <android/util/ProtoOutputStream.h> @@ -23,9 +24,11 @@ #include <log/log_read.h> #include <private/android_logger.h> #include <utils/Errors.h> +#include <utils/JenkinsHash.h> #include <memory> #include <string> +#include <map> #include <vector> namespace android { @@ -77,6 +80,20 @@ public: bool GetBool(size_t key, status_t* err) const; float GetFloat(size_t key, status_t* err) const; + /* + * Get DimensionsValue proto objects from FieldMatcher. + */ + void GetAtomDimensionsValueProtos( + const FieldMatcher& matcher, std::vector<DimensionsValue> *dimensionsValues) const; + bool GetAtomDimensionsValueProto( + const FieldMatcher& matcher, DimensionsValue* dimensionsValue) const; + + /* + * Get a DimensionsValue proto objects from Field. + */ + bool GetSimpleAtomDimensionsValueProto(size_t field, DimensionsValue* dimensionsValue) const; + DimensionsValue GetSimpleAtomDimensionsValueProto(size_t atomField) 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. @@ -87,6 +104,8 @@ public: bool write(int64_t value); bool write(const string& value); bool write(float value); + bool write(const std::vector<AttributionNode>& nodes); + bool write(const AttributionNode& node); /** * Return a string representation of this event. @@ -98,11 +117,6 @@ public: */ void ToProto(android::util::ProtoOutputStream& out) const; - /* - * Get a KeyValuePair proto object. - */ - KeyValuePair GetKeyValueProto(size_t key) const; - /** * Used with the constructor where tag is passed in. Converts the log_event_list to read mode * and prepares the list for reading. @@ -114,10 +128,12 @@ public: */ void setTimestampNs(uint64_t timestampNs) {mTimestampNs = timestampNs;} - int size() const { - return mElements.size(); + inline int size() const { + return mFieldValueMap.size(); } + inline const FieldValueMap& getFieldValueMap() const { return mFieldValueMap; } + private: /** * Don't copy, it's slower. If we really need this we can add it but let's try to @@ -130,8 +146,11 @@ private: */ void init(android_log_context context); - vector<android_log_list_element> mElements; + FieldValueMap mFieldValueMap; + // This field is used when statsD wants to create log event object and write fields to it. After + // calling init() function, this object would be destroyed to save memory usage. + // When the log event is created from log msg, this field is never initiated. android_log_context mContext; uint64_t mTimestampNs; diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp index 51a38b61ccf2..bd5e3cbce07c 100644 --- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp @@ -82,8 +82,8 @@ bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatche mChildren.push_back(childIndex); - const set<int>& childTagIds = allTrackers[childIndex]->getTagIds(); - mTagIds.insert(childTagIds.begin(), childTagIds.end()); + const set<int>& childTagIds = allTrackers[childIndex]->getAtomIds(); + mAtomIds.insert(childTagIds.begin(), childTagIds.end()); } mInitialized = true; @@ -100,7 +100,7 @@ void CombinationLogMatchingTracker::onLogEvent(const LogEvent& event, return; } - if (mTagIds.find(event.GetTagId()) == mTagIds.end()) { + if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) { matcherResults[mIndex] = MatchingState::kNotMatched; return; } diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h index 8162c443fbd3..567944fce8a6 100644 --- a/cmds/statsd/src/matchers/LogMatchingTracker.h +++ b/cmds/statsd/src/matchers/LogMatchingTracker.h @@ -65,8 +65,8 @@ public: // Get the tagIds that this matcher cares about. The combined collection is stored // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses // some memory but hopefully it can save us much CPU time when there is flood of events. - virtual const std::set<int>& getTagIds() const { - return mTagIds; + virtual const std::set<int>& getAtomIds() const { + return mAtomIds; } const std::string& getName() const { @@ -88,7 +88,7 @@ protected: // useful when we have a complex CombinationLogMatcherTracker. // TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a // LogMatchingTracker cares is only a few. - std::set<int> mTagIds; + std::set<int> mAtomIds; }; } // namespace statsd diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp index ac217abecba3..91ef0344da91 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp @@ -30,12 +30,13 @@ using std::vector; SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index, - const SimpleAtomMatcher& matcher) - : LogMatchingTracker(name, index), mMatcher(matcher) { - if (!matcher.has_tag()) { + const SimpleAtomMatcher& matcher, + const UidMap& uidMap) + : LogMatchingTracker(name, index), mMatcher(matcher), mUidMap(uidMap) { + if (!matcher.has_atom_id()) { mInitialized = false; } else { - mTagIds.insert(matcher.tag()); + mAtomIds.insert(matcher.atom_id()); mInitialized = true; } } @@ -59,12 +60,12 @@ void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event, return; } - if (mTagIds.find(event.GetTagId()) == mTagIds.end()) { + if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) { matcherResults[mIndex] = MatchingState::kNotMatched; return; } - bool matched = matchesSimple(mMatcher, event); + bool matched = matchesSimple(mUidMap, mMatcher, event); matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched; VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched); } diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h index 2c188c18c95b..68518f8a224d 100644 --- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h +++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h @@ -24,6 +24,7 @@ #include <vector> #include "LogMatchingTracker.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "packages/UidMap.h" namespace android { namespace os { @@ -32,7 +33,8 @@ namespace statsd { class SimpleLogMatchingTracker : public virtual LogMatchingTracker { public: SimpleLogMatchingTracker(const std::string& name, const int index, - const SimpleAtomMatcher& matcher); + const SimpleAtomMatcher& matcher, + const UidMap& uidMap); ~SimpleLogMatchingTracker(); @@ -47,6 +49,7 @@ public: private: const SimpleAtomMatcher mMatcher; + const UidMap& mUidMap; }; } // namespace statsd diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp index 9e88e5d0d8e3..46d9b92ec94c 100644 --- a/cmds/statsd/src/matchers/matcher_util.cpp +++ b/cmds/statsd/src/matchers/matcher_util.cpp @@ -19,7 +19,9 @@ #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "matchers/LogMatchingTracker.h" #include "matchers/matcher_util.h" +#include "dimension.h" #include "stats_util.h" +#include "field_util.h" #include <log/event_tag_map.h> #include <log/log_event_list.h> @@ -91,127 +93,173 @@ bool combinationMatch(const vector<int>& children, const LogicalOperation& opera return matched; } -bool matchesSimple(const SimpleAtomMatcher& simpleMatcher, const LogEvent& event) { - const int tagId = event.GetTagId(); - - if (simpleMatcher.tag() != tagId) { - return false; - } - // now see if this event is interesting to us -- matches ALL the matchers - // defined in the metrics. - bool allMatched = true; - for (int j = 0; allMatched && j < simpleMatcher.key_value_matcher_size(); j++) { - auto cur = simpleMatcher.key_value_matcher(j); - - // TODO: Check if this key is a magic key (eg package name). - // TODO: Maybe make packages a different type in the config? - int key = cur.key_matcher().key(); +bool IsAttributionUidField(const Field& field) { + return field.child_size() == 1 && field.child(0).field() == 1 + && field.child(0).child_size() == 1 && field.child(0).child(0).field() == 1; +} - const KeyValueMatcher::ValueMatcherCase matcherCase = cur.value_matcher_case(); - if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqString) { - // String fields - status_t err = NO_ERROR; - const char* val = event.GetString(key, &err); - if (err == NO_ERROR && val != NULL) { - if (!(cur.eq_string() == val)) { - allMatched = false; - break; - } - } else { - allMatched = false; - break; - } - } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt || - matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt || - matcherCase == KeyValueMatcher::ValueMatcherCase::kGtInt || - matcherCase == KeyValueMatcher::ValueMatcherCase::kLteInt || - matcherCase == KeyValueMatcher::ValueMatcherCase::kGteInt) { - // Integer fields - status_t err = NO_ERROR; - int64_t val = event.GetLong(key, &err); - if (err == NO_ERROR) { - 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; +bool matchesNonRepeatedField( + const UidMap& uidMap, + const FieldValueMap& fieldMap, + const FieldValueMatcher&matcher, + const Field& field) { + if (matcher.value_matcher_case() == + FieldValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET) { + return !fieldMap.empty() && fieldMap.begin()->first.field() == matcher.field(); + } else if (matcher.value_matcher_case() == FieldValueMatcher::ValueMatcherCase::kMatchesTuple) { + bool allMatched = true; + for (int i = 0; allMatched && i < matcher.matches_tuple().field_value_matcher_size(); ++i) { + const auto& childMatcher = matcher.matches_tuple().field_value_matcher(i); + Field childField = field; + appendLeaf(&childField, childMatcher.field()); + allMatched &= matchFieldSimple(uidMap, fieldMap, childMatcher, childField); + } + return allMatched; + } else { + auto ret = fieldMap.equal_range(field); + int found = 0; + for (auto it = ret.first; it != ret.second; ++it) { + found++; + } + // Not found. + if (found <= 0) { + return false; + } + if (found > 1) { + ALOGE("Found multiple values for optional field."); + return false; + } + bool matched = false; + switch (matcher.value_matcher_case()) { + case FieldValueMatcher::ValueMatcherCase::kEqBool: + // Logd does not support bool, it is int instead. + matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool()); + break; + case FieldValueMatcher::ValueMatcherCase::kEqString: + { + if (IsAttributionUidField(field)) { + const int uid = ret.first->second.value_int(); + std::set<string> packageNames = + uidMap.getAppNamesFromUid(uid, true /* normalize*/); + matched = packageNames.find(matcher.eq_string()) != packageNames.end(); + } else { + matched = (ret.first->second.value_str() == matcher.eq_string()); } - } - } else { - allMatched = false; + } + break; + case FieldValueMatcher::ValueMatcherCase::kEqInt: + matched = (ret.first->second.value_int() == matcher.eq_int()); + break; + case FieldValueMatcher::ValueMatcherCase::kLtInt: + matched = (ret.first->second.value_int() < matcher.lt_int()); + break; + case FieldValueMatcher::ValueMatcherCase::kGtInt: + matched = (ret.first->second.value_int() > matcher.gt_int()); + break; + case FieldValueMatcher::ValueMatcherCase::kLtFloat: + matched = (ret.first->second.value_float() < matcher.lt_float()); + break; + case FieldValueMatcher::ValueMatcherCase::kGtFloat: + matched = (ret.first->second.value_float() > matcher.gt_float()); + break; + case FieldValueMatcher::ValueMatcherCase::kLteInt: + matched = (ret.first->second.value_int() <= matcher.lte_int()); + break; + case FieldValueMatcher::ValueMatcherCase::kGteInt: + matched = (ret.first->second.value_int() >= matcher.gte_int()); + break; + default: break; - } - } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqBool) { - // Boolean fields - status_t err = NO_ERROR; - bool val = event.GetBool(key, &err); - if (err == NO_ERROR) { - if (!(cur.eq_bool() == val)) { - allMatched = false; - break; - } - } else { - allMatched = false; - break; - } - } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat || - matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) { - // Float fields - status_t err = NO_ERROR; - float val = event.GetFloat(key, &err); - if (err == NO_ERROR) { - if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat) { - if (!(val < cur.lt_float())) { - allMatched = false; - break; + } + return matched; + } +} + +bool matchesRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap, + const FieldValueMatcher&matcher, const Field& field) { + if (matcher.position() == Position::FIRST) { + Field first_field = field; + setPositionForLeaf(&first_field, 0); + return matchesNonRepeatedField(uidMap, fieldMap, matcher, first_field); + } else { + auto itLower = fieldMap.lower_bound(field); + if (itLower == fieldMap.end()) { + return false; + } + Field next_field = field; + getNextField(&next_field); + auto itUpper = fieldMap.lower_bound(next_field); + switch (matcher.position()) { + case Position::LAST: + { + itUpper--; + if (itUpper == fieldMap.end()) { + return false; + } else { + Field last_field = field; + int last_index = getPositionByReferenceField(field, itUpper->first); + if (last_index < 0) { + return false; + } + setPositionForLeaf(&last_field, last_index); + return matchesNonRepeatedField(uidMap, fieldMap, matcher, last_field); + } + } + break; + case Position::ANY: + { + std::set<int> indexes; + for (auto it = itLower; it != itUpper; ++it) { + int index = getPositionByReferenceField(field, it->first); + if (index >= 0) { + indexes.insert(index); + } } - } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) { - if (!(val > cur.gt_float())) { - allMatched = false; - break; + bool matched = false; + for (const int index : indexes) { + Field any_field = field; + setPositionForLeaf(&any_field, index); + matched |= matchesNonRepeatedField(uidMap, fieldMap, matcher, any_field); } - } - } else { - allMatched = false; - break; - } - } else { - // If value matcher is not present, assume that we match. - } + return matched; + } + default: + return false; + } } - return allMatched; + } -vector<KeyValuePair> getDimensionKey(const LogEvent& event, - const std::vector<KeyMatcher>& dimensions) { - vector<KeyValuePair> key; - key.reserve(dimensions.size()); - for (const KeyMatcher& dimension : dimensions) { - KeyValuePair k = event.GetKeyValueProto(dimension.key()); - key.push_back(k); +bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap, + const FieldValueMatcher&matcher, const Field& field) { + if (!matcher.has_position()) { + return matchesNonRepeatedField(uidMap, fieldMap, matcher, field); + } else { + return matchesRepeatedField(uidMap, fieldMap, matcher, field); } - return key; } +bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher, + const LogEvent& event) { + if (simpleMatcher.field_value_matcher_size() <= 0) { + return event.GetTagId() == simpleMatcher.atom_id(); + } + Field root_field; + root_field.set_field(simpleMatcher.atom_id()); + FieldValueMatcher root_field_matcher; + root_field_matcher.set_field(simpleMatcher.atom_id()); + for (int i = 0; i < simpleMatcher.field_value_matcher_size(); i++) { + *root_field_matcher.mutable_matches_tuple()->add_field_value_matcher() = + simpleMatcher.field_value_matcher(i); + } + return matchFieldSimple(uidMap, event.getFieldValueMap(), root_field_matcher, root_field); +} + +vector<DimensionsValue> getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher) { + vector<DimensionsValue> values; + findDimensionsValues(event.getFieldValueMap(), matcher, &values); + return values; +} } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h index f54ab3648a19..704cb4c22e00 100644 --- a/cmds/statsd/src/matchers/matcher_util.h +++ b/cmds/statsd/src/matchers/matcher_util.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef MATCHER_UTIL_H -#define MATCHER_UTIL_H +#pragma once #include "logd/LogEvent.h" @@ -28,6 +27,7 @@ #include "frameworks/base/cmds/statsd/src/stats_log.pb.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" #include "stats_util.h" +#include "packages/UidMap.h" namespace android { namespace os { @@ -42,12 +42,14 @@ enum MatchingState { bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation, const std::vector<MatchingState>& matcherResults); -bool matchesSimple(const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper); +bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& dimensionsMap, + const FieldValueMatcher& matcher, const Field& field); -std::vector<KeyValuePair> getDimensionKey(const LogEvent& event, - const std::vector<KeyMatcher>& dimensions); +bool matchesSimple(const UidMap& uidMap, + const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper); + +std::vector<DimensionsValue> getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher); } // namespace statsd } // namespace os } // namespace android -#endif // MATCHER_UTIL_H diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 9031ed018df2..5bf3cffd8bab 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -20,6 +20,7 @@ #include "CountMetricProducer.h" #include "guardrail/StatsdStats.h" #include "stats_util.h" +#include "stats_log_util.h" #include <limits.h> #include <stdlib.h> @@ -51,12 +52,6 @@ const int FIELD_ID_DATA = 1; // for CountMetricData const int FIELD_ID_DIMENSION = 1; const int FIELD_ID_BUCKET_INFO = 2; -// for KeyValuePair -const int FIELD_ID_KEY = 1; -const int FIELD_ID_VALUE_STR = 2; -const int FIELD_ID_VALUE_INT = 3; -const int FIELD_ID_VALUE_BOOL = 4; -const int FIELD_ID_VALUE_FLOAT = 5; // for CountBucketInfo const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; @@ -75,7 +70,7 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric } // TODO: use UidMap if uid->pkg_name is required - mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end()); + mDimensions = metric.dimensions(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), @@ -95,6 +90,24 @@ void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); } +void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { + flushIfNeededLocked(dumpTimeNs); + report->set_metric_name(mName); + report->set_start_report_nanos(mStartTimeNs); + + auto count_metrics = report->mutable_count_metrics(); + for (const auto& counter : mPastBuckets) { + CountMetricData* metricData = count_metrics->add_data(); + *metricData->mutable_dimension() = counter.first.getDimensionsValue(); + for (const auto& bucket : counter.second) { + CountBucketInfo* bucketInfo = metricData->add_bucket_info(); + bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); + bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); + bucketInfo->set_count(bucket.mCount); + } + } +} + void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); @@ -107,28 +120,16 @@ void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, for (const auto& counter : mPastBuckets) { const HashableDimensionKey& hashableKey = counter.first; - const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs(); VLOG(" dimension key %s", hashableKey.c_str()); long long wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - // First fill dimension (KeyValuePairs). - for (const auto& kv : kvs) { - long long dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key()); - if (kv.has_value_str()) { - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str()); - } else if (kv.has_value_int()) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int()); - } else if (kv.has_value_bool()) { - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool()); - } else if (kv.has_value_float()) { - protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float()); - } - protoOutput->end(dimensionToken); - } + // First fill dimension. + long long dimensionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); + writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); + protoOutput->end(dimensionToken); // Then fill bucket_info (CountBucketInfo). for (const auto& bucket : counter.second) { @@ -183,7 +184,7 @@ bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) void CountMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const map<string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) { uint64_t eventTimeNs = event.GetTimestampNs(); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index e32fc06de353..6087ae502e7b 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -51,12 +51,13 @@ public: protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) override; private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; + void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 51ea4b501596..30b105c71500 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -20,6 +20,7 @@ #include "DurationMetricProducer.h" #include "guardrail/StatsdStats.h" #include "stats_util.h" +#include "stats_log_util.h" #include <limits.h> #include <stdlib.h> @@ -50,12 +51,6 @@ const int FIELD_ID_DATA = 1; // for DurationMetricData const int FIELD_ID_DIMENSION = 1; const int FIELD_ID_BUCKET_INFO = 2; -// for KeyValuePair -const int FIELD_ID_KEY = 1; -const int FIELD_ID_VALUE_STR = 2; -const int FIELD_ID_VALUE_INT = 3; -const int FIELD_ID_VALUE_BOOL = 4; -const int FIELD_ID_VALUE_FLOAT = 5; // for DurationBucketInfo const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; @@ -66,7 +61,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat const size_t stopIndex, const size_t stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, - const vector<KeyMatcher>& internalDimension, + const FieldMatcher& internalDimensions, const uint64_t startTimeNs) : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard), mAggregationType(metric.aggregation_type()), @@ -74,7 +69,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat mStopIndex(stopIndex), mStopAllIndex(stopAllIndex), mNested(nesting), - mInternalDimension(internalDimension) { + mInternalDimensions(internalDimensions) { // 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 // not related. Maybe we should add a template in the future?? @@ -85,7 +80,7 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat } // TODO: use UidMap if uid->pkg_name is required - mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end()); + mDimensions = metric.dimensions(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), @@ -151,6 +146,24 @@ void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet, } } +void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { + flushIfNeededLocked(dumpTimeNs); + report->set_metric_name(mName); + report->set_start_report_nanos(mStartTimeNs); + + auto duration_metrics = report->mutable_duration_metrics(); + for (const auto& pair : mPastBuckets) { + DurationMetricData* metricData = duration_metrics->add_data(); + *metricData->mutable_dimension() = pair.first.getDimensionsValue(); + for (const auto& bucket : pair.second) { + auto bucketInfo = metricData->add_bucket_info(); + bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); + bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); + bucketInfo->set_duration_nanos(bucket.mDuration); + } + } +} + void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { flushIfNeededLocked(dumpTimeNs); @@ -163,28 +176,16 @@ void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; - const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs(); VLOG(" dimension key %s", hashableKey.c_str()); long long wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - // First fill dimension (KeyValuePairs). - for (const auto& kv : kvs) { - long long dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key()); - if (kv.has_value_str()) { - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str()); - } else if (kv.has_value_int()) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int()); - } else if (kv.has_value_bool()) { - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool()); - } else if (kv.has_value_float()) { - protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float()); - } - protoOutput->end(dimensionToken); - } + // First fill dimension. + long long dimensionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); + writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); + protoOutput->end(dimensionToken); // Then fill bucket_info (DurationBucketInfo). for (const auto& bucket : pair.second) { @@ -249,7 +250,7 @@ bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newK void DurationMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const map<string, HashableDimensionKey>& conditionKeys, bool condition, + const ConditionKey& conditionKeys, bool condition, const LogEvent& event) { flushIfNeededLocked(event.GetTimestampNs()); @@ -260,7 +261,6 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( return; } - HashableDimensionKey atomKey(getDimensionKey(event, mInternalDimension)); if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) { if (hitGuardRailLocked(eventKey)) { @@ -271,11 +271,25 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( auto it = mCurrentSlicedDuration.find(eventKey); - if (matcherIndex == mStartIndex) { - it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys); - } else if (matcherIndex == mStopIndex) { - it->second->noteStop(atomKey, event.GetTimestampNs(), false); + std::vector<DimensionsValue> values = getDimensionKeys(event, mInternalDimensions); + if (values.empty()) { + if (matcherIndex == mStartIndex) { + it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, + event.GetTimestampNs(), conditionKeys); + } else if (matcherIndex == mStopIndex) { + it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetTimestampNs(), false); + } + } else { + for (const DimensionsValue& value : values) { + if (matcherIndex == mStartIndex) { + it->second->noteStart(HashableDimensionKey(value), condition, + event.GetTimestampNs(), conditionKeys); + } else if (matcherIndex == mStopIndex) { + it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false); + } + } } + } size_t DurationMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index abc89bd5363e..e06b9a14563d 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -42,7 +42,7 @@ public: 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); + const FieldMatcher& internalDimensions, const uint64_t startTimeNs); virtual ~DurationMetricProducer(); @@ -51,12 +51,13 @@ public: protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const std::map<std::string, HashableDimensionKey>& conditionKeys, bool condition, + const ConditionKey& conditionKeys, bool condition, const LogEvent& event) override; private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; + void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; @@ -85,7 +86,7 @@ private: const bool mNested; // The dimension from the atom predicate. e.g., uid, wakelock name. - const vector<KeyMatcher> mInternalDimension; + const FieldMatcher mInternalDimensions; // Save the past buckets and we can clear when the StatsLogReport is dumped. // TODO: Add a lock to mPastBuckets. @@ -109,6 +110,7 @@ private: FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition); FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition); + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 6a072b076bef..c8138d3a7d9a 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -96,6 +96,10 @@ std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& pr return buffer; } +void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { + +} + void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName); @@ -121,7 +125,7 @@ void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, void EventMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) { if (!condition) { return; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index 6120ad8fd6da..a57b07d6648e 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -46,11 +46,12 @@ protected: private: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) override; void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; + void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 47cca0e23892..51fd9fcb335b 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -19,6 +19,8 @@ #include "GaugeMetricProducer.h" #include "guardrail/StatsdStats.h" +#include "dimension.h" +#include "stats_log_util.h" #include <cutils/log.h> @@ -51,12 +53,6 @@ const int FIELD_ID_DATA = 1; // for GaugeMetricData const int FIELD_ID_DIMENSION = 1; const int FIELD_ID_BUCKET_INFO = 2; -// for KeyValuePair -const int FIELD_ID_KEY = 1; -const int FIELD_ID_VALUE_STR = 2; -const int FIELD_ID_VALUE_INT = 3; -const int FIELD_ID_VALUE_BOOL = 4; -const int FIELD_ID_VALUE_FLOAT = 5; // for GaugeBucketInfo const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; @@ -71,18 +67,18 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric mStatsPullerManager(statsPullerManager), mPullTagId(pullTagId), mAtomTagId(atomTagId) { + mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>(); + mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>(); if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) { mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000; } else { mBucketSizeNs = kDefaultGaugemBucketSizeNs; } - for (int i = 0; i < metric.gauge_fields().field_num_size(); i++) { - mGaugeFields.push_back(metric.gauge_fields().field_num(i)); - } + mFieldFilter = metric.gauge_fields_filter(); // TODO: use UidMap if uid->pkg_name is required - mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end()); + mDimensions = metric.dimensions(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), @@ -93,7 +89,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric // Kicks off the puller immediately. if (mPullTagId != -1) { mStatsPullerManager->RegisterReceiver(mPullTagId, this, - metric.bucket().bucket_size_millis()); + metric.bucket().bucket_size_millis()); } VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(), @@ -116,6 +112,10 @@ GaugeMetricProducer::~GaugeMetricProducer() { } } +void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { + flushIfNeededLocked(dumpTimeNs); +} + void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { VLOG("gauge metric %s dump report now...", mName.c_str()); @@ -128,28 +128,16 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; - const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs(); VLOG(" dimension key %s", hashableKey.c_str()); long long wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - // First fill dimension (KeyValuePairs). - for (const auto& kv : kvs) { - long long dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key()); - if (kv.has_value_str()) { - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str()); - } else if (kv.has_value_int()) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int()); - } else if (kv.has_value_bool()) { - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool()); - } else if (kv.has_value_float()) { - protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float()); - } - protoOutput->end(dimensionToken); - } + // First fill dimension. + long long dimensionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); + writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); + protoOutput->end(dimensionToken); // Then fill bucket_info (GaugeBucketInfo). for (const auto& bucket : pair.second) { @@ -160,25 +148,11 @@ void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS, (long long)bucket.mBucketEndNs); long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM); - long long eventToken = protoOutput->start(FIELD_TYPE_MESSAGE | mAtomTagId); - for (const auto& pair : bucket.mEvent->kv) { - if (pair.has_value_int()) { - protoOutput->write(FIELD_TYPE_INT32 | pair.key(), pair.value_int()); - } else if (pair.has_value_long()) { - protoOutput->write(FIELD_TYPE_INT64 | pair.key(), pair.value_long()); - } else if (pair.has_value_str()) { - protoOutput->write(FIELD_TYPE_STRING | pair.key(), pair.value_str()); - } else if (pair.has_value_long()) { - protoOutput->write(FIELD_TYPE_FLOAT | pair.key(), pair.value_float()); - } else if (pair.has_value_bool()) { - protoOutput->write(FIELD_TYPE_BOOL | pair.key(), pair.value_bool()); - } - } - protoOutput->end(eventToken); + writeFieldValueTreeToStream(*bucket.mGaugeFields, protoOutput); protoOutput->end(atomToken); protoOutput->end(bucketInfoToken); - VLOG("\t bucket [%lld - %lld] content: %s", (long long)bucket.mBucketStartNs, - (long long)bucket.mBucketEndNs, bucket.mEvent->ToString().c_str()); + VLOG("\t bucket [%lld - %lld] includes %d gauge fields.", (long long)bucket.mBucketStartNs, + (long long)bucket.mBucketEndNs, (int)bucket.mGaugeFields->size()); } protoOutput->end(wrapperToken); } @@ -223,18 +197,13 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); } -shared_ptr<EventKV> GaugeMetricProducer::getGauge(const LogEvent& event) { - shared_ptr<EventKV> ret = make_shared<EventKV>(); - if (mGaugeFields.size() == 0) { - for (int i = 1; i <= event.size(); i++) { - ret->kv.push_back(event.GetKeyValueProto(i)); - } - } else { - for (int i = 0; i < (int)mGaugeFields.size(); i++) { - ret->kv.push_back(event.GetKeyValueProto(mGaugeFields[i])); - } +std::shared_ptr<FieldValueMap> GaugeMetricProducer::getGaugeFields(const LogEvent& event) { + std::shared_ptr<FieldValueMap> gaugeFields = + std::make_shared<FieldValueMap>(event.getFieldValueMap()); + if (!mFieldFilter.include_all()) { + filterFields(mFieldFilter.fields(), gaugeFields.get()); } - return ret; + return gaugeFields; } void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) { @@ -268,7 +237,7 @@ bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) void GaugeMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const map<string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) { if (condition == false) { return; @@ -285,21 +254,21 @@ void GaugeMetricProducer::onMatchedLogEventInternalLocked( if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end()) { return; } - shared_ptr<EventKV> gauge = getGauge(event); + std::shared_ptr<FieldValueMap> gaugeFields = getGaugeFields(event); if (hitGuardRailLocked(eventKey)) { return; } - (*mCurrentSlicedBucket)[eventKey] = gauge; + (*mCurrentSlicedBucket)[eventKey] = gaugeFields; // Anomaly detection on gauge metric only works when there is one numeric // field specified. if (mAnomalyTrackers.size() > 0) { - if (gauge->kv.size() == 1) { - KeyValuePair pair = gauge->kv[0]; + if (gaugeFields->size() == 1) { + const DimensionsValue& dimensionsValue = gaugeFields->begin()->second; long gaugeVal = 0; - if (pair.has_value_int()) { - gaugeVal = (long)pair.value_int(); - } else if (pair.has_value_long()) { - gaugeVal = pair.value_long(); + if (dimensionsValue.has_value_int()) { + gaugeVal = (long)dimensionsValue.value_int(); + } else if (dimensionsValue.has_value_long()) { + gaugeVal = dimensionsValue.value_long(); } for (auto& tracker : mAnomalyTrackers) { tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, @@ -313,12 +282,12 @@ void GaugeMetricProducer::updateCurrentSlicedBucketForAnomaly() { mCurrentSlicedBucketForAnomaly->clear(); status_t err = NO_ERROR; for (const auto& slice : *mCurrentSlicedBucket) { - KeyValuePair pair = slice.second->kv[0]; + const DimensionsValue& dimensionsValue = slice.second->begin()->second; long gaugeVal = 0; - if (pair.has_value_int()) { - gaugeVal = (long)pair.value_int(); - } else if (pair.has_value_long()) { - gaugeVal = pair.value_long(); + if (dimensionsValue.has_value_int()) { + gaugeVal = (long)dimensionsValue.value_int(); + } else if (dimensionsValue.has_value_long()) { + gaugeVal = dimensionsValue.value_long(); } (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal; } @@ -342,11 +311,10 @@ void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { info.mBucketNum = mCurrentBucketNum; for (const auto& slice : *mCurrentSlicedBucket) { - info.mEvent = slice.second; + info.mGaugeFields = slice.second; auto& bucketList = mPastBuckets[slice.first]; bucketList.push_back(info); - VLOG("gauge metric %s, dump key value: %s -> %s", mName.c_str(), - slice.first.c_str(), slice.second->ToString().c_str()); + VLOG("gauge metric %s, dump key value: %s", mName.c_str(), slice.first.c_str()); } // Reset counters @@ -357,7 +325,7 @@ void GaugeMetricProducer::flushIfNeededLocked(const uint64_t& eventTimeNs) { } } - mCurrentSlicedBucket = std::make_shared<DimToEventKVMap>(); + mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>(); // Adjusts the bucket start time int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs; diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 19d51e8c517e..2a6401d6dcac 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -35,10 +35,13 @@ namespace statsd { struct GaugeBucket { int64_t mBucketStartNs; int64_t mBucketEndNs; - std::shared_ptr<EventKV> mEvent; + std::shared_ptr<FieldValueMap> mGaugeFields; uint64_t mBucketNum; }; +typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<FieldValueMap>> + DimToGaugeFieldsMap; + // This gauge metric producer first register the puller to automatically pull the gauge at the // beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise // proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric @@ -57,12 +60,13 @@ public: protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) override; private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; + void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // for testing GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric, @@ -94,10 +98,10 @@ private: std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets; // The current bucket. - std::shared_ptr<DimToEventKVMap> mCurrentSlicedBucket = std::make_shared<DimToEventKVMap>(); + std::shared_ptr<DimToGaugeFieldsMap> mCurrentSlicedBucket; // The current bucket for anomaly detection. - std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>(); + std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly; // Translate Atom based bucket to single numeric value bucket for anomaly void updateCurrentSlicedBucketForAnomaly(); @@ -105,10 +109,10 @@ private: int mAtomTagId; // Whitelist of fields to report. Empty means all are reported. - std::vector<int> mGaugeFields; + FieldFilter mFieldFilter; // apply a whitelist on the original input - std::shared_ptr<EventKV> getGauge(const LogEvent& event); + std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event); // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const HashableDimensionKey& newKey); diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 528690832f03..4ed8289ab133 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -28,22 +28,12 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo return; } - HashableDimensionKey eventKey; - - if (mDimension.size() > 0) { - vector<KeyValuePair> key = getDimensionKey(event, mDimension); - eventKey = HashableDimensionKey(key); - } else { - eventKey = DEFAULT_DIMENSION_KEY; - } - bool condition; - - map<string, HashableDimensionKey> conditionKeys; + map<string, std::vector<HashableDimensionKey>> conditionKeys; if (mConditionSliced) { for (const auto& link : mConditionLinks) { - HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link); - conditionKeys[link.condition()] = conditionKey; + conditionKeys.insert(std::make_pair(link.condition(), + getDimensionKeysForCondition(event, link))); } if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) { condition = false; @@ -53,7 +43,17 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo } else { condition = mCondition; } - onMatchedLogEventInternalLocked(matcherIndex, eventKey, conditionKeys, condition, event); + + if (mDimensions.child_size() > 0) { + vector<DimensionsValue> dimensionValues = getDimensionKeys(event, mDimensions); + for (const DimensionsValue& dimensionValue : dimensionValues) { + onMatchedLogEventInternalLocked( + matcherIndex, HashableDimensionKey(dimensionValue), conditionKeys, condition, event); + } + } else { + onMatchedLogEventInternalLocked( + matcherIndex, DEFAULT_DIMENSION_KEY, conditionKeys, condition, event); + } } } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 647d8c1dcf79..fe1a53bef735 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -50,6 +50,7 @@ public: mConditionSliced(false), mWizard(wizard), mConditionTrackerIndex(conditionIndex){}; + virtual ~MetricProducer(){}; void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) override{ @@ -90,6 +91,10 @@ public: std::lock_guard<std::mutex> lock(mMutex); return onDumpReportLocked(dumpTimeNs, protoOutput); } + void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) { + std::lock_guard<std::mutex> lock(mMutex); + return onDumpReportLocked(dumpTimeNs, report); + } // Returns the memory in bytes currently used to store this metric's data. Does not change // state. @@ -112,11 +117,16 @@ public: return mBucketSizeNs; } + inline const string& getName() { + return mName; + } + protected: virtual void onConditionChangedLocked(const bool condition, const uint64_t eventTime) = 0; virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0; virtual void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) = 0; + virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0; virtual size_t byteSizeLocked() const = 0; const std::string mName; @@ -140,7 +150,7 @@ protected: int mConditionTrackerIndex; - std::vector<KeyMatcher> mDimension; // The dimension defined in statsd_config + FieldMatcher mDimensions; // The dimension defined in statsd_config std::vector<MetricConditionLink> mConditionLinks; @@ -163,7 +173,7 @@ protected: */ virtual void onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) = 0; // Consume the parsed stats log entry that already matched the "what" of the metric. diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 231bd8e4df99..9749e0f4af4f 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -49,7 +49,7 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, sp<UidMap> uidMap) : mConfigKey(key), mUidMap(uidMap) { mConfigValid = - initStatsdConfig(key, config, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers, + initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap); @@ -142,6 +142,12 @@ void MetricsManager::onUidMapReceived() { initLogSourceWhiteList(); } +void MetricsManager::onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report) { + for (auto& producer : mAllMetricProducers) { + producer->onDumpReport(dumpTimeStampNs, report->add_metrics()); + } +} + void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) { VLOG("=========================Metric Reports Start=========================="); uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC; diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 8faa75d34be9..51c4c90fe2ac 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -45,8 +45,9 @@ public: void onLogEvent(const LogEvent& event); - void onAnomalyAlarmFired(const uint64_t timestampNs, - unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet); + void onAnomalyAlarmFired( + const uint64_t timestampNs, + unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet); void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor); @@ -58,6 +59,7 @@ public: // Config source owner can call onDumpReport() to get all the metrics collected. virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput); + virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report); // Computes the total byte size of all metrics managed by a single config source. // Does not change the state. @@ -128,6 +130,9 @@ private: std::unordered_map<int, std::vector<int>> mConditionToMetricMap; void initLogSourceWhiteList(); + + FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions); + FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 40aed7bcf934..b58dc689fed4 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -19,6 +19,7 @@ #include "ValueMetricProducer.h" #include "guardrail/StatsdStats.h" +#include "stats_log_util.h" #include <cutils/log.h> #include <limits.h> @@ -54,12 +55,6 @@ const int FIELD_ID_DATA = 1; // for ValueMetricData const int FIELD_ID_DIMENSION = 1; const int FIELD_ID_BUCKET_INFO = 2; -// for KeyValuePair -const int FIELD_ID_KEY = 1; -const int FIELD_ID_VALUE_STR = 2; -const int FIELD_ID_VALUE_INT = 3; -const int FIELD_ID_VALUE_BOOL = 4; -const int FIELD_ID_VALUE_FLOAT = 5; // for ValueBucketInfo const int FIELD_ID_START_BUCKET_NANOS = 1; const int FIELD_ID_END_BUCKET_NANOS = 2; @@ -84,7 +79,7 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000; } - mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end()); + mDimensions = metric.dimensions(); if (metric.links().size() > 0) { mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(), @@ -121,6 +116,23 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventT VLOG("Metric %s onSlicedConditionMayChange", mName.c_str()); } +void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) { + flushIfNeededLocked(dumpTimeNs); + report->set_metric_name(mName); + report->set_start_report_nanos(mStartTimeNs); + auto value_metrics = report->mutable_value_metrics(); + for (const auto& pair : mPastBuckets) { + ValueMetricData* metricData = value_metrics->add_data(); + *metricData->mutable_dimension() = pair.first.getDimensionsValue(); + for (const auto& bucket : pair.second) { + ValueBucketInfo* bucketInfo = metricData->add_bucket_info(); + bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs); + bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs); + bucketInfo->set_value(bucket.mValue); + } + } +} + void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, ProtoOutputStream* protoOutput) { VLOG("metric %s dump report now...", mName.c_str()); @@ -132,26 +144,14 @@ void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, for (const auto& pair : mPastBuckets) { const HashableDimensionKey& hashableKey = pair.first; VLOG(" dimension key %s", hashableKey.c_str()); - const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs(); long long wrapperToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); - // First fill dimension (KeyValuePairs). - for (const auto& kv : kvs) { - long long dimensionToken = protoOutput->start( - FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); - protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key()); - if (kv.has_value_str()) { - protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str()); - } else if (kv.has_value_int()) { - protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int()); - } else if (kv.has_value_bool()) { - protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool()); - } else if (kv.has_value_float()) { - protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float()); - } - protoOutput->end(dimensionToken); - } + // First fill dimension. + long long dimensionToken = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION); + writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput); + protoOutput->end(dimensionToken); // Then fill bucket_info (ValueBucketInfo). for (const auto& bucket : pair.second) { @@ -256,7 +256,7 @@ bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) void ValueMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const map<string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) { uint64_t eventTimeNs = event.GetTimestampNs(); if (eventTimeNs < mCurrentBucketStartTimeNs) { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 2f27e4e52aef..6f3b1d1c67d7 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -50,12 +50,13 @@ public: protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const HashableDimensionKey& eventKey, - const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition, + const ConditionKey& conditionKey, bool condition, const LogEvent& event) override; private: void onDumpReportLocked(const uint64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) override; + void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override; // Internal interface to handle condition change. void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override; diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 658b73269284..97dbf7a4f7a1 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -49,7 +49,7 @@ bool handleMetricWithLogTrackers(const string what, const int metricIndex, ALOGW("cannot find the AtomMatcher \"%s\" in config", what.c_str()); return false; } - if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getTagIds().size() > 1) { + if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) { ALOGE("AtomMatcher \"%s\" has more than one tag ids. When a metric has dimension, " "the \"what\" can only about one atom type.", what.c_str()); @@ -92,7 +92,8 @@ bool handleMetricWithConditions( return true; } -bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap, +bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap, + unordered_map<string, int>& logTrackerMap, vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) { vector<AtomMatcher> matcherConfigs; const int atomMatcherCount = config.atom_matcher_size(); @@ -106,7 +107,7 @@ bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& log switch (logMatcher.contents_case()) { case AtomMatcher::ContentsCase::kSimpleAtomMatcher: allAtomMatchers.push_back(new SimpleLogMatchingTracker( - logMatcher.name(), index, logMatcher.simple_atom_matcher())); + logMatcher.name(), index, logMatcher.simple_atom_matcher(), uidMap)); break; case AtomMatcher::ContentsCase::kCombination: allAtomMatchers.push_back( @@ -131,7 +132,7 @@ bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& log return false; } // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only. - const set<int>& tagIds = matcher->getTagIds(); + const set<int>& tagIds = matcher->getAtomIds(); allTagIds.insert(tagIds.begin(), tagIds.end()); } return true; @@ -205,10 +206,13 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti // Align all buckets to same instant in MIN_BUCKET_SIZE_SEC, so that avoid alarm // clock will not grow very aggressive. New metrics will be delayed up to // MIN_BUCKET_SIZE_SEC before starting. - long currentTimeSec = time(nullptr); - uint64_t startTimeNs = (currentTimeSec - kMinBucketSizeSec - - (currentTimeSec - timeBaseSec) % kMinBucketSizeSec) * - NS_PER_SEC; + // Why not use timeBaseSec directly? +// long currentTimeSec = time(nullptr); +// uint64_t startTimeNs = (currentTimeSec - kMinBucketSizeSec - +// (currentTimeSec - timeBaseSec) % kMinBucketSizeSec) * +// NS_PER_SEC; + + uint64_t startTimeNs = timeBaseSec * NS_PER_SEC; // Build MetricProducers for each metric defined in config. // build CountMetricProducer @@ -222,7 +226,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti int metricIndex = allMetricProducers.size(); metricMap.insert({metric.name(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0, + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndex)) { return false; @@ -274,7 +278,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti int trackerIndices[3] = {-1, -1, -1}; if (!simplePredicate.has_start() || !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex, - metric.dimension_size() > 0, allAtomMatchers, + metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[0])) { ALOGE("Duration metrics must specify a valid the start event matcher"); return false; @@ -282,21 +286,19 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti if (simplePredicate.has_stop() && !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex, - metric.dimension_size() > 0, allAtomMatchers, + metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[1])) { return false; } if (simplePredicate.has_stop_all() && !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex, - metric.dimension_size() > 0, allAtomMatchers, + metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndices[2])) { return false; } - vector<KeyMatcher> internalDimension; - internalDimension.insert(internalDimension.begin(), simplePredicate.dimension().begin(), - simplePredicate.dimension().end()); + FieldMatcher internalDimensions = simplePredicate.dimensions(); int conditionIndex = -1; @@ -316,7 +318,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti sp<MetricProducer> durationMetric = new DurationMetricProducer( key, metric, conditionIndex, trackerIndices[0], trackerIndices[1], - trackerIndices[2], nesting, wizard, internalDimension, startTimeNs); + trackerIndices[2], nesting, wizard, internalDimensions, startTimeNs); allMetricProducers.push_back(durationMetric); } @@ -368,7 +370,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti int metricIndex = allMetricProducers.size(); metricMap.insert({metric.name(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0, + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndex)) { return false; @@ -376,10 +378,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex); // If it is pulled atom, it should be simple matcher with one tagId. - if (atomMatcher->getTagIds().size() != 1) { + if (atomMatcher->getAtomIds().size() != 1) { return false; } - int atomTagId = *(atomMatcher->getTagIds().begin()); + int atomTagId = *(atomMatcher->getAtomIds().begin()); int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1; int conditionIndex = -1; @@ -410,15 +412,15 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti return false; } - if ((!metric.gauge_fields().has_include_all() || - (metric.gauge_fields().include_all() == false)) && - metric.gauge_fields().field_num_size() == 0) { + if ((!metric.gauge_fields_filter().has_include_all() || + (metric.gauge_fields_filter().include_all() == false)) && + !hasLeafNode(metric.gauge_fields_filter().fields())) { ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str()); return false; } - if ((metric.gauge_fields().has_include_all() && - metric.gauge_fields().include_all() == true) && - metric.gauge_fields().field_num_size() > 0) { + if ((metric.gauge_fields_filter().has_include_all() && + metric.gauge_fields_filter().include_all() == true) && + hasLeafNode(metric.gauge_fields_filter().fields())) { ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str()); return false; } @@ -426,7 +428,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti int metricIndex = allMetricProducers.size(); metricMap.insert({metric.name(), metricIndex}); int trackerIndex; - if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0, + if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(), allAtomMatchers, logTrackerMap, trackerToMetricMap, trackerIndex)) { return false; @@ -434,10 +436,10 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex); // If it is pulled atom, it should be simple matcher with one tagId. - if (atomMatcher->getTagIds().size() != 1) { + if (atomMatcher->getAtomIds().size() != 1) { return false; } - int atomTagId = *(atomMatcher->getTagIds().begin()); + int atomTagId = *(atomMatcher->getAtomIds().begin()); int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1; int conditionIndex = -1; @@ -462,7 +464,8 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long ti return true; } -bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& metricProducerMap, +bool initAlerts(const StatsdConfig& config, + const unordered_map<string, int>& metricProducerMap, vector<sp<MetricProducer>>& allMetricProducers, vector<sp<AnomalyTracker>>& allAnomalyTrackers) { for (int i = 0; i < config.alert_size(); i++) { @@ -488,7 +491,9 @@ bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& me return true; } -bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, set<int>& allTagIds, +bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, + const UidMap& uidMap, + const long timeBaseSec, set<int>& allTagIds, vector<sp<LogMatchingTracker>>& allAtomMatchers, vector<sp<ConditionTracker>>& allConditionTrackers, vector<sp<MetricProducer>>& allMetricProducers, @@ -500,7 +505,7 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const lo unordered_map<string, int> conditionTrackerMap; unordered_map<string, int> metricProducerMap; - if (!initLogTrackers(config, logTrackerMap, allAtomMatchers, allTagIds)) { + if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) { ALOGE("initLogMatchingTrackers failed"); return false; } diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 333733234699..9ad5176ee6fd 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -43,6 +43,7 @@ namespace statsd { // [allAtomMatchers]: should store the sp to all the LogMatchingTracker // [allTagIds]: contains the set of all interesting tag ids to this config. bool initLogTrackers(const StatsdConfig& config, + const UidMap& uidMap, std::unordered_map<std::string, int>& logTrackerMap, std::vector<sp<LogMatchingTracker>>& allAtomMatchers, std::set<int>& allTagIds); @@ -89,7 +90,10 @@ bool initMetrics( // Initialize MetricsManager from StatsdConfig. // Parameters are the members of MetricsManager. See MetricsManager for declaration. -bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, std::set<int>& allTagIds, +bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, + + const UidMap& uidMap, + const long timeBaseSec, std::set<int>& allTagIds, std::vector<sp<LogMatchingTracker>>& allAtomMatchers, std::vector<sp<ConditionTracker>>& allConditionTrackers, std::vector<sp<MetricProducer>>& allMetricProducers, diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp index 21a9cf319375..416b87b36c54 100644 --- a/cmds/statsd/src/packages/UidMap.cpp +++ b/cmds/statsd/src/packages/UidMap.cpp @@ -404,4 +404,4 @@ set<int32_t> UidMap::getAppUid(const string& package) const { } // namespace statsd } // namespace os -} // namespace android +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h index a9aec94a932e..02dea54f1b50 100644 --- a/cmds/statsd/src/packages/UidMap.h +++ b/cmds/statsd/src/packages/UidMap.h @@ -168,4 +168,3 @@ private: } // namespace statsd } // namespace os } // namespace android - diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 3c85c57ab037..f5282eaaecd4 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -24,8 +24,14 @@ option java_outer_classname = "StatsLog"; import "frameworks/base/cmds/statsd/src/atoms.proto"; -message KeyValuePair { - optional int32 key = 1; +message Field { + optional int32 field = 1; + optional int32 position_index = 2 [default = -1]; + repeated Field child = 3; +} + +message DimensionsValue { + optional int32 field = 1; oneof value { string value_str = 2; @@ -33,9 +39,14 @@ message KeyValuePair { int64 value_long = 4; bool value_bool = 5; float value_float = 6; + DimensionsValueTuple value_tuple = 7; } } +message DimensionsValueTuple { + repeated DimensionsValue dimensions_value = 1; +} + message EventMetricData { optional int64 timestamp_nanos = 1; @@ -51,7 +62,7 @@ message CountBucketInfo { } message CountMetricData { - repeated KeyValuePair dimension = 1; + optional DimensionsValue dimension = 1; repeated CountBucketInfo bucket_info = 2; } @@ -65,7 +76,7 @@ message DurationBucketInfo { } message DurationMetricData { - repeated KeyValuePair dimension = 1; + optional DimensionsValue dimension = 1; repeated DurationBucketInfo bucket_info = 2; } @@ -79,7 +90,7 @@ message ValueBucketInfo { } message ValueMetricData { - repeated KeyValuePair dimension = 1; + optional DimensionsValue dimension = 1; repeated ValueBucketInfo bucket_info = 2; } @@ -93,7 +104,7 @@ message GaugeBucketInfo { } message GaugeMetricData { - repeated KeyValuePair dimension = 1; + optional DimensionsValue dimension = 1; repeated GaugeBucketInfo bucket_info = 2; } diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp new file mode 100644 index 000000000000..476e11724318 --- /dev/null +++ b/cmds/statsd/src/stats_log_util.cpp @@ -0,0 +1,227 @@ +/* + * 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 "stats_log_util.h" + +#include <set> +#include <stack> +#include <utils/Log.h> + +using android::util::FIELD_COUNT_REPEATED; +using android::util::FIELD_TYPE_BOOL; +using android::util::FIELD_TYPE_FLOAT; +using android::util::FIELD_TYPE_INT32; +using android::util::FIELD_TYPE_INT64; +using android::util::FIELD_TYPE_MESSAGE; +using android::util::FIELD_TYPE_STRING; +using android::util::ProtoOutputStream; + +namespace android { +namespace os { +namespace statsd { + +// for DimensionsValue Proto +const int DIMENSIONS_VALUE_FIELD = 1; +const int DIMENSIONS_VALUE_VALUE_STR = 2; +const int DIMENSIONS_VALUE_VALUE_INT = 3; +const int DIMENSIONS_VALUE_VALUE_LONG = 4; +const int DIMENSIONS_VALUE_VALUE_BOOL = 5; +const int DIMENSIONS_VALUE_VALUE_FLOAT = 6; +const int DIMENSIONS_VALUE_VALUE_TUPLE = 7; + +// for MessageValue Proto +const int FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO = 1; + +void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue, + ProtoOutputStream* protoOutput) { + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field()); + switch (dimensionsValue.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR, + dimensionsValue.value_str()); + break; + case DimensionsValue::ValueCase::kValueInt: + protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT, + dimensionsValue.value_int()); + break; + case DimensionsValue::ValueCase::kValueLong: + protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG, + dimensionsValue.value_long()); + break; + case DimensionsValue::ValueCase::kValueBool: + protoOutput->write(FIELD_TYPE_BOOL | DIMENSIONS_VALUE_VALUE_BOOL, + dimensionsValue.value_bool()); + break; + case DimensionsValue::ValueCase::kValueFloat: + protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT, + dimensionsValue.value_float()); + break; + case DimensionsValue::ValueCase::kValueTuple: + { + long long tupleToken = protoOutput->start( + FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE); + for (int i = 0; i < dimensionsValue.value_tuple().dimensions_value_size(); ++i) { + long long token = protoOutput->start( + FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED + | FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO); + writeDimensionsValueProtoToStream( + dimensionsValue.value_tuple().dimensions_value(i), protoOutput); + protoOutput->end(token); + } + protoOutput->end(tupleToken); + } + break; + default: + break; + } +} + +// for Field Proto +const int FIELD_FIELD = 1; +const int FIELD_POSITION_INDEX = 2; +const int FIELD_CHILD = 3; + +void writeFieldProtoToStream( + const Field& field, util::ProtoOutputStream* protoOutput) { + protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field()); + if (field.has_position_index()) { + protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index()); + } + for (int i = 0; i < field.child_size(); ++i) { + long long childToken = protoOutput->start( + FIELD_TYPE_MESSAGE| FIELD_COUNT_REPEATED | FIELD_CHILD); + writeFieldProtoToStream(field.child(i), protoOutput); + protoOutput->end(childToken); + } +} + +namespace { + +void addOrUpdateChildrenMap( + const Field& root, + const Field& node, + std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) { + Field parentNode = root; + if (node.has_position_index()) { + appendLeaf(&parentNode, node.field(), node.position_index()); + } else { + appendLeaf(&parentNode, node.field()); + } + if (childrenMap->find(parentNode) == childrenMap->end()) { + childrenMap->insert(std::make_pair(parentNode, std::set<Field, FieldCmp>{})); + } + auto it = childrenMap->find(parentNode); + for (int i = 0; i < node.child_size(); ++i) { + auto child = node.child(i); + Field childNode = parentNode; + if (child.has_position_index()) { + appendLeaf(&childNode, child.field(), child.position_index()); + } else { + appendLeaf(&childNode, child.field()); + } + it->second.insert(childNode); + addOrUpdateChildrenMap(parentNode, child, childrenMap); + } +} + +void addOrUpdateChildrenMap( + const Field& field, + std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) { + Field root; + addOrUpdateChildrenMap(root, field, childrenMap); +} + +} // namespace + +void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap, + util::ProtoOutputStream* protoOutput) { + std::map<Field, std::set<Field, FieldCmp>, FieldCmp> childrenMap; + // Rebuild the field tree. + for (auto it = fieldValueMap.begin(); it != fieldValueMap.end(); ++it) { + addOrUpdateChildrenMap(it->first, &childrenMap); + } + std::stack<std::pair<long long, Field>> tokenStack; + // Iterate over the node tree to fill the Atom proto. + for (auto it = childrenMap.begin(); it != childrenMap.end(); ++it) { + const Field* nodeLeaf = getSingleLeaf(&it->first); + const int fieldNum = nodeLeaf->field(); + while (!tokenStack.empty()) { + auto currentMsgNode = tokenStack.top().second; + auto currentMsgNodeChildrenIt = childrenMap.find(currentMsgNode); + if (currentMsgNodeChildrenIt->second.find(it->first) == + currentMsgNodeChildrenIt->second.end()) { + protoOutput->end(tokenStack.top().first); + tokenStack.pop(); + } else { + break; + } + } + if (it->second.size() == 0) { + auto itValue = fieldValueMap.find(it->first); + if (itValue != fieldValueMap.end()) { + const DimensionsValue& value = itValue->second; + switch (value.value_case()) { + case DimensionsValue::ValueCase::kValueStr: + protoOutput->write(FIELD_TYPE_STRING | fieldNum, + value.value_str()); + break; + case DimensionsValue::ValueCase::kValueInt: + protoOutput->write(FIELD_TYPE_INT32 | fieldNum, + value.value_int()); + break; + case DimensionsValue::ValueCase::kValueLong: + protoOutput->write(FIELD_TYPE_INT64 | fieldNum, + value.value_long()); + break; + case DimensionsValue::ValueCase::kValueBool: + protoOutput->write(FIELD_TYPE_BOOL | fieldNum, + value.value_bool()); + break; + case DimensionsValue::ValueCase::kValueFloat: + protoOutput->write(FIELD_TYPE_FLOAT | fieldNum, + value.value_float()); + break; + // This would not happen as the node has no child. + case DimensionsValue::ValueCase::kValueTuple: + break; + default: + break; + } + } else { + ALOGE("Leaf node value not found. This should never happen."); + } + } else { + long long token; + if (nodeLeaf->has_position_index()) { + token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum); + } else { + token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum); + } + tokenStack.push(std::make_pair(token, it->first)); + } + } + + while (!tokenStack.empty()) { + protoOutput->end(tokenStack.top().first); + tokenStack.pop(); + } + + +} + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h new file mode 100644 index 000000000000..1f8186083d12 --- /dev/null +++ b/cmds/statsd/src/stats_log_util.h @@ -0,0 +1,41 @@ +/* + * 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. + */ + +#pragma once + +#include <android/util/ProtoOutputStream.h> +#include "frameworks/base/cmds/statsd/src/stats_log.pb.h" +#include "field_util.h" + +namespace android { +namespace os { +namespace statsd { + +// Helper function to write DimensionsValue proto to ProtoOutputStream. +void writeDimensionsValueProtoToStream( + const DimensionsValue& fieldValue, util::ProtoOutputStream* protoOutput); + +// Helper function to write Field proto to ProtoOutputStream. +void writeFieldProtoToStream( + const Field& field, util::ProtoOutputStream* protoOutput); + +// Helper function to construct the field value tree and write to ProtoOutputStream +void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap, + util::ProtoOutputStream* protoOutput); + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h index 1cdf031c7858..e46b8ebe0021 100644 --- a/cmds/statsd/src/stats_util.h +++ b/cmds/statsd/src/stats_util.h @@ -27,45 +27,15 @@ namespace android { namespace os { namespace statsd { -const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(vector<KeyValuePair>()); +const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(); // Minimum bucket size in seconds const long kMinBucketSizeSec = 5 * 60; -typedef std::map<std::string, HashableDimensionKey> ConditionKey; +typedef std::map<std::string, std::vector<HashableDimensionKey>> ConditionKey; typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap; -/* - * In memory rep for LogEvent. Uses much less memory than LogEvent - */ -typedef struct EventKV { - std::vector<KeyValuePair> kv; - string ToString() const { - std::ostringstream result; - result << "{ "; - const size_t N = kv.size(); - for (size_t i = 0; i < N; i++) { - result << " "; - result << (i + 1); - result << "->"; - const auto& pair = kv[i]; - if (pair.has_value_int()) { - result << pair.value_int(); - } else if (pair.has_value_long()) { - result << pair.value_long(); - } else if (pair.has_value_float()) { - result << pair.value_float(); - } else if (pair.has_value_str()) { - result << pair.value_str().c_str(); - } - } - result << " }"; - return result.str(); - } -} EventKV; - -typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<EventKV>> DimToEventKVMap; } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index 4729f6acc621..1ed1e05ba272 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -22,30 +22,52 @@ package android.os.statsd; option java_package = "com.android.internal.os"; option java_outer_classname = "StatsdConfigProto"; -message KeyMatcher { - optional int32 key = 1; +enum Position { + POSITION_UNKNOWN = 0; + FIRST = 1; + LAST = 2; + ANY = 3; +} + +message FieldMatcher { + optional int32 field = 1; - optional bool as_package_name = 2 [default = false]; + optional Position position = 2; + + repeated FieldMatcher child = 3; } -message KeyValueMatcher { - optional KeyMatcher key_matcher = 1; +message FieldValueMatcher { + // Field id, as specified in the atom proto message. + optional int32 field = 1; + + // For repeated fields, specifies the position in the array. + // FIRST and LAST mean that if the values are found at the first + // or last position, it's a match. ANY means that if the values are found + // anywhere in the array, then it's a match. + optional Position position = 2; oneof value_matcher { - bool eq_bool = 2; - string eq_string = 3; - int32 eq_int = 4; + bool eq_bool = 3; + string eq_string = 4; + int32 eq_int = 5; - int64 lt_int = 5; - int64 gt_int = 6; - float lt_float = 7; - float gt_float = 8; + int64 lt_int = 6; + int64 gt_int = 7; + float lt_float = 8; + float gt_float = 9; - int64 lte_int = 9; - int64 gte_int = 10; + int64 lte_int = 10; + int64 gte_int = 11; + + MessageMatcher matches_tuple = 12; } } +message MessageMatcher { + repeated FieldValueMatcher field_value_matcher = 1; +} + enum LogicalOperation { LOGICAL_OPERATION_UNSPECIFIED = 0; AND = 1; @@ -56,9 +78,9 @@ enum LogicalOperation { } message SimpleAtomMatcher { - optional int32 tag = 1; + optional int32 atom_id = 1; - repeated KeyValueMatcher key_value_matcher = 2; + repeated FieldValueMatcher field_value_matcher = 2; } message AtomMatcher { @@ -90,7 +112,7 @@ message SimplePredicate { } optional InitialValue initial_value = 5 [default = FALSE]; - repeated KeyMatcher dimension = 6; + optional FieldMatcher dimensions = 6; } message Predicate { @@ -115,14 +137,14 @@ message Bucket { message MetricConditionLink { optional string condition = 1; - repeated KeyMatcher key_in_what = 2; + optional FieldMatcher dimensions_in_what = 2; - repeated KeyMatcher key_in_condition = 3; + optional FieldMatcher dimensions_in_condition = 3; } message FieldFilter { - optional bool include_all = 1; - repeated int32 field_num = 2; + optional bool include_all = 1 [default = false]; + optional FieldMatcher fields = 2; } message EventMetric { @@ -142,7 +164,7 @@ message CountMetric { optional string condition = 3; - repeated KeyMatcher dimension = 4; + optional FieldMatcher dimensions = 4; optional Bucket bucket = 5; @@ -165,7 +187,7 @@ message DurationMetric { } optional AggregationType aggregation_type = 5 [default = SUM]; - repeated KeyMatcher dimension = 6; + optional FieldMatcher dimensions = 6; optional Bucket bucket = 7; } @@ -175,11 +197,11 @@ message GaugeMetric { optional string what = 2; - optional FieldFilter gauge_fields = 3; + optional FieldFilter gauge_fields_filter = 3; optional string condition = 4; - repeated KeyMatcher dimension = 5; + optional FieldMatcher dimensions = 5; optional Bucket bucket = 6; @@ -195,7 +217,7 @@ message ValueMetric { optional string condition = 4; - repeated KeyMatcher dimension = 5; + optional FieldMatcher dimensions = 5; optional Bucket bucket = 6; diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp index 1ec91558f790..111b4ba7a32c 100644 --- a/cmds/statsd/tests/LogEntryMatcher_test.cpp +++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp @@ -32,65 +32,313 @@ const int FIELD_ID_1 = 1; const int FIELD_ID_2 = 2; const int FIELD_ID_3 = 2; +const int ATTRIBUTION_UID_FIELD_ID = 1; +const int ATTRIBUTION_TAG_FIELD_ID = 2; + // Private API from liblog. extern "C" void android_log_rewind(android_log_context ctx); #ifdef __ANDROID__ TEST(AtomMatcherTest, TestSimpleMatcher) { + UidMap uidMap; + // Set up the matcher AtomMatcher matcher; auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_tag(TAG_ID); + simpleMatcher->set_atom_id(TAG_ID); LogEvent event(TAG_ID, 0); + EXPECT_TRUE(event.write(11)); event.init(); // Test - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Wrong tag id. + simpleMatcher->set_atom_id(TAG_ID + 1); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); +} + +TEST(AtomMatcherTest, TestAttributionMatcher) { + UidMap uidMap; + AttributionNode attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("location1"); + + AttributionNode attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("location2"); + + AttributionNode attribution_node3; + attribution_node3.set_uid(3333); + attribution_node3.set_tag("location3"); + std::vector<AttributionNode> attribution_nodes = + { attribution_node1, attribution_node2, attribution_node3 }; + + // Set up the event + LogEvent event(TAG_ID, 0); + event.write(attribution_nodes); + event.write("some value"); + // Convert to a LogEvent + event.init(); + + // Set up the matcher + AtomMatcher matcher; + auto simpleMatcher = matcher.mutable_simple_atom_matcher(); + simpleMatcher->set_atom_id(TAG_ID); + + // Match first node. + auto attributionMatcher = simpleMatcher->add_field_value_matcher(); + attributionMatcher->set_field(FIELD_ID_1); + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( + ATTRIBUTION_TAG_FIELD_ID); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("tag"); + + auto fieldMatcher = simpleMatcher->add_field_value_matcher(); + fieldMatcher->set_field(FIELD_ID_2); + fieldMatcher->set_eq_string("some value"); + + // Tag not matched. + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("location3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Match last node. + attributionMatcher->set_position(Position::LAST); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Match any node. + attributionMatcher->set_position(Position::ANY); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("location2"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("location4"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Attribution match but primitive field not match. + attributionMatcher->set_position(Position::ANY); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("location2"); + fieldMatcher->set_eq_string("wrong value"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + fieldMatcher->set_eq_string("some value"); + + // Uid match. + attributionMatcher->set_position(Position::ANY); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field( + ATTRIBUTION_UID_FIELD_ID); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("pkg0"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + uidMap.updateMap({1111, 1111, 2222, 3333, 3333} /* uid list */, + {1, 1, 2, 1, 2} /* version list */, + {android::String16("pkg0"), android::String16("pkg1"), + android::String16("pkg1"), android::String16("Pkg2"), + android::String16("PkG3")} /* package name list */); + + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg2"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg0"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg0"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg2"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::LAST); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg0"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg2"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + // Uid + tag. + attributionMatcher->set_position(Position::ANY); + attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field( + ATTRIBUTION_TAG_FIELD_ID); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg0"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location2"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg2"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::FIRST); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg0"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location2"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg2"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location3"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + + attributionMatcher->set_position(Position::LAST); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg0"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg1"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location2"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg2"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location3"); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0) + ->set_eq_string("pkg3"); + attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1) + ->set_eq_string("location1"); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); } TEST(AtomMatcherTest, TestBoolMatcher) { + UidMap uidMap; // Set up the matcher AtomMatcher matcher; auto simpleMatcher = matcher.mutable_simple_atom_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); + simpleMatcher->set_atom_id(TAG_ID); + auto keyValue1 = simpleMatcher->add_field_value_matcher(); + keyValue1->set_field(FIELD_ID_1); + auto keyValue2 = simpleMatcher->add_field_value_matcher(); + keyValue2->set_field(FIELD_ID_2); // Set up the event LogEvent event(TAG_ID, 0); - event.write(true); - event.write(false); + EXPECT_TRUE(event.write(true)); + EXPECT_TRUE(event.write(false)); // Convert to a LogEvent event.init(); // Test keyValue1->set_eq_bool(true); keyValue2->set_eq_bool(false); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue1->set_eq_bool(false); keyValue2->set_eq_bool(false); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); - keyValue1->set_eq_bool(true); - keyValue2->set_eq_bool(false); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + keyValue1->set_eq_bool(false); + keyValue2->set_eq_bool(true); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue1->set_eq_bool(true); keyValue2->set_eq_bool(true); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); } TEST(AtomMatcherTest, TestStringMatcher) { + UidMap uidMap; // Set up the matcher AtomMatcher matcher; auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_tag(TAG_ID); - auto keyValue = simpleMatcher->add_key_value_matcher(); - keyValue->mutable_key_matcher()->set_key(FIELD_ID_1); + simpleMatcher->set_atom_id(TAG_ID); + auto keyValue = simpleMatcher->add_field_value_matcher(); + keyValue->set_field(FIELD_ID_1); keyValue->set_eq_string("some value"); // Set up the event @@ -100,18 +348,19 @@ TEST(AtomMatcherTest, TestStringMatcher) { event.init(); // Test - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); } TEST(AtomMatcherTest, TestMultiFieldsMatcher) { + UidMap uidMap; // Set up the matcher AtomMatcher matcher; auto simpleMatcher = matcher.mutable_simple_atom_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); + simpleMatcher->set_atom_id(TAG_ID); + auto keyValue1 = simpleMatcher->add_field_value_matcher(); + keyValue1->set_field(FIELD_ID_1); + auto keyValue2 = simpleMatcher->add_field_value_matcher(); + keyValue2->set_field(FIELD_ID_2); // Set up the event LogEvent event(TAG_ID, 0); @@ -124,25 +373,26 @@ TEST(AtomMatcherTest, TestMultiFieldsMatcher) { // Test keyValue1->set_eq_int(2); keyValue2->set_eq_int(3); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue1->set_eq_int(2); keyValue2->set_eq_int(4); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue1->set_eq_int(4); keyValue2->set_eq_int(3); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); } TEST(AtomMatcherTest, TestIntComparisonMatcher) { + UidMap uidMap; // Set up the matcher AtomMatcher matcher; auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_tag(TAG_ID); - auto keyValue = simpleMatcher->add_key_value_matcher(); - keyValue->mutable_key_matcher()->set_key(FIELD_ID_1); + simpleMatcher->set_atom_id(TAG_ID); + auto keyValue = simpleMatcher->add_field_value_matcher(); + keyValue->set_field(FIELD_ID_1); // Set up the event LogEvent event(TAG_ID, 0); @@ -153,82 +403,83 @@ TEST(AtomMatcherTest, TestIntComparisonMatcher) { // eq_int keyValue->set_eq_int(10); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_eq_int(11); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_eq_int(12); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); // lt_int keyValue->set_lt_int(10); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_lt_int(11); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_lt_int(12); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); // lte_int keyValue->set_lte_int(10); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_lte_int(11); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_lte_int(12); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); // gt_int keyValue->set_gt_int(10); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_gt_int(11); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_gt_int(12); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); // gte_int keyValue->set_gte_int(10); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_gte_int(11); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event)); keyValue->set_gte_int(12); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event)); } TEST(AtomMatcherTest, TestFloatComparisonMatcher) { + UidMap uidMap; // Set up the matcher AtomMatcher matcher; auto simpleMatcher = matcher.mutable_simple_atom_matcher(); - simpleMatcher->set_tag(TAG_ID); + simpleMatcher->set_atom_id(TAG_ID); - auto keyValue = simpleMatcher->add_key_value_matcher(); - keyValue->mutable_key_matcher()->set_key(FIELD_ID_1); + auto keyValue = simpleMatcher->add_field_value_matcher(); + keyValue->set_field(FIELD_ID_1); LogEvent event1(TAG_ID, 0); keyValue->set_lt_float(10.0); event1.write(10.1f); event1.init(); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event1)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1)); LogEvent event2(TAG_ID, 0); event2.write(9.9f); event2.init(); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event2)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2)); LogEvent event3(TAG_ID, 0); event3.write(10.1f); event3.init(); keyValue->set_gt_float(10.0); - EXPECT_TRUE(matchesSimple(*simpleMatcher, event3)); + EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3)); LogEvent event4(TAG_ID, 0); event4.write(9.9f); event4.init(); - EXPECT_FALSE(matchesSimple(*simpleMatcher, event4)); + EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4)); } // Helper for the composite matchers. void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) { - simpleMatcher->set_tag(tag); - auto keyValue = simpleMatcher->add_key_value_matcher(); - keyValue->mutable_key_matcher()->set_key(key); + simpleMatcher->set_atom_id(tag); + auto keyValue = simpleMatcher->add_field_value_matcher(); + keyValue->set_field(key); keyValue->set_eq_int(val); } diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp new file mode 100644 index 000000000000..fd28460e8e01 --- /dev/null +++ b/cmds/statsd/tests/LogEvent_test.cpp @@ -0,0 +1,573 @@ +// 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 <gtest/gtest.h> +#include <log/log_event_list.h> +#include "src/logd/LogEvent.h" + +#ifdef __ANDROID__ + +namespace android { +namespace os { +namespace statsd { + +TEST(LogEventTest, testEmptyEvent) { + const int32_t TAG_ID = 123; + LogEvent event(TAG_ID, 0); + event.init(); + + DimensionsValue dimensionsValue; + EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(234, &dimensionsValue)); + FieldMatcher dimensions; + dimensions.set_field(event.GetTagId()); + EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); + + dimensions.add_child()->set_field(3); + dimensions.mutable_child(0)->set_position(Position::FIRST); + EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); + + dimensions.mutable_child(0)->set_position(Position::ANY); + EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); + + dimensions.mutable_child(0)->set_position(Position::LAST); + EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue)); +} + +TEST(LogEventTest, testRepeatedAttributionNode) { + const int32_t TAG_ID = 123; + LogEvent event(TAG_ID, 0); + AttributionNode attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("locationService"); + + AttributionNode attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("locationService2"); + + AttributionNode attribution_node3; + attribution_node3.set_uid(3333); + attribution_node3.set_tag("locationService3"); + std::vector<AttributionNode> attribution_nodes = + {attribution_node1, attribution_node2, attribution_node3}; + + // 1nd field: int32. + EXPECT_TRUE(event.write(int32_t(11))); + // 2rd field: float. + EXPECT_TRUE(event.write(3.45f)); + // Here it assume that the atom proto contains a repeated AttributionNode field. + // 3rd field: attribution node. This is repeated field. + EXPECT_TRUE(event.write(attribution_nodes)); + // 4th field: bool. + EXPECT_TRUE(event.write(true)); + // 5th field: long. + EXPECT_TRUE(event.write(uint64_t(1234))); + + event.init(); + + DimensionsValue dimensionsValue; + // Query single primitive fields. + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); + // The bool value is stored in value_int field as logD does not support bool. + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); + + // First attribution. + FieldMatcher first_uid_dimensions; + first_uid_dimensions.set_field(event.GetTagId()); + first_uid_dimensions.add_child()->set_field(3); + first_uid_dimensions.mutable_child(0)->set_position(Position::FIRST); + first_uid_dimensions.mutable_child(0)->add_child()->set_field(1); + EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_uid_dimensions, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 1111); + + FieldMatcher first_tag_dimensions = first_uid_dimensions; + first_tag_dimensions.mutable_child(0)->mutable_child(0)->set_field(2); + EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_tag_dimensions, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_str(), "locationService"); + + FieldMatcher first_attribution_dimensions = first_uid_dimensions; + first_attribution_dimensions.mutable_child(0)->add_child()->set_field(2); + EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_attribution_dimensions, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 1111); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).value_str(), "locationService"); + + FieldMatcher last_attribution_dimensions = first_attribution_dimensions; + last_attribution_dimensions.mutable_child(0)->set_position(Position::LAST); + EXPECT_TRUE(event.GetAtomDimensionsValueProto(last_attribution_dimensions, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 3333); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).value_str(), "locationService3"); + + FieldMatcher any_attribution_dimensions = first_attribution_dimensions; + any_attribution_dimensions.mutable_child(0)->set_position(Position::ANY); + std::vector<DimensionsValue> dimensionsValues; + event.GetAtomDimensionsValueProtos(any_attribution_dimensions, &dimensionsValues); + EXPECT_EQ(dimensionsValues.size(), 3u); + EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId()); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 1111); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).value_str(), "locationService"); + EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId()); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 2222); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).value_str(), "locationService2"); + EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId()); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 3333); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).value_str(), "locationService3"); + + FieldMatcher mixed_dimensions = any_attribution_dimensions; + mixed_dimensions.add_child()->set_field(1000); + mixed_dimensions.add_child()->set_field(6); // missing field. + mixed_dimensions.add_child()->set_field(3); // position not set. + mixed_dimensions.add_child()->set_field(5); + mixed_dimensions.add_child()->set_field(1); + dimensionsValues.clear(); + event.GetAtomDimensionsValueProtos(mixed_dimensions, &dimensionsValues); + EXPECT_EQ(dimensionsValues.size(), 3u); + EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId()); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 3); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), + 1111); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), + "locationService"); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).field(), 5); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).value_long(), long(1234)); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).field(), 1); + EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).value_int(), 11); + + EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId()); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 3); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), + 2222); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), + "locationService2"); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).field(), 5); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).value_long(), long(1234)); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).field(), 1); + EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).value_int(), 11); + + EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId()); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 3); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(), + 3333); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(), + "locationService3"); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).field(), 5); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).value_long(), long(1234)); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).field(), 1); + EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).value_int(), 11); + + FieldMatcher wrong_dimensions = mixed_dimensions; + // Wrong tagId. + wrong_dimensions.set_field(event.GetTagId() + 100); + dimensionsValues.clear(); + event.GetAtomDimensionsValueProtos(wrong_dimensions, &dimensionsValues); + EXPECT_TRUE(dimensionsValues.empty()); +} + +TEST(LogEventTest, testMessageField) { + const int32_t TAG_ID = 123; + LogEvent event(TAG_ID, 0); + AttributionNode attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("locationService"); + + AttributionNode attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("locationService2"); + + // 1nd field: int32. + EXPECT_TRUE(event.write(int32_t(11))); + // 2rd field: float. + EXPECT_TRUE(event.write(3.45f)); + // Here it assume that the atom proto contains two optional AttributionNode fields. + // 3rd field: attribution node. This is not repeated field. + EXPECT_TRUE(event.write(attribution_node1)); + // 4th field: another attribution field. This is not repeated field. + EXPECT_TRUE(event.write(attribution_node2)); + // 5th field: bool. + EXPECT_TRUE(event.write(true)); + // 6th field: long. + EXPECT_TRUE(event.write(uint64_t(1234))); + + event.init(); + + FieldMatcher uid_dimensions1; + uid_dimensions1.set_field(event.GetTagId()); + uid_dimensions1.add_child()->set_field(3); + uid_dimensions1.mutable_child(0)->add_child()->set_field(1); + + FieldMatcher tag_dimensions1; + tag_dimensions1.set_field(event.GetTagId()); + tag_dimensions1.add_child()->set_field(3); + tag_dimensions1.mutable_child(0)->add_child()->set_field(2); + + FieldMatcher attribution_dimensions1; + attribution_dimensions1.set_field(event.GetTagId()); + attribution_dimensions1.add_child()->set_field(3); + attribution_dimensions1.mutable_child(0)->add_child()->set_field(1); + attribution_dimensions1.mutable_child(0)->add_child()->set_field(2); + + FieldMatcher uid_dimensions2 = uid_dimensions1; + uid_dimensions2.mutable_child(0)->set_field(4); + + FieldMatcher tag_dimensions2 = tag_dimensions1; + tag_dimensions2.mutable_child(0)->set_field(4); + + FieldMatcher attribution_dimensions2 = attribution_dimensions1; + attribution_dimensions2.mutable_child(0)->set_field(4); + + FieldMatcher mixed_dimensions = attribution_dimensions1; + mixed_dimensions.add_child()->set_field(4); + mixed_dimensions.mutable_child(1)->add_child()->set_field(1); + mixed_dimensions.add_child()->set_field(1000); + mixed_dimensions.add_child()->set_field(5); + mixed_dimensions.add_child()->set_field(1); + + DimensionsValue dimensionsValue; + + // Query single primitive fields. + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); + // The bool value is stored in value_int field as logD does not support bool. + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); + + // Query atom field 3: attribution node uid field only. + EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions1, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 1111); + + // Query atom field 3: attribution node tag field only. + EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions1, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_str(), "locationService"); + + // Query atom field 3: attribution node uid + tag fields. + EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions1, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 1111); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).value_str(), "locationService"); + + // Query atom field 4: attribution node uid field only. + EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions2, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 2222); + + // Query atom field 4: attribution node tag field only. + EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions2, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_str(), "locationService2"); + + // Query atom field 4: attribution node uid + tag fields. + EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions2, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 2222); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).value_str(), "locationService2"); + + // Query multiple fields: + // 1/ Field 3: attribution uid + tag. + // 2/ Field 4: attribution uid only. + // 3/ Field not exist. + // 4/ Primitive fields #5 + // 5/ Primitive fields #1 + EXPECT_TRUE(event.GetAtomDimensionsValueProto(mixed_dimensions, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 4); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value_size(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(0).value_int(), 1111); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple() + .dimensions_value(1).value_str(), "locationService"); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).field(), 4); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() + .dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() + .dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple() + .dimensions_value(0).value_int(), 2222); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).field(), 5); + // The bool value is stored in value_int field as logD does not support bool. + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).value_int(), true); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).value_int(), 11); +} + +TEST(LogEventTest, testAllPrimitiveFields) { + const int32_t TAG_ID = 123; + LogEvent event(TAG_ID, 0); + + // 1nd field: int32. + EXPECT_TRUE(event.write(int32_t(11))); + // 2rd field: float. + EXPECT_TRUE(event.write(3.45f)); + // 3th field: string. + EXPECT_TRUE(event.write("test")); + // 4th field: bool. + EXPECT_TRUE(event.write(true)); + // 5th field: bool. + EXPECT_TRUE(event.write(false)); + // 6th field: long. + EXPECT_TRUE(event.write(uint64_t(1234))); + + event.init(); + + DimensionsValue dimensionsValue; + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(3, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_str(), "test"); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4); + // The bool value is stored in value_int field as logD does not support bool. + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5); + // The bool value is stored in value_int field as logD does not support bool. + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), false); + + EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue)); + EXPECT_EQ(dimensionsValue.field(), event.GetTagId()); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6); + EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234)); + + // Field not exist. + EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(7, &dimensionsValue)); +} + +TEST(LogEventTest, testWriteAtomProtoToStream) { + AttributionNode attribution_node1; + attribution_node1.set_uid(1111); + attribution_node1.set_tag("locationService"); + + AttributionNode attribution_node2; + attribution_node2.set_uid(2222); + attribution_node2.set_tag("locationService2"); + + AttributionNode attribution_node3; + attribution_node3.set_uid(3333); + attribution_node3.set_tag("locationService3"); + std::vector<AttributionNode> attribution_nodes = + {attribution_node1, attribution_node2, attribution_node3}; + + LogEvent event(1, 0); + EXPECT_TRUE(event.write("222")); + EXPECT_TRUE(event.write(attribution_nodes)); + EXPECT_TRUE(event.write(345)); + EXPECT_TRUE(event.write(attribution_node3)); + EXPECT_TRUE(event.write("hello")); + event.init(); + + util::ProtoOutputStream protoOutput; + // For now only see whether it will crash. + // TODO(yanglu): test parsing from stream. + event.ToProto(protoOutput); +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif
\ No newline at end of file diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp index 3c8ccabed3ae..6cd31dd100c8 100644 --- a/cmds/statsd/tests/MetricsManager_test.cpp +++ b/cmds/statsd/tests/MetricsManager_test.cpp @@ -52,20 +52,20 @@ StatsdConfig buildGoodConfig() { eventMatcher->set_name("SCREEN_IS_ON"); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int( + simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("SCREEN_IS_OFF"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int( + simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); eventMatcher = config.add_atom_matcher(); @@ -80,8 +80,8 @@ StatsdConfig buildGoodConfig() { metric->set_name("3"); metric->set_what("SCREEN_IS_ON"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - KeyMatcher* keyMatcher = metric->add_dimension(); - keyMatcher->set_key(1); + metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/); + metric->mutable_dimensions()->add_child()->set_field(1); auto alert = config.add_alert(); alert->set_name("3"); @@ -100,10 +100,10 @@ StatsdConfig buildCircleMatchers() { eventMatcher->set_name("SCREEN_IS_ON"); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int( + simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); eventMatcher = config.add_atom_matcher(); @@ -129,8 +129,8 @@ StatsdConfig buildAlertWithUnknownMetric() { metric->set_name("3"); metric->set_what("SCREEN_IS_ON"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - KeyMatcher* keyMatcher = metric->add_dimension(); - keyMatcher->set_key(1); + metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/); + metric->mutable_dimensions()->add_child()->set_field(1); auto alert = config.add_alert(); alert->set_name("3"); @@ -149,10 +149,10 @@ StatsdConfig buildMissingMatchers() { eventMatcher->set_name("SCREEN_IS_ON"); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int( + simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); eventMatcher = config.add_atom_matcher(); @@ -181,7 +181,7 @@ StatsdConfig buildMissingPredicate() { eventMatcher->set_name("SCREEN_EVENT"); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(2); + simpleAtomMatcher->set_atom_id(2); return config; } @@ -193,12 +193,12 @@ StatsdConfig buildDimensionMetricsWithMultiTags() { AtomMatcher* eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("BATTERY_VERY_LOW"); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(2); + simpleAtomMatcher->set_atom_id(2); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("BATTERY_VERY_VERY_LOW"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(3); + simpleAtomMatcher->set_atom_id(3); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("BATTERY_LOW"); @@ -213,8 +213,8 @@ StatsdConfig buildDimensionMetricsWithMultiTags() { metric->set_name("3"); metric->set_what("BATTERY_LOW"); metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L); - KeyMatcher* keyMatcher = metric->add_dimension(); - keyMatcher->set_key(1); + // This case is interesting. We want to dimension across two atoms. + metric->mutable_dimensions()->add_child()->set_field(1); auto alert = config.add_alert(); alert->set_name("3"); @@ -233,20 +233,20 @@ StatsdConfig buildCirclePredicates() { eventMatcher->set_name("SCREEN_IS_ON"); SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int( + simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( 2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/); eventMatcher = config.add_atom_matcher(); eventMatcher->set_name("SCREEN_IS_OFF"); simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher(); - simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/); - simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key( + simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/); + simpleAtomMatcher->add_field_value_matcher()->set_field( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/); - simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int( + simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int( 1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/); auto condition = config.add_predicate(); @@ -267,6 +267,7 @@ StatsdConfig buildCirclePredicates() { } TEST(MetricsManagerTest, TestGoodConfig) { + UidMap uidMap; StatsdConfig config = buildGoodConfig(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; @@ -277,7 +278,7 @@ TEST(MetricsManagerTest, TestGoodConfig) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_TRUE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); EXPECT_EQ(1u, allMetricProducers.size()); @@ -285,6 +286,7 @@ TEST(MetricsManagerTest, TestGoodConfig) { } TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { + UidMap uidMap; StatsdConfig config = buildDimensionMetricsWithMultiTags(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; @@ -295,12 +297,13 @@ TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { + UidMap uidMap; StatsdConfig config = buildCircleMatchers(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; @@ -311,12 +314,13 @@ TEST(MetricsManagerTest, TestCircleLogMatcherDependency) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, TestMissingMatchers) { + UidMap uidMap; StatsdConfig config = buildMissingMatchers(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; @@ -326,12 +330,13 @@ TEST(MetricsManagerTest, TestMissingMatchers) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, TestMissingPredicate) { + UidMap uidMap; StatsdConfig config = buildMissingPredicate(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; @@ -341,12 +346,13 @@ TEST(MetricsManagerTest, TestMissingPredicate) { unordered_map<int, std::vector<int>> conditionToMetricMap; unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, TestCirclePredicateDependency) { + UidMap uidMap; StatsdConfig config = buildCirclePredicates(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; @@ -357,12 +363,13 @@ TEST(MetricsManagerTest, TestCirclePredicateDependency) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } TEST(MetricsManagerTest, testAlertWithUnknownMetric) { + UidMap uidMap; StatsdConfig config = buildAlertWithUnknownMetric(); set<int> allTagIds; vector<sp<LogMatchingTracker>> allAtomMatchers; @@ -373,7 +380,7 @@ TEST(MetricsManagerTest, testAlertWithUnknownMetric) { unordered_map<int, std::vector<int>> trackerToMetricMap; unordered_map<int, std::vector<int>> trackerToConditionMap; - EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers, + EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers, allMetricProducers, allAnomalyTrackers, conditionToMetricMap, trackerToMetricMap, trackerToConditionMap)); } diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp index 9b96bb75743a..a27bed552f9e 100644 --- a/cmds/statsd/tests/StatsLogProcessor_test.cpp +++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp @@ -52,7 +52,7 @@ TEST(StatsLogProcessorTest, TestRateLimitByteSize) { sp<UidMap> m = new UidMap(); sp<AnomalyMonitor> anomalyMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. - StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {}); + StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {}); MockMetricsManager mockMetricsManager; @@ -69,7 +69,7 @@ TEST(StatsLogProcessorTest, TestRateLimitBroadcast) { sp<UidMap> m = new UidMap(); sp<AnomalyMonitor> anomalyMonitor; int broadcastCount = 0; - StatsLogProcessor p(m, anomalyMonitor, + StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); MockMetricsManager mockMetricsManager; @@ -93,7 +93,7 @@ TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge) { sp<UidMap> m = new UidMap(); sp<AnomalyMonitor> anomalyMonitor; int broadcastCount = 0; - StatsLogProcessor p(m, anomalyMonitor, + StatsLogProcessor p(m, anomalyMonitor, 0, [&broadcastCount](const ConfigKey& key) { broadcastCount++; }); MockMetricsManager mockMetricsManager; diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp index 3fa96d392927..8a394f75a0d0 100644 --- a/cmds/statsd/tests/UidMap_test.cpp +++ b/cmds/statsd/tests/UidMap_test.cpp @@ -37,7 +37,7 @@ TEST(UidMapTest, TestIsolatedUID) { sp<UidMap> m = new UidMap(); sp<AnomalyMonitor> anomalyMonitor; // Construct the processor with a dummy sendBroadcast function that does nothing. - StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {}); + StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {}); LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1); addEvent.write(100); // parent UID addEvent.write(101); // isolated UID @@ -273,4 +273,4 @@ GTEST_LOG_(INFO) << "This test does nothing.\n"; } // namespace statsd } // namespace os -} // namespace android +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp index a0160541cc97..da872add6c0d 100644 --- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp +++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp @@ -34,14 +34,10 @@ namespace statsd { const ConfigKey kConfigKey(0, "test"); HashableDimensionKey getMockDimensionKey(int key, string value) { - KeyValuePair pair; - pair.set_key(key); - pair.set_value_str(value); - - vector<KeyValuePair> pairs; - pairs.push_back(pair); - - return HashableDimensionKey(pairs); + DimensionsValue dimensionsValue; + dimensionsValue.set_field(key); + dimensionsValue.set_value_str(value); + return HashableDimensionKey(dimensionsValue); } void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list, diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp index eb0fafe781c6..705804fef560 100644 --- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp +++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp @@ -17,6 +17,7 @@ #include <gtest/gtest.h> #include <stdio.h> #include <vector> +#include <numeric> using std::map; using std::unordered_map; @@ -30,15 +31,22 @@ namespace statsd { const ConfigKey kConfigKey(0, "test"); +const int ATTRIBUTION_NODE_FIELD_ID = 1; +const int ATTRIBUTION_UID_FIELD_ID = 1; +const int TAG_ID = 1; + SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse, - bool outputSlicedUid) { + bool outputSlicedUid, Position position) { SimplePredicate simplePredicate; simplePredicate.set_start("WAKE_LOCK_ACQUIRE"); simplePredicate.set_stop("WAKE_LOCK_RELEASE"); simplePredicate.set_stop_all("RELEASE_ALL"); if (outputSlicedUid) { - KeyMatcher* keyMatcher = simplePredicate.add_dimension(); - keyMatcher->set_key(1); + simplePredicate.mutable_dimensions()->set_field(TAG_ID); + simplePredicate.mutable_dimensions()->add_child()->set_field(ATTRIBUTION_NODE_FIELD_ID); + simplePredicate.mutable_dimensions()->mutable_child(0)->set_position(position); + simplePredicate.mutable_dimensions()->mutable_child(0)->add_child()->set_field( + ATTRIBUTION_UID_FIELD_ID); } simplePredicate.set_count_nesting(countNesting); @@ -47,24 +55,55 @@ SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse, return simplePredicate; } -void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) { - event->write(uid); // uid +void writeAttributionNodesToEvent(LogEvent* event, const std::vector<int> &uids) { + std::vector<AttributionNode> nodes; + for (size_t i = 0; i < uids.size(); ++i) { + AttributionNode node; + node.set_uid(uids[i]); + nodes.push_back(node); + } + event->write(nodes); // attribution chain. +} + +void makeWakeLockEvent( + LogEvent* event, const std::vector<int> &uids, const string& wl, int acquire) { + writeAttributionNodesToEvent(event, uids); event->write(wl); event->write(acquire); event->init(); } -map<string, HashableDimensionKey> getWakeLockQueryKey(int key, int uid, - const string& conditionName) { - // test query - KeyValuePair kv1; - kv1.set_key(key); - kv1.set_value_int(uid); - vector<KeyValuePair> kv_list; - kv_list.push_back(kv1); - map<string, HashableDimensionKey> queryKey; - queryKey[conditionName] = HashableDimensionKey(kv_list); - return queryKey; +std::map<string, std::vector<HashableDimensionKey>> getWakeLockQueryKey( + const Position position, + const std::vector<int> &uids, const string& conditionName) { + std::map<string, std::vector<HashableDimensionKey>> outputKeyMap; + std::vector<int> uid_indexes; + switch(position) { + case Position::FIRST: + uid_indexes.push_back(0); + break; + case Position::LAST: + uid_indexes.push_back(uids.size() - 1); + break; + case Position::ANY: + uid_indexes.resize(uids.size()); + std::iota(uid_indexes.begin(), uid_indexes.end(), 0); + break; + default: + break; + } + + for (const int idx : uid_indexes) { + DimensionsValue dimensionsValue; + dimensionsValue.set_field(TAG_ID); + dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID); + dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0) + ->mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID); + dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0) + ->mutable_value_tuple()->mutable_dimensions_value(0)->set_value_int(uids[idx]); + outputKeyMap[conditionName].push_back(HashableDimensionKey(dimensionsValue)); + } + return outputKeyMap; } TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) { @@ -221,91 +260,115 @@ TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) { } TEST(SimpleConditionTrackerTest, TestSlicedCondition) { - SimplePredicate simplePredicate = getWakeLockHeldCondition( - true /*nesting*/, true /*default to false*/, true /*output slice by uid*/); - string conditionName = "WL_HELD_BY_UID2"; - - unordered_map<string, int> trackerNameIndexMap; - trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0; - trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; - trackerNameIndexMap["RELEASE_ALL"] = 2; - - SimpleConditionTracker conditionTracker(kConfigKey, conditionName, - 0 /*condition tracker index*/, simplePredicate, - trackerNameIndexMap); - int uid = 111; - - LogEvent event(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event, uid, "wl1", 1); + for (Position position : + { Position::ANY, Position::FIRST, Position::LAST}) { + SimplePredicate simplePredicate = getWakeLockHeldCondition( + true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, + position); + string conditionName = "WL_HELD_BY_UID2"; + + unordered_map<string, int> trackerNameIndexMap; + trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0; + trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; + trackerNameIndexMap["RELEASE_ALL"] = 2; + + SimpleConditionTracker conditionTracker(kConfigKey, conditionName, + 0 /*condition tracker index*/, simplePredicate, + trackerNameIndexMap); + std::vector<int> uids = {111, 222, 333}; + + LogEvent event(1 /*tagId*/, 0 /*timestamp*/); + makeWakeLockEvent(&event, uids, "wl1", 1); + + // one matched start + vector<MatchingState> matcherState; + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + vector<bool> changedCache(1, false); + + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, + changedCache); + + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); + } + EXPECT_TRUE(changedCache[0]); + + // Now test query + const auto queryKey = getWakeLockQueryKey(position, uids, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + // another wake lock acquired by this uid + LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); + makeWakeLockEvent(&event2, uids, "wl2", 1); + matcherState.clear(); + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_FALSE(changedCache[0]); + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); + } + + // wake lock 1 release + LogEvent event3(1 /*tagId*/, 0 /*timestamp*/); + makeWakeLockEvent(&event3, uids, "wl1", 0); // now release it. + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, + changedCache); + // nothing changes, because wake lock 2 is still held for this uid + EXPECT_FALSE(changedCache[0]); + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size()); + } + + LogEvent event4(1 /*tagId*/, 0 /*timestamp*/); + makeWakeLockEvent(&event4, uids, "wl2", 0); // now release it. + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); + EXPECT_TRUE(changedCache[0]); + + // query again + conditionCache[0] = ConditionState::kNotEvaluated; + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - // one matched start - vector<MatchingState> matcherState; - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - vector<sp<ConditionTracker>> allPredicates; - vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); - vector<bool> changedCache(1, false); - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - - EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - EXPECT_TRUE(changedCache[0]); - - // Now test query - const auto queryKey = getWakeLockQueryKey(1, uid, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - - // another wake lock acquired by this uid - LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event2, uid, "wl2", 1); - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_FALSE(changedCache[0]); - - // wake lock 1 release - LogEvent event3(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event3, uid, "wl1", 0); // now release it. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, - changedCache); - // nothing changes, because wake lock 2 is still held for this uid - EXPECT_FALSE(changedCache[0]); - - LogEvent event4(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event4, uid, "wl2", 0); // now release it. - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); - EXPECT_TRUE(changedCache[0]); + } - // query again - conditionCache[0] = ConditionState::kNotEvaluated; - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { SimplePredicate simplePredicate = getWakeLockHeldCondition( - true /*nesting*/, true /*default to false*/, false /*slice output by uid*/); + true /*nesting*/, true /*default to false*/, false /*slice output by uid*/, + Position::ANY /* position */); string conditionName = "WL_HELD"; unordered_map<string, int> trackerNameIndexMap; @@ -316,13 +379,14 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { SimpleConditionTracker conditionTracker(kConfigKey, conditionName, 0 /*condition tracker index*/, simplePredicate, trackerNameIndexMap); - int uid1 = 111; + + std::vector<int> uid_list1 = {111, 1111, 11111}; string uid1_wl1 = "wl1_1"; - int uid2 = 222; + std::vector<int> uid_list2 = {222, 2222, 22222}; string uid2_wl1 = "wl2_1"; LogEvent event(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event, uid1, uid1_wl1, 1); + makeWakeLockEvent(&event, uid_list1, uid1_wl1, 1); // one matched start for uid1 vector<MatchingState> matcherState; @@ -340,7 +404,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { EXPECT_TRUE(changedCache[0]); // Now test query - map<string, HashableDimensionKey> queryKey; + ConditionKey queryKey; conditionCache[0] = ConditionState::kNotEvaluated; conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); @@ -348,7 +412,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { // another wake lock acquired by this uid LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event2, uid2, uid2_wl1, 1); + makeWakeLockEvent(&event2, uid_list2, uid2_wl1, 1); matcherState.clear(); matcherState.push_back(MatchingState::kMatched); matcherState.push_back(MatchingState::kNotMatched); @@ -360,7 +424,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { // uid1 wake lock 1 release LogEvent event3(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event3, uid1, uid1_wl1, 0); // now release it. + makeWakeLockEvent(&event3, uid_list1, uid1_wl1, 0); // now release it. matcherState.clear(); matcherState.push_back(MatchingState::kNotMatched); matcherState.push_back(MatchingState::kMatched); @@ -372,7 +436,7 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { EXPECT_FALSE(changedCache[0]); LogEvent event4(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event4, uid2, uid2_wl1, 0); // now release it. + makeWakeLockEvent(&event4, uid_list2, uid2_wl1, 0); // now release it. matcherState.clear(); matcherState.push_back(MatchingState::kNotMatched); matcherState.push_back(MatchingState::kMatched); @@ -390,95 +454,111 @@ TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) { } TEST(SimpleConditionTrackerTest, TestStopAll) { - SimplePredicate simplePredicate = getWakeLockHeldCondition( - true /*nesting*/, true /*default to false*/, true /*output slice by uid*/); - string conditionName = "WL_HELD_BY_UID3"; - - unordered_map<string, int> trackerNameIndexMap; - trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0; - trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; - trackerNameIndexMap["RELEASE_ALL"] = 2; - - SimpleConditionTracker conditionTracker(kConfigKey, conditionName, - 0 /*condition tracker index*/, simplePredicate, - trackerNameIndexMap); - int uid1 = 111; - int uid2 = 222; - - LogEvent event(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event, uid1, "wl1", 1); - - // one matched start - vector<MatchingState> matcherState; - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - vector<sp<ConditionTracker>> allPredicates; - vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); - vector<bool> changedCache(1, false); - - conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, - changedCache); - - EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); - EXPECT_TRUE(changedCache[0]); - - // Now test query - const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - - // another wake lock acquired by uid2 - LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); - makeWakeLockEvent(&event2, uid2, "wl2", 1); - matcherState.clear(); - matcherState.push_back(MatchingState::kMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); - EXPECT_TRUE(changedCache[0]); - - // TEST QUERY - const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); - EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); - - - // stop all event - LogEvent event3(2 /*tagId*/, 0 /*timestamp*/); - matcherState.clear(); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kNotMatched); - matcherState.push_back(MatchingState::kMatched); - - conditionCache[0] = ConditionState::kNotEvaluated; - changedCache[0] = false; - conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, - changedCache); - EXPECT_TRUE(changedCache[0]); - EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); - - // TEST QUERY - const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; - - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); - - // TEST QUERY - const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName); - conditionCache[0] = ConditionState::kNotEvaluated; + for (Position position : + {Position::ANY, Position::FIRST, Position::LAST}) { + SimplePredicate simplePredicate = getWakeLockHeldCondition( + true /*nesting*/, true /*default to false*/, true /*output slice by uid*/, + position); + string conditionName = "WL_HELD_BY_UID3"; + + unordered_map<string, int> trackerNameIndexMap; + trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0; + trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1; + trackerNameIndexMap["RELEASE_ALL"] = 2; + + SimpleConditionTracker conditionTracker(kConfigKey, conditionName, + 0 /*condition tracker index*/, simplePredicate, + trackerNameIndexMap); + + std::vector<int> uid_list1 = {111, 1111, 11111}; + std::vector<int> uid_list2 = {222, 2222, 22222}; + + LogEvent event(1 /*tagId*/, 0 /*timestamp*/); + makeWakeLockEvent(&event, uid_list1, "wl1", 1); + + // one matched start + vector<MatchingState> matcherState; + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + vector<sp<ConditionTracker>> allPredicates; + vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated); + vector<bool> changedCache(1, false); + + conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, + changedCache); + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size()); + } + EXPECT_TRUE(changedCache[0]); + + // Now test query + const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + // another wake lock acquired by uid2 + LogEvent event2(1 /*tagId*/, 0 /*timestamp*/); + makeWakeLockEvent(&event2, uid_list2, "wl2", 1); + matcherState.clear(); + matcherState.push_back(MatchingState::kMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, + changedCache); + if (position == Position::FIRST || + position == Position::LAST) { + EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size()); + } else { + EXPECT_EQ(uid_list1.size() + uid_list2.size(), + conditionTracker.mSlicedConditionState.size()); + } + EXPECT_TRUE(changedCache[0]); + + // TEST QUERY + const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + EXPECT_EQ(ConditionState::kTrue, conditionCache[0]); + + + // stop all event + LogEvent event3(2 /*tagId*/, 0 /*timestamp*/); + matcherState.clear(); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kNotMatched); + matcherState.push_back(MatchingState::kMatched); + + conditionCache[0] = ConditionState::kNotEvaluated; + changedCache[0] = false; + conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, + changedCache); + EXPECT_TRUE(changedCache[0]); + EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size()); + + // TEST QUERY + const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + + // TEST QUERY + const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName); + conditionCache[0] = ConditionState::kNotEvaluated; + + conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); + EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); + } - conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache); - EXPECT_EQ(ConditionState::kFalse, conditionCache[0]); } } // namespace statsd diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp new file mode 100644 index 000000000000..c747016161d3 --- /dev/null +++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp @@ -0,0 +1,228 @@ +// 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 <gtest/gtest.h> + +#include "src/StatsLogProcessor.h" +#include "tests/statsd_test_util.h" + +#include <vector> + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +StatsdConfig CreateStatsdConfig() { + StatsdConfig config; + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + + *config.add_atom_matcher() = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = CreateSyncEndAtomMatcher(); + + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + auto appCrashMatcher = CreateProcessCrashAtomMatcher(); + *config.add_atom_matcher() = appCrashMatcher; + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + + auto isSyncingPredicate = CreateIsSyncingPredicate(); + *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions( + android::util::SYNC_STATE_CHANGED, {1 /* uid field */}); + + auto isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ }); + + *config.add_predicate() = screenIsOffPredicate; + *config.add_predicate() = isSyncingPredicate; + *config.add_predicate() = isInBackgroundPredicate; + + auto combinationPredicate = config.add_predicate(); + combinationPredicate->set_name("combinationPredicate"); + combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND); + addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate); + addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate); + addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate); + + auto countMetric = config.add_count_metric(); + countMetric->set_name("AppCrashes"); + countMetric->set_what(appCrashMatcher.name()); + countMetric->set_condition(combinationPredicate->name()); + // The metric is dimensioning by uid only. + *countMetric->mutable_dimensions() = + CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1}); + countMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000LL); + + // Links between crash atom and condition of app is in syncing. + auto links = countMetric->add_links(); + links->set_condition(isSyncingPredicate.name()); + auto dimensionWhat = links->mutable_dimensions_in_what(); + dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + dimensionWhat->add_child()->set_field(1); // uid field. + auto dimensionCondition = links->mutable_dimensions_in_condition(); + dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED); + dimensionCondition->add_child()->set_field(1); // uid field. + + // Links between crash atom and condition of app is in background. + links = countMetric->add_links(); + links->set_condition(isInBackgroundPredicate.name()); + dimensionWhat = links->mutable_dimensions_in_what(); + dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + dimensionWhat->add_child()->set_field(1); // uid field. + dimensionCondition = links->mutable_dimensions_in_condition(); + dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); + dimensionCondition->add_child()->set_field(1); // uid field. + return config; +} + +TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) { + auto config = CreateStatsdConfig(); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = config.count_metric(0).bucket().bucket_size_millis() * 1000 * 1000; + + ConfigKey cfgKey; + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + int appUid = 123; + auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1); + auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201); + auto crashEvent3= CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101); + + auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51); + auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299); + auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001); + + auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16); + auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249); + + auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351); + auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2); + + auto screenTurnedOnEvent = + CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, bucketStartTimeNs + 2); + auto screenTurnedOffEvent = + CreateScreenStateChangedEvent(ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 200); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, + bucketStartTimeNs + 2 * bucketSizeNs - 100); + + auto syncOnEvent1 = + CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50); + auto syncOffEvent1 = + CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300); + auto syncOnEvent2 = + CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000); + + auto moveToBackgroundEvent1 = + CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15); + auto moveToForegroundEvent1 = + CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250); + + auto moveToBackgroundEvent2 = + CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350); + auto moveToForegroundEvent2 = + CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1); + + /* + bucket #1 bucket #2 + + + | | | | | | | | | | (crashEvents) + |-------------------------------------|-----------------------------------|--------- + + | | (MoveToBkground) + + | | (MoveToForeground) + + | | (SyncIsOn) + | (SyncIsOff) + | | (ScreenIsOn) + | (ScreenIsOff) + */ + std::vector<std::unique_ptr<LogEvent>> events; + events.push_back(std::move(crashEvent1)); + events.push_back(std::move(crashEvent2)); + events.push_back(std::move(crashEvent3)); + events.push_back(std::move(crashEvent4)); + events.push_back(std::move(crashEvent5)); + events.push_back(std::move(crashEvent6)); + events.push_back(std::move(crashEvent7)); + events.push_back(std::move(crashEvent8)); + events.push_back(std::move(crashEvent9)); + events.push_back(std::move(crashEvent10)); + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(syncOnEvent1)); + events.push_back(std::move(syncOffEvent1)); + events.push_back(std::move(syncOnEvent2)); + events.push_back(std::move(moveToBackgroundEvent1)); + events.push_back(std::move(moveToForegroundEvent1)); + events.push_back(std::move(moveToBackgroundEvent2)); + events.push_back(std::move(moveToForegroundEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(*event); + } + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); + auto data = reports.reports(0).metrics(0).count_metrics().data(0); + // Validate dimension value. + EXPECT_EQ(data.dimension().field(), + android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1); + // Uid field. + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid); + + reports.Clear(); + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3); + data = reports.reports(0).metrics(0).count_metrics().data(0); + // Validate dimension value. + EXPECT_EQ(data.dimension().field(), + android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1); + // Uid field. + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp new file mode 100644 index 000000000000..8d7b2d511eaa --- /dev/null +++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp @@ -0,0 +1,178 @@ +// 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 <gtest/gtest.h> + +#include "src/StatsLogProcessor.h" +#include "tests/statsd_test_util.h" + +#include <vector> + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) { + StatsdConfig config; + *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); + *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + + auto screenIsOffPredicate = CreateScreenIsOffPredicate(); + *config.add_predicate() = screenIsOffPredicate; + + auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by any attribution node and both by uid and tag. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidAndTagDimensions( + android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST}); + *config.add_predicate() = holdingWakelockPredicate; + + auto durationMetric = config.add_duration_metric(); + durationMetric->set_name("WakelockDuration"); + durationMetric->set_what(holdingWakelockPredicate.name()); + durationMetric->set_condition(screenIsOffPredicate.name()); + durationMetric->set_aggregation_type(aggregationType); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions() = + CreateAttributionUidDimensions( + android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000LL); + return config; +} + +TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) { + ConfigKey cfgKey; + for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE }) { + auto config = CreateStatsdConfig(aggregationType); + uint64_t bucketStartTimeNs = 10000000000; + uint64_t bucketSizeNs = + config.duration_metric(0).bucket().bucket_size_millis() * 1000 * 1000; + + auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey); + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); + + auto screenTurnedOnEvent = + CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, bucketStartTimeNs + 1); + auto screenTurnedOffEvent = + CreateScreenStateChangedEvent(ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 200); + auto screenTurnedOnEvent2 = + CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, + bucketStartTimeNs + bucketSizeNs + 500); + + std::vector<AttributionNode> attributions1 = + {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; + + std::vector<AttributionNode> attributions2 = + {CreateAttribution(111, "App2"), CreateAttribution(222, "GMSCoreModule1"), + CreateAttribution(222, "GMSCoreModule2")}; + + auto acquireEvent1 = CreateAcquireWakelockEvent( + attributions1, "wl1", bucketStartTimeNs + 2); + auto acquireEvent2 = CreateAcquireWakelockEvent( + attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10); + + auto releaseEvent1 = CreateReleaseWakelockEvent( + attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2); + auto releaseEvent2 = CreateReleaseWakelockEvent( + attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 15); + + std::vector<std::unique_ptr<LogEvent>> events; + + events.push_back(std::move(screenTurnedOnEvent)); + events.push_back(std::move(screenTurnedOffEvent)); + events.push_back(std::move(screenTurnedOnEvent2)); + events.push_back(std::move(acquireEvent1)); + events.push_back(std::move(acquireEvent2)); + events.push_back(std::move(releaseEvent1)); + events.push_back(std::move(releaseEvent2)); + + sortLogEventsByTimestamp(&events); + + for (const auto& event : events) { + processor->OnLogEvent(*event); + } + + ConfigMetricsReportList reports; + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + // Only 1 dimension output. The tag dimension in the predicate has been aggregated. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + + auto data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + EXPECT_EQ(data.dimension().field(), + android::util::WAKELOCK_STATE_CHANGED); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1); + // Attribution field. + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1); + // Uid only. + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0) + .value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0) + .value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0) + .value_tuple().dimensions_value(0).value_int(), 111); + // Validate bucket info. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1); + data = reports.reports(0).metrics(0).duration_metrics().data(0); + // The wakelock holding interval starts from the screen off event and to the end of the 1st + // bucket. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200); + + reports.Clear(); + processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports); + EXPECT_EQ(reports.reports_size(), 1); + EXPECT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1); + // Dump the report after the end of 2nd bucket. + EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2); + data = reports.reports(0).metrics(0).duration_metrics().data(0); + // Validate dimension value. + EXPECT_EQ(data.dimension().field(), + android::util::WAKELOCK_STATE_CHANGED); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1); + // Attribution field. + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1); + // Uid only. + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0) + .value_tuple().dimensions_value_size(), 1); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0) + .value_tuple().dimensions_value(0).field(), 1); + EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0) + .value_tuple().dimensions_value(0).value_int(), 111); + // Two output buckets. + // The wakelock holding interval in the 1st bucket starts from the screen off event and to + // the end of the 1st bucket. + EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), + bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200)); + // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and + // ends at the second screen on event. + EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL); + } +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index d3269edddb4b..6e114a628fef 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "src/metrics/CountMetricProducer.h" +#include "src/dimension.h" #include "metrics_test_helper.h" #include <gmock/gmock.h> @@ -137,26 +138,29 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) { int64_t bucketStartTimeNs = 10000000000; int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + int tagId = 1; + int conditionTagId = 2; + CountMetric metric; metric.set_name("1"); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"); MetricConditionLink* link = metric.add_links(); link->set_condition("APP_IN_BACKGROUND_PER_UID"); - link->add_key_in_what()->set_key(1); - link->add_key_in_condition()->set_key(2); + *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1); + *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2); - LogEvent event1(1, bucketStartTimeNs + 1); + LogEvent event1(tagId, bucketStartTimeNs + 1); event1.write("111"); // uid event1.init(); ConditionKey key1; - key1["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "111"); + key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")}; - LogEvent event2(1, bucketStartTimeNs + 10); + LogEvent event2(tagId, bucketStartTimeNs + 10); event2.write("222"); // uid event2.init(); ConditionKey key2; - key2["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "222"); + key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")}; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse)); diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 58a4ac6afd51..8ee94c75356f 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -52,9 +52,10 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { LogEvent event1(tagId, bucketStartTimeNs + 1); LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2); + FieldMatcher dimensions; DurationMetricProducer durationProducer( kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs); + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); @@ -88,9 +89,10 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); + FieldMatcher dimensions; DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs); + 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); EXPECT_FALSE(durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp index baaac67f395b..0ba1c2f522d9 100644 --- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp @@ -13,6 +13,7 @@ // limitations under the License. #include "src/metrics/EventMetricProducer.h" +#include "src/dimension.h" #include "metrics_test_helper.h" #include <gmock/gmock.h> @@ -88,25 +89,28 @@ TEST(EventMetricProducerTest, TestEventsWithSlicedCondition) { uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; + int tagId = 1; + int conditionTagId = 2; + EventMetric metric; metric.set_name("1"); metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"); MetricConditionLink* link = metric.add_links(); link->set_condition("APP_IN_BACKGROUND_PER_UID"); - link->add_key_in_what()->set_key(1); - link->add_key_in_condition()->set_key(2); + *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1); + *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2); - LogEvent event1(1, bucketStartTimeNs + 1); - event1.write("111"); // uid + LogEvent event1(tagId, bucketStartTimeNs + 1); + EXPECT_TRUE(event1.write("111")); event1.init(); ConditionKey key1; - key1["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "111"); + key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")}; - LogEvent event2(1, bucketStartTimeNs + 10); - event2.write("222"); // uid + LogEvent event2(tagId, bucketStartTimeNs + 10); + EXPECT_TRUE(event2.write("222")); event2.init(); ConditionKey key2; - key2["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "222"); + key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")}; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse)); diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp index 584a6d37ad17..359851fbb217 100644 --- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp @@ -47,7 +47,11 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { GaugeMetric metric; metric.set_name(metricName); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.mutable_gauge_fields()->add_field_num(2); + metric.mutable_gauge_fields_filter()->set_include_all(false); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(1); + gaugeFieldMatcher->add_child()->set_field(3); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -64,30 +68,41 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { vector<shared_ptr<LogEvent>> allData; allData.clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); - event->write(tagId); + event->write(10); + event->write("some value"); event->write(11); event->init(); allData.push_back(event); gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(11, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int()); + auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second->begin(); + EXPECT_EQ(10, it->second.value_int()); + it++; + EXPECT_EQ(11, it->second.value_int()); EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); allData.clear(); std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10); - event2->write(tagId); + event2->write(24); + event2->write("some value"); event2->write(25); event2->init(); allData.push_back(event2); gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(25, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int()); + it = gaugeProducer.mCurrentSlicedBucket->begin()->second->begin(); + EXPECT_EQ(24, it->second.value_int()); + it++; + EXPECT_EQ(25, it->second.value_int()); // One dimension. EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(11L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int()); + it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeFields->begin(); + EXPECT_EQ(10L, it->second.value_int()); + it++; + EXPECT_EQ(11L, it->second.value_int()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); @@ -95,7 +110,10 @@ TEST(GaugeMetricProducerTest, TestNoCondition) { // One dimension. EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int()); + it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeFields->begin(); + EXPECT_EQ(24L, it->second.value_int()); + it++; + EXPECT_EQ(25L, it->second.value_int()); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); } @@ -103,7 +121,9 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { GaugeMetric metric; metric.set_name(metricName); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.mutable_gauge_fields()->add_field_num(2); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); metric.set_condition("SCREEN_ON"); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -116,7 +136,7 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); - event->write(tagId); + event->write("some value"); event->write(100); event->init(); data->push_back(event); @@ -128,28 +148,32 @@ TEST(GaugeMetricProducerTest, TestWithCondition) { gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int()); + EXPECT_EQ(100, + gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int()); EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); vector<shared_ptr<LogEvent>> allData; allData.clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); - event->write(1); + event->write("some value"); event->write(110); event->init(); allData.push_back(event); gaugeProducer.onDataPulled(allData); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int()); + EXPECT_EQ(110, + gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); - EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int()); + EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back() + .mGaugeFields->begin()->second.value_int()); gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int()); + EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back() + .mGaugeFields->begin()->second.value_int()); EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum); } @@ -164,7 +188,9 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { GaugeMetric metric; metric.set_name(metricName); metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000); - metric.mutable_gauge_fields()->add_field_num(2); + auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); + gaugeFieldMatcher->set_field(tagId); + gaugeFieldMatcher->add_child()->set_field(2); GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, tagId, tagId, bucketStartTimeNs, pullerManager); @@ -175,46 +201,50 @@ TEST(GaugeMetricProducerTest, TestAnomalyDetection) { alert.set_number_of_buckets(2); sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert); - std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1); - event1->write(1); + int tagId = 1; + std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); + event1->write("some value"); event1->write(13); event1->init(); gaugeProducer.onDataPulled({event1}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int()); + EXPECT_EQ(13L, + gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int()); EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), -1LL); std::shared_ptr<LogEvent> event2 = - std::make_shared<LogEvent>(1, bucketStartTimeNs + bucketSizeNs + 10); - event2->write(1); + std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 10); + event2->write("some value"); event2->write(15); event2->init(); gaugeProducer.onDataPulled({event2}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int()); + EXPECT_EQ(15L, + gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int()); EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event2->GetTimestampNs()); std::shared_ptr<LogEvent> event3 = - std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10); - event3->write(1); + std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10); + event3->write("some value"); event3->write(24); event3->init(); gaugeProducer.onDataPulled({event3}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(24L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int()); + EXPECT_EQ(24L, + gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int()); EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event3->GetTimestampNs()); // The event4 does not have the gauge field. Thus the current bucket value is 0. std::shared_ptr<LogEvent> event4 = - std::make_shared<LogEvent>(1, bucketStartTimeNs + 3 * bucketSizeNs + 10); - event4->write(1); + std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10); + event4->write("some value"); event4->init(); gaugeProducer.onDataPulled({event4}); EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); - EXPECT_EQ(0, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int()); + EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second->empty()); EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event3->GetTimestampNs()); } diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 4ad1db128bde..7843ca085d5b 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -38,10 +38,12 @@ namespace statsd { const ConfigKey kConfigKey(0, "test"); -const HashableDimensionKey eventKey = getMockedDimensionKey(0, "1"); -const HashableDimensionKey conditionKey = getMockedDimensionKey(4, "1"); -const HashableDimensionKey key1 = getMockedDimensionKey(1, "1"); -const HashableDimensionKey key2 = getMockedDimensionKey(1, "2"); +const int TagId = 1; + +const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1"); +const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")}; +const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1"); +const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2"); TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -174,7 +176,7 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey conditionKey1; - HashableDimensionKey eventKey = getMockedDimensionKey(2, "maps"); + HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps"); conditionKey1["APP_BACKGROUND"] = conditionKey; EXPECT_CALL(*wizard, query(_, conditionKey1)) // #4 diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index e0f554d6391e..550b059e4d69 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -35,11 +35,12 @@ namespace os { namespace statsd { const ConfigKey kConfigKey(0, "test"); -const HashableDimensionKey eventKey = getMockedDimensionKey(0, "event"); +const int TagId = 1; +const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event"); -const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(1, "maps"); -const HashableDimensionKey kEventKey1 = getMockedDimensionKey(2, "maps"); -const HashableDimensionKey kEventKey2 = getMockedDimensionKey(3, "maps"); +const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")}; +const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps"); +const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps"); TEST(OringDurationTrackerTest, TestDurationOverlap) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp index a0a854a28d36..fc7245ca54e3 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp +++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp @@ -18,15 +18,12 @@ namespace android { namespace os { namespace statsd { -HashableDimensionKey getMockedDimensionKey(int key, string value) { - KeyValuePair pair; - pair.set_key(key); - pair.set_value_str(value); - - vector<KeyValuePair> pairs; - pairs.push_back(pair); - - return HashableDimensionKey(pairs); +HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) { + DimensionsValue dimensionsValue; + dimensionsValue.set_field(tagId); + dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key); + dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value); + return HashableDimensionKey(dimensionsValue); } } // namespace statsd diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h index 7cb332953016..23e86f92f844 100644 --- a/cmds/statsd/tests/metrics/metrics_test_helper.h +++ b/cmds/statsd/tests/metrics/metrics_test_helper.h @@ -28,7 +28,7 @@ public: MOCK_METHOD2( query, ConditionState(const int conditionIndex, - const std::map<std::string, HashableDimensionKey>& conditionParameters)); + const ConditionKey& conditionParameters)); }; class MockStatsPullerManager : public StatsPullerManager { @@ -38,7 +38,7 @@ public: MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data)); }; -HashableDimensionKey getMockedDimensionKey(int key, std::string value); +HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value); } // namespace statsd } // namespace os diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp new file mode 100644 index 000000000000..39e366fae3f8 --- /dev/null +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -0,0 +1,321 @@ +// 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 <gtest/gtest.h> +#include "statsd_test_util.h" + +namespace android { +namespace os { +namespace statsd { + +AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name, + WakelockStateChanged::State state) { + AtomMatcher atom_matcher; + atom_matcher.set_name(name); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(4); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateAcquireWakelockAtomMatcher() { + return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE); +} + +AtomMatcher CreateReleaseWakelockAtomMatcher() { + return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE); +} + +AtomMatcher CreateScreenStateChangedAtomMatcher( + const string& name, ScreenStateChanged::State state) { + AtomMatcher atom_matcher; + atom_matcher.set_name(name); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(1); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateScreenTurnedOnAtomMatcher() { + return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", ScreenStateChanged::STATE_ON); +} + +AtomMatcher CreateScreenTurnedOffAtomMatcher() { + return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", ScreenStateChanged::STATE_OFF); +} + +AtomMatcher CreateSyncStateChangedAtomMatcher( + const string& name, SyncStateChanged::State state) { + AtomMatcher atom_matcher; + atom_matcher.set_name(name); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(3); // State field. + field_value_matcher->set_eq_int(state); + return atom_matcher; +} + +AtomMatcher CreateSyncStartAtomMatcher() { + return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON); +} + +AtomMatcher CreateSyncEndAtomMatcher() { + return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF); +} + +AtomMatcher CreateActivityForegroundStateChangedAtomMatcher( + const string& name, ActivityForegroundStateChanged::Activity activity) { + AtomMatcher atom_matcher; + atom_matcher.set_name(name); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(4); // Activity field. + field_value_matcher->set_eq_int(activity); + return atom_matcher; +} + +AtomMatcher CreateMoveToBackgroundAtomMatcher() { + return CreateActivityForegroundStateChangedAtomMatcher( + "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND); +} + +AtomMatcher CreateMoveToForegroundAtomMatcher() { + return CreateActivityForegroundStateChangedAtomMatcher( + "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND); +} + +AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher( + const string& name, ProcessLifeCycleStateChanged::Event event) { + AtomMatcher atom_matcher; + atom_matcher.set_name(name); + auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher(); + simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED); + auto field_value_matcher = simple_atom_matcher->add_field_value_matcher(); + field_value_matcher->set_field(3); // Process state field. + field_value_matcher->set_eq_int(event); + return atom_matcher; +} + +AtomMatcher CreateProcessCrashAtomMatcher() { + return CreateProcessLifeCycleStateChangedAtomMatcher( + "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED); +} + + +Predicate CreateScreenIsOnPredicate() { + Predicate predicate; + predicate.set_name("ScreenIsOn"); + predicate.mutable_simple_predicate()->set_start("ScreenTurnedOn"); + predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOff"); + return predicate; +} + +Predicate CreateScreenIsOffPredicate() { + Predicate predicate; + predicate.set_name("ScreenIsOff"); + predicate.mutable_simple_predicate()->set_start("ScreenTurnedOff"); + predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOn"); + return predicate; +} + +Predicate CreateHoldingWakelockPredicate() { + Predicate predicate; + predicate.set_name("HoldingWakelock"); + predicate.mutable_simple_predicate()->set_start("AcquireWakelock"); + predicate.mutable_simple_predicate()->set_stop("ReleaseWakelock"); + return predicate; +} + +Predicate CreateIsSyncingPredicate() { + Predicate predicate; + predicate.set_name("IsSyncing"); + predicate.mutable_simple_predicate()->set_start("SyncStart"); + predicate.mutable_simple_predicate()->set_stop("SyncEnd"); + return predicate; +} + +Predicate CreateIsInBackgroundPredicate() { + Predicate predicate; + predicate.set_name("IsInBackground"); + predicate.mutable_simple_predicate()->set_start("MoveToBackground"); + predicate.mutable_simple_predicate()->set_stop("MoveToForeground"); + return predicate; +} + +void addPredicateToPredicateCombination(const Predicate& predicate, + Predicate* combinationPredicate) { + combinationPredicate->mutable_combination()->add_predicate(predicate.name()); +} + +FieldMatcher CreateAttributionUidDimensions(const int atomId, + const std::vector<Position>& positions) { + FieldMatcher dimensions; + dimensions.set_field(atomId); + for (const auto position : positions) { + auto child = dimensions.add_child(); + child->set_field(1); + child->set_position(position); + child->add_child()->set_field(1); + } + return dimensions; +} + +FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, + const std::vector<Position>& positions) { + FieldMatcher dimensions; + dimensions.set_field(atomId); + for (const auto position : positions) { + auto child = dimensions.add_child(); + child->set_field(1); + child->set_position(position); + child->add_child()->set_field(1); + child->add_child()->set_field(2); + } + return dimensions; +} + +FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) { + FieldMatcher dimensions; + dimensions.set_field(atomId); + for (const int field : fields) { + dimensions.add_child()->set_field(field); + } + return dimensions; +} + +std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( + const ScreenStateChanged::State state, uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs); + EXPECT_TRUE(event->write(state)); + event->init(); + return event; +} + +std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent( + const std::vector<AttributionNode>& attributions, const string& wakelockName, + const WakelockStateChanged::State state, uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs); + event->write(attributions); + event->write(WakelockStateChanged::PARTIAL); + event->write(wakelockName); + event->write(state); + event->init(); + return event; +} + +std::unique_ptr<LogEvent> CreateAcquireWakelockEvent( + const std::vector<AttributionNode>& attributions, + const string& wakelockName, uint64_t timestampNs) { + return CreateWakelockStateChangedEvent( + attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs); +} + +std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( + const std::vector<AttributionNode>& attributions, + const string& wakelockName, uint64_t timestampNs) { + return CreateWakelockStateChangedEvent( + attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs); +} + +std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent( + const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>( + android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs); + event->write(uid); + event->write("pkg_name"); + event->write("class_name"); + event->write(activity); + event->init(); + return event; +} + +std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) { + return CreateActivityForegroundStateChangedEvent( + uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs); +} + +std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) { + return CreateActivityForegroundStateChangedEvent( + uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs); +} + +std::unique_ptr<LogEvent> CreateSyncStateChangedEvent( + const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) { + auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs); + event->write(uid); + event->write(name); + event->write(state); + event->init(); + return event; +} + +std::unique_ptr<LogEvent> CreateSyncStartEvent( + const int uid, const string& name, uint64_t timestampNs){ + return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs); +} + +std::unique_ptr<LogEvent> CreateSyncEndEvent( + const int uid, const string& name, uint64_t timestampNs) { + return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::OFF, timestampNs); +} + +std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent( + const int uid, const ProcessLifeCycleStateChanged::Event event, uint64_t timestampNs) { + auto logEvent = std::make_unique<LogEvent>( + android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs); + logEvent->write(uid); + logEvent->write(""); + logEvent->write(event); + logEvent->init(); + return logEvent; +} + +std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) { + return CreateProcessLifeCycleStateChangedEvent( + uid, ProcessLifeCycleStateChanged::PROCESS_CRASHED, timestampNs); +} + +sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, + const ConfigKey& key) { + sp<UidMap> uidMap = new UidMap(); + sp<AnomalyMonitor> anomalyMonitor = new AnomalyMonitor(10); // 10 seconds + sp<StatsLogProcessor> processor = new StatsLogProcessor( + uidMap, anomalyMonitor, timeBaseSec, [](const ConfigKey&){}); + processor->OnConfigUpdated(key, config); + return processor; +} + +AttributionNode CreateAttribution(const int& uid, const string& tag) { + AttributionNode attribution; + attribution.set_uid(uid); + attribution.set_tag(tag); + return attribution; +} + +void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) { + std::sort(events->begin(), events->end(), + [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) { + return a->GetTimestampNs() < b->GetTimestampNs(); + }); +} + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h new file mode 100644 index 000000000000..282e1b2c70a0 --- /dev/null +++ b/cmds/statsd/tests/statsd_test_util.h @@ -0,0 +1,126 @@ +// 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. + +#pragma once + +#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "statslog.h" +#include "src/logd/LogEvent.h" +#include "src/StatsLogProcessor.h" + +namespace android { +namespace os { +namespace statsd { + +// Create AtomMatcher proto for acquiring wakelock. +AtomMatcher CreateAcquireWakelockAtomMatcher(); + +// Create AtomMatcher proto for releasing wakelock. +AtomMatcher CreateReleaseWakelockAtomMatcher() ; + +// Create AtomMatcher proto for screen turned on. +AtomMatcher CreateScreenTurnedOnAtomMatcher(); + +// Create AtomMatcher proto for screen turned off. +AtomMatcher CreateScreenTurnedOffAtomMatcher(); + +// Create AtomMatcher proto for app sync turned on. +AtomMatcher CreateSyncStartAtomMatcher(); + +// Create AtomMatcher proto for app sync turned off. +AtomMatcher CreateSyncEndAtomMatcher(); + +// Create AtomMatcher proto for app sync moves to background. +AtomMatcher CreateMoveToBackgroundAtomMatcher(); + +// Create AtomMatcher proto for app sync moves to foreground. +AtomMatcher CreateMoveToForegroundAtomMatcher(); + +// Create AtomMatcher proto for process crashes +AtomMatcher CreateProcessCrashAtomMatcher() ; + +// Create Predicate proto for screen is on. +Predicate CreateScreenIsOnPredicate(); + +// Create Predicate proto for screen is off. +Predicate CreateScreenIsOffPredicate(); + +// Create Predicate proto for holding wakelock. +Predicate CreateHoldingWakelockPredicate(); + +// Create a Predicate proto for app syncing. +Predicate CreateIsSyncingPredicate(); + +// Create a Predicate proto for app is in background. +Predicate CreateIsInBackgroundPredicate(); + +// Add a predicate to the predicate combination. +void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); + +// Create dimensions from primitive fields. +FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields); + +// Create dimensions by attribution uid and tag. +FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId, + const std::vector<Position>& positions); + +// Create dimensions by attribution uid only. +FieldMatcher CreateAttributionUidDimensions(const int atomId, + const std::vector<Position>& positions); + +// Create log event for screen state changed. +std::unique_ptr<LogEvent> CreateScreenStateChangedEvent( + const ScreenStateChanged::State state, uint64_t timestampNs); + +// Create log event for app moving to background. +std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs); + +// Create log event for app moving to foreground. +std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs); + +// Create log event when the app sync starts. +std::unique_ptr<LogEvent> CreateSyncStartEvent( + const int uid, const string& name, uint64_t timestampNs); + +// Create log event when the app sync ends. +std::unique_ptr<LogEvent> CreateSyncEndEvent( + const int uid, const string& name, uint64_t timestampNs); + +// Create log event when the app sync ends. +std::unique_ptr<LogEvent> CreateAppCrashEvent( + const int uid, uint64_t timestampNs); + +// Create log event for acquiring wakelock. +std::unique_ptr<LogEvent> CreateAcquireWakelockEvent( + const std::vector<AttributionNode>& attributions, + const string& wakelockName, uint64_t timestampNs); + +// Create log event for releasing wakelock. +std::unique_ptr<LogEvent> CreateReleaseWakelockEvent( + const std::vector<AttributionNode>& attributions, + const string& wakelockName, uint64_t timestampNs); + +// Helper function to create an AttributionNode proto. +AttributionNode CreateAttribution(const int& uid, const string& tag); + +// Create a statsd log event processor upon the start time in seconds, config and key. +sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config, + const ConfigKey& key); + +// Util function to sort the log events by timestamp. +void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events); + +} // namespace statsd +} // namespace os +} // namespace android
\ No newline at end of file diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java index fe3d86d4cd6a..9294681f5baa 100644 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java +++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java @@ -71,20 +71,25 @@ public class DisplayProtoUtils { return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString(); } - private static void displayDimension(StringBuilder sb, List<StatsLog.KeyValuePair> pairs) { - for (com.android.os.StatsLog.KeyValuePair kv : pairs) { - sb.append(kv.getKey()).append(":"); - if (kv.hasValueBool()) { - sb.append(kv.getValueBool()); - } else if (kv.hasValueFloat()) { - sb.append(kv.getValueFloat()); - } else if (kv.hasValueInt()) { - sb.append(kv.getValueInt()); - } else if (kv.hasValueStr()) { - sb.append(kv.getValueStr()); + private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) { + sb.append(dimensionValue.getField()).append(":"); + if (dimensionValue.hasValueBool()) { + sb.append(dimensionValue.getValueBool()); + } else if (dimensionValue.hasValueFloat()) { + sb.append(dimensionValue.getValueFloat()); + } else if (dimensionValue.hasValueInt()) { + sb.append(dimensionValue.getValueInt()); + } else if (dimensionValue.hasValueStr()) { + sb.append(dimensionValue.getValueStr()); + } else if (dimensionValue.hasValueTuple()) { + sb.append("{"); + for (StatsLog.DimensionsValue child : + dimensionValue.getValueTuple().getDimensionsValueList()) { + displayDimension(sb, child); } - sb.append(" "); + sb.append("}"); } + sb.append(" "); } public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { @@ -93,7 +98,7 @@ public class DisplayProtoUtils { sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n"); for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) { sb.append("dimension: "); - displayDimension(sb, duration.getDimensionList()); + displayDimension(sb, duration.getDimension()); sb.append("\n"); for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) { @@ -120,7 +125,7 @@ public class DisplayProtoUtils { sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n"); for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) { sb.append("dimension: "); - displayDimension(sb, count.getDimensionList()); + displayDimension(sb, count.getDimension()); sb.append("\n"); for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) { diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java index 70dd6347e1a4..137fd4d01fda 100644 --- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java +++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java @@ -252,7 +252,9 @@ public class MainActivity extends Activity { Log.d(TAG, "invalid pkg id"); return; } - StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, mUids[id], 0, name, 1); + int[] uids = new int[] {mUids[id]}; + String[] tags = new String[] {"acquire"}; + StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1); StringBuilder sb = new StringBuilder(); sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0) .append(", ").append(name).append(", 1);"); @@ -264,7 +266,9 @@ public class MainActivity extends Activity { Log.d(TAG, "invalid pkg id"); return; } - StatsLog.write(10, mUids[id], 0, name, 0); + int[] uids = new int[] {mUids[id]}; + String[] tags = new String[] {"release"}; + StatsLog.write(10, uids, tags, 0, name, 0); StringBuilder sb = new StringBuilder(); sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0) .append(", ").append(name).append(", 0);"); diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java index 5fc4cf52f850..f516477abf9f 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java @@ -27,8 +27,7 @@ import com.android.internal.os.StatsdConfigProto.MetricConditionLink; import com.android.internal.os.StatsdConfigProto.EventMetric; import com.android.internal.os.StatsdConfigProto.GaugeMetric; import com.android.internal.os.StatsdConfigProto.ValueMetric; -import com.android.internal.os.StatsdConfigProto.KeyMatcher; -import com.android.internal.os.StatsdConfigProto.KeyValueMatcher; +import com.android.internal.os.StatsdConfigProto.FieldValueMatcher; import com.android.internal.os.StatsdConfigProto.AtomMatcher; import com.android.internal.os.StatsdConfigProto.SimplePredicate; import com.android.internal.os.StatsdConfigProto.StatsdConfig; diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java index 735a32729a0f..ba43d57db8af 100644 --- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java +++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java @@ -71,20 +71,25 @@ public class DisplayProtoUtils { return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString(); } - private static void displayDimension(StringBuilder sb, List<StatsLog.KeyValuePair> pairs) { - for (com.android.os.StatsLog.KeyValuePair kv : pairs) { - sb.append(kv.getKey()).append(":"); - if (kv.hasValueBool()) { - sb.append(kv.getValueBool()); - } else if (kv.hasValueFloat()) { - sb.append(kv.getValueFloat()); - } else if (kv.hasValueInt()) { - sb.append(kv.getValueInt()); - } else if (kv.hasValueStr()) { - sb.append(kv.getValueStr()); + private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) { + sb.append(dimensionValue.getField()).append(":"); + if (dimensionValue.hasValueBool()) { + sb.append(dimensionValue.getValueBool()); + } else if (dimensionValue.hasValueFloat()) { + sb.append(dimensionValue.getValueFloat()); + } else if (dimensionValue.hasValueInt()) { + sb.append(dimensionValue.getValueInt()); + } else if (dimensionValue.hasValueStr()) { + sb.append(dimensionValue.getValueStr()); + } else if (dimensionValue.hasValueTuple()) { + sb.append("{"); + for (StatsLog.DimensionsValue child : + dimensionValue.getValueTuple().getDimensionsValueList()) { + displayDimension(sb, child); } - sb.append(" "); + sb.append("}"); } + sb.append(" "); } public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) { @@ -93,7 +98,7 @@ public class DisplayProtoUtils { sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n"); for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) { sb.append("dimension: "); - displayDimension(sb, duration.getDimensionList()); + displayDimension(sb, duration.getDimension()); sb.append("\n"); for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList()) { @@ -120,7 +125,7 @@ public class DisplayProtoUtils { sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n"); for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) { sb.append("dimension: "); - displayDimension(sb, count.getDimensionList()); + displayDimension(sb, count.getDimension()); sb.append("\n"); for (StatsLog.CountBucketInfo info : count.getBucketInfoList()) { diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 89665854e713..72f07b7c8f20 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -4118,13 +4118,10 @@ public class BatteryStatsImpl extends BatteryStats { getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime); - // TODO(statsd): Use the attribution chain specified in WorkChain instead of uid. - // The debug logging here can be deleted once statsd is wired up. - if (DEBUG) { - Slog.w(TAG, "StatsLog [start]: uid=" + uid + ", type=" + type + ", name=" + name - + ", wc=" + wc); + if (wc != null) { + StatsLog.write( + StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 1); } - StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uid, type, name, 1); } } @@ -4161,14 +4158,10 @@ public class BatteryStatsImpl extends BatteryStats { } getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime); - - // TODO(statsd): Use the attribution chain specified in WorkChain instead of uid. - // The debug logging here can be deleted once statsd is wired up. - if (DEBUG) { - Slog.w(TAG, "StatsLog [stop]: uid=" + uid + ", type=" + type + ", name=" + name - + ", wc=" + wc); + if (wc != null) { + StatsLog.write( + StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 0); } - StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uid, type, name, 0); } } diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp index a25784debf12..80853b13a7cc 100644 --- a/tools/stats_log_api_gen/Collation.cpp +++ b/tools/stats_log_api_gen/Collation.cpp @@ -112,7 +112,7 @@ java_type(const FieldDescriptor* field) case FieldDescriptor::TYPE_MESSAGE: // TODO: not the final package name if (field->message_type()->full_name() == - "android.os.statsd.AttributionChain") { + "android.os.statsd.AttributionNode") { return JAVA_TYPE_ATTRIBUTION_CHAIN; } else { return JAVA_TYPE_OBJECT; diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp index 5e93c08e88b8..89749fb52bb4 100644 --- a/tools/stats_log_api_gen/main.cpp +++ b/tools/stats_log_api_gen/main.cpp @@ -769,7 +769,7 @@ run(int argc, char const*const* argv) AtomDecl attributionDecl; vector<java_type_t> attributionSignature; - collate_atom(android::os::statsd::Attribution::descriptor(), + collate_atom(android::os::statsd::AttributionNode::descriptor(), &attributionDecl, &attributionSignature); // Write the .cpp file diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto index 84b22cdaf522..66cbee8f219b 100644 --- a/tools/stats_log_api_gen/test.proto +++ b/tools/stats_log_api_gen/test.proto @@ -39,7 +39,7 @@ enum AnEnum { } message AllTypesAtom { - optional android.os.statsd.AttributionChain attribution_chain = 1; + repeated android.os.statsd.AttributionNode attribution_chain = 1; optional double double_field = 2; optional float float_field = 3; optional int64 int64_field = 4; @@ -99,12 +99,12 @@ message BadSkippedFieldMultiple { } } -message BadAttributionChainPositionAtom { +message BadAttributionNodePositionAtom { optional int32 field1 = 1; - optional android.os.statsd.AttributionChain attribution = 2; + repeated android.os.statsd.AttributionNode attribution = 2; } -message BadAttributionChainPosition { - oneof event { BadAttributionChainPositionAtom bad = 1; } +message BadAttributionNodePosition { + oneof event { BadAttributionNodePositionAtom bad = 1; } } diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp index b2b7298d86c7..9e22cd92fd53 100644 --- a/tools/stats_log_api_gen/test_collation.cpp +++ b/tools/stats_log_api_gen/test_collation.cpp @@ -191,10 +191,10 @@ TEST(CollationTest, FailOnSkippedFieldsMultiple) { * Test that atoms that have an attribution chain not in the first position are * rejected. */ -TEST(CollationTest, FailBadAttributionChainPosition) { +TEST(CollationTest, FailBadAttributionNodePosition) { Atoms atoms; int errorCount = - collate_atoms(BadAttributionChainPosition::descriptor(), &atoms); + collate_atoms(BadAttributionNodePosition::descriptor(), &atoms); EXPECT_EQ(1, errorCount); } |