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