1 /* 2 * Copyright (C) 2017 The Android Open Source Project 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.VtsProfilingRegressionMode; 20 import com.android.vts.util.StatSummary; 21 import com.google.appengine.api.datastore.Entity; 22 import com.google.appengine.api.datastore.Key; 23 import com.google.appengine.api.datastore.KeyFactory; 24 import java.util.ArrayList; 25 import java.util.HashMap; 26 import java.util.List; 27 import java.util.Map; 28 import java.util.logging.Level; 29 import java.util.logging.Logger; 30 31 /** Entity describing a profiling point summary. */ 32 public class ProfilingPointSummaryEntity implements DashboardEntity { 33 protected static final Logger logger = 34 Logger.getLogger(ProfilingPointSummaryEntity.class.getName()); 35 protected static final String DELIMITER = "#"; 36 37 public static final String KIND = "ProfilingPointSummary"; 38 public static final String ALL = "ALL"; 39 40 // Property keys 41 public static final String START_TIME = "startTime"; 42 public static final String MEAN = "mean"; 43 public static final String SUMSQ = "sumSq"; 44 public static final String MIN = "min"; 45 public static final String MAX = "max"; 46 public static final String LABELS = "labels"; 47 public static final String LABEL_MEANS = "labelMeans"; 48 public static final String LABEL_SUMSQS = "labelSumSqs"; 49 public static final String LABEL_MINS = "labelMins"; 50 public static final String LABEL_MAXES = "labelMaxes"; 51 public static final String LABEL_COUNTS = "labelCounts"; 52 public static final String COUNT = "count"; 53 public static final String BRANCH = "branch"; 54 public static final String BUILD_FLAVOR = "buildFlavor"; 55 public static final String SERIES = "series"; 56 57 private final Key key; 58 59 public final StatSummary globalStats; 60 public final List<String> labels; 61 public final Map<String, StatSummary> labelStats; 62 public final String branch; 63 public final String buildFlavor; 64 public final String series; 65 public final long startTime; 66 67 /** 68 * Create a ProfilingPointSummaryEntity object. 69 * 70 * @param parentKey The Key object for the parent TestRunEntity in the database. 71 * @param globalStats The StatSummary object recording global statistics about the profiling 72 * point. 73 * @param labels The list of data labels. 74 * @param labelStats The map from data label to StatSummary object for the label. 75 * @param branch The branch. 76 * @param buildFlavor The device build flavor. 77 * @param series The string describing the profiling point series (e.g. binder or passthrough). 78 * @param startTime The timestamp indicating the beginning of the summary. 79 */ 80 public ProfilingPointSummaryEntity( 81 Key parentKey, 82 StatSummary globalStats, 83 List<String> labels, 84 Map<String, StatSummary> labelStats, 85 String branch, 86 String buildFlavor, 87 String series, 88 long startTime) { 89 this.globalStats = globalStats; 90 this.labels = labels; 91 this.labelStats = labelStats; 92 this.buildFlavor = buildFlavor == null ? ALL : buildFlavor; 93 this.branch = branch == null ? ALL : branch; 94 this.series = series == null ? "" : series; 95 this.startTime = startTime; 96 this.key = createKey(parentKey, this.branch, this.buildFlavor, this.series, this.startTime); 97 } 98 99 /** 100 * Create a new ProfilingPointSummaryEntity object. 101 * 102 * @param parentKey The Key object for the parent TestRunEntity in the database. 103 * @param branch The branch. 104 * @param buildFlavor The buildFlavor name. 105 * @param series The string describing the profiling point series (e.g. binder or passthrough). 106 * @param startTime The timestamp indicating the beginning of the summary. 107 */ 108 public ProfilingPointSummaryEntity( 109 Key parentKey, String branch, String buildFlavor, String series, long startTime) { 110 this( 111 parentKey, 112 new StatSummary(null, VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE), 113 new ArrayList<>(), 114 new HashMap<>(), 115 branch, 116 buildFlavor, 117 series, 118 startTime); 119 } 120 121 /** 122 * Create a key for a ProfilingPointSummaryEntity. 123 * 124 * @param parentKey The Key object for the parent TestRunEntity in the database. 125 * @param branch The branch. 126 * @param buildFlavor The device build flavor. 127 * @param series The string describing the profiling point series (e.g. binder or passthrough). 128 * @param startTime The timestamp indicating the beginning of the summary. 129 * @return a Key object for the ProfilingPointSummaryEntity in the database. 130 */ 131 public static Key createKey( 132 Key parentKey, String branch, String buildFlavor, String series, long startTime) { 133 StringBuilder sb = new StringBuilder(); 134 sb.append(branch); 135 sb.append(DELIMITER); 136 sb.append(buildFlavor); 137 sb.append(DELIMITER); 138 sb.append(series); 139 sb.append(DELIMITER); 140 sb.append(startTime); 141 return KeyFactory.createKey(parentKey, KIND, sb.toString()); 142 } 143 144 /** 145 * Updates the profiling summary with the data from a new profiling report. 146 * 147 * @param profilingRun The profiling point run entity object containing profiling data. 148 */ 149 public void update(ProfilingPointRunEntity profilingRun) { 150 if (profilingRun.labels != null 151 && profilingRun.labels.size() == profilingRun.values.size()) { 152 for (int i = 0; i < profilingRun.labels.size(); i++) { 153 String label = profilingRun.labels.get(i); 154 if (!this.labelStats.containsKey(label)) { 155 StatSummary summary = new StatSummary(label, profilingRun.regressionMode); 156 this.labelStats.put(label, summary); 157 } 158 StatSummary summary = this.labelStats.get(label); 159 summary.updateStats(profilingRun.values.get(i)); 160 } 161 this.labels.clear(); 162 this.labels.addAll(profilingRun.labels); 163 } 164 for (long value : profilingRun.values) { 165 this.globalStats.updateStats(value); 166 } 167 } 168 169 @Override 170 public Entity toEntity() { 171 Entity profilingSummary; 172 profilingSummary = new Entity(this.key); 173 profilingSummary.setUnindexedProperty(MEAN, this.globalStats.getMean()); 174 profilingSummary.setUnindexedProperty(SUMSQ, this.globalStats.getSumSq()); 175 profilingSummary.setUnindexedProperty(MIN, this.globalStats.getMin()); 176 profilingSummary.setUnindexedProperty(MAX, this.globalStats.getMax()); 177 profilingSummary.setUnindexedProperty(COUNT, this.globalStats.getCount()); 178 profilingSummary.setIndexedProperty(START_TIME, this.startTime); 179 profilingSummary.setIndexedProperty(BRANCH, this.branch); 180 profilingSummary.setIndexedProperty(BUILD_FLAVOR, this.buildFlavor); 181 profilingSummary.setIndexedProperty(SERIES, this.series); 182 if (this.labels.size() != 0) { 183 List<Double> labelMeans = new ArrayList<>(); 184 List<Double> labelSumsqs = new ArrayList<>(); 185 List<Double> labelMins = new ArrayList<>(); 186 List<Double> labelMaxes = new ArrayList<>(); 187 List<Long> labelCounts = new ArrayList<>(); 188 for (String label : this.labels) { 189 if (!this.labelStats.containsKey(label)) continue; 190 StatSummary labelStat = this.labelStats.get(label); 191 labelMeans.add(labelStat.getMean()); 192 labelSumsqs.add(labelStat.getSumSq()); 193 labelMins.add(labelStat.getMin()); 194 labelMaxes.add(labelStat.getMax()); 195 labelCounts.add(new Long(labelStat.getCount())); 196 } 197 profilingSummary.setUnindexedProperty(LABELS, this.labels); 198 profilingSummary.setUnindexedProperty(LABEL_MEANS, labelMeans); 199 profilingSummary.setUnindexedProperty(LABEL_SUMSQS, labelSumsqs); 200 profilingSummary.setUnindexedProperty(LABEL_MINS, labelMins); 201 profilingSummary.setUnindexedProperty(LABEL_MAXES, labelMaxes); 202 profilingSummary.setUnindexedProperty(LABEL_COUNTS, labelCounts); 203 } 204 205 return profilingSummary; 206 } 207 208 /** 209 * Convert an Entity object to a ProfilingPointSummaryEntity. 210 * 211 * @param e The entity to process. 212 * @return ProfilingPointSummaryEntity object with the properties from e, or null if 213 * incompatible. 214 */ 215 @SuppressWarnings("unchecked") 216 public static ProfilingPointSummaryEntity fromEntity(Entity e) { 217 if (!e.getKind().equals(KIND) 218 || !e.hasProperty(MEAN) 219 || !e.hasProperty(SUMSQ) 220 || !e.hasProperty(MIN) 221 || !e.hasProperty(MAX) 222 || !e.hasProperty(COUNT) 223 || !e.hasProperty(START_TIME) 224 || !e.hasProperty(BRANCH) 225 || !e.hasProperty(BUILD_FLAVOR) 226 || !e.hasProperty(SERIES)) { 227 logger.log( 228 Level.WARNING, "Missing profiling point attributes in entity: " + e.toString()); 229 return null; 230 } 231 try { 232 Key parentKey = e.getParent(); 233 double mean = (double) e.getProperty(MEAN); 234 double sumsq = (double) e.getProperty(SUMSQ); 235 double min = (double) e.getProperty(MIN); 236 double max = (double) e.getProperty(MAX); 237 int count = (int) (long) e.getProperty(COUNT); 238 StatSummary globalStats = 239 new StatSummary( 240 null, 241 min, 242 max, 243 mean, 244 sumsq, 245 count, 246 VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE); 247 Map<String, StatSummary> labelStats = new HashMap<>(); 248 List<String> labels = new ArrayList<>(); 249 if (e.hasProperty(LABELS)) { 250 labels = (List<String>) e.getProperty(LABELS); 251 List<Double> labelMeans = (List<Double>) e.getProperty(LABEL_MEANS); 252 List<Double> labelSumsqs = (List<Double>) e.getProperty(LABEL_SUMSQS); 253 List<Double> labelMins = (List<Double>) e.getProperty(LABEL_MINS); 254 List<Double> labelMaxes = (List<Double>) e.getProperty(LABEL_MAXES); 255 List<Long> labelCounts = (List<Long>) e.getProperty(LABEL_COUNTS); 256 if (labels.size() != labelMeans.size() 257 || labels.size() != labelSumsqs.size() 258 || labels.size() != labelMins.size() 259 || labels.size() != labelMaxes.size() 260 || labels.size() != labelCounts.size()) { 261 logger.log(Level.WARNING, "Jagged label information for entity: " + e.getKey()); 262 return null; 263 } 264 for (int i = 0; i < labels.size(); ++i) { 265 StatSummary labelStat = 266 new StatSummary( 267 labels.get(i), 268 labelMins.get(i), 269 labelMaxes.get(i), 270 labelMeans.get(i), 271 labelSumsqs.get(i), 272 labelCounts.get(i).intValue(), 273 VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE); 274 labelStats.put(labels.get(i), labelStat); 275 } 276 } 277 String branch = (String) e.getProperty(BRANCH); 278 String buildFlavor = (String) e.getProperty(BUILD_FLAVOR); 279 String series = (String) e.getProperty(SERIES); 280 long startTime = (long) e.getProperty(START_TIME); 281 return new ProfilingPointSummaryEntity( 282 parentKey, 283 globalStats, 284 labels, 285 labelStats, 286 branch, 287 buildFlavor, 288 series, 289 startTime); 290 } catch (ClassCastException exception) { 291 // Invalid cast 292 logger.log(Level.WARNING, "Error parsing profiling point summary entity.", exception); 293 } 294 return null; 295 } 296 } 297