1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.cts.statsd.metric; 17 18 import static com.google.common.truth.Truth.assertThat; 19 20 import android.cts.statsd.atom.DeviceAtomTestCase; 21 22 import com.android.internal.os.StatsdConfigProto.ActivationType; 23 import com.android.internal.os.StatsdConfigProto.AtomMatcher; 24 import com.android.internal.os.StatsdConfigProto.EventActivation; 25 import com.android.internal.os.StatsdConfigProto.FieldFilter; 26 import com.android.internal.os.StatsdConfigProto.FieldMatcher; 27 import com.android.internal.os.StatsdConfigProto.FieldValueMatcher; 28 import com.android.internal.os.StatsdConfigProto.MetricActivation; 29 import com.android.internal.os.StatsdConfigProto.Predicate; 30 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; 31 import com.android.internal.os.StatsdConfigProto.SimplePredicate; 32 import com.android.internal.os.StatsdConfigProto.StatsdConfig; 33 import com.android.internal.os.StatsdConfigProto.TimeUnit; 34 import com.android.internal.os.StatsdConfigProto.ValueMetric; 35 36 import com.android.os.AtomsProto.AppBreadcrumbReported; 37 import com.android.os.AtomsProto.Atom; 38 import com.android.os.AtomsProto.SystemElapsedRealtime; 39 import com.android.os.StatsLog.StatsLogReport; 40 import com.android.os.StatsLog.StatsLogReport.BucketDropReason; 41 import com.android.os.StatsLog.ValueBucketInfo; 42 import com.android.os.StatsLog.ValueMetricData; 43 44 import com.android.tradefed.log.LogUtil; 45 46 public class ValueMetricsTests extends DeviceAtomTestCase { 47 private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0; 48 private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1; 49 private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2; 50 testValueMetric()51 public void testValueMetric() throws Exception { 52 // Add AtomMatcher's. 53 AtomMatcher startAtomMatcher = 54 MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID); 55 AtomMatcher stopAtomMatcher = 56 MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID); 57 AtomMatcher atomMatcher = 58 MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID); 59 60 StatsdConfig.Builder builder = createConfigBuilder(); 61 builder.addAtomMatcher(startAtomMatcher); 62 builder.addAtomMatcher(stopAtomMatcher); 63 builder.addAtomMatcher(atomMatcher); 64 65 // Add ValueMetric. 66 builder.addValueMetric( 67 ValueMetric.newBuilder() 68 .setId(MetricsUtils.VALUE_METRIC_ID) 69 .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID) 70 .setBucket(TimeUnit.CTS) 71 .setValueField(FieldMatcher.newBuilder() 72 .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) 73 .addChild(FieldMatcher.newBuilder().setField( 74 AppBreadcrumbReported.LABEL_FIELD_NUMBER))) 75 .setDimensionsInWhat(FieldMatcher.newBuilder() 76 .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID) 77 .build()) 78 .build()); 79 80 // Upload config. 81 uploadConfig(builder); 82 83 // Create AppBreadcrumbReported Start/Stop events. 84 doAppBreadcrumbReportedStart(1); 85 Thread.sleep(1000); 86 doAppBreadcrumbReportedStop(1); 87 doAppBreadcrumbReportedStart(3); 88 doAppBreadcrumbReportedStop(3); 89 90 // Wait for the metrics to propagate to statsd. 91 Thread.sleep(1000); 92 93 StatsLogReport metricReport = getStatsLogReport(); 94 LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString()); 95 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); 96 assertThat(metricReport.hasValueMetrics()).isTrue(); 97 StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics(); 98 assertThat(valueData.getDataCount()).isEqualTo(1); 99 100 int bucketCount = valueData.getData(0).getBucketInfoCount(); 101 assertThat(bucketCount).isGreaterThan(1); 102 ValueMetricData data = valueData.getData(0); 103 int totalValue = 0; 104 for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) { 105 MetricsUtils.assertBucketTimePresent(bucketInfo); 106 assertThat(bucketInfo.getValuesCount()).isEqualTo(1); 107 assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0); 108 totalValue += (int) bucketInfo.getValues(0).getValueLong(); 109 } 110 assertThat(totalValue).isEqualTo(8); 111 } 112 113 // Test value metric with pulled atoms and across multiple buckets testPullerAcrossBuckets()114 public void testPullerAcrossBuckets() throws Exception { 115 // Add AtomMatcher's. 116 final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START"; 117 final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP"; 118 final String predicateName = "APP_BREADCRUMB_REPORTED_IS_STOP"; 119 120 AtomMatcher startAtomMatcher = 121 MetricsUtils.startAtomMatcher(predicateTrueName.hashCode()); 122 AtomMatcher stopAtomMatcher = 123 MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode()); 124 125 StatsdConfig.Builder builder = createConfigBuilder(); 126 builder.addAtomMatcher(startAtomMatcher); 127 builder.addAtomMatcher(stopAtomMatcher); 128 builder.addPredicate(Predicate.newBuilder() 129 .setId(predicateName.hashCode()) 130 .setSimplePredicate(SimplePredicate.newBuilder() 131 .setStart(predicateTrueName.hashCode()) 132 .setStop(predicateFalseName.hashCode()) 133 .setCountNesting(false) 134 ) 135 ); 136 137 final String atomName = "SYSTEM_ELAPSED_REALTIME"; 138 SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER); 139 builder.addAtomMatcher(AtomMatcher.newBuilder() 140 .setId(atomName.hashCode()) 141 .setSimpleAtomMatcher(sam)); 142 143 // Add ValueMetric. 144 builder.addValueMetric( 145 ValueMetric.newBuilder() 146 .setId(MetricsUtils.VALUE_METRIC_ID) 147 .setWhat(atomName.hashCode()) 148 .setBucket(TimeUnit.ONE_MINUTE) 149 .setValueField(FieldMatcher.newBuilder() 150 .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER) 151 .addChild(FieldMatcher.newBuilder().setField( 152 SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER))) 153 .setCondition(predicateName.hashCode()) 154 .build()); 155 156 // Upload config. 157 uploadConfig(builder); 158 159 // Create AppBreadcrumbReported Start/Stop events. 160 doAppBreadcrumbReportedStart(1); 161 // Wait for 2 min and 1 sec to capture at least 2 buckets 162 Thread.sleep(2*60_000 + 10_000); 163 doAppBreadcrumbReportedStop(1); 164 165 // Wait for the metrics to propagate to statsd. 166 Thread.sleep(1_000); 167 168 StatsLogReport metricReport = getStatsLogReport(); 169 LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString()); 170 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); 171 assertThat(metricReport.hasValueMetrics()).isTrue(); 172 StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics(); 173 assertThat(valueData.getDataCount()).isEqualTo(1); 174 175 int bucketCount = valueData.getData(0).getBucketInfoCount(); 176 // should have at least 2 buckets 177 assertThat(bucketCount).isAtLeast(2); 178 ValueMetricData data = valueData.getData(0); 179 int totalValue = 0; 180 for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) { 181 MetricsUtils.assertBucketTimePresent(bucketInfo); 182 assertThat(bucketInfo.getValuesCount()).isEqualTo(1); 183 assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0); 184 totalValue += (int) bucketInfo.getValues(0).getValueLong(); 185 } 186 // At most we lose one full min bucket 187 assertThat(totalValue).isGreaterThan(130_000 - 60_000); 188 } 189 190 // Test value metric with pulled atoms and across multiple buckets testMultipleEventsPerBucket()191 public void testMultipleEventsPerBucket() throws Exception { 192 // Add AtomMatcher's. 193 final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START"; 194 final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP"; 195 final String predicateName = "APP_BREADCRUMB_REPORTED_IS_STOP"; 196 197 AtomMatcher startAtomMatcher = 198 MetricsUtils.startAtomMatcher(predicateTrueName.hashCode()); 199 AtomMatcher stopAtomMatcher = 200 MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode()); 201 202 StatsdConfig.Builder builder = createConfigBuilder(); 203 builder.addAtomMatcher(startAtomMatcher); 204 builder.addAtomMatcher(stopAtomMatcher); 205 builder.addPredicate(Predicate.newBuilder() 206 .setId(predicateName.hashCode()) 207 .setSimplePredicate(SimplePredicate.newBuilder() 208 .setStart(predicateTrueName.hashCode()) 209 .setStop(predicateFalseName.hashCode()) 210 .setCountNesting(false) 211 ) 212 ); 213 214 final String atomName = "SYSTEM_ELAPSED_REALTIME"; 215 SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER); 216 builder.addAtomMatcher(AtomMatcher.newBuilder() 217 .setId(atomName.hashCode()) 218 .setSimpleAtomMatcher(sam)); 219 220 // Add ValueMetric. 221 builder.addValueMetric( 222 ValueMetric.newBuilder() 223 .setId(MetricsUtils.VALUE_METRIC_ID) 224 .setWhat(atomName.hashCode()) 225 .setBucket(TimeUnit.ONE_MINUTE) 226 .setValueField(FieldMatcher.newBuilder() 227 .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER) 228 .addChild(FieldMatcher.newBuilder().setField( 229 SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER))) 230 .setCondition(predicateName.hashCode()) 231 .build()); 232 233 // Upload config. 234 uploadConfig(builder); 235 236 final int NUM_EVENTS = 10; 237 final long GAP_INTERVAL = 10_000; 238 // Create AppBreadcrumbReported Start/Stop events. 239 for (int i = 0; i < NUM_EVENTS; i ++) { 240 doAppBreadcrumbReportedStart(1); 241 Thread.sleep(GAP_INTERVAL); 242 doAppBreadcrumbReportedStop(1); 243 Thread.sleep(GAP_INTERVAL); 244 } 245 246 // Wait for the metrics to propagate to statsd. 247 Thread.sleep(1_000); 248 249 StatsLogReport metricReport = getStatsLogReport(); 250 LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString()); 251 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); 252 assertThat(metricReport.hasValueMetrics()).isTrue(); 253 StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics(); 254 assertThat(valueData.getDataCount()).isEqualTo(1); 255 256 int bucketCount = valueData.getData(0).getBucketInfoCount(); 257 // should have at least 2 buckets 258 assertThat(bucketCount).isAtLeast(2); 259 ValueMetricData data = valueData.getData(0); 260 int totalValue = 0; 261 for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) { 262 MetricsUtils.assertBucketTimePresent(bucketInfo); 263 assertThat(bucketInfo.getValuesCount()).isEqualTo(1); 264 assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0); 265 totalValue += (int) bucketInfo.getValues(0).getValueLong(); 266 } 267 // At most we lose one full min bucket 268 assertThat((long) totalValue).isGreaterThan(GAP_INTERVAL * NUM_EVENTS - 60_000); 269 } 270 271 // Test value metric with pulled atoms and across multiple buckets testPullerAcrossBucketsWithActivation()272 public void testPullerAcrossBucketsWithActivation() throws Exception { 273 StatsdConfig.Builder builder = createConfigBuilder(); 274 275 // Add AtomMatcher's. 276 int activationAtomMatcherId = 1; 277 int activationAtomMatcherLabel = 1; 278 AtomMatcher activationAtomMatcher = 279 MetricsUtils.appBreadcrumbMatcherWithLabel( 280 activationAtomMatcherId, activationAtomMatcherLabel); 281 final String atomName = "SYSTEM_ELAPSED_REALTIME"; 282 SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder() 283 .setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER); 284 builder.addAtomMatcher(activationAtomMatcher) 285 .addAtomMatcher(AtomMatcher.newBuilder() 286 .setId(atomName.hashCode()) 287 .setSimpleAtomMatcher(sam)); 288 289 // Add ValueMetric. 290 builder.addValueMetric( 291 ValueMetric.newBuilder() 292 .setId(MetricsUtils.VALUE_METRIC_ID) 293 .setWhat(atomName.hashCode()) 294 .setBucket(TimeUnit.ONE_MINUTE) 295 .setValueField(FieldMatcher.newBuilder() 296 .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER) 297 .addChild(FieldMatcher.newBuilder().setField( 298 SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER))) 299 .build()); 300 // Add activation. 301 builder.addMetricActivation(MetricActivation.newBuilder() 302 .setMetricId(MetricsUtils.VALUE_METRIC_ID) 303 .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY) 304 .addEventActivation(EventActivation.newBuilder() 305 .setAtomMatcherId(activationAtomMatcherId) 306 .setTtlSeconds(5))); 307 308 309 // Upload config. 310 uploadConfig(builder); 311 312 // Wait for 1 min and 10 sec to capture at least 1 bucket 313 Thread.sleep(60_000 + 10_000); 314 315 // Wait for the metrics to propagate to statsd. 316 Thread.sleep(1_000); 317 318 StatsLogReport metricReport = getStatsLogReport(); 319 LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString()); 320 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); 321 assertThat(metricReport.getValueMetrics().getDataList()).isEmpty(); 322 // Bucket is skipped because metric is not activated. 323 assertThat(metricReport.getValueMetrics().getSkippedList()).isNotEmpty(); 324 assertThat(metricReport.getValueMetrics().getSkipped(0).getDropEventList()).isNotEmpty(); 325 assertThat(metricReport.getValueMetrics().getSkipped(0).getDropEvent(0).getDropReason()) 326 .isEqualTo(BucketDropReason.NO_DATA); 327 } 328 testValueMetricWithConditionAndActivation()329 public void testValueMetricWithConditionAndActivation() throws Exception { 330 final int conditionLabel = 2; 331 final int activationMatcherId = 5; 332 final int activationMatcherLabel = 5; 333 final int whatMatcherId = 8; 334 final int ttlSec = 5; 335 336 // Add AtomMatchers. 337 AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel( 338 APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, conditionLabel); 339 AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel( 340 APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, conditionLabel); 341 AtomMatcher activationMatcher = 342 MetricsUtils.startAtomMatcherWithLabel( 343 activationMatcherId, activationMatcherLabel); 344 AtomMatcher whatMatcher = 345 MetricsUtils.unspecifiedAtomMatcher(whatMatcherId); 346 347 StatsdConfig.Builder builder = createConfigBuilder() 348 .addAtomMatcher(conditionStartAtomMatcher) 349 .addAtomMatcher(conditionStopAtomMatcher) 350 .addAtomMatcher(whatMatcher) 351 .addAtomMatcher(activationMatcher); 352 353 // Add Predicates. 354 SimplePredicate simplePredicate = SimplePredicate.newBuilder() 355 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID) 356 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID) 357 .build(); 358 Predicate predicate = Predicate.newBuilder() 359 .setId(MetricsUtils.StringToId("Predicate")) 360 .setSimplePredicate(simplePredicate) 361 .build(); 362 builder.addPredicate(predicate); 363 364 // Add ValueMetric. 365 builder 366 .addValueMetric(ValueMetric.newBuilder() 367 .setId(MetricsUtils.VALUE_METRIC_ID) 368 .setWhat(whatMatcher.getId()) 369 .setBucket(TimeUnit.ONE_MINUTE) 370 .setCondition(predicate.getId()) 371 .setValueField(FieldMatcher.newBuilder() 372 .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) 373 .addChild(FieldMatcher.newBuilder() 374 .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)) 375 ) 376 .setDimensionsInWhat(FieldMatcher.newBuilder().setField(whatMatcherId)) 377 ) 378 .addMetricActivation(MetricActivation.newBuilder() 379 .setMetricId(MetricsUtils.VALUE_METRIC_ID) 380 .addEventActivation(EventActivation.newBuilder() 381 .setAtomMatcherId(activationMatcherId) 382 .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY) 383 .setTtlSeconds(ttlSec) 384 ) 385 ); 386 387 uploadConfig(builder); 388 389 // Activate the metric. 390 doAppBreadcrumbReportedStart(activationMatcherLabel); 391 Thread.sleep(10); 392 393 // Set the condition to true. 394 doAppBreadcrumbReportedStart(conditionLabel); 395 Thread.sleep(10); 396 397 // Skipped due to unknown condition at start of bucket. 398 doAppBreadcrumbReported(10); 399 Thread.sleep(10); 400 401 // Skipped due to unknown condition at start of bucket. 402 doAppBreadcrumbReported(200); 403 Thread.sleep(10); 404 405 // Set the condition to false. 406 doAppBreadcrumbReportedStop(conditionLabel); 407 Thread.sleep(10); 408 409 // Log an event that should not be counted because condition is false. 410 doAppBreadcrumbReported(3_000); 411 Thread.sleep(10); 412 413 // Let the metric deactivate. 414 Thread.sleep(ttlSec * 1000); 415 416 // Log an event that should not be counted. 417 doAppBreadcrumbReported(40_000); 418 Thread.sleep(10); 419 420 // Condition to true again. 421 doAppBreadcrumbReportedStart(conditionLabel); 422 Thread.sleep(10); 423 424 // Event should not be counted, metric is still not active. 425 doAppBreadcrumbReported(500_000); 426 Thread.sleep(10); 427 428 // Activate the metric. 429 doAppBreadcrumbReportedStart(activationMatcherLabel); 430 Thread.sleep(10); 431 432 // Log an event that should be counted. 433 doAppBreadcrumbReported(6_000_000); 434 Thread.sleep(10); 435 436 // Let the metric deactivate. 437 Thread.sleep(ttlSec * 1000); 438 439 // Log an event that should not be counted. 440 doAppBreadcrumbReported(70_000_000); 441 Thread.sleep(10); 442 443 // Wait for the metrics to propagate to statsd. 444 Thread.sleep(2000); 445 446 StatsLogReport metricReport = getStatsLogReport(); 447 LogUtil.CLog.d("Received the following data: " + metricReport.toString()); 448 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); 449 assertThat(metricReport.hasValueMetrics()).isTrue(); 450 assertThat(metricReport.getIsActive()).isFalse(); 451 452 StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics(); 453 assertThat(valueData.getDataCount()).isEqualTo(1); 454 assertThat(valueData.getData(0).getBucketInfoCount()).isEqualTo(1); 455 long totalValue = valueData.getData(0).getBucketInfoList().stream() 456 .peek(MetricsUtils::assertBucketTimePresent) 457 .peek(bucketInfo -> assertThat(bucketInfo.getValuesCount()).isEqualTo(1)) 458 .map(bucketInfo -> bucketInfo.getValues(0)) 459 .peek(value -> assertThat(value.getIndex()).isEqualTo(0)) 460 .mapToLong(value -> value.getValueLong()) 461 .sum(); 462 assertThat(totalValue).isEqualTo(6_000_000); 463 } 464 465 } 466