diff options
author | Klaas Boesche <klaasb@google.com> | 2015-10-13 20:10:15 +0000 |
---|---|---|
committer | Florian Weikert <fwe@google.com> | 2015-10-13 21:13:31 +0000 |
commit | bb87ee6f2f93d6790a6c0814851ca6b307f0a4e2 (patch) | |
tree | ba999ecaf70654b038b11e61f6abead7bcc627b8 /src/main/java/com/google/devtools/build/lib | |
parent | eff2b450bd44d019d3b23495e383cfce9e473fe6 (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
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib')
7 files changed, 412 insertions, 59 deletions
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(" ", " ")); + 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, |