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.servlet;
18 
19 import com.android.vts.entity.ProfilingPointEntity;
20 import com.android.vts.entity.ProfilingPointSummaryEntity;
21 import com.android.vts.util.BoxPlot;
22 import com.android.vts.util.DatastoreHelper;
23 import com.android.vts.util.FilterUtil;
24 import com.android.vts.util.GraphSerializer;
25 import com.google.appengine.api.datastore.DatastoreService;
26 import com.google.appengine.api.datastore.DatastoreServiceFactory;
27 import com.google.appengine.api.datastore.Entity;
28 import com.google.appengine.api.datastore.Query;
29 import com.google.appengine.api.datastore.Query.Filter;
30 import com.google.gson.Gson;
31 import com.google.gson.GsonBuilder;
32 import java.io.IOException;
33 import java.util.ArrayList;
34 import java.util.Comparator;
35 import java.util.HashMap;
36 import java.util.HashSet;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.concurrent.TimeUnit;
41 import java.util.logging.Level;
42 import java.util.stream.Collectors;
43 import javax.servlet.RequestDispatcher;
44 import javax.servlet.ServletException;
45 import javax.servlet.http.HttpServletRequest;
46 import javax.servlet.http.HttpServletResponse;
47 
48 /** Servlet for handling requests to load graphs. */
49 public class ShowProfilingOverviewServlet extends BaseServlet {
50     private static final String PROFILING_OVERVIEW_JSP = "WEB-INF/jsp/show_profiling_overview.jsp";
51 
52     @Override
getNavParentType()53     public PageType getNavParentType() {
54         return PageType.PROFILING_LIST;
55     }
56 
57     @Override
getBreadcrumbLinks(HttpServletRequest request)58     public List<Page> getBreadcrumbLinks(HttpServletRequest request) {
59         List<Page> links = new ArrayList<>();
60         String testName = request.getParameter("testName");
61         links.add(new Page(PageType.PROFILING_OVERVIEW, testName, "?testName=" + testName));
62         return links;
63     }
64 
65     @Override
doGetHandler(HttpServletRequest request, HttpServletResponse response)66     public void doGetHandler(HttpServletRequest request, HttpServletResponse response)
67             throws IOException {
68         RequestDispatcher dispatcher = null;
69         DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
70 
71         String testName = request.getParameter("testName");
72         long endTime = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
73         long startTime = endTime - TimeUnit.DAYS.toMicros(30);
74 
75         // Create a query for test runs matching the time window filter
76 
77         Map<String, String[]> parameterMap = request.getParameterMap();
78         boolean hasBranchFilter = parameterMap.containsKey(FilterUtil.FilterKey.BRANCH.getValue());
79         Filter deviceFilter;
80         if (hasBranchFilter) {
81             deviceFilter =
82                     FilterUtil.FilterKey.BRANCH.getFilterForString(
83                             FilterUtil.getFirstParameter(
84                                     parameterMap, FilterUtil.FilterKey.BRANCH.getValue()));
85         } else {
86             deviceFilter =
87                     FilterUtil.FilterKey.BRANCH.getFilterForString(ProfilingPointSummaryEntity.ALL);
88         }
89 
90         boolean hasTargetFilter = parameterMap.containsKey(FilterUtil.FilterKey.TARGET.getValue());
91         if (hasTargetFilter) {
92             deviceFilter =
93                     Query.CompositeFilterOperator.and(
94                             deviceFilter,
95                             FilterUtil.FilterKey.TARGET.getFilterForString(
96                                     FilterUtil.getFirstParameter(
97                                             parameterMap, FilterUtil.FilterKey.TARGET.getValue())));
98         } else {
99             deviceFilter =
100                     Query.CompositeFilterOperator.and(
101                             deviceFilter,
102                             FilterUtil.FilterKey.TARGET.getFilterForString(
103                                     ProfilingPointSummaryEntity.ALL));
104         }
105 
106         Filter startFilter =
107                 new Query.FilterPredicate(
108                         ProfilingPointSummaryEntity.START_TIME,
109                         Query.FilterOperator.GREATER_THAN_OR_EQUAL,
110                         startTime);
111         Filter endFilter =
112                 new Query.FilterPredicate(
113                         ProfilingPointSummaryEntity.START_TIME,
114                         Query.FilterOperator.LESS_THAN_OR_EQUAL,
115                         endTime);
116         Filter timeFilter = Query.CompositeFilterOperator.and(startFilter, endFilter);
117 
118         Filter filter = Query.CompositeFilterOperator.and(timeFilter, deviceFilter);
119 
120         Query profilingPointQuery =
121                 new Query(ProfilingPointEntity.KIND)
122                         .setFilter(
123                                 new Query.FilterPredicate(
124                                         ProfilingPointEntity.TEST_NAME,
125                                         Query.FilterOperator.EQUAL,
126                                         testName));
127 
128         List<ProfilingPointEntity> profilingPoints = new ArrayList<>();
129         for (Entity e :
130                 datastore
131                         .prepare(profilingPointQuery)
132                         .asIterable(DatastoreHelper.getLargeBatchOptions())) {
133             ProfilingPointEntity pp = ProfilingPointEntity.fromEntity(e);
134             if (pp == null) continue;
135             profilingPoints.add(pp);
136         }
137 
138         Map<ProfilingPointEntity, Iterable<Entity>> asyncEntities = new HashMap<>();
139         for (ProfilingPointEntity pp : profilingPoints) {
140             Query profilingQuery =
141                     new Query(ProfilingPointSummaryEntity.KIND)
142                             .setAncestor(pp.getKey())
143                             .setFilter(filter);
144             asyncEntities.put(
145                     pp,
146                     datastore
147                             .prepare(profilingQuery)
148                             .asIterable(DatastoreHelper.getLargeBatchOptions()));
149         }
150 
151         Map<String, BoxPlot> plotMap = new HashMap<>();
152         for (ProfilingPointEntity pp : profilingPoints) {
153             if (!plotMap.containsKey(pp.getProfilingPointName())) {
154                 plotMap.put(
155                         pp.getProfilingPointName(), new BoxPlot(pp.getProfilingPointName(), null, pp.getXLabel()));
156             }
157             BoxPlot plot = plotMap.get(pp.getProfilingPointName());
158             Set<Long> timestamps = new HashSet<>();
159             for (Entity e : asyncEntities.get(pp)) {
160                 ProfilingPointSummaryEntity pps = ProfilingPointSummaryEntity.fromEntity(e);
161                 if (pps == null) continue;
162                 plot.addSeriesData(Long.toString(pps.getStartTime()), pps.getSeries(), pps.getGlobalStats());
163                 timestamps.add(pps.getStartTime());
164             }
165             List<Long> timestampList = new ArrayList<>(timestamps);
166             timestampList.sort(Comparator.reverseOrder());
167             List<String> timestampStrings =
168                     timestampList.stream().map(Object::toString).collect(Collectors.toList());
169             plot.setLabels(timestampStrings);
170         }
171 
172         List<BoxPlot> plots = new ArrayList<>();
173         for (String key : plotMap.keySet()) {
174             BoxPlot plot = plotMap.get(key);
175             if (plot.size() == 0) continue;
176             plots.add(plot);
177         }
178         plots.sort((b1, b2) -> b1.getName().compareTo(b2.getName()));
179 
180         Gson gson =
181                 new GsonBuilder()
182                         .registerTypeHierarchyAdapter(BoxPlot.class, new GraphSerializer())
183                         .create();
184 
185         FilterUtil.setAttributes(request, parameterMap);
186         request.setAttribute("plots", gson.toJson(plots));
187         request.setAttribute("testName", request.getParameter("testName"));
188         request.setAttribute("branches", new Gson().toJson(DatastoreHelper.getAllBranches()));
189         request.setAttribute("devices", new Gson().toJson(DatastoreHelper.getAllBuildFlavors()));
190         dispatcher = request.getRequestDispatcher(PROFILING_OVERVIEW_JSP);
191         try {
192             dispatcher.forward(request, response);
193         } catch (ServletException e) {
194             logger.log(Level.SEVERE, "Servlet Exception caught : ", e);
195         }
196     }
197 }
198