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/CountMetricProducer.h"
16
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <math.h>
20 #include <stdio.h>
21
22 #include <vector>
23
24 #include "metrics_test_helper.h"
25 #include "src/stats_log_util.h"
26 #include "stats_event.h"
27 #include "tests/statsd_test_util.h"
28
29 using namespace testing;
30 using android::sp;
31 using std::set;
32 using std::unordered_map;
33 using std::vector;
34
35 #ifdef __ANDROID__
36
37 namespace android {
38 namespace os {
39 namespace statsd {
40
41
42 namespace {
43 const ConfigKey kConfigKey(0, 12345);
44 const uint64_t protoHash = 0x1234567890;
45
makeLogEvent(LogEvent * logEvent,int64_t timestampNs,int atomId)46 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
47 AStatsEvent* statsEvent = AStatsEvent_obtain();
48 AStatsEvent_setAtomId(statsEvent, atomId);
49 AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
50
51 parseStatsEventToLogEvent(statsEvent, logEvent);
52 }
53
makeLogEvent(LogEvent * logEvent,int64_t timestampNs,int atomId,string uid)54 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId, string uid) {
55 AStatsEvent* statsEvent = AStatsEvent_obtain();
56 AStatsEvent_setAtomId(statsEvent, atomId);
57 AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
58 AStatsEvent_writeString(statsEvent, uid.c_str());
59
60 parseStatsEventToLogEvent(statsEvent, logEvent);
61 }
62
63 } // namespace
64
65 // Setup for parameterized tests.
66 class CountMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
67
68 INSTANTIATE_TEST_SUITE_P(CountMetricProducerTest_PartialBucket,
69 CountMetricProducerTest_PartialBucket,
70 testing::Values(APP_UPGRADE, BOOT_COMPLETE));
71
TEST(CountMetricProducerTest,TestFirstBucket)72 TEST(CountMetricProducerTest, TestFirstBucket) {
73 CountMetric metric;
74 metric.set_id(1);
75 metric.set_bucket(ONE_MINUTE);
76 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
77
78 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
79 CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
80 wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
81 provider);
82 EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
83 EXPECT_EQ(10, countProducer.mCurrentBucketNum);
84 EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
85 }
86
TEST(CountMetricProducerTest,TestNonDimensionalEvents)87 TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
88 int64_t bucketStartTimeNs = 10000000000;
89 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
90 int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
91 int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
92 int tagId = 1;
93
94 CountMetric metric;
95 metric.set_id(1);
96 metric.set_bucket(ONE_MINUTE);
97
98 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
99 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
100 CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
101 wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs,
102 provider);
103
104 // 2 events in bucket 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 + 2, tagId);
109
110 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
111 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
112
113 // Flushes at event #2.
114 countProducer.flushIfNeededLocked(bucketStartTimeNs + 2);
115 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
116
117 // Flushes.
118 countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
119 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
120 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
121 countProducer.mPastBuckets.end());
122 const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
123 ASSERT_EQ(1UL, buckets.size());
124 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
125 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
126 EXPECT_EQ(2LL, buckets[0].mCount);
127
128 // 1 matched event happens in bucket 2.
129 LogEvent event3(/*uid=*/0, /*pid=*/0);
130 makeLogEvent(&event3, bucketStartTimeNs + bucketSizeNs + 2, tagId);
131
132 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
133
134 countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
135 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
136 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
137 countProducer.mPastBuckets.end());
138 ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
139 const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
140 EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
141 EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
142 EXPECT_EQ(1LL, bucketInfo2.mCount);
143
144 // nothing happens in bucket 3. we should not record anything for bucket 3.
145 countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
146 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
147 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
148 countProducer.mPastBuckets.end());
149 const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
150 ASSERT_EQ(2UL, buckets3.size());
151 }
152
TEST(CountMetricProducerTest,TestEventsWithNonSlicedCondition)153 TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
154 int64_t bucketStartTimeNs = 10000000000;
155 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
156
157 CountMetric metric;
158 metric.set_id(1);
159 metric.set_bucket(ONE_MINUTE);
160 metric.set_condition(StringToId("SCREEN_ON"));
161
162 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
163
164 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
165 CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
166 protoHash, bucketStartTimeNs, bucketStartTimeNs, provider);
167 assertConditionTimer(countProducer.mConditionTimer, false, 0, 0);
168
169 countProducer.onConditionChanged(true, bucketStartTimeNs);
170 assertConditionTimer(countProducer.mConditionTimer, true, 0, bucketStartTimeNs);
171
172 LogEvent event1(/*uid=*/0, /*pid=*/0);
173 makeLogEvent(&event1, bucketStartTimeNs + 1, /*atomId=*/1);
174 countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
175
176 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
177
178 countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
179 assertConditionTimer(countProducer.mConditionTimer, false, 2, bucketStartTimeNs + 2);
180
181 // Upon this match event, the matched event1 is flushed.
182 LogEvent event2(/*uid=*/0, /*pid=*/0);
183 makeLogEvent(&event2, bucketStartTimeNs + 10, /*atomId=*/1);
184 countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
185 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
186
187 countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
188 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
189 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
190 countProducer.mPastBuckets.end());
191
192 const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
193 ASSERT_EQ(1UL, buckets.size());
194 const auto& bucketInfo = buckets[0];
195 EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
196 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
197 EXPECT_EQ(1LL, bucketInfo.mCount);
198 }
199
TEST(CountMetricProducerTest,TestEventsWithSlicedCondition)200 TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
201 int64_t bucketStartTimeNs = 10000000000;
202 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
203
204 int tagId = 1;
205 int conditionTagId = 2;
206
207 CountMetric metric;
208 metric.set_id(1);
209 metric.set_bucket(ONE_MINUTE);
210 metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
211 MetricConditionLink* link = metric.add_links();
212 link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
213 buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
214 buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
215
216 LogEvent event1(/*uid=*/0, /*pid=*/0);
217 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
218
219 LogEvent event2(/*uid=*/0, /*pid=*/0);
220 makeLogEvent(&event2, bucketStartTimeNs + 10, tagId, /*uid=*/"222");
221
222 ConditionKey key1;
223 key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
224 getMockedDimensionKey(conditionTagId, 2, "111")};
225
226 ConditionKey key2;
227 key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
228 getMockedDimensionKey(conditionTagId, 2, "222")};
229
230 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
231
232 EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
233
234 EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
235
236 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
237 CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
238 {ConditionState::kUnknown}, wizard, protoHash,
239 bucketStartTimeNs, bucketStartTimeNs, provider);
240
241 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
242 countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
243 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
244
245 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
246 countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
247 ASSERT_EQ(1UL, countProducer.mPastBuckets.size());
248 EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
249 countProducer.mPastBuckets.end());
250 const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
251 ASSERT_EQ(1UL, buckets.size());
252 const auto& bucketInfo = buckets[0];
253 EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
254 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
255 EXPECT_EQ(1LL, bucketInfo.mCount);
256 }
257
TEST_P(CountMetricProducerTest_PartialBucket,TestSplitInCurrentBucket)258 TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInCurrentBucket) {
259 sp<AlarmMonitor> alarmMonitor;
260 int64_t bucketStartTimeNs = 10000000000;
261 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
262 int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
263
264 int tagId = 1;
265 int conditionTagId = 2;
266
267 CountMetric metric;
268 metric.set_id(1);
269 metric.set_bucket(ONE_MINUTE);
270 metric.set_split_bucket_for_app_upgrade(true);
271 Alert alert;
272 alert.set_num_buckets(3);
273 alert.set_trigger_if_sum_gt(2);
274
275 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
276 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
277 CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
278 protoHash, bucketStartTimeNs, bucketStartTimeNs, provider);
279
280 sp<AnomalyTracker> anomalyTracker =
281 countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
282 EXPECT_TRUE(anomalyTracker != nullptr);
283
284 // Bucket is not flushed yet.
285 LogEvent event1(/*uid=*/0, /*pid=*/0);
286 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
287 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
288 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
289 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
290
291 // App upgrade or boot complete forces bucket flush.
292 // Check that there's a past bucket and the bucket end is not adjusted.
293 switch (GetParam()) {
294 case APP_UPGRADE:
295 countProducer.notifyAppUpgrade(eventTimeNs);
296 break;
297 case BOOT_COMPLETE:
298 countProducer.onStatsdInitCompleted(eventTimeNs);
299 break;
300 }
301 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
302 EXPECT_EQ(bucketStartTimeNs,
303 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
304 EXPECT_EQ(eventTimeNs,
305 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
306 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
307 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
308 // Anomaly tracker only contains full buckets.
309 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
310
311 int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
312 // Next event occurs in same bucket as partial bucket created.
313 LogEvent event2(/*uid=*/0, /*pid=*/0);
314 makeLogEvent(&event2, bucketStartTimeNs + 59 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
315 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
316 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
317 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
318 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
319 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
320
321 // Third event in following bucket.
322 LogEvent event3(/*uid=*/0, /*pid=*/0);
323 makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
324 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
325 ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
326 EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
327 EXPECT_EQ(1, countProducer.getCurrentBucketNum());
328 EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
329 }
330
TEST_P(CountMetricProducerTest_PartialBucket,TestSplitInNextBucket)331 TEST_P(CountMetricProducerTest_PartialBucket, TestSplitInNextBucket) {
332 int64_t bucketStartTimeNs = 10000000000;
333 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
334 int64_t eventTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
335
336 int tagId = 1;
337 int conditionTagId = 2;
338
339 CountMetric metric;
340 metric.set_id(1);
341 metric.set_bucket(ONE_MINUTE);
342 metric.set_split_bucket_for_app_upgrade(true);
343
344 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
345 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
346 CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
347 protoHash, bucketStartTimeNs, bucketStartTimeNs, provider);
348
349 // Bucket is flushed yet.
350 LogEvent event1(/*uid=*/0, /*pid=*/0);
351 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
352 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
353 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
354
355 // App upgrade or boot complete forces bucket flush.
356 // Check that there's a past bucket and the bucket end is not adjusted since the upgrade
357 // occurred after the bucket end time.
358 switch (GetParam()) {
359 case APP_UPGRADE:
360 countProducer.notifyAppUpgrade(eventTimeNs);
361 break;
362 case BOOT_COMPLETE:
363 countProducer.onStatsdInitCompleted(eventTimeNs);
364 break;
365 }
366 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
367 EXPECT_EQ(bucketStartTimeNs,
368 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
369 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
370 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
371 EXPECT_EQ(eventTimeNs, countProducer.mCurrentBucketStartTimeNs);
372
373 // Next event occurs in same bucket as partial bucket created.
374 LogEvent event2(/*uid=*/0, /*pid=*/0);
375 makeLogEvent(&event2, bucketStartTimeNs + 70 * NS_PER_SEC + 10, tagId, /*uid=*/"222");
376 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
377 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
378
379 // Third event in following bucket.
380 LogEvent event3(/*uid=*/0, /*pid=*/0);
381 makeLogEvent(&event3, bucketStartTimeNs + 121 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
382 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
383 ASSERT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
384 EXPECT_EQ((int64_t)eventTimeNs,
385 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
386 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
387 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
388 }
389
TEST(CountMetricProducerTest,TestSplitOnAppUpgradeDisabled)390 TEST(CountMetricProducerTest, TestSplitOnAppUpgradeDisabled) {
391 sp<AlarmMonitor> alarmMonitor;
392 int64_t bucketStartTimeNs = 10000000000;
393 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
394 int64_t eventTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
395
396 int tagId = 1;
397 int conditionTagId = 2;
398 CountMetric metric;
399 metric.set_id(1);
400 metric.set_bucket(ONE_MINUTE);
401 metric.set_split_bucket_for_app_upgrade(false);
402 Alert alert;
403 alert.set_num_buckets(3);
404 alert.set_trigger_if_sum_gt(2);
405
406 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
407 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
408 CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
409 protoHash, bucketStartTimeNs, bucketStartTimeNs, provider);
410
411 sp<AnomalyTracker> anomalyTracker =
412 countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
413 EXPECT_TRUE(anomalyTracker != nullptr);
414
415 LogEvent event1(/*uid=*/0, /*pid=*/0);
416 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId, /*uid=*/"111");
417 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
418 ASSERT_EQ(0UL, countProducer.mPastBuckets.size());
419 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
420
421 // App upgrade event occurs. Make sure no bucket is split.
422 // Check that there's a past bucket and the bucket end is not adjusted.
423 countProducer.notifyAppUpgrade(eventTimeNs);
424
425 ASSERT_EQ(0UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
426 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
427 EXPECT_EQ(bucketStartTimeNs, countProducer.mCurrentBucketStartTimeNs);
428 // Anomaly tracker only contains full buckets.
429 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
430
431 int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
432 // Next event occurs in the first bucket.
433 LogEvent event2(/*uid=*/0, /*pid=*/0);
434 makeLogEvent(&event2, eventTimeNs + 10 * NS_PER_SEC, tagId, /*uid=*/"222");
435 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
436 ASSERT_EQ(0UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
437 EXPECT_EQ(bucketStartTimeNs, countProducer.mCurrentBucketStartTimeNs);
438 EXPECT_EQ(0, countProducer.getCurrentBucketNum());
439 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
440
441 // Third event in following bucket.
442 LogEvent event3(/*uid=*/0, /*pid=*/0);
443 makeLogEvent(&event3, bucketStartTimeNs + 62 * NS_PER_SEC + 10, tagId, /*uid=*/"333");
444 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
445 ASSERT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
446 EXPECT_EQ(bucketStartTimeNs,
447 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
448 EXPECT_EQ(bucketStartTimeNs + 60 * NS_PER_SEC,
449 countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
450 EXPECT_EQ(2, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mCount);
451 EXPECT_EQ(bucketStartTimeNs + 60 * NS_PER_SEC, countProducer.mCurrentBucketStartTimeNs);
452 EXPECT_EQ(1, countProducer.getCurrentBucketNum());
453 EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
454 }
455
TEST(CountMetricProducerTest,TestAnomalyDetectionUnSliced)456 TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
457 sp<AlarmMonitor> alarmMonitor;
458 Alert alert;
459 alert.set_id(11);
460 alert.set_metric_id(1);
461 alert.set_trigger_if_sum_gt(2);
462 alert.set_num_buckets(2);
463 const int32_t refPeriodSec = 1;
464 alert.set_refractory_period_secs(refPeriodSec);
465
466 int64_t bucketStartTimeNs = 10000000000;
467 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
468 int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
469 int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
470
471 CountMetric metric;
472 metric.set_id(1);
473 metric.set_bucket(ONE_MINUTE);
474
475 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
476 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
477 CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
478 wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs,
479 provider);
480
481 sp<AnomalyTracker> anomalyTracker =
482 countProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
483
484 int tagId = 1;
485 LogEvent event1(/*uid=*/0, /*pid=*/0);
486 makeLogEvent(&event1, bucketStartTimeNs + 1, tagId);
487 LogEvent event2(/*uid=*/0, /*pid=*/0);
488 makeLogEvent(&event2, bucketStartTimeNs + 2, tagId);
489 LogEvent event3(/*uid=*/0, /*pid=*/0);
490 makeLogEvent(&event3, bucketStartTimeNs + 2 * bucketSizeNs + 1, tagId);
491 LogEvent event4(/*uid=*/0, /*pid=*/0);
492 makeLogEvent(&event4, bucketStartTimeNs + 3 * bucketSizeNs + 1, tagId);
493 LogEvent event5(/*uid=*/0, /*pid=*/0);
494 makeLogEvent(&event5, bucketStartTimeNs + 3 * bucketSizeNs + 2, tagId);
495 LogEvent event6(/*uid=*/0, /*pid=*/0);
496 makeLogEvent(&event6, bucketStartTimeNs + 3 * bucketSizeNs + 3, tagId);
497 LogEvent event7(/*uid=*/0, /*pid=*/0);
498 makeLogEvent(&event7, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, tagId);
499
500 // Two events in bucket #0.
501 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
502 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
503
504 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
505 EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
506 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
507
508 // One event in bucket #2. No alarm as bucket #0 is trashed out.
509 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
510 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
511 EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
512 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
513
514 // Two events in bucket #3.
515 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
516 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
517 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
518 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
519 EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
520 // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
521 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
522 std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
523
524 countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
525 ASSERT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
526 EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
527 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
528 std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
529 }
530
TEST(CountMetricProducerTest,TestOneWeekTimeUnit)531 TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
532 CountMetric metric;
533 metric.set_id(1);
534 metric.set_bucket(ONE_WEEK);
535
536 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
537
538 int64_t oneDayNs = 24 * 60 * 60 * 1e9;
539 int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
540
541 sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
542 CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard,
543 protoHash, oneDayNs, fiveWeeksNs, provider);
544
545 int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
546
547 EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs);
548 EXPECT_EQ(4, countProducer.mCurrentBucketNum);
549 EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs());
550 }
551
552 } // namespace statsd
553 } // namespace os
554 } // namespace android
555 #else
556 GTEST_LOG_(INFO) << "This test does nothing.\n";
557 #endif
558