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