| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package android.cts.statsd.metric; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import android.cts.statsd.atom.DeviceAtomTestCase; |
| |
| import com.android.internal.os.StatsdConfigProto.ActivationType; |
| import com.android.internal.os.StatsdConfigProto.AtomMatcher; |
| import com.android.internal.os.StatsdConfigProto.EventActivation; |
| import com.android.internal.os.StatsdConfigProto.FieldFilter; |
| import com.android.internal.os.StatsdConfigProto.FieldMatcher; |
| import com.android.internal.os.StatsdConfigProto.FieldValueMatcher; |
| import com.android.internal.os.StatsdConfigProto.MetricActivation; |
| import com.android.internal.os.StatsdConfigProto.Predicate; |
| import com.android.internal.os.StatsdConfigProto.SimpleAtomMatcher; |
| import com.android.internal.os.StatsdConfigProto.SimplePredicate; |
| import com.android.internal.os.StatsdConfigProto.StatsdConfig; |
| import com.android.internal.os.StatsdConfigProto.TimeUnit; |
| import com.android.internal.os.StatsdConfigProto.ValueMetric; |
| |
| import com.android.os.AtomsProto.AppBreadcrumbReported; |
| import com.android.os.AtomsProto.Atom; |
| import com.android.os.AtomsProto.SystemElapsedRealtime; |
| import com.android.os.StatsLog.StatsLogReport; |
| import com.android.os.StatsLog.ValueBucketInfo; |
| import com.android.os.StatsLog.ValueMetricData; |
| |
| import com.android.tradefed.log.LogUtil; |
| |
| public class ValueMetricsTests extends DeviceAtomTestCase { |
| private static final int APP_BREADCRUMB_REPORTED_A_MATCH_START_ID = 0; |
| private static final int APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID = 1; |
| private static final int APP_BREADCRUMB_REPORTED_B_MATCH_START_ID = 2; |
| |
| public void testValueMetric() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| // Add AtomMatcher's. |
| AtomMatcher startAtomMatcher = |
| MetricsUtils.startAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID); |
| AtomMatcher stopAtomMatcher = |
| MetricsUtils.stopAtomMatcher(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID); |
| AtomMatcher atomMatcher = |
| MetricsUtils.simpleAtomMatcher(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID); |
| |
| StatsdConfig.Builder builder = createConfigBuilder(); |
| builder.addAtomMatcher(startAtomMatcher); |
| builder.addAtomMatcher(stopAtomMatcher); |
| builder.addAtomMatcher(atomMatcher); |
| |
| // Add ValueMetric. |
| builder.addValueMetric( |
| ValueMetric.newBuilder() |
| .setId(MetricsUtils.VALUE_METRIC_ID) |
| .setWhat(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID) |
| .setBucket(TimeUnit.CTS) |
| .setValueField(FieldMatcher.newBuilder() |
| .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) |
| .addChild(FieldMatcher.newBuilder().setField( |
| AppBreadcrumbReported.LABEL_FIELD_NUMBER))) |
| .setDimensionsInWhat(FieldMatcher.newBuilder() |
| .setField(APP_BREADCRUMB_REPORTED_B_MATCH_START_ID) |
| .build()) |
| .build()); |
| |
| // Upload config. |
| uploadConfig(builder); |
| |
| // Create AppBreadcrumbReported Start/Stop events. |
| doAppBreadcrumbReportedStart(1); |
| Thread.sleep(1000); |
| doAppBreadcrumbReportedStop(1); |
| doAppBreadcrumbReportedStart(3); |
| doAppBreadcrumbReportedStop(3); |
| |
| // Wait for the metrics to propagate to statsd. |
| Thread.sleep(1000); |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString()); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); |
| assertThat(metricReport.hasValueMetrics()).isTrue(); |
| StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics(); |
| assertThat(valueData.getDataCount()).isEqualTo(1); |
| |
| int bucketCount = valueData.getData(0).getBucketInfoCount(); |
| assertThat(bucketCount).isGreaterThan(1); |
| ValueMetricData data = valueData.getData(0); |
| int totalValue = 0; |
| for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) { |
| MetricsUtils.assertBucketTimePresent(bucketInfo); |
| assertThat(bucketInfo.getValuesCount()).isEqualTo(1); |
| assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0); |
| totalValue += (int) bucketInfo.getValues(0).getValueLong(); |
| } |
| assertThat(totalValue).isEqualTo(8); |
| } |
| |
| // Test value metric with pulled atoms and across multiple buckets |
| public void testPullerAcrossBuckets() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| // Add AtomMatcher's. |
| final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START"; |
| final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP"; |
| final String predicateName = "APP_BREADCRUMB_REPORTED_IS_STOP"; |
| |
| AtomMatcher startAtomMatcher = |
| MetricsUtils.startAtomMatcher(predicateTrueName.hashCode()); |
| AtomMatcher stopAtomMatcher = |
| MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode()); |
| |
| StatsdConfig.Builder builder = createConfigBuilder(); |
| builder.addAtomMatcher(startAtomMatcher); |
| builder.addAtomMatcher(stopAtomMatcher); |
| builder.addPredicate(Predicate.newBuilder() |
| .setId(predicateName.hashCode()) |
| .setSimplePredicate(SimplePredicate.newBuilder() |
| .setStart(predicateTrueName.hashCode()) |
| .setStop(predicateFalseName.hashCode()) |
| .setCountNesting(false) |
| ) |
| ); |
| |
| final String atomName = "SYSTEM_ELAPSED_REALTIME"; |
| SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER); |
| builder.addAtomMatcher(AtomMatcher.newBuilder() |
| .setId(atomName.hashCode()) |
| .setSimpleAtomMatcher(sam)); |
| |
| // Add ValueMetric. |
| builder.addValueMetric( |
| ValueMetric.newBuilder() |
| .setId(MetricsUtils.VALUE_METRIC_ID) |
| .setWhat(atomName.hashCode()) |
| .setBucket(TimeUnit.ONE_MINUTE) |
| .setValueField(FieldMatcher.newBuilder() |
| .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER) |
| .addChild(FieldMatcher.newBuilder().setField( |
| SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER))) |
| .setCondition(predicateName.hashCode()) |
| .build()); |
| |
| // Upload config. |
| uploadConfig(builder); |
| |
| // Create AppBreadcrumbReported Start/Stop events. |
| doAppBreadcrumbReportedStart(1); |
| // Wait for 2 min and 1 sec to capture at least 2 buckets |
| Thread.sleep(2*60_000 + 10_000); |
| doAppBreadcrumbReportedStop(1); |
| |
| // Wait for the metrics to propagate to statsd. |
| Thread.sleep(1_000); |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString()); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); |
| assertThat(metricReport.hasValueMetrics()).isTrue(); |
| StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics(); |
| assertThat(valueData.getDataCount()).isEqualTo(1); |
| |
| int bucketCount = valueData.getData(0).getBucketInfoCount(); |
| // should have at least 2 buckets |
| assertThat(bucketCount).isAtLeast(2); |
| ValueMetricData data = valueData.getData(0); |
| int totalValue = 0; |
| for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) { |
| MetricsUtils.assertBucketTimePresent(bucketInfo); |
| assertThat(bucketInfo.getValuesCount()).isEqualTo(1); |
| assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0); |
| totalValue += (int) bucketInfo.getValues(0).getValueLong(); |
| } |
| // At most we lose one full min bucket |
| assertThat(totalValue).isGreaterThan(130_000 - 60_000); |
| } |
| |
| // Test value metric with pulled atoms and across multiple buckets |
| public void testMultipleEventsPerBucket() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| // Add AtomMatcher's. |
| final String predicateTrueName = "APP_BREADCRUMB_REPORTED_START"; |
| final String predicateFalseName = "APP_BREADCRUMB_REPORTED_STOP"; |
| final String predicateName = "APP_BREADCRUMB_REPORTED_IS_STOP"; |
| |
| AtomMatcher startAtomMatcher = |
| MetricsUtils.startAtomMatcher(predicateTrueName.hashCode()); |
| AtomMatcher stopAtomMatcher = |
| MetricsUtils.stopAtomMatcher(predicateFalseName.hashCode()); |
| |
| StatsdConfig.Builder builder = createConfigBuilder(); |
| builder.addAtomMatcher(startAtomMatcher); |
| builder.addAtomMatcher(stopAtomMatcher); |
| builder.addPredicate(Predicate.newBuilder() |
| .setId(predicateName.hashCode()) |
| .setSimplePredicate(SimplePredicate.newBuilder() |
| .setStart(predicateTrueName.hashCode()) |
| .setStop(predicateFalseName.hashCode()) |
| .setCountNesting(false) |
| ) |
| ); |
| |
| final String atomName = "SYSTEM_ELAPSED_REALTIME"; |
| SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder().setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER); |
| builder.addAtomMatcher(AtomMatcher.newBuilder() |
| .setId(atomName.hashCode()) |
| .setSimpleAtomMatcher(sam)); |
| |
| // Add ValueMetric. |
| builder.addValueMetric( |
| ValueMetric.newBuilder() |
| .setId(MetricsUtils.VALUE_METRIC_ID) |
| .setWhat(atomName.hashCode()) |
| .setBucket(TimeUnit.ONE_MINUTE) |
| .setValueField(FieldMatcher.newBuilder() |
| .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER) |
| .addChild(FieldMatcher.newBuilder().setField( |
| SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER))) |
| .setCondition(predicateName.hashCode()) |
| .build()); |
| |
| // Upload config. |
| uploadConfig(builder); |
| |
| final int NUM_EVENTS = 10; |
| final long GAP_INTERVAL = 10_000; |
| // Create AppBreadcrumbReported Start/Stop events. |
| for (int i = 0; i < NUM_EVENTS; i ++) { |
| doAppBreadcrumbReportedStart(1); |
| Thread.sleep(GAP_INTERVAL); |
| doAppBreadcrumbReportedStop(1); |
| Thread.sleep(GAP_INTERVAL); |
| } |
| |
| // Wait for the metrics to propagate to statsd. |
| Thread.sleep(1_000); |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString()); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); |
| assertThat(metricReport.hasValueMetrics()).isTrue(); |
| StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics(); |
| assertThat(valueData.getDataCount()).isEqualTo(1); |
| |
| int bucketCount = valueData.getData(0).getBucketInfoCount(); |
| // should have at least 2 buckets |
| assertThat(bucketCount).isAtLeast(2); |
| ValueMetricData data = valueData.getData(0); |
| int totalValue = 0; |
| for (ValueBucketInfo bucketInfo : data.getBucketInfoList()) { |
| MetricsUtils.assertBucketTimePresent(bucketInfo); |
| assertThat(bucketInfo.getValuesCount()).isEqualTo(1); |
| assertThat(bucketInfo.getValues(0).getIndex()).isEqualTo(0); |
| totalValue += (int) bucketInfo.getValues(0).getValueLong(); |
| } |
| // At most we lose one full min bucket |
| assertThat((long) totalValue).isGreaterThan(GAP_INTERVAL * NUM_EVENTS - 60_000); |
| } |
| |
| // Test value metric with pulled atoms and across multiple buckets |
| public void testPullerAcrossBucketsWithActivation() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| |
| StatsdConfig.Builder builder = createConfigBuilder(); |
| |
| // Add AtomMatcher's. |
| int activationAtomMatcherId = 1; |
| int activationAtomMatcherLabel = 1; |
| AtomMatcher activationAtomMatcher = |
| MetricsUtils.appBreadcrumbMatcherWithLabel( |
| activationAtomMatcherId, activationAtomMatcherLabel); |
| final String atomName = "SYSTEM_ELAPSED_REALTIME"; |
| SimpleAtomMatcher.Builder sam = SimpleAtomMatcher.newBuilder() |
| .setAtomId(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER); |
| builder.addAtomMatcher(activationAtomMatcher) |
| .addAtomMatcher(AtomMatcher.newBuilder() |
| .setId(atomName.hashCode()) |
| .setSimpleAtomMatcher(sam)); |
| |
| // Add ValueMetric. |
| builder.addValueMetric( |
| ValueMetric.newBuilder() |
| .setId(MetricsUtils.VALUE_METRIC_ID) |
| .setWhat(atomName.hashCode()) |
| .setBucket(TimeUnit.ONE_MINUTE) |
| .setValueField(FieldMatcher.newBuilder() |
| .setField(Atom.SYSTEM_ELAPSED_REALTIME_FIELD_NUMBER) |
| .addChild(FieldMatcher.newBuilder().setField( |
| SystemElapsedRealtime.TIME_MILLIS_FIELD_NUMBER))) |
| .build()); |
| // Add activation. |
| builder.addMetricActivation(MetricActivation.newBuilder() |
| .setMetricId(MetricsUtils.VALUE_METRIC_ID) |
| .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY) |
| .addEventActivation(EventActivation.newBuilder() |
| .setAtomMatcherId(activationAtomMatcherId) |
| .setTtlSeconds(5))); |
| |
| |
| // Upload config. |
| uploadConfig(builder); |
| |
| // Wait for 1 min and 10 sec to capture at least 1 bucket |
| Thread.sleep(60_000 + 10_000); |
| |
| // Wait for the metrics to propagate to statsd. |
| Thread.sleep(1_000); |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| LogUtil.CLog.d("Got the following value metric data: " + metricReport.toString()); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); |
| assertThat(metricReport.hasValueMetrics()).isFalse(); |
| } |
| |
| public void testValueMetricWithConditionAndActivation() throws Exception { |
| if (statsdDisabled()) { |
| return; |
| } |
| |
| final int conditionLabel = 2; |
| final int activationMatcherId = 5; |
| final int activationMatcherLabel = 5; |
| final int whatMatcherId = 8; |
| final int ttlSec = 5; |
| |
| // Add AtomMatchers. |
| AtomMatcher conditionStartAtomMatcher = MetricsUtils.startAtomMatcherWithLabel( |
| APP_BREADCRUMB_REPORTED_A_MATCH_START_ID, conditionLabel); |
| AtomMatcher conditionStopAtomMatcher = MetricsUtils.stopAtomMatcherWithLabel( |
| APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID, conditionLabel); |
| AtomMatcher activationMatcher = |
| MetricsUtils.startAtomMatcherWithLabel( |
| activationMatcherId, activationMatcherLabel); |
| AtomMatcher whatMatcher = |
| MetricsUtils.unspecifiedAtomMatcher(whatMatcherId); |
| |
| StatsdConfig.Builder builder = createConfigBuilder() |
| .addAtomMatcher(conditionStartAtomMatcher) |
| .addAtomMatcher(conditionStopAtomMatcher) |
| .addAtomMatcher(whatMatcher) |
| .addAtomMatcher(activationMatcher); |
| |
| // Add Predicates. |
| SimplePredicate simplePredicate = SimplePredicate.newBuilder() |
| .setStart(APP_BREADCRUMB_REPORTED_A_MATCH_START_ID) |
| .setStop(APP_BREADCRUMB_REPORTED_A_MATCH_STOP_ID) |
| .build(); |
| Predicate predicate = Predicate.newBuilder() |
| .setId(MetricsUtils.StringToId("Predicate")) |
| .setSimplePredicate(simplePredicate) |
| .build(); |
| builder.addPredicate(predicate); |
| |
| // Add ValueMetric. |
| builder |
| .addValueMetric(ValueMetric.newBuilder() |
| .setId(MetricsUtils.VALUE_METRIC_ID) |
| .setWhat(whatMatcher.getId()) |
| .setBucket(TimeUnit.ONE_MINUTE) |
| .setCondition(predicate.getId()) |
| .setValueField(FieldMatcher.newBuilder() |
| .setField(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER) |
| .addChild(FieldMatcher.newBuilder() |
| .setField(AppBreadcrumbReported.LABEL_FIELD_NUMBER)) |
| ) |
| .setDimensionsInWhat(FieldMatcher.newBuilder().setField(whatMatcherId)) |
| ) |
| .addMetricActivation(MetricActivation.newBuilder() |
| .setMetricId(MetricsUtils.VALUE_METRIC_ID) |
| .addEventActivation(EventActivation.newBuilder() |
| .setAtomMatcherId(activationMatcherId) |
| .setActivationType(ActivationType.ACTIVATE_IMMEDIATELY) |
| .setTtlSeconds(ttlSec) |
| ) |
| ); |
| |
| uploadConfig(builder); |
| |
| // Activate the metric. |
| doAppBreadcrumbReportedStart(activationMatcherLabel); |
| Thread.sleep(10); |
| |
| // Set the condition to true. |
| doAppBreadcrumbReportedStart(conditionLabel); |
| Thread.sleep(10); |
| |
| // Skipped due to unknown condition at start of bucket. |
| doAppBreadcrumbReported(10); |
| Thread.sleep(10); |
| |
| // Skipped due to unknown condition at start of bucket. |
| doAppBreadcrumbReported(200); |
| Thread.sleep(10); |
| |
| // Set the condition to false. |
| doAppBreadcrumbReportedStop(conditionLabel); |
| Thread.sleep(10); |
| |
| // Log an event that should not be counted because condition is false. |
| doAppBreadcrumbReported(3_000); |
| Thread.sleep(10); |
| |
| // Let the metric deactivate. |
| Thread.sleep(ttlSec * 1000); |
| |
| // Log an event that should not be counted. |
| doAppBreadcrumbReported(40_000); |
| Thread.sleep(10); |
| |
| // Condition to true again. |
| doAppBreadcrumbReportedStart(conditionLabel); |
| Thread.sleep(10); |
| |
| // Event should not be counted, metric is still not active. |
| doAppBreadcrumbReported(500_000); |
| Thread.sleep(10); |
| |
| // Activate the metric. |
| doAppBreadcrumbReportedStart(activationMatcherLabel); |
| Thread.sleep(10); |
| |
| // Log an event that should be counted. |
| doAppBreadcrumbReported(6_000_000); |
| Thread.sleep(10); |
| |
| // Let the metric deactivate. |
| Thread.sleep(ttlSec * 1000); |
| |
| // Log an event that should not be counted. |
| doAppBreadcrumbReported(70_000_000); |
| Thread.sleep(10); |
| |
| // Wait for the metrics to propagate to statsd. |
| Thread.sleep(2000); |
| |
| StatsLogReport metricReport = getStatsLogReport(); |
| LogUtil.CLog.d("Received the following data: " + metricReport.toString()); |
| assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.VALUE_METRIC_ID); |
| assertThat(metricReport.hasValueMetrics()).isTrue(); |
| assertThat(metricReport.getIsActive()).isFalse(); |
| |
| StatsLogReport.ValueMetricDataWrapper valueData = metricReport.getValueMetrics(); |
| assertThat(valueData.getDataCount()).isEqualTo(1); |
| assertThat(valueData.getData(0).getBucketInfoCount()).isEqualTo(1); |
| long totalValue = valueData.getData(0).getBucketInfoList().stream() |
| .peek(MetricsUtils::assertBucketTimePresent) |
| .peek(bucketInfo -> assertThat(bucketInfo.getValuesCount()).isEqualTo(1)) |
| .map(bucketInfo -> bucketInfo.getValues(0)) |
| .peek(value -> assertThat(value.getIndex()).isEqualTo(0)) |
| .mapToLong(value -> value.getValueLong()) |
| .sum(); |
| assertThat(totalValue).isEqualTo(6_000_000); |
| } |
| |
| } |