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
25namespace android {
26namespace os {
27namespace statsd {
28
29enum 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.
37struct 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
51    DurationInfo() : state(kStopped), startCount(0), lastStartTime(0), lastDuration(0){};
52};
53
54struct DurationBucket {
55    int64_t mBucketStartNs;
56    int64_t mBucketEndNs;
57    int64_t mDuration;
58};
59
60class DurationTracker {
61public:
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
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
116    void setEventKey(const MetricDimensionKey& eventKey) {
117         mEventKey = eventKey;
118    }
119
120protected:
121    int64_t getCurrentBucketEndTimeNs() const {
122        return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
123    }
124
125    // Starts the anomaly alarm.
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.
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
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
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.
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