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 "stats_util.h"
24 
25 namespace android {
26 namespace os {
27 namespace statsd {
28 
29 enum DurationState {
30     kStopped = 0,  // The event is stopped.
31     kStarted = 1,  // The event is on going.
32     kPaused = 2,   // The event is started, but condition is false, clock is paused. When condition
33                    // turns to true, kPaused will become kStarted.
34 };
35 
36 // Hold duration information for one atom level duration in current on-going bucket.
37 struct DurationInfo {
38     DurationState state;
39 
40     // the number of starts seen.
41     int32_t startCount;
42 
43     // most recent start time.
44     int64_t lastStartTime;
45     // existing duration in current bucket.
46     int64_t lastDuration;
47     // cache the HashableDimensionKeys we need to query the condition for this duration event.
48     ConditionKey conditionKeys;
49 
DurationInfoDurationInfo50     DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
51 };
52 
53 struct DurationBucket {
54     int64_t mBucketStartNs;
55     int64_t mBucketEndNs;
56     int64_t mDuration;
57 };
58 
59 struct DurationValues {
60     // Recorded duration for current partial bucket.
61     int64_t mDuration;
62 
63     // Sum of past partial bucket durations in current full bucket.
64     // Used for anomaly detection.
65     int64_t mDurationFullBucket;
66 };
67 
68 class DurationTracker {
69 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<DurationAnomalyTracker>> & anomalyTrackers)70     DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
71                     sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
72                     int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
73                     int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
74                     const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
75         : mConfigKey(key),
76           mTrackerId(id),
77           mEventKey(eventKey),
78           mWizard(wizard),
79           mConditionTrackerIndex(conditionIndex),
80           mBucketSizeNs(bucketSizeNs),
81           mNested(nesting),
82           mCurrentBucketStartTimeNs(currentBucketStartNs),
83           mDuration(0),
84           mCurrentBucketNum(currentBucketNum),
85           mStartTimeNs(startTimeNs),
86           mConditionSliced(conditionSliced),
87           mHasLinksToAllConditionDimensionsInTracker(fullLink),
88           mAnomalyTrackers(anomalyTrackers){};
89 
~DurationTracker()90     virtual ~DurationTracker(){};
91 
92     virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
93                            const ConditionKey& conditionKey) = 0;
94     virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
95                           const bool stopAll) = 0;
96     virtual void noteStopAll(const int64_t eventTime) = 0;
97 
98     virtual void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) = 0;
99     virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0;
100 
101     virtual void onStateChanged(const int64_t timestamp, const int32_t atomId,
102                                 const FieldValue& newState) = 0;
103 
104     // Flush stale buckets if needed, and return true if the tracker has no on-going duration
105     // events, so that the owner can safely remove the tracker.
106     virtual bool flushIfNeeded(
107             int64_t timestampNs,
108             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
109 
110     // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from
111     // an app upgrade, we assume that we're trying to form a partial bucket.
112     virtual bool flushCurrentBucket(
113             const int64_t& eventTimeNs,
114             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
115 
116     // Predict the anomaly timestamp given the current status.
117     virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
118                                               const int64_t currentTimestamp) const = 0;
119     // Dump internal states for debugging
120     virtual void dumpStates(FILE* out, bool verbose) const = 0;
121 
122     virtual int64_t getCurrentStateKeyDuration() const = 0;
123 
124     virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0;
125 
126     // Replace old value with new value for the given state atom.
127     virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
128 
129 protected:
getCurrentBucketEndTimeNs()130     int64_t getCurrentBucketEndTimeNs() const {
131         return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
132     }
133 
134     // Starts the anomaly alarm.
startAnomalyAlarm(const int64_t eventTime)135     void startAnomalyAlarm(const int64_t eventTime) {
136         for (auto& anomalyTracker : mAnomalyTrackers) {
137             if (anomalyTracker != nullptr) {
138                 const int64_t alarmTimestampNs =
139                     predictAnomalyTimestampNs(*anomalyTracker, eventTime);
140                 if (alarmTimestampNs > 0) {
141                     anomalyTracker->startAlarm(mEventKey, alarmTimestampNs);
142                 }
143             }
144         }
145     }
146 
147     // Stops the anomaly alarm. If it should have already fired, declare the anomaly now.
stopAnomalyAlarm(const int64_t timestamp)148     void stopAnomalyAlarm(const int64_t timestamp) {
149         for (auto& anomalyTracker : mAnomalyTrackers) {
150             if (anomalyTracker != nullptr) {
151                 anomalyTracker->stopAlarm(mEventKey, timestamp);
152             }
153         }
154     }
155 
addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey,const int64_t & bucketValue,const int64_t & bucketNum)156     void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey,
157                                         const int64_t& bucketValue, const int64_t& bucketNum) {
158         for (auto& anomalyTracker : mAnomalyTrackers) {
159             if (anomalyTracker != nullptr) {
160                 anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum);
161             }
162         }
163     }
164 
detectAndDeclareAnomaly(const int64_t & timestamp,const int64_t & currBucketNum,const int64_t & currentBucketValue)165     void detectAndDeclareAnomaly(const int64_t& timestamp, const int64_t& currBucketNum,
166                                  const int64_t& currentBucketValue) {
167         for (auto& anomalyTracker : mAnomalyTrackers) {
168             if (anomalyTracker != nullptr) {
169                 anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
170                                                         mEventKey, currentBucketValue);
171             }
172         }
173     }
174 
175     // Convenience to compute the current bucket's end time, which is always aligned with the
176     // start time of the metric.
getCurrentBucketEndTimeNs()177     int64_t getCurrentBucketEndTimeNs() {
178         return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
179     }
180 
setEventKey(const MetricDimensionKey & eventKey)181     void setEventKey(const MetricDimensionKey& eventKey) {
182         mEventKey = eventKey;
183     }
184 
185     // A reference to the DurationMetricProducer's config key.
186     const ConfigKey& mConfigKey;
187 
188     const int64_t mTrackerId;
189 
190     MetricDimensionKey mEventKey;
191 
192     sp<ConditionWizard> mWizard;
193 
194     const int mConditionTrackerIndex;
195 
196     const int64_t mBucketSizeNs;
197 
198     const bool mNested;
199 
200     int64_t mCurrentBucketStartTimeNs;
201 
202     int64_t mDuration;  // current recorded duration result (for partial bucket)
203 
204     // Recorded duration results for each state key in the current partial bucket.
205     std::unordered_map<HashableDimensionKey, DurationValues> mStateKeyDurationMap;
206 
207     int64_t mCurrentBucketNum;
208 
209     const int64_t mStartTimeNs;
210 
211     const bool mConditionSliced;
212 
213     bool mHasLinksToAllConditionDimensionsInTracker;
214 
215     std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
216 
217     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
218     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
219     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
220 };
221 
222 }  // namespace statsd
223 }  // namespace os
224 }  // namespace android
225 
226 #endif  // DURATION_TRACKER_H
227