• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/metrics/DurationMetricProducer.h"
16 #include "src/stats_log_util.h"
17 #include "metrics_test_helper.h"
18 #include "src/condition/ConditionWizard.h"
19 
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 #include <stdio.h>
23 #include <set>
24 #include <unordered_map>
25 #include <vector>
26 
27 using namespace android::os::statsd;
28 using namespace testing;
29 using android::sp;
30 using std::set;
31 using std::unordered_map;
32 using std::vector;
33 
34 #ifdef __ANDROID__
35 
36 namespace android {
37 namespace os {
38 namespace statsd {
39 
40 const ConfigKey kConfigKey(0, 12345);
41 
TEST(DurationMetricTrackerTest,TestNoCondition)42 TEST(DurationMetricTrackerTest, TestNoCondition) {
43     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
44     int64_t bucketStartTimeNs = 10000000000;
45     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
46 
47     DurationMetric metric;
48     metric.set_id(1);
49     metric.set_bucket(ONE_MINUTE);
50     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
51 
52     int tagId = 1;
53     LogEvent event1(tagId, bucketStartTimeNs + 1);
54     event1.init();
55     LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2);
56     event2.init();
57 
58     FieldMatcher dimensions;
59     DurationMetricProducer durationProducer(
60             kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
61             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
62     durationProducer.setBucketSize(60 * NS_PER_SEC);
63 
64     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
65     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
66     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
67     EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
68     EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
69                 durationProducer.mPastBuckets.end());
70     const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
71     EXPECT_EQ(2UL, buckets.size());
72     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
73     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
74     EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration);
75     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
76     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs);
77     EXPECT_EQ(2LL, buckets[1].mDuration);
78 }
79 
TEST(DurationMetricTrackerTest,TestNonSlicedCondition)80 TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
81     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
82     int64_t bucketStartTimeNs = 10000000000;
83     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
84 
85     DurationMetric metric;
86     metric.set_id(1);
87     metric.set_bucket(ONE_MINUTE);
88     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
89 
90     int tagId = 1;
91     LogEvent event1(tagId, bucketStartTimeNs + 1);
92     event1.init();
93     LogEvent event2(tagId, bucketStartTimeNs + 2);
94     event2.init();
95     LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
96     event3.init();
97     LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
98     event4.init();
99 
100     FieldMatcher dimensions;
101     DurationMetricProducer durationProducer(
102             kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
103             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
104     durationProducer.setBucketSize(60 * NS_PER_SEC);
105 
106     EXPECT_FALSE(durationProducer.mCondition);
107     EXPECT_FALSE(durationProducer.isConditionSliced());
108 
109     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
110     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
111     durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
112     EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
113 
114     durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
115     durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
116     durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
117     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
118     EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
119     EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
120                 durationProducer.mPastBuckets.end());
121     const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
122     EXPECT_EQ(1UL, buckets2.size());
123     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
124     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
125     EXPECT_EQ(1LL, buckets2[0].mDuration);
126 }
127 
TEST(DurationMetricTrackerTest,TestSumDurationWithUpgrade)128 TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
129     /**
130      * The duration starts from the first bucket, through the two partial buckets (10-70sec),
131      * another bucket, and ends at the beginning of the next full bucket.
132      * Expected buckets:
133      *  - [10,25]: 14 secs
134      *  - [25,70]: All 45 secs
135      *  - [70,130]: All 60 secs
136      *  - [130, 210]: Only 5 secs (event ended at 135sec)
137      */
138     int64_t bucketStartTimeNs = 10000000000;
139     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
140     int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
141     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
142     int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
143 
144     int tagId = 1;
145 
146     DurationMetric metric;
147     metric.set_id(1);
148     metric.set_bucket(ONE_MINUTE);
149     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
150     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
151     FieldMatcher dimensions;
152     DurationMetricProducer durationProducer(
153             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
154             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
155     durationProducer.setBucketSize(60 * NS_PER_SEC);
156 
157     LogEvent start_event(tagId, startTimeNs);
158     start_event.init();
159     durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
160     EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
161     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
162 
163     durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
164     EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
165     std::vector<DurationBucket> buckets =
166             durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
167     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
168     EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs);
169     EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration);
170     EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
171 
172     // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
173     LogEvent end_event(tagId, endTimeNs);
174     end_event.init();
175     durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
176     buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
177     EXPECT_EQ(3UL, buckets.size());
178     EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs);
179     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
180     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration);
181     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
182     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
183     EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
184 }
185 
TEST(DurationMetricTrackerTest,TestSumDurationWithUpgradeInFollowingBucket)186 TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
187     /**
188      * Expected buckets (start at 11s, upgrade at 75s, end at 135s):
189      *  - [10,70]: 59 secs
190      *  - [70,75]: 5 sec
191      *  - [75,130]: 55 secs
192      */
193     int64_t bucketStartTimeNs = 10000000000;
194     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
195     int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
196     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
197     int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
198 
199     int tagId = 1;
200 
201     DurationMetric metric;
202     metric.set_id(1);
203     metric.set_bucket(ONE_MINUTE);
204     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
205     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
206     FieldMatcher dimensions;
207     DurationMetricProducer durationProducer(
208             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
209             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
210     durationProducer.setBucketSize(60 * NS_PER_SEC);
211 
212     LogEvent start_event(tagId, startTimeNs);
213     start_event.init();
214     durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
215     EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
216     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
217 
218     durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
219     EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
220     std::vector<DurationBucket> buckets =
221             durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
222     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
223     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
224     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
225     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
226     EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs);
227     EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
228     EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
229 
230     // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
231     LogEvent end_event(tagId, endTimeNs);
232     end_event.init();
233     durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
234     buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
235     EXPECT_EQ(3UL, buckets.size());
236     EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs);
237     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
238     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration);
239 }
240 
TEST(DurationMetricTrackerTest,TestSumDurationAnomalyWithUpgrade)241 TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
242     sp<AlarmMonitor> alarmMonitor;
243     int64_t bucketStartTimeNs = 10000000000;
244     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
245     int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
246     int64_t startTimeNs = bucketStartTimeNs + 1;
247     int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
248 
249     int tagId = 1;
250 
251     // Setup metric with alert.
252     DurationMetric metric;
253     metric.set_id(1);
254     metric.set_bucket(ONE_MINUTE);
255     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
256     Alert alert;
257     alert.set_num_buckets(3);
258     alert.set_trigger_if_sum_gt(2);
259 
260     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
261     FieldMatcher dimensions;
262     DurationMetricProducer durationProducer(
263             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
264             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
265     durationProducer.setBucketSize(60 * NS_PER_SEC);
266 
267     sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
268     EXPECT_TRUE(anomalyTracker != nullptr);
269 
270     LogEvent start_event(tagId, startTimeNs);
271     start_event.init();
272     durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
273     durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
274     // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
275     LogEvent end_event(tagId, endTimeNs);
276     end_event.init();
277     durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
278 
279     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
280               anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
281 }
282 
TEST(DurationMetricTrackerTest,TestMaxDurationWithUpgrade)283 TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
284     int64_t bucketStartTimeNs = 10000000000;
285     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
286     int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
287     int64_t startTimeNs = bucketStartTimeNs + 1;
288     int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
289 
290     int tagId = 1;
291 
292     DurationMetric metric;
293     metric.set_id(1);
294     metric.set_bucket(ONE_MINUTE);
295     metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
296     LogEvent event1(tagId, startTimeNs);
297     event1.write("111");  // uid
298     event1.init();
299     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
300     FieldMatcher dimensions;
301     DurationMetricProducer durationProducer(
302             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
303             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
304     durationProducer.setBucketSize(60 * NS_PER_SEC);
305 
306     LogEvent start_event(tagId, startTimeNs);
307     start_event.init();
308     durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
309     EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
310     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
311 
312     durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
313     EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
314     EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
315 
316     // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
317     LogEvent end_event(tagId, endTimeNs);
318     end_event.init();
319     durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
320     EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
321 
322     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
323     std::vector<DurationBucket> buckets =
324             durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
325     EXPECT_EQ(1UL, buckets.size());
326     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs);
327     EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs);
328     EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
329 }
330 
TEST(DurationMetricTrackerTest,TestMaxDurationWithUpgradeInNextBucket)331 TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
332     int64_t bucketStartTimeNs = 10000000000;
333     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
334     int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
335     int64_t startTimeNs = bucketStartTimeNs + 1;
336     int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
337 
338     int tagId = 1;
339 
340     DurationMetric metric;
341     metric.set_id(1);
342     metric.set_bucket(ONE_MINUTE);
343     metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
344     LogEvent event1(tagId, startTimeNs);
345     event1.write("111");  // uid
346     event1.init();
347     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
348     FieldMatcher dimensions;
349     DurationMetricProducer durationProducer(
350             kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
351             3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
352     durationProducer.setBucketSize(60 * NS_PER_SEC);
353 
354     LogEvent start_event(tagId, startTimeNs);
355     start_event.init();
356     durationProducer.onMatchedLogEvent(1 /* start index*/, start_event);
357     EXPECT_EQ(0UL, durationProducer.mPastBuckets.size());
358     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
359 
360     durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
361     EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
362     EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
363 
364     // Stop occurs in the same partial bucket as created for the app upgrade.
365     LogEvent end_event(tagId, endTimeNs);
366     end_event.init();
367     durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event);
368     EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
369     EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs);
370 
371     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
372     std::vector<DurationBucket> buckets =
373             durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
374     EXPECT_EQ(1UL, buckets.size());
375     EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs);
376     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
377     EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
378 }
379 
380 }  // namespace statsd
381 }  // namespace os
382 }  // namespace android
383 #else
384 GTEST_LOG_(INFO) << "This test does nothing.\n";
385 #endif
386