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 
onDumpReport(DurationMetricProducer & producer,int64_t dumpTimeNs)57 StatsLogReport onDumpReport(DurationMetricProducer& producer, int64_t dumpTimeNs) {
58     ProtoOutputStream output;
59     set<int32_t> usedUids;
60     producer.onDumpReport(dumpTimeNs, true /*include current partial bucket*/, true /*erase data*/,
61                           FAST, nullptr, usedUids, &output);
62     return outputStreamToProto(&output);
63 }
64 
65 }  // namespace
66 
67 // Setup for parameterized tests.
68 class DurationMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
69 
70 INSTANTIATE_TEST_SUITE_P(DurationMetricProducerTest_PartialBucket,
71                          DurationMetricProducerTest_PartialBucket,
72                          testing::Values(APP_UPGRADE, BOOT_COMPLETE));
73 
TEST(DurationMetricTrackerTest,TestFirstBucket)74 TEST(DurationMetricTrackerTest, TestFirstBucket) {
75     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
76     DurationMetric metric;
77     metric.set_id(1);
78     metric.set_bucket(ONE_MINUTE);
79     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
80 
81     FieldMatcher dimensions;
82     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
83 
84     DurationMetricProducer durationProducer(
85             kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
86             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
87             wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, provider);
88 
89     EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
90     EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
91     EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs());
92 }
93 
TEST(DurationMetricTrackerTest,TestNoCondition)94 TEST(DurationMetricTrackerTest, TestNoCondition) {
95     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
96     int64_t bucketStartTimeNs = 10000000000;
97     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
98 
99     DurationMetric metric;
100     metric.set_id(1);
101     metric.set_bucket(ONE_MINUTE);
102     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
103 
104     int tagId = 1;
105     LogEvent event1(/*uid=*/0, /*pid=*/0);
106     makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
107     LogEvent event2(/*uid=*/0, /*pid=*/0);
108     makeLogEvent(&event2, bucketStartTimeNs + bucketSizeNs + 2, tagId);
109 
110     FieldMatcher dimensions;
111     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
112 
113     DurationMetricProducer durationProducer(
114             kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
115             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
116             wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
117 
118     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
119     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
120     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
121     ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
122     EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
123                 durationProducer.mPastBuckets.end());
124     const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
125     ASSERT_EQ(2UL, buckets.size());
126     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
127     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
128     EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration);
129     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
130     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs);
131     EXPECT_EQ(2LL, buckets[1].mDuration);
132 }
133 
TEST(DurationMetricTrackerTest,TestNonSlicedCondition)134 TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
135     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
136     int64_t bucketStartTimeNs = 10000000000;
137     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
138 
139     DurationMetric metric;
140     metric.set_id(1);
141     metric.set_bucket(ONE_MINUTE);
142     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
143 
144     int tagId = 1;
145     LogEvent event1(/*uid=*/0, /*pid=*/0);
146     makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
147     LogEvent event2(/*uid=*/0, /*pid=*/0);
148     makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
149     LogEvent event3(/*uid=*/0, /*pid=*/0);
150     makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
151     LogEvent event4(/*uid=*/0, /*pid=*/0);
152     makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
153 
154     FieldMatcher dimensions;
155     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
156 
157     DurationMetricProducer durationProducer(
158             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
159             -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
160             3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
161             bucketStartTimeNs, bucketStartTimeNs, provider);
162     durationProducer.mCondition = ConditionState::kFalse;
163 
164     assertConditionTimer(durationProducer.mConditionTimer, false, 0, 0);
165     EXPECT_FALSE(durationProducer.mCondition);
166     EXPECT_FALSE(durationProducer.isConditionSliced());
167 
168     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
169     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
170     durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
171     ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
172 
173     int64_t conditionStartTimeNs = bucketStartTimeNs + bucketSizeNs + 2;
174     int64_t bucket2EndTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
175     durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
176     durationProducer.onConditionChanged(true /* condition */, conditionStartTimeNs);
177     assertConditionTimer(durationProducer.mConditionTimer, true, 0, conditionStartTimeNs);
178     durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
179     durationProducer.flushIfNeededLocked(bucket2EndTimeNs + 1);
180     assertConditionTimer(durationProducer.mConditionTimer, true, 0, bucket2EndTimeNs,
181                          /*currentBucketStartDelayNs=*/1);
182     ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
183     EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
184                 durationProducer.mPastBuckets.end());
185     const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
186     ASSERT_EQ(1UL, buckets2.size());
187     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
188     EXPECT_EQ(bucket2EndTimeNs, buckets2[0].mBucketEndNs);
189     EXPECT_EQ(1LL, buckets2[0].mDuration);
190     EXPECT_EQ(bucket2EndTimeNs - conditionStartTimeNs, buckets2[0].mConditionTrueNs);
191 }
192 
TEST(DurationMetricTrackerTest,TestNonSlicedConditionUnknownState)193 TEST(DurationMetricTrackerTest, TestNonSlicedConditionUnknownState) {
194     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
195     int64_t bucketStartTimeNs = 10000000000;
196     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
197 
198     DurationMetric metric;
199     metric.set_id(1);
200     metric.set_bucket(ONE_MINUTE);
201     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
202 
203     int tagId = 1;
204     LogEvent event1(/*uid=*/0, /*pid=*/0);
205     makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
206     LogEvent event2(/*uid=*/0, /*pid=*/0);
207     makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
208     LogEvent event3(/*uid=*/0, /*pid=*/0);
209     makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 1, tagId);
210     LogEvent event4(/*uid=*/0, /*pid=*/0);
211     makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 3, tagId);
212 
213     FieldMatcher dimensions;
214     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
215 
216     DurationMetricProducer durationProducer(
217             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
218             -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
219             3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
220             bucketStartTimeNs, bucketStartTimeNs, provider);
221 
222     EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
223     EXPECT_FALSE(durationProducer.isConditionSliced());
224 
225     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
226     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
227     durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
228     ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
229 
230     durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
231     durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2);
232     durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
233     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
234     ASSERT_EQ(1UL, durationProducer.mPastBuckets.size());
235     const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
236     ASSERT_EQ(1UL, buckets2.size());
237     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
238     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
239     EXPECT_EQ(1LL, buckets2[0].mDuration);
240 }
241 
TEST_P(DurationMetricProducerTest_PartialBucket,TestSumDuration)242 TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDuration) {
243     /**
244      * The duration starts from the first bucket, through the two partial buckets (10-70sec),
245      * another bucket, and ends at the beginning of the next full bucket.
246      * Expected buckets:
247      *  - [10,25]: 14 secs
248      *  - [25,70]: All 45 secs
249      *  - [70,130]: All 60 secs
250      *  - [130, 210]: Only 5 secs (event ended at 135sec)
251      */
252     int64_t bucketStartTimeNs = 10000000000;
253     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
254     int tagId = 1;
255 
256     DurationMetric metric;
257     metric.set_id(1);
258     metric.set_bucket(ONE_MINUTE);
259     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
260     metric.set_split_bucket_for_app_upgrade(true);
261     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
262     FieldMatcher dimensions;
263     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
264 
265     DurationMetricProducer durationProducer(
266             kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
267             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
268             wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
269 
270     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
271     LogEvent event1(/*uid=*/0, /*pid=*/0);
272     makeLogEvent(&event1, startTimeNs, tagId);
273     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
274     ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
275     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
276 
277     int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
278     switch (GetParam()) {
279         case APP_UPGRADE:
280             durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
281             break;
282         case BOOT_COMPLETE:
283             durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
284             break;
285     }
286     ASSERT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
287     std::vector<DurationBucket> buckets =
288             durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
289     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
290     EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketEndNs);
291     EXPECT_EQ(partialBucketSplitTimeNs - startTimeNs, buckets[0].mDuration);
292     EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
293     EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
294 
295     // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
296     int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
297     LogEvent event2(/*uid=*/0, /*pid=*/0);
298     makeLogEvent(&event2, endTimeNs, tagId);
299     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
300     buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
301     ASSERT_EQ(3UL, buckets.size());
302     EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketStartNs);
303     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs);
304     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - partialBucketSplitTimeNs, buckets[1].mDuration);
305     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs);
306     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
307     EXPECT_EQ(bucketSizeNs, buckets[2].mDuration);
308 }
309 
TEST_P(DurationMetricProducerTest_PartialBucket,TestSumDurationWithSplitInFollowingBucket)310 TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationWithSplitInFollowingBucket) {
311     /**
312      * Expected buckets (start at 11s, upgrade at 75s, end at 135s):
313      *  - [10,70]: 59 secs
314      *  - [70,75]: 5 sec
315      *  - [75,130]: 55 secs
316      */
317     int64_t bucketStartTimeNs = 10000000000;
318     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
319     int tagId = 1;
320 
321     DurationMetric metric;
322     metric.set_id(1);
323     metric.set_bucket(ONE_MINUTE);
324     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
325     metric.set_split_bucket_for_app_upgrade(true);
326     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
327     FieldMatcher dimensions;
328     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
329 
330     DurationMetricProducer durationProducer(
331             kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
332             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
333             wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
334 
335     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
336     LogEvent event1(/*uid=*/0, /*pid=*/0);
337     makeLogEvent(&event1, startTimeNs, tagId);
338     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
339     ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
340     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
341 
342     int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
343     switch (GetParam()) {
344         case APP_UPGRADE:
345             durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
346             break;
347         case BOOT_COMPLETE:
348             durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
349             break;
350     }
351     ASSERT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
352     std::vector<DurationBucket> buckets =
353             durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
354     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
355     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
356     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration);
357     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs);
358     EXPECT_EQ(partialBucketSplitTimeNs, buckets[1].mBucketEndNs);
359     EXPECT_EQ(partialBucketSplitTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration);
360     EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
361     EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
362 
363     // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
364     int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
365     LogEvent event2(/*uid=*/0, /*pid=*/0);
366     makeLogEvent(&event2, endTimeNs, tagId);
367     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
368     buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
369     ASSERT_EQ(3UL, buckets.size());
370     EXPECT_EQ(partialBucketSplitTimeNs, buckets[2].mBucketStartNs);
371     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs);
372     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - partialBucketSplitTimeNs,
373               buckets[2].mDuration);
374 }
375 
TEST_P(DurationMetricProducerTest_PartialBucket,TestSumDurationAnomaly)376 TEST_P(DurationMetricProducerTest_PartialBucket, TestSumDurationAnomaly) {
377     sp<AlarmMonitor> alarmMonitor;
378     int64_t bucketStartTimeNs = 10000000000;
379     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
380     int tagId = 1;
381 
382     // Setup metric with alert.
383     DurationMetric metric;
384     metric.set_id(1);
385     metric.set_bucket(ONE_MINUTE);
386     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
387     metric.set_split_bucket_for_app_upgrade(true);
388     Alert alert;
389     alert.set_num_buckets(3);
390     alert.set_trigger_if_sum_gt(2);
391 
392     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
393     FieldMatcher dimensions;
394     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
395 
396     DurationMetricProducer durationProducer(
397             kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
398             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
399             wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
400 
401     sp<AnomalyTracker> anomalyTracker =
402             durationProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
403     EXPECT_TRUE(anomalyTracker != nullptr);
404 
405     int64_t startTimeNs = bucketStartTimeNs + 1;
406     LogEvent event1(/*uid=*/0, /*pid=*/0);
407     makeLogEvent(&event1, startTimeNs, tagId);
408     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
409 
410     int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
411     switch (GetParam()) {
412         case APP_UPGRADE:
413             durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
414             break;
415         case BOOT_COMPLETE:
416             durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
417             break;
418     }
419 
420     // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
421     int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
422     LogEvent event2(/*uid=*/0, /*pid=*/0);
423     makeLogEvent(&event2, endTimeNs, tagId);
424     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
425 
426     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs,
427               anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
428 }
429 
TEST_P(DurationMetricProducerTest_PartialBucket,TestMaxDuration)430 TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDuration) {
431     int64_t bucketStartTimeNs = 10000000000;
432     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
433     int tagId = 1;
434 
435     DurationMetric metric;
436     metric.set_id(1);
437     metric.set_bucket(ONE_MINUTE);
438     metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
439     metric.set_split_bucket_for_app_upgrade(true);
440 
441     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
442     FieldMatcher dimensions;
443     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
444 
445     DurationMetricProducer durationProducer(
446             kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
447             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
448             wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
449 
450     int64_t startTimeNs = bucketStartTimeNs + 1;
451     LogEvent event1(/*uid=*/0, /*pid=*/0);
452     makeLogEvent(&event1, startTimeNs, tagId);
453     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
454     ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
455     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
456 
457     int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
458     switch (GetParam()) {
459         case APP_UPGRADE:
460             durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
461             break;
462         case BOOT_COMPLETE:
463             durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
464             break;
465     }
466     ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
467     EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
468     EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
469 
470     // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket.
471     int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC;
472     LogEvent event2(/*uid=*/0, /*pid=*/0);
473     makeLogEvent(&event2, endTimeNs, tagId);
474     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
475     ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
476 
477     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
478     std::vector<DurationBucket> buckets =
479             durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
480     ASSERT_EQ(1UL, buckets.size());
481     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs);
482     EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs);
483     EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
484 }
485 
TEST_P(DurationMetricProducerTest_PartialBucket,TestMaxDurationWithSplitInNextBucket)486 TEST_P(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket) {
487     int64_t bucketStartTimeNs = 10000000000;
488     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
489     int tagId = 1;
490 
491     DurationMetric metric;
492     metric.set_id(1);
493     metric.set_bucket(ONE_MINUTE);
494     metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
495     metric.set_split_bucket_for_app_upgrade(true);
496 
497     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
498     FieldMatcher dimensions;
499     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
500 
501     DurationMetricProducer durationProducer(
502             kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
503             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
504             wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
505 
506     int64_t startTimeNs = bucketStartTimeNs + 1;
507     LogEvent event1(/*uid=*/0, /*pid=*/0);
508     makeLogEvent(&event1, startTimeNs, tagId);
509     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
510     ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
511     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
512 
513     int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
514     switch (GetParam()) {
515         case APP_UPGRADE:
516             durationProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
517             break;
518         case BOOT_COMPLETE:
519             durationProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
520             break;
521     }
522     ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
523     EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
524     EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
525 
526     // Stop occurs in the same partial bucket as created for the app upgrade.
527     int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC;
528     LogEvent event2(/*uid=*/0, /*pid=*/0);
529     makeLogEvent(&event2, endTimeNs, tagId);
530     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
531     ASSERT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
532     EXPECT_EQ(partialBucketSplitTimeNs, durationProducer.mCurrentBucketStartTimeNs);
533 
534     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
535     std::vector<DurationBucket> buckets =
536             durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
537     ASSERT_EQ(1UL, buckets.size());
538     EXPECT_EQ(partialBucketSplitTimeNs, buckets[0].mBucketStartNs);
539     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs);
540     EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration);
541 }
542 
TEST(DurationMetricProducerTest,TestSumDurationAppUpgradeSplitDisabled)543 TEST(DurationMetricProducerTest, TestSumDurationAppUpgradeSplitDisabled) {
544     /**
545      * The duration starts from the first bucket, through one full bucket (10-70sec).
546      * The app upgrade should not split a partial bucket.
547      * Expected buckets:
548      *  - [10,70]: All 60 secs
549      *  - [70, 75]: Only 5 secs (event ended at 75sec)
550      */
551     int64_t bucketStartTimeNs = 10000000000;
552     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
553     int tagId = 1;
554 
555     DurationMetric metric;
556     metric.set_id(1);
557     metric.set_bucket(ONE_MINUTE);
558     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
559     metric.set_split_bucket_for_app_upgrade(false);
560     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
561     FieldMatcher dimensions;
562     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
563 
564     DurationMetricProducer durationProducer(
565             kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
566             1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
567             wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs, provider);
568 
569     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
570     LogEvent event1(/*uid=*/0, /*pid=*/0);
571     makeLogEvent(&event1, startTimeNs, tagId);
572     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
573     ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
574     EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs);
575 
576     int64_t appUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
577     durationProducer.notifyAppUpgrade(appUpgradeTimeNs);
578 
579     ASSERT_EQ(0UL, durationProducer.mPastBuckets.size());
580     EXPECT_EQ(0, durationProducer.getCurrentBucketNum());
581 
582     // We skip ahead one bucket, so we fill in one full bucket and expect 0 partial buckets.
583     int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC;
584     LogEvent event2(/*uid=*/0, /*pid=*/0);
585     makeLogEvent(&event2, endTimeNs, tagId);
586     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
587     ASSERT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
588     DurationBucket bucket = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0];
589     EXPECT_EQ(bucketStartTimeNs, bucket.mBucketStartNs);
590     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucket.mBucketEndNs);
591     EXPECT_EQ(bucketSizeNs - 1 * NS_PER_SEC, bucket.mDuration);
592     EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
593 }
594 
TEST(DurationMetricProducerTest,TestClearCurrentSlicedTrackerMapWhenStop)595 TEST(DurationMetricProducerTest, TestClearCurrentSlicedTrackerMapWhenStop) {
596     int64_t bucketStartTimeNs = 10000000000;
597     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
598     int tagId = 1;
599 
600     DurationMetric metric;
601     metric.set_id(1);
602     metric.set_bucket(ONE_MINUTE);
603     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
604     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
605     FieldMatcher dimensions;
606     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
607 
608     LogEvent event1(/*uid=*/0, /*pid=*/0);
609     makeLogEvent(&event1, bucketStartTimeNs + 50, tagId);
610     LogEvent event2(/*uid=*/0, /*pid=*/0);
611     makeLogEvent(&event2, bucketStartTimeNs + 100, tagId);
612     LogEvent event3(/*uid=*/0, /*pid=*/0);
613     makeLogEvent(&event3, bucketStartTimeNs + 150, tagId);
614     LogEvent event4(/*uid=*/0, /*pid=*/0);
615     makeLogEvent(&event4, bucketStartTimeNs + bucketSizeNs + 5, tagId);
616 
617     DurationMetricProducer durationProducer(
618             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
619             -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
620             3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
621             bucketStartTimeNs, bucketStartTimeNs, provider);
622 
623     durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + 5);
624     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
625     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
626     durationProducer.onMatchedLogEvent(1 /* start index*/, event3);
627     durationProducer.onConditionChanged(false /* condition */, bucketStartTimeNs + 200);
628     durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
629     durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
630 
631     ASSERT_TRUE(durationProducer.mCurrentSlicedDurationTrackerMap.empty());
632     EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
633     EXPECT_EQ(1, durationProducer.getCurrentBucketNum());
634 }
635 
TEST(DurationMetricProducerTest,TestCorruptedDataReason_WhatLoss)636 TEST(DurationMetricProducerTest, TestCorruptedDataReason_WhatLoss) {
637     const int64_t bucketStartTimeNs = 10000000000;
638     const int tagId = 1;
639 
640     DurationMetric metric;
641     metric.set_id(1);
642     metric.set_bucket(ONE_MINUTE);
643     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
644     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
645     FieldMatcher dimensions;
646     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
647 
648     DurationMetricProducer durationProducer(
649             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
650             -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
651             3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
652             bucketStartTimeNs, bucketStartTimeNs, provider);
653 
654     durationProducer.onMatchedLogEventLost(tagId, DATA_CORRUPTED_SOCKET_LOSS,
655                                            MetricProducer::LostAtomType::kWhat);
656     {
657         // Check dump report content.
658         StatsLogReport report = onDumpReport(durationProducer, bucketStartTimeNs + 50);
659         EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
660     }
661 
662     durationProducer.onMatchedLogEventLost(tagId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW,
663                                            MetricProducer::LostAtomType::kWhat);
664     {
665         // Check dump report content.
666         StatsLogReport report = onDumpReport(durationProducer, bucketStartTimeNs + 150);
667         EXPECT_THAT(report.data_corrupted_reason(),
668                     ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW, DATA_CORRUPTED_SOCKET_LOSS));
669     }
670 }
671 
TEST(DurationMetricProducerTest,TestCorruptedDataReason_ConditionLoss)672 TEST(DurationMetricProducerTest, TestCorruptedDataReason_ConditionLoss) {
673     const int64_t bucketStartTimeNs = 10000000000;
674     const int conditionId = 10;
675 
676     DurationMetric metric;
677     metric.set_id(1);
678     metric.set_bucket(ONE_MINUTE);
679     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
680     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
681     FieldMatcher dimensions;
682     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
683 
684     DurationMetricProducer durationProducer(
685             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
686             -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
687             3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
688             bucketStartTimeNs, bucketStartTimeNs, provider);
689 
690     durationProducer.onMatchedLogEventLost(conditionId, DATA_CORRUPTED_SOCKET_LOSS,
691                                            MetricProducer::LostAtomType::kCondition);
692     {
693         // Check dump report content.
694         StatsLogReport report = onDumpReport(durationProducer, bucketStartTimeNs + 50);
695         EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
696     }
697 
698     durationProducer.onMatchedLogEventLost(conditionId, DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW,
699                                            MetricProducer::LostAtomType::kCondition);
700     {
701         // Check dump report content.
702         StatsLogReport report = onDumpReport(durationProducer, bucketStartTimeNs + 150);
703         EXPECT_THAT(report.data_corrupted_reason(),
704                     ElementsAre(DATA_CORRUPTED_EVENT_QUEUE_OVERFLOW, DATA_CORRUPTED_SOCKET_LOSS));
705     }
706 }
707 
TEST(DurationMetricProducerTest,TestCorruptedDataReason_StateLoss)708 TEST(DurationMetricProducerTest, TestCorruptedDataReason_StateLoss) {
709     const int64_t bucketStartTimeNs = 10000000000;
710     const int stateAtomId = 10;
711 
712     DurationMetric metric;
713     metric.set_id(1);
714     metric.set_bucket(ONE_MINUTE);
715     metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
716     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
717     FieldMatcher dimensions;
718     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
719 
720     DurationMetricProducer durationProducer(
721             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
722             -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
723             3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
724             bucketStartTimeNs, bucketStartTimeNs, provider);
725 
726     durationProducer.onStateEventLost(stateAtomId, DATA_CORRUPTED_SOCKET_LOSS);
727     {
728         // Check dump report content.
729         StatsLogReport report = onDumpReport(durationProducer, bucketStartTimeNs + 50);
730         EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
731     }
732 
733     // validation that data corruption signal remains accurate after another dump
734     {
735         // Check dump report content.
736         StatsLogReport report = onDumpReport(durationProducer, bucketStartTimeNs + 150);
737         EXPECT_THAT(report.data_corrupted_reason(), ElementsAre(DATA_CORRUPTED_SOCKET_LOSS));
738     }
739 }
740 
741 }  // namespace statsd
742 }  // namespace os
743 }  // namespace android
744 #else
745 GTEST_LOG_(INFO) << "This test does nothing.\n";
746 #endif
747