aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Klaas Boesche <klaasb@google.com>2015-10-13 20:10:15 +0000
committerGravatar Florian Weikert <fwe@google.com>2015-10-13 21:13:31 +0000
commitbb87ee6f2f93d6790a6c0814851ca6b307f0a4e2 (patch)
treeba999ecaf70654b038b11e61f6abead7bcc627b8
parenteff2b450bd44d019d3b23495e383cfce9e473fe6 (diff)
Add HTML profiler execution phase statistics
Re-adds the missing execution phase statistics which got lost in the recent ProfileCommand refactoring. -- MOS_MIGRATED_REVID=105340677
-rw-r--r--src/main/java/BUILD1
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/ProfileInfo.java4
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathHtml.java175
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathText.java69
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/HtmlCreator.java12
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/output/PhaseHtml.java115
-rw-r--r--src/main/java/com/google/devtools/build/lib/profiler/statistics/CriticalPathStatistics.java92
-rw-r--r--src/main/java/com/google/devtools/build/lib/runtime/commands/ProfileCommand.java4
8 files changed, 413 insertions, 59 deletions
diff --git a/src/main/java/BUILD b/src/main/java/BUILD
index 1e30b784ca..89e0276fec 100644
--- a/src/main/java/BUILD
+++ b/src/main/java/BUILD
@@ -189,6 +189,7 @@ java_library(
":vfs",
"//src/main/java/com/google/devtools/build/lib/actions",
"//third_party:guava",
+ "//third_party:jsr305",
],
)
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/ProfileInfo.java b/src/main/java/com/google/devtools/build/lib/profiler/ProfileInfo.java
index b93b8d99dd..10de620193 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/ProfileInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/ProfileInfo.java
@@ -219,6 +219,10 @@ public class ProfileInfo {
return !stats.isEmpty();
}
+ public boolean isFake() {
+ return id < 0;
+ }
+
public long getInheritedDuration() {
return stats.getTotalTime();
}
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathHtml.java b/src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathHtml.java
new file mode 100644
index 0000000000..71299e43d2
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathHtml.java
@@ -0,0 +1,175 @@
+// 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.ProfileInfo.CriticalPathEntry;
+import com.google.devtools.build.lib.profiler.statistics.CriticalPathStatistics;
+import com.google.devtools.build.lib.profiler.statistics.CriticalPathStatistics.MiddleManStatistics;
+import com.google.devtools.build.lib.util.Pair;
+import com.google.devtools.build.lib.util.TimeUtilities;
+
+import java.io.PrintStream;
+
+/**
+ * Generate HTML output from {@link CriticalPathStatistics}.
+ */
+//TODO(bazel-team): Also print remote vs build stats recorded by Logging.CriticalPathStats
+public final class CriticalPathHtml extends HtmlPrinter {
+
+ private final CriticalPathStatistics criticalPathStats;
+ private final long executionTime;
+
+ public CriticalPathHtml(
+ PrintStream out, CriticalPathStatistics critPathStats, long executionTime) {
+ super(out);
+ this.criticalPathStats = critPathStats;
+ this.executionTime = executionTime;
+ }
+
+ public void printTimingBreakdown() {
+ CriticalPathEntry totalPath = criticalPathStats.getTotalPath();
+ CriticalPathEntry optimalPath = criticalPathStats.getOptimalPath();
+ if (totalPath != null) {
+ if (!totalPath.isComponent()) {
+ printCriticalPathTimingBreakdown(totalPath, optimalPath);
+ }
+ } else {
+ lnPrint("Critical path not available because no action graph was generated.");
+ }
+ }
+
+ /**
+ * Print table rows for timing statistics and per path timing percentages.
+ */
+ private void printCriticalPathTimingBreakdown(
+ CriticalPathEntry totalPath, CriticalPathEntry optimalPath) {
+ lnOpen("tr");
+ element("td", "colspan", "4", totalPath.task.type);
+ close();
+
+ lnOpen("tr");
+ element("td", "colspan", "3", "Worker thread scheduling delays");
+ element("td", TimeUtilities.prettyTime(criticalPathStats.getWorkerWaitTime()));
+ close(); // tr
+
+ lnOpen("tr");
+ element("td", "colspan", "3", "Main thread scheduling delays");
+ element("td", TimeUtilities.prettyTime(criticalPathStats.getMainThreadWaitTime()));
+ close(); // tr
+
+ lnOpen("tr");
+ element("td", "colspan", "4", "Critical path time:");
+ close();
+
+ long totalTime = totalPath.cumulativeDuration;
+ lnOpen("tr");
+ element("td", "Actual time");
+ element("td", TimeUtilities.prettyTime(totalTime));
+ element(
+ "td",
+ String.format(
+ "(%s of execution time)", prettyPercentage((double) totalTime / executionTime)));
+ close(); // tr
+
+ long optimalTime = optimalPath.cumulativeDuration;
+ element("td", "colspan", "2", "Time excluding scheduling delays");
+ element("td", TimeUtilities.prettyTime(optimalTime));
+ element(
+ "td",
+ String.format(
+ "(%s of execution time)", prettyPercentage((double) optimalTime / executionTime)));
+ close(); // tr
+
+ // Artificial critical path if we ignore all the time spent in all tasks,
+ // except time directly attributed to the ACTION tasks.
+ lnElement("tr");
+ lnOpen("tr");
+ element("td", "colspan", "4", "Time related to:");
+ close();
+
+ for (Pair<String, Double> relativePathDuration : criticalPathStats) {
+ lnOpen("tr");
+ element("td", "colspan", "3", relativePathDuration.first);
+ element("td", prettyPercentage(relativePathDuration.second));
+ close();
+ }
+ }
+
+ /**
+ * Print total and optimal critical paths if available.
+ */
+ public void printCriticalPaths() {
+ CriticalPathEntry totalPath = criticalPathStats.getTotalPath();
+ printCriticalPath("Critical path", totalPath);
+ // In critical path components we do not record scheduling delay data so it does not make
+ // sense to differentiate it.
+ if (!totalPath.isComponent()) {
+ printCriticalPath(
+ "Critical path excluding scheduling delays", criticalPathStats.getOptimalPath());
+ }
+ }
+
+ private void printCriticalPath(String title, CriticalPathEntry path) {
+ lnOpen("table");
+ lnOpen("tr");
+ element(
+ "td",
+ "colspan",
+ "4",
+ String.format("%s (%s):", title, TimeUtilities.prettyTime(path.cumulativeDuration)));
+ close(); // tr
+
+ lnOpen("tr");
+ boolean pathIsComponent = path.isComponent();
+ element("th", "Id");
+ element("th", "Time");
+ element("th", "Share");
+ if (!pathIsComponent) {
+ element("th", "Critical");
+ }
+ element("th", "Description");
+ close(); // tr
+
+ long totalPathTime = path.cumulativeDuration;
+
+ for (CriticalPathEntry pathEntry : criticalPathStats.getMiddlemanFilteredPath(path)) {
+ String desc = pathEntry.task.getDescription().replace(':', ' ');
+ lnOpen("tr");
+ element("td", pathEntry.task.id);
+ element("td", "style", "text-align: right",
+ TimeUtilities.prettyTime(pathEntry.duration).replace(" ", "&nbsp;"));
+ element("td", prettyPercentage((double) pathEntry.duration / totalPathTime));
+ if (!pathIsComponent) {
+ element("td", prettyPercentage((double) pathEntry.getCriticalTime() / totalPathTime));
+ }
+ element("td", desc);
+ close(); // tr
+ }
+ MiddleManStatistics middleMan = MiddleManStatistics.create(path);
+ if (middleMan.count > 0) {
+ lnOpen("tr");
+ element("td");
+ element("td", TimeUtilities.prettyTime(middleMan.duration));
+ element("td", prettyPercentage((double) middleMan.duration / totalPathTime));
+ if (!pathIsComponent) {
+ element("td", prettyPercentage((double) middleMan.criticalTime / totalPathTime));
+ }
+ element("td", String.format("[%d middleman actions]", middleMan.count));
+ close(); // tr
+ }
+ lnClose(); // table
+ }
+}
+
+
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathText.java b/src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathText.java
index 78ebf2ff54..61c885c4b3 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathText.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/output/CriticalPathText.java
@@ -13,9 +13,9 @@
// limitations under the License.
package com.google.devtools.build.lib.profiler.output;
-import com.google.devtools.build.lib.actions.MiddlemanAction;
import com.google.devtools.build.lib.profiler.ProfileInfo.CriticalPathEntry;
import com.google.devtools.build.lib.profiler.statistics.CriticalPathStatistics;
+import com.google.devtools.build.lib.profiler.statistics.CriticalPathStatistics.MiddleManStatistics;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.TimeUtilities;
@@ -49,7 +49,7 @@ public final class CriticalPathText extends TextPrinter {
}
}
- void printCriticalPathTimingBreakdown(
+ private void printCriticalPathTimingBreakdown(
CriticalPathEntry totalPath, CriticalPathEntry optimalPath) {
lnPrint(totalPath.task.type);
@@ -117,54 +117,41 @@ public final class CriticalPathText extends TextPrinter {
}
long totalPathTime = path.cumulativeDuration;
- int middlemanCount = 0;
- long middlemanDuration = 0L;
- long middlemanCritTime = 0L;
-
- for (; path != null; path = path.next) {
- if (path.task.id < 0) {
- // Ignore fake actions.
- continue;
- } else if (path.task.getDescription().startsWith(MiddlemanAction.MIDDLEMAN_MNEMONIC + " ")
- || path.task.getDescription().startsWith("TargetCompletionMiddleman")) {
- // Aggregate middleman actions.
- middlemanCount++;
- middlemanDuration += path.duration;
- middlemanCritTime += path.getCriticalTime();
+
+ for (CriticalPathEntry pathEntry : criticalPathStats.getMiddlemanFilteredPath(path)) {
+ String desc = pathEntry.task.getDescription().replace(':', ' ');
+ if (isComponent) {
+ lnPrintf(
+ "%6d %11s %8s %s",
+ pathEntry.task.id,
+ TimeUtilities.prettyTime(pathEntry.duration),
+ prettyPercentage((double) pathEntry.duration / totalPathTime),
+ desc);
} else {
- String desc = path.task.getDescription().replace(':', ' ');
- if (isComponent) {
- lnPrintf(
- "%6d %11s %8s %s",
- path.task.id,
- TimeUtilities.prettyTime(path.duration),
- prettyPercentage((double) path.duration / totalPathTime),
- desc);
- } else {
- lnPrintf(
- "%6d %11s %8s %8s %s",
- path.task.id,
- TimeUtilities.prettyTime(path.duration),
- prettyPercentage((double) path.duration / totalPathTime),
- prettyPercentage((double) path.getCriticalTime() / totalPathTime),
- desc);
- }
+ lnPrintf(
+ "%6d %11s %8s %8s %s",
+ pathEntry.task.id,
+ TimeUtilities.prettyTime(pathEntry.duration),
+ prettyPercentage((double) pathEntry.duration / totalPathTime),
+ prettyPercentage((double) pathEntry.getCriticalTime() / totalPathTime),
+ desc);
}
}
- if (middlemanCount > 0) {
+ MiddleManStatistics middleMan = MiddleManStatistics.create(path);
+ if (middleMan.count > 0) {
if (isComponent) {
lnPrintf(
" %11s %8s [%d middleman actions]",
- TimeUtilities.prettyTime(middlemanDuration),
- prettyPercentage((double) middlemanDuration / totalPathTime),
- middlemanCount);
+ TimeUtilities.prettyTime(middleMan.duration),
+ prettyPercentage((double) middleMan.duration / totalPathTime),
+ middleMan.count);
} else {
lnPrintf(
" %11s %8s %8s [%d middleman actions]",
- TimeUtilities.prettyTime(middlemanDuration),
- prettyPercentage((double) middlemanDuration / totalPathTime),
- prettyPercentage((double) middlemanCritTime / totalPathTime),
- middlemanCount);
+ TimeUtilities.prettyTime(middleMan.duration),
+ prettyPercentage((double) middleMan.duration / totalPathTime),
+ prettyPercentage((double) middleMan.criticalTime / totalPathTime),
+ middleMan.count);
}
}
}
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 885c965ee4..ab35718613 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.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;
@@ -151,6 +152,8 @@ public final class HtmlCreator extends HtmlPrinter {
Path htmlFile,
PhaseSummaryStatistics phaseSummaryStats,
EnumMap<ProfilePhase, PhaseStatistics> statistics,
+ CriticalPathStatistics criticalPathStats,
+ int missingActionsCount,
boolean detailed,
int htmlPixelsPerSecond,
int vfsStatsLimit,
@@ -158,7 +161,14 @@ public final class HtmlCreator extends HtmlPrinter {
boolean generateHistograms)
throws IOException {
try (PrintStream out = new PrintStream(new BufferedOutputStream(htmlFile.getOutputStream()))) {
- PhaseHtml phaseHtml = new PhaseHtml(out, phaseSummaryStats, statistics, vfsStatsLimit);
+ 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) {
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 039ea28de7..0403768112 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
@@ -13,8 +13,10 @@
// 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.ProfilerTask;
+import com.google.devtools.build.lib.profiler.statistics.CriticalPathStatistics;
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.PhaseVfsStatistics;
@@ -34,7 +36,9 @@ public final class PhaseHtml extends HtmlPrinter {
private final PhaseSummaryStatistics phaseSummaryStats;
private final EnumMap<ProfilePhase, PhaseStatistics> phaseStatistics;
+ private final Optional<CriticalPathStatistics> criticalPathStatistics;
private final int vfsStatsLimit;
+ private final Optional<Integer> missingActionsCount;
/**
* @param vfsStatsLimit maximum number of VFS statistics to print, or -1 for no limit.
@@ -43,13 +47,31 @@ public final class PhaseHtml extends HtmlPrinter {
PrintStream out,
PhaseSummaryStatistics phaseSummaryStats,
EnumMap<ProfilePhase, PhaseStatistics> phaseStatistics,
+ Optional<CriticalPathStatistics> critPathStats,
+ Optional<Integer> missingActionsCount,
int vfsStatsLimit) {
super(out);
this.phaseSummaryStats = phaseSummaryStats;
this.phaseStatistics = phaseStatistics;
+ this.criticalPathStatistics = critPathStats;
+ this.missingActionsCount = missingActionsCount;
this.vfsStatsLimit = vfsStatsLimit;
}
+ public PhaseHtml(
+ PrintStream out,
+ PhaseSummaryStatistics summaryStatistics,
+ EnumMap<ProfilePhase, PhaseStatistics> summaryPhaseStatistics,
+ int vfsStatsLimit) {
+ this(
+ out,
+ summaryStatistics,
+ summaryPhaseStatistics,
+ Optional.<CriticalPathStatistics>absent(),
+ Optional.<Integer>absent(),
+ vfsStatsLimit);
+ }
+
/**
* Output a style tag with all necessary CSS directives
*/
@@ -92,6 +114,7 @@ public final class PhaseHtml extends HtmlPrinter {
}
printPhaseStatistics(statistics);
}
+ printExecutionPhaseStatistics();
lnElement("div", "style", "clear: both;");
}
@@ -99,28 +122,90 @@ public final class PhaseHtml extends HtmlPrinter {
* Print header and tables for a single phase.
*/
private void printPhaseStatistics(PhaseStatistics phaseStat) {
+ printPhaseHead(phaseStat);
+ printTwoColumnStatistic(
+ String.format("Total %s time", phaseStat.getProfilePhase().nick),
+ phaseStat.getPhaseDurationNanos());
+
+ printTimingDistribution(phaseStat);
+ lnClose(); // table
+ printVfsStatistics(phaseStat.getVfsStatistics());
+ lnClose(); // div
+ }
+
+ private void printPhaseHead(PhaseStatistics phaseStat) {
lnOpen("div", "class", "phase-statistics");
lnElement(
"h3",
String.format(
"%s Phase Information", StringUtil.capitalize(phaseStat.getProfilePhase().nick)));
lnOpen("table", "class", "phase-statistics");
- lnOpen("tr");
- open("td", "class", "left", "colspan", "3");
- printf("Total %s phase time", phaseStat.getProfilePhase().nick);
- close(); // td
- element("td", TimeUtilities.prettyTime(phaseStat.getPhaseDurationNanos()));
- lnClose(); // tr
+ }
+
+ private void printExecutionPhaseStatistics() {
+ PhaseStatistics execPhase = phaseStatistics.get(ProfilePhase.EXECUTE);
+ if (execPhase == null || !execPhase.wasExecuted()) {
+ return;
+ }
+ printPhaseHead(execPhase);
+ for (PhaseStatistics phaseStat :
+ Arrays.asList(
+ phaseStatistics.get(ProfilePhase.PREPARE),
+ execPhase,
+ phaseStatistics.get(ProfilePhase.FINISH))) {
+ if (phaseStat.wasExecuted()) {
+ printTwoColumnStatistic(
+ String.format("Total %s time", phaseStat.getProfilePhase().nick),
+ phaseStat.getPhaseDurationNanos());
+ }
+ }
+
+ long graphTime = execPhase.getTotalDurationNanos(ProfilerTask.ACTION_GRAPH);
+ long execTime = execPhase.getPhaseDurationNanos() - graphTime;
+ printTwoColumnStatistic("Action dependency map creation", graphTime);
+ printTwoColumnStatistic("Actual execution time", execTime);
+
+ CriticalPathHtml criticalPaths = null;
+ if (criticalPathStatistics.isPresent()) {
+ criticalPaths = new CriticalPathHtml(out, criticalPathStatistics.get(), execTime);
+ criticalPaths.printTimingBreakdown();
+ }
+
+ printTimingDistribution(execPhase);
+ lnClose(); // table opened by printPhaseHead
+
+ if (criticalPathStatistics.isPresent()) {
+ criticalPaths.printCriticalPaths();
+ }
+
+ if (missingActionsCount.isPresent() && missingActionsCount.get() > 0) {
+ lnOpen("p");
+ lnPrint(missingActionsCount.get());
+ print(
+ " action(s) are present in the"
+ + " action graph but missing instrumentation data. Most likely the profile file"
+ + " has been created during a failed or aborted build.");
+ lnClose();
+ }
+
+ printVfsStatistics(execPhase.getVfsStatistics());
+ lnClose(); // div
+ }
+
+ /**
+ * Print the table rows for the {@link ProfilerTask} types and their execution times.
+ */
+ private void printTimingDistribution(PhaseStatistics phaseStat) {
if (!phaseStat.isEmpty()) {
lnOpen("tr");
element("td", "class", "left", "colspan", "4", "Total time (across all threads) spent on:");
close(); // tr
lnOpen("tr");
- element("td", "Type");
- element("td", "Total");
- element("td", "Count");
- element("td", "Average");
+ element("th", "Type");
+ element("th", "Total");
+ element("th", "Count");
+ element("th", "Average");
close(); // tr
for (ProfilerTask taskType : phaseStat) {
lnOpen("tr", "class", "phase-task-statistics");
@@ -131,9 +216,6 @@ public final class PhaseHtml extends HtmlPrinter {
close(); // tr
}
}
- lnClose(); // table
- printVfsStatistics(phaseStat.getVfsStatistics());
- lnClose(); // div
}
/**
@@ -202,6 +284,13 @@ public final class PhaseHtml extends HtmlPrinter {
lnClose(); // table
lnClose(); // div
}
+
+ private void printTwoColumnStatistic(String name, long duration) {
+ lnOpen("tr");
+ element("td", "class", "left", "colspan", "3", name);
+ element("td", TimeUtilities.prettyTime(duration));
+ lnClose(); // tr
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/statistics/CriticalPathStatistics.java b/src/main/java/com/google/devtools/build/lib/profiler/statistics/CriticalPathStatistics.java
index 35f4972d43..8631016543 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/statistics/CriticalPathStatistics.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/statistics/CriticalPathStatistics.java
@@ -14,6 +14,7 @@
package com.google.devtools.build.lib.profiler.statistics;
import com.google.common.base.Predicate;
+import com.google.devtools.build.lib.actions.MiddlemanAction;
import com.google.devtools.build.lib.profiler.ProfileInfo;
import com.google.devtools.build.lib.profiler.ProfileInfo.CriticalPathEntry;
import com.google.devtools.build.lib.profiler.ProfileInfo.Task;
@@ -27,6 +28,8 @@ import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
+import javax.annotation.Nullable;
+
/**
* Keeps a predefined list of {@link CriticalPathEntry}'s cumulative durations and allows
* iterating over pairs of their descriptions and relative durations.
@@ -35,8 +38,8 @@ import java.util.List;
public final class CriticalPathStatistics implements Iterable<Pair<String, Double>> {
private static final EnumSet<ProfilerTask> FILTER_NONE = EnumSet.noneOf(ProfilerTask.class);
- /** Always filter out ACTION_LOCK and WAIT tasks to simulate unlimited resource critical path
- * (TODO see comments inside formatExecutionPhaseStatistics() method).
+ /** Always filter out ACTION_LOCK and WAIT tasks to simulate unlimited resource critical path.
+ * @see #optimalPath
*/
private static final EnumSet<ProfilerTask> DEFAULT_FILTER =
EnumSet.of(ProfilerTask.ACTION_LOCK, ProfilerTask.WAIT);
@@ -148,6 +151,42 @@ public final class CriticalPathStatistics implements Iterable<Pair<String, Doubl
return mainThreadWaitTime;
}
+ /**
+ * Constructs a filtered Iterable from a critical path.
+ *
+ * <p>Ignores all fake (task id < 0) path entries and
+ * {@link com.google.devtools.build.lib.actions.MiddlemanAction}-related entries.
+ */
+ public Iterable<CriticalPathEntry> getMiddlemanFilteredPath(final CriticalPathEntry path) {
+ return new Iterable<CriticalPathEntry>() {
+ @Override
+ public Iterator<CriticalPathEntry> iterator() {
+ return new Iterator<CriticalPathEntry>() {
+ private CriticalPathEntry nextEntry = path;
+
+ @Override
+ public boolean hasNext() {
+ return nextEntry != null;
+ }
+
+ @Override
+ public CriticalPathEntry next() {
+ CriticalPathEntry current = nextEntry;
+ do {
+ nextEntry = nextEntry.next;
+ } while (nextEntry != null && (nextEntry.task.isFake() || isMiddleMan(nextEntry.task)));
+ return current;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
@Override
public Iterator<Pair<String, Double>> iterator() {
return new Iterator<Pair<String, Double>>() {
@@ -184,7 +223,7 @@ public final class CriticalPathStatistics implements Iterable<Pair<String, Doubl
}
/**
- * Extract all {@link CriticalPathEntry} durations for the filters defined by {@link #FILTERS}.
+ * Extracts all {@link CriticalPathEntry} durations for the filters defined by {@link #FILTERS}.
*/
private static List<Long> getCriticalPathDurations(ProfileInfo info) {
List<Long> list = new ArrayList<>(FILTERS.size());
@@ -207,5 +246,52 @@ public final class CriticalPathStatistics implements Iterable<Pair<String, Doubl
Collections.addAll(filter, tasks);
return Pair.of(description, filter);
}
+
+ /**
+ * @return Whether the task is {@link MiddlemanAction}-related.
+ */
+ private static boolean isMiddleMan(Task task) {
+ String description = task.getDescription();
+ return description.startsWith(MiddlemanAction.MIDDLEMAN_MNEMONIC + " ")
+ || description.startsWith("TargetCompletionMiddleman");
+ }
+
+ /**
+ * Aggregates statistics related to {@link MiddlemanAction}s on the critical path.
+ */
+ public static final class MiddleManStatistics {
+ public final int count;
+ public final long duration;
+ public final long criticalTime;
+
+ private MiddleManStatistics(int count, long duration, long criticalTime) {
+ this.count = count;
+ this.duration = duration;
+ this.criticalTime = criticalTime;
+ }
+
+ /**
+ * Summarizes middleman actions on the critical path.
+ * @return null if path is null, else the aggregate statistics
+ */
+ public static MiddleManStatistics create(@Nullable CriticalPathEntry path) {
+ if (path == null) {
+ return null;
+ }
+ int middleManCount = 0;
+ long middleManDuration = 0;
+ long middleManCritTime = 0;
+ for (CriticalPathEntry entry = path; entry != null; entry = entry.next) {
+ Task task = entry.task;
+ if (!task.isFake() && isMiddleMan(task)) {
+ // Aggregate middleman actions.
+ middleManCount++;
+ middleManDuration += entry.duration;
+ middleManCritTime += entry.getCriticalTime();
+ }
+ }
+ return new MiddleManStatistics(middleManCount, middleManDuration, middleManCritTime);
+ }
+ }
}
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 b21d7ca20d..993798c373 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
@@ -253,6 +253,7 @@ public final class ProfileCommand implements BlazeCommand {
phase, info, runtime.getWorkspaceName(), opts.vfsStatsLimit > 0));
}
+ CriticalPathStatistics critPathStats = new CriticalPathStatistics(info);
if (opts.html) {
Path htmlFile =
profileFile.getParentDirectory().getChild(profileFile.getBaseName() + ".html");
@@ -264,13 +265,14 @@ public final class ProfileCommand implements BlazeCommand {
htmlFile,
phaseSummaryStatistics,
phaseStatistics,
+ critPathStats,
+ info.getMissingActionsCount(),
opts.htmlDetails,
opts.htmlPixelsPerSecond,
opts.vfsStatsLimit,
opts.chart,
opts.htmlHistograms);
} else {
- CriticalPathStatistics critPathStats = new CriticalPathStatistics(info);
new PhaseText(
out,
phaseSummaryStatistics,