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