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 com.googlecode.objectify.annotation.Cache;
25 import com.googlecode.objectify.annotation.Id;
26 import com.googlecode.objectify.annotation.Ignore;
27 import com.googlecode.objectify.annotation.Index;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.logging.Level;
33 import java.util.logging.Logger;
34 import lombok.Data;
35 import lombok.NoArgsConstructor;
36 
37 import static com.googlecode.objectify.ObjectifyService.ofy;
38 
39 @com.googlecode.objectify.annotation.Entity(name = "ProfilingPointSummary")
40 @Cache
41 @Data
42 @NoArgsConstructor
43 /** Entity describing a profiling point summary. */
44 public class ProfilingPointSummaryEntity implements DashboardEntity {
45     protected static final Logger logger =
46             Logger.getLogger(ProfilingPointSummaryEntity.class.getName());
47     protected static final String DELIMITER = "#";
48 
49     public static final String KIND = "ProfilingPointSummary";
50     public static final String ALL = "ALL";
51 
52     // Property keys
53     public static final String START_TIME = "startTime";
54     public static final String MEAN = "mean";
55     public static final String SUMSQ = "sumSq";
56     public static final String MIN = "min";
57     public static final String MAX = "max";
58     public static final String LABELS = "labels";
59     public static final String LABEL_MEANS = "labelMeans";
60     public static final String LABEL_SUMSQS = "labelSumSqs";
61     public static final String LABEL_MINS = "labelMins";
62     public static final String LABEL_MAXES = "labelMaxes";
63     public static final String LABEL_COUNTS = "labelCounts";
64     public static final String COUNT = "count";
65     public static final String BRANCH = "branch";
66     public static final String BUILD_FLAVOR = "buildFlavor";
67     public static final String SERIES = "series";
68 
69     @Ignore
70     private Key key;
71 
72     /** ID field */
73     @Id private String name;
74 
75     /** branch field */
76     @Index private String branch;
77 
78     /** build field */
79     @Index private String buildFlavor;
80 
81     /** total count */
82     @Index private int count;
83 
84     /** For each label count field */
85     @Index private List<String> labelCounts;
86 
87     /** Maximum value for each label */
88     private List<Integer> labelMaxes;
89 
90     /** Mean value for each label */
91     private List<Integer> labelMeans;
92 
93     /** Minimum value for each label */
94     private List<Integer> labelMins;
95 
96     /** Label name for each label point */
97     private List<String> labels;
98 
99     /** Summation for sequence for each label */
100     private List<Integer> labelSumSqs;
101 
102     /** Maximum value for total */
103     private Long max;
104 
105     /** Mean value for total */
106     private Long mean;
107 
108     /** Minimum value for total */
109     private Long min;
110 
111     /** The list of series */
112     private String series;
113 
114     /** The start time field of the test */
115     private Long startTime;
116 
117     /** The summation of sequences */
118     private Long sumSq;
119 
120     @Ignore private StatSummary globalStats;
121 
122     @Ignore private Map<String, StatSummary> labelStats;
123 
124     /*
125     public final StatSummary globalStats;
126     public final List<String> labels;
127     public final Map<String, StatSummary> labelStats;
128     public final String branch;
129     public final String buildFlavor;
130     public final String series;
131     public final long startTime;
132     */
133 
134     /**
135      * Create a ProfilingPointSummaryEntity object.
136      *
137      * @param parentKey The Key object for the parent TestRunEntity in the database.
138      * @param globalStats The StatSummary object recording global statistics about the profiling
139      *     point.
140      * @param labels The list of data labels.
141      * @param labelStats The map from data label to StatSummary object for the label.
142      * @param branch The branch.
143      * @param buildFlavor The device build flavor.
144      * @param series The string describing the profiling point series (e.g. binder or passthrough).
145      * @param startTime The timestamp indicating the beginning of the summary.
146      */
ProfilingPointSummaryEntity( Key parentKey, StatSummary globalStats, List<String> labels, Map<String, StatSummary> labelStats, String branch, String buildFlavor, String series, long startTime)147     public ProfilingPointSummaryEntity(
148             Key parentKey,
149             StatSummary globalStats,
150             List<String> labels,
151             Map<String, StatSummary> labelStats,
152             String branch,
153             String buildFlavor,
154             String series,
155             long startTime) {
156         this.globalStats = globalStats;
157         this.labels = labels;
158         this.labelStats = labelStats;
159         this.buildFlavor = buildFlavor == null ? ALL : buildFlavor;
160         this.branch = branch == null ? ALL : branch;
161         this.series = series == null ? "" : series;
162         this.startTime = startTime;
163         this.key = createKey(parentKey, this.branch, this.buildFlavor, this.series, this.startTime);
164     }
165 
166     /**
167      * Create a new ProfilingPointSummaryEntity object.
168      *
169      * @param parentKey The Key object for the parent TestRunEntity in the database.
170      * @param branch The branch.
171      * @param buildFlavor The buildFlavor name.
172      * @param series The string describing the profiling point series (e.g. binder or passthrough).
173      * @param startTime The timestamp indicating the beginning of the summary.
174      */
ProfilingPointSummaryEntity( Key parentKey, String branch, String buildFlavor, String series, long startTime)175     public ProfilingPointSummaryEntity(
176             Key parentKey, String branch, String buildFlavor, String series, long startTime) {
177         this(
178                 parentKey,
179                 new StatSummary(null, VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE),
180                 new ArrayList<>(),
181                 new HashMap<>(),
182                 branch,
183                 buildFlavor,
184                 series,
185                 startTime);
186     }
187 
188     /**
189      * Create a key for a ProfilingPointSummaryEntity.
190      *
191      * @param parentKey The Key object for the parent TestRunEntity in the database.
192      * @param branch The branch.
193      * @param buildFlavor The device build flavor.
194      * @param series The string describing the profiling point series (e.g. binder or passthrough).
195      * @param startTime The timestamp indicating the beginning of the summary.
196      * @return a Key object for the ProfilingPointSummaryEntity in the database.
197      */
createKey( Key parentKey, String branch, String buildFlavor, String series, long startTime)198     public static Key createKey(
199             Key parentKey, String branch, String buildFlavor, String series, long startTime) {
200         StringBuilder sb = new StringBuilder();
201         sb.append(branch);
202         sb.append(DELIMITER);
203         sb.append(buildFlavor);
204         sb.append(DELIMITER);
205         sb.append(series);
206         sb.append(DELIMITER);
207         sb.append(startTime);
208         return KeyFactory.createKey(parentKey, KIND, sb.toString());
209     }
210 
211     /**
212      * Updates the profiling summary with the data from a new profiling report.
213      *
214      * @param profilingRun The profiling point run entity object containing profiling data.
215      */
update(ProfilingPointRunEntity profilingRun)216     public void update(ProfilingPointRunEntity profilingRun) {
217         if (profilingRun.getLabels() != null
218                 && profilingRun.getLabels().size() == profilingRun.getValues().size()) {
219             for (int i = 0; i < profilingRun.getLabels().size(); i++) {
220                 String label = profilingRun.getLabels().get(i);
221                 if (!this.labelStats.containsKey(label)) {
222                     VtsProfilingRegressionMode vtsProfilingRegressionMode =
223                             profilingRun.getVtsProfilingRegressionMode(
224                                     profilingRun.getRegressionMode());
225                     StatSummary summary = new StatSummary(label, vtsProfilingRegressionMode);
226                     this.labelStats.put(label, summary);
227                 }
228                 StatSummary summary = this.labelStats.get(label);
229                 summary.updateStats(profilingRun.getValues().get(i));
230             }
231             this.labels.clear();
232             this.labels.addAll(profilingRun.getLabels());
233         }
234         for (long value : profilingRun.getValues()) {
235             this.globalStats.updateStats(value);
236         }
237     }
238 
239     /** Saving function for the instance of this class */
240     @Override
save()241     public com.googlecode.objectify.Key<ProfilingPointSummaryEntity> save() {
242         return ofy().save().entity(this).now();
243     }
244 
toEntity()245     public Entity toEntity() {
246         Entity profilingSummary;
247         profilingSummary = new Entity(this.key);
248         profilingSummary.setUnindexedProperty(MEAN, this.globalStats.getMean());
249         profilingSummary.setUnindexedProperty(SUMSQ, this.globalStats.getSumSq());
250         profilingSummary.setUnindexedProperty(MIN, this.globalStats.getMin());
251         profilingSummary.setUnindexedProperty(MAX, this.globalStats.getMax());
252         profilingSummary.setUnindexedProperty(COUNT, this.globalStats.getCount());
253         profilingSummary.setIndexedProperty(START_TIME, this.startTime);
254         profilingSummary.setIndexedProperty(BRANCH, this.branch);
255         profilingSummary.setIndexedProperty(BUILD_FLAVOR, this.buildFlavor);
256         profilingSummary.setIndexedProperty(SERIES, this.series);
257         if (this.labels.size() != 0) {
258             List<Double> labelMeans = new ArrayList<>();
259             List<Double> labelSumsqs = new ArrayList<>();
260             List<Double> labelMins = new ArrayList<>();
261             List<Double> labelMaxes = new ArrayList<>();
262             List<Long> labelCounts = new ArrayList<>();
263             for (String label : this.labels) {
264                 if (!this.labelStats.containsKey(label)) continue;
265                 StatSummary labelStat = this.labelStats.get(label);
266                 labelMeans.add(labelStat.getMean());
267                 labelSumsqs.add(labelStat.getSumSq());
268                 labelMins.add(labelStat.getMin());
269                 labelMaxes.add(labelStat.getMax());
270                 labelCounts.add(new Long(labelStat.getCount()));
271             }
272             profilingSummary.setUnindexedProperty(LABELS, this.labels);
273             profilingSummary.setUnindexedProperty(LABEL_MEANS, labelMeans);
274             profilingSummary.setUnindexedProperty(LABEL_SUMSQS, labelSumsqs);
275             profilingSummary.setUnindexedProperty(LABEL_MINS, labelMins);
276             profilingSummary.setUnindexedProperty(LABEL_MAXES, labelMaxes);
277             profilingSummary.setUnindexedProperty(LABEL_COUNTS, labelCounts);
278         }
279 
280         return profilingSummary;
281     }
282 
283     /**
284      * Convert an Entity object to a ProfilingPointSummaryEntity.
285      *
286      * @param e The entity to process.
287      * @return ProfilingPointSummaryEntity object with the properties from e, or null if
288      *     incompatible.
289      */
290     @SuppressWarnings("unchecked")
fromEntity(Entity e)291     public static ProfilingPointSummaryEntity fromEntity(Entity e) {
292         if (!e.getKind().equals(KIND)
293                 || !e.hasProperty(MEAN)
294                 || !e.hasProperty(SUMSQ)
295                 || !e.hasProperty(MIN)
296                 || !e.hasProperty(MAX)
297                 || !e.hasProperty(COUNT)
298                 || !e.hasProperty(START_TIME)
299                 || !e.hasProperty(BRANCH)
300                 || !e.hasProperty(BUILD_FLAVOR)
301                 || !e.hasProperty(SERIES)) {
302             logger.log(
303                     Level.WARNING, "Missing profiling point attributes in entity: " + e.toString());
304             return null;
305         }
306         try {
307             Key parentKey = e.getParent();
308             double mean = (double) e.getProperty(MEAN);
309             double sumsq = (double) e.getProperty(SUMSQ);
310             double min = (double) e.getProperty(MIN);
311             double max = (double) e.getProperty(MAX);
312             int count = (int) (long) e.getProperty(COUNT);
313             StatSummary globalStats =
314                     new StatSummary(
315                             null,
316                             min,
317                             max,
318                             mean,
319                             sumsq,
320                             count,
321                             VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE);
322             Map<String, StatSummary> labelStats = new HashMap<>();
323             List<String> labels = new ArrayList<>();
324             if (e.hasProperty(LABELS)) {
325                 labels = (List<String>) e.getProperty(LABELS);
326                 List<Double> labelMeans = (List<Double>) e.getProperty(LABEL_MEANS);
327                 List<Double> labelSumsqs = (List<Double>) e.getProperty(LABEL_SUMSQS);
328                 List<Double> labelMins = (List<Double>) e.getProperty(LABEL_MINS);
329                 List<Double> labelMaxes = (List<Double>) e.getProperty(LABEL_MAXES);
330                 List<Long> labelCounts = (List<Long>) e.getProperty(LABEL_COUNTS);
331                 if (labels.size() != labelMeans.size()
332                         || labels.size() != labelSumsqs.size()
333                         || labels.size() != labelMins.size()
334                         || labels.size() != labelMaxes.size()
335                         || labels.size() != labelCounts.size()) {
336                     logger.log(Level.WARNING, "Jagged label information for entity: " + e.getKey());
337                     return null;
338                 }
339                 for (int i = 0; i < labels.size(); ++i) {
340                     StatSummary labelStat =
341                             new StatSummary(
342                                     labels.get(i),
343                                     labelMins.get(i),
344                                     labelMaxes.get(i),
345                                     labelMeans.get(i),
346                                     labelSumsqs.get(i),
347                                     labelCounts.get(i).intValue(),
348                                     VtsProfilingRegressionMode.UNKNOWN_REGRESSION_MODE);
349                     labelStats.put(labels.get(i), labelStat);
350                 }
351             }
352             String branch = (String) e.getProperty(BRANCH);
353             String buildFlavor = (String) e.getProperty(BUILD_FLAVOR);
354             String series = (String) e.getProperty(SERIES);
355             long startTime = (long) e.getProperty(START_TIME);
356             return new ProfilingPointSummaryEntity(
357                     parentKey,
358                     globalStats,
359                     labels,
360                     labelStats,
361                     branch,
362                     buildFlavor,
363                     series,
364                     startTime);
365         } catch (ClassCastException exception) {
366             // Invalid cast
367             logger.log(Level.WARNING, "Error parsing profiling point summary entity.", exception);
368         }
369         return null;
370     }
371 }
372