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.statsdatom.lib.AtomTestUtils; 21 import android.cts.statsdatom.lib.ConfigUtils; 22 import android.cts.statsdatom.lib.ReportUtils; 23 24 import com.android.internal.os.StatsdConfigProto; 25 import com.android.internal.os.StatsdConfigProto.ActivationType; 26 import com.android.internal.os.StatsdConfigProto.AtomMatcher; 27 import com.android.internal.os.StatsdConfigProto.EventActivation; 28 import com.android.internal.os.StatsdConfigProto.FieldFilter; 29 import com.android.internal.os.StatsdConfigProto.FieldMatcher; 30 import com.android.internal.os.StatsdConfigProto.GaugeMetric; 31 import com.android.internal.os.StatsdConfigProto.MetricActivation; 32 import com.android.internal.os.StatsdConfigProto.Predicate; 33 import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; 34 import com.android.internal.os.StatsdConfigProto.SimplePredicate; 35 import com.android.internal.os.StatsdConfigProto.StatsdConfig; 36 import com.android.internal.os.StatsdConfigProto.TimeUnit; 37 import com.android.os.AtomsProto.AppBreadcrumbReported; 38 import com.android.os.AtomsProto.Atom; 39 import com.android.os.StatsLog; 40 import com.android.os.StatsLog.GaugeBucketInfo; 41 import com.android.os.StatsLog.GaugeMetricData; 42 import com.android.os.StatsLog.StatsLogReport; 43 import com.android.tradefed.log.LogUtil; 44 import com.android.tradefed.testtype.DeviceTestCase; 45 import com.android.tradefed.util.Pair; 46 import com.android.tradefed.util.RunUtil; 47 48 import com.google.protobuf.ExtensionRegistry; 49 50 import java.util.ArrayList; 51 import java.util.Comparator; 52 import java.util.List; 53 54 public class GaugeMetricsTests extends DeviceTestCase { 55 56 private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0; 57 private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1; 58 private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2; 59 60 @Override setUp()61 protected void setUp() throws Exception { 62 super.setUp(); 63 ConfigUtils.removeConfig(getDevice()); 64 ReportUtils.clearReports(getDevice()); 65 RunUtil.getDefault().sleep(1000); 66 } 67 68 @Override tearDown()69 protected void tearDown() throws Exception { 70 ConfigUtils.removeConfig(getDevice()); 71 ReportUtils.clearReports(getDevice()); 72 super.tearDown(); 73 } 74 testGaugeMetric()75 public void testGaugeMetric() throws Exception { 76 // Add AtomMatcher's. 77 AtomMatcher startAtomMatcher = 78 MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID); 79 AtomMatcher stopAtomMatcher = 80 MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID); 81 AtomMatcher atomMatcher = 82 MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID); 83 84 StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder( 85 MetricsUtils.DEVICE_SIDE_TEST_PACKAGE); 86 builder.addAtomMatcher(startAtomMatcher); 87 builder.addAtomMatcher(stopAtomMatcher); 88 builder.addAtomMatcher(atomMatcher); 89 90 // Add Predicate's. 91 SimplePredicate simplePredicate = SimplePredicate.newBuilder() 92 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID) 93 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID) 94 .build(); 95 Predicate predicate = Predicate.newBuilder() 96 .setId(MetricsUtils.StringToId("Predicate")) 97 .setSimplePredicate(simplePredicate) 98 .build(); 99 builder.addPredicate(predicate); 100 101 // Add GaugeMetric. 102 FieldMatcher fieldMatcher = FieldMatcher.newBuilder() 103 .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID) 104 .build(); 105 builder.addGaugeMetric(StatsdConfigProto.GaugeMetric.newBuilder() 106 .setId(MetricsUtils.GAUGE_METRIC_ID) 107 .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID) 108 .setCondition(predicate.getId()) 109 .setGaugeFieldsFilter(FieldFilter.newBuilder() 110 .setIncludeAll(false) 111 .setFields(fieldMatcher) 112 .build()) 113 .setDimensionsInWhat(FieldMatcher.newBuilder() 114 .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID) 115 .addChild(FieldMatcher.newBuilder() 116 .setField(AppBreadcrumbReported.STATE_FIELD_NUMBER) 117 .build()) 118 .build()) 119 .setBucket(StatsdConfigProto.TimeUnit.CTS) 120 .build()); 121 122 // Upload config. 123 ConfigUtils.uploadConfig(getDevice(), builder); 124 125 // Create AppBreadcrumbReported Start/Stop events. 126 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 127 AppBreadcrumbReported.State.START.getNumber(), 0); 128 RunUtil.getDefault().sleep(10); 129 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 130 AppBreadcrumbReported.State.START.getNumber(), 1); 131 RunUtil.getDefault().sleep(10); 132 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 133 AppBreadcrumbReported.State.START.getNumber(), 2); 134 RunUtil.getDefault().sleep(2000); 135 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 136 AppBreadcrumbReported.State.STOP.getNumber(), 2); 137 RunUtil.getDefault().sleep(10); 138 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 139 AppBreadcrumbReported.State.STOP.getNumber(), 0); 140 RunUtil.getDefault().sleep(10); 141 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 142 AppBreadcrumbReported.State.STOP.getNumber(), 1); 143 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 144 AppBreadcrumbReported.State.START.getNumber(), 2); 145 RunUtil.getDefault().sleep(10); 146 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 147 AppBreadcrumbReported.State.START.getNumber(), 1); 148 RunUtil.getDefault().sleep(2000); 149 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 150 AppBreadcrumbReported.State.STOP.getNumber(), 2); 151 RunUtil.getDefault().sleep(10); 152 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 153 AppBreadcrumbReported.State.STOP.getNumber(), 1); 154 155 // Wait for the metrics to propagate to statsd. 156 RunUtil.getDefault().sleep(2000); 157 158 StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(), 159 ExtensionRegistry.getEmptyRegistry()); 160 LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString()); 161 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID); 162 assertThat(metricReport.hasGaugeMetrics()).isTrue(); 163 StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics(); 164 gaugeData = backfillGaugeMetricData(gaugeData); 165 assertThat(gaugeData.getDataCount()).isEqualTo(1); 166 167 int bucketCount = gaugeData.getData(0).getBucketInfoCount(); 168 GaugeMetricData data = gaugeData.getData(0); 169 assertThat(bucketCount).isGreaterThan(2); 170 MetricsUtils.assertBucketTimePresent(data.getBucketInfo(0)); 171 assertThat(data.getBucketInfo(0).getAtomCount()).isEqualTo(1); 172 assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getLabel()) 173 .isEqualTo(0); 174 assertThat(data.getBucketInfo(0).getAtom(0).getAppBreadcrumbReported().getState()) 175 .isEqualTo(AppBreadcrumbReported.State.START); 176 177 MetricsUtils.assertBucketTimePresent(data.getBucketInfo(1)); 178 assertThat(data.getBucketInfo(1).getAtomCount()).isEqualTo(1); 179 180 MetricsUtils.assertBucketTimePresent(data.getBucketInfo(bucketCount - 1)); 181 assertThat(data.getBucketInfo(bucketCount - 1).getAtomCount()).isEqualTo(1); 182 assertThat(data.getBucketInfo(bucketCount - 1).getAtom( 183 0).getAppBreadcrumbReported().getLabel()) 184 .isEqualTo(2); 185 assertThat(data.getBucketInfo(bucketCount - 1).getAtom( 186 0).getAppBreadcrumbReported().getState()) 187 .isEqualTo(AppBreadcrumbReported.State.STOP); 188 } 189 testPulledGaugeMetricWithActivation()190 public void testPulledGaugeMetricWithActivation() throws Exception { 191 // Add AtomMatcher's. 192 int activationAtomMatcherId = 1; 193 int activationAtomMatcherLabel = 1; 194 195 int systemUptimeMatcherId = 2; 196 AtomMatcher activationAtomMatcher = MetricsUtils.appBreadcrumbMatcherWithLabel( 197 activationAtomMatcherId, activationAtomMatcherLabel); 198 AtomMatcher systemUptimeMatcher = AtomMatcher.newBuilder() 199 .setId(systemUptimeMatcherId) 200 .setSimpleAtomMatcher(SimpleAtomMatcher.newBuilder() 201 .setAtomId(Atom.SYSTEM_UPTIME_FIELD_NUMBER)) 202 .build(); 203 204 StatsdConfigProto.StatsdConfig.Builder builder = ConfigUtils.createConfigBuilder( 205 MetricsUtils.DEVICE_SIDE_TEST_PACKAGE); 206 builder.addAtomMatcher(activationAtomMatcher); 207 builder.addAtomMatcher(systemUptimeMatcher); 208 209 // Add GaugeMetric. 210 builder.addGaugeMetric(StatsdConfigProto.GaugeMetric.newBuilder() 211 .setId(MetricsUtils.GAUGE_METRIC_ID) 212 .setWhat(systemUptimeMatcherId) 213 .setGaugeFieldsFilter( 214 FieldFilter.newBuilder().setIncludeAll(true).build()) 215 .setBucket(StatsdConfigProto.TimeUnit.CTS) 216 .build()); 217 218 // Add activation. 219 builder.addMetricActivation(MetricActivation.newBuilder() 220 .setMetricId(MetricsUtils.GAUGE_METRIC_ID) 221 .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY) 222 .addEventActivation(EventActivation.newBuilder() 223 .setAtomMatcherId(activationAtomMatcherId) 224 .setTtlSeconds(5))); 225 226 // Upload config. 227 ConfigUtils.uploadConfig(getDevice(), builder); 228 229 // Plenty of time to pull, but we should not keep the data since we are not active. 230 RunUtil.getDefault().sleep(20_000); 231 232 StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(), 233 ExtensionRegistry.getEmptyRegistry()); 234 LogUtil.CLog.d("Got the following gauge metric data: " + metricReport.toString()); 235 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID); 236 assertThat(metricReport.hasGaugeMetrics()).isFalse(); 237 } 238 testPulledGaugeMetricWithConditionAndActivation()239 public void testPulledGaugeMetricWithConditionAndActivation() throws Exception { 240 final int conditionLabel = 2; 241 final int activationMatcherId = 5; 242 final int activationMatcherLabel = 5; 243 final int whatMatcherId = 8; 244 final int ttlSec = 5; 245 246 // Add AtomMatchers. 247 AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel( 248 APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, conditionLabel); 249 AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel( 250 APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, conditionLabel); 251 AtomMatcher activationMatcher = MetricsUtils.startAtomMatcherWithLabel( 252 activationMatcherId, activationMatcherLabel); 253 AtomMatcher whatMatcher = MetricsUtils.unspecifiedAtomMatcher(whatMatcherId); 254 255 StatsdConfig.Builder builder = 256 ConfigUtils.createConfigBuilder(MetricsUtils.DEVICE_SIDE_TEST_PACKAGE) 257 .addAtomMatcher(conditionStartAtomMatcher) 258 .addAtomMatcher(conditionStopAtomMatcher) 259 .addAtomMatcher(whatMatcher) 260 .addAtomMatcher(activationMatcher); 261 262 // Add Predicates. 263 SimplePredicate simplePredicate = SimplePredicate.newBuilder() 264 .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID) 265 .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID) 266 .build(); 267 Predicate predicate = Predicate.newBuilder() 268 .setId(MetricsUtils.StringToId("Predicate")) 269 .setSimplePredicate(simplePredicate) 270 .build(); 271 builder.addPredicate(predicate); 272 273 // Add GaugeMetric. 274 builder.addGaugeMetric(GaugeMetric.newBuilder() 275 .setId(MetricsUtils.GAUGE_METRIC_ID) 276 .setWhat(whatMatcher.getId()) 277 .setBucket(TimeUnit.ONE_MINUTE) 278 .setCondition(predicate.getId()) 279 .setGaugeFieldsFilter(FieldFilter.newBuilder() 280 .setIncludeAll(false) 281 .setFields(FieldMatcher.newBuilder() 282 .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID))) 283 .setDimensionsInWhat(FieldMatcher.newBuilder().setField(whatMatcherId))) 284 .addMetricActivation(MetricActivation.newBuilder() 285 .setMetricId(MetricsUtils.GAUGE_METRIC_ID) 286 .addEventActivation(EventActivation.newBuilder() 287 .setAtomMatcherId(activationMatcherId) 288 .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY) 289 .setTtlSeconds(ttlSec))); 290 291 ConfigUtils.uploadConfig(getDevice(), builder); 292 293 // Activate the metric. 294 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 295 AppBreadcrumbReported.State.START.getNumber(), activationMatcherLabel); 296 RunUtil.getDefault().sleep(10); 297 298 // Set the condition to true. 299 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 300 AppBreadcrumbReported.State.START.getNumber(), conditionLabel); 301 RunUtil.getDefault().sleep(10); 302 303 // This value is collected. 304 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 305 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 10); 306 RunUtil.getDefault().sleep(10); 307 308 // Ignored; value already collected. 309 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 310 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 20); 311 RunUtil.getDefault().sleep(10); 312 313 // Set the condition to false. 314 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 315 AppBreadcrumbReported.State.STOP.getNumber(), conditionLabel); 316 RunUtil.getDefault().sleep(10); 317 318 // Value not updated because condition is false. 319 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 320 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 30); 321 RunUtil.getDefault().sleep(10); 322 323 // Let the metric deactivate. 324 RunUtil.getDefault().sleep(ttlSec * 1000); 325 326 // Value not collected. 327 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 328 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 40); 329 RunUtil.getDefault().sleep(10); 330 331 // Condition to true again. 332 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 333 AppBreadcrumbReported.State.START.getNumber(), conditionLabel); 334 RunUtil.getDefault().sleep(10); 335 336 // Value not collected. 337 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 338 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 50); 339 RunUtil.getDefault().sleep(10); 340 341 // Activate the metric. 342 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 343 AppBreadcrumbReported.State.START.getNumber(), activationMatcherLabel); 344 RunUtil.getDefault().sleep(10); 345 346 // Value collected. 347 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 348 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 60); 349 RunUtil.getDefault().sleep(10); 350 351 // Let the metric deactivate. 352 RunUtil.getDefault().sleep(ttlSec * 1000); 353 354 // Value not collected. 355 AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice(), 356 AppBreadcrumbReported.State.UNSPECIFIED.getNumber(), 70); 357 RunUtil.getDefault().sleep(10); 358 359 // Wait for the metrics to propagate to statsd. 360 RunUtil.getDefault().sleep(2000); 361 362 StatsLogReport metricReport = ReportUtils.getStatsLogReport(getDevice(), 363 ExtensionRegistry.getEmptyRegistry()); 364 LogUtil.CLog.d("Received the following data: " + metricReport.toString()); 365 assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.GAUGE_METRIC_ID); 366 assertThat(metricReport.hasGaugeMetrics()).isTrue(); 367 assertThat(metricReport.getIsActive()).isFalse(); 368 369 StatsLogReport.GaugeMetricDataWrapper gaugeData = metricReport.getGaugeMetrics(); 370 gaugeData = backfillGaugeMetricData(gaugeData); 371 assertThat(gaugeData.getDataCount()).isEqualTo(1); 372 assertThat(gaugeData.getData(0).getBucketInfoCount()).isEqualTo(2); 373 374 GaugeBucketInfo bucketInfo = gaugeData.getData(0).getBucketInfo(0); 375 MetricsUtils.assertBucketTimePresent(bucketInfo); 376 assertThat(bucketInfo.getAtomCount()).isEqualTo(1); 377 assertThat(bucketInfo.getAtom(0).getAppBreadcrumbReported().getLabel()).isEqualTo(10); 378 379 bucketInfo = gaugeData.getData(0).getBucketInfo(1); 380 MetricsUtils.assertBucketTimePresent(bucketInfo); 381 assertThat(bucketInfo.getAtomCount()).isEqualTo(1); 382 assertThat(bucketInfo.getAtom(0).getAppBreadcrumbReported().getLabel()).isEqualTo(60); 383 } 384 backfillGaugeMetricData( StatsLogReport.GaugeMetricDataWrapper dataWrapper)385 private StatsLogReport.GaugeMetricDataWrapper backfillGaugeMetricData( 386 StatsLogReport.GaugeMetricDataWrapper dataWrapper) { 387 StatsLogReport.GaugeMetricDataWrapper.Builder dataWrapperBuilder = dataWrapper.toBuilder(); 388 List<GaugeMetricData> backfilledMetricData = new ArrayList<>(); 389 for (GaugeMetricData gaugeMetricData : dataWrapperBuilder.getDataList()) { 390 GaugeMetricData.Builder gaugeMetricDataBuilder = gaugeMetricData.toBuilder(); 391 List<GaugeBucketInfo> backfilledBuckets = new ArrayList<>(); 392 for (GaugeBucketInfo bucketInfo : gaugeMetricData.getBucketInfoList()) { 393 backfilledBuckets.add(backfillGaugeBucket(bucketInfo.toBuilder())); 394 } 395 gaugeMetricDataBuilder.clearBucketInfo(); 396 gaugeMetricDataBuilder.addAllBucketInfo(backfilledBuckets); 397 backfilledMetricData.add(gaugeMetricDataBuilder.build()); 398 } 399 dataWrapperBuilder.clearData(); 400 dataWrapperBuilder.addAllData(backfilledMetricData); 401 return dataWrapperBuilder.build(); 402 } 403 backfillGaugeBucket(GaugeBucketInfo.Builder bucketInfoBuilder)404 private GaugeBucketInfo backfillGaugeBucket(GaugeBucketInfo.Builder bucketInfoBuilder) { 405 if (bucketInfoBuilder.getAtomCount() != 0) { 406 return bucketInfoBuilder.build(); 407 } 408 List<Pair<Atom, Long>> atomTimestampData = new ArrayList<>(); 409 for (StatsLog.AggregatedAtomInfo atomInfo : bucketInfoBuilder.getAggregatedAtomInfoList()) { 410 for (long timestampNs : atomInfo.getElapsedTimestampNanosList()) { 411 atomTimestampData.add(Pair.create(atomInfo.getAtom(), timestampNs)); 412 } 413 } 414 atomTimestampData.sort(Comparator.comparing(o -> o.second)); 415 bucketInfoBuilder.clearAggregatedAtomInfo(); 416 for (Pair<Atom, Long> atomTimestamp : atomTimestampData) { 417 bucketInfoBuilder.addAtom(atomTimestamp.first); 418 bucketInfoBuilder.addElapsedTimestampNanos(atomTimestamp.second); 419 } 420 return bucketInfoBuilder.build(); 421 } 422 } 423