summaryrefslogtreecommitdiff
path: root/cmds/statsd/src/anomaly/AnomalyTracker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/statsd/src/anomaly/AnomalyTracker.cpp')
-rw-r--r--cmds/statsd/src/anomaly/AnomalyTracker.cpp321
1 files changed, 0 insertions, 321 deletions
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
deleted file mode 100644
index 619752c7c44a..000000000000
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false // STOPSHIP if true
-#include "Log.h"
-
-#include "AnomalyTracker.h"
-#include "external/Perfetto.h"
-#include "guardrail/StatsdStats.h"
-#include "metadata_util.h"
-#include "stats_log_util.h"
-#include "subscriber_util.h"
-#include "subscriber/IncidentdReporter.h"
-#include "subscriber/SubscriberReporter.h"
-
-#include <inttypes.h>
-#include <statslog_statsd.h>
-#include <time.h>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey)
- : mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) {
- VLOG("AnomalyTracker() called");
- if (mAlert.num_buckets() <= 0) {
- ALOGE("Cannot create AnomalyTracker with %lld buckets", (long long)mAlert.num_buckets());
- return;
- }
- if (!mAlert.has_trigger_if_sum_gt()) {
- ALOGE("Cannot create AnomalyTracker without threshold");
- return;
- }
- resetStorage(); // initialization
-}
-
-AnomalyTracker::~AnomalyTracker() {
- VLOG("~AnomalyTracker() called");
-}
-
-void AnomalyTracker::resetStorage() {
- VLOG("resetStorage() called.");
- mPastBuckets.clear();
- // Excludes the current bucket.
- mPastBuckets.resize(mNumOfPastBuckets);
- mSumOverPastBuckets.clear();
-}
-
-size_t AnomalyTracker::index(int64_t bucketNum) const {
- if (bucketNum < 0) {
- ALOGE("index() was passed a negative bucket number (%lld)!", (long long)bucketNum);
- }
- return bucketNum % mNumOfPastBuckets;
-}
-
-void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) {
- VLOG("advanceMostRecentBucketTo() called.");
- if (mNumOfPastBuckets <= 0) {
- return;
- }
- if (bucketNum <= mMostRecentBucketNum) {
- ALOGW("Cannot advance buckets backwards (bucketNum=%lld but mMostRecentBucketNum=%lld)",
- (long long)bucketNum, (long long)mMostRecentBucketNum);
- return;
- }
- // If in the future (i.e. buckets are ancient), just empty out all past info.
- if (bucketNum >= mMostRecentBucketNum + mNumOfPastBuckets) {
- resetStorage();
- mMostRecentBucketNum = bucketNum;
- return;
- }
-
- // Clear out space by emptying out old mPastBuckets[i] values and update mSumOverPastBuckets.
- for (int64_t i = mMostRecentBucketNum + 1; i <= bucketNum; i++) {
- const int idx = index(i);
- subtractBucketFromSum(mPastBuckets[idx]);
- mPastBuckets[idx] = nullptr; // release (but not clear) the old bucket.
- }
- mMostRecentBucketNum = bucketNum;
-}
-
-void AnomalyTracker::addPastBucket(const MetricDimensionKey& key,
- const int64_t& bucketValue,
- const int64_t& bucketNum) {
- VLOG("addPastBucket(bucketValue) called.");
- if (mNumOfPastBuckets == 0 ||
- bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
- return;
- }
-
- const int bucketIndex = index(bucketNum);
- if (bucketNum <= mMostRecentBucketNum && (mPastBuckets[bucketIndex] != nullptr)) {
- // We need to insert into an already existing past bucket.
- std::shared_ptr<DimToValMap>& bucket = mPastBuckets[bucketIndex];
- auto itr = bucket->find(key);
- if (itr != bucket->end()) {
- // Old entry already exists; update it.
- subtractValueFromSum(key, itr->second);
- itr->second = bucketValue;
- } else {
- bucket->insert({key, bucketValue});
- }
- mSumOverPastBuckets[key] += bucketValue;
- } else {
- // Bucket does not exist yet (in future or was never made), so we must make it.
- std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
- bucket->insert({key, bucketValue});
- addPastBucket(bucket, bucketNum);
- }
-}
-
-void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucket,
- const int64_t& bucketNum) {
- VLOG("addPastBucket(bucket) called.");
- if (mNumOfPastBuckets == 0 ||
- bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
- return;
- }
-
- if (bucketNum <= mMostRecentBucketNum) {
- // We are updating an old bucket, not adding a new one.
- subtractBucketFromSum(mPastBuckets[index(bucketNum)]);
- } else {
- // Clear space for the new bucket to be at bucketNum.
- advanceMostRecentBucketTo(bucketNum);
- }
- mPastBuckets[index(bucketNum)] = bucket;
- addBucketToSum(bucket);
-}
-
-void AnomalyTracker::subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket) {
- if (bucket == nullptr) {
- return;
- }
- for (const auto& keyValuePair : *bucket) {
- subtractValueFromSum(keyValuePair.first, keyValuePair.second);
- }
-}
-
-
-void AnomalyTracker::subtractValueFromSum(const MetricDimensionKey& key,
- const int64_t& bucketValue) {
- auto itr = mSumOverPastBuckets.find(key);
- if (itr == mSumOverPastBuckets.end()) {
- return;
- }
- itr->second -= bucketValue;
- if (itr->second == 0) {
- mSumOverPastBuckets.erase(itr);
- }
-}
-
-void AnomalyTracker::addBucketToSum(const shared_ptr<DimToValMap>& bucket) {
- if (bucket == nullptr) {
- return;
- }
- // For each dimension present in the bucket, add its value to its corresponding sum.
- for (const auto& keyValuePair : *bucket) {
- mSumOverPastBuckets[keyValuePair.first] += keyValuePair.second;
- }
-}
-
-int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
- const int64_t& bucketNum) const {
- if (bucketNum < 0 || mMostRecentBucketNum < 0
- || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
- || bucketNum > mMostRecentBucketNum) {
- return 0;
- }
-
- const auto& bucket = mPastBuckets[index(bucketNum)];
- if (bucket == nullptr) {
- return 0;
- }
- const auto& itr = bucket->find(key);
- return itr == bucket->end() ? 0 : itr->second;
-}
-
-int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const {
- const auto& itr = mSumOverPastBuckets.find(key);
- if (itr != mSumOverPastBuckets.end()) {
- return itr->second;
- }
- return 0;
-}
-
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
- const MetricDimensionKey& key,
- const int64_t& currentBucketValue) {
-
- // currentBucketNum should be the next bucket after pastBuckets. If not, advance so that it is.
- if (currentBucketNum > mMostRecentBucketNum + 1) {
- advanceMostRecentBucketTo(currentBucketNum - 1);
- }
- return mAlert.has_trigger_if_sum_gt() &&
- getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
-}
-
-void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
- const MetricDimensionKey& key, int64_t metricValue) {
- // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
- // real time right now.
- if (isInRefractoryPeriod(timestampNs, key)) {
- VLOG("Skipping anomaly declaration since within refractory period");
- return;
- }
- if (mAlert.has_refractory_period_secs()) {
- mRefractoryPeriodEndsSec[key] = ((timestampNs + NS_PER_SEC - 1) / NS_PER_SEC) // round up
- + mAlert.refractory_period_secs();
- // TODO(b/110563466): If we had access to the bucket_size_millis, consider
- // calling resetStorage()
- // if (mAlert.refractory_period_secs() > mNumOfPastBuckets * bucketSizeNs) {resetStorage();}
- }
-
- if (!mSubscriptions.empty()) {
- ALOGI("An anomaly (%" PRId64 ") %s has occurred! Informing subscribers.",
- mAlert.id(), key.toString().c_str());
- informSubscribers(key, metricId, metricValue);
- } else {
- ALOGI("An anomaly has occurred! (But no subscriber for that alert.)");
- }
-
- StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
-
- // TODO(b/110564268): This should also take in the const MetricDimensionKey& key?
- util::stats_write(util::ANOMALY_DETECTED, mConfigKey.GetUid(),
- mConfigKey.GetId(), mAlert.id());
-}
-
-void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
- const int64_t& currBucketNum, int64_t metricId,
- const MetricDimensionKey& key,
- const int64_t& currentBucketValue) {
- if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
- declareAnomaly(timestampNs, metricId, key, currentBucketValue);
- }
-}
-
-bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs,
- const MetricDimensionKey& key) const {
- const auto& it = mRefractoryPeriodEndsSec.find(key);
- if (it != mRefractoryPeriodEndsSec.end()) {
- return timestampNs < (it->second * (int64_t)NS_PER_SEC);
- }
- return false;
-}
-
-void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
- int64_t metricValue) {
- triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
-}
-
-bool AnomalyTracker::writeAlertMetadataToProto(int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs,
- metadata::AlertMetadata* alertMetadata) {
- bool metadataWritten = false;
-
- if (mRefractoryPeriodEndsSec.empty()) {
- return false;
- }
-
- for (const auto& it: mRefractoryPeriodEndsSec) {
- // Do not write the timestamp to disk if it has already expired
- if (it.second < systemElapsedTimeNs / NS_PER_SEC) {
- continue;
- }
-
- metadataWritten = true;
- if (alertMetadata->alert_dim_keyed_data_size() == 0) {
- alertMetadata->set_alert_id(mAlert.id());
- }
-
- metadata::AlertDimensionKeyedData* keyedData = alertMetadata->add_alert_dim_keyed_data();
- // We convert and write the refractory_end_sec to wall clock time because we do not know
- // when statsd will start again.
- int32_t refractoryEndWallClockSec = (int32_t) ((currentWallClockTimeNs / NS_PER_SEC) +
- (it.second - systemElapsedTimeNs / NS_PER_SEC));
-
- keyedData->set_last_refractory_ends_sec(refractoryEndWallClockSec);
- writeMetricDimensionKeyToMetadataDimensionKey(
- it.first, keyedData->mutable_dimension_key());
- }
-
- return metadataWritten;
-}
-
-void AnomalyTracker::loadAlertMetadata(
- const metadata::AlertMetadata& alertMetadata,
- int64_t currentWallClockTimeNs,
- int64_t systemElapsedTimeNs) {
- for (const metadata::AlertDimensionKeyedData& keyedData :
- alertMetadata.alert_dim_keyed_data()) {
- if ((uint64_t) keyedData.last_refractory_ends_sec() < currentWallClockTimeNs / NS_PER_SEC) {
- // Do not update the timestamp if it has already expired.
- continue;
- }
- MetricDimensionKey metricKey = loadMetricDimensionKeyFromProto(
- keyedData.dimension_key());
- int32_t refractoryPeriodEndsSec = (int32_t) keyedData.last_refractory_ends_sec() -
- currentWallClockTimeNs / NS_PER_SEC + systemElapsedTimeNs / NS_PER_SEC;
- mRefractoryPeriodEndsSec[metricKey] = refractoryPeriodEndsSec;
- }
-}
-
-} // namespace statsd
-} // namespace os
-} // namespace android