aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/profiler/chart/HtmlChartVisitor.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/profiler/chart/HtmlChartVisitor.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/chart/HtmlChartVisitor.java368
1 files changed, 368 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/chart/HtmlChartVisitor.java b/src/main/java/com/google/devtools/build/lib/profiler/chart/HtmlChartVisitor.java
new file mode 100644
index 0000000000..8fa3d0e48b
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/profiler/chart/HtmlChartVisitor.java
@@ -0,0 +1,368 @@
+// Copyright 2014 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.google.devtools.build.lib.profiler.chart;
+
+import com.google.devtools.build.lib.profiler.ProfilePhaseStatistics;
+
+import java.io.PrintStream;
+import java.util.List;
+
+/**
+ * {@link ChartVisitor} that builds HTML from the visited chart and prints it
+ * out to the given {@link PrintStream}.
+ */
+public class HtmlChartVisitor implements ChartVisitor {
+
+ /** The default width of a second in the chart. */
+ private static final int DEFAULT_PIXEL_PER_SECOND = 50;
+
+ /** The horizontal offset of second zero. */
+ private static final int H_OFFSET = 40;
+
+ /** The font size of the row labels. */
+ private static final int ROW_LABEL_FONT_SIZE = 7;
+
+ /** The height of a bar in pixels. */
+ private static final int BAR_HEIGHT = 8;
+
+ /** The space between twp bars in pixels. */
+ private static final int BAR_SPACE = 2;
+
+ /** The height of a row. */
+ private static final int ROW_HEIGHT = BAR_HEIGHT + BAR_SPACE;
+
+ /** The {@link PrintStream} to output the HTML to. */
+ private final PrintStream out;
+
+ /** The maxmimum stop time of any bar in the chart. */
+ private long maxStop;
+
+ /** The width of a second in the chart. */
+ private final int pixelsPerSecond;
+
+ /**
+ * Creates the visitor, with a default width of a second of 50 pixels.
+ *
+ * @param out the {@link PrintStream} to output the HTML to
+ */
+ public HtmlChartVisitor(PrintStream out) {
+ this(out, DEFAULT_PIXEL_PER_SECOND);
+ }
+
+ /**
+ * Creates the visitor.
+ *
+ * @param out the {@link PrintStream} to output the HTML to
+ * @param pixelsPerSecond The width of a second in the chart. (In pixels)
+ */
+ public HtmlChartVisitor(PrintStream out, int pixelsPerSecond) {
+ this.out = out;
+ this.pixelsPerSecond = pixelsPerSecond;
+ }
+
+ @Override
+ public void visit(Chart chart) {
+ maxStop = chart.getMaxStop();
+ out.println("<html><head>");
+ out.printf("<title>%s</title>", chart.getTitle());
+ out.println("<style type=\"text/css\"><!--");
+
+ printCss(chart.getSortedTypes());
+
+ out.println("--></style>");
+ out.println("</head>");
+ out.println("<body>");
+
+ heading(chart.getTitle(), 1);
+
+ printContentBox();
+
+ heading("Tasks", 2);
+ out.println("<p>To get more information about a task point the mouse at one of the bars.</p>");
+
+ out.printf("<div style='position:relative; height: %dpx; margin: %dpx'>\n",
+ chart.getRowCount() * ROW_HEIGHT, H_OFFSET + 10);
+ }
+
+ @Override
+ public void endVisit(Chart chart) {
+ printTimeAxis(chart);
+ out.println("</div>");
+
+ heading("Legend", 2);
+ printLegend(chart.getSortedTypes());
+
+ heading("Statistics", 2);
+ printStatistics(chart.getStatistics());
+
+ out.println("</body>");
+ out.println("</html>");
+}
+
+ @Override
+ public void visit(ChartColumn column) {
+ int width = scale(column.getWidth());
+ if (width == 0) {
+ return;
+ }
+ int left = scale(column.getStart());
+ int height = column.getRowCount() * ROW_HEIGHT;
+ String style = chartTypeNameAsCSSClass(column.getType().getName());
+ box(left, 0, width, height, style, column.getLabel(), 10);
+ }
+
+
+ @Override
+ public void visit(ChartRow slot) {
+ String style = slot.getIndex() % 2 == 0 ? "shade-even" : "shade-odd";
+ int top = slot.getIndex() * ROW_HEIGHT;
+ int width = scale(maxStop) + 1;
+
+ label(-H_OFFSET, top, width + H_OFFSET, ROW_HEIGHT, ROW_LABEL_FONT_SIZE, slot.getId());
+ box(0, top, width, ROW_HEIGHT, style, "", 0);
+ }
+
+ @Override
+ public void visit(ChartBar bar) {
+ int width = scale(bar.getWidth());
+ if (width == 0) {
+ return;
+ }
+ int left = scale(bar.getStart());
+ int top = bar.getRow().getIndex() * ROW_HEIGHT;
+ String style = chartTypeNameAsCSSClass(bar.getType().getName());
+ if (bar.getHighlight()) {
+ style += "-highlight";
+ }
+ box(left, top + 2, width, BAR_HEIGHT, style, bar.getLabel(), 20);
+ }
+
+ @Override
+ public void visit(ChartLine chartLine) {
+ int start = chartLine.getStartRow().getIndex() * ROW_HEIGHT;
+ int stop = chartLine.getStopRow().getIndex() * ROW_HEIGHT;
+ int time = scale(chartLine.getStartTime());
+
+ if (start < stop) {
+ verticalLine(time, start + 1, 1, (stop - start) + ROW_HEIGHT, Color.RED);
+ } else {
+ verticalLine(time, stop + 1, 1, (start - stop) + ROW_HEIGHT, Color.RED);
+ }
+ }
+
+ /**
+ * Converts the given value from the bar of the chart to pixels.
+ */
+ private int scale(long value) {
+ return (int) (value / (1000000000L / pixelsPerSecond));
+ }
+
+ /**
+ * Prints a box with links to the sections of the generated HTML document.
+ */
+ private void printContentBox() {
+ out.println("<div style='position:fixed; top:1em; right:1em; z-index:50; padding: 1ex;"
+ + "border:1px solid #888; background-color:#eee; width:100px'><h3>Content</h3>");
+ out.println("<p style='text-align:left;font-size:small;margin:2px'>"
+ + "<a href='#Tasks'>Tasks</a></p>");
+ out.println("<p style='text-align:left;font-size:small;margin:2px'>"
+ + "<a href='#Legend'>Legend</a></p>");
+ out.println("<p style='text-align:left;font-size:small;margin:2px'>"
+ + "<a href='#Statistics'>Statistics</a></p></div>");
+ }
+
+ /**
+ * Prints the time axis of the chart and vertical lines for every second.
+ */
+ private void printTimeAxis(Chart chart) {
+ int location = 0;
+ int second = 0;
+ int end = scale(chart.getMaxStop());
+ while (location < end) {
+ label(location + 4, -17, pixelsPerSecond, ROW_HEIGHT, 0, second + "s");
+ verticalLine(location, -20, 1, chart.getRowCount() * ROW_HEIGHT + 20, Color.GRAY);
+ location += pixelsPerSecond;
+ second += 1;
+ }
+ }
+
+ private void printCss(List<ChartBarType> types) {
+ out.println("body { font-family: Sans; }");
+ out.printf("div.shade-even { position:absolute; border: 0px; background-color:#dddddd }\n");
+ out.printf("div.shade-odd { position:absolute; border: 0px; background-color:#eeeeee }\n");
+ for (ChartBarType type : types) {
+ String name = chartTypeNameAsCSSClass(type.getName());
+ String color = formatColor(type.getColor());
+
+ out.printf(
+ "div.%s-border { position:absolute; border:1px solid grey; background-color:%s }\n",
+ name, color);
+ out.printf(
+ "div.%s-highlight { position:absolute; border:1px solid red; background-color:%s }\n",
+ name, color);
+ out.printf("div.%s { position:absolute; border:0px; margin:1px; background-color:%s }\n",
+ name, color);
+ }
+ }
+
+ /**
+ * Prints the legend for the chart at the current position in the document. The
+ * legend is printed in columns of 10 rows each.
+ *
+ * @param types the list of {@link ChartBarType}s to print in the legend.
+ */
+ private void printLegend(List<ChartBarType> types) {
+ final int boxHeight = 20;
+ final int lineHeight = 25;
+ final int entriesPerColumn = 10;
+ final int legendWidth = 350;
+ int legendHeight;
+ if (types.size() / entriesPerColumn >= 1) {
+ legendHeight = entriesPerColumn;
+ } else {
+ legendHeight = types.size() % entriesPerColumn;
+ }
+
+ out.printf("<div style='position:relative; height: %dpx;'>",
+ (legendHeight + 1) * lineHeight);
+
+ int left = -legendWidth;
+ int top;
+ int i = 0;
+ for (ChartBarType type : types) {
+ if (i % entriesPerColumn == 0) {
+ left += legendWidth;
+ i = 0;
+ }
+ top = lineHeight * i;
+ String style = chartTypeNameAsCSSClass(type.getName()) + "-border";
+ box(left, top, boxHeight, boxHeight, style, type.getName(), 0);
+ label(left + lineHeight + 10, top, legendWidth - 10, boxHeight, 0, type.getName());
+ i++;
+ }
+ out.println("</div>");
+ }
+
+ private void printStatistics(List<ProfilePhaseStatistics> statistics) {
+ boolean first = true;
+
+ out.println("<table border=\"0\" width=\"100%\"><tr>");
+ for (ProfilePhaseStatistics stat : statistics) {
+ if (!first) {
+ out.println("<td><div style=\"width:20px;\">&#160;</div></td>");
+ } else {
+ first = false;
+ }
+ out.println("<td valign=\"top\">");
+ String title = stat.getTitle();
+ if (title != "") {
+ heading(title, 3);
+ }
+ out.println("<pre>" + stat.getStatistics() + "</pre></td>");
+ }
+ out.println("</tr></table>");
+ }
+
+ /**
+ * Prints a head-line at the current position in the document.
+ *
+ * @param text the text to print
+ * @param level the headline level
+ */
+ private void heading(String text, int level) {
+ anchor(text);
+ out.printf("<h%d >%s</h%d>\n", level, text, level);
+ }
+
+ /**
+ * Prints a box with the given location, size, background color and border.
+ *
+ * @param x the x location of the top left corner of the box
+ * @param y the y location of the top left corner of the box
+ * @param width the width location of the box
+ * @param height the height location of the box
+ * @param style the CSS style class to use for the box
+ * @param title the text displayed when the mouse hovers over the box
+ */
+ private void box(int x, int y, int width, int height, String style, String title, int zIndex) {
+ out.printf("<div class=\"%s\" title=\"%s\" "
+ + "style=\"left:%dpx; top:%dpx; width:%dpx; height:%dpx; z-index:%d\"></div>\n",
+ style, title, x, y, width, height, zIndex);
+ }
+
+ /**
+ * Prints a label with the given location, size, background color and border.
+ *
+ * @param x the x location of the top left corner of the box
+ * @param y the y location of the top left corner of the box
+ * @param width the width location of the box
+ * @param height the height location of the box
+ * @param fontSize the font size of text in the box, 0 for default
+ * @param text the text displayed in the box
+ */
+ private void label(int x, int y, int width, int height, int fontSize, String text) {
+ if (fontSize > 0) {
+ out.printf("<div style=\"position:absolute; left:%dpx; top:%dpx; width:%dpx; "
+ + "height:%dpx; font-size:%dpt\">%s</div>\n",
+ x, y, width, height, fontSize, text);
+ } else {
+ out.printf("<div style=\"position:absolute; left:%dpx; top:%dpx; width:%dpx; "
+ + "height:%dpx\">%s</div>\n",
+ x, y, width, height, text);
+ }
+ }
+
+ /**
+ * Prints a vertical line of given width, height and color at the given
+ * location.
+ *
+ * @param x the x location of the start point of the line
+ * @param y the y location of the start point of the line
+ * @param width the width of the line
+ * @param length the length of the line
+ * @param color the color of the line
+ */
+ private void verticalLine(int x, int y, int width, int length, Color color) {
+ out.printf("<div style='position: absolute; left: %dpx; top: %dpx; width: %dpx; "
+ + "height: %dpx; border-left: %dpx solid %s'" + "></div>\n",
+ x, y, width, length, width, formatColor(color));
+ }
+
+ /**
+ * Prints an HTML anchor with the given name,
+ */
+ private void anchor(String name) {
+ out.println("<a name='" + name + "'/>");
+ }
+
+ /**
+ * Formats the given {@link Color} to a css style color string.
+ */
+ private String formatColor(Color color) {
+ int r = color.getRed();
+ int g = color.getGreen();
+ int b = color.getBlue();
+ int a = color.getAlpha();
+
+ return String.format("rgba(%d,%d,%d,%f)", r, g, b, (a / 255.0));
+ }
+
+ /**
+ * Transform the name into a form suitable as a css class.
+ */
+ private String chartTypeNameAsCSSClass(String name) {
+ return name.replace(' ', '_');
+ }
+}