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);