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