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
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <stdio.h>
20
21 #include <set>
22 #include <unordered_map>
23 #include <vector>
24
25 #include "metrics_test_helper.h"
26 #include "src/condition/ConditionWizard.h"
27 #include "src/stats_log_util.h"
28 #include "stats_event.h"
29 #include "tests/statsd_test_util.h"
30
31 using namespace android::os::statsd;
32 using namespace testing;
33 using android::sp;
34 using std::set;
35 using std::unordered_map;
36 using std::vector;
37
38 #ifdef __ANDROID__
39
40 namespace android {
41 namespace os {
42 namespace statsd {
43
44
45 namespace {
46
47 const ConfigKey kConfigKey(0, 12345);
48 const uint64_t protoHash = 0x1234567890;
makeLogEvent(LogEvent * logEvent,int64_t timestampNs,int atomId)49 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
50 AStatsEvent* statsEvent = AStatsEvent_obtain();
51 AStatsEvent_setAtomId(statsEvent, atomId);
52 AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
53
54 parseStatsEventToLogEvent(statsEvent, logEvent);
55 }
56
57 } // namespace
58
59 // Setup for parameterized tests.
60 class DurationMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
61
62 INSTANTIATE_TEST_SUITE_P(DurationMetricProducerTest_PartialBucket,
63 DurationMetricProducerTest_PartialBucket,
64 testing::Values(APP_UPGRADE, BOOT_COMPLETE));
65
TEST(DurationMetricTrackerTest,TestFirstBucket)66 TEST(DurationMetricTrackerTest, TestFirstBucket) {
67 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
68 DurationMetric metric;
69 metric.set_id(1);
70 metric.set_bucket(ONE_MINUTE);
71 metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
72
73 FieldMatcher dimensions;
74
75 DurationMetricProducer durationProducer(
76 kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
77 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
78 wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
79
80 EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
81 EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
82 EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs());
83 }
84
TEST(DurationMetricTrackerTest,TestNoCondition)85 TEST(DurationMetricTrackerTest, TestNoCondition) {
86 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
87 int64_t bucketStartTimeNs = 10000000000;
88 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
89
90 DurationMetric metric;
91 metric.set_id(1);
92 metric.set_bucket(ONE_MINUTE);
93 metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
94
95 int tagId = 1;
96 LogEvent event1(/*uid=*/0, /*pid=*/0);
97 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
98 LogEvent event2(/*uid=*/0, /*pid=*/0);
99 makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId);
100
101 FieldMatcher dimensions;
102
103 DurationMetricProducer durationProducer(
104 kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
105 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
106 wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
107
108 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
109 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
110 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
111 ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
112 EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
113 durationProducer.mPastBuckets.end());
114 const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
115 ASSERT_EQ(2UL, buckets.size());
116 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
117 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
118 EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration);
119 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
120 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs);
121 EXPECT_EQ(2LL, buckets[1].mDuration);
122 }
123
TEST(DurationMetricTrackerTest,TestNonSlicedCondition)124 TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
125 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
126 int64_t bucketStartTimeNs = 10000000000;
127 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
128
129 DurationMetric metric;
130 metric.set_id(1);
131 metric.set_bucket(ONE_MINUTE);
132 metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
133
134 int tagId = 1;
135 LogEvent event1(/*uid=*/0, /*pid=*/0);
136 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
137 LogEvent event2(/*uid=*/0, /*pid=*/0);
138 makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
139 LogEvent event3(/*uid=*/0, /*pid=*/0);
140 makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
141 LogEvent event4(/*uid=*/0, /*pid=*/0);
142 makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
143
144 FieldMatcher dimensions;
145
146 DurationMetricProducer durationProducer(
147 kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
148 -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
149 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
150 bucketStartTimeNs, bucketStartTimeNs);
151 durationProducer.mCondition = ConditionState::kFalse;
152
153 assertConditionTimer(durationProducer.mConditionTimer, false, 0, 0);
154 EXPECT_FALSE(durationProducer.mCondition);
155 EXPECT_FALSE(durationProducer.isConditionSliced());
156
157 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
158 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
159 durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
160 ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
161
162 int64_t conditionStartTimeNs = bucketStartTimeNs + bucketSizeNs + 2;
163 int64_t bucket2EndTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
164 durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
165 durationProducer.onConditionChanged(true /* condition */, conditionStartTimeNs);
166 assertConditionTimer(durationProducer.mConditionTimer, true, 0, conditionStartTimeNs);
167 durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
168 durationProducer.flushIfNeededLocked(bucket2EndTimeNs + 1);
169 assertConditionTimer(durationProducer.mConditionTimer, true, 0, bucket2EndTimeNs,
170 /*currentBucketStartDelayNs=*/1);
171 ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
172 EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
173 durationProducer.mPastBuckets.end());
174 const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
175 ASSERT_EQ(1UL, buckets2.size());
176 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
177 EXPECT_EQ(bucket2EndTimeNs, buckets2[0].mBucketEndNs);
178 EXPECT_EQ(1LL, buckets2[0].mDuration);
179 EXPECT_EQ(bucket2EndTimeNs - conditionStartTimeNs, buckets2[0].mConditionTrueNs);
180 }
181
TEST(DurationMetricTrackerTest,TestNonSlicedConditionUnknownState)182 TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
183 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
184 int64_t bucketStartTimeNs = 10000000000;
185 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
186
187 DurationMetric metric;
188 metric.set_id(1);
189 metric.set_bucket(ONE_MINUTE);
190 metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
191
192 int tagId = 1;
193 LogEvent event1(/*uid=*/0, /*pid=*/0);
194 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
195 LogEvent event2(/*uid=*/0, /*pid=*/0);
196 makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
197 LogEvent event3(/*uid=*/0, /*pid=*/0);
198 makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
199 LogEvent event4(/*uid=*/0, /*pid=*/0);
200 makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
201
202 FieldMatcher dimensions;
203
204 DurationMetricProducer durationProducer(
205 kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
206 -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
207 3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
208 bucketStartTimeNs, bucketStartTimeNs);
209
210 EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
211 EXPECT_FALSE(durationProducer.isConditionSliced());
212
213 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
214 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
215 durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
216 ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
217
218 durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
219 durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
220 durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
221 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
222 ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
223 const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
224 ASSERT_EQ(1UL, buckets2.size());
225 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
226 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
227 EXPECT_EQ(1LL, buckets2[0].mDuration);
228 }
229
TEST_P(DurationMetricProducerTest_PartialBucket,TestSumDuration)230 TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) {
231 /**
232 * The duration starts from the first bucket, through the two partial buckets (10-70sec),
233 * another bucket, and ends at the beginning of the next full bucket.
234 * Expected buckets:
235 * - [10,25]: 14 secs
236 * - [25,70]: All 45 secs
237 * - [70,130]: All 60 secs
238 * - [130, 210]: Only 5 secs (event ended at 135sec)
239 */
240 int64_t bucketStartTimeNs = 10000000000;
241 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
242 int tagId = 1;
243
244 DurationMetric metric;
245 metric.set_id(1);
246 metric.set_bucket(ONE_MINUTE);
247 metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
248 metric.set_split_bucket_for_app_upgrade(true);
249 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
250 FieldMatcher dimensions;
251
252 DurationMetricProducer durationProducer(
253 kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
254 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
255 wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
256
257 int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
258 LogEvent event1(/*uid=*/0, /*pid=*/0);
259 makeLogEvent(&event1, startTimeNs, tagId);
260 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
261 ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
262 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
263
264 int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
265 switch (GetParam()) {
266 case APP_UPGRADE:
267 durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
268 break;
269 case BOOT_COMPLETE:
270 durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
271 break;
272 }
273 ASSERT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
274 std::vector<DurationBucket> buckets =
275 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
276 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
277 EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketEndNs);
278 EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration);
279 EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
280 EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
281
282 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
283 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
284 LogEvent event2(/*uid=*/0, /*pid=*/0);
285 makeLogEvent(&event2, endTimeNs, tagId);
286 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
287 buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
288 ASSERT_EQ(3UL, buckets.size());
289 EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketStartNs);
290 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
291 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, buckets[1].mDuration);
292 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
293 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
294 EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
295 }
296
TEST_P(DurationMetricProducerTest_PartialBucket,TestSumDurationWithSplitInFollowingBucket)297 TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollowingBucket) {
298 /**
299 * Expected buckets (start at 11s, upgrade at 75s, end at 135s):
300 * - [10,70]: 59 secs
301 * - [70,75]: 5 sec
302 * - [75,130]: 55 secs
303 */
304 int64_t bucketStartTimeNs = 10000000000;
305 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
306 int tagId = 1;
307
308 DurationMetric metric;
309 metric.set_id(1);
310 metric.set_bucket(ONE_MINUTE);
311 metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
312 metric.set_split_bucket_for_app_upgrade(true);
313 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
314 FieldMatcher dimensions;
315
316 DurationMetricProducer durationProducer(
317 kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
318 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
319 wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
320
321 int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
322 LogEvent event1(/*uid=*/0, /*pid=*/0);
323 makeLogEvent(&event1, startTimeNs, tagId);
324 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
325 ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
326 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
327
328 int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
329 switch (GetParam()) {
330 case APP_UPGRADE:
331 durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
332 break;
333 case BOOT_COMPLETE:
334 durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
335 break;
336 }
337 ASSERT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
338 std::vector<DurationBucket> buckets =
339 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
340 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
341 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
342 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
343 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
344 EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketEndNs);
345 EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
346 EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
347 EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
348
349 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
350 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
351 LogEvent event2(/*uid=*/0, /*pid=*/0);
352 makeLogEvent(&event2, endTimeNs, tagId);
353 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
354 buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
355 ASSERT_EQ(3UL, buckets.size());
356 EXPECT_EQ(partialBucketSplitTimeNs, buckets[2].mBucketStartNs);
357 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
358 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs,
359 buckets[2].mDuration);
360 }
361
TEST_P(DurationMetricProducerTest_PartialBucket,TestSumDurationAnomaly)362 TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) {
363 sp<AlarmMonitor> alarmMonitor;
364 int64_t bucketStartTimeNs = 10000000000;
365 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
366 int tagId = 1;
367
368 // Setup metric with alert.
369 DurationMetric metric;
370 metric.set_id(1);
371 metric.set_bucket(ONE_MINUTE);
372 metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
373 metric.set_split_bucket_for_app_upgrade(true);
374 Alert alert;
375 alert.set_num_buckets(3);
376 alert.set_trigger_if_sum_gt(2);
377
378 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
379 FieldMatcher dimensions;
380
381 DurationMetricProducer durationProducer(
382 kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
383 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
384 wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
385
386 sp<AnomalyTracker> anomalyTracker =
387 durationProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
388 EXPECT_TRUE(anomalyTracker != nullptr);
389
390 int64_t startTimeNs = bucketStartTimeNs + 1;
391 LogEvent event1(/*uid=*/0, /*pid=*/0);
392 makeLogEvent(&event1, startTimeNs, tagId);
393 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
394
395 int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
396 switch (GetParam()) {
397 case APP_UPGRADE:
398 durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
399 break;
400 case BOOT_COMPLETE:
401 durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
402 break;
403 }
404
405 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
406 int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
407 LogEvent event2(/*uid=*/0, /*pid=*/0);
408 makeLogEvent(&event2, endTimeNs, tagId);
409 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
410
411 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
412 anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
413 }
414
TEST_P(DurationMetricProducerTest_PartialBucket,TestMaxDuration)415 TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) {
416 int64_t bucketStartTimeNs = 10000000000;
417 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
418 int tagId = 1;
419
420 DurationMetric metric;
421 metric.set_id(1);
422 metric.set_bucket(ONE_MINUTE);
423 metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
424 metric.set_split_bucket_for_app_upgrade(true);
425
426 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
427 FieldMatcher dimensions;
428
429 DurationMetricProducer durationProducer(
430 kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
431 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
432 wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
433
434 int64_t startTimeNs = bucketStartTimeNs + 1;
435 LogEvent event1(/*uid=*/0, /*pid=*/0);
436 makeLogEvent(&event1, startTimeNs, tagId);
437 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
438 ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
439 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
440
441 int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
442 switch (GetParam()) {
443 case APP_UPGRADE:
444 durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
445 break;
446 case BOOT_COMPLETE:
447 durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
448 break;
449 }
450 ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
451 EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
452 EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
453
454 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
455 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
456 LogEvent event2(/*uid=*/0, /*pid=*/0);
457 makeLogEvent(&event2, endTimeNs, tagId);
458 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
459 ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
460
461 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
462 std::vector<DurationBucket> buckets =
463 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
464 ASSERT_EQ(1UL, buckets.size());
465 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs);
466 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs);
467 EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
468 }
469
TEST_P(DurationMetricProducerTest_PartialBucket,TestMaxDurationWithSplitInNextBucket)470 TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) {
471 int64_t bucketStartTimeNs = 10000000000;
472 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
473 int tagId = 1;
474
475 DurationMetric metric;
476 metric.set_id(1);
477 metric.set_bucket(ONE_MINUTE);
478 metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
479 metric.set_split_bucket_for_app_upgrade(true);
480
481 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
482 FieldMatcher dimensions;
483
484 DurationMetricProducer durationProducer(
485 kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
486 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
487 wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
488
489 int64_t startTimeNs = bucketStartTimeNs + 1;
490 LogEvent event1(/*uid=*/0, /*pid=*/0);
491 makeLogEvent(&event1, startTimeNs, tagId);
492 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
493 ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
494 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
495
496 int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
497 switch (GetParam()) {
498 case APP_UPGRADE:
499 durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
500 break;
501 case BOOT_COMPLETE:
502 durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
503 break;
504 }
505 ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
506 EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
507 EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
508
509 // Stop occurs in the same partial bucket as created for the app upgrade.
510 int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
511 LogEvent event2(/*uid=*/0, /*pid=*/0);
512 makeLogEvent(&event2, endTimeNs, tagId);
513 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
514 ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
515 EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
516
517 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
518 std::vector<DurationBucket> buckets =
519 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
520 ASSERT_EQ(1UL, buckets.size());
521 EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketStartNs);
522 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
523 EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
524 }
525
TEST(DurationMetricProducerTest,TestSumDurationAppUpgradeSplitDisabled)526 TEST(DurationMetricProducerTest, TestSumDurationAppUpgradeSplitDisabled) {
527 /**
528 * The duration starts from the first bucket, through one full bucket (10-70sec).
529 * The app upgrade should not split a partial bucket.
530 * Expected buckets:
531 * - [10,70]: All 60 secs
532 * - [70, 75]: Only 5 secs (event ended at 75sec)
533 */
534 int64_t bucketStartTimeNs = 10000000000;
535 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
536 int tagId = 1;
537
538 DurationMetric metric;
539 metric.set_id(1);
540 metric.set_bucket(ONE_MINUTE);
541 metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
542 metric.set_split_bucket_for_app_upgrade(false);
543 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
544 FieldMatcher dimensions;
545
546 DurationMetricProducer durationProducer(
547 kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
548 1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
549 wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
550
551 int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
552 LogEvent event1(/*uid=*/0, /*pid=*/0);
553 makeLogEvent(&event1, startTimeNs, tagId);
554 durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
555 ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
556 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
557
558 int64_t appUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
559 durationProducer.notifyAppUpgrade(appUpgradeTimeNs);
560
561 ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
562 EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
563
564 // We skip ahead one bucket, so we fill in one full bucket and expect 0 partial buckets.
565 int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
566 LogEvent event2(/*uid=*/0, /*pid=*/0);
567 makeLogEvent(&event2, endTimeNs, tagId);
568 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
569 ASSERT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
570 DurationBucket bucket = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0];
571 EXPECT_EQ(bucketStartTimeNs, bucket.mBucketStartNs);
572 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucket.mBucketEndNs);
573 EXPECT_EQ(bucketSizeNs - 1 * NS_PER_SEC, bucket.mDuration);
574 EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
575 }
576
577 } // namespace statsd
578 } // namespace os
579 } // namespace android
580 #else
581 GTEST_LOG_(INFO) << "This test does nothing.\n";
582 #endif
583