Home | History | Annotate | Download | only in servlet
      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
     59     public PageType getNavParentType() {
     60         return PageType.TOT;
     61     }
     62 
     63     @Override
     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      */
     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
    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