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     // TODO: Optimize the way we track sliced condition in duration metrics.
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 };
59 
60 class DurationTracker {
61 public:
DurationTracker(const ConfigKey & key,const int64_t & id,const MetricDimensionKey & eventKey,sp<ConditionWizard> wizard,int conditionIndex,const std::vector<Matcher> & dimensionInCondition,bool nesting,int64_t currentBucketStartNs,int64_t currentBucketNum,int64_t startTimeNs,int64_t bucketSizeNs,bool conditionSliced,bool fullLink,const std::vector<sp<DurationAnomalyTracker>> & anomalyTrackers)62     DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
63                     sp<ConditionWizard> wizard, int conditionIndex,
64                     const std::vector<Matcher>& dimensionInCondition, bool nesting,
65                     int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
66                     int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
67                     const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
68         : mConfigKey(key),
69           mTrackerId(id),
70           mEventKey(eventKey),
71           mWizard(wizard),
72           mConditionTrackerIndex(conditionIndex),
73           mBucketSizeNs(bucketSizeNs),
74           mDimensionInCondition(dimensionInCondition),
75           mNested(nesting),
76           mCurrentBucketStartTimeNs(currentBucketStartNs),
77           mDuration(0),
78           mDurationFullBucket(0),
79           mCurrentBucketNum(currentBucketNum),
80           mStartTimeNs(startTimeNs),
81           mConditionSliced(conditionSliced),
82           mHasLinksToAllConditionDimensionsInTracker(fullLink),
83           mAnomalyTrackers(anomalyTrackers){};
84 
~DurationTracker()85     virtual ~DurationTracker(){};
86 
87     virtual unique_ptr<DurationTracker> clone(const int64_t eventTime) = 0;
88 
89     virtual void noteStart(const HashableDimensionKey& key, bool condition,
90                            const int64_t eventTime, const ConditionKey& conditionKey) = 0;
91     virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
92                           const bool stopAll) = 0;
93     virtual void noteStopAll(const int64_t eventTime) = 0;
94 
95     virtual void onSlicedConditionMayChange(bool overallCondition, const int64_t timestamp) = 0;
96     virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0;
97 
98     // Flush stale buckets if needed, and return true if the tracker has no on-going duration
99     // events, so that the owner can safely remove the tracker.
100     virtual bool flushIfNeeded(
101             int64_t timestampNs,
102             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
103 
104     // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from
105     // an app upgrade, we assume that we're trying to form a partial bucket.
106     virtual bool flushCurrentBucket(
107             const int64_t& eventTimeNs,
108             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
109 
110     // Predict the anomaly timestamp given the current status.
111     virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
112                                               const int64_t currentTimestamp) const = 0;
113     // Dump internal states for debugging
114     virtual void dumpStates(FILE* out, bool verbose) const = 0;
115 
setEventKey(const MetricDimensionKey & eventKey)116     void setEventKey(const MetricDimensionKey& eventKey) {
117          mEventKey = eventKey;
118     }
119 
120 protected:
getCurrentBucketEndTimeNs()121     int64_t getCurrentBucketEndTimeNs() const {
122         return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
123     }
124 
125     // Starts the anomaly alarm.
startAnomalyAlarm(const int64_t eventTime)126     void startAnomalyAlarm(const int64_t eventTime) {
127         for (auto& anomalyTracker : mAnomalyTrackers) {
128             if (anomalyTracker != nullptr) {
129                 const int64_t alarmTimestampNs =
130                     predictAnomalyTimestampNs(*anomalyTracker, eventTime);
131                 if (alarmTimestampNs > 0) {
132                     anomalyTracker->startAlarm(mEventKey, alarmTimestampNs);
133                 }
134             }
135         }
136     }
137 
138     // Stops the anomaly alarm. If it should have already fired, declare the anomaly now.
stopAnomalyAlarm(const int64_t timestamp)139     void stopAnomalyAlarm(const int64_t timestamp) {
140         for (auto& anomalyTracker : mAnomalyTrackers) {
141             if (anomalyTracker != nullptr) {
142                 anomalyTracker->stopAlarm(mEventKey, timestamp);
143             }
144         }
145     }
146 
addPastBucketToAnomalyTrackers(const int64_t & bucketValue,const int64_t & bucketNum)147     void addPastBucketToAnomalyTrackers(const int64_t& bucketValue, const int64_t& bucketNum) {
148         for (auto& anomalyTracker : mAnomalyTrackers) {
149             if (anomalyTracker != nullptr) {
150                 anomalyTracker->addPastBucket(mEventKey, bucketValue, bucketNum);
151             }
152         }
153     }
154 
detectAndDeclareAnomaly(const int64_t & timestamp,const int64_t & currBucketNum,const int64_t & currentBucketValue)155     void detectAndDeclareAnomaly(const int64_t& timestamp, const int64_t& currBucketNum,
156                                  const int64_t& currentBucketValue) {
157         for (auto& anomalyTracker : mAnomalyTrackers) {
158             if (anomalyTracker != nullptr) {
159                 anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mEventKey,
160                                                         currentBucketValue);
161             }
162         }
163     }
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() {
168         return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
169     }
170 
171     // A reference to the DurationMetricProducer's config key.
172     const ConfigKey& mConfigKey;
173 
174     const int64_t mTrackerId;
175 
176     MetricDimensionKey mEventKey;
177 
178     sp<ConditionWizard> mWizard;
179 
180     const int mConditionTrackerIndex;
181 
182     const int64_t mBucketSizeNs;
183 
184     const std::vector<Matcher>& mDimensionInCondition;
185 
186     const bool mNested;
187 
188     int64_t mCurrentBucketStartTimeNs;
189 
190     int64_t mDuration;  // current recorded duration result (for partial bucket)
191 
192     int64_t mDurationFullBucket;  // Sum of past partial buckets in current full bucket.
193 
194     int64_t mCurrentBucketNum;
195 
196     const int64_t mStartTimeNs;
197 
198     const bool mConditionSliced;
199 
200     bool mSameConditionDimensionsInTracker;
201     bool mHasLinksToAllConditionDimensionsInTracker;
202 
203     std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
204 
205     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
206     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
207     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
208 };
209 
210 }  // namespace statsd
211 }  // namespace os
212 }  // namespace android
213 
214 #endif  // DURATION_TRACKER_H
215