blob: 2e0161be809693b5d2b60e0ba968be82269c7af7 [file] [log] [blame]
/*
* Copyright (C) 2017 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 com.android.statsd.loadtest;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import com.android.internal.os.StatsdConfigProto.Predicate;
import com.android.internal.os.StatsdConfigProto.CountMetric;
import com.android.internal.os.StatsdConfigProto.DurationMetric;
import com.android.internal.os.StatsdConfigProto.MetricConditionLink;
import com.android.internal.os.StatsdConfigProto.EventMetric;
import com.android.internal.os.StatsdConfigProto.GaugeMetric;
import com.android.internal.os.StatsdConfigProto.ValueMetric;
import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
import com.android.internal.os.StatsdConfigProto.AtomMatcher;
import com.android.internal.os.StatsdConfigProto.SimplePredicate;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.internal.os.StatsdConfigProto.TimeUnit;
import java.io.InputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Creates StatsdConfig protos for loadtesting.
*/
public class ConfigFactory {
public static class ConfigMetadata {
public final byte[] bytes;
public final int numMetrics;
public ConfigMetadata(byte[] bytes, int numMetrics) {
this.bytes = bytes;
this.numMetrics = numMetrics;
}
}
public static final long CONFIG_ID = 123456789;
private static final String TAG = "loadtest.ConfigFactory";
private final StatsdConfig mTemplate;
public ConfigFactory(Context context) {
// Read the config template from the resoures.
Resources res = context.getResources();
byte[] template = null;
StatsdConfig templateProto = null;
try {
InputStream inputStream = res.openRawResource(R.raw.loadtest_config);
template = new byte[inputStream.available()];
inputStream.read(template);
templateProto = StatsdConfig.parseFrom(template);
} catch (IOException e) {
Log.e(TAG, "Unable to read or parse loadtest config template. Using an empty config.");
}
mTemplate = templateProto == null ? StatsdConfig.newBuilder().build() : templateProto;
Log.d(TAG, "Loadtest template config: " + mTemplate);
}
/**
* Generates a config.
*
* All configs are based on the same template.
* That template is designed to make the most use of the set of atoms that {@code SequencePusher}
* pushes, and to exercise as many of the metrics features as possible.
* Furthermore, by passing a replication factor to this method, one can artificially inflate
* the number of metrics in the config. One can also adjust the bucket size for aggregate
* metrics.
*
* @param replication The number of times each metric is replicated in the config.
* If the config template has n metrics, the generated config will have n * replication
* ones
* @param bucketMillis The bucket size, in milliseconds, for aggregate metrics
* @param placebo If true, only return an empty config
* @return The serialized config and the number of metrics.
*/
public ConfigMetadata getConfig(int replication, TimeUnit bucket, boolean placebo,
boolean includeCount, boolean includeDuration, boolean includeEvent,
boolean includeValue, boolean includeGauge) {
StatsdConfig.Builder config = StatsdConfig.newBuilder()
.setId(CONFIG_ID);
if (placebo) {
replication = 0; // Config will be empty, aside from a name.
}
int numMetrics = 0;
for (int i = 0; i < replication; i++) {
// metrics
if (includeEvent) {
for (EventMetric metric : mTemplate.getEventMetricList()) {
addEventMetric(metric, i, config);
numMetrics++;
}
}
if (includeCount) {
for (CountMetric metric : mTemplate.getCountMetricList()) {
addCountMetric(metric, i, bucket, config);
numMetrics++;
}
}
if (includeDuration) {
for (DurationMetric metric : mTemplate.getDurationMetricList()) {
addDurationMetric(metric, i, bucket, config);
numMetrics++;
}
}
if (includeGauge) {
for (GaugeMetric metric : mTemplate.getGaugeMetricList()) {
addGaugeMetric(metric, i, bucket, config);
numMetrics++;
}
}
if (includeValue) {
for (ValueMetric metric : mTemplate.getValueMetricList()) {
addValueMetric(metric, i, bucket, config);
numMetrics++;
}
}
// predicates
for (Predicate predicate : mTemplate.getPredicateList()) {
addPredicate(predicate, i, config);
}
// matchers
for (AtomMatcher matcher : mTemplate.getAtomMatcherList()) {
addMatcher(matcher, i, config);
}
}
Log.d(TAG, "Loadtest config is : " + config.build());
Log.d(TAG, "Generated config has " + numMetrics + " metrics");
return new ConfigMetadata(config.build().toByteArray(), numMetrics);
}
/**
* Creates {@link MetricConditionLink}s that are identical to the one passed to this method,
* except that the names are appended with the provided suffix.
*/
private List<MetricConditionLink> getLinks(
List<MetricConditionLink> links, int suffix) {
List<MetricConditionLink> newLinks = new ArrayList();
for (MetricConditionLink link : links) {
newLinks.add(link.toBuilder()
.setCondition(link.getCondition() + suffix)
.build());
}
return newLinks;
}
/**
* Creates an {@link EventMetric} based on the template. Makes sure that all names are appended
* with the provided suffix. Then adds that metric to the config.
*/
private void addEventMetric(EventMetric template, int suffix, StatsdConfig.Builder config) {
EventMetric.Builder metric = template.toBuilder()
.setId(template.getId() + suffix)
.setWhat(template.getWhat() + suffix);
if (template.hasCondition()) {
metric.setCondition(template.getCondition() + suffix);
}
if (template.getLinksCount() > 0) {
List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
metric.clearLinks();
metric.addAllLinks(links);
}
config.addEventMetric(metric);
}
/**
* Creates a {@link CountMetric} based on the template. Makes sure that all names are appended
* with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
*/
private void addCountMetric(CountMetric template, int suffix, TimeUnit bucket,
StatsdConfig.Builder config) {
CountMetric.Builder metric = template.toBuilder()
.setId(template.getId() + suffix)
.setWhat(template.getWhat() + suffix);
if (template.hasCondition()) {
metric.setCondition(template.getCondition() + suffix);
}
if (template.getLinksCount() > 0) {
List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
metric.clearLinks();
metric.addAllLinks(links);
}
metric.setBucket(bucket);
config.addCountMetric(metric);
}
/**
* Creates a {@link DurationMetric} based on the template. Makes sure that all names are appended
* with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
*/
private void addDurationMetric(DurationMetric template, int suffix, TimeUnit bucket,
StatsdConfig.Builder config) {
DurationMetric.Builder metric = template.toBuilder()
.setId(template.getId() + suffix)
.setWhat(template.getWhat() + suffix);
if (template.hasCondition()) {
metric.setCondition(template.getCondition() + suffix);
}
if (template.getLinksCount() > 0) {
List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
metric.clearLinks();
metric.addAllLinks(links);
}
metric.setBucket(bucket);
config.addDurationMetric(metric);
}
/**
* Creates a {@link GaugeMetric} based on the template. Makes sure that all names are appended
* with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
*/
private void addGaugeMetric(GaugeMetric template, int suffix, TimeUnit bucket,
StatsdConfig.Builder config) {
GaugeMetric.Builder metric = template.toBuilder()
.setId(template.getId() + suffix)
.setWhat(template.getWhat() + suffix);
if (template.hasCondition()) {
metric.setCondition(template.getCondition() + suffix);
}
if (template.getLinksCount() > 0) {
List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
metric.clearLinks();
metric.addAllLinks(links);
}
metric.setBucket(bucket);
config.addGaugeMetric(metric);
}
/**
* Creates a {@link ValueMetric} based on the template. Makes sure that all names are appended
* with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
*/
private void addValueMetric(ValueMetric template, int suffix, TimeUnit bucket,
StatsdConfig.Builder config) {
ValueMetric.Builder metric = template.toBuilder()
.setId(template.getId() + suffix)
.setWhat(template.getWhat() + suffix);
if (template.hasCondition()) {
metric.setCondition(template.getCondition() + suffix);
}
if (template.getLinksCount() > 0) {
List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
metric.clearLinks();
metric.addAllLinks(links);
}
metric.setBucket(bucket);
config.addValueMetric(metric);
}
/**
* Creates a {@link Predicate} based on the template. Makes sure that all names
* are appended with the provided suffix. Then adds that predicate to the config.
*/
private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) {
Predicate.Builder predicate = template.toBuilder()
.setId(template.getId() + suffix);
if (template.hasCombination()) {
Predicate.Combination.Builder cb = template.getCombination().toBuilder()
.clearPredicate();
for (long child : template.getCombination().getPredicateList()) {
cb.addPredicate(child + suffix);
}
predicate.setCombination(cb.build());
}
if (template.hasSimplePredicate()) {
SimplePredicate.Builder sc = template.getSimplePredicate().toBuilder()
.setStart(template.getSimplePredicate().getStart() + suffix)
.setStop(template.getSimplePredicate().getStop() + suffix);
if (template.getSimplePredicate().hasStopAll()) {
sc.setStopAll(template.getSimplePredicate().getStopAll() + suffix);
}
predicate.setSimplePredicate(sc.build());
}
config.addPredicate(predicate);
}
/**
* Creates a {@link AtomMatcher} based on the template. Makes sure that all names
* are appended with the provided suffix. Then adds that matcher to the config.
*/
private void addMatcher(AtomMatcher template, int suffix, StatsdConfig.Builder config) {
AtomMatcher.Builder matcher = template.toBuilder()
.setId(template.getId() + suffix);
if (template.hasCombination()) {
AtomMatcher.Combination.Builder cb = template.getCombination().toBuilder()
.clearMatcher();
for (long child : template.getCombination().getMatcherList()) {
cb.addMatcher(child + suffix);
}
matcher.setCombination(cb);
}
config.addAtomMatcher(matcher);
}
}