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 #ifndef DURATION_TRACKER_H
18 #define DURATION_TRACKER_H
19 
20 #include "anomaly/DurationAnomalyTracker.h"
21 #include "condition/ConditionWizard.h"
22 #include "config/ConfigKey.h"
23 #include "metrics/parsing_utils/config_update_utils.h"
24 #include "stats_util.h"
25 
26 namespace android {
27 namespace os {
28 namespace statsd {
29 
30 enum DurationState {
31     kStopped = 0,  // The event is stopped.
32     kStarted = 1,  // The event is on going.
33     kPaused = 2,   // The event is started, but condition is false, clock is paused. When condition
34                    // turns to true, kPaused will become kStarted.
35 };
36 
37 // Hold duration information for one atom level duration in current on-going bucket.
38 struct DurationInfo {
39     DurationState state;
40 
41     // the number of starts seen.
42     int32_t startCount;
43 
44     // most recent start time.
45     int64_t lastStartTime;
46     // existing duration in current bucket.
47     int64_t lastDuration;
48     // cache the HashableDimensionKeys we need to query the condition for this duration event.
49     ConditionKey conditionKeys;
50 
DurationInfoDurationInfo51     DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
52 };
53 
54 struct DurationBucket {
55     int64_t mBucketStartNs;
56     int64_t mBucketEndNs;
57     int64_t mDuration;
58     int64_t mConditionTrueNs;
59 
DurationBucketDurationBucket60     DurationBucket() : mBucketStartNs(0), mBucketEndNs(0), mDuration(0), mConditionTrueNs(0){};
61 };
62 
63 struct DurationValues {
64     // Recorded duration for current partial bucket.
65     int64_t mDuration;
66 
67     // Sum of past partial bucket durations in current full bucket.
68     // Used for anomaly detection.
69     int64_t mDurationFullBucket;
70 
DurationValuesDurationValues71     DurationValues() : mDuration(0), mDurationFullBucket(0){};
72 };
73 
74 class DurationTracker {
75 public:
DurationTracker(const ConfigKey & key,const int64_t id,const MetricDimensionKey & eventKey,const sp<ConditionWizard> & wizard,int conditionIndex,bool nesting,int64_t currentBucketStartNs,int64_t currentBucketNum,int64_t startTimeNs,int64_t bucketSizeNs,bool conditionSliced,bool fullLink,const std::vector<sp<AnomalyTracker>> & anomalyTrackers)76     DurationTracker(const ConfigKey& key, const int64_t id, const MetricDimensionKey& eventKey,
77                     const sp<ConditionWizard>& wizard, int conditionIndex, bool nesting,
78                     int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
79                     int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
80                     const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
81         : mConfigKey(key),
82           mTrackerId(id),
83           mEventKey(eventKey),
84           mWizard(wizard),
85           mConditionTrackerIndex(conditionIndex),
86           mBucketSizeNs(bucketSizeNs),
87           mNested(nesting),
88           mCurrentBucketStartTimeNs(currentBucketStartNs),
89           mCurrentBucketNum(currentBucketNum),
90           mStartTimeNs(startTimeNs),
91           mConditionSliced(conditionSliced),
92           mHasLinksToAllConditionDimensionsInTracker(fullLink),
93           mAnomalyTrackers(anomalyTrackers),
94           mHasHitGuardrail(false){};
95 
~DurationTracker()96     virtual ~DurationTracker(){};
97 
onConfigUpdated(const sp<ConditionWizard> & wizard,int conditionTrackerIndex)98     void onConfigUpdated(const sp<ConditionWizard>& wizard, int conditionTrackerIndex) {
99         sp<ConditionWizard> tmpWizard = mWizard;
100         mWizard = wizard;
101         mConditionTrackerIndex = conditionTrackerIndex;
102         mAnomalyTrackers.clear();
103     };
104 
105     virtual void noteStart(const HashableDimensionKey& key, bool condition, int64_t eventTime,
106                            const ConditionKey& conditionKey, size_t dimensionHardLimit) = 0;
107     virtual void noteStop(const HashableDimensionKey& key, int64_t eventTime,
108                           const bool stopAll) = 0;
109     virtual void noteStopAll(const int64_t eventTime) = 0;
110 
111     virtual void onSlicedConditionMayChange(const int64_t timestamp) = 0;
112     virtual void onConditionChanged(bool condition, int64_t timestamp) = 0;
113 
114     virtual void onStateChanged(const int64_t timestamp, const int32_t atomId,
115                                 const FieldValue& newState) = 0;
116 
117     // Flush stale buckets if needed, and return true if the tracker has no on-going duration
118     // events, so that the owner can safely remove the tracker.
119     virtual bool flushIfNeeded(
120             int64_t timestampNs, const optional<UploadThreshold>& uploadThreshold,
121             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
122 
123     // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from
124     // an app upgrade, we assume that we're trying to form a partial bucket.
125     virtual bool flushCurrentBucket(
126             int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
127             const int64_t globalConditionTrueNs,
128             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
129 
130     // Predict the anomaly timestamp given the current status.
131     virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
132                                               const int64_t currentTimestamp) const = 0;
133     // Dump internal states for debugging
134     virtual void dumpStates(int out, bool verbose) const = 0;
135 
136     virtual int64_t getCurrentStateKeyDuration() const = 0;
137 
138     virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0;
139 
140     // Replace old value with new value for the given state atom.
141     virtual void updateCurrentStateKey(int32_t atomId, const FieldValue& newState) = 0;
142 
143     virtual bool hasAccumulatedDuration() const = 0;
144 
addAnomalyTracker(sp<AnomalyTracker> & anomalyTracker,const UpdateStatus & updateStatus,const int64_t updateTimeNs)145     void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, const UpdateStatus& updateStatus,
146                            const int64_t updateTimeNs) {
147         mAnomalyTrackers.push_back(anomalyTracker);
148         // Preserved anomaly trackers will have the correct alarm times.
149         // New/replaced alerts will need to set alarms for pending durations, or may have already
150         // fired if the full bucket duration is high enough.
151         // NB: this depends on a config updating that splits a partial bucket having just happened.
152         // If this constraint changes, predict will return the wrong timestamp.
153         if (updateStatus == UpdateStatus::UPDATE_NEW ||
154             updateStatus == UpdateStatus::UPDATE_PRESERVE) {
155             const int64_t alarmTimeNs = predictAnomalyTimestampNs(*anomalyTracker, updateTimeNs);
156             if (alarmTimeNs <= updateTimeNs || hasStartedDuration()) {
157                 anomalyTracker->startAlarm(mEventKey, std::max(alarmTimeNs, updateTimeNs));
158             }
159         }
160     }
161 
162 protected:
163     virtual bool hasStartedDuration() const = 0;
164 
165     // Convenience to compute the current bucket's end time, which is always aligned with the
166     // start time of the metric.
getCurrentBucketEndTimeNs()167     int64_t getCurrentBucketEndTimeNs() const {
168         return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
169     }
170 
171     // Starts the anomaly alarm.
startAnomalyAlarm(const int64_t eventTime)172     void startAnomalyAlarm(const int64_t eventTime) {
173         for (auto& anomalyTracker : mAnomalyTrackers) {
174             if (anomalyTracker != nullptr) {
175                 const int64_t alarmTimestampNs =
176                     predictAnomalyTimestampNs(*anomalyTracker, eventTime);
177                 if (alarmTimestampNs > 0) {
178                     anomalyTracker->startAlarm(mEventKey, alarmTimestampNs);
179                 }
180             }
181         }
182     }
183 
184     // Stops the anomaly alarm. If it should have already fired, declare the anomaly now.
stopAnomalyAlarm(const int64_t timestamp)185     void stopAnomalyAlarm(const int64_t timestamp) {
186         for (auto& anomalyTracker : mAnomalyTrackers) {
187             if (anomalyTracker != nullptr) {
188                 anomalyTracker->stopAlarm(mEventKey, timestamp);
189             }
190         }
191     }
192 
addPastBucketToAnomalyTrackers(const MetricDimensionKey & eventKey,int64_t bucketValue,int64_t bucketNum)193     void addPastBucketToAnomalyTrackers(const MetricDimensionKey& eventKey, int64_t bucketValue,
194                                         int64_t bucketNum) {
195         for (auto& anomalyTracker : mAnomalyTrackers) {
196             if (anomalyTracker != nullptr) {
197                 anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum);
198             }
199         }
200     }
201 
detectAndDeclareAnomaly(int64_t timestamp,int64_t currBucketNum,int64_t currentBucketValue)202     void detectAndDeclareAnomaly(int64_t timestamp, int64_t currBucketNum,
203                                  int64_t currentBucketValue) {
204         for (auto& anomalyTracker : mAnomalyTrackers) {
205             if (anomalyTracker != nullptr) {
206                 anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
207                                                         mEventKey, currentBucketValue);
208             }
209         }
210     }
211 
setEventKey(const MetricDimensionKey & eventKey)212     void setEventKey(const MetricDimensionKey& eventKey) {
213         mEventKey = eventKey;
214     }
215 
durationPassesThreshold(const optional<UploadThreshold> & uploadThreshold,int64_t duration)216     bool durationPassesThreshold(const optional<UploadThreshold>& uploadThreshold,
217                                  int64_t duration) {
218         if (duration <= 0) {
219             return false;
220         }
221 
222         if (uploadThreshold == nullopt) {
223             return true;
224         }
225 
226         switch (uploadThreshold->value_comparison_case()) {
227             case UploadThreshold::kLtInt:
228                 return duration < uploadThreshold->lt_int();
229             case UploadThreshold::kGtInt:
230                 return duration > uploadThreshold->gt_int();
231             case UploadThreshold::kLteInt:
232                 return duration <= uploadThreshold->lte_int();
233             case UploadThreshold::kGteInt:
234                 return duration >= uploadThreshold->gte_int();
235             default:
236                 ALOGE("Duration metric incorrect upload threshold type used");
237                 return false;
238         }
239     }
240 
241     // A reference to the DurationMetricProducer's config key.
242     const ConfigKey& mConfigKey;
243 
244     const int64_t mTrackerId;
245 
246     MetricDimensionKey mEventKey;
247 
248     sp<ConditionWizard> mWizard;
249 
250     int mConditionTrackerIndex;
251 
252     const int64_t mBucketSizeNs;
253 
254     const bool mNested;
255 
256     int64_t mCurrentBucketStartTimeNs;
257 
258     // Recorded duration results for each state key in the current partial bucket.
259     std::unordered_map<HashableDimensionKey, DurationValues> mStateKeyDurationMap;
260 
261     int64_t mCurrentBucketNum;
262 
263     const int64_t mStartTimeNs;
264 
265     const bool mConditionSliced;
266 
267     bool mHasLinksToAllConditionDimensionsInTracker;
268 
269     std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
270 
271     mutable bool mHasHitGuardrail;
272 
273     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
274     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
275     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
276 
277     FRIEND_TEST(OringDurationTrackerTest_DimLimit, TestDimLimit);
278 
279     FRIEND_TEST(MaxDurationTrackerTest_DimLimit, TestDimLimit);
280 
281     FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
282     FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
283 };
284 
285 }  // namespace statsd
286 }  // namespace os
287 }  // namespace android
288 
289 #endif  // DURATION_TRACKER_H
290