aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build
diff options
context:
space:
mode:
authorGravatar Klaas Boesche <klaasb@google.com>2015-10-09 13:24:28 +0000
committerGravatar Kristina Chodorow <kchodorow@google.com>2015-10-09 14:42:49 +0000
commit6133c3f93d2664f8923ec027443041e595eadead (patch)
tree377504d2d345d6aa9c43cd16f7694f265b9bc258 /src/main/java/com/google/devtools/build
parent3c74af02c9ab519fa9551bf518c834f77babd8a4 (diff)
Add combine option for multiple profile file stats
Add the --combine option to produce a single aggregated statistics output for multiple profile files. Outputs neither Skylark histograms nor the task chart. -- MOS_MIGRATED_REVID=105051164
Diffstat (limited to 'src/main/java/com/google/devtools/build')
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/HtmlCreator.java99
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/MultiProfilePhaseHtml.java110
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/PhaseHtml.java11
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/PhaseText.java9
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/SkylarkHtml.java91
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/statistics/MultiProfileStatistics.java124
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseStatistics.java143
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseSummaryStatistics.java20
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseVfsStatistics.java170
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java151
-rw-r--r--src/main/java/com/google/devtools/build/lib/util/TimeUtilities.java12
11 files changed, 732 insertions, 208 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/output/HtmlCreator.java b/src/main/java/com/google/devtools/build/lib/profiler/output/HtmlCreator.java
index ab703ac2fd..885c965ee4 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/output/HtmlCreator.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/output/HtmlCreator.java
@@ -21,6 +21,7 @@ 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.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;
@@ -40,6 +41,7 @@ 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;
@@ -48,25 +50,21 @@ public final class HtmlCreator extends HtmlPrinter {
String title,
Optional<Chart> chart,
Optional<SkylarkHtml> skylarkStats,
- int htmlPixelsPerSecond,
- PhaseHtml phases) {
+ 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);
}
- public HtmlCreator(
- PrintStream out,
- String title,
- Optional<SkylarkHtml> skylarkStats,
- int htmlPixelsPerSecond,
- PhaseHtml phases) {
- this(out, title, Optional.<Chart>absent(), skylarkStats, htmlPixelsPerSecond, phases);
- }
-
+ /**
+ * Output the HTML depending on which statistics should be printed.
+ */
private void print() {
htmlFrontMatter();
if (chart.isPresent()) {
@@ -77,22 +75,34 @@ public final class HtmlCreator extends HtmlPrinter {
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();
}
@@ -108,6 +118,30 @@ public final class HtmlCreator extends HtmlPrinter {
}
/**
+ * 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
@@ -140,8 +174,49 @@ public final class HtmlCreator extends HtmlPrinter {
}
chart = Optional.of(chartCreator.create());
}
- new HtmlCreator(out, info.comment, chart, skylarkStats, htmlPixelsPerSecond, phaseHtml)
+ 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();
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/output/MultiProfilePhaseHtml.java b/src/main/java/com/google/devtools/build/lib/profiler/output/MultiProfilePhaseHtml.java
new file mode 100644
index 0000000000..cbde62b4b4
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/profiler/output/MultiProfilePhaseHtml.java
@@ -0,0 +1,110 @@
+// 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.devtools.build.lib.profiler.ProfilePhase;
+import com.google.devtools.build.lib.profiler.ProfilerTask;
+import com.google.devtools.build.lib.profiler.statistics.MultiProfileStatistics;
+import com.google.devtools.build.lib.profiler.statistics.PhaseStatistics;
+import com.google.devtools.build.lib.util.TimeUtilities;
+import com.google.devtools.build.lib.vfs.Path;
+
+import java.io.PrintStream;
+import java.util.EnumMap;
+
+/**
+ * Formats the per file phase statistics from {@link MultiProfileStatistics} into HTML tables.
+ */
+public final class MultiProfilePhaseHtml extends HtmlPrinter {
+
+ private final MultiProfileStatistics statistics;
+
+ public MultiProfilePhaseHtml(PrintStream out, MultiProfileStatistics statistics) {
+ super(out);
+ this.statistics = statistics;
+ }
+
+ /**
+ * Prints CSS definitions and JavaScript code.
+ */
+ void printHtmlHead() {
+ lnOpen("style", "type", "text/css", "<!--");
+ lnPrint("div.profiles-table {");
+ lnPrint(" width: 95%; margin: 0 auto; height: auto;");
+ lnPrint("}");
+ lnPrint("-->");
+ close(); // style
+ }
+
+ /**
+ * Prints the table data and JS for each phase and file.
+ *
+ * <p>Code must be added to the callback that is run when the Visualization library has loaded.
+ */
+ public void printVisualizationCallbackJs() {
+ lnPrint("var multiData;");
+ lnPrint("var statsDiv;");
+ lnPrint("var profileTable;");
+ for (ProfilePhase phase : statistics.getSummaryStatistics()) {
+ lnPrintf("statsDiv = document.getElementById('profile_file_stats_%s');", phase.nick);
+ lnPrint("multiData = new google.visualization.DataTable();");
+ lnPrint("multiData.addColumn('string', 'File');");
+ lnPrint("multiData.addColumn('number', 'total');");
+ PhaseStatistics summaryPhaseStatistics = statistics.getSummaryPhaseStatistics(phase);
+ for (ProfilerTask taskType : summaryPhaseStatistics) {
+ lnPrintf("multiData.addColumn('number', '%s %%');", taskType.name());
+ }
+ lnPrint("multiData.addRows([");
+ down();
+ for (Path file : statistics) {
+ EnumMap<ProfilePhase, PhaseStatistics> phases = statistics.getPhaseStatistics(file);
+ PhaseStatistics phaseStatistics = phases.get(phase);
+ lnPrintf("['%s', ", file);
+ long phaseDuration = phaseStatistics.getPhaseDurationNanos();
+ printf("{v:%d, f:'%s'}, ", phaseDuration, TimeUtilities.prettyTime(phaseDuration));
+ for (ProfilerTask taskType : summaryPhaseStatistics) {
+ if (phaseStatistics.wasExecuted(taskType)) {
+ double relative = phaseStatistics.getTotalRelativeDuration(taskType);
+ printf("{v:%.4f, f:'%.3f %%'}, ", relative, relative * 100);
+ } else {
+ print("0, ");
+ }
+ }
+ print("],");
+ }
+ lnPrint("]);");
+ up();
+ lnPrint("profileTable = new google.visualization.Table(statsDiv);");
+ lnPrint("profileTable.draw(multiData, {showRowNumber: true, width: '100%%'});");
+ }
+ }
+
+ /**
+ * Prints divs for the tables of statistics for profile files and their phases.
+ */
+ void printHtmlBody() {
+ lnPrint("<a name='profile_file_stats'/>");
+ lnElement("h3", "Profile File Statistics");
+ lnOpen("div", "class", "profiles-tables", "id", "profile_file_stats");
+ for (ProfilePhase phase : statistics.getSummaryStatistics()) {
+ lnOpen("div");
+ lnElement("h4", phase.nick);
+ lnElement("div", "class", "profiles-table", "id", "profile_file_stats_" + phase.nick);
+ lnClose();
+ }
+ lnClose(); // div
+ }
+}
+
+
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/output/PhaseHtml.java b/src/main/java/com/google/devtools/build/lib/profiler/output/PhaseHtml.java
index 231143177d..039ea28de7 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/output/PhaseHtml.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/output/PhaseHtml.java
@@ -25,7 +25,6 @@ import com.google.devtools.build.lib.util.TimeUtilities;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.EnumMap;
-import java.util.Map.Entry;
/**
* Output {@link PhaseSummaryStatistics}, {@link PhaseStatistics} and {@link PhaseVfsStatistics}
@@ -88,7 +87,7 @@ public final class PhaseHtml extends HtmlPrinter {
for (ProfilePhase phase :
Arrays.asList(ProfilePhase.INIT, ProfilePhase.LOAD, ProfilePhase.ANALYZE)) {
PhaseStatistics statistics = phaseStatistics.get(phase);
- if (!statistics.wasExecuted()) {
+ if (statistics == null || !statistics.wasExecuted()) {
continue;
}
printPhaseStatistics(statistics);
@@ -159,7 +158,7 @@ public final class PhaseHtml extends HtmlPrinter {
for (ProfilerTask type : stats) {
int numPrinted = 0;
- for (Entry<Stat, String> stat : stats.getSortedStatistics(type)) {
+ for (Stat stat : stats.getSortedStatistics(type)) {
lnOpen("tr");
if (vfsStatsLimit != -1 && numPrinted++ == vfsStatsLimit) {
open("td", "class", "center", "colspan", "4");
@@ -169,9 +168,9 @@ public final class PhaseHtml extends HtmlPrinter {
break;
}
element("td", type.name());
- element("td", stat.getKey().count);
- element("td", TimeUtilities.prettyTime(stat.getKey().duration));
- element("td", "class", "left", stat.getValue());
+ element("td", stat.getCount());
+ element("td", TimeUtilities.prettyTime(stat.getDuration()));
+ element("td", "class", "left", stat.path);
close(); // tr
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/output/PhaseText.java b/src/main/java/com/google/devtools/build/lib/profiler/output/PhaseText.java
index e246eb1c71..f3b71098bf 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/output/PhaseText.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/output/PhaseText.java
@@ -26,7 +26,6 @@ import com.google.devtools.build.lib.util.TimeUtilities;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.EnumMap;
-import java.util.Map.Entry;
/**
* Output {@link PhaseSummaryStatistics}, {@link PhaseStatistics} and {@link PhaseVfsStatistics}
@@ -206,7 +205,7 @@ public final class PhaseText extends TextPrinter {
for (ProfilerTask type : stats) {
int numPrinted = 0;
- for (Entry<Stat, String> stat : stats.getSortedStatistics(type)) {
+ for (Stat stat : stats.getSortedStatistics(type)) {
if (vfsStatsLimit != -1 && numPrinted++ == vfsStatsLimit) {
lnPrintf("... %d more ...", stats.getStatisticsCount(type) - vfsStatsLimit);
break;
@@ -214,9 +213,9 @@ public final class PhaseText extends TextPrinter {
lnPrintf(
"%15s %10d %10s %s",
type.name(),
- stat.getKey().count,
- TimeUtilities.prettyTime(stat.getKey().duration),
- stat.getValue());
+ stat.getCount(),
+ TimeUtilities.prettyTime(stat.getDuration()),
+ stat.path);
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/output/SkylarkHtml.java b/src/main/java/com/google/devtools/build/lib/profiler/output/SkylarkHtml.java
index c0e9ee14fa..769e4e3b7f 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/output/SkylarkHtml.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/output/SkylarkHtml.java
@@ -33,6 +33,8 @@ public final class SkylarkHtml extends HtmlPrinter {
* How many characters from the end of the location of a Skylark function to display.
*/
private static final int NUM_LOCATION_CHARS_UNABBREVIATED = 40;
+ private static final String JS_DATA_VAR = "skylarkData";
+ private static final String JS_TABLE_VAR = JS_DATA_VAR + "Table";
private final SkylarkStatistics stats;
private final boolean printHistograms;
@@ -64,48 +66,11 @@ public final class SkylarkHtml extends HtmlPrinter {
lnPrint("-->");
close(); // style
- 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);");
-
- String dataVar = "data";
- String tableVar = dataVar + "Table";
- lnPrintf("var %s = {};\n", dataVar);
- lnPrintf("var %s = {};\n", tableVar);
+ lnPrintf("var %s = {};\n", JS_DATA_VAR);
+ lnPrintf("var %s = {};\n", JS_TABLE_VAR);
lnPrint("var histogramData;");
- lnPrint("function drawVisualization() {");
- down();
- printStatsJs(
- stats.getUserFunctionStatistics(),
- stats.getUserFunctionSelfStatistics(),
- "user",
- dataVar,
- tableVar,
- stats.getUserTotalNanos());
- printStatsJs(
- stats.getBuiltinFunctionStatistics(),
- stats.getBuiltinFunctionSelfStatistics(),
- "builtin",
- dataVar,
- tableVar,
- stats.getBuiltinTotalNanos());
-
- if (printHistograms) {
- printHistogramData();
-
- lnPrint("document.querySelector('#user-close').onclick = function() {");
- lnPrint(" document.querySelector('#user-histogram').style.display = 'none';");
- lnPrint("};");
- lnPrint("document.querySelector('#builtin-close').onclick = function() {");
- lnPrint(" document.querySelector('#builtin-histogram').style.display = 'none';");
- lnPrint("};");
- }
-
- up();
- lnPrint("};");
-
if (printHistograms) {
lnPrint("var options = {");
down();
@@ -122,11 +87,11 @@ public final class SkylarkHtml extends HtmlPrinter {
down();
lnPrint("return function() {");
down();
- printf("var selection = %s[category].getSelection();", tableVar);
+ printf("var selection = %s[category].getSelection();", JS_TABLE_VAR);
lnPrint("if (selection.length < 1) return;");
lnPrint("var item = selection[0];");
- lnPrintf("var loc = %s[category].getValue(item.row, 0);", dataVar);
- lnPrintf("var func = %s[category].getValue(item.row, 1);", dataVar);
+ lnPrintf("var loc = %s[category].getValue(item.row, 0);", JS_DATA_VAR);
+ lnPrintf("var func = %s[category].getValue(item.row, 1);", JS_DATA_VAR);
lnPrint("var histogramDiv = document.getElementById(category+'-histogram');");
if (printHistograms) {
lnPrint("var key = loc + '#' + func;");
@@ -151,6 +116,34 @@ public final class SkylarkHtml extends HtmlPrinter {
lnClose(); // script
}
+ /**
+ * Prints the data for the tables of Skylark function statistics and - if needed - the
+ * histogram data.
+ */
+ void printVisualizationCallbackJs() {
+ printStatsJs(
+ stats.getUserFunctionStatistics(),
+ stats.getUserFunctionSelfStatistics(),
+ "user",
+ stats.getUserTotalNanos());
+ printStatsJs(
+ stats.getBuiltinFunctionStatistics(),
+ stats.getBuiltinFunctionSelfStatistics(),
+ "builtin",
+ stats.getBuiltinTotalNanos());
+
+ if (printHistograms) {
+ printHistogramData();
+
+ lnPrint("document.querySelector('#user-close').onclick = function() {");
+ lnPrint(" document.querySelector('#user-histogram').style.display = 'none';");
+ lnPrint("};");
+ lnPrint("document.querySelector('#builtin-close').onclick = function() {");
+ lnPrint(" document.querySelector('#builtin-histogram').style.display = 'none';");
+ lnPrint("};");
+ }
+ }
+
private void printHistogramData() {
lnPrint("histogramData = {");
down();
@@ -182,10 +175,8 @@ public final class SkylarkHtml extends HtmlPrinter {
Map<String, TasksStatistics> taskStatistics,
Map<String, TasksStatistics> taskSelfStatistics,
String category,
- String dataVar,
- String tableVar,
long totalNanos) {
- String tmpVar = category + dataVar;
+ String tmpVar = category + JS_DATA_VAR;
lnPrintf("var statsDiv = document.getElementById('%s_function_stats');", category);
if (taskStatistics.isEmpty()) {
lnPrint(
@@ -239,18 +230,18 @@ public final class SkylarkHtml extends HtmlPrinter {
}
lnPrint("]);");
up();
- lnPrintf("%s.%s = %s;", dataVar, category, tmpVar);
- lnPrintf("%s.%s = new google.visualization.Table(statsDiv);", tableVar, category);
+ lnPrintf("%s.%s = %s;", JS_DATA_VAR, category, tmpVar);
+ lnPrintf("%s.%s = new google.visualization.Table(statsDiv);", JS_TABLE_VAR, category);
lnPrintf(
"google.visualization.events.addListener(%s.%s, 'select', selectHandler('%s'));",
- tableVar,
+ JS_TABLE_VAR,
category,
category);
lnPrintf(
"%s.%s.draw(%s.%s, {showRowNumber: true, width: '100%%', height: '100%%'});",
- tableVar,
+ JS_TABLE_VAR,
category,
- dataVar,
+ JS_DATA_VAR,
category);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/statistics/MultiProfileStatistics.java b/src/main/java/com/google/devtools/build/lib/profiler/statistics/MultiProfileStatistics.java
new file mode 100644
index 0000000000..a4e8959f44
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/profiler/statistics/MultiProfileStatistics.java
@@ -0,0 +1,124 @@
+// 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.statistics;
+
+import com.google.devtools.build.lib.profiler.ProfileInfo;
+import com.google.devtools.build.lib.profiler.ProfileInfo.InfoListener;
+import com.google.devtools.build.lib.profiler.ProfilePhase;
+import com.google.devtools.build.lib.vfs.Path;
+
+import java.io.IOException;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Builds and aggregates statistics for multiple profile files.
+ */
+public final class MultiProfileStatistics implements Iterable<Path> {
+ private final PhaseSummaryStatistics summaryStatistics;
+ private final EnumMap<ProfilePhase, PhaseStatistics> summaryPhaseStatistics;
+ private final Map<Path, EnumMap<ProfilePhase, PhaseStatistics>> filePhaseStatistics;
+ private final SkylarkStatistics skylarkStatistics;
+
+ private int missingActionsCount;
+ private boolean generateVfsStatistics;
+
+ public MultiProfileStatistics(
+ Path workingDirectory,
+ String workSpaceName,
+ List<String> files,
+ InfoListener listener,
+ boolean generateVfsStatistics) {
+ summaryStatistics = new PhaseSummaryStatistics();
+ summaryPhaseStatistics = new EnumMap<>(ProfilePhase.class);
+ filePhaseStatistics = new HashMap<>();
+ skylarkStatistics = new SkylarkStatistics();
+ this.generateVfsStatistics = generateVfsStatistics;
+ for (String file : files) {
+ loadProfileFile(workingDirectory, workSpaceName, file, listener);
+ }
+ }
+
+ public PhaseSummaryStatistics getSummaryStatistics() {
+ return summaryStatistics;
+ }
+
+ public EnumMap<ProfilePhase, PhaseStatistics> getSummaryPhaseStatistics() {
+ return summaryPhaseStatistics;
+ }
+
+ public PhaseStatistics getSummaryPhaseStatistics(ProfilePhase phase) {
+ return summaryPhaseStatistics.get(phase);
+ }
+
+ public SkylarkStatistics getSkylarkStatistics() {
+ return skylarkStatistics;
+ }
+
+ public int getMissingActionsCount() {
+ return missingActionsCount;
+ }
+
+ public EnumMap<ProfilePhase, PhaseStatistics> getPhaseStatistics(Path file) {
+ return filePhaseStatistics.get(file);
+ }
+
+ @Override
+ public Iterator<Path> iterator() {
+ return filePhaseStatistics.keySet().iterator();
+ }
+
+ /**
+ * Loads a single profile file and adds the statistics to the previously collected ones.
+ */
+ private void loadProfileFile(
+ Path workingDirectory, String workSpaceName, String file, InfoListener listener) {
+ ProfileInfo info;
+ Path profileFile = workingDirectory.getRelative(file);
+ try {
+ info = ProfileInfo.loadProfileVerbosely(profileFile, listener);
+ ProfileInfo.aggregateProfile(info, listener);
+ } catch (IOException e) {
+ listener.warn("Ignoring file " + file + " - cannot load: " + e.getMessage());
+ return;
+ }
+
+ summaryStatistics.addProfileInfo(info);
+
+ EnumMap<ProfilePhase, PhaseStatistics> fileStatistics = new EnumMap<>(ProfilePhase.class);
+ filePhaseStatistics.put(profileFile, fileStatistics);
+
+ for (ProfilePhase phase : ProfilePhase.values()) {
+ PhaseStatistics filePhaseStat =
+ new PhaseStatistics(phase, info, workSpaceName, generateVfsStatistics);
+ fileStatistics.put(phase, filePhaseStat);
+
+ PhaseStatistics summaryPhaseStats;
+ if (summaryPhaseStatistics.containsKey(phase)) {
+ summaryPhaseStats = summaryPhaseStatistics.get(phase);
+ } else {
+ summaryPhaseStats = new PhaseStatistics(phase, generateVfsStatistics);
+ summaryPhaseStatistics.put(phase, summaryPhaseStats);
+ }
+ summaryPhaseStats.add(filePhaseStat);
+ }
+
+ skylarkStatistics.addProfileInfo(info);
+
+ missingActionsCount += info.getMissingActionsCount();
+ }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseStatistics.java b/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseStatistics.java
index 3f6e326adf..96602a9d39 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseStatistics.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseStatistics.java
@@ -13,6 +13,7 @@
// limitations under the License.
package com.google.devtools.build.lib.profiler.statistics;
+import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterators;
import com.google.devtools.build.lib.profiler.ProfileInfo;
@@ -31,39 +32,92 @@ import java.util.List;
public final class PhaseStatistics implements Iterable<ProfilerTask> {
private final ProfilePhase phase;
- private final long phaseDurationNanos;
- private final long totalDurationNanos;
- private final EnumMap<ProfilerTask, AggregateAttr> aggregateTaskStatistics;
+ private long phaseDurationNanos;
+ private long totalDurationNanos;
+ private final EnumMap<ProfilerTask, Long> taskDurations;
+ private final EnumMap<ProfilerTask, Long> taskCounts;
private final PhaseVfsStatistics vfsStatistics;
- private final boolean wasExecuted;
+ private boolean wasExecuted;
+ private int count;
- public PhaseStatistics(ProfilePhase phase, ProfileInfo info, String workSpaceName) {
+ public PhaseStatistics(ProfilePhase phase, boolean generateVfsStatistics) {
this.phase = phase;
- this.aggregateTaskStatistics = new EnumMap<>(ProfilerTask.class);
- Task phaseTask = info.getPhaseTask(phase);
- vfsStatistics = new PhaseVfsStatistics(workSpaceName, phase, info);
- if (phaseTask == null) {
- wasExecuted = false;
- totalDurationNanos = 0;
- phaseDurationNanos = 0;
+ this.taskDurations = new EnumMap<>(ProfilerTask.class);
+ this.taskCounts = new EnumMap<>(ProfilerTask.class);
+ if (generateVfsStatistics) {
+ vfsStatistics = new PhaseVfsStatistics(phase);
} else {
+ vfsStatistics = null;
+ }
+ }
+
+ public PhaseStatistics(ProfilePhase phase, ProfileInfo info, String workSpaceName, boolean vfs) {
+ this(phase, vfs);
+ addProfileInfo(workSpaceName, info);
+ }
+
+ /**
+ * Add statistics from {@link ProfileInfo} to the ones already accumulated for this phase.
+ */
+ public void addProfileInfo(String workSpaceName, ProfileInfo info) {
+ Task phaseTask = info.getPhaseTask(phase);
+ if (phaseTask != null) {
+ if (vfsStatistics != null) {
+ vfsStatistics.addProfileInfo(workSpaceName, info);
+ }
wasExecuted = true;
- phaseDurationNanos = info.getPhaseDuration(phaseTask);
+ long infoPhaseDuration = info.getPhaseDuration(phaseTask);
+ phaseDurationNanos += infoPhaseDuration;
List<Task> taskList = info.getTasksForPhase(phaseTask);
- long duration = phaseDurationNanos;
+ long duration = infoPhaseDuration;
for (Task task : taskList) {
// Tasks on the phaseTask thread already accounted for in the phaseDuration.
if (task.threadId != phaseTask.threadId) {
duration += task.durationNanos;
}
}
- totalDurationNanos = duration;
+ totalDurationNanos += duration;
for (ProfilerTask type : ProfilerTask.values()) {
- aggregateTaskStatistics.put(type, info.getStatsForType(type, taskList));
+ AggregateAttr attr = info.getStatsForType(type, taskList);
+ long totalTime = Math.max(0, attr.totalTime);
+ long count = Math.max(0, attr.count);
+ add(taskCounts, type, count);
+ add(taskDurations, type, totalTime);
}
+ count++;
}
}
+ /**
+ * Add statistics accumulated in another PhaseStatistics object to this one.
+ */
+ public void add(PhaseStatistics other) {
+ Preconditions.checkArgument(
+ phase == other.phase, "Should not combine statistics from different phases");
+ if (other.wasExecuted) {
+ if (vfsStatistics != null && other.vfsStatistics != null) {
+ vfsStatistics.add(other.vfsStatistics);
+ }
+ wasExecuted = true;
+ phaseDurationNanos += other.phaseDurationNanos;
+ totalDurationNanos += other.totalDurationNanos;
+ for (ProfilerTask type : other) {
+ long otherCount = other.getCount(type);
+ long otherDuration = other.getTotalDurationNanos(type);
+ add(taskCounts, type, otherCount);
+ add(taskDurations, type, otherDuration);
+ }
+ count++;
+ }
+ }
+
+ /**
+ * @return how many executions of this phase were accumulated
+ */
+ public int getPhaseCount() {
+ return count;
+ }
+
public ProfilePhase getProfilePhase() {
return phase;
}
@@ -76,7 +130,7 @@ public final class PhaseStatistics implements Iterable<ProfilerTask> {
* @return true if no {@link ProfilerTask}s have been executed in this phase, false otherwise
*/
public boolean isEmpty() {
- return aggregateTaskStatistics.isEmpty();
+ return taskCounts.isEmpty();
}
/**
@@ -98,14 +152,19 @@ public final class PhaseStatistics implements Iterable<ProfilerTask> {
* @return true if a task of the given {@link ProfilerTask} type was executed in this phase
*/
public boolean wasExecuted(ProfilerTask taskType) {
- return aggregateTaskStatistics.get(taskType).count != 0;
+ Long count = taskCounts.get(taskType);
+ return count != null && count != 0;
}
/**
* @return the sum of all task durations of the given type
*/
public long getTotalDurationNanos(ProfilerTask taskType) {
- return aggregateTaskStatistics.get(taskType).totalTime;
+ Long duration = taskDurations.get(taskType);
+ if (duration == null) {
+ return 0;
+ }
+ return duration;
}
/**
@@ -113,8 +172,9 @@ public final class PhaseStatistics implements Iterable<ProfilerTask> {
*/
public double getMeanDuration(ProfilerTask taskType) {
if (wasExecuted(taskType)) {
- AggregateAttr stats = aggregateTaskStatistics.get(taskType);
- return (double) stats.totalTime / stats.count;
+ double duration = taskDurations.get(taskType);
+ long count = taskCounts.get(taskType);
+ return duration / count;
}
return 0;
}
@@ -124,17 +184,30 @@ public final class PhaseStatistics implements Iterable<ProfilerTask> {
* phase duration
*/
public double getTotalRelativeDuration(ProfilerTask taskType) {
- if (wasExecuted(taskType)) {
- return (double) aggregateTaskStatistics.get(taskType).totalTime / totalDurationNanos;
+ Long duration = taskDurations.get(taskType);
+ if (duration == null || duration == 0) {
+ return 0;
}
- return 0;
+ // sanity check for broken profile files
+ Preconditions.checkState(
+ totalDurationNanos != 0,
+ "Profiler tasks of type %s have non-zero duration %s in phase %s but the phase itself has"
+ + " zero duration. Most likely the profile file is broken.",
+ taskType,
+ duration,
+ phase);
+ return (double) duration / totalDurationNanos;
}
/**
* @return how many tasks of the given type were executed in this phase
*/
- public int getCount(ProfilerTask taskType) {
- return aggregateTaskStatistics.get(taskType).count;
+ public long getCount(ProfilerTask taskType) {
+ Long count = taskCounts.get(taskType);
+ if (count == null) {
+ return 0;
+ }
+ return count;
}
/**
@@ -144,14 +217,26 @@ public final class PhaseStatistics implements Iterable<ProfilerTask> {
@Override
public Iterator<ProfilerTask> iterator() {
return Iterators.filter(
- aggregateTaskStatistics.keySet().iterator(),
+ taskCounts.keySet().iterator(),
new Predicate<ProfilerTask>() {
@Override
public boolean apply(ProfilerTask taskType) {
-
- return getTotalDurationNanos(taskType) != 0 && getCount(taskType) != 0;
+ return getTotalDurationNanos(taskType) > 0 && wasExecuted(taskType);
}
});
}
+
+ /**
+ * Helper method to sum up long values within an {@link EnumMap}.
+ */
+ private static <T extends Enum<T>> void add(EnumMap<T, Long> map, T key, long value) {
+ long previous;
+ if (map.containsKey(key)) {
+ previous = map.get(key);
+ } else {
+ previous = 0;
+ }
+ map.put(key, previous + value);
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseSummaryStatistics.java b/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseSummaryStatistics.java
index 965cd78828..59032b13ef 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseSummaryStatistics.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseSummaryStatistics.java
@@ -26,21 +26,31 @@ import java.util.NoSuchElementException;
*/
public final class PhaseSummaryStatistics implements Iterable<ProfilePhase> {
- private final long totalDurationNanos;
+ private long totalDurationNanos;
private final EnumMap<ProfilePhase, Long> durations;
- public PhaseSummaryStatistics(ProfileInfo info) {
+ public PhaseSummaryStatistics() {
durations = new EnumMap<>(ProfilePhase.class);
- long totalDuration = 0;
+ totalDurationNanos = 0;
+ }
+
+ public PhaseSummaryStatistics(ProfileInfo info) {
+ this();
+ addProfileInfo(info);
+ }
+
+ /**
+ * Add a summary of the {@link ProfilePhase}s durations from a {@link ProfileInfo}.
+ */
+ public void addProfileInfo(ProfileInfo info) {
for (ProfilePhase phase : ProfilePhase.values()) {
ProfileInfo.Task phaseTask = info.getPhaseTask(phase);
if (phaseTask != null) {
long phaseDuration = info.getPhaseDuration(phaseTask);
- totalDuration += phaseDuration;
+ totalDurationNanos += phaseDuration;
durations.put(phase, phaseDuration);
}
}
- totalDurationNanos = totalDuration;
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseVfsStatistics.java b/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseVfsStatistics.java
index 4c52c47eb5..58d454f368 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseVfsStatistics.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/statistics/PhaseVfsStatistics.java
@@ -13,10 +13,12 @@
// limitations under the License.
package com.google.devtools.build.lib.profiler.statistics;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.TreeMultimap;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ComparisonChain;
+import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.Table;
+import com.google.common.collect.Table.Cell;
+import com.google.common.collect.Tables;
import com.google.devtools.build.lib.profiler.ProfileInfo;
import com.google.devtools.build.lib.profiler.ProfileInfo.Task;
import com.google.devtools.build.lib.profiler.ProfilePhase;
@@ -28,7 +30,8 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.SortedSet;
/**
* Compute and store statistics of all {@link ProfilerTask}s that begin with VFS_ in sorted order.
@@ -36,34 +39,98 @@ import java.util.Map.Entry;
public final class PhaseVfsStatistics implements Iterable<ProfilerTask> {
/**
- * Pair of duration and count for sorting by duration first and count in case of tie
+ * Duration, count and path for sorting by duration first and count in case of tie. Path for
+ * easy returning of a {@link SortedSet}.
*/
- public static class Stat implements Comparable<Stat> {
- public long duration;
- public long count;
+ public static final class Stat implements Comparable<Stat> {
+ private long duration;
+ private long count;
+ public final String path;
+ public Stat(String path) {
+ this.path = path;
+ }
+
+ public Stat(Stat other) {
+ this.duration = other.duration;
+ this.count = other.count;
+ this.path = other.path;
+ }
+
+ public long getDuration() {
+ return duration;
+ }
+
+ public long getCount() {
+ return count;
+ }
+
+ private void add(Stat other) {
+ this.duration += other.duration;
+ this.count += other.count;
+ }
+
+ private void add(long duration) {
+ this.duration += duration;
+ this.count++;
+ }
+
+ /**
+ * Order first by duration, then count, then path
+ */
@Override
public int compareTo(Stat o) {
- return this.duration == o.duration
- ? Long.compare(this.count, o.count)
- : Long.compare(this.duration, o.duration);
+ return ComparisonChain.start()
+ .compare(duration, o.duration)
+ .compare(count, o.count)
+ .compare(path, o.path)
+ .result();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Stat) {
+ return compareTo((Stat) obj) == 0;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(duration, count, path);
}
}
private final ProfilePhase phase;
- private final EnumMap<ProfilerTask, TreeMultimap<Stat, String>> sortedStatistics;
- private final String workSpaceName;
+ private final Table<ProfilerTask, String, Stat> statistics;
- public PhaseVfsStatistics(final String workSpaceName, ProfilePhase phase, ProfileInfo info) {
- this.workSpaceName = workSpaceName;
+ public PhaseVfsStatistics(ProfilePhase phase) {
this.phase = phase;
- this.sortedStatistics = Maps.newEnumMap(ProfilerTask.class);
+ this.statistics =
+ Tables.newCustomTable(
+ new EnumMap<ProfilerTask, Map<String, Stat>>(ProfilerTask.class),
+ new Supplier<Map<String, Stat>>() {
+ @Override
+ public Map<String, Stat> get() {
+ return new HashMap<>();
+ }
+ });
+ }
+
+ public PhaseVfsStatistics(final String workSpaceName, ProfilePhase phase, ProfileInfo info) {
+ this(phase);
+ addProfileInfo(workSpaceName, info);
+ }
+ /**
+ * Accumulate statistics from another {@link ProfileInfo} in this object.
+ */
+ public void addProfileInfo(final String workSpaceName, ProfileInfo info) {
Task phaseTask = info.getPhaseTask(phase);
if (phaseTask == null) {
return;
}
- collectVfsEntries(info.getTasksForPhase(phaseTask));
+ collectVfsEntries(workSpaceName, info.getTasksForPhase(phaseTask));
}
public ProfilePhase getProfilePhase() {
@@ -71,63 +138,66 @@ public final class PhaseVfsStatistics implements Iterable<ProfilerTask> {
}
public boolean isEmpty() {
- return sortedStatistics.isEmpty();
+ return statistics.isEmpty();
}
- public Iterable<Entry<Stat, String>> getSortedStatistics(ProfilerTask taskType) {
- return sortedStatistics.get(taskType).entries();
+ /**
+ * Builds a new {@link ImmutableSortedSet} of the path statistics for the given
+ * {@link ProfilerTask}.
+ *
+ * <p>{@link Stat}s are sorted by their natural order.
+ */
+ public ImmutableSortedSet<Stat> getSortedStatistics(ProfilerTask taskType) {
+ return ImmutableSortedSet.copyOf(statistics.row(taskType).values());
}
public int getStatisticsCount(ProfilerTask taskType) {
- return sortedStatistics.get(taskType).size();
+ return statistics.row(taskType).size();
}
@Override
public Iterator<ProfilerTask> iterator() {
- return sortedStatistics.keySet().iterator();
+ return statistics.rowKeySet().iterator();
}
/**
- * Group into VFS operations and build maps from path to duration.
+ * Add statistics from another PhaseVfsStatistics aggregation to this one.
*/
- private void collectVfsEntries(List<Task> taskList) {
- EnumMap<ProfilerTask, Map<String, Stat>> stats = Maps.newEnumMap(ProfilerTask.class);
+ public void add(PhaseVfsStatistics other) {
+ for (Cell<ProfilerTask, String, Stat> cell : other.statistics.cellSet()) {
+ Stat stat = statistics.get(cell.getRowKey(), cell.getColumnKey());
+ if (stat == null) {
+ stat = new Stat(cell.getValue());
+ statistics.put(cell.getRowKey(), stat.path, stat);
+ } else {
+ stat.add(cell.getValue());
+ }
+ }
+ }
+
+ /**
+ * Add the VFS operations from the list of tasks to the {@link #statistics} table
+ */
+ private void collectVfsEntries(String workSpaceName, List<Task> taskList) {
for (Task task : taskList) {
- collectVfsEntries(Arrays.asList(task.subtasks));
+ collectVfsEntries(workSpaceName, Arrays.asList(task.subtasks));
if (!task.type.name().startsWith("VFS_")) {
continue;
}
- Map<String, Stat> statsForType = stats.get(task.type);
- if (statsForType == null) {
- statsForType = new HashMap<>();
- stats.put(task.type, statsForType);
- }
-
- String path = currentPathMapping(task.getDescription());
+ String path = pathMapping(workSpaceName, task.getDescription());
- Stat stat = statsForType.get(path);
+ Stat stat = statistics.get(task.type, path);
if (stat == null) {
- stat = new Stat();
+ stat = new Stat(path);
+ statistics.put(task.type, path, stat);
}
- stat.duration += task.durationNanos;
- stat.count++;
- statsForType.put(path, stat);
- }
- // Reverse the maps to get maps from duration to path. We use a TreeMultimap to sort by
- // duration and because durations are not unique.
- for (ProfilerTask type : stats.keySet()) {
- Map<String, Stat> statsForType = stats.get(type);
- TreeMultimap<Stat, String> sortedStats =
- TreeMultimap.create(Ordering.natural().reverse(), Ordering.natural());
-
- Multimaps.invertFrom(Multimaps.forMap(statsForType), sortedStats);
- sortedStatistics.put(type, sortedStats);
+ stat.add(task.durationNanos);
}
}
- private String currentPathMapping(String input) {
+ private String pathMapping(String workSpaceName, String input) {
if (workSpaceName.isEmpty()) {
return input;
} else {
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java b/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java
index 30eeb76111..b21d7ca20d 100644
--- a/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java
+++ b/src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java
@@ -26,6 +26,7 @@ import com.google.devtools.build.lib.profiler.ProfilerTask;
import com.google.devtools.build.lib.profiler.output.HtmlCreator;
import com.google.devtools.build.lib.profiler.output.PhaseText;
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.runtime.BlazeCommand;
@@ -42,6 +43,7 @@ import com.google.devtools.common.options.OptionsBase;
import com.google.devtools.common.options.OptionsParser;
import com.google.devtools.common.options.OptionsProvider;
+import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.EnumMap;
@@ -75,6 +77,16 @@ public final class ProfileCommand implements BlazeCommand {
)
public boolean chart;
+ @Option(
+ name = "combine",
+ defaultValue = "null",
+ help =
+ "If present, the statistics of all given profile files will be combined and output"
+ + " in text/--html format to the file named in the argument. Does not output HTML"
+ + " task charts."
+ )
+ public String combine;
+
@Option(name = "dump",
abbrev='d',
converter = DumpConverter.class,
@@ -178,65 +190,102 @@ public final class ProfileCommand implements BlazeCommand {
env.getReporter().handle(Event.warn(
null, "This information is intended for consumption by Blaze developers"
+ " only, and may change at any time. Script against it at your own risk"));
-
- for (String name : options.getResidue()) {
- Path profileFile = runtime.getWorkingDirectory().getRelative(name);
- try {
- ProfileInfo info = ProfileInfo.loadProfileVerbosely(
- profileFile, getInfoListener(env));
-
- if (opts.dumpMode == null || !opts.dumpMode.contains("unsorted")) {
- ProfileInfo.aggregateProfile(info, getInfoListener(env));
- }
-
- if (opts.taskTree != null) {
- printTaskTree(out, name, info, opts.taskTree, opts.taskTreeThreshold);
- continue;
- }
-
- if (opts.dumpMode != null) {
- dumpProfile(info, out, opts.dumpMode);
- continue;
- }
-
- PhaseSummaryStatistics phaseSummaryStatistics = new PhaseSummaryStatistics(info);
- EnumMap<ProfilePhase, PhaseStatistics> phaseStatistics =
- new EnumMap<>(ProfilePhase.class);
- for (ProfilePhase phase : ProfilePhase.values()) {
- phaseStatistics.put(
- phase, new PhaseStatistics(phase, info, runtime.getWorkspaceName()));
- }
-
+ if (opts.combine != null && opts.dumpMode == null) {
+ MultiProfileStatistics statistics =
+ new MultiProfileStatistics(
+ runtime.getWorkingDirectory(),
+ runtime.getWorkspaceName(),
+ options.getResidue(),
+ getInfoListener(env),
+ opts.vfsStatsLimit > 0);
+ Path outputFile = runtime.getWorkingDirectory().getRelative(opts.combine);
+ try (PrintStream output =
+ new PrintStream(new BufferedOutputStream(outputFile.getOutputStream()))) {
if (opts.html) {
- Path htmlFile =
- profileFile.getParentDirectory().getChild(profileFile.getBaseName() + ".html");
-
- env.getReporter().handle(Event.info("Creating HTML output in " + htmlFile));
-
+ env.getReporter().handle(Event.info("Creating HTML output in " + outputFile));
HtmlCreator.create(
- info,
- htmlFile,
- phaseSummaryStatistics,
- phaseStatistics,
- opts.htmlDetails,
- opts.htmlPixelsPerSecond,
- opts.vfsStatsLimit,
- opts.chart,
- opts.htmlHistograms);
+ output, statistics, opts.htmlDetails, opts.htmlPixelsPerSecond, opts.vfsStatsLimit);
} else {
- CriticalPathStatistics critPathStats = new CriticalPathStatistics(info);
+ env.getReporter().handle(Event.info("Creating text output in " + outputFile));
new PhaseText(
- out,
- phaseSummaryStatistics,
- phaseStatistics,
- Optional.of(critPathStats),
- info.getMissingActionsCount(),
+ output,
+ statistics.getSummaryStatistics(),
+ statistics.getSummaryPhaseStatistics(),
+ Optional.<CriticalPathStatistics>absent(),
+ statistics.getMissingActionsCount(),
opts.vfsStatsLimit)
.print();
}
} catch (IOException e) {
- env.getReporter().handle(Event.error(
- null, "Failed to process file " + name + ": " + e.getMessage()));
+ env
+ .getReporter()
+ .handle(
+ Event.error(
+ "Failed to write to output file " + outputFile + ":" + e.getMessage()));
+ }
+ } else {
+ for (String name : options.getResidue()) {
+ Path profileFile = runtime.getWorkingDirectory().getRelative(name);
+ try {
+ ProfileInfo info = ProfileInfo.loadProfileVerbosely(profileFile, getInfoListener(env));
+
+ if (opts.dumpMode == null || !opts.dumpMode.contains("unsorted")) {
+ ProfileInfo.aggregateProfile(info, getInfoListener(env));
+ }
+
+ if (opts.taskTree != null) {
+ printTaskTree(out, name, info, opts.taskTree, opts.taskTreeThreshold);
+ continue;
+ }
+
+ if (opts.dumpMode != null) {
+ dumpProfile(info, out, opts.dumpMode);
+ continue;
+ }
+
+ PhaseSummaryStatistics phaseSummaryStatistics = new PhaseSummaryStatistics(info);
+ EnumMap<ProfilePhase, PhaseStatistics> phaseStatistics =
+ new EnumMap<>(ProfilePhase.class);
+ for (ProfilePhase phase : ProfilePhase.values()) {
+ phaseStatistics.put(
+ phase,
+ new PhaseStatistics(
+ phase, info, runtime.getWorkspaceName(), opts.vfsStatsLimit > 0));
+ }
+
+ if (opts.html) {
+ Path htmlFile =
+ profileFile.getParentDirectory().getChild(profileFile.getBaseName() + ".html");
+
+ env.getReporter().handle(Event.info("Creating HTML output in " + htmlFile));
+
+ HtmlCreator.create(
+ info,
+ htmlFile,
+ phaseSummaryStatistics,
+ phaseStatistics,
+ opts.htmlDetails,
+ opts.htmlPixelsPerSecond,
+ opts.vfsStatsLimit,
+ opts.chart,
+ opts.htmlHistograms);
+ } else {
+ CriticalPathStatistics critPathStats = new CriticalPathStatistics(info);
+ new PhaseText(
+ out,
+ phaseSummaryStatistics,
+ phaseStatistics,
+ Optional.of(critPathStats),
+ info.getMissingActionsCount(),
+ opts.vfsStatsLimit)
+ .print();
+ }
+ } catch (IOException e) {
+ System.out.println(e);
+ env
+ .getReporter()
+ .handle(Event.error("Failed to analyze profile file(s): " + e.getMessage()));
+ }
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/util/TimeUtilities.java b/src/main/java/com/google/devtools/build/lib/util/TimeUtilities.java
index 13de97cc82..2a1fbebc72 100644
--- a/src/main/java/com/google/devtools/build/lib/util/TimeUtilities.java
+++ b/src/main/java/com/google/devtools/build/lib/util/TimeUtilities.java
@@ -14,6 +14,8 @@
package com.google.devtools.build.lib.util;
+import java.util.concurrent.TimeUnit;
+
/**
* Various utility methods operating on time values.
*/
@@ -38,4 +40,14 @@ public class TimeUtilities {
}
return String.format("%.3f s", ms / 1000.0);
}
+
+ /**
+ * Convert nanoseconds to milliseconds.
+ *
+ * <p>This is different from the methods in {@link TimeUnit} in that it returns and accepts a
+ * double.
+ */
+ public static double nanosToMillis(double timeNanos) {
+ return timeNanos / 1000000;
+ }
}