blob: 854f4ebd4336d6751a0e4a99ac8829fd3a00d859 [file] [log] [blame]
// Copyright 2015 The Bazel Authors. 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.output;
import com.google.common.base.Optional;
import com.google.devtools.build.lib.profiler.ProfilePhase;
import com.google.devtools.build.lib.profiler.analysis.ProfileInfo;
import com.google.devtools.build.lib.profiler.chart.AggregatingChartCreator;
import com.google.devtools.build.lib.profiler.chart.Chart;
import com.google.devtools.build.lib.profiler.chart.ChartCreator;
import com.google.devtools.build.lib.profiler.chart.DetailedChartCreator;
import com.google.devtools.build.lib.profiler.chart.HtmlChartVisitor;
import com.google.devtools.build.lib.profiler.statistics.CriticalPathStatistics;
import com.google.devtools.build.lib.profiler.statistics.MultiProfileStatistics;
import com.google.devtools.build.lib.profiler.statistics.PhaseStatistics;
import com.google.devtools.build.lib.profiler.statistics.PhaseSummaryStatistics;
import com.google.devtools.build.lib.profiler.statistics.SkylarkStatistics;
import com.google.devtools.build.lib.vfs.Path;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.EnumMap;
/**
* Creates an HTML page displaying the various statistics and charts generated
* from the profile file.
*/
public final class HtmlCreator extends HtmlPrinter {
private final Optional<Chart> chart;
private final HtmlChartVisitor chartVisitor;
private final Optional<SkylarkHtml> skylarkStats;
private final Optional<MultiProfilePhaseHtml> multiFileStats;
private final String title;
private final PhaseHtml phases;
private HtmlCreator(
PrintStream out,
String title,
Optional<Chart> chart,
Optional<SkylarkHtml> skylarkStats,
Optional<MultiProfilePhaseHtml> multiFileStats,
PhaseHtml phases,
int htmlPixelsPerSecond) {
super(out);
this.title = title;
this.chart = chart;
this.skylarkStats = skylarkStats;
this.phases = phases;
this.multiFileStats = multiFileStats;
chartVisitor = new HtmlChartVisitor(out, htmlPixelsPerSecond);
}
/**
* Output the HTML depending on which statistics should be printed.
*/
private void print() {
htmlFrontMatter();
if (chart.isPresent()) {
chart.get().accept(chartVisitor);
}
element("a", "name", "Statistics");
element("h2", "Statistics");
phases.print();
if (multiFileStats.isPresent()) {
multiFileStats.get().printHtmlBody();
}
if (skylarkStats.isPresent()) {
skylarkStats.get().printHtmlBody();
}
htmlBackMatter();
}
/**
* Print opening tags, CSS and JavaScript
*/
private void htmlFrontMatter() {
lnOpen("html");
lnOpen("head");
lnElement("title", title);
printVisualizationJs();
if (chart.isPresent()) {
chartVisitor.printCss(chart.get().getSortedTypes());
}
phases.printCss();
if (multiFileStats.isPresent()) {
multiFileStats.get().printHtmlHead();
}
if (skylarkStats.isPresent()) {
skylarkStats.get().printHtmlHead();
}
lnClose();
lnOpen("body");
lnElement("h1", title);
}
private void htmlBackMatter() {
lnClose();
lnClose();
}
/**
* Print code for loading the Google Visualization JS library.
*
* <p>Used for the charts and tables for {@link SkylarkHtml} and {@link MultiProfilePhaseHtml}.
* Also adds a callback on load of the library which draws the charts and tables.
*/
private void printVisualizationJs() {
lnElement("script", "type", "text/javascript", "src", "https://www.google.com/jsapi");
lnOpen("script", "type", "text/javascript");
lnPrint("google.load(\"visualization\", \"1.1\", {packages:[\"corechart\",\"table\"]});");
lnPrint("google.setOnLoadCallback(drawVisualization);");
lnPrint("function drawVisualization() {");
down();
if (skylarkStats.isPresent()) {
skylarkStats.get().printVisualizationCallbackJs();
}
if (multiFileStats.isPresent()) {
multiFileStats.get().printVisualizationCallbackJs();
}
up();
lnPrint("}");
lnClose(); // script
}
/**
* Writes the HTML profiling information.
*
* @throws IOException
*/
public static void create(
ProfileInfo info,
Path htmlFile,
PhaseSummaryStatistics phaseSummaryStats,
EnumMap<ProfilePhase, PhaseStatistics> statistics,
CriticalPathStatistics criticalPathStats,
int missingActionsCount,
boolean detailed,
int htmlPixelsPerSecond,
int vfsStatsLimit,
boolean generateChart,
boolean generateHistograms)
throws IOException {
try (PrintStream out = new PrintStream(new BufferedOutputStream(htmlFile.getOutputStream()))) {
PhaseHtml phaseHtml =
new PhaseHtml(
out,
phaseSummaryStats,
statistics,
Optional.of(criticalPathStats),
Optional.of(missingActionsCount),
vfsStatsLimit);
Optional<SkylarkHtml> skylarkStats = Optional.absent();
Optional<Chart> chart = Optional.absent();
if (detailed) {
skylarkStats =
Optional.of(new SkylarkHtml(out, new SkylarkStatistics(info), generateHistograms));
}
if (generateChart) {
ChartCreator chartCreator;
if (detailed) {
chartCreator = new DetailedChartCreator(info);
} else {
chartCreator = new AggregatingChartCreator(info);
}
chart = Optional.of(chartCreator.create());
}
new HtmlCreator(
out,
info.comment,
chart,
skylarkStats,
Optional.<MultiProfilePhaseHtml>absent(),
phaseHtml,
htmlPixelsPerSecond)
.print();
}
}
/**
* Writes the HTML profiling information for multiple files.
*
* <p>Does not print a {@link Chart} or even multiple charts and no Skylark histograms.
*/
public static void create(
PrintStream out,
MultiProfileStatistics statistics,
boolean detailed,
int htmlPixelsPerSecond,
int vfsStatsLimit) {
PhaseHtml phaseHtml =
new PhaseHtml(
out,
statistics.getSummaryStatistics(),
statistics.getSummaryPhaseStatistics(),
vfsStatsLimit);
Optional<SkylarkHtml> skylarkStats;
if (detailed) {
skylarkStats = Optional.of(new SkylarkHtml(out, statistics.getSkylarkStatistics(), false));
} else {
skylarkStats = Optional.absent();
}
new HtmlCreator(
out,
"Statistics from multiple profile files",
Optional.<Chart>absent(),
skylarkStats,
Optional.of(new MultiProfilePhaseHtml(out, statistics)),
phaseHtml,
htmlPixelsPerSecond)
.print();
}
}