| // 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.analysis.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 |
| } |
| } |
| |
| |