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/matchers/SimpleLogMatchingTracker.h" 16 #include "src/metrics/GaugeMetricProducer.h" 17 #include "src/stats_log_util.h" 18 #include "logd/LogEvent.h" 19 #include "metrics_test_helper.h" 20 #include "tests/statsd_test_util.h" 21 22 #include <gmock/gmock.h> 23 #include <gtest/gtest.h> 24 #include <math.h> 25 #include <stdio.h> 26 #include <vector> 27 28 using namespace testing; 29 using android::sp; 30 using std::set; 31 using std::unordered_map; 32 using std::vector; 33 using std::make_shared; 34 35 #ifdef __ANDROID__ 36 37 namespace android { 38 namespace os { 39 namespace statsd { 40 41 const ConfigKey kConfigKey(0, 12345); 42 const int tagId = 1; 43 const int64_t metricId = 123; 44 const int64_t atomMatcherId = 678; 45 const int logEventMatcherIndex = 0; 46 const int64_t bucketStartTimeNs = 10 * NS_PER_SEC; 47 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 48 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; 49 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; 50 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; 51 const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; 52 53 /* 54 * Tests that the first bucket works correctly 55 */ 56 TEST(GaugeMetricProducerTest, TestFirstBucket) { 57 GaugeMetric metric; 58 metric.set_id(metricId); 59 metric.set_bucket(ONE_MINUTE); 60 metric.mutable_gauge_fields_filter()->set_include_all(false); 61 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); 62 gaugeFieldMatcher->set_field(tagId); 63 gaugeFieldMatcher->add_child()->set_field(1); 64 gaugeFieldMatcher->add_child()->set_field(3); 65 66 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 67 68 UidMap uidMap; 69 SimpleAtomMatcher atomMatcher; 70 atomMatcher.set_atom_id(tagId); 71 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 72 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 73 74 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 75 76 // statsd started long ago. 77 // The metric starts in the middle of the bucket 78 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 79 logEventMatcherIndex, eventMatcherWizard, 80 -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, 81 pullerManager); 82 gaugeProducer.prepareFirstBucket(); 83 84 85 EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs); 86 EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum); 87 EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs()); 88 } 89 90 TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) { 91 GaugeMetric metric; 92 metric.set_id(metricId); 93 metric.set_bucket(ONE_MINUTE); 94 metric.mutable_gauge_fields_filter()->set_include_all(false); 95 metric.set_max_pull_delay_sec(INT_MAX); 96 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); 97 gaugeFieldMatcher->set_field(tagId); 98 gaugeFieldMatcher->add_child()->set_field(1); 99 gaugeFieldMatcher->add_child()->set_field(3); 100 101 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 102 103 UidMap uidMap; 104 SimpleAtomMatcher atomMatcher; 105 atomMatcher.set_atom_id(tagId); 106 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 107 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 108 109 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 110 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); 111 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 112 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 113 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 114 data->clear(); 115 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 116 event->write(3); 117 event->write("some value"); 118 event->write(11); 119 event->init(); 120 data->push_back(event); 121 return true; 122 })); 123 124 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 125 logEventMatcherIndex, eventMatcherWizard, 126 tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, 127 pullerManager); 128 129 gaugeProducer.prepareFirstBucket(); 130 131 vector<shared_ptr<LogEvent>> allData; 132 allData.clear(); 133 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); 134 event->write(10); 135 event->write("some value"); 136 event->write(11); 137 event->init(); 138 allData.push_back(event); 139 140 gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); 141 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 142 auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); 143 EXPECT_EQ(INT, it->mValue.getType()); 144 EXPECT_EQ(10, it->mValue.int_value); 145 it++; 146 EXPECT_EQ(11, it->mValue.int_value); 147 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); 148 EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms 149 .front().mFields->begin()->mValue.int_value); 150 151 allData.clear(); 152 std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10); 153 event2->write(24); 154 event2->write("some value"); 155 event2->write(25); 156 event2->init(); 157 allData.push_back(event2); 158 gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); 159 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 160 it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin(); 161 EXPECT_EQ(INT, it->mValue.getType()); 162 EXPECT_EQ(24, it->mValue.int_value); 163 it++; 164 EXPECT_EQ(INT, it->mValue.getType()); 165 EXPECT_EQ(25, it->mValue.int_value); 166 // One dimension. 167 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); 168 EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); 169 it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); 170 EXPECT_EQ(INT, it->mValue.getType()); 171 EXPECT_EQ(10L, it->mValue.int_value); 172 it++; 173 EXPECT_EQ(INT, it->mValue.getType()); 174 EXPECT_EQ(11L, it->mValue.int_value); 175 176 gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs); 177 EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); 178 // One dimension. 179 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); 180 EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size()); 181 it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin(); 182 EXPECT_EQ(INT, it->mValue.getType()); 183 EXPECT_EQ(24L, it->mValue.int_value); 184 it++; 185 EXPECT_EQ(INT, it->mValue.getType()); 186 EXPECT_EQ(25L, it->mValue.int_value); 187 } 188 189 TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade) { 190 sp<AlarmMonitor> alarmMonitor; 191 GaugeMetric metric; 192 metric.set_id(metricId); 193 metric.set_bucket(ONE_MINUTE); 194 metric.mutable_gauge_fields_filter()->set_include_all(true); 195 196 Alert alert; 197 alert.set_id(101); 198 alert.set_metric_id(metricId); 199 alert.set_trigger_if_sum_gt(25); 200 alert.set_num_buckets(100); 201 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 202 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 203 204 UidMap uidMap; 205 SimpleAtomMatcher atomMatcher; 206 atomMatcher.set_atom_id(tagId); 207 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 208 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 209 210 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 211 logEventMatcherIndex, eventMatcherWizard, 212 -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs, 213 bucketStartTimeNs, pullerManager); 214 gaugeProducer.prepareFirstBucket(); 215 216 sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); 217 EXPECT_TRUE(anomalyTracker != nullptr); 218 219 shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 220 event1->write(1); 221 event1->write(10); 222 event1->init(); 223 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); 224 EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); 225 226 gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 227 EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY)); 228 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 229 EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); 230 EXPECT_EQ(eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); 231 // Partial buckets are not sent to anomaly tracker. 232 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); 233 234 // Create an event in the same partial bucket. 235 shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC); 236 event2->write(1); 237 event2->write(10); 238 event2->init(); 239 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); 240 EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); 241 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 242 EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); 243 // Partial buckets are not sent to anomaly tracker. 244 EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); 245 246 // Next event should trigger creation of new bucket and send previous full bucket to anomaly 247 // tracker. 248 shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC); 249 event3->write(1); 250 event3->write(10); 251 event3->init(); 252 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); 253 EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum); 254 EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 255 EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs); 256 EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); 257 258 // Next event should trigger creation of new bucket. 259 shared_ptr<LogEvent> event4 = 260 make_shared<LogEvent>(tagId, bucketStartTimeNs + 125 * NS_PER_SEC); 261 event4->write(1); 262 event4->write(10); 263 event4->init(); 264 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); 265 EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum); 266 EXPECT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 267 EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); 268 } 269 270 TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) { 271 GaugeMetric metric; 272 metric.set_id(metricId); 273 metric.set_bucket(ONE_MINUTE); 274 metric.set_max_pull_delay_sec(INT_MAX); 275 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); 276 gaugeFieldMatcher->set_field(tagId); 277 gaugeFieldMatcher->add_child()->set_field(2); 278 279 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 280 281 UidMap uidMap; 282 SimpleAtomMatcher atomMatcher; 283 atomMatcher.set_atom_id(tagId); 284 sp<EventMatcherWizard> eventMatcherWizard = 285 new EventMatcherWizard({new SimpleLogMatchingTracker( 286 atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 287 288 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 289 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); 290 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 291 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 292 .WillOnce(Return(false)) 293 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 294 data->clear(); 295 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs); 296 event->write("some value"); 297 event->write(2); 298 event->init(); 299 data->push_back(event); 300 return true; 301 })); 302 303 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 304 logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, 305 bucketStartTimeNs, bucketStartTimeNs, pullerManager); 306 gaugeProducer.prepareFirstBucket(); 307 308 vector<shared_ptr<LogEvent>> allData; 309 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); 310 event->write("some value"); 311 event->write(1); 312 event->init(); 313 allData.push_back(event); 314 gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); 315 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 316 EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() 317 ->second.front() 318 .mFields->begin() 319 ->mValue.int_value); 320 321 gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 322 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 323 EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); 324 EXPECT_EQ((int64_t)eventUpgradeTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); 325 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 326 EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin() 327 ->second.front() 328 .mFields->begin() 329 ->mValue.int_value); 330 331 allData.clear(); 332 event = make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 1); 333 event->write("some value"); 334 event->write(3); 335 event->init(); 336 allData.push_back(event); 337 gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); 338 EXPECT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 339 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 340 EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin() 341 ->second.front() 342 .mFields->begin() 343 ->mValue.int_value); 344 } 345 346 TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) { 347 GaugeMetric metric; 348 metric.set_id(metricId); 349 metric.set_bucket(ONE_MINUTE); 350 metric.set_max_pull_delay_sec(INT_MAX); 351 metric.set_split_bucket_for_app_upgrade(false); 352 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); 353 gaugeFieldMatcher->set_field(tagId); 354 gaugeFieldMatcher->add_child()->set_field(2); 355 356 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 357 358 UidMap uidMap; 359 SimpleAtomMatcher atomMatcher; 360 atomMatcher.set_atom_id(tagId); 361 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 362 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 363 364 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 365 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); 366 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 367 EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); 368 369 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 370 logEventMatcherIndex, eventMatcherWizard, 371 tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, 372 pullerManager); 373 gaugeProducer.prepareFirstBucket(); 374 375 vector<shared_ptr<LogEvent>> allData; 376 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); 377 event->write("some value"); 378 event->write(1); 379 event->init(); 380 allData.push_back(event); 381 gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); 382 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 383 EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() 384 ->second.front() 385 .mFields->begin() 386 ->mValue.int_value); 387 388 gaugeProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 389 EXPECT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 390 EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum); 391 EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs); 392 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 393 EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin() 394 ->second.front() 395 .mFields->begin() 396 ->mValue.int_value); 397 } 398 399 TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) { 400 GaugeMetric metric; 401 metric.set_id(metricId); 402 metric.set_bucket(ONE_MINUTE); 403 metric.set_max_pull_delay_sec(INT_MAX); 404 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); 405 gaugeFieldMatcher->set_field(tagId); 406 gaugeFieldMatcher->add_child()->set_field(2); 407 metric.set_condition(StringToId("SCREEN_ON")); 408 409 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 410 411 UidMap uidMap; 412 SimpleAtomMatcher atomMatcher; 413 atomMatcher.set_atom_id(tagId); 414 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 415 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 416 417 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 418 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); 419 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 420 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 421 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 422 data->clear(); 423 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 424 event->write("some value"); 425 event->write(100); 426 event->init(); 427 data->push_back(event); 428 return true; 429 })); 430 431 GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, 432 logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, 433 bucketStartTimeNs, bucketStartTimeNs, pullerManager); 434 gaugeProducer.prepareFirstBucket(); 435 436 gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8); 437 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 438 EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin() 439 ->second.front() 440 .mFields->begin() 441 ->mValue.int_value); 442 EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); 443 444 vector<shared_ptr<LogEvent>> allData; 445 allData.clear(); 446 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); 447 event->write("some value"); 448 event->write(110); 449 event->init(); 450 allData.push_back(event); 451 gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); 452 453 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 454 EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin() 455 ->second.front() 456 .mFields->begin() 457 ->mValue.int_value); 458 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); 459 EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin() 460 ->second.back() 461 .mGaugeAtoms.front() 462 .mFields->begin() 463 ->mValue.int_value); 464 465 gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10); 466 gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10); 467 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); 468 EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size()); 469 EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin() 470 ->second.back() 471 .mGaugeAtoms.front() 472 .mFields->begin() 473 ->mValue.int_value); 474 } 475 476 TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) { 477 const int conditionTag = 65; 478 GaugeMetric metric; 479 metric.set_id(1111111); 480 metric.set_bucket(ONE_MINUTE); 481 metric.mutable_gauge_fields_filter()->set_include_all(true); 482 metric.set_condition(StringToId("APP_DIED")); 483 metric.set_max_pull_delay_sec(INT_MAX); 484 auto dim = metric.mutable_dimensions_in_what(); 485 dim->set_field(tagId); 486 dim->add_child()->set_field(1); 487 488 dim = metric.mutable_dimensions_in_condition(); 489 dim->set_field(conditionTag); 490 dim->add_child()->set_field(1); 491 492 UidMap uidMap; 493 SimpleAtomMatcher atomMatcher; 494 atomMatcher.set_atom_id(tagId); 495 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 496 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 497 498 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 499 EXPECT_CALL(*wizard, query(_, _, _, _, _, _)) 500 .WillRepeatedly( 501 Invoke([](const int conditionIndex, const ConditionKey& conditionParameters, 502 const vector<Matcher>& dimensionFields, const bool isSubsetDim, 503 const bool isPartialLink, 504 std::unordered_set<HashableDimensionKey>* dimensionKeySet) { 505 dimensionKeySet->clear(); 506 int pos[] = {1, 0, 0}; 507 Field f(conditionTag, pos, 0); 508 HashableDimensionKey key; 509 key.mutableValues()->emplace_back(f, Value((int32_t)1000000)); 510 dimensionKeySet->insert(key); 511 512 return ConditionState::kTrue; 513 })); 514 515 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 516 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); 517 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 518 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 519 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 520 data->clear(); 521 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 522 event->write(1000); 523 event->write(100); 524 event->init(); 525 data->push_back(event); 526 return true; 527 })); 528 529 GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, 530 logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId, 531 bucketStartTimeNs, bucketStartTimeNs, pullerManager); 532 gaugeProducer.prepareFirstBucket(); 533 534 gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8); 535 536 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 537 const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first; 538 EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size()); 539 EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value); 540 541 EXPECT_EQ(1UL, key.getDimensionKeyInCondition().getValues().size()); 542 EXPECT_EQ(1000000, key.getDimensionKeyInCondition().getValues()[0].mValue.int_value); 543 544 EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size()); 545 546 vector<shared_ptr<LogEvent>> allData; 547 allData.clear(); 548 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); 549 event->write(1000); 550 event->write(110); 551 event->init(); 552 allData.push_back(event); 553 gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); 554 555 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 556 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); 557 } 558 559 TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) { 560 sp<AlarmMonitor> alarmMonitor; 561 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 562 563 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 564 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); 565 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); 566 EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Return(false)); 567 568 GaugeMetric metric; 569 metric.set_id(metricId); 570 metric.set_bucket(ONE_MINUTE); 571 metric.set_max_pull_delay_sec(INT_MAX); 572 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); 573 gaugeFieldMatcher->set_field(tagId); 574 gaugeFieldMatcher->add_child()->set_field(2); 575 576 UidMap uidMap; 577 SimpleAtomMatcher atomMatcher; 578 atomMatcher.set_atom_id(tagId); 579 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 580 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 581 582 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 583 logEventMatcherIndex, eventMatcherWizard, 584 tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs, 585 pullerManager); 586 gaugeProducer.prepareFirstBucket(); 587 588 Alert alert; 589 alert.set_id(101); 590 alert.set_metric_id(metricId); 591 alert.set_trigger_if_sum_gt(25); 592 alert.set_num_buckets(2); 593 const int32_t refPeriodSec = 60; 594 alert.set_refractory_period_secs(refPeriodSec); 595 sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor); 596 597 int tagId = 1; 598 std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); 599 event1->write("some value"); 600 event1->write(13); 601 event1->init(); 602 603 gaugeProducer.onDataPulled({event1}, /** succeed */ true, bucketStartTimeNs); 604 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 605 EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin() 606 ->second.front() 607 .mFields->begin() 608 ->mValue.int_value); 609 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U); 610 611 std::shared_ptr<LogEvent> event2 = 612 std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20); 613 event2->write("some value"); 614 event2->write(15); 615 event2->init(); 616 617 gaugeProducer.onDataPulled({event2}, /** succeed */ true, bucketStartTimeNs + bucketSizeNs); 618 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 619 EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin() 620 ->second.front() 621 .mFields->begin() 622 ->mValue.int_value); 623 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 624 std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec); 625 626 std::shared_ptr<LogEvent> event3 = 627 std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10); 628 event3->write("some value"); 629 event3->write(26); 630 event3->init(); 631 632 gaugeProducer.onDataPulled({event3}, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs); 633 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 634 EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin() 635 ->second.front() 636 .mFields->begin() 637 ->mValue.int_value); 638 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 639 std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); 640 641 // The event4 does not have the gauge field. Thus the current bucket value is 0. 642 std::shared_ptr<LogEvent> event4 = 643 std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10); 644 event4->write("some value"); 645 event4->init(); 646 gaugeProducer.onDataPulled({event4}, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs); 647 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 648 EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty()); 649 } 650 651 TEST(GaugeMetricProducerTest, TestPullOnTrigger) { 652 GaugeMetric metric; 653 metric.set_id(metricId); 654 metric.set_bucket(ONE_MINUTE); 655 metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); 656 metric.mutable_gauge_fields_filter()->set_include_all(false); 657 metric.set_max_pull_delay_sec(INT_MAX); 658 auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields(); 659 gaugeFieldMatcher->set_field(tagId); 660 gaugeFieldMatcher->add_child()->set_field(1); 661 662 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 663 664 UidMap uidMap; 665 SimpleAtomMatcher atomMatcher; 666 atomMatcher.set_atom_id(tagId); 667 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 668 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 669 670 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 671 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 672 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 673 data->clear(); 674 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 675 event->write(4); 676 event->init(); 677 data->push_back(event); 678 return true; 679 })) 680 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 681 data->clear(); 682 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); 683 event->write(5); 684 event->init(); 685 data->push_back(event); 686 return true; 687 })) 688 .WillOnce(Return(true)); 689 690 int triggerId = 5; 691 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 692 logEventMatcherIndex, eventMatcherWizard, 693 tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, 694 pullerManager); 695 gaugeProducer.prepareFirstBucket(); 696 697 vector<shared_ptr<LogEvent>> allData; 698 699 EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size()); 700 LogEvent trigger(triggerId, bucketStartTimeNs + 10); 701 trigger.init(); 702 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); 703 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); 704 trigger.setElapsedTimestampNs(bucketStartTimeNs + 20); 705 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); 706 EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); 707 trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1); 708 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); 709 710 EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size()); 711 EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size()); 712 EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin() 713 ->second.back() 714 .mGaugeAtoms[0] 715 .mFields->begin() 716 ->mValue.int_value); 717 EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin() 718 ->second.back() 719 .mGaugeAtoms[1] 720 .mFields->begin() 721 ->mValue.int_value); 722 } 723 724 TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) { 725 GaugeMetric metric; 726 metric.set_id(metricId); 727 metric.set_bucket(ONE_MINUTE); 728 metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES); 729 metric.mutable_gauge_fields_filter()->set_include_all(true); 730 metric.set_max_pull_delay_sec(INT_MAX); 731 auto dimensionMatcher = metric.mutable_dimensions_in_what(); 732 // use field 1 as dimension. 733 dimensionMatcher->set_field(tagId); 734 dimensionMatcher->add_child()->set_field(1); 735 736 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 737 738 UidMap uidMap; 739 SimpleAtomMatcher atomMatcher; 740 atomMatcher.set_atom_id(tagId); 741 sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({ 742 new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); 743 744 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); 745 EXPECT_CALL(*pullerManager, Pull(tagId, _)) 746 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 747 data->clear(); 748 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3); 749 event->write(3); 750 event->write(4); 751 event->init(); 752 data->push_back(event); 753 return true; 754 })) 755 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 756 data->clear(); 757 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); 758 event->write(4); 759 event->write(5); 760 event->init(); 761 data->push_back(event); 762 return true; 763 })) 764 .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { 765 data->clear(); 766 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20); 767 event->write(4); 768 event->write(6); 769 event->init(); 770 data->push_back(event); 771 return true; 772 })) 773 .WillOnce(Return(true)); 774 775 int triggerId = 5; 776 GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 777 logEventMatcherIndex, eventMatcherWizard, 778 tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs, 779 pullerManager); 780 gaugeProducer.prepareFirstBucket(); 781 782 vector<shared_ptr<LogEvent>> allData; 783 784 LogEvent trigger(triggerId, bucketStartTimeNs + 3); 785 trigger.init(); 786 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); 787 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size()); 788 trigger.setElapsedTimestampNs(bucketStartTimeNs + 10); 789 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); 790 EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size()); 791 EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); 792 trigger.setElapsedTimestampNs(bucketStartTimeNs + 20); 793 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); 794 EXPECT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size()); 795 trigger.setElapsedTimestampNs(bucket2StartTimeNs + 1); 796 gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, trigger); 797 798 EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.size()); 799 auto bucketIt = gaugeProducer.mPastBuckets.begin(); 800 EXPECT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size()); 801 EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); 802 EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); 803 bucketIt++; 804 EXPECT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size()); 805 EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value); 806 EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value); 807 EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value); 808 } 809 810 } // namespace statsd 811 } // namespace os 812 } // namespace android 813 #else 814 GTEST_LOG_(INFO) << "This test does nothing.\n"; 815 #endif 816