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 import static com.google.common.truth.Truth.assertWithMessage; 20 21 import android.cts.statsd.atom.DeviceAtomTestCase; 22 23 import com.android.internal.os.StatsdConfigProto; 24 import com.android.internal.os.StatsdConfigProto.FieldMatcher; 25 import com.android.internal.os.StatsdConfigProto.Position; 26 import com.android.os.AtomsProto.Atom; 27 import com.android.os.AtomsProto.AppBreadcrumbReported; 28 import com.android.os.AtomsProto.AttributionNode; 29 import com.android.os.AtomsProto.BleScanStateChanged; 30 import com.android.os.AtomsProto.WakelockStateChanged; 31 import com.android.os.StatsLog; 32 import com.android.os.StatsLog.ConfigMetricsReport; 33 import com.android.os.StatsLog.ConfigMetricsReportList; 34 import com.android.os.StatsLog.CountBucketInfo; 35 import com.android.os.StatsLog.CountMetricData; 36 import com.android.os.StatsLog.StatsLogReport; 37 import com.android.tradefed.device.DeviceNotAvailableException; 38 import com.android.tradefed.log.LogUtil; 39 40 import java.util.Arrays; 41 import java.util.List; 42 import java.util.stream.Collectors; 43 import java.util.stream.IntStream; 44 45 public class CountMetricsTests extends DeviceAtomTestCase { 46 testSimpleEventCountMetric()47 public void testSimpleEventCountMetric() throws Exception { 48 int matcherId = 1; 49 StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder(); 50 builder.addCountMetric(StatsdConfigProto.CountMetric.newBuilder() 51 .setId(MetricsUtils.COUNT_METRIC_ID) 52 .setBucket(StatsdConfigProto.TimeUnit.CTS) 53 .setWhat(matcherId)) 54 .addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId)); 55 uploadConfig(builder); 56 57 doAppBreadcrumbReportedStart(0); 58 Thread.sleep(100); 59 doAppBreadcrumbReportedStop(0); 60 Thread.sleep(2000); // Wait for the metrics to propagate to statsd. 61 62 StatsLogReport metricReport = getStatsLogReport(); 63 LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString()); 64 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID); 65 assertThat(metricReport.hasCountMetrics()).isTrue(); 66 67 StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics(); 68 69 assertThat(countData.getDataCount()).isGreaterThan(0); 70 assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2); 71 } testEventCountWithCondition()72 public void testEventCountWithCondition() throws Exception { 73 int startMatcherId = 1; 74 int endMatcherId = 2; 75 int whatMatcherId = 3; 76 int conditionId = 4; 77 78 StatsdConfigProto.AtomMatcher whatMatcher = 79 MetricsUtils.unspecifiedAtomMatcher(whatMatcherId); 80 81 StatsdConfigProto.AtomMatcher predicateStartMatcher = 82 MetricsUtils.startAtomMatcher(startMatcherId); 83 84 StatsdConfigProto.AtomMatcher predicateEndMatcher = 85 MetricsUtils.stopAtomMatcher(endMatcherId); 86 87 StatsdConfigProto.Predicate p = StatsdConfigProto.Predicate.newBuilder() 88 .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder() 89 .setStart(startMatcherId) 90 .setStop(endMatcherId) 91 .setCountNesting(false)) 92 .setId(conditionId) 93 .build(); 94 95 StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder() 96 .addCountMetric(StatsdConfigProto.CountMetric.newBuilder() 97 .setId(MetricsUtils.COUNT_METRIC_ID) 98 .setBucket(StatsdConfigProto.TimeUnit.CTS) 99 .setWhat(whatMatcherId) 100 .setCondition(conditionId)) 101 .addAtomMatcher(whatMatcher) 102 .addAtomMatcher(predicateStartMatcher) 103 .addAtomMatcher(predicateEndMatcher) 104 .addPredicate(p); 105 106 uploadConfig(builder); 107 108 doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal()); 109 Thread.sleep(10); 110 doAppBreadcrumbReportedStart(0); 111 Thread.sleep(10); 112 doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal()); 113 Thread.sleep(10); 114 doAppBreadcrumbReportedStop(0); 115 Thread.sleep(10); 116 doAppBreadcrumbReported(0, AppBreadcrumbReported.State.UNSPECIFIED.ordinal()); 117 Thread.sleep(2000); // Wait for the metrics to propagate to statsd. 118 119 StatsLogReport metricReport = getStatsLogReport(); 120 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID); 121 assertThat(metricReport.hasCountMetrics()).isTrue(); 122 123 StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics(); 124 125 assertThat(countData.getDataCount()).isGreaterThan(0); 126 assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(1); 127 } 128 testEventCountWithConditionAndActivation()129 public void testEventCountWithConditionAndActivation() throws Exception { 130 int startMatcherId = 1; 131 int startMatcherLabel = 1; 132 int endMatcherId = 2; 133 int endMatcherLabel = 2; 134 int whatMatcherId = 3; 135 int whatMatcherLabel = 3; 136 int conditionId = 4; 137 int activationMatcherId = 5; 138 int activationMatcherLabel = 5; 139 int ttlSec = 5; 140 141 StatsdConfigProto.AtomMatcher whatMatcher = 142 MetricsUtils.appBreadcrumbMatcherWithLabel(whatMatcherId, whatMatcherLabel); 143 144 StatsdConfigProto.AtomMatcher predicateStartMatcher = 145 MetricsUtils.startAtomMatcherWithLabel(startMatcherId, startMatcherLabel); 146 147 StatsdConfigProto.AtomMatcher predicateEndMatcher = 148 MetricsUtils.stopAtomMatcherWithLabel(endMatcherId, endMatcherLabel); 149 150 StatsdConfigProto.AtomMatcher activationMatcher = 151 MetricsUtils.appBreadcrumbMatcherWithLabel(activationMatcherId, 152 activationMatcherLabel); 153 154 StatsdConfigProto.Predicate p = StatsdConfigProto.Predicate.newBuilder() 155 .setSimplePredicate(StatsdConfigProto.SimplePredicate.newBuilder() 156 .setStart(startMatcherId) 157 .setStop(endMatcherId) 158 .setCountNesting(false)) 159 .setId(conditionId) 160 .build(); 161 162 StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder() 163 .addCountMetric(StatsdConfigProto.CountMetric.newBuilder() 164 .setId(MetricsUtils.COUNT_METRIC_ID) 165 .setBucket(StatsdConfigProto.TimeUnit.CTS) 166 .setWhat(whatMatcherId) 167 .setCondition(conditionId) 168 ) 169 .addAtomMatcher(whatMatcher) 170 .addAtomMatcher(predicateStartMatcher) 171 .addAtomMatcher(predicateEndMatcher) 172 .addAtomMatcher(activationMatcher) 173 .addPredicate(p) 174 .addMetricActivation(StatsdConfigProto.MetricActivation.newBuilder() 175 .setMetricId(MetricsUtils.COUNT_METRIC_ID) 176 .setActivationType(StatsdConfigProto.ActivationType.ACTIVATE_IMMEDIATELY) 177 .addEventActivation(StatsdConfigProto.EventActivation.newBuilder() 178 .setAtomMatcherId(activationMatcherId) 179 .setTtlSeconds(ttlSec))); 180 181 uploadConfig(builder); 182 183 // Activate the metric. 184 doAppBreadcrumbReported(activationMatcherLabel); 185 Thread.sleep(10); 186 187 // Set the condition to true. 188 doAppBreadcrumbReportedStart(startMatcherLabel); 189 Thread.sleep(10); 190 191 // Log an event that should be counted. Bucket 1 Count 1. 192 doAppBreadcrumbReported(whatMatcherLabel); 193 Thread.sleep(10); 194 195 // Log an event that should be counted. Bucket 1 Count 2. 196 doAppBreadcrumbReported(whatMatcherLabel); 197 Thread.sleep(10); 198 199 // Set the condition to false. 200 doAppBreadcrumbReportedStop(endMatcherLabel); 201 Thread.sleep(10); 202 203 // Log an event that should not be counted because condition is false. 204 doAppBreadcrumbReported(whatMatcherLabel); 205 Thread.sleep(10); 206 207 // Let the metric deactivate. 208 Thread.sleep(ttlSec * 1000); 209 210 // Log an event that should not be counted. 211 doAppBreadcrumbReported(whatMatcherLabel); 212 Thread.sleep(10); 213 214 // Condition to true again. 215 doAppBreadcrumbReportedStart(startMatcherLabel); 216 Thread.sleep(10); 217 218 // Event should not be counted, metric is still not active. 219 doAppBreadcrumbReported(whatMatcherLabel); 220 Thread.sleep(10); 221 222 // Activate the metric. 223 doAppBreadcrumbReported(activationMatcherLabel); 224 Thread.sleep(10); 225 226 // Log an event that should be counted. 227 doAppBreadcrumbReported(whatMatcherLabel); 228 Thread.sleep(10); 229 230 // Let the metric deactivate. 231 Thread.sleep(ttlSec * 1000); 232 233 // Log an event that should not be counted. 234 doAppBreadcrumbReported(whatMatcherLabel); 235 Thread.sleep(10); 236 237 // Wait for the metrics to propagate to statsd. 238 Thread.sleep(2000); 239 240 StatsLogReport metricReport = getStatsLogReport(); 241 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID); 242 LogUtil.CLog.d("Received the following data: " + metricReport.toString()); 243 assertThat(metricReport.hasCountMetrics()).isTrue(); 244 assertThat(metricReport.getIsActive()).isFalse(); 245 246 StatsLogReport.CountMetricDataWrapper countData = metricReport.getCountMetrics(); 247 assertThat(countData.getDataCount()).isEqualTo(1); 248 assertThat(countData.getData(0).getBucketInfoCount()).isEqualTo(2); 249 assertThat(countData.getData(0).getBucketInfo(0).getCount()).isEqualTo(2); 250 assertThat(countData.getData(0).getBucketInfo(1).getCount()).isEqualTo(1); 251 } 252 testPartialBucketCountMetric()253 public void testPartialBucketCountMetric() throws Exception { 254 int matcherId = 1; 255 StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder(); 256 builder 257 .addCountMetric( 258 StatsdConfigProto.CountMetric.newBuilder() 259 .setId(MetricsUtils.COUNT_METRIC_ID) 260 .setBucket(StatsdConfigProto.TimeUnit.ONE_DAY) // Ensures partial bucket. 261 .setWhat(matcherId) 262 .setSplitBucketForAppUpgrade(true)) 263 .addAtomMatcher(MetricsUtils.simpleAtomMatcher(matcherId)); 264 uploadConfig(builder); 265 266 doAppBreadcrumbReportedStart(0); 267 268 builder.getCountMetricBuilder(0).setBucket(StatsdConfigProto.TimeUnit.CTS); 269 uploadConfig(builder); // The count metric had a partial bucket. 270 doAppBreadcrumbReportedStart(0); 271 Thread.sleep(10); 272 doAppBreadcrumbReportedStart(0); 273 Thread.sleep(WAIT_TIME_LONG); // Finish the current bucket. 274 275 ConfigMetricsReportList reports = getReportList(); 276 LogUtil.CLog.d("Got following report list: " + reports.toString()); 277 278 assertThat(reports.getReportsCount()).isEqualTo(2); 279 boolean inOrder = reports.getReports(0).getCurrentReportWallClockNanos() < 280 reports.getReports(1).getCurrentReportWallClockNanos(); 281 282 // Only 1 metric, so there should only be 1 StatsLogReport. 283 for (ConfigMetricsReport report : reports.getReportsList()) { 284 assertThat(report.getMetricsCount()).isEqualTo(1); 285 assertThat(report.getMetrics(0).getCountMetrics().getDataCount()).isEqualTo(1); 286 } 287 CountMetricData data1 = 288 reports.getReports(inOrder? 0 : 1).getMetrics(0).getCountMetrics().getData(0); 289 CountMetricData data2 = 290 reports.getReports(inOrder? 1 : 0).getMetrics(0).getCountMetrics().getData(0); 291 // Data1 should have only 1 bucket, and it should be a partial bucket. 292 // The count should be 1. 293 assertThat(data1.getBucketInfoCount()).isEqualTo(1); 294 CountBucketInfo bucketInfo = data1.getBucketInfo(0); 295 assertThat(bucketInfo.getCount()).isEqualTo(1); 296 assertWithMessage("First report's bucket should be less than 1 day") 297 .that(bucketInfo.getEndBucketElapsedNanos()) 298 .isLessThan(bucketInfo.getStartBucketElapsedNanos() + 299 1_000_000_000L * 60L * 60L * 24L); 300 301 //Second report should have a count of 2. 302 assertThat(data2.getBucketInfoCount()).isAtMost(2); 303 int totalCount = 0; 304 for (CountBucketInfo bucket : data2.getBucketInfoList()) { 305 totalCount += bucket.getCount(); 306 } 307 assertThat(totalCount).isEqualTo(2); 308 } 309 310 public void testSlicedStateCountMetricNoReset() throws Exception { 311 int whatMatcherId = 3; 312 int stateId = 4; 313 int onStateGroupId = 5; 314 int offStateGroupId = 6; 315 316 // Atom 9998 { 317 // repeated AttributionNode attribution_node = 1; 318 // optional WakeLockLevelEnum type = 2; 319 // optional string tag = 3; 320 // } 321 int whatAtomId = 9_998; 322 323 StatsdConfigProto.AtomMatcher whatMatcher = 324 MetricsUtils.getAtomMatcher(whatAtomId) 325 .setId(whatMatcherId) 326 .build(); 327 328 StatsdConfigProto.State state = StatsdConfigProto.State.newBuilder() 329 .setId(stateId) 330 .setAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER) 331 .setMap(StatsdConfigProto.StateMap.newBuilder() 332 .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder() 333 .setGroupId(onStateGroupId) 334 .addValue(WakelockStateChanged.State.ACQUIRE_VALUE) 335 .addValue(WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE) 336 ) 337 .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder() 338 .setGroupId(offStateGroupId) 339 .addValue(WakelockStateChanged.State.RELEASE_VALUE) 340 .addValue(WakelockStateChanged.State.CHANGE_RELEASE_VALUE) 341 ) 342 ) 343 .build(); 344 345 StatsdConfigProto.MetricStateLink stateLink = StatsdConfigProto.MetricStateLink.newBuilder() 346 .setStateAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER) 347 .setFieldsInWhat(FieldMatcher.newBuilder() 348 .setField(whatAtomId) 349 .addChild(FieldMatcher.newBuilder() 350 .setField(1) 351 .setPosition(Position.FIRST) 352 .addChild(FieldMatcher.newBuilder() 353 .setField(AttributionNode.UID_FIELD_NUMBER) 354 ) 355 ) 356 .addChild(FieldMatcher.newBuilder() 357 .setField(2) 358 ) 359 .addChild(FieldMatcher.newBuilder() 360 .setField(3) 361 ) 362 ) 363 .setFieldsInState(FieldMatcher.newBuilder() 364 .setField(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER) 365 .addChild(FieldMatcher.newBuilder() 366 .setField(WakelockStateChanged.ATTRIBUTION_NODE_FIELD_NUMBER) 367 .setPosition(Position.FIRST) 368 .addChild(FieldMatcher.newBuilder() 369 .setField(AttributionNode.UID_FIELD_NUMBER) 370 ) 371 ) 372 .addChild(FieldMatcher.newBuilder() 373 .setField(WakelockStateChanged.TYPE_FIELD_NUMBER) 374 ) 375 .addChild(FieldMatcher.newBuilder() 376 .setField(WakelockStateChanged.TAG_FIELD_NUMBER) 377 ) 378 ) 379 .build(); 380 381 StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder() 382 .addCountMetric(StatsdConfigProto.CountMetric.newBuilder() 383 .setId(MetricsUtils.COUNT_METRIC_ID) 384 .setBucket(StatsdConfigProto.TimeUnit.CTS) 385 .setWhat(whatMatcherId) 386 .addSliceByState(stateId) 387 .addStateLink(stateLink) 388 ) 389 .addAtomMatcher(whatMatcher) 390 .addState(state); 391 uploadConfig(builder); 392 393 runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSliceByWakelockState"); 394 395 StatsLogReport metricReport = getStatsLogReport(); 396 LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString()); 397 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID); 398 assertThat(metricReport.hasCountMetrics()).isTrue(); 399 400 StatsLogReport.CountMetricDataWrapper dataWrapper = metricReport.getCountMetrics(); 401 assertThat(dataWrapper.getDataCount()).isEqualTo(2); 402 403 404 List<CountMetricData> sortedDataList = IntStream.range(0, dataWrapper.getDataCount()) 405 .mapToObj(i -> { 406 CountMetricData data = dataWrapper.getData(i); 407 assertWithMessage("Unexpected SliceByState count for data[%s]", "" + i) 408 .that(data.getSliceByStateCount()).isEqualTo(1); 409 return data; 410 }) 411 .sorted((data1, data2) -> 412 Long.compare(data1.getSliceByState(0).getGroupId(), 413 data2.getSliceByState(0).getGroupId()) 414 ) 415 .collect(Collectors.toList()); 416 417 CountMetricData data = sortedDataList.get(0); 418 assertThat(data.getSliceByState(0).getAtomId()) 419 .isEqualTo(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER); 420 assertThat(data.getSliceByState(0).getGroupId()) 421 .isEqualTo(onStateGroupId); 422 long totalCount = data.getBucketInfoList().stream() 423 .mapToLong(CountBucketInfo::getCount) 424 .sum(); 425 assertThat(totalCount).isEqualTo(6); 426 427 data = sortedDataList.get(1); 428 assertThat(data.getSliceByState(0).getAtomId()) 429 .isEqualTo(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER); 430 assertThat(data.getSliceByState(0).getGroupId()) 431 .isEqualTo(offStateGroupId); 432 totalCount = data.getBucketInfoList().stream() 433 .mapToLong(CountBucketInfo::getCount) 434 .sum(); 435 assertThat(totalCount).isEqualTo(3); 436 } 437 } 438