• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016 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.servlet;
18 
19 import com.android.vts.entity.TestEntity;
20 import com.android.vts.util.EmailHelper;
21 import com.android.vts.util.PerformanceSummary;
22 import com.android.vts.util.PerformanceUtil;
23 import com.android.vts.util.PerformanceUtil.TimeInterval;
24 import com.android.vts.util.ProfilingPointSummary;
25 import com.android.vts.util.StatSummary;
26 import com.google.appengine.api.datastore.DatastoreService;
27 import com.google.appengine.api.datastore.DatastoreServiceFactory;
28 import com.google.appengine.api.datastore.Entity;
29 import com.google.appengine.api.datastore.Key;
30 import com.google.appengine.api.datastore.Query;
31 import java.io.IOException;
32 import java.math.RoundingMode;
33 import java.text.DecimalFormat;
34 import java.text.SimpleDateFormat;
35 import java.util.ArrayList;
36 import java.util.Date;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Set;
40 import javax.servlet.http.HttpServletRequest;
41 import javax.servlet.http.HttpServletResponse;
42 
43 /** Represents the notifications service which is automatically called on a fixed schedule. */
44 public class VtsPerformanceJobServlet extends BaseServlet {
45     private static final String MEAN = "Mean";
46     private static final String MAX = "Max";
47     private static final String MIN = "Min";
48     private static final String MIN_DELTA = "ΔMin (%)";
49     private static final String MAX_DELTA = "ΔMax (%)";
50     private static final String HIGHER_IS_BETTER =
51             "Note: Higher values are better. Maximum is the best-case performance.";
52     private static final String LOWER_IS_BETTER =
53             "Note: Lower values are better. Minimum is the best-case performance.";
54     private static final String STD = "Std";
55     private static final String SUBJECT_PREFIX = "Daily Performance Digest: ";
56     private static final String LAST_WEEK = "Last Week";
57     private static final String LABEL_STYLE = "font-family: arial";
58     private static final String SUBTEXT_STYLE = "font-family: arial; font-size: 12px";
59     private static final String TABLE_STYLE =
60             "width: 100%; border-collapse: collapse; border: 1px solid black; font-size: 12px; font-family: arial;";
61     private static final String SECTION_LABEL_STYLE =
62             "border: 1px solid black; border-bottom: none; background-color: lightgray;";
63     private static final String COL_LABEL_STYLE =
64             "border: 1px solid black; border-bottom-width: 2px; border-top: 1px dotted gray; background-color: lightgray;";
65     private static final String HEADER_COL_STYLE =
66             "border-top: 1px dotted gray; border-right: 2px solid black; text-align: right; background-color: lightgray;";
67     private static final String INNER_CELL_STYLE =
68             "border-top: 1px dotted gray; border-right: 1px dotted gray; text-align: right;";
69     private static final String OUTER_CELL_STYLE =
70             "border-top: 1px dotted gray; border-right: 2px solid black; text-align: right;";
71 
72     private static final DecimalFormat FORMATTER;
73 
74     /**
75      * Initialize the decimal formatter.
76      */
77     static {
78         FORMATTER = new DecimalFormat("#.##");
79         FORMATTER.setRoundingMode(RoundingMode.HALF_UP);
80     }
81 
82     @Override
getNavbarLinks(HttpServletRequest request)83     public List<String[]> getNavbarLinks(HttpServletRequest request) {
84         return null;
85     }
86 
87     /**
88      * Generates an HTML summary of the performance changes for the profiling results in the
89      * specified
90      * table.
91      *
92      * <p>Retrieves the past 24 hours of profiling data and compares it to the 24 hours that
93      * preceded
94      * it. Creates a table representation of the mean and standard deviation for each profiling
95      * point.
96      * When performance degrades, the cell is shaded red.
97      *
98      * @param testName The name of the test whose profiling data to summarize.
99      * @param perfSummaries List of PerformanceSummary objects for each profiling run (in reverse
100      *     chronological order).
101      * @param labels List of string labels for use as the column headers.
102      * @returns An HTML string containing labeled table summaries.
103      */
getPeformanceSummary( String testName, List<PerformanceSummary> perfSummaries, List<String> labels)104     public static String getPeformanceSummary(
105             String testName, List<PerformanceSummary> perfSummaries, List<String> labels) {
106         if (perfSummaries.size() == 0)
107             return "";
108         PerformanceSummary now = perfSummaries.get(0);
109         String tableHTML = "<p style='" + LABEL_STYLE + "'><b>";
110         tableHTML += testName + "</b></p>";
111         for (String profilingPoint : now.getProfilingPointNames()) {
112             ProfilingPointSummary summary = now.getProfilingPointSummary(profilingPoint);
113             tableHTML += "<table cellpadding='2' style='" + TABLE_STYLE + "'>";
114 
115             // Format header rows
116             String[] headerRows = new String[] {profilingPoint, summary.yLabel};
117             int colspan = labels.size() * 4;
118             for (String content : headerRows) {
119                 tableHTML += "<tr><td colspan='" + colspan + "'>" + content + "</td></tr>";
120             }
121 
122             // Format section labels
123             tableHTML += "<tr>";
124             for (int i = 0; i < labels.size(); i++) {
125                 String content = labels.get(i);
126                 tableHTML += "<th style='" + SECTION_LABEL_STYLE + "' ";
127                 if (i == 0)
128                     tableHTML += "colspan='1'";
129                 else if (i == 1)
130                     tableHTML += "colspan='3'";
131                 else
132                     tableHTML += "colspan='4'";
133                 tableHTML += ">" + content + "</th>";
134             }
135             tableHTML += "</tr>";
136 
137             String deltaString;
138             String bestCaseString;
139             String subtext;
140             switch (now.getProfilingPointSummary(profilingPoint).getRegressionMode()) {
141                 case VTS_REGRESSION_MODE_DECREASING:
142                     deltaString = MAX_DELTA;
143                     bestCaseString = MAX;
144                     subtext = HIGHER_IS_BETTER;
145                     break;
146                 default:
147                     deltaString = MIN_DELTA;
148                     bestCaseString = MIN;
149                     subtext = LOWER_IS_BETTER;
150                     break;
151             }
152 
153             // Format column labels
154             tableHTML += "<tr>";
155             for (int i = 0; i < labels.size(); i++) {
156                 if (i > 1) {
157                     tableHTML += "<th style='" + COL_LABEL_STYLE + "'>" + deltaString + "</th>";
158                 }
159                 if (i == 0) {
160                     tableHTML += "<th style='" + COL_LABEL_STYLE + "'>";
161                     tableHTML += summary.xLabel + "</th>";
162                 } else if (i > 0) {
163                     tableHTML += "<th style='" + COL_LABEL_STYLE + "'>" + bestCaseString + "</th>";
164                     tableHTML += "<th style='" + COL_LABEL_STYLE + "'>" + MEAN + "</th>";
165                     tableHTML += "<th style='" + COL_LABEL_STYLE + "'>" + STD + "</th>";
166                 }
167             }
168             tableHTML += "</tr>";
169 
170             // Populate data cells
171             for (StatSummary stats : summary) {
172                 String label = stats.getLabel();
173                 tableHTML += "<tr><td style='" + HEADER_COL_STYLE + "'>" + label;
174                 tableHTML += "</td><td style='" + INNER_CELL_STYLE + "'>";
175                 tableHTML += FORMATTER.format(stats.getBestCase()) + "</td>";
176                 tableHTML += "<td style='" + INNER_CELL_STYLE + "'>";
177                 tableHTML +=  FORMATTER.format(stats.getMean()) + "</td>";
178                 tableHTML += "<td style='" + OUTER_CELL_STYLE + "'>";
179                 tableHTML +=  FORMATTER.format(stats.getStd()) + "</td>";
180                 for (int i = 1; i < perfSummaries.size(); i++) {
181                     PerformanceSummary oldPerfSummary = perfSummaries.get(i);
182                     if (oldPerfSummary.hasProfilingPoint(profilingPoint)) {
183                         StatSummary baseline =
184                                 oldPerfSummary.getProfilingPointSummary(profilingPoint)
185                                         .getStatSummary(label);
186                         tableHTML += PerformanceUtil.getBestCasePerformanceComparisonHTML(
187                                 baseline, stats, "", "", INNER_CELL_STYLE, OUTER_CELL_STYLE);
188                     } else
189                         tableHTML += "<td></td><td></td><td></td><td></td>";
190                 }
191                 tableHTML += "</tr>";
192             }
193             tableHTML += "</table>";
194             tableHTML += "<i style='" + SUBTEXT_STYLE + "'>" + subtext + "</i><br><br>";
195         }
196         return tableHTML;
197     }
198 
199     @Override
doGet(HttpServletRequest request, HttpServletResponse response)200     public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
201         doGetHandler(request, response);
202     }
203 
204     @Override
doGetHandler(HttpServletRequest request, HttpServletResponse response)205     public void doGetHandler(HttpServletRequest request, HttpServletResponse response)
206             throws IOException {
207         DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
208         Set<Key> allTestKeys = new HashSet<>();
209 
210         Query q = new Query(TestEntity.KIND).setKeysOnly();
211         for (Entity test : datastore.prepare(q).asIterable()) {
212             if (test.getKey().getName() == null) {
213                 continue;
214             }
215             allTestKeys.add(test.getKey());
216         }
217 
218         // Add today to the list of time intervals to analyze
219         List<TimeInterval> timeIntervals = new ArrayList<>();
220         long now = System.currentTimeMillis();
221         String dateString = new SimpleDateFormat("MM-dd-yyyy").format(new Date(now));
222         TimeInterval today = new TimeInterval(now - ONE_DAY / MILLI_TO_MICRO, now, dateString);
223         timeIntervals.add(today);
224 
225         // Add yesterday as a baseline time interval for analysis
226         long oneDayAgo = now - ONE_DAY / MILLI_TO_MICRO;
227         String dateStringYesterday = new SimpleDateFormat("MM-dd-yyyy").format(new Date(oneDayAgo));
228         TimeInterval yesterday = new TimeInterval(
229                 oneDayAgo - ONE_DAY / MILLI_TO_MICRO, oneDayAgo, dateStringYesterday);
230         timeIntervals.add(yesterday);
231 
232         // Add last week as a baseline time interval for analysis
233         long oneWeek = 7 * ONE_DAY / MILLI_TO_MICRO;
234         long oneWeekAgo = now - oneWeek;
235         TimeInterval lastWeek = new TimeInterval(oneWeekAgo - oneWeek, oneWeekAgo, LAST_WEEK);
236         timeIntervals.add(lastWeek);
237 
238         for (Key testKey : allTestKeys) {
239             List<PerformanceSummary> perfSummaries = new ArrayList<>();
240             List<String> labels = new ArrayList<>();
241             labels.add("");
242             for (TimeInterval interval : timeIntervals) {
243                 PerformanceSummary perfSummary = new PerformanceSummary();
244                 PerformanceUtil.updatePerformanceSummary(
245                         testKey.getName(), interval.start, interval.end, null, perfSummary);
246                 if (perfSummary.size() == 0) {
247                     continue;
248                 }
249                 perfSummaries.add(perfSummary);
250                 labels.add(interval.label);
251             }
252             String body = getPeformanceSummary(testKey.getName(), perfSummaries, labels);
253             if (body == null || body.equals("")) {
254                 continue;
255             }
256             List<String> emails = EmailHelper.getSubscriberEmails(testKey);
257             if (emails.size() == 0) {
258                 continue;
259             }
260             String subject = SUBJECT_PREFIX + testKey.getName();
261             EmailHelper.send(emails, subject, body);
262         }
263     }
264 }
265