blob: 22b70982e72958874446af07eaf418089de77ab6 [file] [log] [blame]
/*
* Copyright (c) 2016 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
package com.android.vts.servlet;
import com.android.vts.entity.DeviceInfoEntity;
import com.android.vts.entity.ProfilingPointRunEntity;
import com.android.vts.entity.TestEntity;
import com.android.vts.entity.TestRunEntity;
import com.android.vts.util.DatastoreHelper;
import com.android.vts.util.FilterUtil;
import com.android.vts.util.Graph;
import com.android.vts.util.GraphSerializer;
import com.android.vts.util.Histogram;
import com.android.vts.util.LineGraph;
import com.android.vts.util.PerformanceUtil;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
/** Servlet for handling requests to load graphs. */
public class ShowGraphServlet extends BaseServlet {
private static final String GRAPH_JSP = "WEB-INF/jsp/show_graph.jsp";
private static final long DEFAULT_FILTER_OPTION = -1;
private static final String HIDL_HAL_OPTION = "hidl_hal_mode";
private static final String[] splitKeysArray = new String[] {HIDL_HAL_OPTION};
private static final Set<String> splitKeySet = new HashSet<>(Arrays.asList(splitKeysArray));
private static final String PROFILING_DATA_ALERT = "No profiling data was found.";
@Override
public PageType getNavParentType() {
return PageType.TOT;
}
@Override
public List<Page> getBreadcrumbLinks(HttpServletRequest request) {
List<Page> links = new ArrayList<>();
String testName = request.getParameter("testName");
links.add(new Page(PageType.TABLE, testName, "?testName=" + testName));
String profilingPointName = request.getParameter("profilingPoint");
links.add(
new Page(
PageType.GRAPH,
"?testName=" + testName + "&profilingPoint=" + profilingPointName));
return links;
}
/**
* Process a profiling report message and add it to the map of graphs.
*
* @param profilingRun The Entity of a profiling point run to process.
* @param idString The ID derived from the test run to identify the profiling report.
* @param graphMap A map from graph name to Graph object.
*/
private static void processProfilingRun(
Entity profilingRun, String idString, Map<String, Graph> graphMap) {
ProfilingPointRunEntity pt = ProfilingPointRunEntity.fromEntity(profilingRun);
if (pt == null) return;
String name = PerformanceUtil.getOptionAlias(pt, splitKeySet);
Graph g = null;
if (pt.getLabels() != null && pt.getLabels().size() == pt.getValues().size()) {
g = new LineGraph(name);
} else if (pt.getLabels() == null && pt.getValues().size() > 0) {
g = new Histogram(name);
} else {
return;
}
if (!graphMap.containsKey(name)) {
graphMap.put(name, g);
}
graphMap.get(name).addData(idString, pt);
}
/**
* Get a summary string describing the devices in the test run.
*
* @param devices The list of device descriptors for a particular test run.
* @return A string describing the devices in the test run.
*/
private static String getDeviceSummary(List<DeviceInfoEntity> devices) {
if (devices == null) return null;
List<String> buildInfos = new ArrayList<>();
for (DeviceInfoEntity device : devices) {
buildInfos.add(device.getProduct() + " (" + device.getBuildId() + ")");
}
return StringUtils.join(buildInfos, ", ");
}
@Override
public void doGetHandler(HttpServletRequest request, HttpServletResponse response)
throws IOException {
RequestDispatcher dispatcher = null;
DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
String testName = request.getParameter("testName");
String profilingPointName = request.getParameter("profilingPoint");
String selectedDevice = request.getParameter("device");
Long endTime = null;
if (request.getParameter("endTime") != null) {
String time = request.getParameter("endTime");
try {
endTime = Long.parseLong(time);
} catch (NumberFormatException e) {
}
}
if (endTime == null) {
endTime = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
}
Long startTime = endTime - TimeUnit.DAYS.toMicros(1);
// Set of device names
List<String> devices = DatastoreHelper.getAllBuildFlavors();
if (!devices.contains(selectedDevice)) selectedDevice = null;
Map<String, Graph> graphMap = new HashMap<>();
// Create a query for test runs matching the time window filter
Key parentKey = KeyFactory.createKey(TestEntity.KIND, testName);
Filter timeFilter =
FilterUtil.getTimeFilter(parentKey, TestRunEntity.KIND, startTime, endTime);
Query testRunQuery =
new Query(TestRunEntity.KIND)
.setAncestor(parentKey)
.setFilter(timeFilter)
.setKeysOnly();
// Process the test runs in the query
List<Key> gets = new ArrayList<>();
for (Entity testRun :
datastore
.prepare(testRunQuery)
.asIterable(DatastoreHelper.getLargeBatchOptions())) {
gets.add(
KeyFactory.createKey(
testRun.getKey(), ProfilingPointRunEntity.KIND, profilingPointName));
}
Map<Key, Entity> profilingPoints = datastore.get(gets);
Map<Key, Entity> testRunProfiling = new HashMap<>();
for (Key key : profilingPoints.keySet()) {
testRunProfiling.put(key.getParent(), profilingPoints.get(key));
}
Filter deviceFilter =
FilterUtil.getDeviceTimeFilter(parentKey, TestRunEntity.KIND, startTime, endTime);
if (selectedDevice != null) {
deviceFilter =
Query.CompositeFilterOperator.and(
deviceFilter,
new Query.FilterPredicate(
DeviceInfoEntity.BUILD_FLAVOR,
Query.FilterOperator.EQUAL,
selectedDevice));
}
Query deviceQuery =
new Query(DeviceInfoEntity.KIND)
.setAncestor(parentKey)
.setFilter(deviceFilter)
.setKeysOnly();
gets = new ArrayList<>();
for (Entity device :
datastore.prepare(deviceQuery).asIterable(DatastoreHelper.getLargeBatchOptions())) {
if (testRunProfiling.containsKey(device.getParent())) {
gets.add(device.getKey());
}
}
Map<Key, Entity> deviceInfos = datastore.get(gets);
Map<Key, List<DeviceInfoEntity>> testRunDevices = new HashMap<>();
for (Key deviceKey : deviceInfos.keySet()) {
if (!testRunDevices.containsKey(deviceKey.getParent())) {
testRunDevices.put(deviceKey.getParent(), new ArrayList<DeviceInfoEntity>());
}
DeviceInfoEntity device = DeviceInfoEntity.fromEntity(deviceInfos.get(deviceKey));
if (device == null) continue;
testRunDevices.get(deviceKey.getParent()).add(device);
}
for (Key runKey : testRunProfiling.keySet()) {
String idString = getDeviceSummary(testRunDevices.get(runKey));
if (idString != null) {
processProfilingRun(testRunProfiling.get(runKey), idString, graphMap);
}
}
// Get the names of the graphs to render
String[] names = graphMap.keySet().toArray(new String[graphMap.size()]);
Arrays.sort(names);
List<Graph> graphList = new ArrayList<>();
boolean hasHistogram = false;
for (String name : names) {
Graph g = graphMap.get(name);
if (g.size() > 0) {
graphList.add(g);
if (g instanceof Histogram) hasHistogram = true;
}
}
String filterVal = request.getParameter("filterVal");
try {
Long.parseLong(filterVal);
} catch (NumberFormatException e) {
filterVal = Long.toString(DEFAULT_FILTER_OPTION);
}
request.setAttribute("testName", request.getParameter("testName"));
request.setAttribute("filterVal", filterVal);
request.setAttribute("endTime", new Gson().toJson(endTime));
request.setAttribute("devices", devices);
request.setAttribute("selectedDevice", selectedDevice);
request.setAttribute("showFilterDropdown", hasHistogram);
if (graphList.size() == 0) request.setAttribute("error", PROFILING_DATA_ALERT);
Gson gson =
new GsonBuilder()
.registerTypeHierarchyAdapter(Graph.class, new GraphSerializer())
.create();
request.setAttribute("graphs", gson.toJson(graphList));
request.setAttribute("profilingPointName", profilingPointName);
dispatcher = request.getRequestDispatcher(GRAPH_JSP);
try {
dispatcher.forward(request, response);
} catch (ServletException e) {
logger.log(Level.SEVERE, "Servlet Excpetion caught : ", e);
}
}
}