diff options
10 files changed, 157 insertions, 151 deletions
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 66e8c6103ae8..0fac11836caa 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -111,16 +111,16 @@ void DurationMetricProducer::startNewProtoOutputStreamLocked(long long startTime } unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker( - const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) const { + const HashableDimensionKey& eventKey) const { switch (mMetric.aggregation_type()) { case DurationMetric_AggregationType_SUM: return make_unique<OringDurationTracker>( mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket); + mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); case DurationMetric_AggregationType_MAX_SPARSE: return make_unique<MaxDurationTracker>( mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested, - mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket); + mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers); } } @@ -168,13 +168,6 @@ std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReportLocked continue; } - // If there is no duration bucket info for this key, don't include it in the report. - // For example, duration started, but condition is never turned to true. - // TODO: Only add the key to the map when we add duration buckets info for it. - if (pair.second.size() == 0) { - continue; - } - long long wrapperToken = mProto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA); @@ -217,7 +210,7 @@ std::unique_ptr<std::vector<uint8_t>> DurationMetricProducer::onDumpReportLocked (long long)mCurrentBucketStartTimeNs); std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(); startNewProtoOutputStreamLocked(endTime); - // TODO: Properly clear the old buckets. + mPastBuckets.clear(); return buffer; } @@ -227,7 +220,7 @@ void DurationMetricProducer::flushIfNeededLocked(const uint64_t& eventTime) { } VLOG("flushing..........."); for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) { - if (it->second->flushIfNeeded(eventTime)) { + if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) { VLOG("erase bucket for key %s", it->first.c_str()); it = mCurrentSlicedDuration.erase(it); } else { @@ -279,7 +272,7 @@ void DurationMetricProducer::onMatchedLogEventInternalLocked( if (hitGuardRailLocked(eventKey)) { return; } - mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]); + mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey); } auto it = mCurrentSlicedDuration.find(eventKey); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 8e32d148e221..8ba241ea5497 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -104,7 +104,7 @@ private: // Helper function to create a duration tracker given the metric aggregation type. std::unique_ptr<DurationTracker> createDurationTracker( - const HashableDimensionKey& eventKey, std::vector<DurationBucket>& bucket) const; + const HashableDimensionKey& eventKey) const; // Util function to check whether the specified dimension hits the guardrail. bool hitGuardRailLocked(const HashableDimensionKey& newKey); diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 834f7f5b0b86..3c714b3923e8 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -63,8 +63,7 @@ public: DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket) + const std::vector<sp<AnomalyTracker>>& anomalyTrackers) : mConfigKey(key), mName(name), mEventKey(eventKey), @@ -73,7 +72,6 @@ public: mBucketSizeNs(bucketSizeNs), mNested(nesting), mCurrentBucketStartTimeNs(currentBucketStartNs), - mBucket(bucket), mDuration(0), mCurrentBucketNum(0), mAnomalyTrackers(anomalyTrackers){}; @@ -91,7 +89,9 @@ public: // Flush stale buckets if needed, and return true if the tracker has no on-going duration // events, so that the owner can safely remove the tracker. - virtual bool flushIfNeeded(uint64_t timestampNs) = 0; + virtual bool flushIfNeeded( + uint64_t timestampNs, + std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0; // Predict the anomaly timestamp given the current status. virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, @@ -159,8 +159,6 @@ protected: uint64_t mCurrentBucketStartTimeNs; - std::vector<DurationBucket>& mBucket; // where to write output - int64_t mDuration; // current recorded duration result uint64_t mCurrentBucketNum; diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp index 4b346dd25050..08c91351c49e 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp @@ -28,10 +28,9 @@ MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket) + const std::vector<sp<AnomalyTracker>>& anomalyTrackers) : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - bucketSizeNs, anomalyTrackers, bucket) { + bucketSizeNs, anomalyTrackers) { } bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) { @@ -145,7 +144,8 @@ void MaxDurationTracker::noteStopAll(const uint64_t eventTime) { } } -bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) { +bool MaxDurationTracker::flushIfNeeded( + uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) { if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) { return false; } @@ -202,7 +202,7 @@ bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) { if (mDuration != 0) { info.mDuration = mDuration; - mBucket.push_back(info); + (*output)[mEventKey].push_back(info); addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); VLOG(" final duration for last bucket: %lld", (long long)mDuration); } @@ -215,7 +215,7 @@ bool MaxDurationTracker::flushIfNeeded(uint64_t eventTime) { info.mBucketEndNs = endTime + mBucketSizeNs * i; info.mBucketNum = mCurrentBucketNum + i; info.mDuration = mBucketSizeNs; - mBucket.push_back(info); + (*output)[mEventKey].push_back(info); addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); VLOG(" filling gap bucket with duration %lld", (long long)mBucketSizeNs); } diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h index e0d1466c6fc4..10eddb8bee6e 100644 --- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h @@ -32,15 +32,16 @@ public: const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket); + const std::vector<sp<AnomalyTracker>>& anomalyTrackers); void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; void noteStop(const HashableDimensionKey& key, const uint64_t eventTime, const bool stopAll) override; void noteStopAll(const uint64_t eventTime) override; - bool flushIfNeeded(uint64_t timestampNs) override; + bool flushIfNeeded( + uint64_t timestampNs, + std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override; void onSlicedConditionMayChange(const uint64_t timestamp) override; void onConditionChanged(bool condition, const uint64_t timestamp) override; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp index abdfbc06d7a7..8122744b32b5 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp @@ -29,10 +29,9 @@ OringDurationTracker::OringDurationTracker(const ConfigKey& key, const string& n sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket) + const std::vector<sp<AnomalyTracker>>& anomalyTrackers) : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, - bucketSizeNs, anomalyTrackers, bucket), + bucketSizeNs, anomalyTrackers), mStarted(), mPaused() { mLastStartTime = 0; @@ -128,7 +127,8 @@ void OringDurationTracker::noteStopAll(const uint64_t timestamp) { mConditionKeyMap.clear(); } -bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { +bool OringDurationTracker::flushIfNeeded( + uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) { if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) { return false; } @@ -145,7 +145,7 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { } if (mDuration > 0) { current_info.mDuration = mDuration; - mBucket.push_back(current_info); + (*output)[mEventKey].push_back(current_info); addPastBucketToAnomalyTrackers(current_info.mDuration, current_info.mBucketNum); VLOG(" duration: %lld", (long long)current_info.mDuration); } @@ -157,9 +157,9 @@ bool OringDurationTracker::flushIfNeeded(uint64_t eventTime) { info.mBucketEndNs = info.mBucketStartNs + mBucketSizeNs; info.mBucketNum = mCurrentBucketNum + i; info.mDuration = mBucketSizeNs; - mBucket.push_back(info); - addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); - VLOG(" add filling bucket with duration %lld", (long long)info.mDuration); + (*output)[mEventKey].push_back(info); + addPastBucketToAnomalyTrackers(info.mDuration, info.mBucketNum); + VLOG(" add filling bucket with duration %lld", (long long)info.mDuration); } } mCurrentBucketStartTimeNs += numBucketsForward * mBucketSizeNs; diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h index a8404a961dd2..b7d3cba7055e 100644 --- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h @@ -31,8 +31,7 @@ public: const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs, uint64_t bucketSizeNs, - const std::vector<sp<AnomalyTracker>>& anomalyTrackers, - std::vector<DurationBucket>& bucket); + const std::vector<sp<AnomalyTracker>>& anomalyTrackers); void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime, const ConditionKey& conditionKey) override; @@ -43,7 +42,9 @@ public: void onSlicedConditionMayChange(const uint64_t timestamp) override; void onConditionChanged(bool condition, const uint64_t timestamp) override; - bool flushIfNeeded(uint64_t timestampNs) override; + bool flushIfNeeded( + uint64_t timestampNs, + std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override; int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker, const uint64_t currentTimestamp) const override; diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp index 3f2b7cdd22ee..3158c2757263 100644 --- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp @@ -89,7 +89,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); DurationMetricProducer durationProducer( - kConfigKey, metric, 0 /*no condition*/, 1 /* start index */, 2 /* stop index */, + kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs); EXPECT_FALSE(durationProducer.mCondition); EXPECT_FALSE(durationProducer.isConditionSliced()); @@ -97,12 +97,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { durationProducer.onMatchedLogEvent(1 /* start index*/, event1, false /* scheduledPull */); durationProducer.onMatchedLogEvent(2 /* stop index*/, event2, false /* scheduledPull */); durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); - EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) != - durationProducer.mPastBuckets.end()); - const auto& buckets1 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY]; - EXPECT_EQ(0UL, buckets1.size()); - + EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); durationProducer.onMatchedLogEvent(1 /* start index*/, event3, false /* scheduledPull */); durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp index 5d47437c486c..1adcc1146c42 100644 --- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp @@ -37,18 +37,19 @@ namespace os { namespace statsd { const ConfigKey kConfigKey(0, "test"); +const string eventKey = "event"; TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey key1; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("1", true, bucketStartTimeNs, key1); // Event starts again. This would not change anything as it already starts. @@ -60,50 +61,53 @@ TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) { tracker.noteStart("2", true, bucketStartTimeNs + 20, key1); tracker.noteStop("2", bucketStartTimeNs + 40, false /*stop all*/); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(20ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(20ULL, buckets[eventKey][0].mDuration); } TEST(MaxDurationTrackerTest, TestStopAll) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey key1; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("1", true, bucketStartTimeNs + 1, key1); // Another event starts in this bucket. tracker.noteStart("2", true, bucketStartTimeNs + 20, key1); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets); tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40); EXPECT_TRUE(tracker.mInfos.empty()); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(40ULL, buckets[1].mDuration); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + + tracker.flushIfNeeded(bucketStartTimeNs + 3 * bucketSizeNs + 40, &buckets); + EXPECT_EQ(2u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(40ULL, buckets[eventKey][1].mDuration); } TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey key1; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs, + bucketSizeNs, {}); // The event starts. tracker.noteStart("", true, bucketStartTimeNs + 1, key1); @@ -112,25 +116,26 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) { tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1); // The event stops at early 4th bucket. - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20); + tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets); tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/); - EXPECT_EQ(3u, buckets.size()); - EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[0].mDuration); - EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[1].mDuration); - EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[2].mDuration); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(3u, buckets[eventKey].size()); + EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[eventKey][0].mDuration); + EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][1].mDuration); + EXPECT_EQ((unsigned long long)bucketSizeNs, buckets[eventKey][2].mDuration); } TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; ConditionKey key1; uint64_t bucketStartTimeNs = 10000000000; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs, + bucketSizeNs, {}); // 2 starts tracker.noteStart("", true, bucketStartTimeNs + 1, key1); @@ -138,20 +143,21 @@ TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) { // one stop tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/); - tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1); + tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(2u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); // real stop now. tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false); - tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1); + tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets); - EXPECT_EQ(3u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); - EXPECT_EQ(5ULL, buckets[2].mDuration); + EXPECT_EQ(3u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); + EXPECT_EQ(5ULL, buckets[eventKey][2].mDuration); } TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { @@ -163,15 +169,15 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { EXPECT_CALL(*wizard, query(_, key1)) // #4 .WillOnce(Return(ConditionState::kFalse)); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; int64_t durationTimeNs = 2 * 1000; - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, bucketStartTimeNs, + bucketSizeNs, {}); EXPECT_TRUE(tracker.mAnomalyTrackers.empty()); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); @@ -180,9 +186,10 @@ TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) { tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(5ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration); } TEST(MaxDurationTrackerTest, TestAnomalyDetection) { @@ -193,7 +200,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { alert.set_number_of_buckets(2); alert.set_refractory_period_secs(1); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; @@ -202,8 +209,8 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); - MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs, - bucketSizeNs, {anomalyTracker}, buckets); + MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs, + bucketSizeNs, {anomalyTracker}); tracker.noteStart("1", true, eventStartTimeNs, key1); tracker.noteStop("1", eventStartTimeNs + 10, false); @@ -211,7 +218,7 @@ TEST(MaxDurationTrackerTest, TestAnomalyDetection) { EXPECT_EQ(10LL, tracker.mDuration); tracker.noteStart("2", true, eventStartTimeNs + 20, key1); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets); tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false); EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration); EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp index 6913c8161345..fa7b9a734524 100644 --- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp +++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp @@ -35,6 +35,7 @@ namespace os { namespace statsd { const ConfigKey kConfigKey(0, "test"); +const string eventKey = "event"; TEST(OringDurationTrackerTest, TestDurationOverlap) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); @@ -42,15 +43,15 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); @@ -58,9 +59,11 @@ TEST(OringDurationTrackerTest, TestDurationOverlap) { EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(durationTimeNs, buckets[0].mDuration); + tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(durationTimeNs, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestDurationNested) { @@ -69,14 +72,14 @@ TEST(OringDurationTrackerTest, TestDurationNested) { ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1); // overlapping wl @@ -84,9 +87,10 @@ TEST(OringDurationTrackerTest, TestDurationNested) { tracker.noteStop("2:maps", eventStartTimeNs + 2000, false); tracker.noteStop("2:maps", eventStartTimeNs + 2003, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(2003ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestStopAll) { @@ -95,23 +99,24 @@ TEST(OringDurationTrackerTest, TestStopAll) { ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1); // overlapping wl tracker.noteStopAll(eventStartTimeNs + 2003); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(2003ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(2003ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { @@ -120,32 +125,33 @@ TEST(OringDurationTrackerTest, TestCrossBucketBoundary) { ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets); tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1); EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); + EXPECT_EQ(2u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 10, false); tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 12, false); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12); - EXPECT_EQ(2u, buckets.size()); - EXPECT_EQ(bucketSizeNs - 1, buckets[0].mDuration); - EXPECT_EQ(bucketSizeNs, buckets[1].mDuration); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(2u, buckets[eventKey].size()); + EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration); + EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration); } TEST(OringDurationTrackerTest, TestDurationConditionChange) { @@ -157,15 +163,15 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { EXPECT_CALL(*wizard, query(_, key1)) // #4 .WillOnce(Return(ConditionState::kFalse)); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); @@ -173,9 +179,10 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange) { tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(5ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(5ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestDurationConditionChange2) { @@ -189,15 +196,15 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { .WillOnce(Return(ConditionState::kFalse)) .WillOnce(Return(ConditionState::kTrue)); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; uint64_t durationTimeNs = 2 * 1000; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false, + bucketStartTimeNs, bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); // condition to false; record duration 5n @@ -207,9 +214,10 @@ TEST(OringDurationTrackerTest, TestDurationConditionChange2) { // 2nd duration: 1000ns tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(1005ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(1005ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { @@ -221,14 +229,14 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { EXPECT_CALL(*wizard, query(_, key1)) // #4 .WillOnce(Return(ConditionState::kFalse)); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; uint64_t bucketStartTimeNs = 10000000000; uint64_t eventStartTimeNs = bucketStartTimeNs + 1; uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL; - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {}); tracker.noteStart("2:maps", true, eventStartTimeNs, key1); tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1); @@ -239,9 +247,10 @@ TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) { tracker.noteStop("2:maps", eventStartTimeNs + 2003, false); - tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1); - EXPECT_EQ(1u, buckets.size()); - EXPECT_EQ(15ULL, buckets[0].mDuration); + tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets); + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); + EXPECT_EQ(15ULL, buckets[eventKey][0].mDuration); } TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { @@ -252,7 +261,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { alert.set_number_of_buckets(2); alert.set_refractory_period_secs(1); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; @@ -261,8 +270,8 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs, - bucketSizeNs, {anomalyTracker}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs, + bucketSizeNs, {anomalyTracker}); // Nothing in the past bucket. tracker.noteStart("", true, eventStartTimeNs, key1); @@ -270,7 +279,7 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs)); tracker.noteStop("", eventStartTimeNs + 3, false); - EXPECT_EQ(0u, buckets.size()); + EXPECT_EQ(0u, buckets[eventKey].size()); uint64_t event1StartTimeNs = eventStartTimeNs + 10; tracker.noteStart("1", true, event1StartTimeNs, key1); @@ -279,11 +288,13 @@ TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) { tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs)); uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10; - tracker.flushIfNeeded(event1StopTimeNs); + tracker.flushIfNeeded(event1StopTimeNs, &buckets); tracker.noteStop("1", event1StopTimeNs, false); - EXPECT_EQ(1u, buckets.size()); + + EXPECT_TRUE(buckets.find(eventKey) != buckets.end()); + EXPECT_EQ(1u, buckets[eventKey].size()); EXPECT_EQ(3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10, - buckets[0].mDuration); + buckets[eventKey][0].mDuration); const int64_t bucket0Duration = 3ULL + bucketStartTimeNs + bucketSizeNs - eventStartTimeNs - 10; const int64_t bucket1Duration = eventStartTimeNs + 10 - bucketStartTimeNs; @@ -312,7 +323,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { alert.set_number_of_buckets(2); alert.set_refractory_period_secs(1); - vector<DurationBucket> buckets; + unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets; sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); ConditionKey key1; key1["APP_BACKGROUND"] = "1:maps|"; @@ -321,8 +332,8 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { uint64_t bucketSizeNs = 30 * NS_PER_SEC; sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert); - OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true /*nesting*/, - bucketStartTimeNs, bucketSizeNs, {anomalyTracker}, buckets); + OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/, + bucketStartTimeNs, bucketSizeNs, {anomalyTracker}); tracker.noteStart("", true, eventStartTimeNs, key1); tracker.noteStop("", eventStartTimeNs + 10, false); @@ -336,7 +347,7 @@ TEST(OringDurationTrackerTest, TestAnomalyDetection) { EXPECT_EQ(1u, anomalyTracker->mAlarms.size()); EXPECT_EQ((long long)(51ULL * NS_PER_SEC), (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC)); - tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25); + tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets); tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false); EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs)); EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25), |