1 // Copyright (C) 2018 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 <gtest/gtest.h>
16
17 #include "src/statsd_metadata.pb.h"
18 #include "src/StatsLogProcessor.h"
19 #include "src/stats_log_util.h"
20 #include "tests/statsd_test_util.h"
21
22 #include <vector>
23
24 namespace android {
25 namespace os {
26 namespace statsd {
27
28 #ifdef __ANDROID__
29
30 namespace {
31
CreateStatsdConfig(int num_buckets,int threshold,int refractory_period_sec)32 StatsdConfig CreateStatsdConfig(int num_buckets, int threshold, int refractory_period_sec) {
33 StatsdConfig config;
34 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
35 auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
36
37 *config.add_atom_matcher() = wakelockAcquireMatcher;
38
39 auto countMetric = config.add_count_metric();
40 countMetric->set_id(123456);
41 countMetric->set_what(wakelockAcquireMatcher.id());
42 *countMetric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
43 util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
44 countMetric->set_bucket(FIVE_MINUTES);
45
46 auto alert = config.add_alert();
47 alert->set_id(StringToId("alert"));
48 alert->set_metric_id(123456);
49 alert->set_num_buckets(num_buckets);
50 alert->set_refractory_period_secs(refractory_period_sec);
51 alert->set_trigger_if_sum_gt(threshold);
52 return config;
53 }
54
55 } // namespace
56
TEST(AnomalyDetectionE2eTest,TestSlicedCountMetric_single_bucket)57 TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
58 const int num_buckets = 1;
59 const int threshold = 3;
60 const int refractory_period_sec = 10;
61 auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
62 const uint64_t alert_id = config.alert(0).id();
63
64 int64_t bucketStartTimeNs = 10000000000;
65 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
66
67 ConfigKey cfgKey;
68 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
69 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
70 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
71 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
72
73 sp<AnomalyTracker> anomalyTracker =
74 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
75
76 std::vector<int> attributionUids1 = {111};
77 std::vector<string> attributionTags1 = {"App1"};
78 std::vector<int> attributionUids2 = {111, 222};
79 std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
80 std::vector<int> attributionUids3 = {111, 333};
81 std::vector<string> attributionTags3 = {"App1", "App3"};
82 std::vector<int> attributionUids4 = {222, 333};
83 std::vector<string> attributionTags4 = {"GMSCoreModule1", "App3"};
84 std::vector<int> attributionUids5 = {222};
85 std::vector<string> attributionTags5 = {"GMSCoreModule1"};
86
87 FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
88 Value((int32_t)111));
89 HashableDimensionKey whatKey1({fieldValue1});
90 MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
91
92 FieldValue fieldValue2(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
93 Value((int32_t)222));
94 HashableDimensionKey whatKey2({fieldValue2});
95 MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
96
97 auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
98 attributionTags1, "wl1");
99 processor->OnLogEvent(event.get());
100 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
101
102 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids4, attributionTags4,
103 "wl2");
104 processor->OnLogEvent(event.get());
105 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
106
107 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
108 "wl1");
109 processor->OnLogEvent(event.get());
110 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
111
112 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids5, attributionTags5,
113 "wl2");
114 processor->OnLogEvent(event.get());
115 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
116
117 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids3, attributionTags3,
118 "wl1");
119 processor->OnLogEvent(event.get());
120 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
121
122 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids5, attributionTags5,
123 "wl2");
124 processor->OnLogEvent(event.get());
125 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
126
127 // Fired alarm and refractory period end timestamp updated.
128 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 5, attributionUids1, attributionTags1,
129 "wl1");
130 processor->OnLogEvent(event.get());
131 EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
132 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
133
134 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 100, attributionUids1, attributionTags1,
135 "wl1");
136 processor->OnLogEvent(event.get());
137 EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
138 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
139
140 event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, attributionUids1,
141 attributionTags1, "wl1");
142 processor->OnLogEvent(event.get());
143 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
144 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
145
146 event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
147 attributionTags1, "wl1");
148 processor->OnLogEvent(event.get());
149 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
150 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
151
152 event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids4,
153 attributionTags4, "wl2");
154 processor->OnLogEvent(event.get());
155 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
156
157 event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids5,
158 attributionTags5, "wl2");
159 processor->OnLogEvent(event.get());
160 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
161
162 event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 3, attributionUids5,
163 attributionTags5, "wl2");
164 processor->OnLogEvent(event.get());
165 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
166
167 event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 4, attributionUids5,
168 attributionTags5, "wl2");
169 processor->OnLogEvent(event.get());
170 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
171 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
172 }
173
TEST(AnomalyDetectionE2eTest,TestSlicedCountMetric_multiple_buckets)174 TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
175 const int num_buckets = 3;
176 const int threshold = 3;
177 const int refractory_period_sec = 10;
178 auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
179 const uint64_t alert_id = config.alert(0).id();
180
181 int64_t bucketStartTimeNs = 10000000000;
182 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
183
184 ConfigKey cfgKey;
185 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
186 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
187 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
188 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
189
190 sp<AnomalyTracker> anomalyTracker =
191 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
192
193 std::vector<int> attributionUids1 = {111};
194 std::vector<string> attributionTags1 = {"App1"};
195 std::vector<int> attributionUids2 = {111, 222};
196 std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
197
198 FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
199 Value((int32_t)111));
200 HashableDimensionKey whatKey1({fieldValue1});
201 MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
202
203 auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
204 attributionTags1, "wl1");
205 processor->OnLogEvent(event.get());
206 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
207
208 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3, attributionUids2, attributionTags2,
209 "wl1");
210 processor->OnLogEvent(event.get());
211 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
212
213 // Fired alarm and refractory period end timestamp updated.
214 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 4, attributionUids1, attributionTags1,
215 "wl1");
216 processor->OnLogEvent(event.get());
217 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
218
219 event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, attributionUids1,
220 attributionTags1, "wl1");
221 processor->OnLogEvent(event.get());
222 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
223 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
224
225 event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2, attributionUids2,
226 attributionTags2, "wl1");
227 processor->OnLogEvent(event.get());
228 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
229 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
230
231 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 1, attributionUids2,
232 attributionTags2, "wl1");
233 processor->OnLogEvent(event.get());
234 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
235 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
236
237 event = CreateAcquireWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs + 2, attributionUids2,
238 attributionTags2, "wl1");
239 processor->OnLogEvent(event.get());
240 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
241 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
242 }
243
TEST(AnomalyDetectionE2eTest,TestCountMetric_save_refractory_to_disk_no_data_written)244 TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk_no_data_written) {
245 const int num_buckets = 1;
246 const int threshold = 0;
247 const int refractory_period_sec = 86400 * 365; // 1 year
248 auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
249 const int64_t alert_id = config.alert(0).id();
250
251 int64_t bucketStartTimeNs = 10000000000;
252
253 int configUid = 2000;
254 int64_t configId = 1000;
255 ConfigKey cfgKey(configUid, configId);
256 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
257 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
258 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
259 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
260
261 metadata::StatsMetadataList result;
262 int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
263 int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
264 processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result);
265
266 ASSERT_EQ(result.stats_metadata_size(), 0);
267 }
268
TEST(AnomalyDetectionE2eTest,TestCountMetric_save_refractory_to_disk)269 TEST(AnomalyDetectionE2eTest, TestCountMetric_save_refractory_to_disk) {
270 const int num_buckets = 1;
271 const int threshold = 0;
272 const int refractory_period_sec = 86400 * 365; // 1 year
273 auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
274 const int64_t alert_id = config.alert(0).id();
275
276 int64_t bucketStartTimeNs = 10000000000;
277
278 int configUid = 2000;
279 int64_t configId = 1000;
280 ConfigKey cfgKey(configUid, configId);
281 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
282 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
283 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
284 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
285
286 sp<AnomalyTracker> anomalyTracker =
287 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
288
289 std::vector<int> attributionUids1 = {111};
290 std::vector<string> attributionTags1 = {"App1"};
291 std::vector<int> attributionUids2 = {111, 222};
292 std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
293
294 FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
295 Value((int32_t)111));
296 HashableDimensionKey whatKey1({fieldValue1});
297 MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
298
299 auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
300 attributionTags1, "wl1");
301 processor->OnLogEvent(event.get());
302 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1,
303 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
304
305 metadata::StatsMetadataList result;
306 int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
307 int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
308 processor->WriteMetadataToProto(mockWallClockNs, mockElapsedTimeNs, &result);
309
310 metadata::StatsMetadata statsMetadata = result.stats_metadata(0);
311 ASSERT_EQ(result.stats_metadata_size(), 1);
312 EXPECT_EQ(statsMetadata.config_key().config_id(), configId);
313 EXPECT_EQ(statsMetadata.config_key().uid(), configUid);
314
315 metadata::AlertMetadata alertMetadata = statsMetadata.alert_metadata(0);
316 ASSERT_EQ(statsMetadata.alert_metadata_size(), 1);
317 EXPECT_EQ(alertMetadata.alert_id(), alert_id);
318 metadata::AlertDimensionKeyedData keyedData = alertMetadata.alert_dim_keyed_data(0);
319 ASSERT_EQ(alertMetadata.alert_dim_keyed_data_size(), 1);
320 EXPECT_EQ(keyedData.last_refractory_ends_sec(),
321 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) -
322 mockElapsedTimeNs / NS_PER_SEC +
323 mockWallClockNs / NS_PER_SEC);
324
325 metadata::MetricDimensionKey metadataDimKey = keyedData.dimension_key();
326 metadata::FieldValue dimKeyInWhat = metadataDimKey.dimension_key_in_what(0);
327 EXPECT_EQ(dimKeyInWhat.field().tag(), fieldValue1.mField.getTag());
328 EXPECT_EQ(dimKeyInWhat.field().field(), fieldValue1.mField.getField());
329 EXPECT_EQ(dimKeyInWhat.value_int(), fieldValue1.mValue.int_value);
330 }
331
TEST(AnomalyDetectionE2eTest,TestCountMetric_load_refractory_from_disk)332 TEST(AnomalyDetectionE2eTest, TestCountMetric_load_refractory_from_disk) {
333 const int num_buckets = 1;
334 const int threshold = 0;
335 const int refractory_period_sec = 86400 * 365; // 1 year
336 auto config = CreateStatsdConfig(num_buckets, threshold, refractory_period_sec);
337 const int64_t alert_id = config.alert(0).id();
338
339 int64_t bucketStartTimeNs = 10000000000;
340
341 int configUid = 2000;
342 int64_t configId = 1000;
343 ConfigKey cfgKey(configUid, configId);
344 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
345 ASSERT_EQ(processor->mMetricsManagers.size(), 1u);
346 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
347 ASSERT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
348
349 sp<AnomalyTracker> anomalyTracker =
350 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
351
352 std::vector<int> attributionUids1 = {111};
353 std::vector<string> attributionTags1 = {"App1"};
354 std::vector<int> attributionUids2 = {111, 222};
355 std::vector<string> attributionTags2 = {"App1", "GMSCoreModule1"};
356
357 FieldValue fieldValue1(Field(util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
358 Value((int32_t)111));
359 HashableDimensionKey whatKey1({fieldValue1});
360 MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
361
362 auto event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2, attributionUids1,
363 attributionTags1, "wl1");
364 processor->OnLogEvent(event.get());
365 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 2) / NS_PER_SEC + 1,
366 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
367
368 int64_t mockWallClockNs = 1584991200 * NS_PER_SEC;
369 int64_t mockElapsedTimeNs = bucketStartTimeNs + 5000 * NS_PER_SEC;
370 processor->SaveMetadataToDisk(mockWallClockNs, mockElapsedTimeNs);
371
372 auto processor2 = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
373 int64_t mockElapsedTimeSinceBoot = 10 * NS_PER_SEC;
374 processor2->LoadMetadataFromDisk(mockWallClockNs, mockElapsedTimeSinceBoot);
375
376 sp<AnomalyTracker> anomalyTracker2 =
377 processor2->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
378 EXPECT_EQ(anomalyTracker2->getRefractoryPeriodEndsSec(dimensionKey1) -
379 mockElapsedTimeSinceBoot / NS_PER_SEC,
380 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1) -
381 mockElapsedTimeNs / NS_PER_SEC);
382 }
383
384 #else
385 GTEST_LOG_(INFO) << "This test does nothing.\n";
386 #endif
387
388 } // namespace statsd
389 } // namespace os
390 } // namespace android
391