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