• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
addSubscription(const Subscription & subscription)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.
setAlarmMonitor(const sp<AlarmMonitor> & alarmMonitor)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.
getAnomalyThreshold()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.
getRefractoryPeriodEndsSec(const MetricDimensionKey & key)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.
getNumOfPastBuckets()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.
informAlarmsFired(const int64_t & timestampNs,unordered_set<sp<const InternalAlarm>,SpHash<InternalAlarm>> & firedAlarms)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.
getAlarmTimestampSec(const MetricDimensionKey & dimensionKey)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