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