Loading cmds/statsd/src/anomaly/AnomalyTracker.cpp +18 −39 Original line number Diff line number Diff line Loading @@ -162,45 +162,23 @@ int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) c return 0; } bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const DimToValMap& currentBucket) { if (currentBucketNum > mMostRecentBucketNum + 1) { addPastBucket(nullptr, currentBucketNum - 1); } for (auto itr = currentBucket.begin(); itr != currentBucket.end(); itr++) { if (itr->second + getSumOverPastBuckets(itr->first) > mAlert.trigger_if_sum_gt()) { return true; } } // In theory, we also need to check the dimsions not in the current bucket. In single-thread // mode, usually we could avoid the following loops. for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) { if (itr->second > mAlert.trigger_if_sum_gt()) { return true; } } return false; } bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key, const int64_t& currentBucketValue) { if (currentBucketNum > mMostRecentBucketNum + 1) { // TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this. addPastBucket(key, 0, currentBucketNum - 1); } return mAlert.has_trigger_if_sum_gt() && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); } void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { // TODO: This should also take in the const HashableDimensionKey& key, to pass // more details to incidentd and to make mRefractoryPeriodEndsSec key-specific. void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) { // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now. if (isInRefractoryPeriod(timestampNs)) { if (isInRefractoryPeriod(timestampNs, key)) { VLOG("Skipping anomaly declaration since within refractory period"); return; } // TODO(guardrail): Consider guarding against too short refractory periods. mLastAnomalyTimestampNs = timestampNs; mRefractoryPeriodEndsSec[key] = (timestampNs / NS_PER_SEC) + mAlert.refractory_period_secs(); // TODO: If we had access to the bucket_size_millis, consider calling resetStorage() // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) { resetStorage(); } Loading @@ -208,7 +186,7 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { if (!mSubscriptions.empty()) { if (mAlert.has_id()) { ALOGI("An anomaly (%llu) has occurred! Informing subscribers.",mAlert.id()); informSubscribers(); informSubscribers(key); } else { ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers."); } Loading @@ -218,6 +196,7 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id()); // TODO: This should also take in the const HashableDimensionKey& key? android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(), mConfigKey.GetId(), mAlert.id()); } Loading @@ -227,24 +206,24 @@ void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key, const int64_t& currentBucketValue) { if (detectAnomaly(currBucketNum, key, currentBucketValue)) { declareAnomaly(timestampNs); declareAnomaly(timestampNs, key); } } void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const DimToValMap& currentBucket) { if (detectAnomaly(currBucketNum, currentBucket)) { declareAnomaly(timestampNs); bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key) { const auto& it = mRefractoryPeriodEndsSec.find(key); if (it != mRefractoryPeriodEndsSec.end()) { if ((timestampNs / NS_PER_SEC) <= it->second) { return true; } else { mRefractoryPeriodEndsSec.erase(key); } } bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs) const { return mLastAnomalyTimestampNs >= 0 && timestampNs - mLastAnomalyTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC; return false; } void AnomalyTracker::informSubscribers() { void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) { VLOG("informSubscribers called."); if (mSubscriptions.empty()) { ALOGE("Attempt to call with no subscribers."); Loading cmds/statsd/src/anomaly/AnomalyTracker.h +14 −12 Original line number Diff line number Diff line Loading @@ -52,16 +52,13 @@ public: const int64_t& bucketNum); // Returns true if detected anomaly for the existing buckets on one or more dimension keys. bool detectAnomaly(const int64_t& currBucketNum, const DimToValMap& currentBucket); bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key, const int64_t& currentBucketValue); // Informs incidentd about the detected alert. void declareAnomaly(const uint64_t& timestampNs); void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key); // Detects the alert and informs the incidentd when applicable. void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const DimToValMap& currentBucket); void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const HashableDimensionKey& key, const int64_t& currentBucketValue); Loading @@ -82,9 +79,11 @@ public: return mAlert.trigger_if_sum_gt(); } // Helper function to return the timestamp of the last detected anomaly. inline int64_t getLastAnomalyTimestampNs() const { return mLastAnomalyTimestampNs; // Returns the refractory period timestamp (in seconds) for the given key. // If there is no stored refractory period ending timestamp, returns 0. uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const { const auto& it = mRefractoryPeriodEndsSec.find(key); return it != mRefractoryPeriodEndsSec.end() ? it->second : 0; } inline int getNumOfPastBuckets() const { Loading Loading @@ -121,8 +120,11 @@ protected: // The bucket number of the last added bucket. int64_t mMostRecentBucketNum = -1; // The timestamp when the last anomaly was declared. int64_t mLastAnomalyTimestampNs = -1; // Map from each dimension to the timestamp that its refractory period (if this anomaly was // declared for that dimension) ends, in seconds. Only anomalies that occur after this period // ends will be declared. // Entries may be, but are not guaranteed to be, removed after the period is finished. unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec; void flushPastBuckets(const int64_t& currBucketNum); Loading @@ -133,7 +135,7 @@ protected: // and remove any items with value 0. void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket); bool isInRefractoryPeriod(const uint64_t& timestampNs) const; bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key); // Calculates the corresponding bucket index within the circular array. size_t index(int64_t bucketNum) const; Loading @@ -142,12 +144,12 @@ protected: virtual void resetStorage(); // Informs the subscribers that an anomaly has occurred. void informSubscribers(); void informSubscribers(const HashableDimensionKey& key); FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); }; } // namespace statsd Loading cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +3 −3 Original line number Diff line number Diff line Loading @@ -46,7 +46,7 @@ void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensio if (itr->second != nullptr && static_cast<uint32_t>(timestampNs / NS_PER_SEC) >= itr->second->timestampSec) { declareAnomaly(timestampNs); declareAnomaly(timestampNs, dimensionKey); stopAlarm(dimensionKey); } } Loading @@ -55,7 +55,7 @@ void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey const uint64_t& timestampNs) { uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC); if (isInRefractoryPeriod(timestampNs)) { if (isInRefractoryPeriod(timestampNs, dimensionKey)) { VLOG("Skipping setting anomaly alarm since it'd fall in the refractory period"); return; } Loading Loading @@ -104,7 +104,7 @@ void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs, // Now declare each of these alarms to have fired. for (const auto& kv : matchedAlarms) { declareAnomaly(timestampNs /* TODO: , kv.first */); declareAnomaly(timestampNs, kv.first); mAlarms.erase(kv.first); firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. } Loading cmds/statsd/src/anomaly/DurationAnomalyTracker.h +3 −3 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ public: const uint64_t& timestampNs); // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker // and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor. // and removes it from firedAlarms. // TODO: This will actually be called from a different thread, so make it thread-safe! // This means that almost every function in DurationAnomalyTracker needs to be locked. // But this should be done at the level of StatsLogProcessor, which needs to lock Loading @@ -70,10 +70,10 @@ protected: void resetStorage() override; FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection); }; } // namespace statsd Loading cmds/statsd/src/metrics/CountMetricProducer.h +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ private: FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents); FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); }; } // namespace statsd Loading Loading
cmds/statsd/src/anomaly/AnomalyTracker.cpp +18 −39 Original line number Diff line number Diff line Loading @@ -162,45 +162,23 @@ int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) c return 0; } bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const DimToValMap& currentBucket) { if (currentBucketNum > mMostRecentBucketNum + 1) { addPastBucket(nullptr, currentBucketNum - 1); } for (auto itr = currentBucket.begin(); itr != currentBucket.end(); itr++) { if (itr->second + getSumOverPastBuckets(itr->first) > mAlert.trigger_if_sum_gt()) { return true; } } // In theory, we also need to check the dimsions not in the current bucket. In single-thread // mode, usually we could avoid the following loops. for (auto itr = mSumOverPastBuckets.begin(); itr != mSumOverPastBuckets.end(); itr++) { if (itr->second > mAlert.trigger_if_sum_gt()) { return true; } } return false; } bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key, const int64_t& currentBucketValue) { if (currentBucketNum > mMostRecentBucketNum + 1) { // TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this. addPastBucket(key, 0, currentBucketNum - 1); } return mAlert.has_trigger_if_sum_gt() && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt(); } void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { // TODO: This should also take in the const HashableDimensionKey& key, to pass // more details to incidentd and to make mRefractoryPeriodEndsSec key-specific. void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) { // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now. if (isInRefractoryPeriod(timestampNs)) { if (isInRefractoryPeriod(timestampNs, key)) { VLOG("Skipping anomaly declaration since within refractory period"); return; } // TODO(guardrail): Consider guarding against too short refractory periods. mLastAnomalyTimestampNs = timestampNs; mRefractoryPeriodEndsSec[key] = (timestampNs / NS_PER_SEC) + mAlert.refractory_period_secs(); // TODO: If we had access to the bucket_size_millis, consider calling resetStorage() // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) { resetStorage(); } Loading @@ -208,7 +186,7 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { if (!mSubscriptions.empty()) { if (mAlert.has_id()) { ALOGI("An anomaly (%llu) has occurred! Informing subscribers.",mAlert.id()); informSubscribers(); informSubscribers(key); } else { ALOGI("An anomaly (with no id) has occurred! Not informing any subscribers."); } Loading @@ -218,6 +196,7 @@ void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs) { StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id()); // TODO: This should also take in the const HashableDimensionKey& key? android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(), mConfigKey.GetId(), mAlert.id()); } Loading @@ -227,24 +206,24 @@ void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key, const int64_t& currentBucketValue) { if (detectAnomaly(currBucketNum, key, currentBucketValue)) { declareAnomaly(timestampNs); declareAnomaly(timestampNs, key); } } void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const DimToValMap& currentBucket) { if (detectAnomaly(currBucketNum, currentBucket)) { declareAnomaly(timestampNs); bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key) { const auto& it = mRefractoryPeriodEndsSec.find(key); if (it != mRefractoryPeriodEndsSec.end()) { if ((timestampNs / NS_PER_SEC) <= it->second) { return true; } else { mRefractoryPeriodEndsSec.erase(key); } } bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs) const { return mLastAnomalyTimestampNs >= 0 && timestampNs - mLastAnomalyTimestampNs <= mAlert.refractory_period_secs() * NS_PER_SEC; return false; } void AnomalyTracker::informSubscribers() { void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) { VLOG("informSubscribers called."); if (mSubscriptions.empty()) { ALOGE("Attempt to call with no subscribers."); Loading
cmds/statsd/src/anomaly/AnomalyTracker.h +14 −12 Original line number Diff line number Diff line Loading @@ -52,16 +52,13 @@ public: const int64_t& bucketNum); // Returns true if detected anomaly for the existing buckets on one or more dimension keys. bool detectAnomaly(const int64_t& currBucketNum, const DimToValMap& currentBucket); bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key, const int64_t& currentBucketValue); // Informs incidentd about the detected alert. void declareAnomaly(const uint64_t& timestampNs); void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key); // Detects the alert and informs the incidentd when applicable. void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const DimToValMap& currentBucket); void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum, const HashableDimensionKey& key, const int64_t& currentBucketValue); Loading @@ -82,9 +79,11 @@ public: return mAlert.trigger_if_sum_gt(); } // Helper function to return the timestamp of the last detected anomaly. inline int64_t getLastAnomalyTimestampNs() const { return mLastAnomalyTimestampNs; // Returns the refractory period timestamp (in seconds) for the given key. // If there is no stored refractory period ending timestamp, returns 0. uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const { const auto& it = mRefractoryPeriodEndsSec.find(key); return it != mRefractoryPeriodEndsSec.end() ? it->second : 0; } inline int getNumOfPastBuckets() const { Loading Loading @@ -121,8 +120,11 @@ protected: // The bucket number of the last added bucket. int64_t mMostRecentBucketNum = -1; // The timestamp when the last anomaly was declared. int64_t mLastAnomalyTimestampNs = -1; // Map from each dimension to the timestamp that its refractory period (if this anomaly was // declared for that dimension) ends, in seconds. Only anomalies that occur after this period // ends will be declared. // Entries may be, but are not guaranteed to be, removed after the period is finished. unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec; void flushPastBuckets(const int64_t& currBucketNum); Loading @@ -133,7 +135,7 @@ protected: // and remove any items with value 0. void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket); bool isInRefractoryPeriod(const uint64_t& timestampNs) const; bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key); // Calculates the corresponding bucket index within the circular array. size_t index(int64_t bucketNum) const; Loading @@ -142,12 +144,12 @@ protected: virtual void resetStorage(); // Informs the subscribers that an anomaly has occurred. void informSubscribers(); void informSubscribers(const HashableDimensionKey& key); FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets); FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets); FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); }; } // namespace statsd Loading
cmds/statsd/src/anomaly/DurationAnomalyTracker.cpp +3 −3 Original line number Diff line number Diff line Loading @@ -46,7 +46,7 @@ void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensio if (itr->second != nullptr && static_cast<uint32_t>(timestampNs / NS_PER_SEC) >= itr->second->timestampSec) { declareAnomaly(timestampNs); declareAnomaly(timestampNs, dimensionKey); stopAlarm(dimensionKey); } } Loading @@ -55,7 +55,7 @@ void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey const uint64_t& timestampNs) { uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC); if (isInRefractoryPeriod(timestampNs)) { if (isInRefractoryPeriod(timestampNs, dimensionKey)) { VLOG("Skipping setting anomaly alarm since it'd fall in the refractory period"); return; } Loading Loading @@ -104,7 +104,7 @@ void DurationAnomalyTracker::informAlarmsFired(const uint64_t& timestampNs, // Now declare each of these alarms to have fired. for (const auto& kv : matchedAlarms) { declareAnomaly(timestampNs /* TODO: , kv.first */); declareAnomaly(timestampNs, kv.first); mAlarms.erase(kv.first); firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it. } Loading
cmds/statsd/src/anomaly/DurationAnomalyTracker.h +3 −3 Original line number Diff line number Diff line Loading @@ -50,7 +50,7 @@ public: const uint64_t& timestampNs); // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker // and removes it from firedAlarms. Does NOT remove the alarm from the AnomalyMonitor. // and removes it from firedAlarms. // TODO: This will actually be called from a different thread, so make it thread-safe! // This means that almost every function in DurationAnomalyTracker needs to be locked. // But this should be done at the level of StatsLogProcessor, which needs to lock Loading @@ -70,10 +70,10 @@ protected: void resetStorage() override; FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); FRIEND_TEST(MaxDurationTrackerTest, TestAnomalyDetection); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetection); }; } // namespace statsd Loading
cmds/statsd/src/metrics/CountMetricProducer.h +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ private: FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents); FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetection); FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced); }; } // namespace statsd Loading