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 #include "src/stats_log_util.h"
17 #include "metrics_test_helper.h"
18 #include "tests/statsd_test_util.h"
19 
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22 #include <math.h>
23 #include <stdio.h>
24 #include <vector>
25 
26 using namespace testing;
27 using android::sp;
28 using std::set;
29 using std::unordered_map;
30 using std::vector;
31 
32 #ifdef __ANDROID__
33 
34 namespace android {
35 namespace os {
36 namespace statsd {
37 
38 const ConfigKey kConfigKey(0, 12345);
39 
TEST(CountMetricProducerTest,TestFirstBucket)40 TEST(CountMetricProducerTest, TestFirstBucket) {
41     CountMetric metric;
42     metric.set_id(1);
43     metric.set_bucket(ONE_MINUTE);
44     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
45 
46     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
47                                       5, 600 * NS_PER_SEC + NS_PER_SEC/2);
48     EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
49     EXPECT_EQ(10, countProducer.mCurrentBucketNum);
50     EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
51 }
52 
TEST(CountMetricProducerTest,TestNonDimensionalEvents)53 TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
54     int64_t bucketStartTimeNs = 10000000000;
55     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
56     int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
57     int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
58     int tagId = 1;
59 
60     CountMetric metric;
61     metric.set_id(1);
62     metric.set_bucket(ONE_MINUTE);
63 
64     LogEvent event1(tagId, bucketStartTimeNs + 1);
65     event1.init();
66     LogEvent event2(tagId, bucketStartTimeNs + 2);
67     event2.init();
68 
69     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
70 
71     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
72                                       bucketStartTimeNs, bucketStartTimeNs);
73 
74     // 2 events in bucket 1.
75     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
76     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
77 
78     // Flushes at event #2.
79     countProducer.flushIfNeededLocked(bucketStartTimeNs + 2);
80     EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
81 
82     // Flushes.
83     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
84     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
85     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
86                 countProducer.mPastBuckets.end());
87     const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
88     EXPECT_EQ(1UL, buckets.size());
89     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
90     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
91     EXPECT_EQ(2LL, buckets[0].mCount);
92 
93     // 1 matched event happens in bucket 2.
94     LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 2);
95     event3.init();
96 
97     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
98     countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
99     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
100     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
101                 countProducer.mPastBuckets.end());
102     EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
103     const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
104     EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
105     EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
106     EXPECT_EQ(1LL, bucketInfo2.mCount);
107 
108     // nothing happens in bucket 3. we should not record anything for bucket 3.
109     countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
110     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
111     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
112                 countProducer.mPastBuckets.end());
113     const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
114     EXPECT_EQ(2UL, buckets3.size());
115 }
116 
TEST(CountMetricProducerTest,TestEventsWithNonSlicedCondition)117 TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
118     int64_t bucketStartTimeNs = 10000000000;
119     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
120 
121     CountMetric metric;
122     metric.set_id(1);
123     metric.set_bucket(ONE_MINUTE);
124     metric.set_condition(StringToId("SCREEN_ON"));
125 
126     LogEvent event1(1, bucketStartTimeNs + 1);
127     event1.init();
128 
129     LogEvent event2(1, bucketStartTimeNs + 10);
130     event2.init();
131 
132     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
133 
134     CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs);
135 
136     countProducer.onConditionChanged(true, bucketStartTimeNs);
137     countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
138     EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
139 
140     countProducer.onConditionChanged(false /*new condition*/, bucketStartTimeNs + 2);
141     // Upon this match event, the matched event1 is flushed.
142     countProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
143     EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
144 
145     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
146     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
147     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
148                 countProducer.mPastBuckets.end());
149     {
150         const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
151         EXPECT_EQ(1UL, buckets.size());
152         const auto& bucketInfo = buckets[0];
153         EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
154         EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
155         EXPECT_EQ(1LL, bucketInfo.mCount);
156     }
157 }
158 
TEST(CountMetricProducerTest,TestEventsWithSlicedCondition)159 TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
160     int64_t bucketStartTimeNs = 10000000000;
161     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
162 
163     int tagId = 1;
164     int conditionTagId = 2;
165 
166     CountMetric metric;
167     metric.set_id(1);
168     metric.set_bucket(ONE_MINUTE);
169     metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
170     MetricConditionLink* link = metric.add_links();
171     link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
172     buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
173     buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
174 
175     LogEvent event1(tagId, bucketStartTimeNs + 1);
176     event1.write("111");  // uid
177     event1.init();
178     ConditionKey key1;
179     key1[StringToId("APP_IN_BACKGROUND_PER_UID")] =
180         {getMockedDimensionKey(conditionTagId, 2, "111")};
181 
182     LogEvent event2(tagId, bucketStartTimeNs + 10);
183     event2.write("222");  // uid
184     event2.init();
185     ConditionKey key2;
186     key2[StringToId("APP_IN_BACKGROUND_PER_UID")] =
187         {getMockedDimensionKey(conditionTagId, 2, "222")};
188 
189     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
190     EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
191 
192     EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
193 
194     CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
195                                       bucketStartTimeNs, bucketStartTimeNs);
196 
197     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
198     countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
199     EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
200 
201     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
202     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
203     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
204     EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
205                 countProducer.mPastBuckets.end());
206     const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
207     EXPECT_EQ(1UL, buckets.size());
208     const auto& bucketInfo = buckets[0];
209     EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
210     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.mBucketEndNs);
211     EXPECT_EQ(1LL, bucketInfo.mCount);
212 }
213 
TEST(CountMetricProducerTest,TestEventWithAppUpgrade)214 TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
215     sp<AlarmMonitor> alarmMonitor;
216     int64_t bucketStartTimeNs = 10000000000;
217     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
218     int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
219 
220     int tagId = 1;
221     int conditionTagId = 2;
222 
223     CountMetric metric;
224     metric.set_id(1);
225     metric.set_bucket(ONE_MINUTE);
226     Alert alert;
227     alert.set_num_buckets(3);
228     alert.set_trigger_if_sum_gt(2);
229     LogEvent event1(tagId, bucketStartTimeNs + 1);
230     event1.write("111");  // uid
231     event1.init();
232     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
233     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
234                                       bucketStartTimeNs, bucketStartTimeNs);
235 
236     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
237     EXPECT_TRUE(anomalyTracker != nullptr);
238 
239     // Bucket is flushed yet.
240     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
241     EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
242     EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
243 
244     // App upgrade forces bucket flush.
245     // Check that there's a past bucket and the bucket end is not adjusted.
246     countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
247     EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
248     EXPECT_EQ((long long)bucketStartTimeNs,
249               countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
250     EXPECT_EQ((long long)eventUpgradeTimeNs,
251               countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
252     EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
253     // Anomaly tracker only contains full buckets.
254     EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
255 
256     int64_t lastEndTimeNs = countProducer.getCurrentBucketEndTimeNs();
257     // Next event occurs in same bucket as partial bucket created.
258     LogEvent event2(tagId, bucketStartTimeNs + 59 * NS_PER_SEC + 10);
259     event2.write("222");  // uid
260     event2.init();
261     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
262     EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
263     EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
264     EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
265 
266     // Third event in following bucket.
267     LogEvent event3(tagId, bucketStartTimeNs + 62 * NS_PER_SEC + 10);
268     event3.write("333");  // uid
269     event3.init();
270     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
271     EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
272     EXPECT_EQ(lastEndTimeNs, countProducer.mCurrentBucketStartTimeNs);
273     EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
274 }
275 
TEST(CountMetricProducerTest,TestEventWithAppUpgradeInNextBucket)276 TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
277     int64_t bucketStartTimeNs = 10000000000;
278     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
279     int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC;
280 
281     int tagId = 1;
282     int conditionTagId = 2;
283 
284     CountMetric metric;
285     metric.set_id(1);
286     metric.set_bucket(ONE_MINUTE);
287     LogEvent event1(tagId, bucketStartTimeNs + 1);
288     event1.write("111");  // uid
289     event1.init();
290     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
291     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
292                                       bucketStartTimeNs, bucketStartTimeNs);
293 
294     // Bucket is flushed yet.
295     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
296     EXPECT_EQ(0UL, countProducer.mPastBuckets.size());
297 
298     // App upgrade forces bucket flush.
299     // Check that there's a past bucket and the bucket end is not adjusted.
300     countProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
301     EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
302     EXPECT_EQ((int64_t)bucketStartTimeNs,
303               countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
304     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
305               countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
306     EXPECT_EQ(eventUpgradeTimeNs, countProducer.mCurrentBucketStartTimeNs);
307 
308     // Next event occurs in same bucket as partial bucket created.
309     LogEvent event2(tagId, bucketStartTimeNs + 70 * NS_PER_SEC + 10);
310     event2.write("222");  // uid
311     event2.init();
312     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
313     EXPECT_EQ(1UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
314 
315     // Third event in following bucket.
316     LogEvent event3(tagId, bucketStartTimeNs + 121 * NS_PER_SEC + 10);
317     event3.write("333");  // uid
318     event3.init();
319     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
320     EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
321     EXPECT_EQ((int64_t)eventUpgradeTimeNs,
322               countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketStartNs);
323     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
324               countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mBucketEndNs);
325 }
326 
TEST(CountMetricProducerTest,TestAnomalyDetectionUnSliced)327 TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
328     sp<AlarmMonitor> alarmMonitor;
329     Alert alert;
330     alert.set_id(11);
331     alert.set_metric_id(1);
332     alert.set_trigger_if_sum_gt(2);
333     alert.set_num_buckets(2);
334     const int32_t refPeriodSec = 1;
335     alert.set_refractory_period_secs(refPeriodSec);
336 
337     int64_t bucketStartTimeNs = 10000000000;
338     int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
339     int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
340     int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
341 
342     CountMetric metric;
343     metric.set_id(1);
344     metric.set_bucket(ONE_MINUTE);
345 
346     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
347     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
348                                       bucketStartTimeNs, bucketStartTimeNs);
349 
350     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
351 
352     int tagId = 1;
353     LogEvent event1(tagId, bucketStartTimeNs + 1);
354     event1.init();
355     LogEvent event2(tagId, bucketStartTimeNs + 2);
356     event2.init();
357     LogEvent event3(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1);
358     event3.init();
359     LogEvent event4(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1);
360     event4.init();
361     LogEvent event5(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2);
362     event5.init();
363     LogEvent event6(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 3);
364     event6.init();
365     LogEvent event7(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
366     event7.init();
367 
368     // Two events in bucket #0.
369     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
370     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
371 
372     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
373     EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
374     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
375 
376     // One event in bucket #2. No alarm as bucket #0 is trashed out.
377     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
378     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
379     EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
380     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
381 
382     // Two events in bucket #3.
383     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
384     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event5);
385     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event6);
386     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
387     EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
388     // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
389     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
390             std::ceil(1.0 * event5.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
391 
392     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
393     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
394     EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
395     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
396             std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
397 }
398 
399 }  // namespace statsd
400 }  // namespace os
401 }  // namespace android
402 #else
403 GTEST_LOG_(INFO) << "This test does nothing.\n";
404 #endif
405