diff options
| author | 2020-11-13 00:31:07 -0800 | |
|---|---|---|
| committer | 2020-12-01 00:02:07 -0800 | |
| commit | 933b6e35312d6cd507f373d463a819a4f9c15f13 (patch) | |
| tree | 6c63a845b93c1d083235b6076af31a2e7679d580 | |
| parent | e935342b74ec742da46cf3bef305cd4aaf5e55e6 (diff) | |
Init duration metric if condition existed
Initialize a duration metric with the dimensions that are true if the
"what" predicate previously existed.
Assumes that the internal dimensions are a superset of the
dimensions_in_what, the condition links, and the state links
Test: atest statsd_test
Bug: 167491517
Change-Id: I8dd984fb9cb575d6d541f80cb3b1e626ce4e5f27
| -rw-r--r-- | cmds/statsd/src/condition/CombinationConditionTracker.h | 10 | ||||
| -rw-r--r-- | cmds/statsd/src/condition/ConditionTracker.h | 5 | ||||
| -rw-r--r-- | cmds/statsd/src/condition/ConditionWizard.h | 6 | ||||
| -rw-r--r-- | cmds/statsd/src/condition/SimpleConditionTracker.h | 11 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/DurationMetricProducer.cpp | 74 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/DurationMetricProducer.h | 13 | ||||
| -rw-r--r-- | cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp | 12 | ||||
| -rw-r--r-- | cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp | 297 | ||||
| -rw-r--r-- | cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp | 52 | ||||
| -rw-r--r-- | cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp | 5 | ||||
| -rw-r--r-- | cmds/statsd/tests/statsd_test_util.cpp | 15 | ||||
| -rw-r--r-- | cmds/statsd/tests/statsd_test_util.h | 2 |
12 files changed, 419 insertions, 83 deletions
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h index 672d61c82268..d355146c13ef 100644 --- a/cmds/statsd/src/condition/CombinationConditionTracker.h +++ b/cmds/statsd/src/condition/CombinationConditionTracker.h @@ -85,16 +85,14 @@ public: const std::vector<sp<ConditionTracker>>& allConditions, const vector<Matcher>& dimensions) const override; - void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const override { + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const override { if (mSlicedChildren.size() == 1) { - return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions( - allConditions, dimensions); + return allConditions[mSlicedChildren.front()]->getSlicedDimensionMap(allConditions); } + return nullptr; } - private: LogicalOperation mLogicalOperation; diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h index 5a6b8cf334eb..7af022a5677a 100644 --- a/cmds/statsd/src/condition/ConditionTracker.h +++ b/cmds/statsd/src/condition/ConditionTracker.h @@ -135,9 +135,8 @@ public: return mProtoHash; } - virtual void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const = 0; + virtual const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const = 0; virtual bool IsChangedDimensionTrackable() const = 0; diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h index 892647910d9f..43db94cfa47d 100644 --- a/cmds/statsd/src/condition/ConditionWizard.h +++ b/cmds/statsd/src/condition/ConditionWizard.h @@ -53,9 +53,9 @@ public: ConditionState getUnSlicedPartConditionState(const int index) { return mAllConditions[index]->getUnSlicedPartConditionState(); } - void getTrueSlicedDimensions(const int index, - std::set<HashableDimensionKey>* trueDimensions) const { - return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions); + + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(const int index) const { + return mAllConditions[index]->getSlicedDimensionMap(mAllConditions); } private: diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h index 7a8b40108448..2fbc8a7b4ea8 100644 --- a/cmds/statsd/src/condition/SimpleConditionTracker.h +++ b/cmds/statsd/src/condition/SimpleConditionTracker.h @@ -74,14 +74,9 @@ public: } } - void getTrueSlicedDimensions( - const std::vector<sp<ConditionTracker>>& allConditions, - std::set<HashableDimensionKey>* dimensions) const override { - for (const auto& itr : mSlicedConditionState) { - if (itr.second > 0) { - dimensions->insert(itr.first); - } - } + const std::map<HashableDimensionKey, int>* getSlicedDimensionMap( + const std::vector<sp<ConditionTracker>>& allConditions) const override { + return &mSlicedConditionState; } bool IsChangedDimensionTrackable() const override { return true; } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 8869241ab8aa..bbaa20f232a9 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -67,8 +67,8 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; DurationMetricProducer::DurationMetricProducer( const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const int startIndex, - const int stopIndex, const int stopAllIndex, const bool nesting, + const vector<ConditionState>& initialConditionCache, const int whatIndex, + const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const uint64_t protoHash, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, @@ -140,6 +140,8 @@ DurationMetricProducer::DurationMetricProducer( mCurrentBucketStartTimeNs = startTimeNs; VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs); + + initTrueDimensions(whatIndex, startTimeNs); } DurationMetricProducer::~DurationMetricProducer() { @@ -224,6 +226,23 @@ bool DurationMetricProducer::onConfigUpdatedLocked( return true; } +void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) { + std::lock_guard<std::mutex> lock(mMutex); + // Currently whatIndex will only be -1 in tests. In the future, we might want to avoid creating + // a ConditionTracker if the condition is only used in the "what" of a duration metric. In that + // scenario, -1 can also be passed. + if (whatIndex == -1) { + return; + } + const map<HashableDimensionKey, int>* slicedWhatMap = mWizard->getSlicedDimensionMap(whatIndex); + for (const auto& [internalDimKey, count] : *slicedWhatMap) { + for (int i = 0; i < count; i++) { + // Fake start events. + handleMatchedLogEventValuesLocked(mStartIndex, internalDimKey.getValues(), startTimeNs); + } + } +} + sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker( const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) { std::lock_guard<std::mutex> lock(mMutex); @@ -330,14 +349,14 @@ void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool conditio // state based on the new unsliced condition state. if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr || (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) { - std::set<HashableDimensionKey> trueConditionDimensions; - mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions); + const map<HashableDimensionKey, int>* slicedConditionMap = + mWizard->getSlicedDimensionMap(mConditionTrackerIndex); for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { HashableDimensionKey linkedConditionDimensionKey; getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0], &linkedConditionDimensionKey); - if (trueConditionDimensions.find(linkedConditionDimensionKey) != - trueConditionDimensions.end()) { + const auto& slicedConditionIt = slicedConditionMap->find(linkedConditionDimensionKey); + if (slicedConditionIt != slicedConditionMap->end() && slicedConditionIt->second > 0) { whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime); } } @@ -595,8 +614,9 @@ bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey } void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, - bool condition, const LogEvent& event) { + const ConditionKey& conditionKeys, bool condition, + const int64_t eventTimeNs, + const vector<FieldValue>& eventValues) { const auto& whatKey = eventKey.getDimensionKeyInWhat(); auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey); if (whatIt == mCurrentSlicedDurationTrackerMap.end()) { @@ -608,20 +628,17 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey auto it = mCurrentSlicedDurationTrackerMap.find(whatKey); if (mUseWhatDimensionAsInternalDimension) { - it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys); + it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys); return; } if (mInternalDimensions.empty()) { - it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(), - conditionKeys); + it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys); } else { HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY; - filterValues(mInternalDimensions, event.getValues(), &dimensionKey); - it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(), - conditionKeys); + filterValues(mInternalDimensions, eventValues, &dimensionKey); + it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys); } - } void DurationMetricProducer::onMatchedLogEventInternalLocked( @@ -633,26 +650,32 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { - int64_t eventTimeNs = event.GetElapsedTimestampNs(); + handleMatchedLogEventValuesLocked(matcherIndex, event.getValues(), + event.GetElapsedTimestampNs()); +} + +void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matcherIndex, + const vector<FieldValue>& values, + const int64_t eventTimeNs) { if (eventTimeNs < mTimeBaseNs) { return; } if (mIsActive) { - flushIfNeededLocked(event.GetElapsedTimestampNs()); + flushIfNeededLocked(eventTimeNs); } // Handles Stopall events. if ((int)matcherIndex == mStopAllIndex) { for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { - whatIt.second->noteStopAll(event.GetElapsedTimestampNs()); + whatIt.second->noteStopAll(eventTimeNs); } return; } HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY; if (!mDimensionsInWhat.empty()) { - filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); + filterValues(mDimensionsInWhat, values, &dimensionInWhat); } // Stores atom id to primary key pairs for each state atom that the metric is @@ -663,8 +686,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, // field values from the log event. These values will form a primary key // that will be used to query StateTracker for the correct state value. for (const auto& stateLink : mMetric2StateLinks) { - getDimensionForState(event.getValues(), stateLink, - &statePrimaryKeys[stateLink.stateAtomId]); + getDimensionForState(values, stateLink, &statePrimaryKeys[stateLink.stateAtomId]); } // For each sliced state, query StateTracker for the state value using @@ -695,19 +717,19 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, if (mUseWhatDimensionAsInternalDimension) { auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false); + whatIt->second->noteStop(dimensionInWhat, eventTimeNs, false); } return; } HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY; if (!mInternalDimensions.empty()) { - filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey); + filterValues(mInternalDimensions, values, &internalDimensionKey); } auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { - whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false); + whatIt->second->noteStop(internalDimensionKey, eventTimeNs, false); } return; } @@ -716,7 +738,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, ConditionKey conditionKey; if (mConditionSliced) { for (const auto& link : mMetric2ConditionLinks) { - getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]); + getDimensionForCondition(values, link, &conditionKey[link.conditionId]); } auto conditionState = @@ -731,7 +753,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, condition = condition && mIsActive; handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition, - event); + eventTimeNs, values); } size_t DurationMetricProducer::byteSizeLocked() const { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 5feb09fc1c98..9fb586f7262f 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -40,8 +40,8 @@ class DurationMetricProducer : public MetricProducer { public: DurationMetricProducer( const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const int startIndex, - const int stopIndex, const int stopAllIndex, const bool nesting, + const vector<ConditionState>& initialConditionCache, const int whatIndex, + const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const uint64_t protoHash, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, @@ -74,8 +74,15 @@ protected: const std::map<int, HashableDimensionKey>& statePrimaryKeys) override; private: + // Initializes true dimensions of the 'what' predicate. Only to be called during initialization. + void initTrueDimensions(const int whatIndex, const int64_t startTimeNs); + + void handleMatchedLogEventValuesLocked(const size_t matcherIndex, + const std::vector<FieldValue>& values, + const int64_t eventTimeNs); void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, - bool condition, const LogEvent& event); + bool condition, const int64_t eventTimeNs, + const vector<FieldValue>& eventValues); void onDumpReportLocked(const int64_t dumpTimeNs, const bool include_current_partial_bucket, diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 4474df4346cf..ff7938cc7243 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -450,7 +450,8 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( return nullopt; } - const Predicate& durationWhat = config.predicate(what_it->second); + const int whatIndex = what_it->second; + const Predicate& durationWhat = config.predicate(whatIndex); if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { ALOGE("DurationMetric's \"what\" must be a simple condition"); return nullopt; @@ -536,10 +537,11 @@ optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( return nullopt; } - return {new DurationMetricProducer( - key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex, - nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs, - eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; + return {new DurationMetricProducer(key, metric, conditionIndex, initialConditionCache, + whatIndex, startIndex, stopIndex, stopAllIndex, nesting, + wizard, metricHash, internalDimensions, timeBaseNs, + currentTimeNs, eventActivationMap, eventDeactivationMap, + slicedStateAtoms, stateGroupMap)}; } optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( diff --git a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp index e01a0b63a0ca..22c4f5de2537 100644 --- a/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ConfigUpdate_e2e_test.cpp @@ -14,8 +14,6 @@ #include <gtest/gtest.h> -#include <thread> // std::this_thread::sleep_for - #include "android-base/stringprintf.h" #include "src/StatsLogProcessor.h" #include "src/storage/StorageManager.h" @@ -28,6 +26,7 @@ namespace statsd { #ifdef __ANDROID__ #define STATS_DATA_DIR "/data/misc/stats-data" using android::base::StringPrintf; +using namespace std; namespace { @@ -298,6 +297,300 @@ TEST_P(ConfigUpdateE2eTest, TestConfigTtl) { &buffer); } +TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhat) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + *config.add_predicate() = holdingWakelockPredicate; + + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + // Create a wakelock acquire, causing the condition to be true. + unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, + attributionUids1, attributionTags1, + "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + durationMetric->set_bucket(FIVE_MINUTES); + + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true); + + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 1:20 + processor->OnLogEvent(event.get()); + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 1); + DurationMetricData data = metricData.data(0); + ASSERT_EQ(data.bucket_info_size(), 1); + + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.start_bucket_elapsed_nanos(), updateTimeNs); + EXPECT_EQ(bucketInfo.end_bucket_elapsed_nanos(), dumpTimeNs); + EXPECT_EQ(bucketInfo.duration_nanos(), 20 * NS_PER_SEC); +} + +TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher(); + *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher(); + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = holdingWakelockPredicate; + + Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate(); + *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/}); + *config.add_predicate() = isInBackgroundPredicate; + + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123, app2Uid = 456; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + vector<int> attributionUids2 = {app2Uid}; + vector<string> attributionTags2 = {"App2"}; + unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC, + attributionUids1, attributionTags1, + "wl1"); // 0:10 + processor->OnLogEvent(event.get()); + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, app1Uid); // 0:22 + processor->OnLogEvent(event.get()); + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids2, + attributionTags2, + "wl1"); // 0:35 + processor->OnLogEvent(event.get()); + + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->set_condition(isInBackgroundPredicate.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + // Links between wakelock state atom and condition of app is in background. + auto links = durationMetric->add_links(); + links->set_condition(isInBackgroundPredicate.id()); + *links->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *links->mutable_fields_in_condition() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/}); + + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true); + + event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 73 * NS_PER_SEC, app2Uid); // 1:13 + processor->OnLogEvent(event.get()); + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 1:24 + processor->OnLogEvent(event.get()); + + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 2); + + DurationMetricData data = metricData.data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ASSERT_EQ(data.bucket_info_size(), 1); + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 24 * NS_PER_SEC); + + data = metricData.data(1); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app2Uid); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 17 * NS_PER_SEC); +} + +TEST(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) { + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); + *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); + *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); + + Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate(); + // The predicate is dimensioning by first attribution node by uid. + *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *config.add_predicate() = holdingWakelockPredicate; + + auto uidProcessState = CreateUidProcessState(); + *config.add_state() = uidProcessState; + + // Count metric. We don't care about this one. Only use it so the StateTracker gets persisted. + CountMetric* countMetric = config.add_count_metric(); + countMetric->set_id(StringToId("Tmp")); + countMetric->set_what(config.atom_matcher(0).id()); + countMetric->add_slice_by_state(uidProcessState.id()); + // The metric is dimensioning by first attribution node and only by uid. + *countMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + countMetric->set_bucket(FIVE_MINUTES); + auto stateLink = countMetric->add_state_link(); + stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); + *stateLink->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *stateLink->mutable_fields_in_state() = + CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); + config.add_no_report_metric(countMetric->id()); + + ConfigKey key(123, 987); + uint64_t bucketStartTimeNs = 10000000000; // 0:10 + uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL; + sp<StatsLogProcessor> processor = + CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key); + + int app1Uid = 123, app2Uid = 456; + vector<int> attributionUids1 = {app1Uid}; + vector<string> attributionTags1 = {"App1"}; + vector<int> attributionUids2 = {app2Uid}; + vector<string> attributionTags2 = {"App2"}; + unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 10 * NS_PER_SEC, app1Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:10 + processor->OnLogEvent(event.get()); + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 22 * NS_PER_SEC, attributionUids1, + attributionTags1, + "wl1"); // 0:22 + processor->OnLogEvent(event.get()); + event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 30 * NS_PER_SEC, app2Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); // 0:30 + processor->OnLogEvent(event.get()); + + // Add metric. + DurationMetric* durationMetric = config.add_duration_metric(); + durationMetric->set_id(StringToId("WakelockDuration")); + durationMetric->set_what(holdingWakelockPredicate.id()); + durationMetric->add_slice_by_state(uidProcessState.id()); + durationMetric->set_aggregation_type(DurationMetric::SUM); + // The metric is dimensioning by first attribution node and only by uid. + *durationMetric->mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + durationMetric->set_bucket(FIVE_MINUTES); + // Links between wakelock state atom and condition of app is in background. + stateLink = durationMetric->add_state_link(); + stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED); + *stateLink->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *stateLink->mutable_fields_in_state() = + CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/}); + + uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC; // 1:00 + processor->OnConfigUpdated(updateTimeNs, key, config, /*modular*/ true); + + event = CreateAcquireWakelockEvent(bucketStartTimeNs + 72 * NS_PER_SEC, attributionUids2, + attributionTags2, + "wl1"); // 1:13 + processor->OnLogEvent(event.get()); + event = CreateUidProcessStateChangedEvent( + bucketStartTimeNs + 75 * NS_PER_SEC, app1Uid, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); // 1:15 + processor->OnLogEvent(event.get()); + event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1, + attributionTags1, "wl1"); // 1:24 + processor->OnLogEvent(event.get()); + + uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC; // 1:30 + ConfigMetricsReportList reports; + vector<uint8_t> buffer; + processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer); + EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size())); + backfillDimensionPath(&reports); + backfillStringInReport(&reports); + backfillStartEndTimestamp(&reports); + ASSERT_EQ(reports.reports_size(), 1); + ASSERT_EQ(reports.reports(0).metrics_size(), 1); + EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics()); + + StatsLogReport::DurationMetricDataWrapper metricData; + sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData); + ASSERT_EQ(metricData.data_size(), 3); + + DurationMetricData data = metricData.data(0); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + DurationBucketInfo bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 15 * NS_PER_SEC); + + data = metricData.data(1); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app1Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 9 * NS_PER_SEC); + + data = metricData.data(2); + ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED, + app2Uid); + ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED, + android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND); + ASSERT_EQ(data.bucket_info_size(), 1); + bucketInfo = data.bucket_info(0); + EXPECT_EQ(bucketInfo.duration_nanos(), 18 * NS_PER_SEC); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index d1f89775ed6a..bb2ede4ddc2b 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -73,9 +73,9 @@ TEST(DurationMetricTrackerTest, TestFirstBucket) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, 5, - 600 * NS_PER_SEC + NS_PER_SEC / 2); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, durationProducer.mCurrentBucketNum); @@ -101,9 +101,9 @@ TEST(DurationMetricTrackerTest, TestNoCondition) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); durationProducer.onMatchedLogEvent(1 /* start index*/, event1); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); @@ -145,8 +145,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); + -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); durationProducer.mCondition = ConditionState::kFalse; EXPECT_FALSE(durationProducer.mCondition); @@ -195,8 +196,9 @@ TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) { DurationMetricProducer durationProducer( kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown}, - 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, - wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); + -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */, + 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, + bucketStartTimeNs, bucketStartTimeNs); EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -240,9 +242,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -303,9 +305,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollo FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -367,9 +369,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); EXPECT_TRUE(anomalyTracker != nullptr); @@ -413,9 +415,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) { FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1; LogEvent event1(/*uid=*/0, /*pid=*/0); @@ -467,9 +469,9 @@ TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextB FieldMatcher dimensions; DurationMetricProducer durationProducer( - kConfigKey, metric, -1 /* no condition */, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions, - bucketStartTimeNs, bucketStartTimeNs); + kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs); int64_t startTimeNs = bucketStartTimeNs + 1; LogEvent event1(/*uid=*/0, /*pid=*/0); diff --git a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp index 9e2350b33018..88df2ff0caa8 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp @@ -875,8 +875,9 @@ TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) { FieldMatcher dimensions; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer( - kConfigKey, metric, -1 /*no condition*/, {}, 1 /* start index */, 2 /* stop index */, - 3 /* stop_all index */, false /*nesting*/, wizard, 0x0123456789, dimensions, 0, 0)}); + kConfigKey, metric, -1 /*no condition*/, {}, -1 /* what index not needed*/, + 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, + wizard, 0x0123456789, dimensions, 0, 0)}); sp<AlarmMonitor> anomalyAlarmMonitor; EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt); } diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 1761d5d9e1fa..153b696808dc 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -1091,6 +1091,21 @@ void ValidateAttributionUidAndTagDimension( .value_tuple().dimensions_value(1).value_str(), tag); } +void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues, + int atomId, int64_t value) { + ASSERT_EQ(stateValues.size(), 1); + ASSERT_EQ(stateValues[0].atom_id(), atomId); + switch (stateValues[0].contents_case()) { + case StateValue::ContentsCase::kValue: + EXPECT_EQ(stateValues[0].value(), (int32_t)value); + break; + case StateValue::ContentsCase::kGroupId: + EXPECT_EQ(stateValues[0].group_id(), value); + break; + default: + FAIL() << "State value should have either a value or a group id"; + } +} bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) { if (s1.field() != s2.field()) { return false; diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 1220019e2353..c51491244fd8 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -347,6 +347,8 @@ void ValidateAttributionUidAndTagDimension( const DimensionsValue& value, int atomId, int uid, const std::string& tag); void ValidateAttributionUidAndTagDimension( const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag); +void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues, + int atomId, int64_t value); struct DimensionsPair { DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2) |