From c876b492cebd146d75f92741954f5a7957dccdca Mon Sep 17 00:00:00 2001 From: tsaichristine Date: Tue, 10 Dec 2019 13:47:05 -0800 Subject: [PATCH] Slice by state in ValueMetricProducer - Added #onStateChanged logic to ValueMetricProducer - #onDumpReport fills in slice_by_state values in ValueMetricProducer - Base information is tracked in a separate data structure and keyed by dimensions_in_what HashableDimensionKey. The current state key is also stored in base info so we can close the interval for this state once we get a new state key. - Added unit tests for ValueMetricProducer onStateChange Other changes: - mCondition parameter was removed from #pullAndMatchEventsLocked and #accumulateEvents because it is unused in these functions. - The event key is initialized in each metric's internal onMatchedLogEvent function. This allows ValueMetric to skip events that have primary keys that do not match the current state change primary key. Test: bit statsd_test:* Change-Id: I565c6d251262be57abf10fdef243b8bdc01f3772 --- cmds/statsd/src/HashableDimensionKey.cpp | 4 + cmds/statsd/src/HashableDimensionKey.h | 2 + cmds/statsd/src/StatsLogProcessor.h | 4 + .../src/metrics/CountMetricProducer.cpp | 4 +- cmds/statsd/src/metrics/CountMetricProducer.h | 4 +- .../src/metrics/DurationMetricProducer.cpp | 4 +- .../src/metrics/DurationMetricProducer.h | 4 +- .../src/metrics/EventMetricProducer.cpp | 4 +- cmds/statsd/src/metrics/EventMetricProducer.h | 4 +- .../src/metrics/GaugeMetricProducer.cpp | 4 +- cmds/statsd/src/metrics/GaugeMetricProducer.h | 4 +- cmds/statsd/src/metrics/MetricProducer.cpp | 5 +- cmds/statsd/src/metrics/MetricProducer.h | 8 +- cmds/statsd/src/metrics/MetricsManager.h | 4 + .../src/metrics/ValueMetricProducer.cpp | 156 ++- cmds/statsd/src/metrics/ValueMetricProducer.h | 32 +- .../src/metrics/metrics_manager_util.cpp | 44 +- cmds/statsd/src/state/StateManager.cpp | 10 +- cmds/statsd/src/state/StateManager.h | 11 +- cmds/statsd/src/stats_log.proto | 4 +- cmds/statsd/src/statsd_config.proto | 6 +- .../statsd/tests/e2e/CountMetric_e2e_test.cpp | 3 - .../tests/e2e/ValueMetric_pull_e2e_test.cpp | 162 +++ .../metrics/ValueMetricProducer_test.cpp | 1190 ++++++++++++++--- cmds/statsd/tests/state/StateTracker_test.cpp | 1 + cmds/statsd/tests/statsd_test_util.h | 3 + 26 files changed, 1422 insertions(+), 259 deletions(-) diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp index f9f11b267d5ea..109785f649e46 100644 --- a/cmds/statsd/src/HashableDimensionKey.cpp +++ b/cmds/statsd/src/HashableDimensionKey.cpp @@ -152,6 +152,10 @@ bool LessThan(const vector& s1, const vector& s2) { return false; } +bool HashableDimensionKey::operator!=(const HashableDimensionKey& that) const { + return !((*this) == that); +} + bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const { if (mValues.size() != that.getValues().size()) { return false; diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h index b9b86ce13c8bb..654e1358f2a1c 100644 --- a/cmds/statsd/src/HashableDimensionKey.h +++ b/cmds/statsd/src/HashableDimensionKey.h @@ -71,6 +71,8 @@ public: std::string toString() const; + bool operator!=(const HashableDimensionKey& that) const; + bool operator==(const HashableDimensionKey& that) const; bool operator<(const HashableDimensionKey& that) const; diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 68a51efb031a5..c569bc1e33f76 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -284,6 +284,10 @@ private: FRIEND_TEST(DurationMetricE2eTest, TestWithCondition); FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition); FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition); + + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index c1f95ee58217d..21ffff32f5398 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -277,8 +277,8 @@ bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { void CountMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, - const LogEvent& event) { + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const map& statePrimaryKeys) { int64_t eventTimeNs = event.GetElapsedTimestampNs(); flushIfNeededLocked(eventTimeNs); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 7b6c7e0ef0bec..a4711e8357f21 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -59,8 +59,8 @@ public: protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, - const LogEvent& event) override; + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const std::map& statePrimaryKeys) override; private: diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index fee5e6e3cb466..35c6d373418eb 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -541,8 +541,8 @@ void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey void DurationMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, bool condition, - const LogEvent& event) { + const ConditionKey& conditionKeys, bool condition, const LogEvent& event, + const map& statePrimaryKeys) { ALOGW("Not used in duration tracker."); } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 7457d7fb2dd96..45908fb48f752 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -59,8 +59,8 @@ protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKeys, bool condition, - const LogEvent& event) override; + const ConditionKey& conditionKeys, bool condition, const LogEvent& event, + const std::map& statePrimaryKeys) override; private: void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys, diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index 32eb077e1cf44..6833f8dd01142 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -143,8 +143,8 @@ void EventMetricProducer::onConditionChangedLocked(const bool conditionMet, void EventMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, - const LogEvent& event) { + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const map& statePrimaryKeys) { if (!condition) { return; } diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index dca37e8790a40..e8f2119a170c4 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -47,8 +47,8 @@ public: private: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, - const LogEvent& event) override; + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const std::map& statePrimaryKeys) override; void onDumpReportLocked(const int64_t dumpTimeNs, const bool include_current_partial_bucket, diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 4ab6ec376190d..4ab6fd48f1db5 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -449,8 +449,8 @@ bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) { void GaugeMetricProducer::onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, - const LogEvent& event) { + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const map& statePrimaryKeys) { if (condition == false) { return; } diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index 12dcaa4539e9b..284bcc5d10aa8 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -95,8 +95,8 @@ public: protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, - const LogEvent& event) override; + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const std::map& statePrimaryKeys) override; private: void onDumpReportLocked(const int64_t dumpTimeNs, diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index cf1d2f3269489..5c29cb3c27fe4 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -133,8 +133,8 @@ void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const Lo HashableDimensionKey dimensionInWhat; filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat); MetricDimensionKey metricKey(dimensionInWhat, stateValuesKey); - onMatchedLogEventInternalLocked( - matcherIndex, metricKey, conditionKey, condition, event); + onMatchedLogEventInternalLocked(matcherIndex, metricKey, conditionKey, condition, event, + statePrimaryKeys); } bool MetricProducer::evaluateActiveStateLocked(int64_t elapsedTimestampNs) { @@ -269,6 +269,7 @@ void MetricProducer::getMappedStateValue(const int32_t atomId, const HashableDim FieldValue* value) { if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) { value->mValue = Value(StateTracker::kStateUnknown); + value->mField.setTag(atomId); ALOGW("StateTracker not found for state atom %d", atomId); return; } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 30675fcf8fbc4..99f0c64bd47c5 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -330,8 +330,8 @@ protected: */ virtual void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, - const LogEvent& event) = 0; + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const map& statePrimaryKeys) = 0; // Consume the parsed stats log entry that already matched the "what" of the metric. virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event); @@ -475,6 +475,10 @@ protected: FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBootMultipleActivationsDifferentActivationTypes); FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); + + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 1fda6960b59b5..6d20822fd54cf 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -289,6 +289,10 @@ private: FRIEND_TEST(DurationMetricE2eTest, TestWithCondition); FRIEND_TEST(DurationMetricE2eTest, TestWithSlicedCondition); FRIEND_TEST(DurationMetricE2eTest, TestWithActivationAndSlicedCondition); + + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState); + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions); + FRIEND_TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index d8f399f754872..d2db6e9c9ead3 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -66,6 +66,7 @@ const int FIELD_ID_DROP_TIME = 2; const int FIELD_ID_DIMENSION_IN_WHAT = 1; const int FIELD_ID_BUCKET_INFO = 3; const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4; +const int FIELD_ID_SLICE_BY_STATE = 6; // for ValueBucketInfo const int FIELD_ID_VALUE_INDEX = 1; const int FIELD_ID_VALUE_LONG = 2; @@ -146,6 +147,14 @@ ValueMetricProducer::ValueMetricProducer( mConditionSliced = true; } + for (const auto& stateLink : metric.state_link()) { + Metric2State ms; + ms.stateAtomId = stateLink.state_atom_id(); + translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields); + translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields); + mMetric2StateLinks.push_back(ms); + } + int64_t numBucketsForward = calcBucketsForwardCount(startTimeNs); mCurrentBucketNum += numBucketsForward; @@ -181,6 +190,33 @@ ValueMetricProducer::~ValueMetricProducer() { } } +void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId, + const HashableDimensionKey& primaryKey, int oldState, + int newState) { + VLOG("ValueMetric %lld onStateChanged time %lld, State %d, key %s, %d -> %d", + (long long)mMetricId, (long long)eventTimeNs, atomId, primaryKey.toString().c_str(), + oldState, newState); + // If condition is not true, we do not need to pull for this state change. + if (mCondition != ConditionState::kTrue) { + return; + } + bool isEventLate = eventTimeNs < mCurrentBucketStartTimeNs; + if (isEventLate) { + VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, + (long long)mCurrentBucketStartTimeNs); + invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET); + return; + } + mStateChangePrimaryKey.first = atomId; + mStateChangePrimaryKey.second = primaryKey; + if (mIsPulled) { + pullAndMatchEventsLocked(eventTimeNs); + } + mStateChangePrimaryKey.first = 0; + mStateChangePrimaryKey.second = DEFAULT_DIMENSION_KEY; + flushIfNeededLocked(eventTimeNs); +} + void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) { VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId); @@ -281,6 +317,14 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, FIELD_ID_DIMENSION_LEAF_IN_WHAT, str_set, protoOutput); } + // Then fill slice_by_state. + for (auto state : dimensionKey.getStateValuesKey().getValues()) { + uint64_t stateToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | + FIELD_ID_SLICE_BY_STATE); + writeStateToProto(state, protoOutput); + protoOutput->end(stateToken); + } + // Then fill bucket_info (ValueBucketInfo). for (const auto& bucket : pair.second) { uint64_t bucketInfoToken = protoOutput->start( @@ -300,7 +344,7 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS, (long long)bucket.mConditionTrueNs); } - for (int i = 0; i < (int)bucket.valueIndex.size(); i ++) { + for (int i = 0; i < (int)bucket.valueIndex.size(); i++) { int index = bucket.valueIndex[i]; const Value& value = bucket.values[i]; uint64_t valueToken = protoOutput->start( @@ -358,9 +402,10 @@ void ValueMetricProducer::invalidateCurrentBucket(const int64_t dropTimeNs, } void ValueMetricProducer::resetBase() { - for (auto& slice : mCurrentSlicedBucket) { - for (auto& interval : slice.second) { - interval.hasBase = false; + for (auto& slice : mCurrentBaseInfo) { + for (auto& baseInfo : slice.second) { + baseInfo.hasBase = false; + baseInfo.hasCurrentState = false; } } mHasGlobalBase = false; @@ -558,14 +603,20 @@ void ValueMetricProducer::accumulateEvents(const std::vectorsecond) { + baseInfo.hasBase = false; + baseInfo.hasCurrentState = false; } } } @@ -674,17 +725,30 @@ bool getDoubleOrLong(const LogEvent& event, const Matcher& matcher, Value& ret) return false; } -void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIndex, - const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, - bool condition, const LogEvent& event) { +void ValueMetricProducer::onMatchedLogEventInternalLocked( + const size_t matcherIndex, const MetricDimensionKey& eventKey, + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const map& statePrimaryKeys) { + auto whatKey = eventKey.getDimensionKeyInWhat(); + auto stateKey = eventKey.getStateValuesKey(); + + // Skip this event if a state changed occurred for a different primary key. + auto it = statePrimaryKeys.find(mStateChangePrimaryKey.first); + // Check that both the atom id and the primary key are equal. + if (it != statePrimaryKeys.end() && it->second != mStateChangePrimaryKey.second) { + VLOG("ValueMetric skip event with primary key %s because state change primary key " + "is %s", + it->second.toString().c_str(), mStateChangePrimaryKey.second.toString().c_str()); + return; + } + int64_t eventTimeNs = event.GetElapsedTimestampNs(); if (eventTimeNs < mCurrentBucketStartTimeNs) { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); return; } - mMatchedMetricDimensionKeys.insert(eventKey); + mMatchedMetricDimensionKeys.insert(whatKey); if (!mIsPulled) { // We cannot flush without doing a pull first. @@ -709,10 +773,26 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn if (hitGuardRailLocked(eventKey)) { return; } - vector& multiIntervals = mCurrentSlicedBucket[eventKey]; - if (multiIntervals.size() < mFieldMatchers.size()) { + vector& baseInfos = mCurrentBaseInfo[whatKey]; + if (baseInfos.size() < mFieldMatchers.size()) { VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); - multiIntervals.resize(mFieldMatchers.size()); + baseInfos.resize(mFieldMatchers.size()); + } + + for (auto baseInfo : baseInfos) { + if (!baseInfo.hasCurrentState) { + baseInfo.currentState = DEFAULT_DIMENSION_KEY; + baseInfo.hasCurrentState = true; + } + } + + // We need to get the intervals stored with the previous state key so we can + // close these value intervals. + const auto oldStateKey = baseInfos[0].currentState; + vector& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]; + if (intervals.size() < mFieldMatchers.size()) { + VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size()); + intervals.resize(mFieldMatchers.size()); } // We only use anomaly detection under certain cases. @@ -725,7 +805,8 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn for (int i = 0; i < (int)mFieldMatchers.size(); i++) { const Matcher& matcher = mFieldMatchers[i]; - Interval& interval = multiIntervals[i]; + BaseInfo& baseInfo = baseInfos[i]; + Interval& interval = intervals[i]; interval.valueIndex = i; Value value; if (!getDoubleOrLong(event, matcher, value)) { @@ -736,60 +817,61 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn interval.seenNewData = true; if (mUseDiff) { - if (!interval.hasBase) { + if (!baseInfo.hasBase) { if (mHasGlobalBase && mUseZeroDefaultBase) { // The bucket has global base. This key does not. // Optionally use zero as base. - interval.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE); - interval.hasBase = true; + baseInfo.base = (value.type == LONG ? ZERO_LONG : ZERO_DOUBLE); + baseInfo.hasBase = true; } else { // no base. just update base and return. - interval.base = value; - interval.hasBase = true; + baseInfo.base = value; + baseInfo.hasBase = true; // If we're missing a base, do not use anomaly detection on incomplete data useAnomalyDetection = false; - // Continue (instead of return) here in order to set interval.base and - // interval.hasBase for other intervals + // Continue (instead of return) here in order to set baseInfo.base and + // baseInfo.hasBase for other baseInfos continue; } } + Value diff; switch (mValueDirection) { case ValueMetric::INCREASING: - if (value >= interval.base) { - diff = value - interval.base; + if (value >= baseInfo.base) { + diff = value - baseInfo.base; } else if (mUseAbsoluteValueOnReset) { diff = value; } else { VLOG("Unexpected decreasing value"); StatsdStats::getInstance().notePullDataError(mPullTagId); - interval.base = value; + baseInfo.base = value; // If we've got bad data, do not use anomaly detection useAnomalyDetection = false; continue; } break; case ValueMetric::DECREASING: - if (interval.base >= value) { - diff = interval.base - value; + if (baseInfo.base >= value) { + diff = baseInfo.base - value; } else if (mUseAbsoluteValueOnReset) { diff = value; } else { VLOG("Unexpected increasing value"); StatsdStats::getInstance().notePullDataError(mPullTagId); - interval.base = value; + baseInfo.base = value; // If we've got bad data, do not use anomaly detection useAnomalyDetection = false; continue; } break; case ValueMetric::ANY: - diff = value - interval.base; + diff = value - baseInfo.base; break; default: break; } - interval.base = value; + baseInfo.base = value; value = diff; } @@ -814,12 +896,13 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn interval.hasValue = true; } interval.sampleSize += 1; + baseInfo.currentState = stateKey; } // Only trigger the tracker if all intervals are correct if (useAnomalyDetection) { // TODO: propgate proper values down stream when anomaly support doubles - long wholeBucketVal = multiIntervals[0].value.long_value; + long wholeBucketVal = intervals[0].value.long_value; auto prev = mCurrentFullBucket.find(eventKey); if (prev != mCurrentFullBucket.end()) { wholeBucketVal += prev->second; @@ -953,6 +1036,7 @@ void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) } else { it++; } + // TODO: remove mCurrentBaseInfo entries when obsolete } mCurrentBucketIsInvalid = false; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 4eae99bfcba40..19fb6942928f7 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -83,11 +83,14 @@ public: flushCurrentBucketLocked(eventTimeNs, eventTimeNs); }; + void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey, + int oldState, int newState) override; + protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, - const ConditionKey& conditionKey, bool condition, - const LogEvent& event) override; + const ConditionKey& conditionKey, bool condition, const LogEvent& event, + const std::map& statePrimaryKeys) override; private: void onDumpReportLocked(const int64_t dumpTimeNs, @@ -144,7 +147,10 @@ private: std::vector mFieldMatchers; // Value fields for matching. - std::set mMatchedMetricDimensionKeys; + std::set mMatchedMetricDimensionKeys; + + // Holds the atom id, primary key pair from a state change. + pair mStateChangePrimaryKey; // tagId for pulled data. -1 if this is not pulled const int mPullTagId; @@ -156,10 +162,6 @@ private: typedef struct { // Index in multi value aggregation. int valueIndex; - // Holds current base value of the dimension. Take diff and update if necessary. - Value base; - // Whether there is a base to diff to. - bool hasBase; // Current value, depending on the aggregation type. Value value; // Number of samples collected. @@ -171,8 +173,21 @@ private: bool seenNewData = false; } Interval; + typedef struct { + // Holds current base value of the dimension. Take diff and update if necessary. + Value base; + // Whether there is a base to diff to. + bool hasBase; + // Last seen state value(s). + HashableDimensionKey currentState; + // Whether this dimensions in what key has a current state key. + bool hasCurrentState; + } BaseInfo; + std::unordered_map> mCurrentSlicedBucket; + std::unordered_map> mCurrentBaseInfo; + std::unordered_map mCurrentFullBucket; // Save the past buckets and we can clear when the StatsLogReport is dumped. @@ -285,6 +300,9 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate); FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput); FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedState); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMap); + FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions); FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 2ad8217c45d4b..73c121242523a 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -18,11 +18,12 @@ #include "Log.h" #include "metrics_manager_util.h" -#include "MetricProducer.h" #include #include "atoms_info.h" +#include "FieldValue.h" +#include "MetricProducer.h" #include "condition/CombinationConditionTracker.h" #include "condition/SimpleConditionTracker.h" #include "condition/StateConditionTracker.h" @@ -173,6 +174,14 @@ bool handleMetricWithStates( return true; } +bool handleMetricWithStateLink(const FieldMatcher& stateMatcher, + const vector& dimensionsInWhat) { + vector stateMatchers; + translateFieldMatcher(stateMatcher, &stateMatchers); + + return subsetDimensions(stateMatchers, dimensionsInWhat); +} + // Validates a metricActivation and populates state. // EventActivationMap and EventDeactivationMap are supplied to a MetricProducer // to provide the producer with state about its activators and deactivators. @@ -669,18 +678,41 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + std::vector slicedStateAtoms; + unordered_map> stateGroupMap; + if (metric.slice_by_state_size() > 0) { + if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, + allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { + return false; + } + } else { + if (metric.state_link_size() > 0) { + ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state"); + return false; + } + } + + // Check that all metric state links are a subset of dimensions_in_what fields. + std::vector dimensionsInWhat; + translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); + for (const auto& stateLink : metric.state_link()) { + if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { + return false; + } + } + unordered_map> eventActivationMap; unordered_map>> eventDeactivationMap; - bool success = handleMetricActivation(config, metric.id(), metricIndex, - metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap, - deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap, - eventDeactivationMap); + bool success = handleMetricActivation( + config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation, eventActivationMap, eventDeactivationMap); if (!success) return false; sp valueProducer = new ValueMetricProducer( key, metric, conditionIndex, wizard, trackerIndex, matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager, eventActivationMap, - eventDeactivationMap); + eventDeactivationMap, slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(valueProducer); } diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp index 80d398339ebb9..ea776fae05833 100644 --- a/cmds/statsd/src/state/StateManager.cpp +++ b/cmds/statsd/src/state/StateManager.cpp @@ -28,13 +28,17 @@ StateManager& StateManager::getInstance() { return sStateManager; } +void StateManager::clear() { + mStateTrackers.clear(); +} + void StateManager::onLogEvent(const LogEvent& event) { if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) { mStateTrackers[event.GetTagId()]->onLogEvent(event); } } -bool StateManager::registerListener(int32_t atomId, wp listener) { +bool StateManager::registerListener(const int32_t atomId, wp listener) { // Check if state tracker already exists. if (mStateTrackers.find(atomId) == mStateTrackers.end()) { // Create a new state tracker iff atom is a state atom. @@ -50,7 +54,7 @@ bool StateManager::registerListener(int32_t atomId, wp listener) return true; } -void StateManager::unregisterListener(int32_t atomId, wp listener) { +void StateManager::unregisterListener(const int32_t atomId, wp listener) { std::unique_lock lock(mMutex); // Hold the sp<> until the lock is released so that ~StateTracker() is @@ -74,7 +78,7 @@ void StateManager::unregisterListener(int32_t atomId, wp listener lock.unlock(); } -bool StateManager::getStateValue(int32_t atomId, const HashableDimensionKey& key, +bool StateManager::getStateValue(const int32_t atomId, const HashableDimensionKey& key, FieldValue* output) const { auto it = mStateTrackers.find(atomId); if (it != mStateTrackers.end()) { diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h index a6053e6f317e6..8bc24612be904 100644 --- a/cmds/statsd/src/state/StateManager.h +++ b/cmds/statsd/src/state/StateManager.h @@ -40,30 +40,33 @@ public: // Returns a pointer to the single, shared StateManager object. static StateManager& getInstance(); + // Unregisters all listeners and removes all trackers from StateManager. + void clear(); + // Notifies the correct StateTracker of an event. void onLogEvent(const LogEvent& event); // Returns true if atomId is being tracked and is associated with a state // atom. StateManager notifies the correct StateTracker to register listener. // If the correct StateTracker does not exist, a new StateTracker is created. - bool registerListener(int32_t atomId, wp listener); + bool registerListener(const int32_t atomId, wp listener); // Notifies the correct StateTracker to unregister a listener // and removes the tracker if it no longer has any listeners. - void unregisterListener(int32_t atomId, wp listener); + void unregisterListener(const int32_t atomId, wp listener); // Returns true if the StateTracker exists and queries for the // original state value mapped to the given query key. The state value is // stored and output in a FieldValue class. // Returns false if the StateTracker doesn't exist. - bool getStateValue(int32_t atomId, const HashableDimensionKey& queryKey, + bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey, FieldValue* output) const; inline int getStateTrackersCount() const { return mStateTrackers.size(); } - inline int getListenersCount(int32_t atomId) const { + inline int getListenersCount(const int32_t atomId) const { auto it = mStateTrackers.find(atomId); if (it != mStateTrackers.end()) { return it->second->getListenersCount(); diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 8b4d78143b61a..c45274e4a3de1 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -147,12 +147,14 @@ message ValueBucketInfo { message ValueMetricData { optional DimensionsValue dimensions_in_what = 1; - optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; + repeated StateValue slice_by_state = 6; repeated ValueBucketInfo bucket_info = 3; repeated DimensionsValue dimension_leaf_values_in_what = 4; + optional DimensionsValue dimensions_in_condition = 2 [deprecated = true]; + repeated DimensionsValue dimension_leaf_values_in_condition = 5 [deprecated = true]; } diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index a22805b6b525e..736aa9be2fdbb 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -290,12 +290,14 @@ message ValueMetric { optional FieldMatcher dimensions_in_what = 5; - optional FieldMatcher dimensions_in_condition = 9 [deprecated = true]; + repeated int64 slice_by_state = 18; optional TimeUnit bucket = 6; repeated MetricConditionLink links = 7; + repeated MetricStateLink state_link = 19; + enum AggregationType { SUM = 1; MIN = 2; @@ -325,6 +327,8 @@ message ValueMetric { optional int32 max_pull_delay_sec = 16 [default = 10]; optional bool split_bucket_for_app_upgrade = 17 [default = true]; + + optional FieldMatcher dimensions_in_condition = 9 [deprecated = true]; } message Alert { diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp index 0f51c1b5a4ce7..15fc468ffe570 100644 --- a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -27,9 +27,6 @@ namespace statsd { #ifdef __ANDROID__ -const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED; -const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED; - /** * Test a count metric that has one slice_by_state with no primary fields. * diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp index fb878dc7efed1..e8d2ec514cad5 100644 --- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp +++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp @@ -369,6 +369,168 @@ TEST(ValueMetricE2eTest, TestPulledEvents_WithActivation) { EXPECT_EQ(1, bucketInfo.values_size()); } +/** + * Test initialization of a simple value metric that is sliced by a state. + * + * ValueCpuUserTimePerScreenState + */ +TEST(ValueMetricE2eTest, TestInitWithSlicedState) { + // Create config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto pulledAtomMatcher = + CreateSimpleAtomMatcher("TestMatcher", android::util::SUBSYSTEM_SLEEP_STATE); + *config.add_atom_matcher() = pulledAtomMatcher; + + auto screenState = CreateScreenState(); + *config.add_state() = screenState; + + // Create value metric that slices by screen state without a map. + int64_t metricId = 123456; + auto valueMetric = config.add_value_metric(); + valueMetric->set_id(metricId); + valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); + valueMetric->set_what(pulledAtomMatcher.id()); + *valueMetric->mutable_value_field() = + CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); + valueMetric->add_slice_by_state(screenState.id()); + valueMetric->set_max_pull_delay_sec(INT_MAX); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + const uint64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000LL; + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that StateTrackers were initialized correctly. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + // Check that ValueMetricProducer was initialized correctly. + EXPECT_EQ(1U, processor->mMetricsManagers.size()); + sp metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(1, metricsManager->mAllMetricProducers.size()); + sp metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(1, metricProducer->mSlicedStateAtoms.size()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0)); + EXPECT_EQ(0, metricProducer->mStateGroupMap.size()); +} + +/** + * Test initialization of a value metric that is sliced by state and has + * dimensions_in_what. + * + * ValueCpuUserTimePerUidPerUidProcessState + */ +TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithDimensions) { + // Create config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto cpuTimePerUidMatcher = + CreateSimpleAtomMatcher("CpuTimePerUidMatcher", android::util::CPU_TIME_PER_UID); + *config.add_atom_matcher() = cpuTimePerUidMatcher; + + auto uidProcessState = CreateUidProcessState(); + *config.add_state() = uidProcessState; + + // Create value metric that slices by screen state with a complete map. + int64_t metricId = 123456; + auto valueMetric = config.add_value_metric(); + valueMetric->set_id(metricId); + valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); + valueMetric->set_what(cpuTimePerUidMatcher.id()); + *valueMetric->mutable_value_field() = + CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); + *valueMetric->mutable_dimensions_in_what() = + CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */}); + valueMetric->add_slice_by_state(uidProcessState.id()); + MetricStateLink* stateLink = valueMetric->add_state_link(); + stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); + auto fieldsInWhat = stateLink->mutable_fields_in_what(); + *fieldsInWhat = CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */}); + auto fieldsInState = stateLink->mutable_fields_in_state(); + *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); + valueMetric->set_max_pull_delay_sec(INT_MAX); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // Check that StateTrackers were initialized correctly. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); + + // Check that ValueMetricProducer was initialized correctly. + EXPECT_EQ(1U, processor->mMetricsManagers.size()); + sp metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(1, metricsManager->mAllMetricProducers.size()); + sp metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(1, metricProducer->mSlicedStateAtoms.size()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, metricProducer->mSlicedStateAtoms.at(0)); + EXPECT_EQ(0, metricProducer->mStateGroupMap.size()); +} + +/** + * Test initialization of a value metric that is sliced by state and has + * dimensions_in_what. + * + * ValueCpuUserTimePerUidPerUidProcessState + */ +TEST(ValueMetricE2eTest, TestInitWithSlicedState_WithIncorrectDimensions) { + // Create config. + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto cpuTimePerUidMatcher = + CreateSimpleAtomMatcher("CpuTimePerUidMatcher", android::util::CPU_TIME_PER_UID); + *config.add_atom_matcher() = cpuTimePerUidMatcher; + + auto uidProcessState = CreateUidProcessState(); + *config.add_state() = uidProcessState; + + // Create value metric that slices by screen state with a complete map. + int64_t metricId = 123456; + auto valueMetric = config.add_value_metric(); + valueMetric->set_id(metricId); + valueMetric->set_bucket(TimeUnit::FIVE_MINUTES); + valueMetric->set_what(cpuTimePerUidMatcher.id()); + *valueMetric->mutable_value_field() = + CreateDimensions(android::util::CPU_TIME_PER_UID, {2 /* user_time_micros */}); + valueMetric->add_slice_by_state(uidProcessState.id()); + MetricStateLink* stateLink = valueMetric->add_state_link(); + stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); + auto fieldsInWhat = stateLink->mutable_fields_in_what(); + *fieldsInWhat = CreateDimensions(android::util::CPU_TIME_PER_UID, {1 /* uid */}); + auto fieldsInState = stateLink->mutable_fields_in_state(); + *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); + valueMetric->set_max_pull_delay_sec(INT_MAX); + + // Initialize StatsLogProcessor. + const uint64_t bucketStartTimeNs = 10000000000; // 0:10 + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); + + // No StateTrackers are initialized. + EXPECT_EQ(0, StateManager::getInstance().getStateTrackersCount()); + + // Config initialization fails. + EXPECT_EQ(0, processor->mMetricsManagers.size()); +} + #else GTEST_LOG_(INFO) << "This test does nothing.\n"; #endif diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index da0a672a01626..92e8241d9ec22 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -148,6 +148,26 @@ class ValueMetricProducerTestHelper { return valueProducer; } + static sp createValueProducerWithState( + sp& pullerManager, ValueMetric& metric, + vector slicedStateAtoms, + unordered_map> stateGroupMap) { + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp wizard = new NaggyMock(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + sp valueProducer = new ValueMetricProducer( + kConfigKey, metric, -1 /* no condition */, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, + {}, slicedStateAtoms, stateGroupMap); + return valueProducer; + } + static ValueMetric createMetric() { ValueMetric metric; metric.set_id(metricId); @@ -163,8 +183,13 @@ class ValueMetricProducerTestHelper { metric.set_condition(StringToId("SCREEN_ON")); return metric; } -}; + static ValueMetric createMetricWithState(string state) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + metric.add_slice_by_state(StringToId(state)); + return metric; + } +}; /* * Tests that the first bucket works correctly @@ -253,10 +278,12 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval = + valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(11, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(11, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); @@ -273,9 +300,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(23, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(23, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(12, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); @@ -294,9 +322,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(36, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(13, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); @@ -394,9 +423,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { })); sp valueProducer = new ValueMetricProducer( - kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager); vector> allData; allData.clear(); @@ -411,9 +439,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(11, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(11, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); @@ -430,8 +459,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { // No new data seen, so data has been cleared. EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(11, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(11, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); @@ -447,10 +476,11 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; // the base was reset - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(36, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size()); @@ -482,9 +512,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(11, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(11, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -498,8 +529,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(10, curInterval.base.long_value); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); @@ -515,8 +547,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(36, curInterval.base.long_value); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(26, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); @@ -549,9 +582,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(11, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(11, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -565,8 +599,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(10, curInterval.base.long_value); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -579,8 +614,9 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(36, curInterval.base.long_value); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(26, curInterval.value.long_value); EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); @@ -633,9 +669,10 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; // startUpdated:false sum:0 start:100 - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(100, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -652,8 +689,9 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(110, curInterval.base.long_value); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(110, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); @@ -663,9 +701,10 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(20, curInterval.value.long_value); - EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(false, curBaseInfo.hasBase); valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1}); @@ -879,6 +918,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(10, curInterval.value.long_value); EXPECT_EQ(true, curInterval.hasValue); @@ -1066,10 +1106,11 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; // startUpdated:true sum:0 start:11 - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(11, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(11, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -1084,9 +1125,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; // tartUpdated:false sum:12 - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(23, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(23, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}); @@ -1103,9 +1145,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; // startUpdated:false sum:12 - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(36, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(36, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}); } @@ -1148,16 +1191,18 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(100, curInterval.base.long_value); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); // pull on bucket boundary come late, condition change happens before it valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); - EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(false, curBaseInfo.hasBase); // Now the alarm is delivered. // since the condition turned to off before this pull finish, it has no effect @@ -1167,7 +1212,8 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); } @@ -1220,9 +1266,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; // startUpdated:false sum:0 start:100 - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(100, curInterval.base.long_value); + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -1231,15 +1278,17 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); // condition changed to true again, before the pull alarm is delivered valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(130, curInterval.base.long_value); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(130, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); // Now the alarm is delivered, but it is considered late, the data will be used @@ -1249,8 +1298,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(140, curInterval.base.long_value); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(140, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}); @@ -1477,8 +1527,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(10, curInterval.base.long_value); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(10, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); @@ -1497,8 +1548,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(15, curInterval.base.long_value); + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); shared_ptr event4 = make_shared(tagId, bucket2StartTimeNs + 15); @@ -1508,8 +1560,9 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(15, curInterval.base.long_value); + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(15, curBaseInfo.base.long_value); EXPECT_EQ(true, curInterval.hasValue); valueProducer.flushIfNeededLocked(bucket3StartTimeNs); @@ -1550,27 +1603,29 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval0 = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; - ValueMetricProducer::Interval curInterval1 = - valueProducer.mCurrentSlicedBucket.begin()->second[1]; - EXPECT_EQ(true, curInterval0.hasBase); - EXPECT_EQ(10, curInterval0.base.long_value); - EXPECT_EQ(false, curInterval0.hasValue); - EXPECT_EQ(true, curInterval1.hasBase); - EXPECT_EQ(20, curInterval1.base.long_value); - EXPECT_EQ(false, curInterval1.hasValue); + ValueMetricProducer::Interval curInterval = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(10, curBaseInfo.base.long_value); + EXPECT_EQ(false, curInterval.hasValue); + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(20, curBaseInfo.base.long_value); + EXPECT_EQ(false, curInterval.hasValue); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); // has one slice EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - EXPECT_EQ(true, curInterval0.hasValue); - EXPECT_EQ(5, curInterval0.value.long_value); - EXPECT_EQ(true, curInterval1.hasValue); - EXPECT_EQ(2, curInterval1.value.long_value); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(5, curInterval.value.long_value); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; + EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(2, curInterval.value.long_value); // no change in first value field shared_ptr event3 = make_shared(tagId, bucket2StartTimeNs + 10); @@ -1580,14 +1635,17 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { event3->init(); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - EXPECT_EQ(true, curInterval0.hasBase); - EXPECT_EQ(15, curInterval0.base.long_value); - EXPECT_EQ(true, curInterval0.hasValue); - EXPECT_EQ(true, curInterval1.hasBase); - EXPECT_EQ(25, curInterval1.base.long_value); - EXPECT_EQ(true, curInterval1.hasValue); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(15, curBaseInfo.base.long_value); + EXPECT_EQ(true, curInterval.hasValue); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(25, curBaseInfo.base.long_value); + EXPECT_EQ(true, curInterval.hasValue); shared_ptr event4 = make_shared(tagId, bucket2StartTimeNs + 15); event4->write(1); @@ -1596,14 +1654,16 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { event4->init(); valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; - EXPECT_EQ(true, curInterval0.hasBase); - EXPECT_EQ(15, curInterval0.base.long_value); - EXPECT_EQ(true, curInterval0.hasValue); - EXPECT_EQ(true, curInterval1.hasBase); - EXPECT_EQ(29, curInterval1.base.long_value); - EXPECT_EQ(true, curInterval1.hasValue); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(15, curBaseInfo.base.long_value); + EXPECT_EQ(true, curInterval.hasValue); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(29, curBaseInfo.base.long_value); + EXPECT_EQ(true, curInterval.hasValue); valueProducer.flushIfNeededLocked(bucket3StartTimeNs); @@ -1650,9 +1710,11 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); auto iter = valueProducer->mCurrentSlicedBucket.begin(); auto& interval1 = iter->second[0]; + auto iterBase = valueProducer->mCurrentBaseInfo.begin(); + auto& baseInfo1 = iterBase->second[0]; EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(3, interval1.base.long_value); + EXPECT_EQ(true, baseInfo1.hasBase); + EXPECT_EQ(3, baseInfo1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -1672,8 +1734,8 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(11, interval1.base.long_value); + EXPECT_EQ(true, baseInfo1.hasBase); + EXPECT_EQ(11, baseInfo1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); @@ -1683,11 +1745,19 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { break; } } + auto itBase = valueProducer->mCurrentBaseInfo.begin(); + for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { + if (itBase != iterBase) { + break; + } + } EXPECT_TRUE(it != iter); + EXPECT_TRUE(itBase != iterBase); auto& interval2 = it->second[0]; + auto& baseInfo2 = itBase->second[0]; EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(4, interval2.base.long_value); + EXPECT_EQ(true, baseInfo2.hasBase); + EXPECT_EQ(4, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(4, interval2.value.long_value); @@ -1725,11 +1795,13 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - auto iter = valueProducer->mCurrentSlicedBucket.begin(); - auto& interval1 = iter->second[0]; - EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(3, interval1.base.long_value); + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto& interval1 = it->second[0]; + auto& baseInfo1 = + valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(true, baseInfo1.hasBase); + EXPECT_EQ(3, baseInfo1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -1749,22 +1821,31 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(11, interval1.base.long_value); + EXPECT_EQ(true, baseInfo1.hasBase); + EXPECT_EQ(11, baseInfo1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); - auto it = valueProducer->mCurrentSlicedBucket.begin(); - for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { - if (it != iter) { + auto it2 = valueProducer->mCurrentSlicedBucket.begin(); + for (; it2 != valueProducer->mCurrentSlicedBucket.end(); it2++) { + if (it2 != it) { break; } } - EXPECT_TRUE(it != iter); - auto& interval2 = it->second[0]; - EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(4, interval2.base.long_value); + // auto itBase = valueProducer->mCurrentBaseInfo.begin(); + // for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { + // if (itBase != iterBase) { + // break; + // } + // } + EXPECT_TRUE(it2 != it); + // EXPECT_TRUE(itBase != iterBase); + auto& interval2 = it2->second[0]; + auto& baseInfo2 = + valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0]; + EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(true, baseInfo2.hasBase); + EXPECT_EQ(4, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(4, interval2.value.long_value); EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); @@ -1779,8 +1860,8 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(5, interval2.base.long_value); + EXPECT_EQ(true, baseInfo2.hasBase); + EXPECT_EQ(5, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); @@ -1799,17 +1880,24 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - auto it1 = std::next(valueProducer->mCurrentSlicedBucket.begin())->second[0]; - EXPECT_EQ(true, it1.hasBase); - EXPECT_EQ(13, it1.base.long_value); - EXPECT_EQ(false, it1.hasValue); - EXPECT_EQ(8, it1.value.long_value); - auto it2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, it2.hasBase); - EXPECT_EQ(5, it2.base.long_value); - EXPECT_EQ(false, it2.hasValue); - EXPECT_EQ(5, it2.value.long_value); + it = valueProducer->mCurrentSlicedBucket.begin(); + it2 = std::next(valueProducer->mCurrentSlicedBucket.begin()); + interval1 = it->second[0]; + interval2 = it2->second[0]; + baseInfo1 = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0]; + baseInfo2 = valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0]; + + EXPECT_EQ(true, baseInfo1.hasBase); + EXPECT_EQ(5, baseInfo1.base.long_value); + EXPECT_EQ(false, interval1.hasValue); + EXPECT_EQ(5, interval1.value.long_value); EXPECT_EQ(true, valueProducer->mHasGlobalBase); + + EXPECT_EQ(true, baseInfo2.hasBase); + EXPECT_EQ(13, baseInfo2.base.long_value); + EXPECT_EQ(false, interval2.hasValue); + EXPECT_EQ(8, interval2.value.long_value); + EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); } @@ -1839,9 +1927,11 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); auto iter = valueProducer->mCurrentSlicedBucket.begin(); auto& interval1 = iter->second[0]; + auto iterBase = valueProducer->mCurrentBaseInfo.begin(); + auto& baseInfo1 = iterBase->second[0]; EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(3, interval1.base.long_value); + EXPECT_EQ(true, baseInfo1.hasBase); + EXPECT_EQ(3, baseInfo1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); vector> allData; @@ -1860,8 +1950,8 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(11, interval1.base.long_value); + EXPECT_EQ(true, baseInfo1.hasBase); + EXPECT_EQ(11, baseInfo1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); EXPECT_FALSE(interval1.seenNewData); @@ -1873,11 +1963,19 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { break; } } + auto itBase = valueProducer->mCurrentBaseInfo.begin(); + for (; itBase != valueProducer->mCurrentBaseInfo.end(); it++) { + if (itBase != iterBase) { + break; + } + } EXPECT_TRUE(it != iter); + EXPECT_TRUE(itBase != iterBase); auto& interval2 = it->second[0]; + auto& baseInfo2 = itBase->second[0]; EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(4, interval2.base.long_value); + EXPECT_EQ(true, baseInfo2.hasBase); + EXPECT_EQ(4, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); @@ -1890,12 +1988,13 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { event1->init(); allData.push_back(event1); valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - // Only one interval left. One was trimmed. + // Only one interval left. One was trimmed. EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); - EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(5, interval2.base.long_value); + EXPECT_EQ(true, baseInfo2.hasBase); + EXPECT_EQ(5, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}); @@ -1909,8 +2008,9 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(14, interval2.base.long_value); + baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, baseInfo2.hasBase); + EXPECT_EQ(14, baseInfo2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); @@ -1946,14 +2046,15 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfB EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(100, curInterval.base.long_value); + ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); vector> allData; valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); - EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer->mHasGlobalBase); } @@ -1983,8 +2084,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(100, curInterval.base.long_value); + ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); @@ -1993,7 +2095,7 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { // has one slice EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, curInterval.hasBase); + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, valueProducer->mHasGlobalBase); } @@ -2035,7 +2137,8 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer->mHasGlobalBase); } @@ -2118,8 +2221,9 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(100, curInterval.base.long_value); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(100, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); } @@ -2181,8 +2285,9 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(140, curInterval.base.long_value); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(140, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); @@ -2222,7 +2327,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) { // First onConditionChanged .WillOnce(Invoke([](int tagId, vector>* data) { for (int i = 0; i < 2000; i++) { - shared_ptr event = make_shared(tagId, bucketStartTimeNs + 1); + shared_ptr event = + make_shared(tagId, bucketStartTimeNs + 1); event->write(i); event->write(i); event->init(); @@ -2338,8 +2444,9 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(140, curInterval.base.long_value); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(140, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); @@ -2429,7 +2536,8 @@ TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer->mHasGlobalBase); @@ -2523,7 +2631,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); @@ -2531,7 +2640,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, valueProducer->mHasGlobalBase); } @@ -2579,7 +2689,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); @@ -2589,9 +2700,10 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; // Data is empty, base should be reset. - EXPECT_EQ(false, curInterval.hasBase); - EXPECT_EQ(5, curInterval.base.long_value); + EXPECT_EQ(false, curBaseInfo.hasBase); + EXPECT_EQ(5, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); @@ -2638,12 +2750,14 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { // Key 1 should be reset since in not present in the most pull. EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); auto iterator = valueProducer->mCurrentSlicedBucket.begin(); - EXPECT_EQ(true, iterator->second[0].hasBase); - EXPECT_EQ(2, iterator->second[0].base.long_value); + auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin(); + EXPECT_EQ(true, baseInfoIter->second[0].hasBase); + EXPECT_EQ(2, baseInfoIter->second[0].base.long_value); EXPECT_EQ(false, iterator->second[0].hasValue); iterator++; - EXPECT_EQ(false, iterator->second[0].hasBase); - EXPECT_EQ(1, iterator->second[0].base.long_value); + baseInfoIter++; + EXPECT_EQ(false, baseInfoIter->second[0].hasBase); + EXPECT_EQ(1, baseInfoIter->second[0].base.long_value); EXPECT_EQ(false, iterator->second[0].hasValue); EXPECT_EQ(true, valueProducer->mHasGlobalBase); @@ -2715,8 +2829,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(true, curInterval.hasBase); - EXPECT_EQ(5, curInterval.base.long_value); + auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(true, curBaseInfo.hasBase); + EXPECT_EQ(5, curBaseInfo.base.long_value); EXPECT_EQ(false, curInterval.hasValue); valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10); @@ -2827,6 +2942,7 @@ TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(2, curInterval.value.long_value); @@ -2918,8 +3034,7 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { ProtoOutputStream output; std::set strSet; - valueProducer.onDumpReport(bucketStartTimeNs + 10, - true /* include recent buckets */, true, + valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true, FAST, &strSet, &output); StatsLogReport report = outputStreamToProto(&output); @@ -2970,9 +3085,8 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { ProtoOutputStream output; std::set strSet; - valueProducer.onDumpReport(bucket4StartTimeNs, - false /* include recent buckets */, true, - FAST, &strSet, &output); + valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST, + &strSet, &output); StatsLogReport report = outputStreamToProto(&output); // Previous bucket is part of the report. @@ -3023,8 +3137,7 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { ProtoOutputStream output; std::set strSet; - valueProducer.onDumpReport(bucketStartTimeNs + 10, - true /* include recent buckets */, true, + valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, &strSet, &output); StatsLogReport report = outputStreamToProto(&output); @@ -3058,15 +3171,15 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges // condition becomes true .WillOnce(Invoke([](int tagId, vector>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent( - bucketStartTimeNs + 30, 10)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10)); return true; })) // condition becomes false .WillOnce(Invoke([](int tagId, vector>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent( - bucketStartTimeNs + 50, 20)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 50, 20)); return true; })); sp valueProducer = @@ -3079,11 +3192,11 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(true, curInterval.hasValue); EXPECT_EQ(20, curInterval.value.long_value); - // Now the alarm is delivered. Condition is off though. vector> allData; allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110)); @@ -3091,7 +3204,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}); curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); + curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); } @@ -3104,8 +3218,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { // condition becomes true .WillOnce(Invoke([](int tagId, vector>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent( - bucketStartTimeNs + 30, 10)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10)); return true; })); sp valueProducer = @@ -3122,7 +3236,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) { assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}); ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); + ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0]; + EXPECT_EQ(false, curBaseInfo.hasBase); EXPECT_EQ(false, curInterval.hasValue); } @@ -3153,8 +3268,8 @@ TEST(ValueMetricProducerTest, TestPulledData_noDiff_withFailure) { // condition becomes true .WillOnce(Invoke([](int tagId, vector>* data) { data->clear(); - data->push_back(ValueMetricProducerTestHelper::createEvent( - bucketStartTimeNs + 30, 10)); + data->push_back( + ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10)); return true; })) .WillOnce(Return(false)); @@ -3768,6 +3883,725 @@ TEST(ValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) { EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 200), dropEvent.drop_time_millis()); } +/* + * Test metric with a simple sliced state + * - Increasing values + * - Using diff + * - Second field is value field + */ +TEST(ValueMetricProducerTest, TestSlicedState) { + // Set up ValueMetricProducer. + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE"); + sp pullerManager = new StrictMock(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // ValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs); + event->write("field1"); + event->write(3); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to ON. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 5); + event->write("field1"); + event->write(5); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to OFF. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 10); + event->write("field1"); + event->write(9); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to ON. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 15); + event->write("field1"); + event->write(21); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 50); + event->write("field1"); + event->write(30); + event->init(); + data->push_back(event); + return true; + })); + + sp valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {android::util::SCREEN_STATE_CHANGED}, {}); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().clear(); + StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + // Bucket status after metric initialized. + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(3, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, kStateUnknown} + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after screen state change kStateUnknown->ON. + auto screenEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 5); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(5, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, kStateUnknown} + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change ON->OFF. + screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 10); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(9, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, ON} + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + // Value for dimension, state key {{}, kStateUnknown} + it++; + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change OFF->ON. + screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + bucketStartTimeNs + 15); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(21, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, OFF} + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(12, it->second[0].value.long_value); + // Value for dimension, state key {{}, ON} + it++; + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, + it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + // Value for dimension, state key {{}, kStateUnknown} + it++; + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Start dump report and check output. + ProtoOutputStream output; + std::set strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true, + NO_TIME_CONSTRAINTS, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + EXPECT_EQ(3, report.value_metrics().data_size()); + + auto data = report.value_metrics().data(0); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); + + data = report.value_metrics().data(1); + EXPECT_EQ(1, report.value_metrics().data(1).bucket_info_size()); + EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value()); + + data = report.value_metrics().data(2); + EXPECT_EQ(1, report.value_metrics().data(2).bucket_info_size()); + EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value()); +} + +/* + * Test metric with sliced state with map + * - Increasing values + * - Using diff + * - Second field is value field + */ +TEST(ValueMetricProducerTest, TestSlicedStateWithMap) { + // Set up ValueMetricProducer. + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF"); + sp pullerManager = new StrictMock(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // ValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs); + event->write("field1"); + event->write(3); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to ON. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 5); + event->write("field1"); + event->write(5); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to VR. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 10); + event->write("field1"); + event->write(9); + event->init(); + data->push_back(event); + return true; + })) + // Screen state change to OFF. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 15); + event->write("field1"); + event->write(21); + event->init(); + data->push_back(event); + return true; + })) + // Dump report requested. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 50); + event->write("field1"); + event->write(30); + event->init(); + data->push_back(event); + return true; + })); + + const StateMap& stateMap = CreateScreenStateOnOffMap(); + const StateMap_StateGroup screenOnGroup = stateMap.group(0); + const StateMap_StateGroup screenOffGroup = stateMap.group(1); + + unordered_map> stateGroupMap; + for (auto group : stateMap.group()) { + for (auto value : group.value()) { + stateGroupMap[SCREEN_STATE_ATOM_ID][value] = group.group_id(); + } + } + + sp valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {android::util::SCREEN_STATE_CHANGED}, stateGroupMap); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().clear(); + StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID)); + + // Bucket status after metric initialized. + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + auto it = valueProducer->mCurrentSlicedBucket.begin(); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(3, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, {}} + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after screen state change kStateUnknown->ON. + auto screenEvent = CreateScreenStateChangedEvent( + android::view::DisplayStateEnum::DISPLAY_STATE_ON, bucketStartTimeNs + 5); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(5, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, kStateUnknown} + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change ON->VR (also ON). + screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_VR, + bucketStartTimeNs + 10); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(9, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, ON GROUP} + EXPECT_EQ(screenOnGroup.group_id(), + it->first.getStateValuesKey().getValues()[0].mValue.long_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + // Value for dimension, state key {{}, kStateUnknown} + it++; + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Bucket status after screen state change VR->OFF. + screenEvent = CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, + bucketStartTimeNs + 15); + StateManager::getInstance().onLogEvent(*screenEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {} + it = valueProducer->mCurrentSlicedBucket.begin(); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(21, itBase->second[0].base.long_value); + // Value for dimension, state key {{}, ON GROUP} + EXPECT_EQ(screenOnGroup.group_id(), + it->first.getStateValuesKey().getValues()[0].mValue.long_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(16, it->second[0].value.long_value); + // Value for dimension, state key {{}, kStateUnknown} + it++; + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Start dump report and check output. + ProtoOutputStream output; + std::set strSet; + valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true, + NO_TIME_CONSTRAINTS, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + EXPECT_EQ(3, report.value_metrics().data_size()); + + auto data = report.value_metrics().data(0); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); + + data = report.value_metrics().data(1); + EXPECT_EQ(1, report.value_metrics().data(1).bucket_info_size()); + EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id()); + + data = report.value_metrics().data(2); + EXPECT_EQ(1, report.value_metrics().data(2).bucket_info_size()); + EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); + EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_group_id()); + EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id()); +} + +/* + * Test metric that slices by state with a primary field and has dimensions + * - Increasing values + * - Using diff + * - Second field is value field + */ +TEST(ValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) { + // Set up ValueMetricProducer. + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE"); + metric.mutable_dimensions_in_what()->set_field(tagId); + metric.mutable_dimensions_in_what()->add_child()->set_field(1); + + MetricStateLink* stateLink = metric.add_state_link(); + stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID); + auto fieldsInWhat = stateLink->mutable_fields_in_what(); + *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */}); + auto fieldsInState = stateLink->mutable_fields_in_state(); + *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */}); + + sp pullerManager = new StrictMock(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // ValueMetricProducer initialized. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs); + event->write(2 /* uid */); + event->write(7); + event->init(); + data->push_back(event); + + event = make_shared(tagId, bucketStartTimeNs); + event->write(1 /* uid */); + event->write(3); + event->init(); + data->push_back(event); + return true; + })) + // Uid 1 process state change from kStateUnknown -> Foreground + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 20); + event->write(1 /* uid */); + event->write(6); + event->init(); + data->push_back(event); + + // This event should be skipped. + event = make_shared(tagId, bucketStartTimeNs + 20); + event->write(2 /* uid */); + event->write(8); + event->init(); + data->push_back(event); + return true; + })) + // Uid 2 process state change from kStateUnknown -> Background + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucketStartTimeNs + 40); + event->write(2 /* uid */); + event->write(9); + event->init(); + data->push_back(event); + + // This event should be skipped. + event = make_shared(tagId, bucketStartTimeNs + 40); + event->write(1 /* uid */); + event->write(12); + event->init(); + data->push_back(event); + return true; + })) + // Uid 1 process state change from Foreground -> Background + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucket2StartTimeNs + 20); + event->write(1 /* uid */); + event->write(13); + event->init(); + data->push_back(event); + + // This event should be skipped. + event = make_shared(tagId, bucket2StartTimeNs + 20); + event->write(2 /* uid */); + event->write(11); + event->init(); + data->push_back(event); + return true; + })) + // Uid 1 process state change from Background -> Foreground + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucket2StartTimeNs + 40); + event->write(1 /* uid */); + event->write(17); + event->init(); + data->push_back(event); + + // This event should be skipped. + event = make_shared(tagId, bucket2StartTimeNs + 40); + event->write(2 /* uid */); + event->write(15); + event->init(); + data->push_back(event); + return true; + })) + // Dump report pull. + .WillOnce(Invoke([](int tagId, vector>* data) { + data->clear(); + shared_ptr event = make_shared(tagId, bucket2StartTimeNs + 50); + event->write(2 /* uid */); + event->write(20); + event->init(); + data->push_back(event); + + event = make_shared(tagId, bucket2StartTimeNs + 50); + event->write(1 /* uid */); + event->write(21); + event->init(); + data->push_back(event); + return true; + })); + + sp valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithState( + pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}); + + // Set up StateManager and check that StateTrackers are initialized. + StateManager::getInstance().clear(); + StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer); + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID)); + + // Bucket status after metric initialized. + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {uid 1}. + auto it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(3, itBase->second[0].base.long_value); + // Value for dimension, state key {{uid 1}, kStateUnknown} + // TODO(tsaichristine): test equality of state values key + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + // Base for dimension key {uid 2} + it++; + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(7, itBase->second[0].base.long_value); + // Value for dimension, state key {{uid 2}, kStateUnknown} + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after uid 1 process state change kStateUnknown -> Foreground. + auto uidProcessEvent = CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucketStartTimeNs + 20); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {uid 1}. + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(6, itBase->second[0].base.long_value); + // Value for key {uid 1, kStateUnknown}. + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(3, it->second[0].value.long_value); + + // Base for dimension key {uid 2} + it++; + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(7, itBase->second[0].base.long_value); + // Value for key {uid 2, kStateUnknown} + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after uid 2 process state change kStateUnknown -> Background. + uidProcessEvent = CreateUidProcessStateChangedEvent( + 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucketStartTimeNs + 40); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + // Base for dimension key {uid 1}. + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(6, itBase->second[0].base.long_value); + // Value for key {uid 1, kStateUnknown}. + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(3, it->second[0].value.long_value); + + // Base for dimension key {uid 2} + it++; + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(9, itBase->second[0].base.long_value); + // Value for key {uid 2, kStateUnknown} + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(2, it->second[0].value.long_value); + + // Pull at end of first bucket. + vector> allData; + shared_ptr event = make_shared(tagId, bucket2StartTimeNs); + event->write(1 /* uid */); + event->write(10); + event->init(); + allData.push_back(event); + + event = make_shared(tagId, bucket2StartTimeNs); + event->write(2 /* uid */); + event->write(15); + event->init(); + allData.push_back(event); + + valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1); + + // Buckets flushed after end of first bucket. + // None of the buckets should have a value. + EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(4UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); + // Base for dimension key {uid 2}. + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(15, itBase->second[0].base.long_value); + // Value for key {uid 2, BACKGROUND}. + EXPECT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(1006, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + + // Base for dimension key {uid 1} + it++; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(10, itBase->second[0].base.long_value); + // Value for key {uid 1, kStateUnknown} + EXPECT_EQ(0, it->first.getStateValuesKey().getValues().size()); + // EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + + // Value for key {uid 1, FOREGROUND} + it++; + EXPECT_EQ(1, it->first.getStateValuesKey().getValues().size()); + EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(false, it->second[0].hasValue); + + // Value for key {uid 2, kStateUnknown} + it++; + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after uid 1 process state change from Foreground -> Background. + uidProcessEvent = CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND, bucket2StartTimeNs + 20); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + + EXPECT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(4UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); + // Base for dimension key {uid 2}. + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(15, itBase->second[0].base.long_value); + // Value for key {uid 2, BACKGROUND}. + EXPECT_EQ(false, it->second[0].hasValue); + // Base for dimension key {uid 1} + it++; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(13, itBase->second[0].base.long_value); + // Value for key {uid 1, kStateUnknown} + EXPECT_EQ(false, it->second[0].hasValue); + // Value for key {uid 1, FOREGROUND} + it++; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(3, it->second[0].value.long_value); + // Value for key {uid 2, kStateUnknown} + it++; + EXPECT_EQ(false, it->second[0].hasValue); + + // Bucket status after uid 1 process state change Background->Foreground. + uidProcessEvent = CreateUidProcessStateChangedEvent( + 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND, bucket2StartTimeNs + 40); + StateManager::getInstance().onLogEvent(*uidProcessEvent); + + EXPECT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(2UL, valueProducer->mCurrentBaseInfo.size()); + // Base for dimension key {uid 2} + it = valueProducer->mCurrentSlicedBucket.begin(); + EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(true, itBase->second[0].hasBase); + EXPECT_EQ(15, itBase->second[0].base.long_value); + EXPECT_EQ(false, it->second[0].hasValue); + + it++; + EXPECT_EQ(false, it->second[0].hasValue); + + // Base for dimension key {uid 1} + it++; + EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); + itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat()); + EXPECT_EQ(17, itBase->second[0].base.long_value); + // Value for key {uid 1, BACKGROUND} + EXPECT_EQ(1006, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(4, it->second[0].value.long_value); + // Value for key {uid 1, FOREGROUND} + it++; + EXPECT_EQ(1005, it->first.getStateValuesKey().getValues()[0].mValue.int_value); + EXPECT_EQ(true, it->second[0].hasValue); + EXPECT_EQ(3, it->second[0].value.long_value); + + // Start dump report and check output. + ProtoOutputStream output; + std::set strSet; + valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true, + NO_TIME_CONSTRAINTS, &strSet, &output); + + StatsLogReport report = outputStreamToProto(&output); + EXPECT_TRUE(report.has_value_metrics()); + EXPECT_EQ(5, report.value_metrics().data_size()); + + auto data = report.value_metrics().data(0); + EXPECT_EQ(1, data.bucket_info_size()); + EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long()); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, + data.slice_by_state(0).value()); + + data = report.value_metrics().data(1); + EXPECT_EQ(1, report.value_metrics().data(1).bucket_info_size()); + EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long()); + + data = report.value_metrics().data(2); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND, + data.slice_by_state(0).value()); + EXPECT_EQ(2, report.value_metrics().data(2).bucket_info_size()); + EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long()); + EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long()); + + data = report.value_metrics().data(3); + EXPECT_EQ(1, report.value_metrics().data(3).bucket_info_size()); + EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long()); + + data = report.value_metrics().data(4); + EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id()); + EXPECT_TRUE(data.slice_by_state(0).has_value()); + EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND, + data.slice_by_state(0).value()); + EXPECT_EQ(2, report.value_metrics().data(4).bucket_info_size()); + EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long()); + EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long()); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp index 395167b998d02..26a3733ed5984 100644 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -146,6 +146,7 @@ TEST(StateListenerTest, TestStateListenerWeakPointer) { TEST(StateManagerTest, TestStateManagerGetInstance) { sp listener1 = new TestStateListener(); StateManager& mgr = StateManager::getInstance(); + mgr.clear(); mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); EXPECT_EQ(1, mgr.getStateTrackersCount()); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 010c194aa915a..9bdfeebe561f9 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -30,6 +30,9 @@ namespace statsd { using android::util::ProtoReader; using google::protobuf::RepeatedPtrField; +const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED; +const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED; + // Converts a ProtoOutputStream to a StatsLogReport proto. StatsLogReport outputStreamToProto(ProtoOutputStream* proto);