Home | History | Annotate | Download | only in anomaly
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #pragma once
     18 
     19 #include <memory>  // unique_ptr
     20 
     21 #include <stdlib.h>
     22 
     23 #include <gtest/gtest_prod.h>
     24 #include <utils/RefBase.h>
     25 
     26 #include "AlarmMonitor.h"
     27 #include "config/ConfigKey.h"
     28 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert
     29 #include "stats_util.h"  // HashableDimensionKey and DimToValMap
     30 
     31 namespace android {
     32 namespace os {
     33 namespace statsd {
     34 
     35 using std::shared_ptr;
     36 using std::unordered_map;
     37 
     38 // Does NOT allow negative values.
     39 class AnomalyTracker : public virtual RefBase {
     40 public:
     41     AnomalyTracker(const Alert& alert, const ConfigKey& configKey);
     42 
     43     virtual ~AnomalyTracker();
     44 
     45     // Add subscriptions that depend on this alert.
     46     void addSubscription(const Subscription& subscription) {
     47         mSubscriptions.push_back(subscription);
     48     }
     49 
     50     // Adds a bucket for the given bucketNum (index starting at 0).
     51     // If a bucket for bucketNum already exists, it will be replaced.
     52     // Also, advances to bucketNum (if not in the past), effectively filling any intervening
     53     // buckets with 0s.
     54     void addPastBucket(std::shared_ptr<DimToValMap> bucket, const int64_t& bucketNum);
     55 
     56     // Inserts (or replaces) the bucket entry for the given bucketNum at the given key to be the
     57     // given bucketValue. If the bucket does not exist, it will be created.
     58     // Also, advances to bucketNum (if not in the past), effectively filling any intervening
     59     // buckets with 0s.
     60     void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
     61                        const int64_t& bucketNum);
     62 
     63     // Returns true if, based on past buckets plus the new currentBucketValue (which generally
     64     // represents the partially-filled current bucket), an anomaly has happened.
     65     // Also advances to currBucketNum-1.
     66     bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
     67                        const int64_t& currentBucketValue);
     68 
     69     // Informs incidentd about the detected alert.
     70     void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
     71                         int64_t metricValue);
     72 
     73     // Detects if, based on past buckets plus the new currentBucketValue (which generally
     74     // represents the partially-filled current bucket), an anomaly has happened, and if so,
     75     // declares an anomaly and informs relevant subscribers.
     76     // Also advances to currBucketNum-1.
     77     void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
     78                                  int64_t metricId, const MetricDimensionKey& key,
     79                                  const int64_t& currentBucketValue);
     80 
     81     // Init the AlarmMonitor which is shared across anomaly trackers.
     82     virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
     83         return; // Base AnomalyTracker class has no need for the AlarmMonitor.
     84     }
     85 
     86     // Returns the sum of all past bucket values for the given dimension key.
     87     int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
     88 
     89     // Returns the value for a past bucket, or 0 if that bucket doesn't exist.
     90     int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
     91 
     92     // Returns the anomaly threshold set in the configuration.
     93     inline int64_t getAnomalyThreshold() const {
     94         return mAlert.trigger_if_sum_gt();
     95     }
     96 
     97     // Returns the refractory period ending timestamp (in seconds) for the given key.
     98     // Before this moment, any detected anomaly will be ignored.
     99     // If there is no stored refractory period ending timestamp, returns 0.
    100     uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const {
    101         const auto& it = mRefractoryPeriodEndsSec.find(key);
    102         return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
    103     }
    104 
    105     // Returns the (constant) number of past buckets this anomaly tracker can store.
    106     inline int getNumOfPastBuckets() const {
    107         return mNumOfPastBuckets;
    108     }
    109 
    110     // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
    111     // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor.
    112     virtual void informAlarmsFired(const int64_t& timestampNs,
    113             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
    114         return; // The base AnomalyTracker class doesn't have alarms.
    115     }
    116 
    117 protected:
    118     // For testing only.
    119     // Returns the alarm timestamp in seconds for the query dimension if it exists. Otherwise
    120     // returns 0.
    121     virtual uint32_t getAlarmTimestampSec(const MetricDimensionKey& dimensionKey) const {
    122         return 0;   // The base AnomalyTracker class doesn't have alarms.
    123     }
    124 
    125     // statsd_config.proto Alert message that defines this tracker.
    126     const Alert mAlert;
    127 
    128     // The subscriptions that depend on this alert.
    129     std::vector<Subscription> mSubscriptions;
    130 
    131     // A reference to the Alert's config key.
    132     const ConfigKey mConfigKey;
    133 
    134     // Number of past buckets. One less than the total number of buckets needed
    135     // for the anomaly detection (since the current bucket is not in the past).
    136     const int mNumOfPastBuckets;
    137 
    138     // Values for each of the past mNumOfPastBuckets buckets. Always of size mNumOfPastBuckets.
    139     // mPastBuckets[i] can be null, meaning that no data is present in that bucket.
    140     std::vector<shared_ptr<DimToValMap>> mPastBuckets;
    141 
    142     // Cached sum over all existing buckets in mPastBuckets.
    143     // Its buckets never contain entries of 0.
    144     DimToValMap mSumOverPastBuckets;
    145 
    146     // The bucket number of the last added bucket.
    147     int64_t mMostRecentBucketNum = -1;
    148 
    149     // Map from each dimension to the timestamp that its refractory period (if this anomaly was
    150     // declared for that dimension) ends, in seconds. From this moment and onwards, anomalies
    151     // can be declared again.
    152     // Entries may be, but are not guaranteed to be, removed after the period is finished.
    153     unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
    154 
    155     // Advances mMostRecentBucketNum to bucketNum, deleting any data that is now too old.
    156     // Specifically, since it is now too old, removes the data for
    157     //   [mMostRecentBucketNum - mNumOfPastBuckets + 1, bucketNum - mNumOfPastBuckets].
    158     void advanceMostRecentBucketTo(const int64_t& bucketNum);
    159 
    160     // Add the information in the given bucket to mSumOverPastBuckets.
    161     void addBucketToSum(const shared_ptr<DimToValMap>& bucket);
    162 
    163     // Subtract the information in the given bucket from mSumOverPastBuckets
    164     // and remove any items with value 0.
    165     void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
    166 
    167     // From mSumOverPastBuckets[key], subtracts bucketValue, removing it if it is now 0.
    168     void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue);
    169 
    170     // Returns true if in the refractory period, else false.
    171     bool isInRefractoryPeriod(const int64_t& timestampNs, const MetricDimensionKey& key) const;
    172 
    173     // Calculates the corresponding bucket index within the circular array.
    174     // Requires bucketNum >= 0.
    175     size_t index(int64_t bucketNum) const;
    176 
    177     // Resets all bucket data. For use when all the data gets stale.
    178     virtual void resetStorage();
    179 
    180     // Informs the subscribers (incidentd, perfetto, broadcasts, etc) that an anomaly has occurred.
    181     void informSubscribers(const MetricDimensionKey& key, int64_t metricId, int64_t metricValue);
    182 
    183     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
    184     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
    185     FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
    186     FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
    187     FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
    188     FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
    189     FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
    190 };
    191 
    192 }  // namespace statsd
    193 }  // namespace os
    194 }  // namespace android
    195