Merge "Fix typo in gcda parser setter."
diff --git a/web/dashboard/appengine/servlet/src/main/java/com/android/vts/servlet/VtsPerformanceJobServlet.java b/web/dashboard/appengine/servlet/src/main/java/com/android/vts/servlet/VtsPerformanceJobServlet.java
index e3ab261..c8d204c 100644
--- a/web/dashboard/appengine/servlet/src/main/java/com/android/vts/servlet/VtsPerformanceJobServlet.java
+++ b/web/dashboard/appengine/servlet/src/main/java/com/android/vts/servlet/VtsPerformanceJobServlet.java
@@ -18,27 +18,20 @@
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
-import org.apache.hadoop.hbase.client.Result;
-import org.apache.hadoop.hbase.client.ResultScanner;
-import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
-import org.apache.hadoop.hbase.util.Bytes;
-import com.android.vts.proto.VtsReportMessage;
-import com.android.vts.proto.VtsReportMessage.ProfilingReportMessage;
-import com.android.vts.proto.VtsReportMessage.TestReportMessage;
import com.android.vts.proto.VtsReportMessage.VtsProfilingRegressionMode;
import com.android.vts.util.BigtableHelper;
import com.android.vts.util.EmailHelper;
+import com.android.vts.util.MathUtil;
+import com.android.vts.util.PerformanceUtil;
+import com.android.vts.util.PerformanceUtil.TimeInterval;
import com.android.vts.util.ProfilingPointSummary;
import com.android.vts.util.StatSummary;
import com.google.protobuf.ByteString;
import java.io.IOException;
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -52,9 +45,7 @@
public class VtsPerformanceJobServlet extends BaseServlet {
private static final String STATUS_TABLE = "vts_status_table";
- private static final byte[] RESULTS_FAMILY = Bytes.toBytes("test");
private static final int N_DIGITS = 2;
- private static final byte[] DATA_QUALIFIER = Bytes.toBytes("data");
private static final long MILLI_TO_MICRO = 1000; // conversion factor from milli to micro units
private static final String MEAN = "Mean";
@@ -75,104 +66,6 @@
private static final String INNER_CELL_STYLE = "border-top: 1px dotted gray; border-right: 1px dotted gray; text-align: right;";
private static final String OUTER_CELL_STYLE = "border-top: 1px dotted gray; border-right: 2px solid black; text-align: right;";
- private static class TimeInterval {
- public final long start;
- public final long end;
- public final String label;
-
- public TimeInterval(long start, long end, String label) {
- this.start = start;
- this.end = end;
- this.label = label;
- }
- }
-
- /**
- * Produces a map with summaries for each profiling point present in the specified table.
- * @param tableName The name of the table whose profiling vectors to retrieve.
- * @param startTime The (inclusive) start time in milliseconds to scan from.
- * @param endTime The (exclusive) end time in milliseconds at which to stop scanning.
- * @returns A map with profiling point name keys and ProfilingPointSummary objects as values.
- * @throws IOException
- */
- private static Map<String, ProfilingPointSummary> getProfilingSummaryMap(
- String tableName, long startTime, long endTime) throws IOException {
- Table table = BigtableHelper.getTable(TableName.valueOf(tableName));
- Scan scan = new Scan();
- scan.setStartRow(Bytes.toBytes(Long.toString(startTime * MILLI_TO_MICRO)));
- scan.setStopRow(Bytes.toBytes(Long.toString(endTime * MILLI_TO_MICRO)));
- ResultScanner scanner = table.getScanner(scan);
-
- Map<String, ProfilingPointSummary> profilingSummaryMap = new HashMap<>();
-
- for (Result result = scanner.next(); result != null; result = scanner.next()) {
- byte[] value = result.getValue(RESULTS_FAMILY, DATA_QUALIFIER);
- TestReportMessage testReportMessage = VtsReportMessage.TestReportMessage
- .parseFrom(value);
- String buildId = testReportMessage.getBuildInfo().getId().toStringUtf8();
-
- // filter empty build IDs and add only numbers
- if (buildId.length() == 0) continue;
-
- // filter empty device info lists
- if (testReportMessage.getDeviceInfoList().size() == 0) continue;
-
- String firstDeviceBuildId = testReportMessage.getDeviceInfoList().get(0)
- .getBuildId().toStringUtf8();
-
- try {
- // filter non-integer build IDs
- Integer.parseInt(buildId);
- Integer.parseInt(firstDeviceBuildId);
- } catch (NumberFormatException e) {
- /* skip a non-post-submit build */
- continue;
- }
- for (ProfilingReportMessage profilingReportMessage :
- testReportMessage.getProfilingList()) {
- switch(profilingReportMessage.getRegressionMode()) {
- case VTS_REGRESSION_MODE_DISABLED:
- continue;
- default:
- break;
- }
- String name = profilingReportMessage.getName().toStringUtf8();
- switch(profilingReportMessage.getType()) {
- case UNKNOWN_VTS_PROFILING_TYPE:
- case VTS_PROFILING_TYPE_TIMESTAMP :
- break;
- case VTS_PROFILING_TYPE_LABELED_VECTOR :
- if (profilingReportMessage.getLabelList().size() == 0 ||
- profilingReportMessage.getLabelList().size() !=
- profilingReportMessage.getValueList().size()) {
- continue;
- }
- if (!profilingSummaryMap.containsKey(name)) {
- profilingSummaryMap.put(name, new ProfilingPointSummary());
- }
- profilingSummaryMap.get(name).update(profilingReportMessage);
- break;
- default :
- break;
- }
- }
- }
- return profilingSummaryMap;
- }
-
- /**
- * Rounds a double to a fixed number of digits.
- * @param value The number to be rounded.
- * @param digits The number of decimal places of accuracy.
- * @returns The string representation of 'value' rounded to 'digits' number of decimal places.
- */
- private static String round(double value, int digits) {
- String format = "#." + new String(new char[digits]).replace("\0", "#");
- DecimalFormat formatter = new DecimalFormat(format);
- formatter.setRoundingMode(RoundingMode.CEILING);
- return formatter.format(value);
- }
-
/**
* Creates the HTML for a table cell representing the percent change between two numbers.
*
@@ -195,7 +88,7 @@
if (baseline != 0) {
double pctChange = delta / baseline;
alpha = pctChange * 2;
- pctChangeString = round(pctChange * 100, N_DIGITS) + " %";
+ pctChangeString = MathUtil.round(pctChange * 100, N_DIGITS) + " %";
} else if (delta != 0){
// If the percent change is undefined, the cell will be solid red or white
alpha = (int) Math.signum(delta); // get the sign of the delta (+1, 0, -1)
@@ -238,10 +131,12 @@
// of percentage change, reaching a ceiling at 100% change (e.g. a doubling).
row += getPercentChangeHTML(baselineValue, testValue, INNER_CELL_STYLE,
test.getRegressionMode());
- row += "<td style='" + INNER_CELL_STYLE + "'>" + round(baseline.getMin(), N_DIGITS);
- row += "</td><td style='" + INNER_CELL_STYLE + "'>" + round(baseline.getMean(), N_DIGITS);
+ row += "<td style='" + INNER_CELL_STYLE + "'>";
+ row += MathUtil.round(baseline.getMin(), N_DIGITS);
+ row += "</td><td style='" + INNER_CELL_STYLE + "'>";
+ row += MathUtil.round(baseline.getMean(), N_DIGITS);
row += "</td><td style='" + OUTER_CELL_STYLE + "'>";
- row += round(baseline.getStd(), N_DIGITS) + "</td>";
+ row += MathUtil.round(baseline.getStd(), N_DIGITS) + "</td>";
return row;
}
@@ -324,11 +219,11 @@
ByteString label = stats.getLabel();
tableHTML += "<tr><td style='" + HEADER_COL_STYLE +"'>" + label.toStringUtf8();
tableHTML += "</td><td style='" + INNER_CELL_STYLE + "'>";
- tableHTML += round(stats.getMin(), N_DIGITS) + "</td>";
+ tableHTML += MathUtil.round(stats.getMin(), N_DIGITS) + "</td>";
tableHTML += "<td style='" + INNER_CELL_STYLE + "'>";
- tableHTML += round(stats.getMean(), N_DIGITS) + "</td>";
+ tableHTML += MathUtil.round(stats.getMean(), N_DIGITS) + "</td>";
tableHTML += "<td style='" + OUTER_CELL_STYLE + "'>";
- tableHTML += round(stats.getStd(), N_DIGITS) + "</td>";
+ tableHTML += MathUtil.round(stats.getStd(), N_DIGITS) + "</td>";
for (int i = 1; i < summaryMaps.size(); i++) {
Map<String, ProfilingPointSummary> summaryMapOld = summaryMaps.get(i);
if (summaryMapOld.containsKey(profilingPoint)) {
@@ -389,8 +284,8 @@
List<String> labels = new ArrayList<>();
labels.add("");
for (TimeInterval interval : timeIntervals) {
- Map<String, ProfilingPointSummary> summaryMap = getProfilingSummaryMap(
- tableName, interval.start, interval.end);
+ Map<String, ProfilingPointSummary> summaryMap =
+ PerformanceUtil.getProfilingSummaryMap(tableName, interval.start, interval.end);
if (summaryMap == null || summaryMap.size() == 0) continue;
summaryMaps.add(summaryMap);
labels.add(interval.label);
diff --git a/web/dashboard/appengine/servlet/src/main/java/com/android/vts/util/MathUtil.java b/web/dashboard/appengine/servlet/src/main/java/com/android/vts/util/MathUtil.java
new file mode 100644
index 0000000..e1bedc3
--- /dev/null
+++ b/web/dashboard/appengine/servlet/src/main/java/com/android/vts/util/MathUtil.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright 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.util;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+
+/**
+ * MathUtil, a helper class for mathematical operations.
+ **/
+public class MathUtil {
+
+ /**
+ * Rounds a double to a fixed number of digits.
+ * @param value The number to be rounded.
+ * @param digits The number of decimal places of accuracy.
+ * @returns The string representation of 'value' rounded to 'digits' number of decimal places.
+ */
+ public static String round(double value, int digits) {
+ String format = "#." + new String(new char[digits]).replace("\0", "#");
+ DecimalFormat formatter = new DecimalFormat(format);
+ formatter.setRoundingMode(RoundingMode.HALF_UP);
+ return formatter.format(value);
+ }
+}
diff --git a/web/dashboard/appengine/servlet/src/main/java/com/android/vts/util/PerformanceUtil.java b/web/dashboard/appengine/servlet/src/main/java/com/android/vts/util/PerformanceUtil.java
new file mode 100644
index 0000000..bd20b36
--- /dev/null
+++ b/web/dashboard/appengine/servlet/src/main/java/com/android/vts/util/PerformanceUtil.java
@@ -0,0 +1,129 @@
+/**
+ * Copyright 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.util;
+
+import com.android.vts.proto.VtsReportMessage;
+import com.android.vts.proto.VtsReportMessage.ProfilingReportMessage;
+import com.android.vts.proto.VtsReportMessage.TestReportMessage;
+import com.android.vts.proto.VtsReportMessage.VtsProfilingRegressionMode;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.util.Bytes;
+
+/**
+ * PerformanceUtil, a helper class for analyzing profiling and performance data.
+ **/
+public class PerformanceUtil {
+ protected static Logger logger = Logger.getLogger(PerformanceUtil.class.getName());
+
+ private static final byte[] RESULTS_FAMILY = Bytes.toBytes("test");
+ private static final byte[] DATA_QUALIFIER = Bytes.toBytes("data");
+ private static final long MILLI_TO_MICRO = 1000; // conversion factor from milli to micro units
+
+ public static class TimeInterval {
+ public final long start;
+ public final long end;
+ public final String label;
+
+ public TimeInterval(long start, long end, String label) {
+ this.start = start;
+ this.end = end;
+ this.label = label;
+ }
+ }
+
+ /**
+ * Produces a map with summaries for each profiling point present in the specified table.
+ * @param tableName The name of the table whose profiling vectors to retrieve.
+ * @param startTime The (inclusive) start time in milliseconds to scan from.
+ * @param endTime The (exclusive) end time in milliseconds at which to stop scanning.
+ * @returns A map with profiling point name keys and ProfilingPointSummary objects as values.
+ * @throws IOException
+ */
+ public static Map<String, ProfilingPointSummary> getProfilingSummaryMap(
+ String tableName, long startTime, long endTime) throws IOException {
+ Table table = BigtableHelper.getTable(TableName.valueOf(tableName));
+ Scan scan = new Scan();
+ scan.setStartRow(Bytes.toBytes(Long.toString(startTime * MILLI_TO_MICRO)));
+ scan.setStopRow(Bytes.toBytes(Long.toString(endTime * MILLI_TO_MICRO)));
+ ResultScanner scanner = table.getScanner(scan);
+
+ Map<String, ProfilingPointSummary> profilingSummaryMap = new HashMap<>();
+
+ for (Result result = scanner.next(); result != null; result = scanner.next()) {
+ byte[] value = result.getValue(RESULTS_FAMILY, DATA_QUALIFIER);
+ TestReportMessage testReportMessage = VtsReportMessage.TestReportMessage
+ .parseFrom(value);
+ String buildId = testReportMessage.getBuildInfo().getId().toStringUtf8();
+
+ // filter empty build IDs and add only numbers
+ if (buildId.length() == 0) continue;
+
+ // filter empty device info lists
+ if (testReportMessage.getDeviceInfoList().size() == 0) continue;
+
+ String firstDeviceBuildId = testReportMessage.getDeviceInfoList().get(0)
+ .getBuildId().toStringUtf8();
+
+ try {
+ // filter non-integer build IDs
+ Integer.parseInt(buildId);
+ Integer.parseInt(firstDeviceBuildId);
+ } catch (NumberFormatException e) {
+ /* skip a non-post-submit build */
+ continue;
+ }
+ for (ProfilingReportMessage profilingReportMessage :
+ testReportMessage.getProfilingList()) {
+ if (profilingReportMessage.getRegressionMode() ==
+ VtsProfilingRegressionMode.VTS_REGRESSION_MODE_DISABLED) {
+ continue;
+ }
+
+ String name = profilingReportMessage.getName().toStringUtf8();
+
+ switch (profilingReportMessage.getType()) {
+ case UNKNOWN_VTS_PROFILING_TYPE:
+ case VTS_PROFILING_TYPE_TIMESTAMP :
+ break;
+ case VTS_PROFILING_TYPE_LABELED_VECTOR :
+ if (profilingReportMessage.getLabelList().size() == 0 ||
+ profilingReportMessage.getLabelList().size() !=
+ profilingReportMessage.getValueList().size()) {
+ logger.log(Level.SEVERE, "Invalid profiling report sizes : ", name);
+ continue;
+ }
+ if (!profilingSummaryMap.containsKey(name)) {
+ profilingSummaryMap.put(name, new ProfilingPointSummary());
+ }
+ profilingSummaryMap.get(name).update(profilingReportMessage);
+ break;
+ default :
+ break;
+ }
+ }
+ }
+ return profilingSummaryMap;
+ }
+}
diff --git a/web/dashboard/appengine/servlet/src/test/java/com/android/vts/util/MathUtilTest.java b/web/dashboard/appengine/servlet/src/test/java/com/android/vts/util/MathUtilTest.java
new file mode 100644
index 0000000..e8532b9
--- /dev/null
+++ b/web/dashboard/appengine/servlet/src/test/java/com/android/vts/util/MathUtilTest.java
@@ -0,0 +1,40 @@
+package com.android.vts.util;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import com.android.vts.util.MathUtil;
+
+public class MathUtilTest {
+ private static int N_DIGITS = 3;
+
+ /**
+ * Test rounding of a whole number.
+ */
+ @Test
+ public void testSimple() {
+ int number = 5;
+ String rounded = MathUtil.round(number, N_DIGITS);
+ assertEquals(Integer.toString(number), rounded);
+ }
+
+ /**
+ * Test rounding of a number with fewer significant figures than rounding digits.
+ */
+ @Test
+ public void testBasicDecimal() {
+ double number = 5.5;
+ String rounded = MathUtil.round(number, N_DIGITS);
+ assertEquals(Double.toString(number), rounded);
+ }
+
+ /**
+ * Test rounding of a number with infinite significant figures.
+ */
+ @Test
+ public void testInfiniteDecimal() {
+ double number = 1.0 / 3;
+ String rounded = MathUtil.round(number, N_DIGITS);
+ assertEquals("0.333", rounded);
+ }
+}