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