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.util.DatastoreHelper; 20 import com.android.vts.util.PerformanceSummary; 21 import com.android.vts.util.PerformanceUtil; 22 import com.android.vts.util.ProfilingPointSummary; 23 import com.android.vts.util.StatSummary; 24 import java.io.IOException; 25 import java.math.RoundingMode; 26 import java.text.DecimalFormat; 27 import java.util.ArrayList; 28 import java.util.List; 29 import java.util.concurrent.TimeUnit; 30 import java.util.logging.Level; 31 import javax.servlet.RequestDispatcher; 32 import javax.servlet.ServletException; 33 import javax.servlet.http.HttpServletRequest; 34 import javax.servlet.http.HttpServletResponse; 35 36 /** Servlet for producing tabular performance summaries. */ 37 public class ShowPerformanceDigestServlet extends BaseServlet { 38 private static final String PERF_DIGEST_JSP = "WEB-INF/jsp/show_performance_digest.jsp"; 39 40 private static final String MEAN = "Mean"; 41 private static final String MIN = "Min"; 42 private static final String MAX = "Max"; 43 private static final String MEAN_DELTA = "ΔMean (%)"; 44 private static final String HIGHER_IS_BETTER = 45 "Note: Higher values are better. Maximum is the best-case performance."; 46 private static final String LOWER_IS_BETTER = 47 "Note: Lower values are better. Minimum is the best-case performance."; 48 private static final String STD = "Std"; 49 50 private static final DecimalFormat FORMATTER; 51 52 /** Initialize the decimal formatter. */ 53 static { 54 FORMATTER = new DecimalFormat("#.##"); 55 FORMATTER.setRoundingMode(RoundingMode.HALF_UP); 56 } 57 58 @Override getNavParentType()59 public PageType getNavParentType() { 60 return PageType.TOT; 61 } 62 63 @Override getBreadcrumbLinks(HttpServletRequest request)64 public List<Page> getBreadcrumbLinks(HttpServletRequest request) { 65 List<Page> links = new ArrayList<>(); 66 String testName = request.getParameter("testName"); 67 links.add(new Page(PageType.TABLE, testName, "?testName=" + testName)); 68 links.add(new Page(PageType.PERFORMANCE_DIGEST, "?testName=" + testName)); 69 return links; 70 } 71 72 /** 73 * Generates an HTML summary of the performance changes for the profiling results in the 74 * specified table. 75 * 76 * <p>Retrieves the past 24 hours of profiling data and compares it to the 24 hours that 77 * preceded it. Creates a table representation of the mean and standard deviation for each 78 * profiling point. When performance degrades, the cell is shaded red. 79 * 80 * @param profilingPoint The name of the profiling point to summarize. 81 * @param testSummary The ProfilingPointSummary object to compare against. 82 * @param perfSummaries List of PerformanceSummary objects for each profiling run (in reverse 83 * chronological order). 84 * @param sectionLabels HTML string for the section labels (i.e. for each time interval). 85 * @returns An HTML string for a table comparing the profiling point results across time 86 * intervals. 87 */ getPerformanceSummary( String profilingPoint, ProfilingPointSummary testSummary, List<PerformanceSummary> perfSummaries, String sectionLabels)88 public static String getPerformanceSummary( 89 String profilingPoint, 90 ProfilingPointSummary testSummary, 91 List<PerformanceSummary> perfSummaries, 92 String sectionLabels) { 93 String tableHTML = "<table>"; 94 95 // Format section labels 96 tableHTML += "<tr>"; 97 tableHTML += "<th class='section-label grey lighten-2'>"; 98 tableHTML += testSummary.yLabel + "</th>"; 99 tableHTML += sectionLabels; 100 tableHTML += "</tr>"; 101 102 String bestCaseString; 103 switch (testSummary.getRegressionMode()) { 104 case VTS_REGRESSION_MODE_DECREASING: 105 bestCaseString = MAX; 106 break; 107 default: 108 bestCaseString = MIN; 109 break; 110 } 111 112 // Format column labels 113 tableHTML += "<tr>"; 114 for (int i = 0; i <= perfSummaries.size() + 1; i++) { 115 if (i > 1) { 116 tableHTML += "<th class='section-label grey lighten-2'>" + MEAN_DELTA + "</th>"; 117 } 118 if (i == 0) { 119 tableHTML += "<th class='section-label grey lighten-2'>"; 120 tableHTML += testSummary.xLabel + "</th>"; 121 } else if (i > 0) { 122 tableHTML += "<th class='section-label grey lighten-2'>" + bestCaseString + "</th>"; 123 tableHTML += "<th class='section-label grey lighten-2'>" + MEAN + "</th>"; 124 tableHTML += "<th class='section-label grey lighten-2'>" + STD + "</th>"; 125 } 126 } 127 tableHTML += "</tr>"; 128 129 // Populate data cells 130 for (StatSummary stats : testSummary) { 131 String label = stats.getLabel(); 132 tableHTML += "<tr><td class='axis-label grey lighten-2'>" + label; 133 tableHTML += "</td><td class='cell inner-cell'>"; 134 tableHTML += FORMATTER.format(stats.getBestCase()) + "</td>"; 135 tableHTML += "<td class='cell inner-cell'>"; 136 tableHTML += FORMATTER.format(stats.getMean()) + "</td>"; 137 tableHTML += "<td class='cell outer-cell'>"; 138 if (stats.getCount() < 2) { 139 tableHTML += " - </td>"; 140 } else { 141 tableHTML += FORMATTER.format(stats.getStd()) + "</td>"; 142 } 143 for (PerformanceSummary prevPerformance : perfSummaries) { 144 if (prevPerformance.hasProfilingPoint(profilingPoint)) { 145 StatSummary baseline = 146 prevPerformance 147 .getProfilingPointSummary(profilingPoint) 148 .getStatSummary(label); 149 tableHTML += 150 PerformanceUtil.getAvgCasePerformanceComparisonHTML( 151 baseline, stats, "cell inner-cell", "cell outer-cell", "", ""); 152 } else { 153 tableHTML += "<td></td><td></td><td></td><td></td>"; 154 } 155 } 156 tableHTML += "</tr>"; 157 } 158 tableHTML += "</table>"; 159 return tableHTML; 160 } 161 162 @Override doGetHandler(HttpServletRequest request, HttpServletResponse response)163 public void doGetHandler(HttpServletRequest request, HttpServletResponse response) 164 throws IOException { 165 RequestDispatcher dispatcher = null; 166 String testName = request.getParameter("testName"); 167 String selectedDevice = request.getParameter("device"); 168 Long startTime = null; 169 if (request.getParameter("startTime") != null) { 170 String time = request.getParameter("startTime"); 171 try { 172 startTime = Long.parseLong(time); 173 } catch (NumberFormatException e) { 174 logger.log(Level.WARNING, "Invalid start time passed to digest servlet: " + time); 175 } 176 } 177 if (startTime == null) { 178 startTime = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); 179 } 180 181 // Add today to the list of time intervals to analyze 182 List<PerformanceSummary> summaries = new ArrayList<>(); 183 PerformanceSummary today = 184 new PerformanceSummary(startTime - TimeUnit.DAYS.toMicros(1), startTime); 185 summaries.add(today); 186 187 // Add yesterday as a baseline time interval for analysis 188 long oneDayAgo = startTime - TimeUnit.DAYS.toMicros(1); 189 PerformanceSummary yesterday = 190 new PerformanceSummary(oneDayAgo - TimeUnit.DAYS.toMicros(1), oneDayAgo); 191 summaries.add(yesterday); 192 193 // Add last week as a baseline time interval for analysis 194 long oneWeek = TimeUnit.DAYS.toMicros(7); 195 long oneWeekAgo = startTime - oneWeek; 196 String spanString = "<span class='date-label'>"; 197 String label = 198 spanString + TimeUnit.MICROSECONDS.toMillis(oneWeekAgo - oneWeek) + "</span>"; 199 label += " - " + spanString + TimeUnit.MICROSECONDS.toMillis(oneWeekAgo) + "</span>"; 200 PerformanceSummary lastWeek = 201 new PerformanceSummary(oneWeekAgo - oneWeek, oneWeekAgo, label); 202 summaries.add(lastWeek); 203 PerformanceUtil.updatePerformanceSummary( 204 testName, oneWeekAgo - oneWeek, startTime, selectedDevice, summaries); 205 206 int colCount = 0; 207 String sectionLabels = ""; 208 List<PerformanceSummary> nonEmptySummaries = new ArrayList<>(); 209 for (PerformanceSummary perfSummary : summaries) { 210 if (perfSummary.size() == 0) continue; 211 212 nonEmptySummaries.add(perfSummary); 213 String content = perfSummary.label; 214 sectionLabels += "<th class='section-label grey lighten-2 center' "; 215 if (colCount++ == 0) sectionLabels += "colspan='3'"; 216 else sectionLabels += "colspan='4'"; 217 sectionLabels += ">" + content + "</th>"; 218 } 219 220 List<String> tables = new ArrayList<>(); 221 List<String> tableTitles = new ArrayList<>(); 222 List<String> tableSubtitles = new ArrayList<>(); 223 if (nonEmptySummaries.size() != 0) { 224 PerformanceSummary todayPerformance = nonEmptySummaries.remove(0); 225 String[] profilingNames = todayPerformance.getProfilingPointNames(); 226 227 for (String profilingPointName : profilingNames) { 228 ProfilingPointSummary baselinePerformance = 229 todayPerformance.getProfilingPointSummary(profilingPointName); 230 String table = 231 getPerformanceSummary( 232 profilingPointName, 233 baselinePerformance, 234 nonEmptySummaries, 235 sectionLabels); 236 if (table != null) { 237 tables.add(table); 238 tableTitles.add(profilingPointName); 239 switch (baselinePerformance.getRegressionMode()) { 240 case VTS_REGRESSION_MODE_DECREASING: 241 tableSubtitles.add(HIGHER_IS_BETTER); 242 break; 243 default: 244 tableSubtitles.add(LOWER_IS_BETTER); 245 break; 246 } 247 } 248 } 249 } 250 251 request.setAttribute("testName", testName); 252 request.setAttribute("tables", tables); 253 request.setAttribute("tableTitles", tableTitles); 254 request.setAttribute("tableSubtitles", tableSubtitles); 255 request.setAttribute("startTime", Long.toString(startTime)); 256 request.setAttribute("selectedDevice", selectedDevice); 257 request.setAttribute("devices", DatastoreHelper.getAllBuildFlavors()); 258 259 dispatcher = request.getRequestDispatcher(PERF_DIGEST_JSP); 260 try { 261 dispatcher.forward(request, response); 262 } catch (ServletException e) { 263 logger.log(Level.SEVERE, "Servlet Exception caught : " + e.toString()); 264 } 265 } 266 } 267