1 /* 2 * Copyright (c) 2017 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you 5 * may not use this file except in compliance with the License. You may 6 * 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 13 * implied. See the License for the specific language governing 14 * permissions and limitations under the License. 15 */ 16 17 package com.android.vts.entity; 18 19 import com.android.vts.proto.VtsReportMessage.ProfilingReportMessage; 20 import com.android.vts.proto.VtsReportMessage.VtsProfilingRegressionMode; 21 import com.android.vts.proto.VtsReportMessage.VtsProfilingType; 22 import com.google.appengine.api.datastore.Entity; 23 import com.google.appengine.api.datastore.Key; 24 import com.google.appengine.api.datastore.KeyFactory; 25 import com.google.common.collect.Lists; 26 import com.google.protobuf.ByteString; 27 import com.googlecode.objectify.annotation.Cache; 28 import com.googlecode.objectify.annotation.Id; 29 import com.googlecode.objectify.annotation.Ignore; 30 import com.googlecode.objectify.annotation.Index; 31 import com.googlecode.objectify.annotation.Parent; 32 import java.util.ArrayList; 33 import java.util.Date; 34 import java.util.List; 35 import java.util.Objects; 36 import lombok.Data; 37 import lombok.NoArgsConstructor; 38 import lombok.extern.log4j.Log4j2; 39 40 import static com.googlecode.objectify.ObjectifyService.ofy; 41 42 @com.googlecode.objectify.annotation.Entity(name = "ProfilingPointRun") 43 @Cache 44 @Data 45 @NoArgsConstructor 46 @Log4j2 47 /** Entity describing a profiling point execution. */ 48 public class ProfilingPointRunEntity implements DashboardEntity { 49 50 public static final String KIND = "ProfilingPointRun"; 51 52 // Property keys 53 public static final String TYPE = "type"; 54 public static final String REGRESSION_MODE = "regressionMode"; 55 public static final String LABELS = "labels"; 56 public static final String VALUES = "values"; 57 public static final String X_LABEL = "xLabel"; 58 public static final String Y_LABEL = "yLabel"; 59 public static final String OPTIONS = "options"; 60 61 /** This value will set the limit size of values array field */ 62 public static final int VALUE_SIZE_LIMIT = 50000; 63 64 @Ignore 65 private Key key; 66 67 /** ID field using profilingPointName */ 68 @Id 69 private String name; 70 71 /** parent field based on Test and TestRun key */ 72 @Parent 73 private com.googlecode.objectify.Key<?> parent; 74 75 /** VtsProfilingType in ProfilingPointRunEntity class */ 76 private int type; 77 78 /** VtsProfilingType in ProfilingPointRunEntity class */ 79 private int regressionMode; 80 81 /** list of label name */ 82 private List<String> labels; 83 84 /** list of values */ 85 private List<Long> values; 86 87 /** X axis label name */ 88 private String xLabel; 89 90 /** Y axis label name */ 91 private String yLabel; 92 93 /** Test Suite file name field */ 94 private List<String> options; 95 96 /** When this record was created or updated */ 97 @Index Date updated; 98 99 /** 100 * Create a ProfilingPointRunEntity object. 101 * 102 * @param parentKey The Key object for the parent TestRunEntity in datastore. 103 * @param name The name of the profiling point. 104 * @param type The (number) type of the profiling point data. 105 * @param regressionMode The (number) mode to use for detecting regression. 106 * @param labels List of data labels, or null if the data is unlabeled. 107 * @param values List of data values. 108 * @param xLabel The x axis label. 109 * @param yLabel The y axis label. 110 * @param options The list of key=value options for the profiling point run. 111 */ ProfilingPointRunEntity( Key parentKey, String name, int type, int regressionMode, List<String> labels, List<Long> values, String xLabel, String yLabel, List<String> options)112 public ProfilingPointRunEntity( 113 Key parentKey, 114 String name, 115 int type, 116 int regressionMode, 117 List<String> labels, 118 List<Long> values, 119 String xLabel, 120 String yLabel, 121 List<String> options) { 122 this.key = KeyFactory.createKey(parentKey, KIND, name); 123 this.name = name; 124 this.type = type; 125 this.regressionMode = regressionMode; 126 this.labels = labels == null ? null : new ArrayList<>(labels); 127 this.values = new ArrayList<>(values); 128 this.xLabel = xLabel; 129 this.yLabel = yLabel; 130 this.options = options; 131 this.updated = new Date(); 132 } 133 134 135 /** 136 * Create a ProfilingPointRunEntity object. 137 * 138 * @param parent The objectify Key for the parent TestRunEntity in datastore. 139 * @param name The name of the profiling point. 140 * @param type The (number) type of the profiling point data. 141 * @param regressionMode The (number) mode to use for detecting regression. 142 * @param labels List of data labels, or null if the data is unlabeled. 143 * @param values List of data values. 144 * @param xLabel The x axis label. 145 * @param yLabel The y axis label. 146 * @param options The list of key=value options for the profiling point run. 147 */ ProfilingPointRunEntity( com.googlecode.objectify.Key parent, String name, int type, int regressionMode, List<String> labels, List<Long> values, String xLabel, String yLabel, List<String> options)148 public ProfilingPointRunEntity( 149 com.googlecode.objectify.Key parent, 150 String name, 151 int type, 152 int regressionMode, 153 List<String> labels, 154 List<Long> values, 155 String xLabel, 156 String yLabel, 157 List<String> options) { 158 this.parent = parent; 159 this.name = name; 160 this.type = type; 161 this.regressionMode = regressionMode; 162 this.labels = labels == null ? null : new ArrayList<>(labels); 163 this.values = new ArrayList<>(values); 164 this.xLabel = xLabel; 165 this.yLabel = yLabel; 166 this.options = options; 167 this.updated = new Date(); 168 } 169 170 /** 171 * Get VtsProfilingType from int value. 172 * 173 * @return VtsProfilingType class. 174 */ getVtsProfilingType(int type)175 public VtsProfilingType getVtsProfilingType(int type) { 176 return VtsProfilingType.forNumber(type); 177 } 178 179 /** 180 * Get VtsProfilingRegressionMode from int value. 181 * 182 * @return VtsProfilingType class. 183 */ getVtsProfilingRegressionMode(int regressionMode)184 public VtsProfilingRegressionMode getVtsProfilingRegressionMode(int regressionMode) { 185 return VtsProfilingRegressionMode.forNumber(regressionMode); 186 } 187 188 /** 189 * Save multi rows function when the record exceed the limit which is 1MB. 190 * 191 * @return ProfilingPointRunEntity's key value. 192 */ saveMultiRow()193 public com.googlecode.objectify.Key<ProfilingPointRunEntity> saveMultiRow() { 194 if (this.getValues().size() > VALUE_SIZE_LIMIT) { 195 196 List<List<Long>> partitionedValueList = 197 Lists.partition(this.getValues(), VALUE_SIZE_LIMIT); 198 int partitionedValueListSize = partitionedValueList.size(); 199 200 List<List<String>> partitionedLabelList = new ArrayList<>(); 201 if (Objects.nonNull(this.getLabels()) && this.getLabels().size() > VALUE_SIZE_LIMIT) { 202 partitionedLabelList = Lists.partition(this.getLabels(), VALUE_SIZE_LIMIT); 203 } 204 205 com.googlecode.objectify.Key<ProfilingPointRunEntity> profilingPointRunEntityKey = null; 206 if (partitionedValueListSize < VALUE_SIZE_LIMIT) { 207 for (int index = 0; index < partitionedValueListSize; index++) { 208 if (index > 0) { 209 this.values.addAll(partitionedValueList.get(index)); 210 if (index < partitionedLabelList.size()) { 211 this.labels.addAll(partitionedLabelList.get(index)); 212 } 213 } else { 214 this.values = partitionedValueList.get(index); 215 if (index < partitionedLabelList.size()) { 216 this.labels = partitionedLabelList.get(index); 217 } 218 } 219 profilingPointRunEntityKey = ofy().save().entity(this).now(); 220 } 221 } 222 return profilingPointRunEntityKey; 223 } else { 224 return ofy().save().entity(this).now(); 225 } 226 } 227 228 /** Saving function for the instance of this class */ 229 @Override save()230 public com.googlecode.objectify.Key<ProfilingPointRunEntity> save() { 231 return ofy().save().entity(this).now(); 232 } 233 toEntity()234 public Entity toEntity() { 235 Entity profilingRun = new Entity(this.key); 236 profilingRun.setUnindexedProperty(TYPE, this.type); 237 profilingRun.setUnindexedProperty(REGRESSION_MODE, this.regressionMode); 238 if (this.labels != null) { 239 profilingRun.setUnindexedProperty(LABELS, this.labels); 240 } 241 profilingRun.setUnindexedProperty(VALUES, this.values); 242 profilingRun.setUnindexedProperty(X_LABEL, this.xLabel); 243 profilingRun.setUnindexedProperty(Y_LABEL, this.yLabel); 244 if (this.options != null) { 245 profilingRun.setUnindexedProperty(OPTIONS, this.options); 246 } 247 248 return profilingRun; 249 } 250 251 /** 252 * Convert an Entity object to a ProflilingPointRunEntity. 253 * 254 * @param e The entity to process. 255 * @return ProfilingPointRunEntity object with the properties from e, or null if incompatible. 256 */ 257 @SuppressWarnings("unchecked") fromEntity(Entity e)258 public static ProfilingPointRunEntity fromEntity(Entity e) { 259 if (!e.getKind().equals(KIND) 260 || e.getKey().getName() == null 261 || !e.hasProperty(TYPE) 262 || !e.hasProperty(REGRESSION_MODE) 263 || !e.hasProperty(VALUES) 264 || !e.hasProperty(X_LABEL) 265 || !e.hasProperty(Y_LABEL)) { 266 log.error("Missing profiling point attributes in entity: " + e.toString()); 267 return null; 268 } 269 try { 270 Key parentKey = e.getParent(); 271 String name = e.getKey().getName(); 272 int type = (int) (long) e.getProperty(TYPE); 273 int regressionMode = (int) (long) e.getProperty(REGRESSION_MODE); 274 List<Long> values = (List<Long>) e.getProperty(VALUES); 275 String xLabel = (String) e.getProperty(X_LABEL); 276 String yLabel = (String) e.getProperty(Y_LABEL); 277 List<String> labels = null; 278 if (e.hasProperty(LABELS)) { 279 labels = (List<String>) e.getProperty(LABELS); 280 } 281 List<String> options = null; 282 if (e.hasProperty(OPTIONS)) { 283 options = (List<String>) e.getProperty(OPTIONS); 284 } 285 return new ProfilingPointRunEntity( 286 parentKey, name, type, regressionMode, labels, values, xLabel, yLabel, options); 287 } catch (ClassCastException exception) { 288 // Invalid cast 289 log.warn("Error parsing profiling point run entity.", exception); 290 } 291 return null; 292 } 293 294 /** 295 * Convert a coverage report to a CoverageEntity. 296 * 297 * @param parent The ancestor objectify key for the coverage entity. 298 * @param profilingReport The profiling report containing profiling data. 299 * @return The ProfilingPointRunEntity for the profiling report message, or null if incompatible 300 */ fromProfilingReport( com.googlecode.objectify.Key parent, ProfilingReportMessage profilingReport)301 public static ProfilingPointRunEntity fromProfilingReport( 302 com.googlecode.objectify.Key parent, ProfilingReportMessage profilingReport) { 303 if (!profilingReport.hasName() 304 || !profilingReport.hasType() 305 || profilingReport.getType() == VtsProfilingType.UNKNOWN_VTS_PROFILING_TYPE 306 || !profilingReport.hasRegressionMode() 307 || !profilingReport.hasXAxisLabel() 308 || !profilingReport.hasYAxisLabel()) { 309 return null; // invalid profiling report; 310 } 311 String name = profilingReport.getName().toStringUtf8(); 312 VtsProfilingType type = profilingReport.getType(); 313 VtsProfilingRegressionMode regressionMode = profilingReport.getRegressionMode(); 314 String xLabel = profilingReport.getXAxisLabel().toStringUtf8(); 315 String yLabel = profilingReport.getYAxisLabel().toStringUtf8(); 316 List<Long> values; 317 List<String> labels = null; 318 switch (type) { 319 case VTS_PROFILING_TYPE_TIMESTAMP: 320 if (!profilingReport.hasStartTimestamp() 321 || !profilingReport.hasEndTimestamp() 322 || profilingReport.getEndTimestamp() 323 < profilingReport.getStartTimestamp()) { 324 return null; // missing timestamp 325 } 326 long value = 327 profilingReport.getEndTimestamp() - profilingReport.getStartTimestamp(); 328 values = new ArrayList<>(); 329 values.add(value); 330 break; 331 case VTS_PROFILING_TYPE_LABELED_VECTOR: 332 if (profilingReport.getValueCount() != profilingReport.getLabelCount()) { 333 return null; // jagged data 334 } 335 labels = new ArrayList<>(); 336 for (ByteString label : profilingReport.getLabelList()) { 337 labels.add(label.toStringUtf8()); 338 } 339 values = profilingReport.getValueList(); 340 break; 341 case VTS_PROFILING_TYPE_UNLABELED_VECTOR: 342 values = profilingReport.getValueList(); 343 break; 344 default: // should never happen 345 return null; 346 } 347 if (values.size() > VALUE_SIZE_LIMIT) { 348 values = values.subList(0, VALUE_SIZE_LIMIT); 349 } 350 List<String> options = null; 351 if (profilingReport.getOptionsCount() > 0) { 352 options = new ArrayList<>(); 353 for (ByteString optionBytes : profilingReport.getOptionsList()) { 354 options.add(optionBytes.toStringUtf8()); 355 } 356 } 357 return new ProfilingPointRunEntity( 358 parent, 359 name, 360 type.getNumber(), 361 regressionMode.getNumber(), 362 labels, 363 values, 364 xLabel, 365 yLabel, 366 options); 367 } 368 } 369