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/metrics/DurationMetricProducer.h" 16#include "src/stats_log_util.h" 17#include "metrics_test_helper.h" 18#include "src/condition/ConditionWizard.h" 19 20#include <gmock/gmock.h> 21#include <gtest/gtest.h> 22#include <stdio.h> 23#include <set> 24#include <unordered_map> 25#include <vector> 26 27using namespace android::os::statsd; 28using namespace testing; 29using android::sp; 30using std::set; 31using std::unordered_map; 32using std::vector; 33 34#ifdef __ANDROID__ 35 36namespace android { 37namespace os { 38namespace statsd { 39 40const ConfigKey kConfigKey(0, 12345); 41 42TEST(DurationMetricTrackerTest, TestNoCondition) { 43 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 44 int64_t bucketStartTimeNs = 10000000000; 45 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 46 47 DurationMetric metric; 48 metric.set_id(1); 49 metric.set_bucket(ONE_MINUTE); 50 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 51 52 int tagId = 1; 53 LogEvent event1(tagId, bucketStartTimeNs + 1); 54 event1.init(); 55 LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2); 56 event2.init(); 57 58 FieldMatcher dimensions; 59 DurationMetricProducer durationProducer( 60 kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */, 61 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); 62 durationProducer.setBucketSize(60 * NS_PER_SEC); 63 64 durationProducer.onMatchedLogEvent(1 /* start index*/, event1); 65 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); 66 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); 67 EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); 68 EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != 69 durationProducer.mPastBuckets.end()); 70 const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 71 EXPECT_EQ(2UL, buckets.size()); 72 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); 73 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); 74 EXPECT_EQ(bucketSizeNs - 1LL, buckets[0].mDuration); 75 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); 76 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[1].mBucketEndNs); 77 EXPECT_EQ(2LL, buckets[1].mDuration); 78} 79 80TEST(DurationMetricTrackerTest, TestNonSlicedCondition) { 81 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 82 int64_t bucketStartTimeNs = 10000000000; 83 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 84 85 DurationMetric metric; 86 metric.set_id(1); 87 metric.set_bucket(ONE_MINUTE); 88 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 89 90 int tagId = 1; 91 LogEvent event1(tagId, bucketStartTimeNs + 1); 92 event1.init(); 93 LogEvent event2(tagId, bucketStartTimeNs + 2); 94 event2.init(); 95 LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1); 96 event3.init(); 97 LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3); 98 event4.init(); 99 100 FieldMatcher dimensions; 101 DurationMetricProducer durationProducer( 102 kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */, 103 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); 104 durationProducer.setBucketSize(60 * NS_PER_SEC); 105 106 EXPECT_FALSE(durationProducer.mCondition); 107 EXPECT_FALSE(durationProducer.isConditionSliced()); 108 109 durationProducer.onMatchedLogEvent(1 /* start index*/, event1); 110 durationProducer.onMatchedLogEvent(2 /* stop index*/, event2); 111 durationProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1); 112 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 113 114 durationProducer.onMatchedLogEvent(1 /* start index*/, event3); 115 durationProducer.onConditionChanged(true /* condition */, bucketStartTimeNs + bucketSizeNs + 2); 116 durationProducer.onMatchedLogEvent(2 /* stop index*/, event4); 117 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); 118 EXPECT_EQ(1UL, durationProducer.mPastBuckets.size()); 119 EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) != 120 durationProducer.mPastBuckets.end()); 121 const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 122 EXPECT_EQ(1UL, buckets2.size()); 123 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs); 124 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs); 125 EXPECT_EQ(1LL, buckets2[0].mDuration); 126} 127 128TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) { 129 /** 130 * The duration starts from the first bucket, through the two partial buckets (10-70sec), 131 * another bucket, and ends at the beginning of the next full bucket. 132 * Expected buckets: 133 * - [10,25]: 14 secs 134 * - [25,70]: All 45 secs 135 * - [70,130]: All 60 secs 136 * - [130, 210]: Only 5 secs (event ended at 135sec) 137 */ 138 int64_t bucketStartTimeNs = 10000000000; 139 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 140 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; 141 int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; 142 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; 143 144 int tagId = 1; 145 146 DurationMetric metric; 147 metric.set_id(1); 148 metric.set_bucket(ONE_MINUTE); 149 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 150 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 151 FieldMatcher dimensions; 152 DurationMetricProducer durationProducer( 153 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 154 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); 155 durationProducer.setBucketSize(60 * NS_PER_SEC); 156 157 LogEvent start_event(tagId, startTimeNs); 158 start_event.init(); 159 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 160 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 161 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); 162 163 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 164 EXPECT_EQ(1UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 165 std::vector<DurationBucket> buckets = 166 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 167 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); 168 EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketEndNs); 169 EXPECT_EQ(eventUpgradeTimeNs - startTimeNs, buckets[0].mDuration); 170 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 171 172 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. 173 LogEvent end_event(tagId, endTimeNs); 174 end_event.init(); 175 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 176 buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 177 EXPECT_EQ(3UL, buckets.size()); 178 EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketStartNs); 179 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketEndNs); 180 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - eventUpgradeTimeNs, buckets[1].mDuration); 181 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[2].mBucketStartNs); 182 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); 183 EXPECT_EQ(bucketSizeNs, buckets[2].mDuration); 184} 185 186TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) { 187 /** 188 * Expected buckets (start at 11s, upgrade at 75s, end at 135s): 189 * - [10,70]: 59 secs 190 * - [70,75]: 5 sec 191 * - [75,130]: 55 secs 192 */ 193 int64_t bucketStartTimeNs = 10000000000; 194 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 195 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; 196 int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC; 197 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; 198 199 int tagId = 1; 200 201 DurationMetric metric; 202 metric.set_id(1); 203 metric.set_bucket(ONE_MINUTE); 204 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 205 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 206 FieldMatcher dimensions; 207 DurationMetricProducer durationProducer( 208 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 209 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); 210 durationProducer.setBucketSize(60 * NS_PER_SEC); 211 212 LogEvent start_event(tagId, startTimeNs); 213 start_event.init(); 214 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 215 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 216 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); 217 218 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 219 EXPECT_EQ(2UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 220 std::vector<DurationBucket> buckets = 221 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 222 EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs); 223 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs); 224 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, buckets[0].mDuration); 225 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[1].mBucketStartNs); 226 EXPECT_EQ(eventUpgradeTimeNs, buckets[1].mBucketEndNs); 227 EXPECT_EQ(eventUpgradeTimeNs - (bucketStartTimeNs + bucketSizeNs), buckets[1].mDuration); 228 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 229 230 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. 231 LogEvent end_event(tagId, endTimeNs); 232 end_event.init(); 233 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 234 buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 235 EXPECT_EQ(3UL, buckets.size()); 236 EXPECT_EQ(eventUpgradeTimeNs, buckets[2].mBucketStartNs); 237 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[2].mBucketEndNs); 238 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs - eventUpgradeTimeNs, buckets[2].mDuration); 239} 240 241TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) { 242 sp<AlarmMonitor> alarmMonitor; 243 int64_t bucketStartTimeNs = 10000000000; 244 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 245 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; 246 int64_t startTimeNs = bucketStartTimeNs + 1; 247 int64_t endTimeNs = startTimeNs + 65 * NS_PER_SEC; 248 249 int tagId = 1; 250 251 // Setup metric with alert. 252 DurationMetric metric; 253 metric.set_id(1); 254 metric.set_bucket(ONE_MINUTE); 255 metric.set_aggregation_type(DurationMetric_AggregationType_SUM); 256 Alert alert; 257 alert.set_num_buckets(3); 258 alert.set_trigger_if_sum_gt(2); 259 260 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 261 FieldMatcher dimensions; 262 DurationMetricProducer durationProducer( 263 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 264 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); 265 durationProducer.setBucketSize(60 * NS_PER_SEC); 266 267 sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor); 268 EXPECT_TRUE(anomalyTracker != nullptr); 269 270 LogEvent start_event(tagId, startTimeNs); 271 start_event.init(); 272 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 273 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 274 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. 275 LogEvent end_event(tagId, endTimeNs); 276 end_event.init(); 277 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 278 279 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs - startTimeNs, 280 anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY)); 281} 282 283TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) { 284 int64_t bucketStartTimeNs = 10000000000; 285 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 286 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; 287 int64_t startTimeNs = bucketStartTimeNs + 1; 288 int64_t endTimeNs = startTimeNs + 125 * NS_PER_SEC; 289 290 int tagId = 1; 291 292 DurationMetric metric; 293 metric.set_id(1); 294 metric.set_bucket(ONE_MINUTE); 295 metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); 296 LogEvent event1(tagId, startTimeNs); 297 event1.write("111"); // uid 298 event1.init(); 299 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 300 FieldMatcher dimensions; 301 DurationMetricProducer durationProducer( 302 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 303 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); 304 durationProducer.setBucketSize(60 * NS_PER_SEC); 305 306 LogEvent start_event(tagId, startTimeNs); 307 start_event.init(); 308 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 309 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 310 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); 311 312 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 313 EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 314 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 315 316 // We skip ahead one bucket, so we fill in the first two partial buckets and one full bucket. 317 LogEvent end_event(tagId, endTimeNs); 318 end_event.init(); 319 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 320 EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 321 322 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1); 323 std::vector<DurationBucket> buckets = 324 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 325 EXPECT_EQ(1UL, buckets.size()); 326 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketStartNs); 327 EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, buckets[0].mBucketEndNs); 328 EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); 329} 330 331TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) { 332 int64_t bucketStartTimeNs = 10000000000; 333 int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL; 334 int64_t eventUpgradeTimeNs = bucketStartTimeNs + 65 * NS_PER_SEC; 335 int64_t startTimeNs = bucketStartTimeNs + 1; 336 int64_t endTimeNs = startTimeNs + 115 * NS_PER_SEC; 337 338 int tagId = 1; 339 340 DurationMetric metric; 341 metric.set_id(1); 342 metric.set_bucket(ONE_MINUTE); 343 metric.set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE); 344 LogEvent event1(tagId, startTimeNs); 345 event1.write("111"); // uid 346 event1.init(); 347 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); 348 FieldMatcher dimensions; 349 DurationMetricProducer durationProducer( 350 kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */, 351 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs); 352 durationProducer.setBucketSize(60 * NS_PER_SEC); 353 354 LogEvent start_event(tagId, startTimeNs); 355 start_event.init(); 356 durationProducer.onMatchedLogEvent(1 /* start index*/, start_event); 357 EXPECT_EQ(0UL, durationProducer.mPastBuckets.size()); 358 EXPECT_EQ(bucketStartTimeNs, durationProducer.mCurrentBucketStartTimeNs); 359 360 durationProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1); 361 EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 362 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 363 364 // Stop occurs in the same partial bucket as created for the app upgrade. 365 LogEvent end_event(tagId, endTimeNs); 366 end_event.init(); 367 durationProducer.onMatchedLogEvent(2 /* stop index*/, end_event); 368 EXPECT_EQ(0UL, durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); 369 EXPECT_EQ(eventUpgradeTimeNs, durationProducer.mCurrentBucketStartTimeNs); 370 371 durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1); 372 std::vector<DurationBucket> buckets = 373 durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY]; 374 EXPECT_EQ(1UL, buckets.size()); 375 EXPECT_EQ(eventUpgradeTimeNs, buckets[0].mBucketStartNs); 376 EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets[0].mBucketEndNs); 377 EXPECT_EQ(endTimeNs - startTimeNs, buckets[0].mDuration); 378} 379 380} // namespace statsd 381} // namespace os 382} // namespace android 383#else 384GTEST_LOG_(INFO) << "This test does nothing.\n"; 385#endif 386