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/StatsLogProcessor.h"
18 #include "src/stats_log_util.h"
19 #include "tests/statsd_test_util.h"
20
21 #include <vector>
22
23 namespace android {
24 namespace os {
25 namespace statsd {
26
27 #ifdef __ANDROID__
28
29 namespace {
30
CreateStatsdConfig(int num_buckets,int threshold)31 StatsdConfig CreateStatsdConfig(int num_buckets, int threshold) {
32 StatsdConfig config;
33 config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
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 android::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(10);
50 alert->set_trigger_if_sum_gt(threshold);
51 return config;
52 }
53
54 } // namespace
55
TEST(AnomalyDetectionE2eTest,TestSlicedCountMetric_single_bucket)56 TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_single_bucket) {
57 const int num_buckets = 1;
58 const int threshold = 3;
59 auto config = CreateStatsdConfig(num_buckets, threshold);
60 const uint64_t alert_id = config.alert(0).id();
61 const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
62
63 int64_t bucketStartTimeNs = 10000000000;
64 int64_t bucketSizeNs =
65 TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
66
67 ConfigKey cfgKey;
68 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
69 EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
70 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
71 EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
72
73 sp<AnomalyTracker> anomalyTracker =
74 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
75
76 std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
77 std::vector<AttributionNodeInternal> attributions2 = {
78 CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
79 std::vector<AttributionNodeInternal> attributions3 = {
80 CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
81 std::vector<AttributionNodeInternal> attributions4 = {
82 CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
83 std::vector<AttributionNodeInternal> attributions5 = {
84 CreateAttribution(222, "GMSCoreModule1") };
85
86 FieldValue fieldValue1(Field(android::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(android::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(attributions1, "wl1", bucketStartTimeNs + 2);
97 processor->OnLogEvent(event.get());
98 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
99
100 event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + 2);
101 processor->OnLogEvent(event.get());
102 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
103
104 event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
105 processor->OnLogEvent(event.get());
106 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
107
108 event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 3);
109 processor->OnLogEvent(event.get());
110 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
111
112 event = CreateAcquireWakelockEvent(attributions3, "wl1", bucketStartTimeNs + 4);
113 processor->OnLogEvent(event.get());
114 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
115
116 event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + 4);
117 processor->OnLogEvent(event.get());
118 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
119
120 // Fired alarm and refractory period end timestamp updated.
121 event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 5);
122 processor->OnLogEvent(event.get());
123 EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
124 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
125
126 event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 100);
127 processor->OnLogEvent(event.get());
128 EXPECT_EQ(refractory_period_sec + bucketStartTimeNs / NS_PER_SEC + 1,
129 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
130
131 event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs - 1);
132 processor->OnLogEvent(event.get());
133 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
134 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
135
136 event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
137 processor->OnLogEvent(event.get());
138 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs - 1) / NS_PER_SEC + 1,
139 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
140
141 event = CreateAcquireWakelockEvent(attributions4, "wl2", bucketStartTimeNs + bucketSizeNs + 1);
142 processor->OnLogEvent(event.get());
143 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
144
145 event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 2);
146 processor->OnLogEvent(event.get());
147 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
148
149 event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 3);
150 processor->OnLogEvent(event.get());
151 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
152
153 event = CreateAcquireWakelockEvent(attributions5, "wl2", bucketStartTimeNs + bucketSizeNs + 4);
154 processor->OnLogEvent(event.get());
155 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 4) / NS_PER_SEC + 1,
156 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2));
157 }
158
TEST(AnomalyDetectionE2eTest,TestSlicedCountMetric_multiple_buckets)159 TEST(AnomalyDetectionE2eTest, TestSlicedCountMetric_multiple_buckets) {
160 const int num_buckets = 3;
161 const int threshold = 3;
162 auto config = CreateStatsdConfig(num_buckets, threshold);
163 const uint64_t alert_id = config.alert(0).id();
164 const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs();
165
166 int64_t bucketStartTimeNs = 10000000000;
167 int64_t bucketSizeNs =
168 TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
169
170 ConfigKey cfgKey;
171 auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
172 EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
173 EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
174 EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
175
176 sp<AnomalyTracker> anomalyTracker =
177 processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0];
178
179 std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1")};
180 std::vector<AttributionNodeInternal> attributions2 = {
181 CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1")};
182 std::vector<AttributionNodeInternal> attributions3 = {
183 CreateAttribution(111, "App1"), CreateAttribution(333, "App3")};
184 std::vector<AttributionNodeInternal> attributions4 = {
185 CreateAttribution(222, "GMSCoreModule1"), CreateAttribution(333, "App3")};
186 std::vector<AttributionNodeInternal> attributions5 = {
187 CreateAttribution(222, "GMSCoreModule1") };
188
189 FieldValue fieldValue1(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
190 Value((int32_t)111));
191 HashableDimensionKey whatKey1({fieldValue1});
192 MetricDimensionKey dimensionKey1(whatKey1, DEFAULT_DIMENSION_KEY);
193
194 FieldValue fieldValue2(Field(android::util::WAKELOCK_STATE_CHANGED, (int32_t)0x02010101),
195 Value((int32_t)222));
196 HashableDimensionKey whatKey2({fieldValue2});
197 MetricDimensionKey dimensionKey2(whatKey2, DEFAULT_DIMENSION_KEY);
198
199 auto event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 2);
200 processor->OnLogEvent(event.get());
201 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
202
203 event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + 3);
204 processor->OnLogEvent(event.get());
205 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
206
207 // Fired alarm and refractory period end timestamp updated.
208 event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + 4);
209 processor->OnLogEvent(event.get());
210 EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
211
212 event = CreateAcquireWakelockEvent(attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1);
213 processor->OnLogEvent(event.get());
214 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
215 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
216
217 event = CreateAcquireWakelockEvent(attributions2, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
218 processor->OnLogEvent(event.get());
219 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
220 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
221
222 event = CreateAcquireWakelockEvent(
223 attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 1);
224 processor->OnLogEvent(event.get());
225 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + 1) / NS_PER_SEC + 1,
226 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
227
228 event = CreateAcquireWakelockEvent(
229 attributions2, "wl1", bucketStartTimeNs + 3 * bucketSizeNs + 2);
230 processor->OnLogEvent(event.get());
231 EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + 3 * bucketSizeNs + 2) / NS_PER_SEC + 1,
232 anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1));
233 }
234
235 #else
236 GTEST_LOG_(INFO) << "This test does nothing.\n";
237 #endif
238
239 } // namespace statsd
240 } // namespace os
241 } // namespace android
242