Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 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/anomaly/DurationAnomalyTracker.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 | |
| 32 | StatsdConfig CreateStatsdConfig(int num_buckets, |
| 33 | uint64_t threshold_ns, |
| 34 | DurationMetric::AggregationType aggregationType, |
| 35 | bool nesting) { |
| 36 | StatsdConfig config; |
| 37 | config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. |
| 38 | *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher(); |
| 39 | *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher(); |
| 40 | *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher(); |
| 41 | *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher(); |
| 42 | |
| 43 | auto screenIsOffPredicate = CreateScreenIsOffPredicate(); |
| 44 | *config.add_predicate() = screenIsOffPredicate; |
| 45 | |
| 46 | auto holdingWakelockPredicate = CreateHoldingWakelockPredicate(); |
| 47 | FieldMatcher dimensions = CreateAttributionUidDimensions( |
Jeffrey Huang | 3eb84d4 | 2020-03-17 10:31:22 -0700 | [diff] [blame] | 48 | util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 49 | dimensions.add_child()->set_field(3); // The wakelock tag is set in field 3 of the wakelock. |
| 50 | *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions; |
| 51 | holdingWakelockPredicate.mutable_simple_predicate()->set_count_nesting(nesting); |
| 52 | *config.add_predicate() = holdingWakelockPredicate; |
| 53 | |
| 54 | auto durationMetric = config.add_duration_metric(); |
| 55 | durationMetric->set_id(StringToId("WakelockDuration")); |
| 56 | durationMetric->set_what(holdingWakelockPredicate.id()); |
| 57 | durationMetric->set_condition(screenIsOffPredicate.id()); |
| 58 | durationMetric->set_aggregation_type(aggregationType); |
| 59 | *durationMetric->mutable_dimensions_in_what() = |
Jeffrey Huang | 3eb84d4 | 2020-03-17 10:31:22 -0700 | [diff] [blame] | 60 | CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); |
Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 61 | durationMetric->set_bucket(FIVE_MINUTES); |
| 62 | |
| 63 | auto alert = config.add_alert(); |
| 64 | alert->set_id(StringToId("alert")); |
| 65 | alert->set_metric_id(StringToId("WakelockDuration")); |
| 66 | alert->set_num_buckets(num_buckets); |
| 67 | alert->set_refractory_period_secs(2); |
| 68 | alert->set_trigger_if_sum_gt(threshold_ns); |
| 69 | return config; |
| 70 | } |
| 71 | |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 72 | std::vector<int> attributionUids1 = {111, 222}; |
| 73 | std::vector<string> attributionTags1 = {"App1", "GMSCoreModule1"}; |
Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 74 | |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 75 | std::vector<int> attributionUids2 = {111, 222}; |
| 76 | std::vector<string> attributionTags2 = {"App2", "GMSCoreModule1"}; |
Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 77 | |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 78 | std::vector<int> attributionUids3 = {222}; |
| 79 | std::vector<string> attributionTags3 = {"GMSCoreModule1"}; |
Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 80 | |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 81 | MetricDimensionKey dimensionKey1( |
Jeffrey Huang | 3eb84d4 | 2020-03-17 10:31:22 -0700 | [diff] [blame] | 82 | HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED, |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 83 | (int32_t)0x02010101), |
| 84 | Value((int32_t)111))}), |
| 85 | DEFAULT_DIMENSION_KEY); |
Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 86 | |
| 87 | MetricDimensionKey dimensionKey2( |
Jeffrey Huang | 3eb84d4 | 2020-03-17 10:31:22 -0700 | [diff] [blame] | 88 | HashableDimensionKey({FieldValue(Field(util::WAKELOCK_STATE_CHANGED, |
Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 89 | (int32_t)0x02010101), Value((int32_t)222))}), |
| 90 | DEFAULT_DIMENSION_KEY); |
| 91 | |
tsaichristine | 7747d37 | 2020-02-28 17:36:59 -0800 | [diff] [blame] | 92 | } // namespace |
| 93 | |
| 94 | TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket) { |
| 95 | const int num_buckets = 1; |
| 96 | const uint64_t threshold_ns = NS_PER_SEC; |
| 97 | auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); |
| 98 | const uint64_t alert_id = config.alert(0).id(); |
| 99 | const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); |
| 100 | |
| 101 | int64_t bucketStartTimeNs = 10 * NS_PER_SEC; |
| 102 | int64_t bucketSizeNs = |
| 103 | TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; |
| 104 | |
| 105 | ConfigKey cfgKey; |
| 106 | auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); |
| 107 | EXPECT_EQ(processor->mMetricsManagers.size(), 1u); |
| 108 | EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); |
| 109 | EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); |
| 110 | |
| 111 | sp<AnomalyTracker> anomalyTracker = |
| 112 | processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; |
| 113 | |
| 114 | auto screen_on_event = CreateScreenStateChangedEvent( |
| 115 | bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON); |
| 116 | auto screen_off_event = CreateScreenStateChangedEvent( |
| 117 | bucketStartTimeNs + 10, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); |
| 118 | processor->OnLogEvent(screen_on_event.get()); |
| 119 | processor->OnLogEvent(screen_off_event.get()); |
| 120 | |
| 121 | // Acquire wakelock wl1. |
| 122 | auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 11, attributionUids1, |
| 123 | attributionTags1, "wl1"); |
| 124 | processor->OnLogEvent(acquire_event.get()); |
| 125 | EXPECT_EQ((bucketStartTimeNs + 11 + threshold_ns) / NS_PER_SEC + 1, |
| 126 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 127 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 128 | |
| 129 | // Release wakelock wl1. No anomaly detected. Alarm cancelled at the "release" event. |
| 130 | auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 101, attributionUids1, |
| 131 | attributionTags1, "wl1"); |
| 132 | processor->OnLogEvent(release_event.get()); |
| 133 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 134 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 135 | |
| 136 | // Acquire wakelock wl1 within bucket #0. |
| 137 | acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 110, attributionUids2, |
| 138 | attributionTags2, "wl1"); |
| 139 | processor->OnLogEvent(acquire_event.get()); |
| 140 | EXPECT_EQ((bucketStartTimeNs + 110 + threshold_ns - 90) / NS_PER_SEC + 1, |
| 141 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 142 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 143 | |
| 144 | // Release wakelock wl1. One anomaly detected. |
| 145 | release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 109, |
| 146 | attributionUids2, attributionTags2, "wl1"); |
| 147 | processor->OnLogEvent(release_event.get()); |
| 148 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 149 | EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, |
| 150 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 151 | |
| 152 | // Acquire wakelock wl1. |
| 153 | acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + NS_PER_SEC + 112, |
| 154 | attributionUids1, attributionTags1, "wl1"); |
| 155 | processor->OnLogEvent(acquire_event.get()); |
| 156 | // Wakelock has been hold longer than the threshold in bucket #0. The alarm is set at the |
| 157 | // end of the refractory period. |
| 158 | const int64_t alarmFiredTimestampSec0 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); |
| 159 | EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + NS_PER_SEC + 109) / NS_PER_SEC + 1, |
| 160 | (uint32_t)alarmFiredTimestampSec0); |
| 161 | |
| 162 | // Anomaly alarm fired. |
| 163 | auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( |
| 164 | static_cast<uint32_t>(alarmFiredTimestampSec0)); |
| 165 | EXPECT_EQ(1u, alarmSet.size()); |
| 166 | processor->onAnomalyAlarmFired(alarmFiredTimestampSec0 * NS_PER_SEC, alarmSet); |
| 167 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 168 | EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, |
| 169 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 170 | |
| 171 | // Release wakelock wl1. |
| 172 | release_event = |
| 173 | CreateReleaseWakelockEvent(alarmFiredTimestampSec0 * NS_PER_SEC + NS_PER_SEC + 1, |
| 174 | attributionUids1, attributionTags1, "wl1"); |
| 175 | processor->OnLogEvent(release_event.get()); |
| 176 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 177 | // Within refractory period. No more anomaly detected. |
| 178 | EXPECT_EQ(refractory_period_sec + alarmFiredTimestampSec0, |
| 179 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 180 | |
| 181 | // Acquire wakelock wl1. |
| 182 | acquire_event = |
| 183 | CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC - 11, |
| 184 | attributionUids2, attributionTags2, "wl1"); |
| 185 | processor->OnLogEvent(acquire_event.get()); |
| 186 | const int64_t alarmFiredTimestampSec1 = anomalyTracker->getAlarmTimestampSec(dimensionKey1); |
| 187 | EXPECT_EQ((bucketStartTimeNs + bucketSizeNs - 5 * NS_PER_SEC) / NS_PER_SEC, |
| 188 | (uint64_t)alarmFiredTimestampSec1); |
| 189 | |
| 190 | // Release wakelock wl1. |
| 191 | release_event = |
| 192 | CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10, |
| 193 | attributionUids2, attributionTags2, "wl1"); |
| 194 | processor->OnLogEvent(release_event.get()); |
| 195 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 196 | EXPECT_EQ(refractory_period_sec + |
| 197 | (bucketStartTimeNs + bucketSizeNs - 4 * NS_PER_SEC - 10) / NS_PER_SEC + 1, |
| 198 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 199 | |
| 200 | alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( |
| 201 | static_cast<uint32_t>(alarmFiredTimestampSec1)); |
| 202 | EXPECT_EQ(0u, alarmSet.size()); |
| 203 | |
| 204 | // Acquire wakelock wl1 near the end of bucket #0. |
| 205 | acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 2, |
| 206 | attributionUids1, attributionTags1, "wl1"); |
| 207 | processor->OnLogEvent(acquire_event.get()); |
| 208 | EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC, |
| 209 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 210 | |
| 211 | // Release the event at early bucket #1. |
| 212 | release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + NS_PER_SEC - 1, |
| 213 | attributionUids1, attributionTags1, "wl1"); |
| 214 | processor->OnLogEvent(release_event.get()); |
| 215 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 216 | // Anomaly detected when stopping the alarm. The refractory period does not change. |
| 217 | EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, |
| 218 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 219 | |
| 220 | // Condition changes to false. |
| 221 | screen_on_event = |
| 222 | CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 20, |
| 223 | android::view::DisplayStateEnum::DISPLAY_STATE_ON); |
| 224 | processor->OnLogEvent(screen_on_event.get()); |
| 225 | EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, |
| 226 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 227 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 228 | |
| 229 | acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 30, |
| 230 | attributionUids2, attributionTags2, "wl1"); |
| 231 | processor->OnLogEvent(acquire_event.get()); |
| 232 | // The condition is false. Do not start the alarm. |
| 233 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 234 | EXPECT_EQ(refractory_period_sec + (bucketStartTimeNs + bucketSizeNs + NS_PER_SEC) / NS_PER_SEC, |
| 235 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 236 | |
| 237 | // Condition turns true. |
| 238 | screen_off_event = |
| 239 | CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC, |
| 240 | android::view::DisplayStateEnum::DISPLAY_STATE_OFF); |
| 241 | processor->OnLogEvent(screen_off_event.get()); |
| 242 | EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs + NS_PER_SEC + threshold_ns) / NS_PER_SEC, |
| 243 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 244 | |
| 245 | // Condition turns to false. |
| 246 | screen_on_event = |
| 247 | CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1, |
| 248 | android::view::DisplayStateEnum::DISPLAY_STATE_ON); |
| 249 | processor->OnLogEvent(screen_on_event.get()); |
| 250 | // Condition turns to false. Cancelled the alarm. |
| 251 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 252 | // Detected one anomaly. |
| 253 | EXPECT_EQ(refractory_period_sec + |
| 254 | (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 1) / NS_PER_SEC + 1, |
| 255 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 256 | |
| 257 | // Condition turns to true again. |
| 258 | screen_off_event = |
| 259 | CreateScreenStateChangedEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC + 2, |
| 260 | android::view::DisplayStateEnum::DISPLAY_STATE_OFF); |
| 261 | processor->OnLogEvent(screen_off_event.get()); |
| 262 | EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 2 + 1, |
| 263 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 264 | |
| 265 | release_event = |
| 266 | CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC, |
| 267 | attributionUids2, attributionTags2, "wl1"); |
| 268 | processor->OnLogEvent(release_event.get()); |
| 269 | EXPECT_EQ(refractory_period_sec + |
| 270 | (bucketStartTimeNs + 2 * bucketSizeNs + 5 * NS_PER_SEC) / NS_PER_SEC, |
| 271 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 272 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 273 | } |
| 274 | |
| 275 | TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets) { |
| 276 | const int num_buckets = 3; |
| 277 | const uint64_t threshold_ns = NS_PER_SEC; |
| 278 | auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, true); |
| 279 | const uint64_t alert_id = config.alert(0).id(); |
| 280 | const uint32_t refractory_period_sec = config.alert(0).refractory_period_secs(); |
| 281 | |
| 282 | int64_t bucketStartTimeNs = 10 * NS_PER_SEC; |
| 283 | int64_t bucketSizeNs = |
| 284 | TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; |
| 285 | |
| 286 | ConfigKey cfgKey; |
| 287 | auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); |
| 288 | EXPECT_EQ(processor->mMetricsManagers.size(), 1u); |
| 289 | EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); |
| 290 | EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); |
| 291 | |
| 292 | sp<AnomalyTracker> anomalyTracker = |
| 293 | processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; |
| 294 | |
| 295 | auto screen_off_event = CreateScreenStateChangedEvent( |
| 296 | bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); |
| 297 | processor->OnLogEvent(screen_off_event.get()); |
| 298 | |
| 299 | // Acquire wakelock "wc1" in bucket #0. |
| 300 | auto acquire_event = |
| 301 | CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - NS_PER_SEC / 2 - 1, |
| 302 | attributionUids1, attributionTags1, "wl1"); |
| 303 | processor->OnLogEvent(acquire_event.get()); |
| 304 | EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, |
| 305 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 306 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 307 | |
| 308 | // Release wakelock "wc1" in bucket #0. |
| 309 | auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs - 1, |
| 310 | attributionUids1, attributionTags1, "wl1"); |
| 311 | processor->OnLogEvent(release_event.get()); |
| 312 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 313 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 314 | |
| 315 | // Acquire wakelock "wc1" in bucket #1. |
| 316 | acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 1, |
| 317 | attributionUids2, attributionTags2, "wl1"); |
| 318 | processor->OnLogEvent(acquire_event.get()); |
| 319 | EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 1, |
| 320 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 321 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 322 | |
| 323 | release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + bucketSizeNs + 100, |
| 324 | attributionUids2, attributionTags2, "wl1"); |
| 325 | processor->OnLogEvent(release_event.get()); |
| 326 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 327 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 328 | |
| 329 | // Acquire wakelock "wc2" in bucket #2. |
| 330 | acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, |
| 331 | attributionUids3, attributionTags3, "wl2"); |
| 332 | processor->OnLogEvent(acquire_event.get()); |
| 333 | EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2, |
| 334 | anomalyTracker->getAlarmTimestampSec(dimensionKey2)); |
| 335 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); |
| 336 | |
| 337 | // Release wakelock "wc2" in bucket #2. |
| 338 | release_event = |
| 339 | CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC, |
| 340 | attributionUids3, attributionTags3, "wl2"); |
| 341 | processor->OnLogEvent(release_event.get()); |
| 342 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); |
| 343 | EXPECT_EQ(refractory_period_sec + |
| 344 | (bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC) / NS_PER_SEC, |
| 345 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey2)); |
| 346 | |
| 347 | // Acquire wakelock "wc1" in bucket #2. |
| 348 | acquire_event = |
| 349 | CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2 * NS_PER_SEC, |
| 350 | attributionUids2, attributionTags2, "wl1"); |
| 351 | processor->OnLogEvent(acquire_event.get()); |
| 352 | EXPECT_EQ((bucketStartTimeNs + 2 * bucketSizeNs) / NS_PER_SEC + 2 + 1, |
| 353 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 354 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 355 | |
| 356 | // Release wakelock "wc1" in bucket #2. |
| 357 | release_event = |
| 358 | CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC, |
| 359 | attributionUids2, attributionTags2, "wl1"); |
| 360 | processor->OnLogEvent(release_event.get()); |
| 361 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 362 | EXPECT_EQ(refractory_period_sec + |
| 363 | (int64_t)(bucketStartTimeNs + 2 * bucketSizeNs + 2.5 * NS_PER_SEC) / |
| 364 | NS_PER_SEC + |
| 365 | 1, |
| 366 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 367 | |
| 368 | acquire_event = |
| 369 | CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 4, |
| 370 | attributionUids3, attributionTags3, "wl2"); |
| 371 | processor->OnLogEvent(acquire_event.get()); |
| 372 | acquire_event = |
| 373 | CreateAcquireWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs - NS_PER_SEC + 5, |
| 374 | attributionUids1, attributionTags1, "wl1"); |
| 375 | processor->OnLogEvent(acquire_event.get()); |
| 376 | EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, |
| 377 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 378 | EXPECT_EQ((bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + 1, |
| 379 | anomalyTracker->getAlarmTimestampSec(dimensionKey2)); |
| 380 | |
| 381 | release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 2, |
| 382 | attributionUids3, attributionTags3, "wl2"); |
| 383 | processor->OnLogEvent(release_event.get()); |
| 384 | release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 6 * bucketSizeNs + 6, |
| 385 | attributionUids1, attributionTags1, "wl1"); |
| 386 | processor->OnLogEvent(release_event.get()); |
| 387 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 388 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey2)); |
| 389 | // The buckets are not messed up across dimensions. Only one dimension has anomaly triggered. |
| 390 | EXPECT_EQ(refractory_period_sec + (int64_t)(bucketStartTimeNs + 6 * bucketSizeNs) / NS_PER_SEC + |
| 391 | 1, |
| 392 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 393 | } |
| 394 | |
| 395 | TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period) { |
| 396 | const int num_buckets = 2; |
| 397 | const uint64_t threshold_ns = 3 * NS_PER_SEC; |
| 398 | auto config = CreateStatsdConfig(num_buckets, threshold_ns, DurationMetric::SUM, false); |
| 399 | int64_t bucketStartTimeNs = 10 * NS_PER_SEC; |
| 400 | int64_t bucketSizeNs = |
| 401 | TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000; |
| 402 | |
| 403 | const uint64_t alert_id = config.alert(0).id(); |
| 404 | const uint32_t refractory_period_sec = 3 * bucketSizeNs / NS_PER_SEC; |
| 405 | config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec); |
| 406 | |
| 407 | ConfigKey cfgKey; |
| 408 | auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey); |
| 409 | EXPECT_EQ(processor->mMetricsManagers.size(), 1u); |
| 410 | EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid()); |
| 411 | EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size()); |
| 412 | |
| 413 | sp<AnomalyTracker> anomalyTracker = |
| 414 | processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers[0]; |
| 415 | |
| 416 | auto screen_off_event = CreateScreenStateChangedEvent( |
| 417 | bucketStartTimeNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_OFF); |
| 418 | processor->OnLogEvent(screen_off_event.get()); |
| 419 | |
| 420 | // Acquire wakelock "wc1" in bucket #0. |
| 421 | auto acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs - 100, |
| 422 | attributionUids1, attributionTags1, "wl1"); |
| 423 | processor->OnLogEvent(acquire_event.get()); |
| 424 | EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, |
| 425 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 426 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 427 | |
| 428 | // Acquire the wakelock "wc1" again. |
| 429 | acquire_event = |
| 430 | CreateAcquireWakelockEvent(bucketStartTimeNs + bucketSizeNs + 2 * NS_PER_SEC + 1, |
| 431 | attributionUids1, attributionTags1, "wl1"); |
| 432 | processor->OnLogEvent(acquire_event.get()); |
| 433 | // The alarm does not change. |
| 434 | EXPECT_EQ((bucketStartTimeNs + bucketSizeNs) / NS_PER_SEC + 3, |
| 435 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 436 | EXPECT_EQ(0u, anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 437 | |
| 438 | // Anomaly alarm fired late. |
| 439 | const int64_t firedAlarmTimestampNs = bucketStartTimeNs + 2 * bucketSizeNs - NS_PER_SEC; |
| 440 | auto alarmSet = processor->getAnomalyAlarmMonitor()->popSoonerThan( |
| 441 | static_cast<uint32_t>(firedAlarmTimestampNs / NS_PER_SEC)); |
| 442 | EXPECT_EQ(1u, alarmSet.size()); |
| 443 | processor->onAnomalyAlarmFired(firedAlarmTimestampNs, alarmSet); |
| 444 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 445 | EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, |
| 446 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 447 | |
| 448 | acquire_event = CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs - 100, |
| 449 | attributionUids1, attributionTags1, "wl1"); |
| 450 | processor->OnLogEvent(acquire_event.get()); |
| 451 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 452 | EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, |
| 453 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 454 | |
| 455 | auto release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 1, |
| 456 | attributionUids1, attributionTags1, "wl1"); |
| 457 | processor->OnLogEvent(release_event.get()); |
| 458 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 459 | // Within the refractory period. No anomaly. |
| 460 | EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, |
| 461 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 462 | |
| 463 | // A new wakelock, but still within refractory period. |
| 464 | acquire_event = |
| 465 | CreateAcquireWakelockEvent(bucketStartTimeNs + 2 * bucketSizeNs + 10 * NS_PER_SEC, |
| 466 | attributionUids1, attributionTags1, "wl1"); |
| 467 | processor->OnLogEvent(acquire_event.get()); |
| 468 | EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, |
| 469 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 470 | |
| 471 | release_event = CreateReleaseWakelockEvent(bucketStartTimeNs + 3 * bucketSizeNs - NS_PER_SEC, |
| 472 | attributionUids1, attributionTags1, "wl1"); |
| 473 | // Still in the refractory period. No anomaly. |
| 474 | processor->OnLogEvent(release_event.get()); |
| 475 | EXPECT_EQ(refractory_period_sec + firedAlarmTimestampNs / NS_PER_SEC, |
| 476 | anomalyTracker->getRefractoryPeriodEndsSec(dimensionKey1)); |
| 477 | |
| 478 | acquire_event = |
| 479 | CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 5, |
| 480 | attributionUids1, attributionTags1, "wl1"); |
| 481 | processor->OnLogEvent(acquire_event.get()); |
| 482 | EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, |
| 483 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 484 | |
| 485 | release_event = |
| 486 | CreateReleaseWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 4, |
| 487 | attributionUids1, attributionTags1, "wl1"); |
| 488 | processor->OnLogEvent(release_event.get()); |
| 489 | EXPECT_EQ(0u, anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 490 | |
| 491 | acquire_event = |
| 492 | CreateAcquireWakelockEvent(bucketStartTimeNs + 5 * bucketSizeNs - 3 * NS_PER_SEC - 3, |
| 493 | attributionUids1, attributionTags1, "wl1"); |
| 494 | processor->OnLogEvent(acquire_event.get()); |
| 495 | EXPECT_EQ((bucketStartTimeNs + 5 * bucketSizeNs) / NS_PER_SEC, |
| 496 | anomalyTracker->getAlarmTimestampSec(dimensionKey1)); |
| 497 | } |
Yangster-mac | be10ddf | 2018-03-13 15:39:51 -0700 | [diff] [blame] | 498 | |
| 499 | #else |
| 500 | GTEST_LOG_(INFO) << "This test does nothing.\n"; |
| 501 | #endif |
| 502 | |
| 503 | } // namespace statsd |
| 504 | } // namespace os |
| 505 | } // namespace android |