Home | History | Annotate | Download | only in loadtest
      1 /*
      2  * Copyright (C) 2017 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 com.android.statsd.loadtest;
     17 
     18 import android.content.Context;
     19 import android.content.res.Resources;
     20 import android.util.Log;
     21 
     22 import com.android.internal.os.StatsdConfigProto.Predicate;
     23 import com.android.internal.os.StatsdConfigProto.CountMetric;
     24 import com.android.internal.os.StatsdConfigProto.DurationMetric;
     25 import com.android.internal.os.StatsdConfigProto.MetricConditionLink;
     26 import com.android.internal.os.StatsdConfigProto.EventMetric;
     27 import com.android.internal.os.StatsdConfigProto.GaugeMetric;
     28 import com.android.internal.os.StatsdConfigProto.ValueMetric;
     29 import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
     30 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
     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 
     35 import java.io.InputStream;
     36 import java.io.IOException;
     37 import java.util.ArrayList;
     38 import java.util.List;
     39 
     40 /**
     41  * Creates StatsdConfig protos for loadtesting.
     42  */
     43 public class ConfigFactory {
     44     public static class ConfigMetadata {
     45         public final byte[] bytes;
     46         public final int numMetrics;
     47 
     48         public ConfigMetadata(byte[] bytes, int numMetrics) {
     49             this.bytes = bytes;
     50             this.numMetrics = numMetrics;
     51         }
     52     }
     53 
     54     public static final long CONFIG_ID = 123456789;
     55 
     56     private static final String TAG = "loadtest.ConfigFactory";
     57 
     58     private final StatsdConfig mTemplate;
     59 
     60     public ConfigFactory(Context context) {
     61         // Read the config template from the resoures.
     62         Resources res = context.getResources();
     63         byte[] template = null;
     64         StatsdConfig templateProto = null;
     65         try {
     66             InputStream inputStream = res.openRawResource(R.raw.loadtest_config);
     67             template = new byte[inputStream.available()];
     68             inputStream.read(template);
     69             templateProto = StatsdConfig.parseFrom(template);
     70         } catch (IOException e) {
     71             Log.e(TAG, "Unable to read or parse loadtest config template. Using an empty config.");
     72         }
     73         mTemplate = templateProto == null ? StatsdConfig.newBuilder().build() : templateProto;
     74 
     75         Log.d(TAG, "Loadtest template config: " + mTemplate);
     76     }
     77 
     78     /**
     79      * Generates a config.
     80      *
     81      * All configs are based on the same template.
     82      * That template is designed to make the most use of the set of atoms that {@code SequencePusher}
     83      * pushes, and to exercise as many of the metrics features as possible.
     84      * Furthermore, by passing a replication factor to this method, one can artificially inflate
     85      * the number of metrics in the config. One can also adjust the bucket size for aggregate
     86      * metrics.
     87      *
     88      * @param replication The number of times each metric is replicated in the config.
     89      *        If the config template has n metrics, the generated config will have n * replication
     90      *        ones
     91      * @param bucketMillis The bucket size, in milliseconds, for aggregate metrics
     92      * @param placebo If true, only return an empty config
     93      * @return The serialized config and the number of metrics.
     94      */
     95     public ConfigMetadata getConfig(int replication, TimeUnit bucket, boolean placebo,
     96             boolean includeCount, boolean includeDuration, boolean includeEvent,
     97             boolean includeValue, boolean includeGauge) {
     98         StatsdConfig.Builder config = StatsdConfig.newBuilder()
     99             .setId(CONFIG_ID);
    100         if (placebo) {
    101           replication = 0;  // Config will be empty, aside from a name.
    102         }
    103         int numMetrics = 0;
    104         for (int i = 0; i < replication; i++) {
    105             // metrics
    106             if (includeEvent) {
    107                 for (EventMetric metric : mTemplate.getEventMetricList()) {
    108                     addEventMetric(metric, i, config);
    109                     numMetrics++;
    110                 }
    111             }
    112             if (includeCount) {
    113                 for (CountMetric metric : mTemplate.getCountMetricList()) {
    114                     addCountMetric(metric, i, bucket, config);
    115                     numMetrics++;
    116                 }
    117             }
    118             if (includeDuration) {
    119                 for (DurationMetric metric : mTemplate.getDurationMetricList()) {
    120                     addDurationMetric(metric, i, bucket, config);
    121                     numMetrics++;
    122                 }
    123             }
    124             if (includeGauge) {
    125                 for (GaugeMetric metric : mTemplate.getGaugeMetricList()) {
    126                     addGaugeMetric(metric, i, bucket, config);
    127                     numMetrics++;
    128                 }
    129             }
    130             if (includeValue) {
    131                 for (ValueMetric metric : mTemplate.getValueMetricList()) {
    132                     addValueMetric(metric, i, bucket, config);
    133                     numMetrics++;
    134                 }
    135             }
    136             // predicates
    137             for (Predicate predicate : mTemplate.getPredicateList()) {
    138               addPredicate(predicate, i, config);
    139             }
    140             // matchers
    141             for (AtomMatcher matcher : mTemplate.getAtomMatcherList()) {
    142               addMatcher(matcher, i, config);
    143             }
    144         }
    145 
    146         Log.d(TAG, "Loadtest config is : " + config.build());
    147         Log.d(TAG, "Generated config has " + numMetrics + " metrics");
    148 
    149         return new ConfigMetadata(config.build().toByteArray(), numMetrics);
    150     }
    151 
    152     /**
    153      * Creates {@link MetricConditionLink}s that are identical to the one passed to this method,
    154      * except that the names are appended with the provided suffix.
    155      */
    156     private List<MetricConditionLink> getLinks(
    157         List<MetricConditionLink> links, int suffix) {
    158         List<MetricConditionLink> newLinks = new ArrayList();
    159         for (MetricConditionLink link : links) {
    160             newLinks.add(link.toBuilder()
    161                 .setCondition(link.getCondition() + suffix)
    162                 .build());
    163         }
    164         return newLinks;
    165     }
    166 
    167     /**
    168      * Creates an {@link EventMetric} based on the template. Makes sure that all names are appended
    169      * with the provided suffix. Then adds that metric to the config.
    170      */
    171     private void addEventMetric(EventMetric template, int suffix, StatsdConfig.Builder config) {
    172         EventMetric.Builder metric = template.toBuilder()
    173             .setId(template.getId() + suffix)
    174             .setWhat(template.getWhat() + suffix);
    175         if (template.hasCondition()) {
    176             metric.setCondition(template.getCondition() + suffix);
    177         }
    178         if (template.getLinksCount() > 0) {
    179             List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
    180             metric.clearLinks();
    181             metric.addAllLinks(links);
    182         }
    183         config.addEventMetric(metric);
    184     }
    185 
    186     /**
    187      * Creates a {@link CountMetric} based on the template. Makes sure that all names are appended
    188      * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
    189      */
    190     private void addCountMetric(CountMetric template, int suffix, TimeUnit bucket,
    191         StatsdConfig.Builder config) {
    192         CountMetric.Builder metric = template.toBuilder()
    193             .setId(template.getId() + suffix)
    194             .setWhat(template.getWhat() + suffix);
    195         if (template.hasCondition()) {
    196             metric.setCondition(template.getCondition() + suffix);
    197         }
    198         if (template.getLinksCount() > 0) {
    199             List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
    200             metric.clearLinks();
    201             metric.addAllLinks(links);
    202         }
    203         metric.setBucket(bucket);
    204         config.addCountMetric(metric);
    205     }
    206 
    207     /**
    208      * Creates a {@link DurationMetric} based on the template. Makes sure that all names are appended
    209      * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
    210      */
    211     private void addDurationMetric(DurationMetric template, int suffix, TimeUnit bucket,
    212         StatsdConfig.Builder config) {
    213         DurationMetric.Builder metric = template.toBuilder()
    214             .setId(template.getId() + suffix)
    215             .setWhat(template.getWhat() + suffix);
    216         if (template.hasCondition()) {
    217             metric.setCondition(template.getCondition() + suffix);
    218         }
    219         if (template.getLinksCount() > 0) {
    220             List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
    221             metric.clearLinks();
    222             metric.addAllLinks(links);
    223         }
    224         metric.setBucket(bucket);
    225         config.addDurationMetric(metric);
    226     }
    227 
    228     /**
    229      * Creates a {@link GaugeMetric} based on the template. Makes sure that all names are appended
    230      * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
    231      */
    232     private void addGaugeMetric(GaugeMetric template, int suffix, TimeUnit bucket,
    233         StatsdConfig.Builder config) {
    234         GaugeMetric.Builder metric = template.toBuilder()
    235             .setId(template.getId() + suffix)
    236             .setWhat(template.getWhat() + suffix);
    237         if (template.hasCondition()) {
    238             metric.setCondition(template.getCondition() + suffix);
    239         }
    240         if (template.getLinksCount() > 0) {
    241             List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
    242             metric.clearLinks();
    243             metric.addAllLinks(links);
    244         }
    245         metric.setBucket(bucket);
    246         config.addGaugeMetric(metric);
    247     }
    248 
    249     /**
    250      * Creates a {@link ValueMetric} based on the template. Makes sure that all names are appended
    251      * with the provided suffix, and overrides the bucket size. Then adds that metric to the config.
    252      */
    253     private void addValueMetric(ValueMetric template, int suffix, TimeUnit bucket,
    254         StatsdConfig.Builder config) {
    255         ValueMetric.Builder metric = template.toBuilder()
    256             .setId(template.getId() + suffix)
    257             .setWhat(template.getWhat() + suffix);
    258         if (template.hasCondition()) {
    259             metric.setCondition(template.getCondition() + suffix);
    260         }
    261         if (template.getLinksCount() > 0) {
    262             List<MetricConditionLink> links = getLinks(template.getLinksList(), suffix);
    263             metric.clearLinks();
    264             metric.addAllLinks(links);
    265         }
    266         metric.setBucket(bucket);
    267         config.addValueMetric(metric);
    268     }
    269 
    270     /**
    271      * Creates a {@link Predicate} based on the template. Makes sure that all names
    272      * are appended with the provided suffix. Then adds that predicate to the config.
    273      */
    274     private void addPredicate(Predicate template, int suffix, StatsdConfig.Builder config) {
    275         Predicate.Builder predicate = template.toBuilder()
    276             .setId(template.getId() + suffix);
    277         if (template.hasCombination()) {
    278             Predicate.Combination.Builder cb = template.getCombination().toBuilder()
    279                 .clearPredicate();
    280             for (long child : template.getCombination().getPredicateList()) {
    281                 cb.addPredicate(child + suffix);
    282             }
    283             predicate.setCombination(cb.build());
    284         }
    285         if (template.hasSimplePredicate()) {
    286             SimplePredicate.Builder sc = template.getSimplePredicate().toBuilder()
    287                 .setStart(template.getSimplePredicate().getStart() + suffix)
    288                 .setStop(template.getSimplePredicate().getStop() + suffix);
    289             if (template.getSimplePredicate().hasStopAll()) {
    290                 sc.setStopAll(template.getSimplePredicate().getStopAll() + suffix);
    291             }
    292             predicate.setSimplePredicate(sc.build());
    293         }
    294         config.addPredicate(predicate);
    295     }
    296 
    297     /**
    298      * Creates a {@link AtomMatcher} based on the template. Makes sure that all names
    299      * are appended with the provided suffix. Then adds that matcher to the config.
    300      */
    301     private void addMatcher(AtomMatcher template, int suffix, StatsdConfig.Builder config) {
    302         AtomMatcher.Builder matcher = template.toBuilder()
    303             .setId(template.getId() + suffix);
    304         if (template.hasCombination()) {
    305             AtomMatcher.Combination.Builder cb = template.getCombination().toBuilder()
    306                 .clearMatcher();
    307             for (long child : template.getCombination().getMatcherList()) {
    308                 cb.addMatcher(child + suffix);
    309             }
    310             matcher.setCombination(cb);
    311         }
    312         config.addAtomMatcher(matcher);
    313     }
    314 }
    315