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