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.DeviceInfoEntity; 20 import com.android.vts.entity.ProfilingPointRunEntity; 21 import com.android.vts.entity.TestEntity; 22 import com.android.vts.entity.TestRunEntity; 23 import com.android.vts.util.DatastoreHelper; 24 import com.android.vts.util.FilterUtil; 25 import com.android.vts.util.Graph; 26 import com.android.vts.util.GraphSerializer; 27 import com.android.vts.util.Histogram; 28 import com.android.vts.util.LineGraph; 29 import com.android.vts.util.PerformanceUtil; 30 import com.google.appengine.api.datastore.DatastoreService; 31 import com.google.appengine.api.datastore.DatastoreServiceFactory; 32 import com.google.appengine.api.datastore.Entity; 33 import com.google.appengine.api.datastore.Key; 34 import com.google.appengine.api.datastore.KeyFactory; 35 import com.google.appengine.api.datastore.Query; 36 import com.google.appengine.api.datastore.Query.Filter; 37 import com.google.gson.Gson; 38 import com.google.gson.GsonBuilder; 39 import java.io.IOException; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.HashMap; 43 import java.util.HashSet; 44 import java.util.List; 45 import java.util.Map; 46 import java.util.Set; 47 import java.util.concurrent.TimeUnit; 48 import java.util.logging.Level; 49 import javax.servlet.RequestDispatcher; 50 import javax.servlet.ServletException; 51 import javax.servlet.http.HttpServletRequest; 52 import javax.servlet.http.HttpServletResponse; 53 import org.apache.commons.lang.StringUtils; 54 55 /** Servlet for handling requests to load graphs. */ 56 public class ShowGraphServlet extends BaseServlet { 57 private static final String GRAPH_JSP = "WEB-INF/jsp/show_graph.jsp"; 58 private static final long DEFAULT_FILTER_OPTION = -1; 59 60 private static final String HIDL_HAL_OPTION = "hidl_hal_mode"; 61 private static final String[] splitKeysArray = new String[] {HIDL_HAL_OPTION}; 62 private static final Set<String> splitKeySet = new HashSet<>(Arrays.asList(splitKeysArray)); 63 private static final String PROFILING_DATA_ALERT = "No profiling data was found."; 64 65 @Override getNavParentType()66 public PageType getNavParentType() { 67 return PageType.TOT; 68 } 69 70 @Override getBreadcrumbLinks(HttpServletRequest request)71 public List<Page> getBreadcrumbLinks(HttpServletRequest request) { 72 List<Page> links = new ArrayList<>(); 73 String testName = request.getParameter("testName"); 74 links.add(new Page(PageType.TABLE, testName, "?testName=" + testName)); 75 76 String profilingPointName = request.getParameter("profilingPoint"); 77 links.add( 78 new Page( 79 PageType.GRAPH, 80 "?testName=" + testName + "&profilingPoint=" + profilingPointName)); 81 return links; 82 } 83 84 /** 85 * Process a profiling report message and add it to the map of graphs. 86 * 87 * @param profilingRun The Entity of a profiling point run to process. 88 * @param idString The ID derived from the test run to identify the profiling report. 89 * @param graphMap A map from graph name to Graph object. 90 */ processProfilingRun( Entity profilingRun, String idString, Map<String, Graph> graphMap)91 private static void processProfilingRun( 92 Entity profilingRun, String idString, Map<String, Graph> graphMap) { 93 ProfilingPointRunEntity pt = ProfilingPointRunEntity.fromEntity(profilingRun); 94 if (pt == null) return; 95 String name = PerformanceUtil.getOptionAlias(pt, splitKeySet); 96 Graph g = null; 97 if (pt.getLabels() != null && pt.getLabels().size() == pt.getValues().size()) { 98 g = new LineGraph(name); 99 } else if (pt.getLabels() == null && pt.getValues().size() > 0) { 100 g = new Histogram(name); 101 } else { 102 return; 103 } 104 if (!graphMap.containsKey(name)) { 105 graphMap.put(name, g); 106 } 107 graphMap.get(name).addData(idString, pt); 108 } 109 110 /** 111 * Get a summary string describing the devices in the test run. 112 * 113 * @param devices The list of device descriptors for a particular test run. 114 * @return A string describing the devices in the test run. 115 */ getDeviceSummary(List<DeviceInfoEntity> devices)116 private static String getDeviceSummary(List<DeviceInfoEntity> devices) { 117 if (devices == null) return null; 118 List<String> buildInfos = new ArrayList<>(); 119 for (DeviceInfoEntity device : devices) { 120 buildInfos.add(device.getProduct() + " (" + device.getBuildId() + ")"); 121 } 122 return StringUtils.join(buildInfos, ", "); 123 } 124 125 @Override doGetHandler(HttpServletRequest request, HttpServletResponse response)126 public void doGetHandler(HttpServletRequest request, HttpServletResponse response) 127 throws IOException { 128 RequestDispatcher dispatcher = null; 129 DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 130 String testName = request.getParameter("testName"); 131 String profilingPointName = request.getParameter("profilingPoint"); 132 String selectedDevice = request.getParameter("device"); 133 Long endTime = null; 134 if (request.getParameter("endTime") != null) { 135 String time = request.getParameter("endTime"); 136 try { 137 endTime = Long.parseLong(time); 138 } catch (NumberFormatException e) { 139 } 140 } 141 if (endTime == null) { 142 endTime = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis()); 143 } 144 Long startTime = endTime - TimeUnit.DAYS.toMicros(1); 145 146 // Set of device names 147 List<String> devices = DatastoreHelper.getAllBuildFlavors(); 148 if (!devices.contains(selectedDevice)) selectedDevice = null; 149 150 Map<String, Graph> graphMap = new HashMap<>(); 151 152 // Create a query for test runs matching the time window filter 153 Key parentKey = KeyFactory.createKey(TestEntity.KIND, testName); 154 Filter timeFilter = 155 FilterUtil.getTimeFilter(parentKey, TestRunEntity.KIND, startTime, endTime); 156 Query testRunQuery = 157 new Query(TestRunEntity.KIND) 158 .setAncestor(parentKey) 159 .setFilter(timeFilter) 160 .setKeysOnly(); 161 162 // Process the test runs in the query 163 List<Key> gets = new ArrayList<>(); 164 for (Entity testRun : 165 datastore 166 .prepare(testRunQuery) 167 .asIterable(DatastoreHelper.getLargeBatchOptions())) { 168 gets.add( 169 KeyFactory.createKey( 170 testRun.getKey(), ProfilingPointRunEntity.KIND, profilingPointName)); 171 } 172 Map<Key, Entity> profilingPoints = datastore.get(gets); 173 Map<Key, Entity> testRunProfiling = new HashMap<>(); 174 for (Key key : profilingPoints.keySet()) { 175 testRunProfiling.put(key.getParent(), profilingPoints.get(key)); 176 } 177 178 Filter deviceFilter = 179 FilterUtil.getDeviceTimeFilter(parentKey, TestRunEntity.KIND, startTime, endTime); 180 if (selectedDevice != null) { 181 deviceFilter = 182 Query.CompositeFilterOperator.and( 183 deviceFilter, 184 new Query.FilterPredicate( 185 DeviceInfoEntity.BUILD_FLAVOR, 186 Query.FilterOperator.EQUAL, 187 selectedDevice)); 188 } 189 Query deviceQuery = 190 new Query(DeviceInfoEntity.KIND) 191 .setAncestor(parentKey) 192 .setFilter(deviceFilter) 193 .setKeysOnly(); 194 gets = new ArrayList<>(); 195 for (Entity device : 196 datastore.prepare(deviceQuery).asIterable(DatastoreHelper.getLargeBatchOptions())) { 197 if (testRunProfiling.containsKey(device.getParent())) { 198 gets.add(device.getKey()); 199 } 200 } 201 202 Map<Key, Entity> deviceInfos = datastore.get(gets); 203 Map<Key, List<DeviceInfoEntity>> testRunDevices = new HashMap<>(); 204 for (Key deviceKey : deviceInfos.keySet()) { 205 if (!testRunDevices.containsKey(deviceKey.getParent())) { 206 testRunDevices.put(deviceKey.getParent(), new ArrayList<DeviceInfoEntity>()); 207 } 208 DeviceInfoEntity device = DeviceInfoEntity.fromEntity(deviceInfos.get(deviceKey)); 209 if (device == null) continue; 210 testRunDevices.get(deviceKey.getParent()).add(device); 211 } 212 213 for (Key runKey : testRunProfiling.keySet()) { 214 String idString = getDeviceSummary(testRunDevices.get(runKey)); 215 if (idString != null) { 216 processProfilingRun(testRunProfiling.get(runKey), idString, graphMap); 217 } 218 } 219 220 // Get the names of the graphs to render 221 String[] names = graphMap.keySet().toArray(new String[graphMap.size()]); 222 Arrays.sort(names); 223 224 List<Graph> graphList = new ArrayList<>(); 225 boolean hasHistogram = false; 226 for (String name : names) { 227 Graph g = graphMap.get(name); 228 if (g.size() > 0) { 229 graphList.add(g); 230 if (g instanceof Histogram) hasHistogram = true; 231 } 232 } 233 234 String filterVal = request.getParameter("filterVal"); 235 try { 236 Long.parseLong(filterVal); 237 } catch (NumberFormatException e) { 238 filterVal = Long.toString(DEFAULT_FILTER_OPTION); 239 } 240 request.setAttribute("testName", request.getParameter("testName")); 241 request.setAttribute("filterVal", filterVal); 242 request.setAttribute("endTime", new Gson().toJson(endTime)); 243 request.setAttribute("devices", devices); 244 request.setAttribute("selectedDevice", selectedDevice); 245 request.setAttribute("showFilterDropdown", hasHistogram); 246 if (graphList.size() == 0) request.setAttribute("error", PROFILING_DATA_ALERT); 247 248 Gson gson = 249 new GsonBuilder() 250 .registerTypeHierarchyAdapter(Graph.class, new GraphSerializer()) 251 .create(); 252 request.setAttribute("graphs", gson.toJson(graphList)); 253 254 request.setAttribute("profilingPointName", profilingPointName); 255 dispatcher = request.getRequestDispatcher(GRAPH_JSP); 256 try { 257 dispatcher.forward(request, response); 258 } catch (ServletException e) { 259 logger.log(Level.SEVERE, "Servlet Excpetion caught : ", e); 260 } 261 } 262 } 263